Merge "Clear suggestionSpan reference once popup window is closing."
diff --git a/Android.mk b/Android.mk
index 9029f4e..71bba0f 100644
--- a/Android.mk
+++ b/Android.mk
@@ -68,6 +68,8 @@
core/java/android/app/IActivityContainerCallback.aidl \
core/java/android/app/IActivityController.aidl \
core/java/android/app/IActivityPendingResult.aidl \
+ core/java/android/app/IAlarmCompleteListener.aidl \
+ core/java/android/app/IAlarmListener.aidl \
core/java/android/app/IAlarmManager.aidl \
core/java/android/app/IAppTask.aidl \
core/java/android/app/ITaskStackListener.aidl \
@@ -257,6 +259,7 @@
core/java/android/view/accessibility/IAccessibilityManager.aidl \
core/java/android/view/accessibility/IAccessibilityManagerClient.aidl \
core/java/android/view/IApplicationToken.aidl \
+ core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl \
core/java/android/view/IAssetAtlas.aidl \
core/java/android/view/IGraphicsStats.aidl \
core/java/android/view/IInputFilter.aidl \
@@ -686,7 +689,7 @@
# Common sources for doc check and api check
common_src_files := \
$(call find-other-html-files, $(html_dirs)) \
- $(addprefix ../../libcore/, $(libcore_to_document)) \
+ $(addprefix ../../, $(libcore_to_document)) \
$(addprefix ../../external/junit/, $(junit_to_document))
# These are relative to frameworks/base
diff --git a/api/current.txt b/api/current.txt
index ef18bf9..dabc084 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -241,10 +241,8 @@
field public static final int activatedBackgroundIndicator = 16843517; // 0x10102fd
field public static final int activityCloseEnterAnimation = 16842938; // 0x10100ba
field public static final int activityCloseExitAnimation = 16842939; // 0x10100bb
- field public static final int activityHeight = 16844021; // 0x10104f5
field public static final int activityOpenEnterAnimation = 16842936; // 0x10100b8
field public static final int activityOpenExitAnimation = 16842937; // 0x10100b9
- field public static final int activityWidth = 16844020; // 0x10104f4
field public static final int addPrintersActivity = 16843750; // 0x10103e6
field public static final int addStatesFromChildren = 16842992; // 0x10100f0
field public static final int adjustViewBounds = 16843038; // 0x101011e
@@ -323,7 +321,7 @@
field public static final int buttonBarNeutralButtonStyle = 16843914; // 0x101048a
field public static final int buttonBarPositiveButtonStyle = 16843913; // 0x1010489
field public static final int buttonBarStyle = 16843566; // 0x101032e
- field public static final int buttonGravity = 16844029; // 0x10104fd
+ field public static final int buttonGravity = 16844031; // 0x10104ff
field public static final int buttonStyle = 16842824; // 0x1010048
field public static final int buttonStyleInset = 16842826; // 0x101004a
field public static final int buttonStyleSmall = 16842825; // 0x1010049
@@ -373,7 +371,7 @@
field public static final int codes = 16843330; // 0x1010242
field public static final int collapseColumns = 16843083; // 0x101014b
field public static final int collapseContentDescription = 16843984; // 0x10104d0
- field public static final int collapseIcon = 16844030; // 0x10104fe
+ field public static final int collapseIcon = 16844032; // 0x1010500
field public static final int color = 16843173; // 0x10101a5
field public static final int colorAccent = 16843829; // 0x1010435
field public static final int colorActivatedHighlight = 16843664; // 0x1010390
@@ -414,7 +412,7 @@
field public static final int contentInsetRight = 16843862; // 0x1010456
field public static final int contentInsetStart = 16843859; // 0x1010453
field public static final int contextClickable = 16844007; // 0x10104e7
- field public static final int contextPopupMenuStyle = 16844032; // 0x1010500
+ field public static final int contextPopupMenuStyle = 16844034; // 0x1010502
field public static final int controlX1 = 16843772; // 0x10103fc
field public static final int controlX2 = 16843774; // 0x10103fe
field public static final int controlY1 = 16843773; // 0x10103fd
@@ -501,6 +499,7 @@
field public static final int ellipsize = 16842923; // 0x10100ab
field public static final int ems = 16843096; // 0x1010158
field public static final int enabled = 16842766; // 0x101000e
+ field public static final int encryptionAware = 16844038; // 0x1010506
field public static final int end = 16843996; // 0x10104dc
field public static final int endColor = 16843166; // 0x101019e
field public static final deprecated int endYear = 16843133; // 0x101017d
@@ -561,6 +560,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 forceDeviceEncrypted = 16844037; // 0x1010505
field public static final int foreground = 16843017; // 0x1010109
field public static final int foregroundGravity = 16843264; // 0x1010200
field public static final int foregroundTint = 16843885; // 0x101046d
@@ -673,8 +673,10 @@
field public static final int indicatorStart = 16843729; // 0x10103d1
field public static final int inflatedId = 16842995; // 0x10100f3
field public static final int initOrder = 16842778; // 0x101001a
+ field public static final int initialHeight = 16844021; // 0x10104f5
field public static final int initialKeyguardLayout = 16843714; // 0x10103c2
field public static final int initialLayout = 16843345; // 0x1010251
+ field public static final int initialWidth = 16844020; // 0x10104f4
field public static final int innerRadius = 16843359; // 0x101025f
field public static final int innerRadiusRatio = 16843163; // 0x101019b
field public static final deprecated int inputMethod = 16843112; // 0x1010168
@@ -780,7 +782,7 @@
field public static final int layout_y = 16843136; // 0x1010180
field public static final int left = 16843181; // 0x10101ad
field public static final int letterSpacing = 16843958; // 0x10104b6
- field public static final int level = 16844031; // 0x10104ff
+ field public static final int level = 16844033; // 0x1010501
field public static final int lineSpacingExtra = 16843287; // 0x1010217
field public static final int lineSpacingMultiplier = 16843288; // 0x1010218
field public static final int lines = 16843092; // 0x1010154
@@ -813,7 +815,7 @@
field public static final int marqueeRepeatLimit = 16843293; // 0x101021d
field public static final int matchOrder = 16843855; // 0x101044f
field public static final int max = 16843062; // 0x1010136
- field public static final int maxButtonHeight = 16844028; // 0x10104fc
+ field public static final int maxButtonHeight = 16844030; // 0x10104fe
field public static final int maxDate = 16843584; // 0x1010340
field public static final int maxEms = 16843095; // 0x1010157
field public static final int maxHeight = 16843040; // 0x1010120
@@ -841,6 +843,7 @@
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 minimumHorizontalAngle = 16843901; // 0x101047d
field public static final int minimumVerticalAngle = 16843902; // 0x101047e
field public static final int mipMap = 16843725; // 0x10103cd
@@ -997,7 +1000,7 @@
field public static final int resizeClip = 16843983; // 0x10104cf
field public static final int resizeMode = 16843619; // 0x1010363
field public static final int resizeable = 16843405; // 0x101028d
- field public static final int resizeableActivity = 16844022; // 0x10104f6
+ field public static final int resizeableActivity = 16844023; // 0x10104f7
field public static final int resource = 16842789; // 0x1010025
field public static final int restoreAnyVersion = 16843450; // 0x10102ba
field public static final deprecated int restoreNeedsApplication = 16843421; // 0x101029d
@@ -1173,6 +1176,7 @@
field public static final int summaryOn = 16843247; // 0x10101ef
field public static final int supportsAssist = 16844016; // 0x10104f0
field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1
+ field public static final int supportsPictureInPicture = 16844024; // 0x10104f8
field public static final int supportsRtl = 16843695; // 0x10103af
field public static final int supportsSwitchingToNextInputMethod = 16843755; // 0x10103eb
field public static final int supportsUploading = 16843419; // 0x101029b
@@ -1221,7 +1225,7 @@
field public static final int textAppearanceListItemSmall = 16843679; // 0x101039f
field public static final int textAppearanceMedium = 16842817; // 0x1010041
field public static final int textAppearanceMediumInverse = 16842820; // 0x1010044
- field public static final int textAppearancePopupMenuHeader = 16844033; // 0x1010501
+ field public static final int textAppearancePopupMenuHeader = 16844035; // 0x1010503
field public static final int textAppearanceSearchResultSubtitle = 16843424; // 0x10102a0
field public static final int textAppearanceSearchResultTitle = 16843425; // 0x10102a1
field public static final int textAppearanceSmall = 16842818; // 0x1010042
@@ -1290,11 +1294,11 @@
field public static final int tintMode = 16843771; // 0x10103fb
field public static final int title = 16843233; // 0x10101e1
field public static final int titleCondensed = 16843234; // 0x10101e2
- field public static final int titleMargin = 16844023; // 0x10104f7
- field public static final int titleMarginBottom = 16844027; // 0x10104fb
- field public static final int titleMarginEnd = 16844025; // 0x10104f9
- field public static final int titleMarginStart = 16844024; // 0x10104f8
- field public static final int titleMarginTop = 16844026; // 0x10104fa
+ field public static final int titleMargin = 16844025; // 0x10104f9
+ field public static final int titleMarginBottom = 16844029; // 0x10104fd
+ field public static final int titleMarginEnd = 16844027; // 0x10104fb
+ field public static final int titleMarginStart = 16844026; // 0x10104fa
+ field public static final int titleMarginTop = 16844028; // 0x10104fc
field public static final int titleTextAppearance = 16843822; // 0x101042e
field public static final int titleTextColor = 16844003; // 0x10104e3
field public static final int titleTextStyle = 16843512; // 0x10102f8
@@ -1394,6 +1398,7 @@
field public static final int windowAllowReturnTransitionOverlap = 16843835; // 0x101043b
field public static final int windowAnimationStyle = 16842926; // 0x10100ae
field public static final int windowBackground = 16842836; // 0x1010054
+ field public static final int windowBackgroundFallback = 16844036; // 0x1010504
field public static final int windowClipToOutline = 16843947; // 0x10104ab
field public static final int windowCloseOnTouchOutside = 16843611; // 0x101035b
field public static final int windowContentOverlay = 16842841; // 0x1010059
@@ -2876,6 +2881,7 @@
method public android.animation.TimeInterpolator getInterpolator();
method public java.util.ArrayList<android.animation.Animator.AnimatorListener> getListeners();
method public abstract long getStartDelay();
+ method public long getTotalDuration();
method public boolean isPaused();
method public abstract boolean isRunning();
method public boolean isStarted();
@@ -2891,6 +2897,7 @@
method public void setupEndValues();
method public void setupStartValues();
method public void start();
+ field public static final long DURATION_INFINITE = -1L; // 0xffffffffffffffffL
}
public static abstract interface Animator.AnimatorListener {
@@ -3759,17 +3766,21 @@
public class AlarmManager {
method public void cancel(android.app.PendingIntent);
+ method public void cancel(android.app.AlarmManager.OnAlarmListener);
method public android.app.AlarmManager.AlarmClockInfo getNextAlarmClock();
method public void set(int, long, android.app.PendingIntent);
+ method public void set(int, long, java.lang.String, android.app.AlarmManager.OnAlarmListener, android.os.Handler);
method public void setAlarmClock(android.app.AlarmManager.AlarmClockInfo, android.app.PendingIntent);
method public void setAndAllowWhileIdle(int, long, android.app.PendingIntent);
method public void setExact(int, long, android.app.PendingIntent);
+ method public void setExact(int, long, java.lang.String, android.app.AlarmManager.OnAlarmListener, android.os.Handler);
method public void setExactAndAllowWhileIdle(int, long, android.app.PendingIntent);
method public void setInexactRepeating(int, long, long, android.app.PendingIntent);
method public void setRepeating(int, long, long, android.app.PendingIntent);
method public void setTime(long);
method public void setTimeZone(java.lang.String);
method public void setWindow(int, long, long, android.app.PendingIntent);
+ method public void setWindow(int, long, long, java.lang.String, android.app.AlarmManager.OnAlarmListener, android.os.Handler);
field public static final java.lang.String ACTION_NEXT_ALARM_CLOCK_CHANGED = "android.app.action.NEXT_ALARM_CLOCK_CHANGED";
field public static final int ELAPSED_REALTIME = 3; // 0x3
field public static final int ELAPSED_REALTIME_WAKEUP = 2; // 0x2
@@ -3791,6 +3802,10 @@
field public static final android.os.Parcelable.Creator<android.app.AlarmManager.AlarmClockInfo> CREATOR;
}
+ public static abstract interface AlarmManager.OnAlarmListener {
+ method public abstract void onAlarm();
+ }
+
public class AlertDialog extends android.app.Dialog implements android.content.DialogInterface {
ctor protected AlertDialog(android.content.Context);
ctor protected AlertDialog(android.content.Context, boolean, android.content.DialogInterface.OnCancelListener);
@@ -4756,6 +4771,7 @@
method public android.graphics.drawable.Icon getLargeIcon();
method public android.graphics.drawable.Icon getSmallIcon();
method public java.lang.String getSortKey();
+ method public android.app.Notification.Topic[] getTopics();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.media.AudioAttributes AUDIO_ATTRIBUTES_DEFAULT;
field public static final java.lang.String CATEGORY_ALARM = "alarm";
@@ -4824,17 +4840,17 @@
field public android.app.Notification.Action[] actions;
field public android.media.AudioAttributes audioAttributes;
field public deprecated int audioStreamType;
- field public android.widget.RemoteViews bigContentView;
+ field public deprecated android.widget.RemoteViews bigContentView;
field public java.lang.String category;
field public int color;
field public android.app.PendingIntent contentIntent;
- field public android.widget.RemoteViews contentView;
+ field public deprecated android.widget.RemoteViews contentView;
field public int defaults;
field public android.app.PendingIntent deleteIntent;
field public android.os.Bundle extras;
field public int flags;
field public android.app.PendingIntent fullScreenIntent;
- field public android.widget.RemoteViews headsUpContentView;
+ field public deprecated android.widget.RemoteViews headsUpContentView;
field public deprecated int icon;
field public int iconLevel;
field public deprecated android.graphics.Bitmap largeIcon;
@@ -4920,18 +4936,27 @@
method public android.app.Notification.Builder addAction(android.app.Notification.Action);
method public android.app.Notification.Builder addExtras(android.os.Bundle);
method public android.app.Notification.Builder addPerson(java.lang.String);
+ method public android.app.Notification.Builder addTopic(android.app.Notification.Topic);
method public android.app.Notification build();
method public android.app.Notification.Builder extend(android.app.Notification.Extender);
method public android.os.Bundle getExtras();
method public deprecated android.app.Notification getNotification();
+ method public android.widget.RemoteViews makeBigContentView();
+ method public android.widget.RemoteViews makeContentView();
+ method public android.widget.RemoteViews makeHeadsUpContentView();
+ method public static android.app.Notification.Builder recoverBuilder(android.content.Context, android.app.Notification);
+ method public android.app.Notification.Builder setActions(android.app.Notification.Action...);
method public android.app.Notification.Builder setAutoCancel(boolean);
method public android.app.Notification.Builder setCategory(java.lang.String);
method public android.app.Notification.Builder setColor(int);
- method public android.app.Notification.Builder setContent(android.widget.RemoteViews);
+ method public deprecated android.app.Notification.Builder setContent(android.widget.RemoteViews);
method public 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);
+ method public android.app.Notification.Builder setCustomBigContentView(android.widget.RemoteViews);
+ method public android.app.Notification.Builder setCustomContentView(android.widget.RemoteViews);
+ method public android.app.Notification.Builder setCustomHeadsUpContentView(android.widget.RemoteViews);
method public android.app.Notification.Builder setDefaults(int);
method public android.app.Notification.Builder setDeleteIntent(android.app.PendingIntent);
method public android.app.Notification.Builder setExtras(android.os.Bundle);
@@ -5027,6 +5052,16 @@
field protected android.app.Notification.Builder mBuilder;
}
+ public static class Notification.Topic implements android.os.Parcelable {
+ ctor public Notification.Topic(java.lang.String, java.lang.CharSequence);
+ method public android.app.Notification.Topic clone();
+ method public int describeContents();
+ method public java.lang.String getId();
+ method public java.lang.CharSequence getLabel();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.Notification.Topic> CREATOR;
+ }
+
public static final class Notification.WearableExtender implements android.app.Notification.Extender {
ctor public Notification.WearableExtender();
ctor public Notification.WearableExtender(android.app.Notification);
@@ -5697,6 +5732,7 @@
method public int getStorageEncryptionStatus();
method public android.app.admin.SystemUpdatePolicy getSystemUpdatePolicy();
method public java.util.List<android.os.PersistableBundle> getTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName);
+ method public android.os.Bundle getUserRestrictions(android.content.ComponentName);
method public boolean hasCaCertInstalled(android.content.ComponentName, byte[]);
method public boolean hasGrantedPolicy(android.content.ComponentName, int);
method public boolean installCaCert(android.content.ComponentName, byte[]);
@@ -5708,6 +5744,7 @@
method public boolean isLockTaskPermitted(java.lang.String);
method public boolean isMasterVolumeMuted(android.content.ComponentName);
method public boolean isProfileOwnerApp(java.lang.String);
+ method public boolean isProvisioningAllowed(java.lang.String);
method public boolean isUninstallBlocked(android.content.ComponentName, java.lang.String);
method public void lockNow();
method public void removeActiveAdmin(android.content.ComponentName);
@@ -5856,6 +5893,7 @@
method public java.lang.String getStructuredData();
method public android.net.Uri getWebUri();
method public boolean isAppProvidedIntent();
+ method public boolean isAppProvidedWebUri();
method public void setClipData(android.content.ClipData);
method public void setIntent(android.content.Intent);
method public void setStructuredData(java.lang.String);
@@ -7704,6 +7742,7 @@
method public final android.content.res.ColorStateList getColorStateList(int);
method public abstract android.content.ContentResolver getContentResolver();
method public abstract java.io.File getDatabasePath(java.lang.String);
+ method public abstract java.io.File getDeviceEncryptedFilesDir();
method public abstract java.io.File getDir(java.lang.String, int);
method public final android.graphics.drawable.Drawable getDrawable(int);
method public abstract java.io.File getExternalCacheDir();
@@ -7886,6 +7925,7 @@
method public java.io.File getCodeCacheDir();
method public android.content.ContentResolver getContentResolver();
method public java.io.File getDatabasePath(java.lang.String);
+ method public java.io.File getDeviceEncryptedFilesDir();
method public java.io.File getDir(java.lang.String, int);
method public java.io.File getExternalCacheDir();
method public java.io.File[] getExternalCacheDirs();
@@ -8922,8 +8962,8 @@
field public int configChanges;
field public int documentLaunchMode;
field public int flags;
- field public android.content.pm.ActivityInfo.InitialLayout initialLayout;
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;
@@ -8936,11 +8976,12 @@
field public int uiOptions;
}
- public static final class ActivityInfo.InitialLayout {
- ctor public ActivityInfo.InitialLayout(int, float, int, float, int);
+ public static final class ActivityInfo.Layout {
+ ctor public ActivityInfo.Layout(int, float, int, float, 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 width;
field public final float widthFraction;
}
@@ -9024,6 +9065,7 @@
field public android.content.pm.ApplicationInfo applicationInfo;
field public int descriptionRes;
field public boolean enabled;
+ field public boolean encryptionAware;
field public boolean exported;
field public java.lang.String processName;
}
@@ -9391,6 +9433,7 @@
field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT = "android.hardware.faketouch.multitouch.distinct";
field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_JAZZHAND = "android.hardware.faketouch.multitouch.jazzhand";
field public static final java.lang.String FEATURE_FINGERPRINT = "android.hardware.fingerprint";
+ field public static final java.lang.String FEATURE_FREEFORM_WINDOW_MANAGEMENT = "android.software.freeform_window_management";
field public static final java.lang.String FEATURE_GAMEPAD = "android.hardware.gamepad";
field public static final java.lang.String FEATURE_HIFI_SENSORS = "android.hardware.sensor.hifi_sensors";
field public static final java.lang.String FEATURE_HOME_SCREEN = "android.software.home_screen";
@@ -9444,6 +9487,7 @@
field public static final int GET_CONFIGURATIONS = 16384; // 0x4000
field public static final int GET_DISABLED_COMPONENTS = 512; // 0x200
field public static final int GET_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000
+ field public static final int GET_ENCRYPTION_UNAWARE_COMPONENTS = 262144; // 0x40000
field public static final int GET_GIDS = 256; // 0x100
field public static final int GET_INSTRUMENTATION = 16; // 0x10
field public static final int GET_INTENT_FILTERS = 32; // 0x20
@@ -15469,6 +15513,7 @@
field public static final int BUFFER_FLAG_KEY_FRAME = 1; // 0x1
field public static final deprecated int BUFFER_FLAG_SYNC_FRAME = 1; // 0x1
field public static final int CONFIGURE_FLAG_ENCODE = 1; // 0x1
+ field public static final int CRYPTO_MODE_AES_CBC = 2; // 0x2
field public static final int CRYPTO_MODE_AES_CTR = 1; // 0x1
field public static final int CRYPTO_MODE_UNENCRYPTED = 0; // 0x0
field public static final deprecated int INFO_OUTPUT_BUFFERS_CHANGED = -3; // 0xfffffffd
@@ -15520,6 +15565,7 @@
public static final class MediaCodec.CryptoInfo {
ctor public MediaCodec.CryptoInfo();
method public void set(int, int[], int[], byte[], byte[], int);
+ method public void setPattern(android.media.MediaCodec.CryptoInfo.Pattern);
field public byte[] iv;
field public byte[] key;
field public int mode;
@@ -15528,6 +15574,13 @@
field public int numSubSamples;
}
+ public static final class MediaCodec.CryptoInfo.Pattern {
+ ctor public MediaCodec.CryptoInfo.Pattern(int, int);
+ method public int getEncryptBlocks();
+ method public int getSkipBlocks();
+ method public void set(int, int);
+ }
+
public static abstract interface MediaCodec.OnFrameRenderedListener {
method public abstract void onFrameRendered(android.media.MediaCodec, long, long);
}
@@ -18173,6 +18226,24 @@
public class MtpEvent {
ctor public MtpEvent();
method public int getEventCode();
+ field public static final int EVENT_CANCEL_TRANSACTION = 16385; // 0x4001
+ field public static final int EVENT_CAPTURE_COMPLETE = 16397; // 0x400d
+ field public static final int EVENT_DEVICE_INFO_CHANGED = 16392; // 0x4008
+ field public static final int EVENT_DEVICE_PROP_CHANGED = 16390; // 0x4006
+ field public static final int EVENT_DEVICE_RESET = 16395; // 0x400b
+ field public static final int EVENT_OBJECT_ADDED = 16386; // 0x4002
+ field public static final int EVENT_OBJECT_INFO_CHANGED = 16391; // 0x4007
+ field public static final int EVENT_OBJECT_PROP_CHANGED = 51201; // 0xc801
+ field public static final int EVENT_OBJECT_PROP_DESC_CHANGED = 51202; // 0xc802
+ field public static final int EVENT_OBJECT_REFERENCES_CHANGED = 51203; // 0xc803
+ field public static final int EVENT_OBJECT_REMOVED = 16387; // 0x4003
+ field public static final int EVENT_REQUEST_OBJECT_TRANSFER = 16393; // 0x4009
+ field public static final int EVENT_STORAGE_INFO_CHANGED = 16396; // 0x400c
+ field public static final int EVENT_STORE_ADDED = 16388; // 0x4004
+ field public static final int EVENT_STORE_FULL = 16394; // 0x400a
+ field public static final int EVENT_STORE_REMOVED = 16389; // 0x4005
+ field public static final int EVENT_UNDEFINED = 16384; // 0x4000
+ field public static final int EVENT_UNREPORTED_STATUS = 16398; // 0x400e
}
public final class MtpObjectInfo {
@@ -22117,6 +22188,7 @@
field public static final int GL_ACTIVE_PROGRAM = 33369; // 0x8259
field public static final int GL_ACTIVE_RESOURCES = 37621; // 0x92f5
field public static final int GL_ACTIVE_VARIABLES = 37637; // 0x9305
+ field public static final int GL_ALL_BARRIER_BITS = -1; // 0xffffffff
field public static final int GL_ALL_SHADER_BITS = -1; // 0xffffffff
field public static final int GL_ARRAY_SIZE = 37627; // 0x92fb
field public static final int GL_ARRAY_STRIDE = 37630; // 0x92fe
@@ -22140,6 +22212,7 @@
field public static final int GL_DISPATCH_INDIRECT_BUFFER_BINDING = 37103; // 0x90ef
field public static final int GL_DRAW_INDIRECT_BUFFER = 36671; // 0x8f3f
field public static final int GL_DRAW_INDIRECT_BUFFER_BINDING = 36675; // 0x8f43
+ field public static final int GL_ELEMENT_ARRAY_BARRIER_BIT = 2; // 0x2
field public static final int GL_FRAGMENT_SHADER_BIT = 2; // 0x2
field public static final int GL_FRAMEBUFFER_BARRIER_BIT = 1024; // 0x400
field public static final int GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS = 37652; // 0x9314
@@ -22229,6 +22302,7 @@
field public static final int GL_SAMPLE_MASK = 36433; // 0x8e51
field public static final int GL_SAMPLE_MASK_VALUE = 36434; // 0x8e52
field public static final int GL_SAMPLE_POSITION = 36432; // 0x8e50
+ field public static final int GL_SHADER_IMAGE_ACCESS_BARRIER_BIT = 32; // 0x20
field public static final int GL_SHADER_STORAGE_BARRIER_BIT = 8192; // 0x2000
field public static final int GL_SHADER_STORAGE_BLOCK = 37606; // 0x92e6
field public static final int GL_SHADER_STORAGE_BUFFER = 37074; // 0x90d2
@@ -22274,6 +22348,7 @@
field public static final int GL_UNSIGNED_INT_IMAGE_3D = 36964; // 0x9064
field public static final int GL_UNSIGNED_INT_IMAGE_CUBE = 36966; // 0x9066
field public static final int GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE = 37130; // 0x910a
+ field public static final int GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT = 1; // 0x1
field public static final int GL_VERTEX_ATTRIB_BINDING = 33492; // 0x82d4
field public static final int GL_VERTEX_ATTRIB_RELATIVE_OFFSET = 33493; // 0x82d5
field public static final int GL_VERTEX_BINDING_BUFFER = 36687; // 0x8f4f
@@ -25000,6 +25075,7 @@
field public static final int OUTGOING_TYPE = 2; // 0x2
field public static final java.lang.String PHONE_ACCOUNT_COMPONENT_NAME = "subscription_component_name";
field public static final java.lang.String PHONE_ACCOUNT_ID = "subscription_id";
+ field public static final java.lang.String POST_DIAL_DIGITS = "post_dial_digits";
field public static final int PRESENTATION_ALLOWED = 1; // 0x1
field public static final int PRESENTATION_PAYPHONE = 4; // 0x4
field public static final int PRESENTATION_RESTRICTED = 2; // 0x2
@@ -26159,6 +26235,7 @@
field public static final int FLAG_SUPPORTS_MOVE = 256; // 0x100
field public static final int FLAG_SUPPORTS_RENAME = 64; // 0x40
field public static final int FLAG_SUPPORTS_THUMBNAIL = 1; // 0x1
+ field public static final int FLAG_SUPPORTS_TYPED_DOCUMENT = 512; // 0x200
field public static final int FLAG_SUPPORTS_WRITE = 2; // 0x2
field public static final java.lang.String MIME_TYPE_DIR = "vnd.android.document/directory";
}
@@ -26196,6 +26273,7 @@
method public final android.os.ParcelFileDescriptor openFile(android.net.Uri, java.lang.String, android.os.CancellationSignal) throws java.io.FileNotFoundException;
method public final android.content.res.AssetFileDescriptor openTypedAssetFile(android.net.Uri, java.lang.String, android.os.Bundle) throws java.io.FileNotFoundException;
method public final android.content.res.AssetFileDescriptor openTypedAssetFile(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException;
+ method public android.content.res.AssetFileDescriptor openTypedDocument(java.lang.String, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException;
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
method public abstract android.database.Cursor queryChildDocuments(java.lang.String, java.lang.String[], java.lang.String) throws java.io.FileNotFoundException;
method public abstract android.database.Cursor queryDocument(java.lang.String, java.lang.String[]) throws java.io.FileNotFoundException;
@@ -26722,6 +26800,7 @@
field public static final deprecated java.lang.String DEVICE_PROVISIONED = "device_provisioned";
field public static final java.lang.String ENABLED_ACCESSIBILITY_SERVICES = "enabled_accessibility_services";
field public static final java.lang.String ENABLED_INPUT_METHODS = "enabled_input_methods";
+ field public static final java.lang.String ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES = "enabled_notification_policy_access_packages";
field public static final deprecated java.lang.String HTTP_PROXY = "http_proxy";
field public static final java.lang.String INPUT_METHOD_SELECTOR_VISIBILITY = "input_method_selector_visibility";
field public static final java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
@@ -30497,6 +30576,8 @@
field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4
field public static final int CAPABILITY_VIDEO_CALLING = 8; // 0x8
field public static final android.os.Parcelable.Creator<android.telecom.PhoneAccount> CREATOR;
+ field public static final java.lang.String EXTRA_CALL_SUBJECT_CHARACTER_ENCODING = "android.telecom.extra.CALL_SUBJECT_CHARACTER_ENCODING";
+ field public static final java.lang.String EXTRA_CALL_SUBJECT_MAX_LENGTH = "android.telecom.extra.CALL_SUBJECT_MAX_LENGTH";
field public static final int NO_HIGHLIGHT_COLOR = 0; // 0x0
field public static final int NO_RESOURCE_ID = -1; // 0xffffffff
field public static final java.lang.String SCHEME_SIP = "sip";
@@ -31867,6 +31948,7 @@
method public java.io.File getCodeCacheDir();
method public android.content.ContentResolver getContentResolver();
method public java.io.File getDatabasePath(java.lang.String);
+ method public java.io.File getDeviceEncryptedFilesDir();
method public java.io.File getDir(java.lang.String, int);
method public java.io.File getExternalCacheDir();
method public java.io.File[] getExternalCacheDirs();
@@ -34207,6 +34289,18 @@
public deprecated class FloatMath {
}
+ public abstract class FloatProperty extends android.util.Property {
+ ctor public FloatProperty(java.lang.String);
+ method public final void set(T, java.lang.Float);
+ method public abstract void setValue(T, float);
+ }
+
+ public abstract class IntProperty extends android.util.Property {
+ ctor public IntProperty(java.lang.String);
+ method public final void set(T, java.lang.Integer);
+ method public abstract void setValue(T, int);
+ }
+
public final class JsonReader implements java.io.Closeable {
ctor public JsonReader(java.io.Reader);
method public void beginArray() throws java.io.IOException;
@@ -35295,8 +35389,10 @@
field public static final int KEYCODE_CLEAR = 28; // 0x1c
field public static final int KEYCODE_COMMA = 55; // 0x37
field public static final int KEYCODE_CONTACTS = 207; // 0xcf
+ field public static final int KEYCODE_COPY = 278; // 0x116
field public static final int KEYCODE_CTRL_LEFT = 113; // 0x71
field public static final int KEYCODE_CTRL_RIGHT = 114; // 0x72
+ field public static final int KEYCODE_CUT = 277; // 0x115
field public static final int KEYCODE_D = 32; // 0x20
field public static final int KEYCODE_DEL = 67; // 0x43
field public static final int KEYCODE_DPAD_CENTER = 23; // 0x17
@@ -35414,6 +35510,7 @@
field public static final int KEYCODE_PAGE_DOWN = 93; // 0x5d
field public static final int KEYCODE_PAGE_UP = 92; // 0x5c
field public static final int KEYCODE_PAIRING = 225; // 0xe1
+ field public static final int KEYCODE_PASTE = 279; // 0x117
field public static final int KEYCODE_PERIOD = 56; // 0x38
field public static final int KEYCODE_PICTSYMBOLS = 94; // 0x5e
field public static final int KEYCODE_PLUS = 81; // 0x51
@@ -36422,6 +36519,7 @@
method public boolean performHapticFeedback(int);
method public boolean performHapticFeedback(int, int);
method public boolean performLongClick();
+ method public boolean performLongClick(float, float);
method public void playSoundEffect(int);
method public boolean post(java.lang.Runnable);
method public boolean postDelayed(java.lang.Runnable, long);
@@ -37399,6 +37497,8 @@
method public abstract void setContentView(int);
method public abstract void setContentView(android.view.View);
method public abstract void setContentView(android.view.View, android.view.ViewGroup.LayoutParams);
+ method public abstract void setDecorView(android.view.View);
+ method public abstract void setDecorView(int);
method protected void setDefaultWindowFormat(int);
method public void setDimAmount(float);
method public void setElevation(float);
@@ -39157,6 +39257,7 @@
method public abstract android.net.Uri getUrl();
method public abstract boolean hasGesture();
method public abstract boolean isForMainFrame();
+ method public abstract boolean isRedirect();
}
public class WebResourceResponse {
@@ -39194,6 +39295,7 @@
method public abstract java.lang.String getDefaultTextEncodingName();
method public static java.lang.String getDefaultUserAgent(android.content.Context);
method public abstract deprecated android.webkit.WebSettings.ZoomDensity getDefaultZoom();
+ method public abstract int getDisabledActionModeMenuItems();
method public abstract boolean getDisplayZoomControls();
method public abstract boolean getDomStorageEnabled();
method public abstract java.lang.String getFantasyFontFamily();
@@ -39237,6 +39339,7 @@
method public abstract void setDefaultFontSize(int);
method public abstract void setDefaultTextEncodingName(java.lang.String);
method public abstract deprecated void setDefaultZoom(android.webkit.WebSettings.ZoomDensity);
+ method public abstract void setDisabledActionModeMenuItems(int);
method public abstract void setDisplayZoomControls(boolean);
method public abstract void setDomStorageEnabled(boolean);
method public abstract deprecated void setEnableSmoothTransition(boolean);
@@ -39276,6 +39379,10 @@
field public static final int LOAD_DEFAULT = -1; // 0xffffffff
field public static final deprecated int LOAD_NORMAL = 0; // 0x0
field public static final int LOAD_NO_CACHE = 2; // 0x2
+ field public static final int MENU_ITEM_NONE = 0; // 0x0
+ field public static final int MENU_ITEM_PROCESS_TEXT = 4; // 0x4
+ field public static final int MENU_ITEM_SHARE = 1; // 0x1
+ field public static final int MENU_ITEM_WEB_SEARCH = 2; // 0x2
field public static final int MIXED_CONTENT_ALWAYS_ALLOW = 0; // 0x0
field public static final int MIXED_CONTENT_COMPATIBILITY_MODE = 2; // 0x2
field public static final int MIXED_CONTENT_NEVER_ALLOW = 1; // 0x1
@@ -39513,7 +39620,8 @@
method public deprecated android.webkit.WebResourceResponse shouldInterceptRequest(android.webkit.WebView, java.lang.String);
method public android.webkit.WebResourceResponse shouldInterceptRequest(android.webkit.WebView, android.webkit.WebResourceRequest);
method public boolean shouldOverrideKeyEvent(android.webkit.WebView, android.view.KeyEvent);
- method public boolean shouldOverrideUrlLoading(android.webkit.WebView, java.lang.String);
+ method public deprecated boolean shouldOverrideUrlLoading(android.webkit.WebView, java.lang.String);
+ method public boolean shouldOverrideUrlLoading(android.webkit.WebView, android.webkit.WebResourceRequest);
field public static final int ERROR_AUTHENTICATION = -4; // 0xfffffffc
field public static final int ERROR_BAD_URL = -12; // 0xfffffff4
field public static final int ERROR_CONNECT = -6; // 0xfffffffa
diff --git a/api/system-current.txt b/api/system-current.txt
index 0b216bdd..19d9aa2 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -333,10 +333,8 @@
field public static final int activatedBackgroundIndicator = 16843517; // 0x10102fd
field public static final int activityCloseEnterAnimation = 16842938; // 0x10100ba
field public static final int activityCloseExitAnimation = 16842939; // 0x10100bb
- field public static final int activityHeight = 16844021; // 0x10104f5
field public static final int activityOpenEnterAnimation = 16842936; // 0x10100b8
field public static final int activityOpenExitAnimation = 16842937; // 0x10100b9
- field public static final int activityWidth = 16844020; // 0x10104f4
field public static final int addPrintersActivity = 16843750; // 0x10103e6
field public static final int addStatesFromChildren = 16842992; // 0x10100f0
field public static final int adjustViewBounds = 16843038; // 0x101011e
@@ -415,7 +413,7 @@
field public static final int buttonBarNeutralButtonStyle = 16843914; // 0x101048a
field public static final int buttonBarPositiveButtonStyle = 16843913; // 0x1010489
field public static final int buttonBarStyle = 16843566; // 0x101032e
- field public static final int buttonGravity = 16844029; // 0x10104fd
+ field public static final int buttonGravity = 16844031; // 0x10104ff
field public static final int buttonStyle = 16842824; // 0x1010048
field public static final int buttonStyleInset = 16842826; // 0x101004a
field public static final int buttonStyleSmall = 16842825; // 0x1010049
@@ -465,7 +463,7 @@
field public static final int codes = 16843330; // 0x1010242
field public static final int collapseColumns = 16843083; // 0x101014b
field public static final int collapseContentDescription = 16843984; // 0x10104d0
- field public static final int collapseIcon = 16844030; // 0x10104fe
+ field public static final int collapseIcon = 16844032; // 0x1010500
field public static final int color = 16843173; // 0x10101a5
field public static final int colorAccent = 16843829; // 0x1010435
field public static final int colorActivatedHighlight = 16843664; // 0x1010390
@@ -506,7 +504,7 @@
field public static final int contentInsetRight = 16843862; // 0x1010456
field public static final int contentInsetStart = 16843859; // 0x1010453
field public static final int contextClickable = 16844007; // 0x10104e7
- field public static final int contextPopupMenuStyle = 16844032; // 0x1010500
+ field public static final int contextPopupMenuStyle = 16844034; // 0x1010502
field public static final int controlX1 = 16843772; // 0x10103fc
field public static final int controlX2 = 16843774; // 0x10103fe
field public static final int controlY1 = 16843773; // 0x10103fd
@@ -593,6 +591,7 @@
field public static final int ellipsize = 16842923; // 0x10100ab
field public static final int ems = 16843096; // 0x1010158
field public static final int enabled = 16842766; // 0x101000e
+ field public static final int encryptionAware = 16844038; // 0x1010506
field public static final int end = 16843996; // 0x10104dc
field public static final int endColor = 16843166; // 0x101019e
field public static final deprecated int endYear = 16843133; // 0x101017d
@@ -653,6 +652,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 forceDeviceEncrypted = 16844037; // 0x1010505
field public static final int foreground = 16843017; // 0x1010109
field public static final int foregroundGravity = 16843264; // 0x1010200
field public static final int foregroundTint = 16843885; // 0x101046d
@@ -765,8 +765,10 @@
field public static final int indicatorStart = 16843729; // 0x10103d1
field public static final int inflatedId = 16842995; // 0x10100f3
field public static final int initOrder = 16842778; // 0x101001a
+ field public static final int initialHeight = 16844021; // 0x10104f5
field public static final int initialKeyguardLayout = 16843714; // 0x10103c2
field public static final int initialLayout = 16843345; // 0x1010251
+ field public static final int initialWidth = 16844020; // 0x10104f4
field public static final int innerRadius = 16843359; // 0x101025f
field public static final int innerRadiusRatio = 16843163; // 0x101019b
field public static final deprecated int inputMethod = 16843112; // 0x1010168
@@ -872,7 +874,7 @@
field public static final int layout_y = 16843136; // 0x1010180
field public static final int left = 16843181; // 0x10101ad
field public static final int letterSpacing = 16843958; // 0x10104b6
- field public static final int level = 16844031; // 0x10104ff
+ field public static final int level = 16844033; // 0x1010501
field public static final int lineSpacingExtra = 16843287; // 0x1010217
field public static final int lineSpacingMultiplier = 16843288; // 0x1010218
field public static final int lines = 16843092; // 0x1010154
@@ -905,7 +907,7 @@
field public static final int marqueeRepeatLimit = 16843293; // 0x101021d
field public static final int matchOrder = 16843855; // 0x101044f
field public static final int max = 16843062; // 0x1010136
- field public static final int maxButtonHeight = 16844028; // 0x10104fc
+ field public static final int maxButtonHeight = 16844030; // 0x10104fe
field public static final int maxDate = 16843584; // 0x1010340
field public static final int maxEms = 16843095; // 0x1010157
field public static final int maxHeight = 16843040; // 0x1010120
@@ -933,6 +935,7 @@
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 minimumHorizontalAngle = 16843901; // 0x101047d
field public static final int minimumVerticalAngle = 16843902; // 0x101047e
field public static final int mipMap = 16843725; // 0x10103cd
@@ -1089,7 +1092,7 @@
field public static final int resizeClip = 16843983; // 0x10104cf
field public static final int resizeMode = 16843619; // 0x1010363
field public static final int resizeable = 16843405; // 0x101028d
- field public static final int resizeableActivity = 16844022; // 0x10104f6
+ field public static final int resizeableActivity = 16844023; // 0x10104f7
field public static final int resource = 16842789; // 0x1010025
field public static final int restoreAnyVersion = 16843450; // 0x10102ba
field public static final deprecated int restoreNeedsApplication = 16843421; // 0x101029d
@@ -1269,6 +1272,7 @@
field public static final int summaryOn = 16843247; // 0x10101ef
field public static final int supportsAssist = 16844016; // 0x10104f0
field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1
+ field public static final int supportsPictureInPicture = 16844024; // 0x10104f8
field public static final int supportsRtl = 16843695; // 0x10103af
field public static final int supportsSwitchingToNextInputMethod = 16843755; // 0x10103eb
field public static final int supportsUploading = 16843419; // 0x101029b
@@ -1317,7 +1321,7 @@
field public static final int textAppearanceListItemSmall = 16843679; // 0x101039f
field public static final int textAppearanceMedium = 16842817; // 0x1010041
field public static final int textAppearanceMediumInverse = 16842820; // 0x1010044
- field public static final int textAppearancePopupMenuHeader = 16844033; // 0x1010501
+ field public static final int textAppearancePopupMenuHeader = 16844035; // 0x1010503
field public static final int textAppearanceSearchResultSubtitle = 16843424; // 0x10102a0
field public static final int textAppearanceSearchResultTitle = 16843425; // 0x10102a1
field public static final int textAppearanceSmall = 16842818; // 0x1010042
@@ -1386,11 +1390,11 @@
field public static final int tintMode = 16843771; // 0x10103fb
field public static final int title = 16843233; // 0x10101e1
field public static final int titleCondensed = 16843234; // 0x10101e2
- field public static final int titleMargin = 16844023; // 0x10104f7
- field public static final int titleMarginBottom = 16844027; // 0x10104fb
- field public static final int titleMarginEnd = 16844025; // 0x10104f9
- field public static final int titleMarginStart = 16844024; // 0x10104f8
- field public static final int titleMarginTop = 16844026; // 0x10104fa
+ field public static final int titleMargin = 16844025; // 0x10104f9
+ field public static final int titleMarginBottom = 16844029; // 0x10104fd
+ field public static final int titleMarginEnd = 16844027; // 0x10104fb
+ field public static final int titleMarginStart = 16844026; // 0x10104fa
+ field public static final int titleMarginTop = 16844028; // 0x10104fc
field public static final int titleTextAppearance = 16843822; // 0x101042e
field public static final int titleTextColor = 16844003; // 0x10104e3
field public static final int titleTextStyle = 16843512; // 0x10102f8
@@ -1490,6 +1494,7 @@
field public static final int windowAllowReturnTransitionOverlap = 16843835; // 0x101043b
field public static final int windowAnimationStyle = 16842926; // 0x10100ae
field public static final int windowBackground = 16842836; // 0x1010054
+ field public static final int windowBackgroundFallback = 16844036; // 0x1010504
field public static final int windowClipToOutline = 16843947; // 0x10104ab
field public static final int windowCloseOnTouchOutside = 16843611; // 0x101035b
field public static final int windowContentOverlay = 16842841; // 0x1010059
@@ -2975,6 +2980,7 @@
method public android.animation.TimeInterpolator getInterpolator();
method public java.util.ArrayList<android.animation.Animator.AnimatorListener> getListeners();
method public abstract long getStartDelay();
+ method public long getTotalDuration();
method public boolean isPaused();
method public abstract boolean isRunning();
method public boolean isStarted();
@@ -2990,6 +2996,7 @@
method public void setupEndValues();
method public void setupStartValues();
method public void start();
+ field public static final long DURATION_INFINITE = -1L; // 0xffffffffffffffffL
}
public static abstract interface Animator.AnimatorListener {
@@ -3868,18 +3875,23 @@
public class AlarmManager {
method public void cancel(android.app.PendingIntent);
+ method public void cancel(android.app.AlarmManager.OnAlarmListener);
method public android.app.AlarmManager.AlarmClockInfo getNextAlarmClock();
method public void set(int, long, android.app.PendingIntent);
+ method public void set(int, long, java.lang.String, android.app.AlarmManager.OnAlarmListener, android.os.Handler);
method public void set(int, long, long, long, android.app.PendingIntent, android.os.WorkSource);
+ method public void set(int, long, long, long, android.app.AlarmManager.OnAlarmListener, android.os.Handler, android.os.WorkSource);
method public void setAlarmClock(android.app.AlarmManager.AlarmClockInfo, android.app.PendingIntent);
method public void setAndAllowWhileIdle(int, long, android.app.PendingIntent);
method public void setExact(int, long, android.app.PendingIntent);
+ method public void setExact(int, long, java.lang.String, android.app.AlarmManager.OnAlarmListener, android.os.Handler);
method public void setExactAndAllowWhileIdle(int, long, android.app.PendingIntent);
method public void setInexactRepeating(int, long, long, android.app.PendingIntent);
method public void setRepeating(int, long, long, android.app.PendingIntent);
method public void setTime(long);
method public void setTimeZone(java.lang.String);
method public void setWindow(int, long, long, android.app.PendingIntent);
+ method public void setWindow(int, long, long, java.lang.String, android.app.AlarmManager.OnAlarmListener, android.os.Handler);
field public static final java.lang.String ACTION_NEXT_ALARM_CLOCK_CHANGED = "android.app.action.NEXT_ALARM_CLOCK_CHANGED";
field public static final int ELAPSED_REALTIME = 3; // 0x3
field public static final int ELAPSED_REALTIME_WAKEUP = 2; // 0x2
@@ -3901,6 +3913,10 @@
field public static final android.os.Parcelable.Creator<android.app.AlarmManager.AlarmClockInfo> CREATOR;
}
+ public static abstract interface AlarmManager.OnAlarmListener {
+ method public abstract void onAlarm();
+ }
+
public class AlertDialog extends android.app.Dialog implements android.content.DialogInterface {
ctor protected AlertDialog(android.content.Context);
ctor protected AlertDialog(android.content.Context, boolean, android.content.DialogInterface.OnCancelListener);
@@ -4873,6 +4889,7 @@
method public android.graphics.drawable.Icon getLargeIcon();
method public android.graphics.drawable.Icon getSmallIcon();
method public java.lang.String getSortKey();
+ method public android.app.Notification.Topic[] getTopics();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.media.AudioAttributes AUDIO_ATTRIBUTES_DEFAULT;
field public static final java.lang.String CATEGORY_ALARM = "alarm";
@@ -4941,17 +4958,17 @@
field public android.app.Notification.Action[] actions;
field public android.media.AudioAttributes audioAttributes;
field public deprecated int audioStreamType;
- field public android.widget.RemoteViews bigContentView;
+ field public deprecated android.widget.RemoteViews bigContentView;
field public java.lang.String category;
field public int color;
field public android.app.PendingIntent contentIntent;
- field public android.widget.RemoteViews contentView;
+ field public deprecated android.widget.RemoteViews contentView;
field public int defaults;
field public android.app.PendingIntent deleteIntent;
field public android.os.Bundle extras;
field public int flags;
field public android.app.PendingIntent fullScreenIntent;
- field public android.widget.RemoteViews headsUpContentView;
+ field public deprecated android.widget.RemoteViews headsUpContentView;
field public deprecated int icon;
field public int iconLevel;
field public deprecated android.graphics.Bitmap largeIcon;
@@ -5037,18 +5054,27 @@
method public android.app.Notification.Builder addAction(android.app.Notification.Action);
method public android.app.Notification.Builder addExtras(android.os.Bundle);
method public android.app.Notification.Builder addPerson(java.lang.String);
+ method public android.app.Notification.Builder addTopic(android.app.Notification.Topic);
method public android.app.Notification build();
method public android.app.Notification.Builder extend(android.app.Notification.Extender);
method public android.os.Bundle getExtras();
method public deprecated android.app.Notification getNotification();
+ method public android.widget.RemoteViews makeBigContentView();
+ method public android.widget.RemoteViews makeContentView();
+ method public android.widget.RemoteViews makeHeadsUpContentView();
+ method public static android.app.Notification.Builder recoverBuilder(android.content.Context, android.app.Notification);
+ method public android.app.Notification.Builder setActions(android.app.Notification.Action...);
method public android.app.Notification.Builder setAutoCancel(boolean);
method public android.app.Notification.Builder setCategory(java.lang.String);
method public android.app.Notification.Builder setColor(int);
- method public android.app.Notification.Builder setContent(android.widget.RemoteViews);
+ method public deprecated android.app.Notification.Builder setContent(android.widget.RemoteViews);
method public 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);
+ method public android.app.Notification.Builder setCustomBigContentView(android.widget.RemoteViews);
+ method public android.app.Notification.Builder setCustomContentView(android.widget.RemoteViews);
+ method public android.app.Notification.Builder setCustomHeadsUpContentView(android.widget.RemoteViews);
method public android.app.Notification.Builder setDefaults(int);
method public android.app.Notification.Builder setDeleteIntent(android.app.PendingIntent);
method public android.app.Notification.Builder setExtras(android.os.Bundle);
@@ -5144,6 +5170,16 @@
field protected android.app.Notification.Builder mBuilder;
}
+ public static class Notification.Topic implements android.os.Parcelable {
+ ctor public Notification.Topic(java.lang.String, java.lang.CharSequence);
+ method public android.app.Notification.Topic clone();
+ method public int describeContents();
+ method public java.lang.String getId();
+ method public java.lang.CharSequence getLabel();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.Notification.Topic> CREATOR;
+ }
+
public static final class Notification.WearableExtender implements android.app.Notification.Extender {
ctor public Notification.WearableExtender();
ctor public Notification.WearableExtender(android.app.Notification);
@@ -5826,6 +5862,7 @@
method public int getStorageEncryptionStatus();
method public android.app.admin.SystemUpdatePolicy getSystemUpdatePolicy();
method public java.util.List<android.os.PersistableBundle> getTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName);
+ method public android.os.Bundle getUserRestrictions(android.content.ComponentName);
method public boolean hasCaCertInstalled(android.content.ComponentName, byte[]);
method public boolean hasGrantedPolicy(android.content.ComponentName, int);
method public boolean installCaCert(android.content.ComponentName, byte[]);
@@ -5837,6 +5874,7 @@
method public boolean isLockTaskPermitted(java.lang.String);
method public boolean isMasterVolumeMuted(android.content.ComponentName);
method public boolean isProfileOwnerApp(java.lang.String);
+ method public boolean isProvisioningAllowed(java.lang.String);
method public boolean isUninstallBlocked(android.content.ComponentName, java.lang.String);
method public void lockNow();
method public void notifyPendingSystemUpdate(long);
@@ -5989,6 +6027,7 @@
method public java.lang.String getStructuredData();
method public android.net.Uri getWebUri();
method public boolean isAppProvidedIntent();
+ method public boolean isAppProvidedWebUri();
method public void setClipData(android.content.ClipData);
method public void setIntent(android.content.Intent);
method public void setStructuredData(java.lang.String);
@@ -7944,7 +7983,9 @@
method public final int getColor(int);
method public final android.content.res.ColorStateList getColorStateList(int);
method public abstract android.content.ContentResolver getContentResolver();
+ method public abstract java.io.File getCredentialEncryptedFilesDir();
method public abstract java.io.File getDatabasePath(java.lang.String);
+ method public abstract java.io.File getDeviceEncryptedFilesDir();
method public abstract java.io.File getDir(java.lang.String, int);
method public final android.graphics.drawable.Drawable getDrawable(int);
method public abstract java.io.File getExternalCacheDir();
@@ -8134,7 +8175,9 @@
method public java.lang.ClassLoader getClassLoader();
method public java.io.File getCodeCacheDir();
method public android.content.ContentResolver getContentResolver();
+ method public java.io.File getCredentialEncryptedFilesDir();
method public java.io.File getDatabasePath(java.lang.String);
+ method public java.io.File getDeviceEncryptedFilesDir();
method public java.io.File getDir(java.lang.String, int);
method public java.io.File getExternalCacheDir();
method public java.io.File[] getExternalCacheDirs();
@@ -9179,8 +9222,8 @@
field public int configChanges;
field public int documentLaunchMode;
field public int flags;
- field public android.content.pm.ActivityInfo.InitialLayout initialLayout;
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;
@@ -9193,11 +9236,12 @@
field public int uiOptions;
}
- public static final class ActivityInfo.InitialLayout {
- ctor public ActivityInfo.InitialLayout(int, float, int, float, int);
+ public static final class ActivityInfo.Layout {
+ ctor public ActivityInfo.Layout(int, float, int, float, 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 width;
field public final float widthFraction;
}
@@ -9281,6 +9325,7 @@
field public android.content.pm.ApplicationInfo applicationInfo;
field public int descriptionRes;
field public boolean enabled;
+ field public boolean encryptionAware;
field public boolean exported;
field public java.lang.String processName;
}
@@ -9519,6 +9564,7 @@
method public void setAppLabel(java.lang.CharSequence);
method public void setAppPackageName(java.lang.String);
method public void setGrantedRuntimePermissions(java.lang.String[]);
+ method public void setInstallFlagsQuick();
method public void setInstallLocation(int);
method public void setOriginatingUid(int);
method public void setOriginatingUri(android.net.Uri);
@@ -9684,6 +9730,7 @@
field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT = "android.hardware.faketouch.multitouch.distinct";
field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_JAZZHAND = "android.hardware.faketouch.multitouch.jazzhand";
field public static final java.lang.String FEATURE_FINGERPRINT = "android.hardware.fingerprint";
+ field public static final java.lang.String FEATURE_FREEFORM_WINDOW_MANAGEMENT = "android.software.freeform_window_management";
field public static final java.lang.String FEATURE_GAMEPAD = "android.hardware.gamepad";
field public static final java.lang.String FEATURE_HIFI_SENSORS = "android.hardware.sensor.hifi_sensors";
field public static final java.lang.String FEATURE_HOME_SCREEN = "android.software.home_screen";
@@ -9737,6 +9784,7 @@
field public static final int GET_CONFIGURATIONS = 16384; // 0x4000
field public static final int GET_DISABLED_COMPONENTS = 512; // 0x200
field public static final int GET_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000
+ field public static final int GET_ENCRYPTION_UNAWARE_COMPONENTS = 262144; // 0x40000
field public static final int GET_GIDS = 256; // 0x100
field public static final int GET_INSTRUMENTATION = 16; // 0x10
field public static final int GET_INTENT_FILTERS = 32; // 0x20
@@ -14445,7 +14493,7 @@
field public static final int POWER_STATUS_TRANSIENT_TO_ON = 2; // 0x2
field public static final int POWER_STATUS_TRANSIENT_TO_STANDBY = 3; // 0x3
field public static final int POWER_STATUS_UNKNOWN = -1; // 0xffffffff
- field public static final int RESULT_ALREADY_IN_PROGRESS = 4; // 0x4
+ field public static final deprecated int RESULT_ALREADY_IN_PROGRESS = 4; // 0x4
field public static final int RESULT_COMMUNICATION_FAILED = 7; // 0x7
field public static final int RESULT_EXCEPTION = 5; // 0x5
field public static final int RESULT_INCORRECT_MODE = 6; // 0x6
@@ -14881,6 +14929,7 @@
public static class RadioManager.FmBandConfig extends android.hardware.radio.RadioManager.BandConfig {
method public boolean getAf();
+ method public boolean getEa();
method public boolean getRds();
method public boolean getStereo();
method public boolean getTa();
@@ -14892,6 +14941,7 @@
ctor public RadioManager.FmBandConfig.Builder(android.hardware.radio.RadioManager.FmBandConfig);
method public android.hardware.radio.RadioManager.FmBandConfig build();
method public android.hardware.radio.RadioManager.FmBandConfig.Builder setAf(boolean);
+ method public android.hardware.radio.RadioManager.FmBandConfig.Builder setEa(boolean);
method public android.hardware.radio.RadioManager.FmBandConfig.Builder setRds(boolean);
method public android.hardware.radio.RadioManager.FmBandConfig.Builder setStereo(boolean);
method public android.hardware.radio.RadioManager.FmBandConfig.Builder setTa(boolean);
@@ -14899,6 +14949,7 @@
public static class RadioManager.FmBandDescriptor extends android.hardware.radio.RadioManager.BandDescriptor {
method public boolean isAfSupported();
+ method public boolean isEaSupported();
method public boolean isRdsSupported();
method public boolean isStereoSupported();
method public boolean isTaSupported();
@@ -14938,6 +14989,7 @@
method public boolean containsKey(java.lang.String);
method public int describeContents();
method public android.graphics.Bitmap getBitmap(java.lang.String);
+ method public android.hardware.radio.RadioMetadata.Clock getClock(java.lang.String);
method public int getInt(java.lang.String);
method public java.lang.String getString(java.lang.String);
method public java.util.Set<java.lang.String> keySet();
@@ -14947,6 +14999,7 @@
field public static final java.lang.String METADATA_KEY_ALBUM = "android.hardware.radio.metadata.ALBUM";
field public static final java.lang.String METADATA_KEY_ART = "android.hardware.radio.metadata.ART";
field public static final java.lang.String METADATA_KEY_ARTIST = "android.hardware.radio.metadata.ARTIST";
+ field public static final java.lang.String METADATA_KEY_CLOCK = "android.hardware.radio.metadata.CLOCK";
field public static final java.lang.String METADATA_KEY_GENRE = "android.hardware.radio.metadata.GENRE";
field public static final java.lang.String METADATA_KEY_ICON = "android.hardware.radio.metadata.ICON";
field public static final java.lang.String METADATA_KEY_RBDS_PTY = "android.hardware.radio.metadata.RBDS_PTY";
@@ -14962,10 +15015,20 @@
ctor public RadioMetadata.Builder(android.hardware.radio.RadioMetadata);
method public android.hardware.radio.RadioMetadata build();
method public android.hardware.radio.RadioMetadata.Builder putBitmap(java.lang.String, android.graphics.Bitmap);
+ method public android.hardware.radio.RadioMetadata.Builder putClock(java.lang.String, long, int);
method public android.hardware.radio.RadioMetadata.Builder putInt(java.lang.String, int);
method public android.hardware.radio.RadioMetadata.Builder putString(java.lang.String, java.lang.String);
}
+ public static final class RadioMetadata.Clock implements android.os.Parcelable {
+ ctor public RadioMetadata.Clock(long, int);
+ method public int describeContents();
+ method public int getTimezoneOffsetMinutes();
+ method public long getUtcEpochSeconds();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.hardware.radio.RadioMetadata.Clock> CREATOR;
+ }
+
public abstract class RadioTuner {
ctor public RadioTuner();
method public abstract int cancel();
@@ -14994,6 +15057,7 @@
method public void onAntennaState(boolean);
method public void onConfigurationChanged(android.hardware.radio.RadioManager.BandConfig);
method public void onControlChanged(boolean);
+ method public void onEmergencyAnnouncement(boolean);
method public void onError(int);
method public void onMetadataChanged(android.hardware.radio.RadioMetadata);
method public void onProgramInfoChanged(android.hardware.radio.RadioManager.ProgramInfo);
@@ -16734,6 +16798,7 @@
field public static final int BUFFER_FLAG_KEY_FRAME = 1; // 0x1
field public static final deprecated int BUFFER_FLAG_SYNC_FRAME = 1; // 0x1
field public static final int CONFIGURE_FLAG_ENCODE = 1; // 0x1
+ field public static final int CRYPTO_MODE_AES_CBC = 2; // 0x2
field public static final int CRYPTO_MODE_AES_CTR = 1; // 0x1
field public static final int CRYPTO_MODE_UNENCRYPTED = 0; // 0x0
field public static final deprecated int INFO_OUTPUT_BUFFERS_CHANGED = -3; // 0xfffffffd
@@ -16785,6 +16850,7 @@
public static final class MediaCodec.CryptoInfo {
ctor public MediaCodec.CryptoInfo();
method public void set(int, int[], int[], byte[], byte[], int);
+ method public void setPattern(android.media.MediaCodec.CryptoInfo.Pattern);
field public byte[] iv;
field public byte[] key;
field public int mode;
@@ -16793,6 +16859,13 @@
field public int numSubSamples;
}
+ public static final class MediaCodec.CryptoInfo.Pattern {
+ ctor public MediaCodec.CryptoInfo.Pattern(int, int);
+ method public int getEncryptBlocks();
+ method public int getSkipBlocks();
+ method public void set(int, int);
+ }
+
public static abstract interface MediaCodec.OnFrameRenderedListener {
method public abstract void onFrameRendered(android.media.MediaCodec, long, long);
}
@@ -19685,6 +19758,24 @@
public class MtpEvent {
ctor public MtpEvent();
method public int getEventCode();
+ field public static final int EVENT_CANCEL_TRANSACTION = 16385; // 0x4001
+ field public static final int EVENT_CAPTURE_COMPLETE = 16397; // 0x400d
+ field public static final int EVENT_DEVICE_INFO_CHANGED = 16392; // 0x4008
+ field public static final int EVENT_DEVICE_PROP_CHANGED = 16390; // 0x4006
+ field public static final int EVENT_DEVICE_RESET = 16395; // 0x400b
+ field public static final int EVENT_OBJECT_ADDED = 16386; // 0x4002
+ field public static final int EVENT_OBJECT_INFO_CHANGED = 16391; // 0x4007
+ field public static final int EVENT_OBJECT_PROP_CHANGED = 51201; // 0xc801
+ field public static final int EVENT_OBJECT_PROP_DESC_CHANGED = 51202; // 0xc802
+ field public static final int EVENT_OBJECT_REFERENCES_CHANGED = 51203; // 0xc803
+ field public static final int EVENT_OBJECT_REMOVED = 16387; // 0x4003
+ field public static final int EVENT_REQUEST_OBJECT_TRANSFER = 16393; // 0x4009
+ field public static final int EVENT_STORAGE_INFO_CHANGED = 16396; // 0x400c
+ field public static final int EVENT_STORE_ADDED = 16388; // 0x4004
+ field public static final int EVENT_STORE_FULL = 16394; // 0x400a
+ field public static final int EVENT_STORE_REMOVED = 16389; // 0x4005
+ field public static final int EVENT_UNDEFINED = 16384; // 0x4000
+ field public static final int EVENT_UNREPORTED_STATUS = 16398; // 0x400e
}
public final class MtpObjectInfo {
@@ -20217,6 +20308,7 @@
method public static void incrementOperationCount(int, int);
method public static void setThreadStatsTag(int);
method public static void setThreadStatsTagBackup();
+ method public static void setThreadStatsTagRestore();
method public static void setThreadStatsUid(int);
method public static void tagSocket(java.net.Socket) throws java.net.SocketException;
method public static void untagSocket(java.net.Socket) throws java.net.SocketException;
@@ -20468,6 +20560,7 @@
public class X509TrustManagerExtensions {
ctor public X509TrustManagerExtensions(javax.net.ssl.X509TrustManager) throws java.lang.IllegalArgumentException;
method public java.util.List<java.security.cert.X509Certificate> checkServerTrusted(java.security.cert.X509Certificate[], java.lang.String, java.lang.String) throws java.security.cert.CertificateException;
+ method public boolean isSameTrustConfiguration(java.lang.String, java.lang.String);
method public boolean isUserAddedCertificate(java.security.cert.X509Certificate);
}
@@ -20768,7 +20861,7 @@
package android.net.wifi {
- public class BatchedScanResult implements android.os.Parcelable {
+ public deprecated class BatchedScanResult implements android.os.Parcelable {
ctor public BatchedScanResult();
ctor public BatchedScanResult(android.net.wifi.BatchedScanResult);
method public int describeContents();
@@ -21179,7 +21272,7 @@
method public boolean disableNetwork(int);
method public boolean disconnect();
method public boolean enableNetwork(int, boolean);
- method public java.util.List<android.net.wifi.BatchedScanResult> getBatchedScanResults();
+ method public deprecated java.util.List<android.net.wifi.BatchedScanResult> getBatchedScanResults();
method public java.util.List<android.net.wifi.WifiConfiguration> getConfiguredNetworks();
method public android.net.wifi.WifiInfo getConnectionInfo();
method public android.net.wifi.WifiConnectionStatistics getConnectionStatistics();
@@ -21189,7 +21282,7 @@
method public java.util.List<android.net.wifi.ScanResult> getScanResults();
method public int getWifiState();
method public boolean is5GHzBandSupported();
- method public boolean isBatchedScanSupported();
+ method public deprecated boolean isBatchedScanSupported();
method public boolean isDeviceToApRttSupported();
method public boolean isDeviceToDeviceRttSupported();
method public boolean isEnhancedPowerReportingSupported();
@@ -21209,7 +21302,7 @@
method public void setTdlsEnabled(java.net.InetAddress, boolean);
method public void setTdlsEnabledWithMacAddress(java.lang.String, boolean);
method public boolean setWifiEnabled(boolean);
- method public boolean startLocationRestrictedScan(android.os.WorkSource);
+ method public deprecated boolean startLocationRestrictedScan(android.os.WorkSource);
method public boolean startScan();
method public boolean startScan(android.os.WorkSource);
method public void startWps(android.net.wifi.WpsInfo, android.net.wifi.WifiManager.WpsCallback);
@@ -24061,6 +24154,7 @@
field public static final int GL_ACTIVE_PROGRAM = 33369; // 0x8259
field public static final int GL_ACTIVE_RESOURCES = 37621; // 0x92f5
field public static final int GL_ACTIVE_VARIABLES = 37637; // 0x9305
+ field public static final int GL_ALL_BARRIER_BITS = -1; // 0xffffffff
field public static final int GL_ALL_SHADER_BITS = -1; // 0xffffffff
field public static final int GL_ARRAY_SIZE = 37627; // 0x92fb
field public static final int GL_ARRAY_STRIDE = 37630; // 0x92fe
@@ -24084,6 +24178,7 @@
field public static final int GL_DISPATCH_INDIRECT_BUFFER_BINDING = 37103; // 0x90ef
field public static final int GL_DRAW_INDIRECT_BUFFER = 36671; // 0x8f3f
field public static final int GL_DRAW_INDIRECT_BUFFER_BINDING = 36675; // 0x8f43
+ field public static final int GL_ELEMENT_ARRAY_BARRIER_BIT = 2; // 0x2
field public static final int GL_FRAGMENT_SHADER_BIT = 2; // 0x2
field public static final int GL_FRAMEBUFFER_BARRIER_BIT = 1024; // 0x400
field public static final int GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS = 37652; // 0x9314
@@ -24173,6 +24268,7 @@
field public static final int GL_SAMPLE_MASK = 36433; // 0x8e51
field public static final int GL_SAMPLE_MASK_VALUE = 36434; // 0x8e52
field public static final int GL_SAMPLE_POSITION = 36432; // 0x8e50
+ field public static final int GL_SHADER_IMAGE_ACCESS_BARRIER_BIT = 32; // 0x20
field public static final int GL_SHADER_STORAGE_BARRIER_BIT = 8192; // 0x2000
field public static final int GL_SHADER_STORAGE_BLOCK = 37606; // 0x92e6
field public static final int GL_SHADER_STORAGE_BUFFER = 37074; // 0x90d2
@@ -24218,6 +24314,7 @@
field public static final int GL_UNSIGNED_INT_IMAGE_3D = 36964; // 0x9064
field public static final int GL_UNSIGNED_INT_IMAGE_CUBE = 36966; // 0x9066
field public static final int GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE = 37130; // 0x910a
+ field public static final int GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT = 1; // 0x1
field public static final int GL_VERTEX_ATTRIB_BINDING = 33492; // 0x82d4
field public static final int GL_VERTEX_ATTRIB_RELATIVE_OFFSET = 33493; // 0x82d5
field public static final int GL_VERTEX_BINDING_BUFFER = 36687; // 0x8f4f
@@ -25721,7 +25818,8 @@
ctor public UserHandle(android.os.Parcel);
method public int describeContents();
method public int getIdentifier();
- method public boolean isOwner();
+ method public deprecated boolean isOwner();
+ method public boolean isSystem();
method public static int myUserId();
method public static android.os.UserHandle readFromParcel(android.os.Parcel);
method public void writeToParcel(android.os.Parcel, int);
@@ -26956,6 +27054,7 @@
field public static final int OUTGOING_TYPE = 2; // 0x2
field public static final java.lang.String PHONE_ACCOUNT_COMPONENT_NAME = "subscription_component_name";
field public static final java.lang.String PHONE_ACCOUNT_ID = "subscription_id";
+ field public static final java.lang.String POST_DIAL_DIGITS = "post_dial_digits";
field public static final int PRESENTATION_ALLOWED = 1; // 0x1
field public static final int PRESENTATION_PAYPHONE = 4; // 0x4
field public static final int PRESENTATION_RESTRICTED = 2; // 0x2
@@ -28145,6 +28244,7 @@
field public static final int FLAG_SUPPORTS_MOVE = 256; // 0x100
field public static final int FLAG_SUPPORTS_RENAME = 64; // 0x40
field public static final int FLAG_SUPPORTS_THUMBNAIL = 1; // 0x1
+ field public static final int FLAG_SUPPORTS_TYPED_DOCUMENT = 512; // 0x200
field public static final int FLAG_SUPPORTS_WRITE = 2; // 0x2
field public static final java.lang.String MIME_TYPE_DIR = "vnd.android.document/directory";
}
@@ -28182,6 +28282,7 @@
method public final android.os.ParcelFileDescriptor openFile(android.net.Uri, java.lang.String, android.os.CancellationSignal) throws java.io.FileNotFoundException;
method public final android.content.res.AssetFileDescriptor openTypedAssetFile(android.net.Uri, java.lang.String, android.os.Bundle) throws java.io.FileNotFoundException;
method public final android.content.res.AssetFileDescriptor openTypedAssetFile(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException;
+ method public android.content.res.AssetFileDescriptor openTypedDocument(java.lang.String, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException;
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
method public abstract android.database.Cursor queryChildDocuments(java.lang.String, java.lang.String[], java.lang.String) throws java.io.FileNotFoundException;
method public abstract android.database.Cursor queryDocument(java.lang.String, java.lang.String[]) throws java.io.FileNotFoundException;
@@ -28811,6 +28912,7 @@
field public static final deprecated java.lang.String DEVICE_PROVISIONED = "device_provisioned";
field public static final java.lang.String ENABLED_ACCESSIBILITY_SERVICES = "enabled_accessibility_services";
field public static final java.lang.String ENABLED_INPUT_METHODS = "enabled_input_methods";
+ field public static final java.lang.String ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES = "enabled_notification_policy_access_packages";
field public static final deprecated java.lang.String HTTP_PROXY = "http_proxy";
field public static final java.lang.String INPUT_METHOD_SELECTOR_VISIBILITY = "input_method_selector_visibility";
field public static final java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
@@ -32697,6 +32799,8 @@
field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4
field public static final int CAPABILITY_VIDEO_CALLING = 8; // 0x8
field public static final android.os.Parcelable.Creator<android.telecom.PhoneAccount> CREATOR;
+ field public static final java.lang.String EXTRA_CALL_SUBJECT_CHARACTER_ENCODING = "android.telecom.extra.CALL_SUBJECT_CHARACTER_ENCODING";
+ field public static final java.lang.String EXTRA_CALL_SUBJECT_MAX_LENGTH = "android.telecom.extra.CALL_SUBJECT_MAX_LENGTH";
field public static final int NO_HIGHLIGHT_COLOR = 0; // 0x0
field public static final int NO_RESOURCE_ID = -1; // 0xffffffff
field public static final java.lang.String SCHEME_SIP = "sip";
@@ -34152,7 +34256,9 @@
method public java.lang.ClassLoader getClassLoader();
method public java.io.File getCodeCacheDir();
method public android.content.ContentResolver getContentResolver();
+ method public java.io.File getCredentialEncryptedFilesDir();
method public java.io.File getDatabasePath(java.lang.String);
+ method public java.io.File getDeviceEncryptedFilesDir();
method public java.io.File getDir(java.lang.String, int);
method public java.io.File getExternalCacheDir();
method public java.io.File[] getExternalCacheDirs();
@@ -36502,6 +36608,18 @@
public deprecated class FloatMath {
}
+ public abstract class FloatProperty extends android.util.Property {
+ ctor public FloatProperty(java.lang.String);
+ method public final void set(T, java.lang.Float);
+ method public abstract void setValue(T, float);
+ }
+
+ public abstract class IntProperty extends android.util.Property {
+ ctor public IntProperty(java.lang.String);
+ method public final void set(T, java.lang.Integer);
+ method public abstract void setValue(T, int);
+ }
+
public final class JsonReader implements java.io.Closeable {
ctor public JsonReader(java.io.Reader);
method public void beginArray() throws java.io.IOException;
@@ -37590,8 +37708,10 @@
field public static final int KEYCODE_CLEAR = 28; // 0x1c
field public static final int KEYCODE_COMMA = 55; // 0x37
field public static final int KEYCODE_CONTACTS = 207; // 0xcf
+ field public static final int KEYCODE_COPY = 278; // 0x116
field public static final int KEYCODE_CTRL_LEFT = 113; // 0x71
field public static final int KEYCODE_CTRL_RIGHT = 114; // 0x72
+ field public static final int KEYCODE_CUT = 277; // 0x115
field public static final int KEYCODE_D = 32; // 0x20
field public static final int KEYCODE_DEL = 67; // 0x43
field public static final int KEYCODE_DPAD_CENTER = 23; // 0x17
@@ -37709,6 +37829,7 @@
field public static final int KEYCODE_PAGE_DOWN = 93; // 0x5d
field public static final int KEYCODE_PAGE_UP = 92; // 0x5c
field public static final int KEYCODE_PAIRING = 225; // 0xe1
+ field public static final int KEYCODE_PASTE = 279; // 0x117
field public static final int KEYCODE_PERIOD = 56; // 0x38
field public static final int KEYCODE_PICTSYMBOLS = 94; // 0x5e
field public static final int KEYCODE_PLUS = 81; // 0x51
@@ -38717,6 +38838,7 @@
method public boolean performHapticFeedback(int);
method public boolean performHapticFeedback(int, int);
method public boolean performLongClick();
+ method public boolean performLongClick(float, float);
method public void playSoundEffect(int);
method public boolean post(java.lang.Runnable);
method public boolean postDelayed(java.lang.Runnable, long);
@@ -39694,6 +39816,8 @@
method public abstract void setContentView(int);
method public abstract void setContentView(android.view.View);
method public abstract void setContentView(android.view.View, android.view.ViewGroup.LayoutParams);
+ method public abstract void setDecorView(android.view.View);
+ method public abstract void setDecorView(int);
method protected void setDefaultWindowFormat(int);
method public void setDimAmount(float);
method public void setDisableWallpaperTouchEvents(boolean);
@@ -41515,6 +41639,7 @@
method public abstract android.net.Uri getUrl();
method public abstract boolean hasGesture();
method public abstract boolean isForMainFrame();
+ method public abstract boolean isRedirect();
}
public class WebResourceResponse {
@@ -41554,6 +41679,7 @@
method public abstract java.lang.String getDefaultTextEncodingName();
method public static java.lang.String getDefaultUserAgent(android.content.Context);
method public abstract deprecated android.webkit.WebSettings.ZoomDensity getDefaultZoom();
+ method public abstract int getDisabledActionModeMenuItems();
method public abstract boolean getDisplayZoomControls();
method public abstract boolean getDomStorageEnabled();
method public abstract java.lang.String getFantasyFontFamily();
@@ -41603,6 +41729,7 @@
method public abstract void setDefaultFontSize(int);
method public abstract void setDefaultTextEncodingName(java.lang.String);
method public abstract deprecated void setDefaultZoom(android.webkit.WebSettings.ZoomDensity);
+ method public abstract void setDisabledActionModeMenuItems(int);
method public abstract void setDisplayZoomControls(boolean);
method public abstract void setDomStorageEnabled(boolean);
method public abstract deprecated void setEnableSmoothTransition(boolean);
@@ -41647,6 +41774,10 @@
field public static final int LOAD_DEFAULT = -1; // 0xffffffff
field public static final deprecated int LOAD_NORMAL = 0; // 0x0
field public static final int LOAD_NO_CACHE = 2; // 0x2
+ field public static final int MENU_ITEM_NONE = 0; // 0x0
+ field public static final int MENU_ITEM_PROCESS_TEXT = 4; // 0x4
+ field public static final int MENU_ITEM_SHARE = 1; // 0x1
+ field public static final int MENU_ITEM_WEB_SEARCH = 2; // 0x2
field public static final int MIXED_CONTENT_ALWAYS_ALLOW = 0; // 0x0
field public static final int MIXED_CONTENT_COMPATIBILITY_MODE = 2; // 0x2
field public static final int MIXED_CONTENT_NEVER_ALLOW = 1; // 0x1
@@ -41918,7 +42049,8 @@
method public deprecated android.webkit.WebResourceResponse shouldInterceptRequest(android.webkit.WebView, java.lang.String);
method public android.webkit.WebResourceResponse shouldInterceptRequest(android.webkit.WebView, android.webkit.WebResourceRequest);
method public boolean shouldOverrideKeyEvent(android.webkit.WebView, android.view.KeyEvent);
- method public boolean shouldOverrideUrlLoading(android.webkit.WebView, java.lang.String);
+ method public deprecated boolean shouldOverrideUrlLoading(android.webkit.WebView, java.lang.String);
+ method public boolean shouldOverrideUrlLoading(android.webkit.WebView, android.webkit.WebResourceRequest);
field public static final int ERROR_AUTHENTICATION = -4; // 0xfffffffc
field public static final int ERROR_BAD_URL = -12; // 0xfffffff4
field public static final int ERROR_CONNECT = -6; // 0xfffffffa
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 81da6af..7f33cb5 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -18,7 +18,7 @@
package com.android.commands.am;
-import static android.app.ActivityManager.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.RESIZE_MODE_SYSTEM;
import static android.app.ActivityManager.RESIZE_MODE_USER;
@@ -120,7 +120,7 @@
out.println(
"usage: am [subcommand] [options]\n" +
"usage: am start [-D] [-W] [-P <FILE>] [--start-profiler <FILE>]\n" +
- " [--sampling INTERVAL] [-R COUNT] [-S] [--opengl-trace]\n" +
+ " [--sampling INTERVAL] [-R COUNT] [-S]\n" +
" [--track-allocation] [--user <USER_ID> | current] <INTENT>\n" +
" am startservice [--user <USER_ID> | current] <INTENT>\n" +
" am stopservice [--user <USER_ID> | current] <INTENT>\n" +
@@ -154,7 +154,7 @@
" am stack movetask <TASK_ID> <STACK_ID> [true|false]\n" +
" am stack resize <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>\n" +
" am stack size-docked-stack-test: <STEP_SIZE> <l|t|r|b> [DELAY_MS]\n" +
- " am stack split <STACK_ID> <v|h> [INTENT]\n" +
+ " am stack move-top-activity-to-pinned-stack: <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>\n" +
" am stack positiontask <TASK_ID> <STACK_ID> <POSITION>\n" +
" am stack list\n" +
" am stack info <STACK_ID>\n" +
@@ -182,7 +182,6 @@
" -R: repeat the activity launch <COUNT> times. Prior to each repeat,\n" +
" the top activity will be finished.\n" +
" -S: force stop the target app before starting the activity\n" +
- " --opengl-trace: enable tracing of OpenGL functions\n" +
" --track-allocation: enable tracking of object allocations\n" +
" --user <USER_ID> | current: Specify which user to run as; if not\n" +
" specified then run as the current user.\n" +
@@ -298,11 +297,9 @@
" <STEP_SIZE> increments from the side <l>eft, <t>op, <r>ight, or <b>ottom\n" +
" applying the optional [DELAY_MS] between each step.\n" +
"\n" +
- "am stack split: split <STACK_ID> into 2 stacks <v>ertically or <h>orizontally\n" +
- " starting the new stack with [INTENT] if specified. If [INTENT] isn't\n" +
- " specified and the current stack has more than one task, then the top task\n" +
- " of the current task will be moved to the new stack. Command will also force\n" +
- " all current tasks in both stacks to be resizeable.\n" +
+ "am stack move-top-activity-to-pinned-stack: moves the top activity from\n" +
+ " <STACK_ID> to the pinned stack using <LEFT,TOP,RIGHT,BOTTOM> for the\n" +
+ " bounds of the pinned stack.\n" +
"\n" +
"am stack positiontask: place <TASK_ID> in <STACK_ID> at <POSITION>" +
"\n" +
@@ -739,8 +736,6 @@
mRepeat = Integer.parseInt(nextArgRequired());
} else if (opt.equals("-S")) {
mStopOption = true;
- } else if (opt.equals("--opengl-trace")) {
- mStartFlags |= ActivityManager.START_FLAG_OPENGL_TRACES;
} else if (opt.equals("--track-allocation")) {
mStartFlags |= ActivityManager.START_FLAG_TRACK_ALLOCATION;
} else if (opt.equals("--user")) {
@@ -1957,25 +1952,34 @@
private void runStack() throws Exception {
String op = nextArgRequired();
- if (op.equals("start")) {
- runStackStart();
- } else if (op.equals("movetask")) {
- runStackMoveTask();
- } else if (op.equals("resize")) {
- runStackResize();
- } else if (op.equals("positiontask")) {
- runStackPositionTask();
- } else if (op.equals("list")) {
- runStackList();
- } else if (op.equals("info")) {
- runStackInfo();
- } else if (op.equals("split")) {
- runStackSplit();
- } else if (op.equals("size-docked-stack-test")) {
- runStackSizeDockedStackTest();
- } else {
- showError("Error: unknown command '" + op + "'");
- return;
+ switch (op) {
+ case "start":
+ runStackStart();
+ break;
+ case "movetask":
+ runStackMoveTask();
+ break;
+ case "resize":
+ runStackResize();
+ break;
+ case "positiontask":
+ runStackPositionTask();
+ break;
+ case "list":
+ runStackList();
+ break;
+ case "info":
+ runStackInfo();
+ break;
+ case "move-top-activity-to-pinned-stack":
+ runMoveTopActivityToPinnedStack();
+ break;
+ case "size-docked-stack-test":
+ runStackSizeDockedStackTest();
+ break;
+ default:
+ showError("Error: unknown command '" + op + "'");
+ break;
}
}
@@ -2075,61 +2079,21 @@
}
}
- private void runStackSplit() throws Exception {
- final int stackId = Integer.valueOf(nextArgRequired());
- final String splitDirection = nextArgRequired();
- Intent intent = null;
- try {
- intent = makeIntent(UserHandle.USER_CURRENT);
- } catch (IllegalArgumentException e) {
- // no intent supplied.
+ private void runMoveTopActivityToPinnedStack() throws Exception {
+ int stackId = Integer.valueOf(nextArgRequired());
+ final Rect bounds = getBounds();
+ if (bounds == null) {
+ System.err.println("Error: invalid input bounds");
+ return;
}
try {
- final StackInfo currentStackInfo = mAm.getStackInfo(stackId);
- // Calculate bounds for new and current stack.
- final Rect currentStackBounds = new Rect(currentStackInfo.bounds);
- final Rect newStackBounds = new Rect(currentStackInfo.bounds);
- if ("v".equals(splitDirection)) {
- currentStackBounds.right = newStackBounds.left = currentStackInfo.bounds.centerX();
- } else if ("h".equals(splitDirection)) {
- currentStackBounds.bottom = newStackBounds.top = currentStackInfo.bounds.centerY();
- } else {
- showError("Error: unknown split direction '" + splitDirection + "'");
- return;
+ if (!mAm.moveTopActivityToPinnedStack(stackId, bounds)) {
+ showError("Didn't move top activity to pinned stack.");
}
-
- // Create new stack
- IActivityContainer container = mAm.createStackOnDisplay(currentStackInfo.displayId);
- if (container == null) {
- showError("Error: Unable to create new stack...");
- }
-
- final int newStackId = container.getStackId();
-
- if (intent != null) {
- container.startActivity(intent);
- } else if (currentStackInfo.taskIds != null && currentStackInfo.taskIds.length > 1) {
- // Move top task over to new stack
- mAm.moveTaskToStack(currentStackInfo.taskIds[currentStackInfo.taskIds.length - 1],
- newStackId, true);
- }
-
- final StackInfo newStackInfo = mAm.getStackInfo(newStackId);
-
- // Make all tasks in the stacks resizeable.
- for (int taskId : currentStackInfo.taskIds) {
- mAm.setTaskResizeable(taskId, true);
- }
-
- for (int taskId : newStackInfo.taskIds) {
- mAm.setTaskResizeable(taskId, true);
- }
-
- // Resize stacks
- mAm.resizeStack(currentStackInfo.stackId, currentStackBounds, false);
- mAm.resizeStack(newStackInfo.stackId, newStackBounds, false);
} catch (RemoteException e) {
+ showError("Unable to move top activity: " + e);
+ return;
}
}
diff --git a/cmds/idmap/create.cpp b/cmds/idmap/create.cpp
index 9d60ee1..c13d318 100644
--- a/cmds/idmap/create.cpp
+++ b/cmds/idmap/create.cpp
@@ -1,6 +1,6 @@
#include "idmap.h"
-#include <UniquePtr.h>
+#include <memory>
#include <androidfw/AssetManager.h>
#include <androidfw/ResourceTypes.h>
#include <androidfw/ZipFileRO.h>
@@ -15,7 +15,7 @@
namespace {
int get_zip_entry_crc(const char *zip_path, const char *entry_name, uint32_t *crc)
{
- UniquePtr<ZipFileRO> zip(ZipFileRO::open(zip_path));
+ std::unique_ptr<ZipFileRO> zip(ZipFileRO::open(zip_path));
if (zip.get() == NULL) {
return -1;
}
@@ -57,7 +57,7 @@
int write_idmap(int fd, const uint32_t *data, size_t size)
{
- if (lseek(fd, SEEK_SET, 0) < 0) {
+ if (lseek(fd, 0, SEEK_SET) < 0) {
return -1;
}
size_t bytesLeft = size;
@@ -86,7 +86,7 @@
char buf[N];
size_t bytesLeft = N;
- if (lseek(idmap_fd, SEEK_SET, 0) < 0) {
+ if (lseek(idmap_fd, 0, SEEK_SET) < 0) {
return true;
}
for (;;) {
diff --git a/cmds/idmap/scan.cpp b/cmds/idmap/scan.cpp
index f1b2f9e..6d30f0d 100644
--- a/cmds/idmap/scan.cpp
+++ b/cmds/idmap/scan.cpp
@@ -4,7 +4,7 @@
#include "idmap.h"
-#include <UniquePtr.h>
+#include <memory>
#include <androidfw/ResourceTypes.h>
#include <androidfw/StreamingZipInflater.h>
#include <androidfw/ZipFileRO.h>
@@ -120,7 +120,7 @@
int parse_apk(const char *path, const char *target_package_name)
{
- UniquePtr<ZipFileRO> zip(ZipFileRO::open(path));
+ std::unique_ptr<ZipFileRO> zip(ZipFileRO::open(path));
if (zip.get() == NULL) {
ALOGW("%s: failed to open zip %s\n", __FUNCTION__, path);
return -1;
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 393956f..659dc73 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -25,7 +25,6 @@
import android.accounts.IAccountManager;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
-import android.app.IActivityManager;
import android.app.PackageInstallObserver;
import android.content.ComponentName;
import android.content.Context;
@@ -34,30 +33,24 @@
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
-import android.content.pm.FeatureInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageInstaller;
import android.content.pm.IPackageManager;
-import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
import android.content.pm.PackageInstaller.SessionParams;
-import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
-import android.content.pm.ParceledListSlice;
-import android.content.pm.PermissionGroupInfo;
-import android.content.pm.PermissionInfo;
import android.content.pm.UserInfo;
-import android.content.pm.VerificationParams;
-import android.content.res.AssetManager;
-import android.content.res.Resources;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IUserManager;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -66,25 +59,18 @@
import android.text.format.DateUtils;
import android.util.Log;
-import libcore.io.IoUtils;
-
import com.android.internal.content.PackageHelper;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.SizedInputStream;
+import libcore.io.IoUtils;
+
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.WeakHashMap;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
@@ -96,9 +82,6 @@
IUserManager mUm;
IAccountManager mAm;
- private WeakHashMap<String, Resources> mResourceCache
- = new WeakHashMap<String, Resources>();
-
private String[] mArgs;
private int mNextArg;
private String mCurArgData;
@@ -120,7 +103,7 @@
System.exit(exitCode);
}
- public int run(String[] args) throws IOException, RemoteException {
+ public int run(String[] args) throws RemoteException {
boolean validCommand = false;
if (args.length < 1) {
return showUsage();
@@ -156,19 +139,19 @@
}
if ("install-create".equals(op)) {
- return runInstallCreate();
+ return runInstallSession();
}
if ("install-write".equals(op)) {
- return runInstallWrite();
+ return runInstallSession();
}
if ("install-commit".equals(op)) {
- return runInstallCommit();
+ return runInstallSession();
}
if ("install-abandon".equals(op) || "install-destroy".equals(op)) {
- return runInstallAbandon();
+ return runInstallSession();
}
if ("set-installer".equals(op)) {
@@ -275,15 +258,15 @@
if (args.length == 1) {
if (args[0].equalsIgnoreCase("-l")) {
validCommand = true;
- return runListPackages(false);
- } else if (args[0].equalsIgnoreCase("-lf")){
+ return runShellCommand("package", new String[] { "list", "package" });
+ } else if (args[0].equalsIgnoreCase("-lf")) {
validCommand = true;
- return runListPackages(true);
+ return runShellCommand("package", new String[] { "list", "package", "-f" });
}
} else if (args.length == 2) {
if (args[0].equalsIgnoreCase("-p")) {
validCommand = true;
- return displayPackageFilePath(args[1]);
+ return displayPackageFilePath(args[1], UserHandle.USER_SYSTEM);
}
}
return 1;
@@ -297,6 +280,226 @@
}
}
+ private int runShellCommand(String serviceName, String[] args) {
+ final HandlerThread handlerThread = new HandlerThread("results");
+ handlerThread.start();
+ try {
+ ServiceManager.getService(serviceName).shellCommand(
+ FileDescriptor.in, FileDescriptor.out, FileDescriptor.err,
+ args, new ResultReceiver(new Handler(handlerThread.getLooper())));
+ return 0;
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ } finally {
+ handlerThread.quitSafely();
+ }
+ return -1;
+ }
+
+ private static class LocalIntentReceiver {
+ private final SynchronousQueue<Intent> mResult = new SynchronousQueue<>();
+
+ private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
+ @Override
+ public int send(int code, Intent intent, String resolvedType,
+ IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
+ try {
+ mResult.offer(intent, 5, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ return 0;
+ }
+ };
+
+ public IntentSender getIntentSender() {
+ return new IntentSender((IIntentSender) mLocalSender);
+ }
+
+ public Intent getResult() {
+ try {
+ return mResult.take();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ private int translateUserId(int userId, String logContext) {
+ return ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, true, true, logContext, "pm command");
+ }
+
+ private static String checkAbiArgument(String abi) {
+ if (TextUtils.isEmpty(abi)) {
+ throw new IllegalArgumentException("Missing ABI argument");
+ }
+ if ("-".equals(abi)) {
+ return abi;
+ }
+ final String[] supportedAbis = Build.SUPPORTED_ABIS;
+ for (String supportedAbi : supportedAbis) {
+ if (supportedAbi.equals(abi)) {
+ return abi;
+ }
+ }
+ throw new IllegalArgumentException("ABI " + abi + " not supported on this device");
+ }
+
+ private void writeSessionFile(int sessionId, String path, long sizeBytes) throws RemoteException {
+ final SessionInfo info = mInstaller.getSessionInfo(sessionId);
+
+ PackageInstaller.Session session = null;
+ InputStream in = null;
+ OutputStream out = null;
+ try {
+ session = new PackageInstaller.Session(mInstaller.openSession(sessionId));
+
+ if (path == null) {
+ in = new SizedInputStream(System.in, sizeBytes);
+ } else {
+ in = new FileInputStream(path);
+ }
+ out = session.openWrite("base.apk", 0, sizeBytes);
+
+ int total = 0;
+ byte[] buffer = new byte[65536];
+ int c;
+ while ((c = in.read(buffer)) != -1) {
+ total += c;
+ out.write(buffer, 0, c);
+
+ if (info.sizeBytes > 0) {
+ final float fraction = ((float) c / (float) info.sizeBytes);
+ session.addProgress(fraction);
+ }
+ }
+ session.fsync(out);
+ } catch (IOException ignore) {
+ } finally {
+ IoUtils.closeQuietly(out);
+ IoUtils.closeQuietly(in);
+ IoUtils.closeQuietly(session);
+ }
+ }
+
+ private int commitSessionFile(int sessionId) throws RemoteException {
+ PackageInstaller.Session session = null;
+ try {
+ session = new PackageInstaller.Session(mInstaller.openSession(sessionId));
+
+ final LocalIntentReceiver receiver = new LocalIntentReceiver();
+ session.commit(receiver.getIntentSender());
+
+ final Intent result = receiver.getResult();
+ final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_FAILURE);
+ if (status == PackageInstaller.STATUS_SUCCESS) {
+ System.out.println("Success");
+ } else {
+ System.err.println("Failure ["
+ + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
+ }
+ return status;
+ } finally {
+ IoUtils.closeQuietly(session);
+ }
+ }
+
+ /*
+ * Keep this around to support existing users of the "pm install" command that may not be
+ * able to be updated [or, at least informed the API has changed] such as ddmlib.
+ *
+ * Moving the implementation of "pm install" to "cmd package install" changes the executing
+ * context. Instead of being a stand alone process, "cmd package install" runs in the
+ * system_server process. Due to SELinux rules, system_server cannot access many directories;
+ * one of which being the package install staging directory [/data/local/tmp].
+ *
+ * The use of "adb install" or "cmd package install" over "pm install" is highly encouraged.
+ */
+ private int runInstall() throws RemoteException {
+ int userId = UserHandle.USER_ALL;
+ String installerPackageName = null;
+
+ final SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
+
+ String opt;
+ while ((opt = nextOption()) != null) {
+ if (opt.equals("-l")) {
+ params.installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
+ } else if (opt.equals("-r")) {
+ params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
+ } else if (opt.equals("-i")) {
+ installerPackageName = nextArg();
+ if (installerPackageName == null) {
+ throw new IllegalArgumentException("Missing installer package");
+ }
+ } else if (opt.equals("-t")) {
+ params.installFlags |= PackageManager.INSTALL_ALLOW_TEST;
+ } else if (opt.equals("-s")) {
+ params.installFlags |= PackageManager.INSTALL_EXTERNAL;
+ } else if (opt.equals("-f")) {
+ params.installFlags |= PackageManager.INSTALL_INTERNAL;
+ } else if (opt.equals("-d")) {
+ params.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
+ } else if (opt.equals("-g")) {
+ params.installFlags |= PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS;
+ } else if (opt.equals("--originating-uri")) {
+ params.originatingUri = Uri.parse(nextOptionData());
+ } else if (opt.equals("--referrer")) {
+ params.referrerUri = Uri.parse(nextOptionData());
+ } else if (opt.equals("-p")) {
+ params.mode = SessionParams.MODE_INHERIT_EXISTING;
+ params.appPackageName = nextOptionData();
+ if (params.appPackageName == null) {
+ throw new IllegalArgumentException("Missing inherit package name");
+ }
+ } else if (opt.equals("-S")) {
+ params.setSize(Long.parseLong(nextOptionData()));
+ } else if (opt.equals("--abi")) {
+ params.abiOverride = checkAbiArgument(nextOptionData());
+ } else if (opt.equals("--user")) {
+ userId = Integer.parseInt(nextOptionData());
+ } else if (opt.equals("--install-location")) {
+ params.installLocation = Integer.parseInt(nextOptionData());
+ } else if (opt.equals("--force-uuid")) {
+ params.installFlags |= PackageManager.INSTALL_FORCE_VOLUME_UUID;
+ params.volumeUuid = nextOptionData();
+ if ("internal".equals(params.volumeUuid)) {
+ params.volumeUuid = null;
+ }
+ } else {
+ throw new IllegalArgumentException("Unknown option " + opt);
+ }
+ }
+
+ userId = translateUserId(userId, "runInstallCreate");
+ if (userId == UserHandle.USER_ALL) {
+ userId = UserHandle.USER_SYSTEM;
+ params.installFlags |= PackageManager.INSTALL_ALL_USERS;
+ }
+
+ long sizeBytes = params.sizeBytes;
+ String path = nextArg();
+ if ("-".equals(path)) {
+ path = null;
+ } else if (path != null) {
+ final File file = new File(path);
+ if (file.isFile()) {
+ sizeBytes = file.length();
+ }
+ }
+
+ final int sessionId = mInstaller.createSession(params, installerPackageName, userId);
+
+ writeSessionFile(sessionId, path, sizeBytes);
+ return commitSessionFile(sessionId);
+ }
+
+ private int runInstallSession() {
+ return runShellCommand("package", mArgs);
+ }
+
/**
* Execute the list sub-command.
*
@@ -308,471 +511,36 @@
* pm list instrumentation
*/
private int runList() {
- String type = nextArg();
- if (type == null) {
- System.err.println("Error: didn't specify type of data to list");
- return 1;
+ final String type = nextArg();
+ if ("users".equals(type)) {
+ return runShellCommand("user", new String[] { "list" });
}
- if ("package".equals(type) || "packages".equals(type)) {
- return runListPackages(false);
- } else if ("permission-groups".equals(type)) {
- return runListPermissionGroups();
- } else if ("permissions".equals(type)) {
- return runListPermissions();
- } else if ("features".equals(type)) {
- return runListFeatures();
- } else if ("libraries".equals(type)) {
- return runListLibraries();
- } else if ("instrumentation".equals(type)) {
- return runListInstrumentation();
- } else if ("users".equals(type)) {
- return runListUsers();
- } else {
- System.err.println("Error: unknown list type '" + type + "'");
- return 1;
- }
+ return runShellCommand("package", mArgs);
}
- /**
- * Lists all the installed packages.
- */
- private int runListPackages(boolean showApplicationPackage) {
- int getFlags = 0;
- boolean listDisabled = false, listEnabled = false;
- boolean listSystem = false, listThirdParty = false;
- boolean listInstaller = false;
- int userId = UserHandle.USER_SYSTEM;
- try {
- String opt;
- while ((opt=nextOption()) != null) {
- if (opt.equals("-l")) {
- // old compat
- } else if (opt.equals("-lf")) {
- showApplicationPackage = true;
- } else if (opt.equals("-f")) {
- showApplicationPackage = true;
- } else if (opt.equals("-d")) {
- listDisabled = true;
- } else if (opt.equals("-e")) {
- listEnabled = true;
- } else if (opt.equals("-s")) {
- listSystem = true;
- } else if (opt.equals("-3")) {
- listThirdParty = true;
- } else if (opt.equals("-i")) {
- listInstaller = true;
- } else if (opt.equals("--user")) {
- userId = Integer.parseInt(nextArg());
- } else if (opt.equals("-u")) {
- getFlags |= PackageManager.GET_UNINSTALLED_PACKAGES;
- } else {
- System.err.println("Error: Unknown option: " + opt);
- return 1;
- }
- }
- } catch (RuntimeException ex) {
- System.err.println("Error: " + ex.toString());
- return 1;
- }
-
- String filter = nextArg();
-
- try {
- final List<PackageInfo> packages = getInstalledPackages(mPm, getFlags, userId);
-
- int count = packages.size();
- for (int p = 0 ; p < count ; p++) {
- PackageInfo info = packages.get(p);
- if (filter != null && !info.packageName.contains(filter)) {
- continue;
- }
- final boolean isSystem =
- (info.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0;
- if ((!listDisabled || !info.applicationInfo.enabled) &&
- (!listEnabled || info.applicationInfo.enabled) &&
- (!listSystem || isSystem) &&
- (!listThirdParty || !isSystem)) {
- System.out.print("package:");
- if (showApplicationPackage) {
- System.out.print(info.applicationInfo.sourceDir);
- System.out.print("=");
- }
- System.out.print(info.packageName);
- if (listInstaller) {
- System.out.print(" installer=");
- System.out.print(mPm.getInstallerPackageName(info.packageName));
- }
- System.out.println();
- }
- }
- return 0;
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- return 1;
- }
- }
-
- @SuppressWarnings("unchecked")
- private List<PackageInfo> getInstalledPackages(IPackageManager pm, int flags, int userId)
- throws RemoteException {
- ParceledListSlice<PackageInfo> slice = pm.getInstalledPackages(flags, userId);
- return slice.getList();
- }
-
- /**
- * Lists all of the features supported by the current device.
- *
- * pm list features
- */
- private int runListFeatures() {
- try {
- List<FeatureInfo> list = new ArrayList<FeatureInfo>();
- FeatureInfo[] rawList = mPm.getSystemAvailableFeatures();
- for (int i=0; i<rawList.length; i++) {
- list.add(rawList[i]);
- }
-
-
- // Sort by name
- Collections.sort(list, new Comparator<FeatureInfo>() {
- public int compare(FeatureInfo o1, FeatureInfo o2) {
- if (o1.name == o2.name) return 0;
- if (o1.name == null) return -1;
- if (o2.name == null) return 1;
- return o1.name.compareTo(o2.name);
- }
- });
-
- int count = (list != null) ? list.size() : 0;
- for (int p = 0; p < count; p++) {
- FeatureInfo fi = list.get(p);
- System.out.print("feature:");
- if (fi.name != null) System.out.println(fi.name);
- else System.out.println("reqGlEsVersion=0x"
- + Integer.toHexString(fi.reqGlEsVersion));
- }
- return 0;
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- return 1;
- }
- }
-
- /**
- * Lists all of the libraries supported by the current device.
- *
- * pm list libraries
- */
- private int runListLibraries() {
- try {
- List<String> list = new ArrayList<String>();
- String[] rawList = mPm.getSystemSharedLibraryNames();
- for (int i=0; i<rawList.length; i++) {
- list.add(rawList[i]);
- }
-
-
- // Sort by name
- Collections.sort(list, new Comparator<String>() {
- public int compare(String o1, String o2) {
- if (o1 == o2) return 0;
- if (o1 == null) return -1;
- if (o2 == null) return 1;
- return o1.compareTo(o2);
- }
- });
-
- int count = (list != null) ? list.size() : 0;
- for (int p = 0; p < count; p++) {
- String lib = list.get(p);
- System.out.print("library:");
- System.out.println(lib);
- }
- return 0;
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- return 1;
- }
- }
-
- /**
- * Lists all of the installed instrumentation, or all for a given package
- *
- * pm list instrumentation [package] [-f]
- */
- private int runListInstrumentation() {
- int flags = 0; // flags != 0 is only used to request meta-data
- boolean showPackage = false;
- String targetPackage = null;
-
- try {
- String opt;
- while ((opt=nextArg()) != null) {
- if (opt.equals("-f")) {
- showPackage = true;
- } else if (opt.charAt(0) != '-') {
- targetPackage = opt;
- } else {
- System.err.println("Error: Unknown option: " + opt);
- return 1;
- }
- }
- } catch (RuntimeException ex) {
- System.err.println("Error: " + ex.toString());
- return 1;
- }
-
- try {
- List<InstrumentationInfo> list = mPm.queryInstrumentation(targetPackage, flags);
-
- // Sort by target package
- Collections.sort(list, new Comparator<InstrumentationInfo>() {
- public int compare(InstrumentationInfo o1, InstrumentationInfo o2) {
- return o1.targetPackage.compareTo(o2.targetPackage);
- }
- });
-
- int count = (list != null) ? list.size() : 0;
- for (int p = 0; p < count; p++) {
- InstrumentationInfo ii = list.get(p);
- System.out.print("instrumentation:");
- if (showPackage) {
- System.out.print(ii.sourceDir);
- System.out.print("=");
- }
- ComponentName cn = new ComponentName(ii.packageName, ii.name);
- System.out.print(cn.flattenToShortString());
- System.out.print(" (target=");
- System.out.print(ii.targetPackage);
- System.out.println(")");
- }
- return 0;
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- return 1;
- }
- }
-
- /**
- * Lists all the known permission groups.
- */
- private int runListPermissionGroups() {
- try {
- List<PermissionGroupInfo> pgs = mPm.getAllPermissionGroups(0);
-
- int count = pgs.size();
- for (int p = 0 ; p < count ; p++) {
- PermissionGroupInfo pgi = pgs.get(p);
- System.out.print("permission group:");
- System.out.println(pgi.name);
- }
- return 0;
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- return 1;
- }
- }
-
- private String loadText(PackageItemInfo pii, int res, CharSequence nonLocalized) {
- if (nonLocalized != null) {
- return nonLocalized.toString();
- }
- if (res != 0) {
- Resources r = getResources(pii);
- if (r != null) {
- try {
- return r.getString(res);
- } catch (Resources.NotFoundException e) {
- }
- }
- }
- return null;
- }
-
- /**
- * Lists all the permissions in a group.
- */
- private int runListPermissions() {
- try {
- boolean labels = false;
- boolean groups = false;
- boolean userOnly = false;
- boolean summary = false;
- boolean dangerousOnly = false;
- String opt;
- while ((opt=nextOption()) != null) {
- if (opt.equals("-f")) {
- labels = true;
- } else if (opt.equals("-g")) {
- groups = true;
- } else if (opt.equals("-s")) {
- groups = true;
- labels = true;
- summary = true;
- } else if (opt.equals("-u")) {
- userOnly = true;
- } else if (opt.equals("-d")) {
- dangerousOnly = true;
- } else {
- System.err.println("Error: Unknown option: " + opt);
- return 1;
- }
- }
-
- String grp = nextArg();
- ArrayList<String> groupList = new ArrayList<String>();
- if (groups) {
- List<PermissionGroupInfo> infos =
- mPm.getAllPermissionGroups(0);
- for (int i=0; i<infos.size(); i++) {
- groupList.add(infos.get(i).name);
- }
- groupList.add(null);
- } else {
- groupList.add(grp);
- }
-
- if (dangerousOnly) {
- System.out.println("Dangerous Permissions:");
- System.out.println("");
- doListPermissions(groupList, groups, labels, summary,
- PermissionInfo.PROTECTION_DANGEROUS,
- PermissionInfo.PROTECTION_DANGEROUS);
- if (userOnly) {
- System.out.println("Normal Permissions:");
- System.out.println("");
- doListPermissions(groupList, groups, labels, summary,
- PermissionInfo.PROTECTION_NORMAL,
- PermissionInfo.PROTECTION_NORMAL);
- }
- } else if (userOnly) {
- System.out.println("Dangerous and Normal Permissions:");
- System.out.println("");
- doListPermissions(groupList, groups, labels, summary,
- PermissionInfo.PROTECTION_NORMAL,
- PermissionInfo.PROTECTION_DANGEROUS);
- } else {
- System.out.println("All Permissions:");
- System.out.println("");
- doListPermissions(groupList, groups, labels, summary,
- -10000, 10000);
- }
- return 0;
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- return 1;
- }
- }
-
- private void doListPermissions(ArrayList<String> groupList,
- boolean groups, boolean labels, boolean summary,
- int startProtectionLevel, int endProtectionLevel)
- throws RemoteException {
- for (int i=0; i<groupList.size(); i++) {
- String groupName = groupList.get(i);
- String prefix = "";
- if (groups) {
- if (i > 0) System.out.println("");
- if (groupName != null) {
- PermissionGroupInfo pgi = mPm.getPermissionGroupInfo(
- groupName, 0);
- if (summary) {
- Resources res = getResources(pgi);
- if (res != null) {
- System.out.print(loadText(pgi, pgi.labelRes,
- pgi.nonLocalizedLabel) + ": ");
- } else {
- System.out.print(pgi.name + ": ");
-
- }
- } else {
- System.out.println((labels ? "+ " : "")
- + "group:" + pgi.name);
- if (labels) {
- System.out.println(" package:" + pgi.packageName);
- Resources res = getResources(pgi);
- if (res != null) {
- System.out.println(" label:"
- + loadText(pgi, pgi.labelRes,
- pgi.nonLocalizedLabel));
- System.out.println(" description:"
- + loadText(pgi, pgi.descriptionRes,
- pgi.nonLocalizedDescription));
- }
- }
- }
- } else {
- System.out.println(((labels && !summary)
- ? "+ " : "") + "ungrouped:");
- }
- prefix = " ";
- }
- List<PermissionInfo> ps = mPm.queryPermissionsByGroup(
- groupList.get(i), 0);
- int count = ps.size();
- boolean first = true;
- for (int p = 0 ; p < count ; p++) {
- PermissionInfo pi = ps.get(p);
- if (groups && groupName == null && pi.group != null) {
- continue;
- }
- final int base = pi.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
- if (base < startProtectionLevel
- || base > endProtectionLevel) {
- continue;
- }
- if (summary) {
- if (first) {
- first = false;
- } else {
- System.out.print(", ");
- }
- Resources res = getResources(pi);
- if (res != null) {
- System.out.print(loadText(pi, pi.labelRes,
- pi.nonLocalizedLabel));
- } else {
- System.out.print(pi.name);
- }
- } else {
- System.out.println(prefix + (labels ? "+ " : "")
- + "permission:" + pi.name);
- if (labels) {
- System.out.println(prefix + " package:" + pi.packageName);
- Resources res = getResources(pi);
- if (res != null) {
- System.out.println(prefix + " label:"
- + loadText(pi, pi.labelRes,
- pi.nonLocalizedLabel));
- System.out.println(prefix + " description:"
- + loadText(pi, pi.descriptionRes,
- pi.nonLocalizedDescription));
- }
- System.out.println(prefix + " protectionLevel:"
- + PermissionInfo.protectionToString(pi.protectionLevel));
- }
- }
- }
-
- if (summary) {
- System.out.println("");
- }
- }
+ private int runUninstall() {
+ return runShellCommand("package", mArgs);
}
private int runPath() {
+ int userId = UserHandle.USER_SYSTEM;
+ String option = nextOption();
+ if (option != null && option.equals("--user")) {
+ String optionData = nextOptionData();
+ if (optionData == null || !isNumber(optionData)) {
+ System.err.println("Error: no USER_ID specified");
+ return showUsage();
+ } else {
+ userId = Integer.parseInt(optionData);
+ }
+ }
+
String pkg = nextArg();
if (pkg == null) {
System.err.println("Error: no package specified");
return 1;
}
- return displayPackageFilePath(pkg);
+ return displayPackageFilePath(pkg, userId);
}
private int runDump() {
@@ -807,49 +575,6 @@
}
}
- /**
- * Converts a failure code into a string by using reflection to find a matching constant
- * in PackageManager.
- */
- private String installFailureToString(LocalPackageInstallObserver obs) {
- final int result = obs.result;
- Field[] fields = PackageManager.class.getFields();
- for (Field f: fields) {
- if (f.getType() == int.class) {
- int modifiers = f.getModifiers();
- // only look at public final static fields.
- if (((modifiers & Modifier.FINAL) != 0) &&
- ((modifiers & Modifier.PUBLIC) != 0) &&
- ((modifiers & Modifier.STATIC) != 0)) {
- String fieldName = f.getName();
- if (fieldName.startsWith("INSTALL_FAILED_") ||
- fieldName.startsWith("INSTALL_PARSE_FAILED_")) {
- // get the int value and compare it to result.
- try {
- if (result == f.getInt(null)) {
- StringBuilder sb = new StringBuilder(64);
- sb.append(fieldName);
- if (obs.extraPermission != null) {
- sb.append(" perm=");
- sb.append(obs.extraPermission);
- }
- if (obs.extraPackage != null) {
- sb.append(" pkg=" + obs.extraPackage);
- }
- return sb.toString();
- }
- } catch (IllegalAccessException e) {
- // this shouldn't happen since we only look for public static fields.
- }
- }
- }
- }
- }
-
- // couldn't find a matching constant? return the value
- return Integer.toString(result);
- }
-
// pm set-app-link [--user USER_ID] PACKAGE {always|ask|always-ask|never|undefined}
private int runSetAppLink() {
int userId = UserHandle.USER_SYSTEM;
@@ -864,8 +589,7 @@
}
} else {
System.err.println("Error: unknown option: " + opt);
- showUsage();
- return 1;
+ return showUsage();
}
}
@@ -873,16 +597,14 @@
final String pkg = nextArg();
if (pkg == null) {
System.err.println("Error: no package specified.");
- showUsage();
- return 1;
+ return showUsage();
}
// State to apply; {always|ask|never|undefined}, required
final String modeString = nextArg();
if (modeString == null) {
System.err.println("Error: no app link state specified.");
- showUsage();
- return 1;
+ return showUsage();
}
final int newMode;
@@ -951,8 +673,7 @@
}
} else {
System.err.println("Error: unknown option: " + opt);
- showUsage();
- return 1;
+ return showUsage();
}
}
@@ -960,8 +681,7 @@
final String pkg = nextArg();
if (pkg == null) {
System.err.println("Error: no package specified.");
- showUsage();
- return 1;
+ return showUsage();
}
try {
@@ -1044,316 +764,6 @@
}
}
- private int runInstall() {
- int installFlags = 0;
- int userId = UserHandle.USER_ALL;
- String installerPackageName = null;
-
- String opt;
-
- String originatingUriString = null;
- String referrer = null;
- String abi = null;
-
- while ((opt=nextOption()) != null) {
- if (opt.equals("-l")) {
- installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
- } else if (opt.equals("-r")) {
- installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
- } else if (opt.equals("-i")) {
- installerPackageName = nextOptionData();
- if (installerPackageName == null) {
- System.err.println("Error: no value specified for -i");
- return 1;
- }
- } else if (opt.equals("-t")) {
- installFlags |= PackageManager.INSTALL_ALLOW_TEST;
- } else if (opt.equals("-s")) {
- // Override if -s option is specified.
- installFlags |= PackageManager.INSTALL_EXTERNAL;
- } else if (opt.equals("-f")) {
- // Override if -s option is specified.
- installFlags |= PackageManager.INSTALL_INTERNAL;
- } else if (opt.equals("-d")) {
- installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
- } else if (opt.equals("-g")) {
- installFlags |= PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS;
- } else if (opt.equals("--originating-uri")) {
- originatingUriString = nextOptionData();
- if (originatingUriString == null) {
- System.err.println("Error: must supply argument for --originating-uri");
- return 1;
- }
- } else if (opt.equals("--referrer")) {
- referrer = nextOptionData();
- if (referrer == null) {
- System.err.println("Error: must supply argument for --referrer");
- return 1;
- }
- } else if (opt.equals("--abi")) {
- abi = checkAbiArgument(nextOptionData());
- } else if (opt.equals("--user")) {
- userId = Integer.parseInt(nextOptionData());
- } else {
- System.err.println("Error: Unknown option: " + opt);
- return 1;
- }
- }
-
- userId = translateUserId(userId, "runInstall");
- if (userId == UserHandle.USER_ALL) {
- userId = UserHandle.USER_SYSTEM;
- installFlags |= PackageManager.INSTALL_ALL_USERS;
- }
-
- final Uri verificationURI;
- final Uri originatingURI;
- final Uri referrerURI;
-
- if (originatingUriString != null) {
- originatingURI = Uri.parse(originatingUriString);
- } else {
- originatingURI = null;
- }
-
- if (referrer != null) {
- referrerURI = Uri.parse(referrer);
- } else {
- referrerURI = null;
- }
-
- // Populate apkURI, must be present
- final String apkFilePath = nextArg();
- System.err.println("\tpkg: " + apkFilePath);
- if (apkFilePath == null) {
- System.err.println("Error: no package specified");
- return 1;
- }
-
- // Populate verificationURI, optionally present
- final String verificationFilePath = nextArg();
- if (verificationFilePath != null) {
- System.err.println("\tver: " + verificationFilePath);
- verificationURI = Uri.fromFile(new File(verificationFilePath));
- } else {
- verificationURI = null;
- }
-
- LocalPackageInstallObserver obs = new LocalPackageInstallObserver();
- try {
- VerificationParams verificationParams = new VerificationParams(verificationURI,
- originatingURI, referrerURI, VerificationParams.NO_UID, null);
-
- mPm.installPackageAsUser(apkFilePath, obs.getBinder(), installFlags,
- installerPackageName, verificationParams, abi, userId);
-
- synchronized (obs) {
- while (!obs.finished) {
- try {
- obs.wait();
- } catch (InterruptedException e) {
- }
- }
- if (obs.result == PackageManager.INSTALL_SUCCEEDED) {
- System.out.println("Success");
- return 0;
- } else {
- System.err.println("Failure ["
- + installFailureToString(obs)
- + "]");
- return 1;
- }
- }
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- return 1;
- }
- }
-
- /**
- * @param userId The user id to be translated.
- * @param logContext Optional human readable text to provide some context in error log.
- * @return Translated concrete user id. This will include USER_ALL.
- */
- private int translateUserId(int userId, String logContext) {
- return ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
- userId, true, true, logContext, "pm command");
- }
-
- private int runInstallCreate() throws RemoteException {
- int userId = UserHandle.USER_ALL;
- String installerPackageName = null;
-
- final SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
-
- String opt;
- while ((opt = nextOption()) != null) {
- if (opt.equals("-l")) {
- params.installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
- } else if (opt.equals("-r")) {
- params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
- } else if (opt.equals("-i")) {
- installerPackageName = nextArg();
- if (installerPackageName == null) {
- throw new IllegalArgumentException("Missing installer package");
- }
- } else if (opt.equals("-t")) {
- params.installFlags |= PackageManager.INSTALL_ALLOW_TEST;
- } else if (opt.equals("-s")) {
- params.installFlags |= PackageManager.INSTALL_EXTERNAL;
- } else if (opt.equals("-f")) {
- params.installFlags |= PackageManager.INSTALL_INTERNAL;
- } else if (opt.equals("-d")) {
- params.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
- } else if (opt.equals("-g")) {
- params.installFlags |= PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS;
- } else if (opt.equals("--originating-uri")) {
- params.originatingUri = Uri.parse(nextOptionData());
- } else if (opt.equals("--referrer")) {
- params.referrerUri = Uri.parse(nextOptionData());
- } else if (opt.equals("-p")) {
- params.mode = SessionParams.MODE_INHERIT_EXISTING;
- params.appPackageName = nextOptionData();
- if (params.appPackageName == null) {
- throw new IllegalArgumentException("Missing inherit package name");
- }
- } else if (opt.equals("-S")) {
- params.setSize(Long.parseLong(nextOptionData()));
- } else if (opt.equals("--abi")) {
- params.abiOverride = checkAbiArgument(nextOptionData());
- } else if (opt.equals("--user")) {
- userId = Integer.parseInt(nextOptionData());
- } else if (opt.equals("--install-location")) {
- params.installLocation = Integer.parseInt(nextOptionData());
- } else if (opt.equals("--force-uuid")) {
- params.installFlags |= PackageManager.INSTALL_FORCE_VOLUME_UUID;
- params.volumeUuid = nextOptionData();
- if ("internal".equals(params.volumeUuid)) {
- params.volumeUuid = null;
- }
- } else {
- throw new IllegalArgumentException("Unknown option " + opt);
- }
- }
-
- userId = translateUserId(userId, "runInstallCreate");
- if (userId == UserHandle.USER_ALL) {
- userId = UserHandle.USER_SYSTEM;
- params.installFlags |= PackageManager.INSTALL_ALL_USERS;
- }
-
- final int sessionId = mInstaller.createSession(params, installerPackageName, userId);
-
- // NOTE: adb depends on parsing this string
- System.out.println("Success: created install session [" + sessionId + "]");
- return 0;
- }
-
- private int runInstallWrite() throws IOException, RemoteException {
- long sizeBytes = -1;
-
- String opt;
- while ((opt = nextOption()) != null) {
- if (opt.equals("-S")) {
- sizeBytes = Long.parseLong(nextOptionData());
- } else {
- throw new IllegalArgumentException("Unknown option: " + opt);
- }
- }
-
- final int sessionId = Integer.parseInt(nextArg());
- final String splitName = nextArg();
-
- String path = nextArg();
- if ("-".equals(path)) {
- path = null;
- } else if (path != null) {
- final File file = new File(path);
- if (file.isFile()) {
- sizeBytes = file.length();
- }
- }
-
- final SessionInfo info = mInstaller.getSessionInfo(sessionId);
-
- PackageInstaller.Session session = null;
- InputStream in = null;
- OutputStream out = null;
- try {
- session = new PackageInstaller.Session(mInstaller.openSession(sessionId));
-
- if (path != null) {
- in = new FileInputStream(path);
- } else {
- in = new SizedInputStream(System.in, sizeBytes);
- }
- out = session.openWrite(splitName, 0, sizeBytes);
-
- int total = 0;
- byte[] buffer = new byte[65536];
- int c;
- while ((c = in.read(buffer)) != -1) {
- total += c;
- out.write(buffer, 0, c);
-
- if (info.sizeBytes > 0) {
- final float fraction = ((float) c / (float) info.sizeBytes);
- session.addProgress(fraction);
- }
- }
- session.fsync(out);
-
- System.out.println("Success: streamed " + total + " bytes");
- return 0;
- } finally {
- IoUtils.closeQuietly(out);
- IoUtils.closeQuietly(in);
- IoUtils.closeQuietly(session);
- }
- }
-
- private int runInstallCommit() throws RemoteException {
- final int sessionId = Integer.parseInt(nextArg());
-
- PackageInstaller.Session session = null;
- try {
- session = new PackageInstaller.Session(mInstaller.openSession(sessionId));
-
- final LocalIntentReceiver receiver = new LocalIntentReceiver();
- session.commit(receiver.getIntentSender());
-
- final Intent result = receiver.getResult();
- final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_FAILURE);
- if (status == PackageInstaller.STATUS_SUCCESS) {
- System.out.println("Success");
- return 0;
- } else {
- Log.e(TAG, "Failure details: " + result.getExtras());
- System.err.println("Failure ["
- + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
- return 1;
- }
- } finally {
- IoUtils.closeQuietly(session);
- }
- }
-
- private int runInstallAbandon() throws RemoteException {
- final int sessionId = Integer.parseInt(nextArg());
-
- PackageInstaller.Session session = null;
- try {
- session = new PackageInstaller.Session(mInstaller.openSession(sessionId));
- session.abandon();
- System.out.println("Success");
- return 0;
- } finally {
- IoUtils.closeQuietly(session);
- }
- }
-
private int runSetInstaller() throws RemoteException {
final String targetPackage = nextArg();
final String installerPackageName = nextArg();
@@ -1378,8 +788,7 @@
String optionData = nextOptionData();
if (optionData == null || !isNumber(optionData)) {
System.err.println("Error: no USER_ID specified");
- showUsage();
- return 1;
+ return showUsage();
} else {
userId = Integer.parseInt(optionData);
}
@@ -1389,8 +798,7 @@
flags |= UserInfo.FLAG_RESTRICTED;
} else {
System.err.println("Error: unknown option " + opt);
- showUsage();
- return 1;
+ return showUsage();
}
}
String arg = nextArg();
@@ -1454,29 +862,6 @@
}
}
- public int runListUsers() {
- try {
- IActivityManager am = ActivityManagerNative.getDefault();
-
- List<UserInfo> users = mUm.getUsers(false);
- if (users == null) {
- System.err.println("Error: couldn't get users");
- return 1;
- } else {
- System.out.println("Users:");
- for (int i = 0; i < users.size(); i++) {
- String running = am.isUserRunning(users.get(i).id, false) ? " running" : "";
- System.out.println("\t" + users.get(i).toString() + running);
- }
- return 0;
- }
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- return 1;
- }
- }
-
public int runGetMaxUsers() {
System.out.println("Maximum supported users: " + UserManager.getMaxSupportedUsers());
return 0;
@@ -1547,81 +932,6 @@
}
}
- private int runUninstall() throws RemoteException {
- int flags = 0;
- int userId = UserHandle.USER_ALL;
-
- String opt;
- while ((opt=nextOption()) != null) {
- if (opt.equals("-k")) {
- flags |= PackageManager.DELETE_KEEP_DATA;
- } else if (opt.equals("--user")) {
- String param = nextArg();
- if (isNumber(param)) {
- userId = Integer.parseInt(param);
- } else {
- showUsage();
- System.err.println("Error: Invalid user: " + param);
- return 1;
- }
- } else {
- System.err.println("Error: Unknown option: " + opt);
- return 1;
- }
- }
-
- String pkg = nextArg();
- if (pkg == null) {
- System.err.println("Error: no package specified");
- showUsage();
- return 1;
- }
-
- userId = translateUserId(userId, "runUninstall");
- if (userId == UserHandle.USER_ALL) {
- userId = UserHandle.USER_SYSTEM;
- flags |= PackageManager.DELETE_ALL_USERS;
- } else {
- PackageInfo info;
- try {
- info = mPm.getPackageInfo(pkg, 0, userId);
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- return 1;
- }
- if (info == null) {
- System.err.println("Failure - not installed for " + userId);
- return 1;
- }
- final boolean isSystem =
- (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
- // If we are being asked to delete a system app for just one
- // user set flag so it disables rather than reverting to system
- // version of the app.
- if (isSystem) {
- flags |= PackageManager.DELETE_SYSTEM_APP;
- }
- }
-
- final LocalIntentReceiver receiver = new LocalIntentReceiver();
- mInstaller.uninstall(pkg, null /* callerPackageName */, flags,
- receiver.getIntentSender(), userId);
-
- final Intent result = receiver.getResult();
- final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_FAILURE);
- if (status == PackageInstaller.STATUS_SUCCESS) {
- System.out.println("Success");
- return 0;
- } else {
- Log.e(TAG, "Failure details: " + result.getExtras());
- System.err.println("Failure ["
- + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
- return 1;
- }
- }
-
static class ClearDataObserver extends IPackageDataObserver.Stub {
boolean finished;
boolean result;
@@ -1637,14 +947,13 @@
}
private int runClear() {
- int userId = 0;
+ int userId = UserHandle.USER_SYSTEM;
String option = nextOption();
if (option != null && option.equals("--user")) {
String optionData = nextOptionData();
if (optionData == null || !isNumber(optionData)) {
System.err.println("Error: no USER_ID specified");
- showUsage();
- return 1;
+ return showUsage();
} else {
userId = Integer.parseInt(optionData);
}
@@ -1653,8 +962,7 @@
String pkg = nextArg();
if (pkg == null) {
System.err.println("Error: no package specified");
- showUsage();
- return 1;
+ return showUsage();
}
ClearDataObserver obs = new ClearDataObserver();
@@ -1709,14 +1017,13 @@
}
private int runSetEnabledSetting(int state) {
- int userId = 0;
+ int userId = UserHandle.USER_SYSTEM;
String option = nextOption();
if (option != null && option.equals("--user")) {
String optionData = nextOptionData();
if (optionData == null || !isNumber(optionData)) {
System.err.println("Error: no USER_ID specified");
- showUsage();
- return 1;
+ return showUsage();
} else {
userId = Integer.parseInt(optionData);
}
@@ -1725,8 +1032,7 @@
String pkg = nextArg();
if (pkg == null) {
System.err.println("Error: no package or component specified");
- showUsage();
- return 1;
+ return showUsage();
}
ComponentName cn = ComponentName.unflattenFromString(pkg);
if (cn == null) {
@@ -1758,14 +1064,13 @@
}
private int runSetHiddenSetting(boolean state) {
- int userId = 0;
+ int userId = UserHandle.USER_SYSTEM;
String option = nextOption();
if (option != null && option.equals("--user")) {
String optionData = nextOptionData();
if (optionData == null || !isNumber(optionData)) {
System.err.println("Error: no USER_ID specified");
- showUsage();
- return 1;
+ return showUsage();
} else {
userId = Integer.parseInt(optionData);
}
@@ -1774,8 +1079,7 @@
String pkg = nextArg();
if (pkg == null) {
System.err.println("Error: no package or component specified");
- showUsage();
- return 1;
+ return showUsage();
}
try {
mPm.setApplicationHiddenSettingAsUser(pkg, state, userId);
@@ -1802,14 +1106,12 @@
String pkg = nextArg();
if (pkg == null) {
System.err.println("Error: no package specified");
- showUsage();
- return 1;
+ return showUsage();
}
String perm = nextArg();
if (perm == null) {
System.err.println("Error: no permission specified");
- showUsage();
- return 1;
+ return showUsage();
}
try {
@@ -1825,8 +1127,7 @@
return 1;
} catch (IllegalArgumentException e) {
System.err.println("Bad argument: " + e.toString());
- showUsage();
- return 1;
+ return showUsage();
} catch (SecurityException e) {
System.err.println("Operation not allowed: " + e.toString());
return 1;
@@ -1843,8 +1144,7 @@
return 1;
} catch (IllegalArgumentException e) {
System.err.println("Bad argument: " + e.toString());
- showUsage();
- return 1;
+ return showUsage();
} catch (SecurityException e) {
System.err.println("Operation not allowed: " + e.toString());
return 1;
@@ -1855,14 +1155,12 @@
final String permission = nextArg();
if (permission == null) {
System.err.println("Error: no permission specified");
- showUsage();
- return 1;
+ return showUsage();
}
final String enforcedRaw = nextArg();
if (enforcedRaw == null) {
System.err.println("Error: no enforcement specified");
- showUsage();
- return 1;
+ return showUsage();
}
final boolean enforced = Boolean.parseBoolean(enforcedRaw);
try {
@@ -1874,8 +1172,7 @@
return 1;
} catch (IllegalArgumentException e) {
System.err.println("Bad argument: " + e.toString());
- showUsage();
- return 1;
+ return showUsage();
} catch (SecurityException e) {
System.err.println("Operation not allowed: " + e.toString());
return 1;
@@ -1901,8 +1198,7 @@
String size = nextArg();
if (size == null) {
System.err.println("Error: no size specified");
- showUsage();
- return 1;
+ return showUsage();
}
int len = size.length();
long multiplier = 1;
@@ -1916,8 +1212,7 @@
multiplier = 1024L*1024L*1024L;
} else {
System.err.println("Invalid suffix: " + c);
- showUsage();
- return 1;
+ return showUsage();
}
size = size.substring(0, len-1);
}
@@ -1926,8 +1221,7 @@
sizeVal = Long.parseLong(size) * multiplier;
} catch (NumberFormatException e) {
System.err.println("Error: expected number at: " + size);
- showUsage();
- return 1;
+ return showUsage();
}
String volumeUuid = nextArg();
if ("internal".equals(volumeUuid)) {
@@ -1951,8 +1245,7 @@
return 1;
} catch (IllegalArgumentException e) {
System.err.println("Bad argument: " + e.toString());
- showUsage();
- return 1;
+ return showUsage();
} catch (SecurityException e) {
System.err.println("Operation not allowed: " + e.toString());
return 1;
@@ -1963,9 +1256,9 @@
* Displays the package file for a package.
* @param pckg
*/
- private int displayPackageFilePath(String pckg) {
+ private int displayPackageFilePath(String pckg, int userId) {
try {
- PackageInfo info = mPm.getPackageInfo(pckg, 0, 0);
+ PackageInfo info = mPm.getPackageInfo(pckg, 0, userId);
if (info != null && info.applicationInfo != null) {
System.out.print("package:");
System.out.println(info.applicationInfo.sourceDir);
@@ -1984,72 +1277,6 @@
return 1;
}
- private Resources getResources(PackageItemInfo pii) {
- Resources res = mResourceCache.get(pii.packageName);
- if (res != null) return res;
-
- try {
- ApplicationInfo ai = mPm.getApplicationInfo(pii.packageName, 0, 0);
- AssetManager am = new AssetManager();
- am.addAssetPath(ai.publicSourceDir);
- res = new Resources(am, null, null);
- mResourceCache.put(pii.packageName, res);
- return res;
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- return null;
- }
- }
-
- private static String checkAbiArgument(String abi) {
- if (TextUtils.isEmpty(abi)) {
- throw new IllegalArgumentException("Missing ABI argument");
- }
-
- if ("-".equals(abi)) {
- return abi;
- }
-
- final String[] supportedAbis = Build.SUPPORTED_ABIS;
- for (String supportedAbi : supportedAbis) {
- if (supportedAbi.equals(abi)) {
- return abi;
- }
- }
-
- throw new IllegalArgumentException("ABI " + abi + " not supported on this device");
- }
-
- private static class LocalIntentReceiver {
- private final SynchronousQueue<Intent> mResult = new SynchronousQueue<>();
-
- private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
- @Override
- public int send(int code, Intent intent, String resolvedType,
- IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
- try {
- mResult.offer(intent, 5, TimeUnit.SECONDS);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- return 0;
- }
- };
-
- public IntentSender getIntentSender() {
- return new IntentSender((IIntentSender) mLocalSender);
- }
-
- public Intent getResult() {
- try {
- return mResult.take();
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
- }
-
private String nextOption() {
if (mNextArg >= mArgs.length) {
return null;
@@ -2097,14 +1324,7 @@
}
private static int showUsage() {
- System.err.println("usage: pm list packages [-f] [-d] [-e] [-s] [-3] [-i] [-u] [--user USER_ID] [FILTER]");
- System.err.println(" pm list permission-groups");
- System.err.println(" pm list permissions [-g] [-f] [-d] [-u] [GROUP]");
- System.err.println(" pm list instrumentation [-f] [TARGET-PACKAGE]");
- System.err.println(" pm list features");
- System.err.println(" pm list libraries");
- System.err.println(" pm list users");
- System.err.println(" pm path PACKAGE");
+ System.err.println("usage: pm path [--user USER_ID] PACKAGE");
System.err.println(" pm dump PACKAGE");
System.err.println(" pm install [-lrtsfd] [-i PACKAGE] [--user USER_ID] [PATH]");
System.err.println(" pm install-create [-lrtsfdp] [-i PACKAGE] [-S BYTES]");
@@ -2138,34 +1358,8 @@
System.err.println(" pm remove-user USER_ID");
System.err.println(" pm get-max-users");
System.err.println("");
- System.err.println("pm list packages: prints all packages, optionally only");
- System.err.println(" those whose package name contains the text in FILTER. Options:");
- System.err.println(" -f: see their associated file.");
- System.err.println(" -d: filter to only show disbled packages.");
- System.err.println(" -e: filter to only show enabled packages.");
- System.err.println(" -s: filter to only show system packages.");
- System.err.println(" -3: filter to only show third party packages.");
- System.err.println(" -i: see the installer for the packages.");
- System.err.println(" -u: also include uninstalled packages.");
- System.err.println("");
- System.err.println("pm list permission-groups: prints all known permission groups.");
- System.err.println("");
- System.err.println("pm list permissions: prints all known permissions, optionally only");
- System.err.println(" those in GROUP. Options:");
- System.err.println(" -g: organize by group.");
- System.err.println(" -f: print all information.");
- System.err.println(" -s: short summary.");
- System.err.println(" -d: only list dangerous permissions.");
- System.err.println(" -u: list only the permissions users will see.");
- System.err.println("");
- System.err.println("pm list instrumentation: use to list all test packages; optionally");
- System.err.println(" supply <TARGET-PACKAGE> to list the test packages for a particular");
- System.err.println(" application. Options:");
- System.err.println(" -f: list the .apk file for the test package.");
- System.err.println("");
- System.err.println("pm list features: prints all features of the system.");
- System.err.println("");
- System.err.println("pm list users: prints all users on the system.");
+ System.err.println("NOTE: 'pm list' commands have moved! Run 'adb shell cmd package'");
+ System.err.println(" to display the new commands.");
System.err.println("");
System.err.println("pm path: print the path to the .apk of the given PACKAGE.");
System.err.println("");
diff --git a/cmds/sm/src/com/android/commands/sm/Sm.java b/cmds/sm/src/com/android/commands/sm/Sm.java
index 0a1ba4d1..b208e43 100644
--- a/cmds/sm/src/com/android/commands/sm/Sm.java
+++ b/cmds/sm/src/com/android/commands/sm/Sm.java
@@ -42,6 +42,7 @@
} catch (Exception e) {
if (e instanceof IllegalArgumentException) {
showUsage();
+ System.exit(1);
}
Log.e(TAG, "Error", e);
System.err.println("Error: " + e);
@@ -85,6 +86,8 @@
runBenchmark();
} else if ("forget".equals(op)) {
runForget();
+ } else if ("set-emulate-fbe".equals(op)) {
+ runSetEmulateFbe();
} else {
throw new IllegalArgumentException();
}
@@ -136,6 +139,12 @@
StorageManager.DEBUG_FORCE_ADOPTABLE);
}
+ public void runSetEmulateFbe() throws RemoteException {
+ final boolean emulateFbe = Boolean.parseBoolean(nextArg());
+ mSm.setDebugFlags(emulateFbe ? StorageManager.DEBUG_EMULATE_FBE : 0,
+ StorageManager.DEBUG_EMULATE_FBE);
+ }
+
public void runPartition() throws RemoteException {
final String diskId = nextArg();
final String type = nextArg();
@@ -204,6 +213,8 @@
System.err.println("");
System.err.println(" sm forget [UUID|all]");
System.err.println("");
+ System.err.println(" sm set-emulate-fbe [true|false]");
+ System.err.println("");
return 1;
}
}
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
index d331c2a..844063c 100644
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -28,7 +28,6 @@
/**
* The value used to indicate infinite duration (e.g. when Animators repeat infinitely).
- * @hide
*/
public static final long DURATION_INFINITE = -1;
/**
@@ -191,11 +190,18 @@
/**
* Gets the total duration of the animation, accounting for animation sequences, start delay,
* and repeating. Return {@link #DURATION_INFINITE} if the duration is infinite.
- * @hide
- * TODO: Unhide
+ *
+ * @return Total time an animation takes to finish, starting from the time {@link #start()}
+ * is called. {@link #DURATION_INFINITE} will be returned if the animation or any
+ * child animation repeats infinite times.
*/
public long getTotalDuration() {
- return getStartDelay() + getDuration();
+ long duration = getDuration();
+ if (duration == DURATION_INFINITE) {
+ return DURATION_INFINITE;
+ } else {
+ return getStartDelay() + duration;
+ }
}
/**
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index d444638..1ab55dd 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -1030,9 +1030,6 @@
}
}
- /**
- * @hide
- */
@Override
public long getTotalDuration() {
updateAnimatorsDuration();
diff --git a/core/java/android/animation/ObjectAnimator.java b/core/java/android/animation/ObjectAnimator.java
index 26c886e..0b751b2 100644
--- a/core/java/android/animation/ObjectAnimator.java
+++ b/core/java/android/animation/ObjectAnimator.java
@@ -23,9 +23,9 @@
import android.graphics.PointF;
import android.util.Log;
import android.util.Property;
+import android.view.animation.AccelerateDecelerateInterpolator;
import java.lang.ref.WeakReference;
-import java.util.ArrayList;
/**
* This subclass of {@link ValueAnimator} provides support for animating properties on target objects.
@@ -53,7 +53,8 @@
* from the target object when the animator starts, just like animators with only one
* value specified. In addition, an optional interpolator can be specified. The interpolator will
* be applied on the interval between the keyframe that the interpolator is set on and the previous
- * keyframe. When no interpolator is supplied, the default linear interpolator will be used. </p>
+ * keyframe. When no interpolator is supplied, the default {@link AccelerateDecelerateInterpolator}
+ * will be used. </p>
*
* {@sample development/samples/ApiDemos/res/anim/object_animator_pvh_kf_interpolated.xml KeyframeResources}
*
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 4b3df30..7f9a5d3 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -536,9 +536,6 @@
return mDuration;
}
- /**
- * @hide
- */
@Override
public long getTotalDuration() {
if (mRepeatCount == INFINITE) {
@@ -958,7 +955,6 @@
mStarted = true;
mPaused = false;
mRunning = false;
- mAnimationEndRequested = false;
AnimationHandler animationHandler = AnimationHandler.getInstance();
animationHandler.addAnimationFrameCallback(this, (long) (mStartDelay * sDurationScale));
@@ -1126,6 +1122,8 @@
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, getNameForTrace(),
System.identityHashCode(this));
}
+
+ mAnimationEndRequested = false;
initAnimation();
mRunning = true;
if (mSeekFraction >= 0) {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index d47c0aa..472d97f 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -705,6 +705,8 @@
private static final String SAVED_DIALOGS_TAG = "android:savedDialogs";
private static final String SAVED_DIALOG_KEY_PREFIX = "android:dialog_";
private static final String SAVED_DIALOG_ARGS_KEY_PREFIX = "android:dialog_args_";
+ private static final String HAS_CURENT_PERMISSIONS_REQUEST_KEY =
+ "android:hasCurrentPermissionsRequest";
private static final String REQUEST_PERMISSIONS_WHO_PREFIX = "@android:requestPermissions:";
@@ -813,6 +815,8 @@
SharedElementCallback mEnterTransitionListener = SharedElementCallback.NULL_CALLBACK;
SharedElementCallback mExitTransitionListener = SharedElementCallback.NULL_CALLBACK;
+ private boolean mHasCurrentPermissionsRequest;
+
/** Return the intent that started this activity. */
public Intent getIntent() {
return mIntent;
@@ -1314,6 +1318,7 @@
onSaveInstanceState(outState);
saveManagedDialogs(outState);
mActivityTransitionState.saveState(outState);
+ storeHasCurrentPermissionRequest(outState);
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState);
}
@@ -1329,6 +1334,7 @@
final void performSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
onSaveInstanceState(outState, outPersistentState);
saveManagedDialogs(outState);
+ storeHasCurrentPermissionRequest(outState);
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState +
", " + outPersistentState);
}
@@ -2722,8 +2728,8 @@
/**
* Called to move the window and its activity/task to a different stack container.
* For example, a window can move between
- * {@link android.app.ActivityManager#FULLSCREEN_WORKSPACE_STACK_ID} stack and
- * {@link android.app.ActivityManager#FREEFORM_WORKSPACE_STACK_ID} stack.
+ * {@link android.app.ActivityManager.StackId#FULLSCREEN_WORKSPACE_STACK_ID} stack and
+ * {@link android.app.ActivityManager.StackId#FREEFORM_WORKSPACE_STACK_ID} stack.
*
* @param stackId stack Id to change to.
* @hide
@@ -3862,8 +3868,15 @@
* @see #shouldShowRequestPermissionRationale(String)
*/
public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
+ if (mHasCurrentPermissionsRequest) {
+ Log.w(TAG, "Can reqeust only one set of permissions at a time");
+ // Dispatch the callback with empty arrays which means a cancellation.
+ onRequestPermissionsResult(requestCode, new String[0], new int[0]);
+ return;
+ }
Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
+ mHasCurrentPermissionsRequest = true;
}
/**
@@ -6301,12 +6314,14 @@
}
final void performCreate(Bundle icicle) {
+ restoreHasCurrentPermissionRequest(icicle);
onCreate(icicle);
mActivityTransitionState.readState(icicle);
performCreateCommon();
}
final void performCreate(Bundle icicle, PersistableBundle persistentState) {
+ restoreHasCurrentPermissionRequest(icicle);
onCreate(icicle, persistentState);
mActivityTransitionState.readState(icicle);
performCreateCommon();
@@ -6489,6 +6504,19 @@
return mResumed;
}
+ private void storeHasCurrentPermissionRequest(Bundle bundle) {
+ if (bundle != null && mHasCurrentPermissionsRequest) {
+ bundle.putBoolean(HAS_CURENT_PERMISSIONS_REQUEST_KEY, true);
+ }
+ }
+
+ private void restoreHasCurrentPermissionRequest(Bundle bundle) {
+ if (bundle != null) {
+ mHasCurrentPermissionsRequest = bundle.getBoolean(
+ HAS_CURENT_PERMISSIONS_REQUEST_KEY, false);
+ }
+ }
+
void dispatchActivityResult(String who, int requestCode,
int resultCode, Intent data) {
if (false) Log.v(
@@ -6616,6 +6644,7 @@
}
private void dispatchRequestPermissionsResult(int requestCode, Intent data) {
+ mHasCurrentPermissionsRequest = false;
// If the package installer crashed we may have not data - best effort.
String[] permissions = (data != null) ? data.getStringArrayExtra(
PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES) : new String[0];
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 61a9a84..65de4ca 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -73,7 +73,6 @@
*/
public class ActivityManager {
private static String TAG = "ActivityManager";
- private static boolean localLOGV = false;
private static int gMaxRecentTasks = -1;
@@ -204,17 +203,10 @@
/**
* Flag for IActivityManaqer.startActivity: launch the app for
- * OpenGL tracing.
- * @hide
- */
- public static final int START_FLAG_OPENGL_TRACES = 1<<2;
-
- /**
- * Flag for IActivityManaqer.startActivity: launch the app for
* allocation tracking.
* @hide
*/
- public static final int START_FLAG_TRACK_ALLOCATION = 1<<3;
+ public static final int START_FLAG_TRACK_ALLOCATION = 1<<2;
/**
* Result for IActivityManaqer.broadcastIntent: success!
@@ -404,53 +396,112 @@
*/
public static final int COMPAT_MODE_TOGGLE = 2;
- /**
- * Invalid stack ID.
- * @hide
- */
- public static final int INVALID_STACK_ID = -1;
+ /** @hide */
+ public static class StackId {
+ /** Invalid stack ID. */
+ public static final int INVALID_STACK_ID = -1;
- /**
- * First static stack ID.
- * @hide
- */
- public static final int FIRST_STATIC_STACK_ID = 0;
+ /** First static stack ID. */
+ public static final int FIRST_STATIC_STACK_ID = 0;
- /**
- * Home activity stack ID.
- * @hide
- */
- public static final int HOME_STACK_ID = FIRST_STATIC_STACK_ID;
+ /** Home activity stack ID. */
+ public static final int HOME_STACK_ID = FIRST_STATIC_STACK_ID;
- /**
- * ID of stack where fullscreen activities are normally launched into.
- * @hide
- */
- public static final int FULLSCREEN_WORKSPACE_STACK_ID = 1;
+ /** ID of stack where fullscreen activities are normally launched into. */
+ public static final int FULLSCREEN_WORKSPACE_STACK_ID = 1;
- /**
- * ID of stack where freeform/resized activities are normally launched into.
- * @hide
- */
- public static final int FREEFORM_WORKSPACE_STACK_ID = FULLSCREEN_WORKSPACE_STACK_ID + 1;
+ /** ID of stack where freeform/resized activities are normally launched into. */
+ public static final int FREEFORM_WORKSPACE_STACK_ID = FULLSCREEN_WORKSPACE_STACK_ID + 1;
- /**
- * ID of stack that occupies a dedicated region of the screen.
- * @hide
- */
- public static final int DOCKED_STACK_ID = FREEFORM_WORKSPACE_STACK_ID + 1;
+ /** ID of stack that occupies a dedicated region of the screen. */
+ public static final int DOCKED_STACK_ID = FREEFORM_WORKSPACE_STACK_ID + 1;
- /**
- * Last static stack stack ID.
- * @hide
- */
- public static final int LAST_STATIC_STACK_ID = DOCKED_STACK_ID;
+ /** ID of stack that always on top (always visible) when it exist. */
+ public static final int PINNED_STACK_ID = DOCKED_STACK_ID + 1;
- /**
- * Start of ID range used by stacks that are created dynamically.
- * @hide
- */
- public static final int FIRST_DYNAMIC_STACK_ID = LAST_STATIC_STACK_ID + 1;
+ /** Last static stack stack ID. */
+ public static final int LAST_STATIC_STACK_ID = PINNED_STACK_ID;
+
+ /** Start of ID range used by stacks that are created dynamically. */
+ public static final int FIRST_DYNAMIC_STACK_ID = LAST_STATIC_STACK_ID + 1;
+
+ public static boolean isStaticStack(int stackId) {
+ return stackId >= FIRST_STATIC_STACK_ID && stackId <= LAST_STATIC_STACK_ID;
+ }
+
+ /**
+ * Returns true if the activities contained in the input stack display a shadow around
+ * their border.
+ */
+ public static boolean hasWindowShadow(int stackId) {
+ return stackId == FREEFORM_WORKSPACE_STACK_ID || stackId == PINNED_STACK_ID;
+ }
+
+ /**
+ * Returns true if the activities contained in the input stack display a decor view.
+ */
+ public static boolean hasWindowDecor(int stackId) {
+ return stackId == FREEFORM_WORKSPACE_STACK_ID;
+ }
+
+ /**
+ * Returns true if the tasks contained in the stack can be resized independently of the
+ * stack.
+ */
+ public static boolean isTaskResizeAllowed(int stackId) {
+ return stackId == FREEFORM_WORKSPACE_STACK_ID;
+ }
+
+ /**
+ * Returns true if the task bounds should persist across power cycles.
+ */
+ public static boolean persistTaskBounds(int stackId) {
+ return isStaticStack(stackId) &&
+ stackId != DOCKED_STACK_ID && stackId != PINNED_STACK_ID;
+ }
+
+ /**
+ * Returns true if dynamic stacks are allowed to be visible behind the input stack.
+ */
+ public static boolean isDynamicStacksVisibleBehindAllowed(int stackId) {
+ return stackId == PINNED_STACK_ID;
+ }
+
+ /**
+ * Returns true if we try to maintain focus in the current stack when the top activity
+ * finishes.
+ */
+ public static boolean keepFocusInStackIfPossible(int stackId) {
+ return stackId == FREEFORM_WORKSPACE_STACK_ID
+ || stackId == DOCKED_STACK_ID || stackId == PINNED_STACK_ID;
+ }
+
+ /**
+ * Returns true if Stack size is affected by the docked stack changing size.
+ */
+ public static boolean isResizeableByDockedStack(int stackId) {
+ return isStaticStack(stackId) &&
+ stackId != DOCKED_STACK_ID && stackId != PINNED_STACK_ID;
+ }
+
+ /**
+ * Returns true if the size of tasks in the input stack are affected by the docked stack
+ * changing size.
+ */
+ public static boolean isTaskResizeableByDockedStack(int stackId) {
+ return isStaticStack(stackId) && stackId != FREEFORM_WORKSPACE_STACK_ID
+ && stackId != DOCKED_STACK_ID && stackId != PINNED_STACK_ID;
+ }
+
+ /**
+ * Returns true if the windows of tasks being moved to this stack should be preserved so
+ * there isn't a display gap.
+ */
+ public static boolean preserveWindowOnTaskMove(int stackId) {
+ return stackId == FULLSCREEN_WORKSPACE_STACK_ID
+ || stackId == DOCKED_STACK_ID || stackId == PINNED_STACK_ID;
+ }
+ }
/**
* Input parameter to {@link android.app.IActivityManager#moveTaskToDockedStack} which
@@ -1016,6 +1067,12 @@
*/
public int numActivities;
+ /**
+ * The bounds of the task.
+ * @hide
+ */
+ public Rect bounds;
+
public RecentTaskInfo() {
}
@@ -1053,6 +1110,12 @@
ComponentName.writeToParcel(baseActivity, dest);
ComponentName.writeToParcel(topActivity, dest);
dest.writeInt(numActivities);
+ if (bounds != null) {
+ dest.writeInt(1);
+ bounds.writeToParcel(dest, 0);
+ } else {
+ dest.writeInt(0);
+ }
}
public void readFromParcel(Parcel source) {
@@ -1073,6 +1136,8 @@
baseActivity = ComponentName.readFromParcel(source);
topActivity = ComponentName.readFromParcel(source);
numActivities = source.readInt();
+ bounds = source.readInt() > 0 ?
+ Rect.CREATOR.createFromParcel(source) : null;
}
public static final Creator<RecentTaskInfo> CREATOR
@@ -1200,6 +1265,12 @@
public int id;
/**
+ * The stack that currently contains this task.
+ * @hide
+ */
+ public int stackId;
+
+ /**
* The component launched as the first activity in the task. This can
* be considered the "application" of this task.
*/
@@ -1248,6 +1319,7 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
+ dest.writeInt(stackId);
ComponentName.writeToParcel(baseActivity, dest);
ComponentName.writeToParcel(topActivity, dest);
if (thumbnail != null) {
@@ -1264,6 +1336,7 @@
public void readFromParcel(Parcel source) {
id = source.readInt();
+ stackId = source.readInt();
baseActivity = ComponentName.readFromParcel(source);
topActivity = ComponentName.readFromParcel(source);
if (source.readInt() != 0) {
@@ -1458,14 +1531,100 @@
}
}
+ /**
+ * Metadata related to the {@link TaskThumbnail}.
+ *
+ * @hide
+ */
+ public static class TaskThumbnailInfo implements Parcelable {
+ /** @hide */
+ public static final String ATTR_TASK_THUMBNAILINFO_PREFIX = "task_thumbnailinfo_";
+ private static final String ATTR_TASK_WIDTH =
+ ATTR_TASK_THUMBNAILINFO_PREFIX + "task_width";
+ private static final String ATTR_TASK_HEIGHT =
+ ATTR_TASK_THUMBNAILINFO_PREFIX + "task_height";
+ private static final String ATTR_SCREEN_ORIENTATION =
+ ATTR_TASK_THUMBNAILINFO_PREFIX + "screen_orientation";
+
+ public int taskWidth;
+ public int taskHeight;
+ public int screenOrientation;
+
+ public TaskThumbnailInfo() {
+ // Do nothing
+ }
+
+ private TaskThumbnailInfo(Parcel source) {
+ readFromParcel(source);
+ }
+
+ /**
+ * Resets this info state to the initial state.
+ * @hide
+ */
+ public void reset() {
+ taskWidth = 0;
+ taskHeight = 0;
+ screenOrientation = 0;
+ }
+
+ /** @hide */
+ public void saveToXml(XmlSerializer out) throws IOException {
+ out.attribute(null, ATTR_TASK_WIDTH, Integer.toString(taskWidth));
+ out.attribute(null, ATTR_TASK_HEIGHT, Integer.toString(taskHeight));
+ out.attribute(null, ATTR_SCREEN_ORIENTATION, Integer.toString(screenOrientation));
+ }
+
+ /** @hide */
+ public void restoreFromXml(String attrName, String attrValue) {
+ if (ATTR_TASK_WIDTH.equals(attrName)) {
+ taskWidth = Integer.parseInt(attrValue);
+ } else if (ATTR_TASK_HEIGHT.equals(attrName)) {
+ taskHeight = Integer.parseInt(attrValue);
+ } else if (ATTR_SCREEN_ORIENTATION.equals(attrName)) {
+ screenOrientation = Integer.parseInt(attrValue);
+ }
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(taskWidth);
+ dest.writeInt(taskHeight);
+ dest.writeInt(screenOrientation);
+ }
+
+ public void readFromParcel(Parcel source) {
+ taskWidth = source.readInt();
+ taskHeight = source.readInt();
+ screenOrientation = source.readInt();
+ }
+
+ public static final Creator<TaskThumbnailInfo> CREATOR = new Creator<TaskThumbnailInfo>() {
+ public TaskThumbnailInfo createFromParcel(Parcel source) {
+ return new TaskThumbnailInfo(source);
+ }
+ public TaskThumbnailInfo[] newArray(int size) {
+ return new TaskThumbnailInfo[size];
+ }
+ };
+ }
+
/** @hide */
public static class TaskThumbnail implements Parcelable {
public Bitmap mainThumbnail;
public ParcelFileDescriptor thumbnailFileDescriptor;
+ public TaskThumbnailInfo thumbnailInfo;
public TaskThumbnail() {
}
+ private TaskThumbnail(Parcel source) {
+ readFromParcel(source);
+ }
+
public int describeContents() {
if (thumbnailFileDescriptor != null) {
return thumbnailFileDescriptor.describeContents();
@@ -1486,6 +1645,12 @@
} else {
dest.writeInt(0);
}
+ if (thumbnailInfo != null) {
+ dest.writeInt(1);
+ thumbnailInfo.writeToParcel(dest, flags);
+ } else {
+ dest.writeInt(0);
+ }
}
public void readFromParcel(Parcel source) {
@@ -1499,6 +1664,11 @@
} else {
thumbnailFileDescriptor = null;
}
+ if (source.readInt() != 0) {
+ thumbnailInfo = TaskThumbnailInfo.CREATOR.createFromParcel(source);
+ } else {
+ thumbnailInfo = null;
+ }
}
public static final Creator<TaskThumbnail> CREATOR = new Creator<TaskThumbnail>() {
@@ -1509,10 +1679,6 @@
return new TaskThumbnail[size];
}
};
-
- private TaskThumbnail(Parcel source) {
- readFromParcel(source);
- }
}
/** @hide */
@@ -2879,6 +3045,11 @@
}
}
+ /** {@hide} */
+ public static final int FLAG_OR_STOPPED = 1 << 0;
+ /** {@hide} */
+ public static final int FLAG_WITH_AMNESIA = 1 << 1;
+
/**
* Return whether the given user is actively running. This means that
* the user is in the "started" state, not "stopped" -- it is currently
@@ -2890,7 +3061,7 @@
*/
public boolean isUserRunning(int userid) {
try {
- return ActivityManagerNative.getDefault().isUserRunning(userid, false);
+ return ActivityManagerNative.getDefault().isUserRunning(userid, 0);
} catch (RemoteException e) {
return false;
}
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index b97f947..4449e4f 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -116,21 +116,22 @@
static public void noteWakeupAlarm(PendingIntent ps, int sourceUid, String sourcePkg,
String tag) {
try {
- getDefault().noteWakeupAlarm(ps.getTarget(), sourceUid, sourcePkg, tag);
+ getDefault().noteWakeupAlarm((ps != null) ? ps.getTarget() : null,
+ sourceUid, sourcePkg, tag);
} catch (RemoteException ex) {
}
}
static public void noteAlarmStart(PendingIntent ps, int sourceUid, String tag) {
try {
- getDefault().noteAlarmStart(ps.getTarget(), sourceUid, tag);
+ getDefault().noteAlarmStart((ps != null) ? ps.getTarget() : null, sourceUid, tag);
} catch (RemoteException ex) {
}
}
static public void noteAlarmFinish(PendingIntent ps, int sourceUid, String tag) {
try {
- getDefault().noteAlarmFinish(ps.getTarget(), sourceUid, tag);
+ getDefault().noteAlarmFinish((ps != null) ? ps.getTarget() : null, sourceUid, tag);
} catch (RemoteException ex) {
}
}
@@ -755,10 +756,24 @@
return true;
}
+ case MOVE_TOP_ACTIVITY_TO_PINNED_STACK: {
+ data.enforceInterface(IActivityManager.descriptor);
+ final int stackId = data.readInt();
+ final Rect r = Rect.CREATOR.createFromParcel(data);
+ final boolean res = moveTopActivityToPinnedStack(stackId, r);
+ reply.writeNoException();
+ reply.writeInt(res ? 1 : 0);
+ return true;
+ }
+
case RESIZE_STACK_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
final int stackId = data.readInt();
- Rect r = Rect.CREATOR.createFromParcel(data);
+ final boolean hasRect = data.readInt() != 0;
+ Rect r = null;
+ if (hasRect) {
+ r = Rect.CREATOR.createFromParcel(data);
+ }
final boolean allowResizeInDockedMode = data.readInt() == 1;
resizeStack(stackId, r, allowResizeInDockedMode);
reply.writeNoException();
@@ -1752,7 +1767,7 @@
} else {
bundle = data.readBundle();
}
- final ActivityOptions options = bundle == null ? null : new ActivityOptions(bundle);
+ final ActivityOptions options = ActivityOptions.fromBundle(bundle);
boolean converted = convertToTranslucent(token, options);
reply.writeNoException();
reply.writeInt(converted ? 1 : 0);
@@ -1969,8 +1984,8 @@
case IS_USER_RUNNING_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
int userid = data.readInt();
- boolean orStopping = data.readInt() != 0;
- boolean result = isUserRunning(userid, orStopping);
+ int _flags = data.readInt();
+ boolean result = isUserRunning(userid, _flags);
reply.writeNoException();
reply.writeInt(result ? 1 : 0);
return true;
@@ -2493,7 +2508,7 @@
} else {
bundle = data.readBundle();
}
- final ActivityOptions options = bundle == null ? null : new ActivityOptions(bundle);
+ final ActivityOptions options = ActivityOptions.fromBundle(bundle);
startInPlaceAnimationOnFrontMostApplication(options);
reply.writeNoException();
return true;
@@ -2670,19 +2685,10 @@
case REPORT_SIZE_CONFIGURATIONS: {
data.enforceInterface(IActivityManager.descriptor);
IBinder token = data.readStrongBinder();
- int horizontalSize = data.readInt();
- int[] horizontal = null;
- if (horizontalSize > 0) {
- horizontal = new int[horizontalSize];
- data.readIntArray(horizontal);
- }
- int[] vertical = null;
- int verticalSize = data.readInt();
- if (verticalSize > 0) {
- vertical = new int[verticalSize];
- data.readIntArray(vertical);
- }
- reportSizeConfigurations(token, horizontal, vertical);
+ int[] horizontal = readIntArray(data);
+ int[] vertical = readIntArray(data);
+ int[] smallest = readIntArray(data);
+ reportSizeConfigurations(token, horizontal, vertical, smallest);
return true;
}
case SUPPRESS_RESIZE_CONFIG_CHANGES_TRANSACTION: {
@@ -2704,6 +2710,16 @@
return super.onTransact(code, data, reply, flags);
}
+ private int[] readIntArray(Parcel data) {
+ int[] smallest = null;
+ int smallestSize = data.readInt();
+ if (smallestSize > 0) {
+ smallest = new int[smallestSize];
+ data.readIntArray(smallest);
+ }
+ return smallest;
+ }
+
public IBinder asBinder() {
return this;
}
@@ -3555,6 +3571,22 @@
reply.recycle();
}
@Override
+ public boolean moveTopActivityToPinnedStack(int stackId, Rect r)
+ throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(stackId);
+ r.writeToParcel(data, 0);
+ mRemote.transact(MOVE_TOP_ACTIVITY_TO_PINNED_STACK, data, reply, 0);
+ reply.readException();
+ final boolean res = reply.readInt() != 0;
+ data.recycle();
+ reply.recycle();
+ return res;
+ }
+ @Override
public void resizeStack(int stackId, Rect r, boolean allowResizeInDockedMode)
throws RemoteException
{
@@ -3562,7 +3594,12 @@
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeInt(stackId);
- r.writeToParcel(data, 0);
+ if (r != null) {
+ data.writeInt(1);
+ r.writeToParcel(data, 0);
+ } else {
+ data.writeInt(0);
+ }
data.writeInt(allowResizeInDockedMode ? 1 : 0);
mRemote.transact(RESIZE_STACK_TRANSACTION, data, reply, 0);
reply.readException();
@@ -4894,8 +4931,7 @@
data.writeStrongBinder(token);
mRemote.transact(GET_ACTIVITY_OPTIONS_TRANSACTION, data, reply, 0);
reply.readException();
- Bundle bundle = reply.readBundle();
- ActivityOptions options = bundle == null ? null : new ActivityOptions(bundle);
+ ActivityOptions options = ActivityOptions.fromBundle(reply.readBundle());
data.recycle();
reply.recycle();
return options;
@@ -5229,12 +5265,12 @@
return userInfo;
}
- public boolean isUserRunning(int userid, boolean orStopping) throws RemoteException {
+ public boolean isUserRunning(int userid, int flags) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeInt(userid);
- data.writeInt(orStopping ? 1 : 0);
+ data.writeInt(flags);
mRemote.transact(IS_USER_RUNNING_TRANSACTION, data, reply, 0);
reply.readException();
boolean result = reply.readInt() != 0;
@@ -6214,29 +6250,30 @@
@Override
public void reportSizeConfigurations(IBinder token, int[] horizontalSizeConfiguration,
- int[] verticalSizeConfigurations) throws RemoteException {
+ int[] verticalSizeConfigurations, int[] smallestSizeConfigurations)
+ throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(token);
- if (horizontalSizeConfiguration == null) {
- data.writeInt(0);
- } else {
- data.writeInt(horizontalSizeConfiguration.length);
- data.writeIntArray(horizontalSizeConfiguration);
- }
- if (verticalSizeConfigurations == null) {
- data.writeInt(0);
- } else {
- data.writeInt(verticalSizeConfigurations.length);
- data.writeIntArray(verticalSizeConfigurations);
- }
+ writeIntArray(horizontalSizeConfiguration, data);
+ writeIntArray(verticalSizeConfigurations, data);
+ writeIntArray(smallestSizeConfigurations, data);
mRemote.transact(REPORT_SIZE_CONFIGURATIONS, data, reply, 0);
reply.readException();
data.recycle();
reply.recycle();
}
+ private static void writeIntArray(int[] array, Parcel data) {
+ if (array == null) {
+ data.writeInt(0);
+ } else {
+ data.writeInt(array.length);
+ data.writeIntArray(array);
+ }
+ }
+
@Override
public void suppressResizeConfigChanges(boolean suppress) throws RemoteException {
Parcel data = Parcel.obtain();
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 933f98d..57900aa 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -16,6 +16,8 @@
package android.app;
+import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
@@ -23,10 +25,12 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.IRemoteCallback;
+import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.util.Pair;
import android.util.Slog;
+import android.view.AppTransitionAnimationSpec;
import android.view.View;
import android.view.Window;
@@ -127,6 +131,24 @@
public static final String KEY_ANIM_START_LISTENER = "android:activity.animStartListener";
/**
+ * Callback for when the last frame of the animation is played.
+ * @hide
+ */
+ private static final String KEY_ANIMATION_FINISHED_LISTENER =
+ "android:activity.animationFinishedListener";
+
+ /**
+ * Descriptions of app transition animations to be played during the activity launch.
+ */
+ private static final String KEY_ANIM_SPECS = "android:activity.animSpecs";
+
+ /**
+ * Where the docked stack should be positioned.
+ * @hide
+ */
+ private static final String KEY_DOCK_CREATE_MODE = "android:activity.dockCreateMode";
+
+ /**
* For Activity transitions, the calling Activity's TransitionListener used to
* notify the called Activity when the shared element and the exit transitions
* complete.
@@ -183,6 +205,7 @@
private int mWidth;
private int mHeight;
private IRemoteCallback mAnimationStartedListener;
+ private IRemoteCallback mAnimationFinishedListener;
private ResultReceiver mTransitionReceiver;
private boolean mIsReturning;
private ArrayList<String> mSharedElementNames;
@@ -190,6 +213,8 @@
private int mResultCode;
private int mExitCoordinatorIndex;
private PendingIntent mUsageTimeReport;
+ private int mDockCreateMode = DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+ private AppTransitionAnimationSpec mAnimSpecs[];
/**
* Create an ActivityOptions specifying a custom animation to run when
@@ -263,16 +288,15 @@
return opts;
}
- private void setOnAnimationStartedListener(Handler handler,
- OnAnimationStartedListener listener) {
+ private void setOnAnimationStartedListener(final Handler handler,
+ final OnAnimationStartedListener listener) {
if (listener != null) {
- final Handler h = handler;
- final OnAnimationStartedListener finalListener = listener;
mAnimationStartedListener = new IRemoteCallback.Stub() {
- @Override public void sendResult(Bundle data) throws RemoteException {
- h.post(new Runnable() {
+ @Override
+ public void sendResult(Bundle data) throws RemoteException {
+ handler.post(new Runnable() {
@Override public void run() {
- finalListener.onAnimationStarted();
+ listener.onAnimationStarted();
}
});
}
@@ -289,6 +313,32 @@
void onAnimationStarted();
}
+ private void setOnAnimationFinishedListener(final Handler handler,
+ final OnAnimationFinishedListener listener) {
+ if (listener != null) {
+ mAnimationFinishedListener = new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle data) throws RemoteException {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ listener.onAnimationFinished();
+ }
+ });
+ }
+ };
+ }
+ }
+
+ /**
+ * Callback for use with {@link ActivityOptions#makeThumbnailAspectScaleDownAnimation}
+ * to find out when the given animation has drawn its last frame.
+ * @hide
+ */
+ public interface OnAnimationFinishedListener {
+ void onAnimationFinished();
+ }
+
/**
* Create an ActivityOptions specifying an animation where the new
* activity is scaled from a small originating area of the screen to
@@ -503,6 +553,20 @@
return opts;
}
+ /** @hide */
+ public static ActivityOptions makeThumbnailAspectScaleDownAnimation(View source,
+ AppTransitionAnimationSpec[] specs, Handler handler,
+ OnAnimationStartedListener onAnimationStartedListener,
+ OnAnimationFinishedListener onAnimationFinishedListener) {
+ ActivityOptions opts = new ActivityOptions();
+ opts.mPackageName = source.getContext().getPackageName();
+ opts.mAnimationType = ANIM_THUMBNAIL_ASPECT_SCALE_DOWN;
+ opts.mAnimSpecs = specs;
+ opts.setOnAnimationStartedListener(handler, onAnimationStartedListener);
+ opts.setOnAnimationFinishedListener(handler, onAnimationFinishedListener);
+ return opts;
+ }
+
/**
* Create an ActivityOptions to transition between Activities using cross-Activity scene
* animations. This method carries the position of one shared element to the started Activity.
@@ -688,6 +752,18 @@
mExitCoordinatorIndex = opts.getInt(KEY_EXIT_COORDINATOR_INDEX);
break;
}
+ 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);
+ mAnimSpecs = new AppTransitionAnimationSpec[specs.length];
+ for (int i = specs.length - 1; i >= 0; i--) {
+ mAnimSpecs[i] = (AppTransitionAnimationSpec) specs[i];
+ }
+ }
+ if (opts.containsKey(KEY_ANIMATION_FINISHED_LISTENER)) {
+ mAnimationFinishedListener = IRemoteCallback.Stub.asInterface(
+ opts.getBinder(KEY_ANIMATION_FINISHED_LISTENER));
+ }
}
/** @hide */
@@ -763,6 +839,11 @@
}
/** @hide */
+ public IRemoteCallback getAnimationFinishedListener() {
+ return mAnimationFinishedListener;
+ }
+
+ /** @hide */
public int getExitCoordinatorKey() { return mExitCoordinatorIndex; }
/** @hide */
@@ -800,12 +881,28 @@
}
/** @hide */
- public static void abort(Bundle options) {
+ public AppTransitionAnimationSpec[] getAnimSpecs() { return mAnimSpecs; }
+
+ /** @hide */
+ public static ActivityOptions fromBundle(Bundle bOptions) {
+ return bOptions != null ? new ActivityOptions(bOptions) : null;
+ }
+
+ /** @hide */
+ public static void abort(ActivityOptions options) {
if (options != null) {
- (new ActivityOptions(options)).abort();
+ options.abort();
}
}
+ /** @hide */
+ public int getDockCreateMode() { return mDockCreateMode; }
+
+ /** @hide */
+ public void setDockCreateMode(int dockCreateMode) {
+ mDockCreateMode = dockCreateMode;
+ }
+
/**
* Update the current values in this ActivityOptions from those supplied
* in <var>otherOptions</var>. Any values
@@ -880,6 +977,8 @@
mExitCoordinatorIndex = otherOptions.mExitCoordinatorIndex;
break;
}
+ mAnimSpecs = otherOptions.mAnimSpecs;
+ mAnimationFinishedListener = otherOptions.mAnimationFinishedListener;
}
/**
@@ -945,6 +1044,13 @@
b.putInt(KEY_EXIT_COORDINATOR_INDEX, mExitCoordinatorIndex);
break;
}
+ b.putInt(KEY_DOCK_CREATE_MODE, mDockCreateMode);
+ if (mAnimSpecs != null) {
+ b.putParcelableArray(KEY_ANIM_SPECS, mAnimSpecs);
+ }
+ if (mAnimationFinishedListener != null) {
+ b.putBinder(KEY_ANIMATION_FINISHED_LISTENER, mAnimationFinishedListener.asBinder());
+ }
return b;
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index beaf3cf..b3d6382 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -88,6 +88,7 @@
import android.util.Pair;
import android.util.PrintWriterPrinter;
import android.util.Slog;
+import android.util.SparseIntArray;
import android.util.SuperNotCalledException;
import android.view.Display;
import android.view.HardwareRenderer;
@@ -469,7 +470,6 @@
IUiAutomationConnection instrumentationUiAutomationConnection;
int debugMode;
boolean enableBinderTracking;
- boolean enableOpenGlTrace;
boolean trackAllocation;
boolean restrictedBackupMode;
boolean persistent;
@@ -805,10 +805,9 @@
ProfilerInfo profilerInfo, Bundle instrumentationArgs,
IInstrumentationWatcher instrumentationWatcher,
IUiAutomationConnection instrumentationUiConnection, int debugMode,
- boolean enableBinderTracking, boolean enableOpenGlTrace,
- boolean trackAllocation, boolean isRestrictedBackupMode,
- boolean persistent, Configuration config, CompatibilityInfo compatInfo,
- Map<String, IBinder> services, Bundle coreSettings) {
+ boolean enableBinderTracking, boolean trackAllocation,
+ boolean isRestrictedBackupMode, boolean persistent, Configuration config,
+ CompatibilityInfo compatInfo, Map<String, IBinder> services, Bundle coreSettings) {
if (services != null) {
// Setup the service cache in the ServiceManager
@@ -864,7 +863,6 @@
data.instrumentationUiAutomationConnection = instrumentationUiConnection;
data.debugMode = debugMode;
data.enableBinderTracking = enableBinderTracking;
- data.enableOpenGlTrace = enableOpenGlTrace;
data.trackAllocation = trackAllocation;
data.restrictedBackupMode = isRestrictedBackupMode;
data.persistent = persistent;
@@ -2641,32 +2639,24 @@
if (configurations == null) {
return;
}
- IntArray horizontal = new IntArray();
- IntArray vertical = new IntArray();
+ SparseIntArray horizontal = new SparseIntArray();
+ SparseIntArray vertical = new SparseIntArray();
+ SparseIntArray smallest = new SparseIntArray();
for (int i = configurations.length - 1; i >= 0; i--) {
Configuration config = configurations[i];
if (config.screenHeightDp != Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
- vertical.add(config.screenHeightDp);
+ vertical.put(config.screenHeightDp, 0);
}
if (config.screenWidthDp != Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
- horizontal.add(config.screenWidthDp);
+ horizontal.put(config.screenWidthDp, 0);
}
if (config.smallestScreenWidthDp != Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
- vertical.add(config.smallestScreenWidthDp);
- horizontal.add(config.smallestScreenWidthDp);
+ smallest.put(config.smallestScreenWidthDp, 0);
}
}
- int[] horizontalArray = null;
- if (horizontal.size() > 0) {
- horizontalArray = horizontal.toArray();
- }
- int[] verticalArray = null;
- if (vertical.size() > 0) {
- verticalArray = vertical.toArray();
- }
try {
- ActivityManagerNative.getDefault().reportSizeConfigurations(r.token, horizontalArray,
- verticalArray);
+ ActivityManagerNative.getDefault().reportSizeConfigurations(r.token,
+ horizontal.copyKeys(), vertical.copyKeys(), smallest.copyKeys());
} catch (RemoteException ex) {
}
@@ -4776,11 +4766,6 @@
}
}
- // Enable OpenGL tracing if required
- if (data.enableOpenGlTrace) {
- GLUtils.setTracingLevel(1);
- }
-
// Allow application-generated systrace messages if we're debuggable.
boolean isAppDebuggable = (data.appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
Trace.setAppTracingAllowed(isAppDebuggable);
@@ -4849,13 +4834,7 @@
// Continue loading instrumentation.
if (ii != null) {
final ApplicationInfo instrApp = new ApplicationInfo();
- instrApp.packageName = ii.packageName;
- instrApp.sourceDir = ii.sourceDir;
- instrApp.publicSourceDir = ii.publicSourceDir;
- instrApp.splitSourceDirs = ii.splitSourceDirs;
- instrApp.splitPublicSourceDirs = ii.splitPublicSourceDirs;
- instrApp.dataDir = ii.dataDir;
- instrApp.nativeLibraryDir = ii.nativeLibraryDir;
+ ii.copyTo(instrApp);
final LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
appContext.getClassLoader(), false, true, false);
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index 371c923..c075ed6 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -24,8 +24,6 @@
import android.content.Intent;
import android.content.IntentSender;
import android.graphics.SurfaceTexture;
-import android.os.Handler;
-import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Message;
import android.os.OperationCanceledException;
@@ -45,6 +43,17 @@
import dalvik.system.CloseGuard;
import java.lang.ref.WeakReference;
+import java.util.ArrayDeque;
+import java.util.concurrent.Executor;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.android.internal.annotations.GuardedBy;
+
/** @hide */
public class ActivityView extends ViewGroup {
@@ -53,9 +62,64 @@
private static final int MSG_SET_SURFACE = 1;
- DisplayMetrics mMetrics = new DisplayMetrics();
+ private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
+ private static final int MINIMUM_POOL_SIZE = 1;
+ private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
+ private static final int KEEP_ALIVE = 1;
+
+ private static final ThreadFactory sThreadFactory = new ThreadFactory() {
+ private final AtomicInteger mCount = new AtomicInteger(1);
+
+ public Thread newThread(Runnable r) {
+ return new Thread(r, "ActivityView #" + mCount.getAndIncrement());
+ }
+ };
+
+ private static final BlockingQueue<Runnable> sPoolWorkQueue =
+ new LinkedBlockingQueue<Runnable>(128);
+
+ /**
+ * An {@link Executor} that can be used to execute tasks in parallel.
+ */
+ private static final Executor sExecutor = new ThreadPoolExecutor(MINIMUM_POOL_SIZE,
+ MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
+
+
+ private static class SerialExecutor implements Executor {
+ private final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
+ private Runnable mActive;
+
+ public synchronized void execute(final Runnable r) {
+ mTasks.offer(new Runnable() {
+ public void run() {
+ try {
+ r.run();
+ } finally {
+ scheduleNext();
+ }
+ }
+ });
+ if (mActive == null) {
+ scheduleNext();
+ }
+ }
+
+ protected synchronized void scheduleNext() {
+ if ((mActive = mTasks.poll()) != null) {
+ sExecutor.execute(mActive);
+ }
+ }
+ }
+
+ private final SerialExecutor mExecutor = new SerialExecutor();
+
+ private final int mDensityDpi;
private final TextureView mTextureView;
+
+ @GuardedBy("mActivityContainerLock")
private ActivityContainerWrapper mActivityContainer;
+ private Object mActivityContainerLock = new Object();
+
private Activity mActivity;
private int mWidth;
private int mHeight;
@@ -63,8 +127,6 @@
private int mLastVisibility;
private ActivityViewCallback mActivityViewCallback;
- private HandlerThread mThread = new HandlerThread("ActivityViewThread");
- private Handler mHandler;
public ActivityView(Context context) {
this(context, null);
@@ -97,28 +159,14 @@
+ e);
}
- mThread.start();
- mHandler = new Handler(mThread.getLooper()) {
- @Override
- public void handleMessage(Message msg) {
- super.handleMessage(msg);
- if (msg.what == MSG_SET_SURFACE) {
- try {
- mActivityContainer.setSurface((Surface) msg.obj, msg.arg1, msg.arg2,
- mMetrics.densityDpi);
- } catch (RemoteException e) {
- throw new RuntimeException(
- "ActivityView: Unable to set surface of ActivityContainer. " + e);
- }
- }
- }
- };
mTextureView = new TextureView(context);
mTextureView.setSurfaceTextureListener(new ActivityViewSurfaceTextureListener());
addView(mTextureView);
WindowManager wm = (WindowManager)mActivity.getSystemService(Context.WINDOW_SERVICE);
- wm.getDefaultDisplay().getMetrics(mMetrics);
+ DisplayMetrics metrics = new DisplayMetrics();
+ wm.getDefaultDisplay().getMetrics(metrics);
+ mDensityDpi = metrics.densityDpi;
mLastVisibility = getVisibility();
@@ -131,15 +179,13 @@
}
@Override
- protected void onVisibilityChanged(View changedView, int visibility) {
+ protected void onVisibilityChanged(View changedView, final int visibility) {
super.onVisibilityChanged(changedView, visibility);
if (mSurface != null && (visibility == View.GONE || mLastVisibility == View.GONE)) {
- Message msg = Message.obtain(mHandler, MSG_SET_SURFACE);
- msg.obj = (visibility == View.GONE) ? null : mSurface;
- msg.arg1 = mWidth;
- msg.arg2 = mHeight;
- mHandler.sendMessage(msg);
+ if (DEBUG) Log.v(TAG, "visibility changed; enqueing runnable");
+ final Surface surface = (visibility == View.GONE) ? null : mSurface;
+ setSurfaceAsync(surface, mWidth, mHeight, mDensityDpi, false);
}
mLastVisibility = visibility;
}
@@ -230,8 +276,10 @@
Log.e(TAG, "Duplicate call to release");
return;
}
- mActivityContainer.release();
- mActivityContainer = null;
+ synchronized (mActivityContainerLock) {
+ mActivityContainer.release();
+ mActivityContainer = null;
+ }
if (mSurface != null) {
mSurface.release();
@@ -239,25 +287,39 @@
}
mTextureView.setSurfaceTextureListener(null);
-
- mThread.quit();
}
- private void attachToSurfaceWhenReady() {
- final SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture();
- if (surfaceTexture == null || mSurface != null) {
- // Either not ready to attach, or already attached.
- return;
- }
-
- mSurface = new Surface(surfaceTexture);
- try {
- mActivityContainer.setSurface(mSurface, mWidth, mHeight, mMetrics.densityDpi);
- } catch (RemoteException e) {
- mSurface.release();
- mSurface = null;
- throw new RuntimeException("ActivityView: Unable to create ActivityContainer. " + e);
- }
+ private void setSurfaceAsync(final Surface surface, final int width, final int height,
+ final int densityDpi, final boolean callback) {
+ mExecutor.execute(new Runnable() {
+ public void run() {
+ try {
+ synchronized (mActivityContainerLock) {
+ if (mActivityContainer != null) {
+ mActivityContainer.setSurface(surface, width, height, densityDpi);
+ }
+ }
+ } catch (RemoteException e) {
+ throw new RuntimeException(
+ "ActivityView: Unable to set surface of ActivityContainer. ",
+ e);
+ }
+ if (callback) {
+ post(new Runnable() {
+ @Override
+ public void run() {
+ if (mActivityViewCallback != null) {
+ if (surface != null) {
+ mActivityViewCallback.onSurfaceAvailable(ActivityView.this);
+ } else {
+ mActivityViewCallback.onSurfaceDestroyed(ActivityView.this);
+ }
+ }
+ }
+ });
+ }
+ }
+ });
}
/**
@@ -308,10 +370,8 @@
+ height);
mWidth = width;
mHeight = height;
- attachToSurfaceWhenReady();
- if (mActivityViewCallback != null) {
- mActivityViewCallback.onSurfaceAvailable(ActivityView.this);
- }
+ mSurface = new Surface(surfaceTexture);
+ setSurfaceAsync(mSurface, mWidth, mHeight, mDensityDpi, true);
}
@Override
@@ -331,15 +391,7 @@
if (DEBUG) Log.d(TAG, "onSurfaceTextureDestroyed");
mSurface.release();
mSurface = null;
- try {
- mActivityContainer.setSurface(null, mWidth, mHeight, mMetrics.densityDpi);
- } catch (RemoteException e) {
- throw new RuntimeException(
- "ActivityView: Unable to set surface of ActivityContainer. " + e);
- }
- if (mActivityViewCallback != null) {
- mActivityViewCallback.onSurfaceDestroyed(ActivityView.this);
- }
+ setSurfaceAsync(null, mWidth, mHeight, mDensityDpi, true);
return true;
}
diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java
index fb03b62..bf2e13a 100644
--- a/core/java/android/app/AlarmManager.java
+++ b/core/java/android/app/AlarmManager.java
@@ -21,15 +21,21 @@
import android.content.Context;
import android.content.Intent;
import android.os.Build;
+import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.os.WorkSource;
import android.text.TextUtils;
+import android.util.Log;
+
import libcore.util.ZoneInfoDB;
import java.io.IOException;
+import java.lang.ref.WeakReference;
+import java.util.WeakHashMap;
/**
* This class provides access to the system alarm services. These allow you
@@ -72,6 +78,8 @@
* Context.getSystemService(Context.ALARM_SERVICE)}.
*/
public class AlarmManager {
+ private static final String TAG = "AlarmManager";
+
/**
* Alarm time in {@link System#currentTimeMillis System.currentTimeMillis()}
* (wall clock time in UTC), which will wake up the device when
@@ -160,9 +168,89 @@
public static final int FLAG_IDLE_UNTIL = 1<<4;
private final IAlarmManager mService;
+ private final String mPackageName;
private final boolean mAlwaysExact;
private final int mTargetSdkVersion;
+ private final Handler mMainThreadHandler;
+ /**
+ * Direct-notification alarms: the requester must be running continuously from the
+ * time the alarm is set to the time it is delivered, or delivery will fail. Only
+ * one-shot alarms can be set using this mechanism, not repeating alarms.
+ */
+ public interface OnAlarmListener {
+ /**
+ * Callback method that is invoked by the system when the alarm time is reached.
+ */
+ public void onAlarm();
+ }
+
+ final class ListenerWrapper extends IAlarmListener.Stub implements Runnable {
+ final OnAlarmListener mListener;
+ Handler mHandler;
+ IAlarmCompleteListener mCompletion;
+
+ public ListenerWrapper(OnAlarmListener listener) {
+ mListener = listener;
+ }
+
+ public void setHandler(Handler h) {
+ mHandler = h;
+ }
+
+ public void cancel() {
+ try {
+ mService.remove(null, this);
+ } catch (RemoteException ex) {
+ }
+
+ synchronized (AlarmManager.class) {
+ if (sWrappers != null) {
+ sWrappers.remove(mListener);
+ }
+ }
+ }
+
+ @Override
+ public void doAlarm(IAlarmCompleteListener alarmManager) {
+ mCompletion = alarmManager;
+ mHandler.post(this);
+ }
+
+ @Override
+ public void run() {
+ // Remove this listener from the wrapper cache first; the server side
+ // already considers it gone
+ synchronized (AlarmManager.class) {
+ if (sWrappers != null) {
+ sWrappers.remove(mListener);
+ }
+ }
+
+ // Now deliver it to the app
+ try {
+ mListener.onAlarm();
+ } finally {
+ // No catch -- make sure to report completion to the system process,
+ // but continue to allow the exception to crash the app.
+
+ try {
+ mCompletion.alarmComplete(this);
+ } catch (Exception e) {
+ Log.e(TAG, "Unable to report completion to Alarm Manager!", e);
+ }
+ }
+ }
+ }
+
+ // Tracking of the OnAlarmListener -> wrapper mapping, for cancel() support.
+ // Access is synchronized on the AlarmManager class object.
+ //
+ // These are weak references so that we don't leak listener references if, for
+ // example, the pending-alarm messages are posted to a HandlerThread that is
+ // disposed of prior to alarm delivery. The underlying messages will be GC'd
+ // but this static reference would still persist, orphaned, never deallocated.
+ private static WeakHashMap<OnAlarmListener, WeakReference<ListenerWrapper>> sWrappers;
/**
* package private on purpose
@@ -170,8 +258,10 @@
AlarmManager(IAlarmManager service, Context ctx) {
mService = service;
+ mPackageName = ctx.getPackageName();
mTargetSdkVersion = ctx.getApplicationInfo().targetSdkVersion;
mAlwaysExact = (mTargetSdkVersion < Build.VERSION_CODES.KITKAT);
+ mMainThreadHandler = new Handler(ctx.getMainLooper());
}
private long legacyExactLength() {
@@ -249,7 +339,37 @@
* @see #RTC_WAKEUP
*/
public void set(int type, long triggerAtMillis, PendingIntent operation) {
- setImpl(type, triggerAtMillis, legacyExactLength(), 0, 0, operation, null, null);
+ setImpl(type, triggerAtMillis, legacyExactLength(), 0, 0, operation, null, null,
+ null, null, null);
+ }
+
+ /**
+ * Direct callback version of {@link #set(int, long, PendingIntent)}. Rather than
+ * supplying a PendingIntent to be sent when the alarm time is reached, this variant
+ * supplies an {@link OnAlarmListener} instance that will be invoked at that time.
+ * <p>
+ * The OnAlarmListener's {@link OnAlarmListener#onAlarm() onAlarm()} method will be
+ * invoked via the specified target Handler, or on the application's main looper
+ * if {@code null} is passed as the {@code targetHandler} parameter.
+ *
+ * @param type One of {@link #ELAPSED_REALTIME}, {@link #ELAPSED_REALTIME_WAKEUP},
+ * {@link #RTC}, or {@link #RTC_WAKEUP}.
+ * @param triggerAtMillis time in milliseconds that the alarm should go
+ * off, using the appropriate clock (depending on the alarm type).
+ * @param tag string describing the alarm, used for logging and battery-use
+ * attribution
+ * @param listener {@link OnAlarmListener} instance whose
+ * {@link OnAlarmListener#onAlarm() onAlarm()} method will be
+ * called when the alarm time is reached. A given OnAlarmListener instance can
+ * only be the target of a single pending alarm, just as a given PendingIntent
+ * can only be used with one alarm at a time.
+ * @param targetHandler {@link Handler} on which to execute the listener's onAlarm()
+ * callback, or {@code null} to run that callback on the main looper.
+ */
+ public void set(int type, long triggerAtMillis, String tag, OnAlarmListener listener,
+ Handler targetHandler) {
+ setImpl(type, triggerAtMillis, legacyExactLength(), 0, 0, null, listener, tag,
+ targetHandler, null, null);
}
/**
@@ -310,8 +430,8 @@
*/
public void setRepeating(int type, long triggerAtMillis,
long intervalMillis, PendingIntent operation) {
- setImpl(type, triggerAtMillis, legacyExactLength(), intervalMillis, 0, operation, null,
- null);
+ setImpl(type, triggerAtMillis, legacyExactLength(), intervalMillis, 0, operation,
+ null, null, null, null, null);
}
/**
@@ -361,7 +481,23 @@
*/
public void setWindow(int type, long windowStartMillis, long windowLengthMillis,
PendingIntent operation) {
- setImpl(type, windowStartMillis, windowLengthMillis, 0, 0, operation, null, null);
+ setImpl(type, windowStartMillis, windowLengthMillis, 0, 0, operation,
+ null, null, null, null, null);
+ }
+
+ /**
+ * Direct callback version of {@link #setWindow(int, long, long, PendingIntent)}. Rather
+ * than supplying a PendingIntent to be sent when the alarm time is reached, this variant
+ * supplies an {@link OnAlarmListener} instance that will be invoked at that time.
+ * <p>
+ * The OnAlarmListener {@link OnAlarmListener#onAlarm() onAlarm()} method will be
+ * invoked via the specified target Handler, or on the application's main looper
+ * if {@code null} is passed as the {@code targetHandler} parameter.
+ */
+ public void setWindow(int type, long windowStartMillis, long windowLengthMillis,
+ String tag, OnAlarmListener listener, Handler targetHandler) {
+ setImpl(type, windowStartMillis, windowLengthMillis, 0, 0, null, listener, tag,
+ targetHandler, null, null);
}
/**
@@ -399,7 +535,23 @@
* @see #RTC_WAKEUP
*/
public void setExact(int type, long triggerAtMillis, PendingIntent operation) {
- setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, 0, operation, null, null);
+ setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, 0, operation, null, null, null,
+ null, null);
+ }
+
+ /**
+ * Direct callback version of {@link #setExact(int, long, PendingIntent)}. Rather
+ * than supplying a PendingIntent to be sent when the alarm time is reached, this variant
+ * supplies an {@link OnAlarmListener} instance that will be invoked at that time.
+ * <p>
+ * The OnAlarmListener's {@link OnAlarmListener#onAlarm() onAlarm()} method will be
+ * invoked via the specified target Handler, or on the application's main looper
+ * if {@code null} is passed as the {@code targetHandler} parameter.
+ */
+ public void setExact(int type, long triggerAtMillis, String tag, OnAlarmListener listener,
+ Handler targetHandler) {
+ setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, 0, null, listener, tag,
+ targetHandler, null, null);
}
/**
@@ -408,7 +560,8 @@
* @hide
*/
public void setIdleUntil(int type, long triggerAtMillis, PendingIntent operation) {
- setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, FLAG_IDLE_UNTIL, operation, null, null);
+ setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, FLAG_IDLE_UNTIL, operation,
+ null, null, null, null, null);
}
/**
@@ -436,19 +589,38 @@
* @see android.content.Intent#filterEquals
*/
public void setAlarmClock(AlarmClockInfo info, PendingIntent operation) {
- setImpl(RTC_WAKEUP, info.getTriggerTime(), WINDOW_EXACT, 0, 0, operation, null, info);
+ setImpl(RTC_WAKEUP, info.getTriggerTime(), WINDOW_EXACT, 0, 0, operation,
+ null, null, null, null, info);
}
/** @hide */
@SystemApi
public void set(int type, long triggerAtMillis, long windowMillis, long intervalMillis,
PendingIntent operation, WorkSource workSource) {
- setImpl(type, triggerAtMillis, windowMillis, intervalMillis, 0, operation, workSource,
- null);
+ setImpl(type, triggerAtMillis, windowMillis, intervalMillis, 0, operation, null, null,
+ null, workSource, null);
+ }
+
+ /**
+ * Direct callback version of {@link #set(int, long, long, long, PendingIntent, WorkSource)}.
+ * Note that repeating alarms must use the PendingIntent variant, not an OnAlarmListener.
+ * <p>
+ * The OnAlarmListener's {@link OnAlarmListener#onAlarm() onAlarm()} method will be
+ * invoked via the specified target Handler, or on the application's main looper
+ * if {@code null} is passed as the {@code targetHandler} parameter.
+ *
+ * @hide
+ */
+ @SystemApi
+ public void set(int type, long triggerAtMillis, long windowMillis, long intervalMillis,
+ OnAlarmListener listener, Handler targetHandler, WorkSource workSource) {
+ setImpl(type, triggerAtMillis, windowMillis, intervalMillis, 0, null, listener, null,
+ targetHandler, workSource, null);
}
private void setImpl(int type, long triggerAtMillis, long windowMillis, long intervalMillis,
- int flags, PendingIntent operation, WorkSource workSource, AlarmClockInfo alarmClock) {
+ int flags, PendingIntent operation, final OnAlarmListener listener, String listenerTag,
+ Handler targetHandler, WorkSource workSource, AlarmClockInfo alarmClock) {
if (triggerAtMillis < 0) {
/* NOTYET
if (mAlwaysExact) {
@@ -460,9 +632,30 @@
triggerAtMillis = 0;
}
+ ListenerWrapper recipientWrapper = null;
+ if (listener != null) {
+ synchronized (AlarmManager.class) {
+ if (sWrappers == null) {
+ sWrappers = new WeakHashMap<OnAlarmListener, WeakReference<ListenerWrapper>>();
+ }
+
+ WeakReference<ListenerWrapper> wrapperRef = sWrappers.get(listener);
+ // no existing wrapper *or* we've lost our weak ref to it => build a new one
+ if (wrapperRef == null ||
+ (recipientWrapper = wrapperRef.get()) == null) {
+ recipientWrapper = new ListenerWrapper(listener);
+ wrapperRef = new WeakReference<ListenerWrapper>(recipientWrapper);
+ sWrappers.put(listener, wrapperRef);
+ }
+ }
+
+ final Handler handler = (targetHandler != null) ? targetHandler : mMainThreadHandler;
+ recipientWrapper.setHandler(handler);
+ }
+
try {
- mService.set(type, triggerAtMillis, windowMillis, intervalMillis, flags, operation,
- workSource, alarmClock);
+ mService.set(mPackageName, type, triggerAtMillis, windowMillis, intervalMillis, flags,
+ operation, recipientWrapper, listenerTag, workSource, alarmClock);
} catch (RemoteException ex) {
}
}
@@ -562,7 +755,8 @@
*/
public void setInexactRepeating(int type, long triggerAtMillis,
long intervalMillis, PendingIntent operation) {
- setImpl(type, triggerAtMillis, WINDOW_HEURISTIC, intervalMillis, 0, operation, null, null);
+ setImpl(type, triggerAtMillis, WINDOW_HEURISTIC, intervalMillis, 0, operation, null,
+ null, null, null, null);
}
/**
@@ -611,8 +805,8 @@
* @see #RTC_WAKEUP
*/
public void setAndAllowWhileIdle(int type, long triggerAtMillis, PendingIntent operation) {
- setImpl(type, triggerAtMillis, WINDOW_HEURISTIC, 0, FLAG_ALLOW_WHILE_IDLE, operation,
- null, null);
+ setImpl(type, triggerAtMillis, WINDOW_HEURISTIC, 0, FLAG_ALLOW_WHILE_IDLE,
+ operation, null, null, null, null, null);
}
/**
@@ -666,7 +860,7 @@
*/
public void setExactAndAllowWhileIdle(int type, long triggerAtMillis, PendingIntent operation) {
setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, FLAG_ALLOW_WHILE_IDLE, operation,
- null, null);
+ null, null, null, null, null);
}
/**
@@ -680,13 +874,44 @@
* @see #set
*/
public void cancel(PendingIntent operation) {
+ if (operation == null) {
+ throw new NullPointerException("operation");
+ }
+
try {
- mService.remove(operation);
+ mService.remove(operation, null);
} catch (RemoteException ex) {
}
}
/**
+ * Remove any alarm scheduled to be delivered to the given {@link OnAlarmListener}.
+ *
+ * @param listener OnAlarmListener instance that is the target of a currently-set alarm.
+ */
+ public void cancel(OnAlarmListener listener) {
+ if (listener == null) {
+ throw new NullPointerException("listener");
+ }
+
+ ListenerWrapper wrapper = null;
+ synchronized (AlarmManager.class) {
+ final WeakReference<ListenerWrapper> wrapperRef;
+ wrapperRef = sWrappers.get(listener);
+ if (wrapperRef != null) {
+ wrapper = wrapperRef.get();
+ }
+ }
+
+ if (wrapper == null) {
+ Log.w(TAG, "Unrecognized alarm listener " + listener);
+ return;
+ }
+
+ wrapper.cancel();
+ }
+
+ /**
* Set the system wall clock time.
* Requires the permission android.permission.SET_TIME.
*
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 09c0a6e..77a9795 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -653,7 +653,7 @@
null, //WRITE_SETTINGS
UserManager.DISALLOW_CREATE_WINDOWS, //SYSTEM_ALERT_WINDOW
null, //ACCESS_NOTIFICATIONS
- null, //CAMERA
+ UserManager.DISALLOW_CAMERA, //CAMERA
UserManager.DISALLOW_RECORD_AUDIO, //RECORD_AUDIO
null, //PLAY_AUDIO
null, //READ_CLIPBOARD
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index bead625..bfd9ca5 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -294,7 +294,6 @@
IUiAutomationConnection.Stub.asInterface(binder);
int testMode = data.readInt();
boolean enableBinderTracking = data.readInt() != 0;
- boolean openGlTrace = data.readInt() != 0;
boolean trackAllocation = data.readInt() != 0;
boolean restrictedBackupMode = (data.readInt() != 0);
boolean persistent = (data.readInt() != 0);
@@ -304,8 +303,8 @@
Bundle coreSettings = data.readBundle();
bindApplication(packageName, info, providers, testName, profilerInfo, testArgs,
testWatcher, uiAutomationConnection, testMode, enableBinderTracking,
- openGlTrace, trackAllocation, restrictedBackupMode, persistent, config,
- compatInfo, services, coreSettings);
+ trackAllocation, restrictedBackupMode, persistent, config, compatInfo, services,
+ coreSettings);
return true;
}
@@ -1020,10 +1019,9 @@
List<ProviderInfo> providers, ComponentName testName, ProfilerInfo profilerInfo,
Bundle testArgs, IInstrumentationWatcher testWatcher,
IUiAutomationConnection uiAutomationConnection, int debugMode,
- boolean enableBinderTracking, boolean openGlTrace, boolean trackAllocation,
- boolean restrictedBackupMode, boolean persistent, Configuration config,
- CompatibilityInfo compatInfo, Map<String, IBinder> services,
- Bundle coreSettings) throws RemoteException {
+ boolean enableBinderTracking, boolean trackAllocation, boolean restrictedBackupMode,
+ boolean persistent, Configuration config, CompatibilityInfo compatInfo,
+ Map<String, IBinder> services, Bundle coreSettings) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
data.writeString(packageName);
@@ -1046,7 +1044,6 @@
data.writeStrongInterface(uiAutomationConnection);
data.writeInt(debugMode);
data.writeInt(enableBinderTracking ? 1 : 0);
- data.writeInt(openGlTrace ? 1 : 0);
data.writeInt(trackAllocation ? 1 : 0);
data.writeInt(restrictedBackupMode ? 1 : 0);
data.writeInt(persistent ? 1 : 0);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index fca5567..bc7c3d0 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -443,6 +443,22 @@
}
@Override
+ public File getDeviceEncryptedFilesDir() {
+ if (mPackageInfo != null) {
+ return mPackageInfo.getDeviceEncryptedDataDirFile();
+ }
+ throw new RuntimeException("Not supported in system context");
+ }
+
+ @Override
+ public File getCredentialEncryptedFilesDir() {
+ if (mPackageInfo != null) {
+ return mPackageInfo.getCredentialEncryptedDataDirFile();
+ }
+ throw new RuntimeException("Not supported in system context");
+ }
+
+ @Override
public File getNoBackupFilesDir() {
synchronized (mSync) {
if (mNoBackupFilesDir == null) {
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index b44aab7..a73ad09 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -465,9 +465,6 @@
// If set this fragment is being retained across the current config change.
boolean mRetaining;
- // If set this fragment's loaders are being retained across the current config change.
- boolean mRetainLoader;
-
// If set this fragment has menu items to contribute.
boolean mHasMenu;
@@ -1413,7 +1410,8 @@
/**
* Called to do initial creation of a fragment. This is called after
* {@link #onAttach(Activity)} and before
- * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}.
+ * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}, but is not called if the fragment
+ * instance is retained across Activity re-creation (see {@link #setRetainInstance(boolean)}).
*
* <p>Note that this can be called while the fragment's activity is
* still in the process of being created. As such, you can not rely
@@ -1652,8 +1650,10 @@
}
/**
- * Called when the fragment is no longer attached to its activity. This
- * is called after {@link #onDestroy()}.
+ * Called when the fragment is no longer attached to its activity. This is called after
+ * {@link #onDestroy()}, except in the cases where the fragment instance is retained across
+ * Activity re-creation (see {@link #setRetainInstance(boolean)}), in which case it is called
+ * after {@link #onStop()}.
*/
public void onDetach() {
mCalled = true;
@@ -2415,7 +2415,7 @@
mLoaderManager = mHost.getLoaderManager(mWho, mLoadersStarted, false);
}
if (mLoaderManager != null) {
- if (mRetainLoader) {
+ if (mHost.getRetainLoaders()) {
mLoaderManager.doRetain();
} else {
mLoaderManager.doStop();
diff --git a/core/java/android/app/FragmentController.java b/core/java/android/app/FragmentController.java
index 1b45137..28dadfa 100644
--- a/core/java/android/app/FragmentController.java
+++ b/core/java/android/app/FragmentController.java
@@ -341,7 +341,6 @@
*/
public void doLoaderStop(boolean retain) {
mHost.doLoaderStop(retain);
- mHost.mFragmentManager.setRetainLoader(retain);
}
/**
diff --git a/core/java/android/app/FragmentHostCallback.java b/core/java/android/app/FragmentHostCallback.java
index 7b01307..13517e6 100644
--- a/core/java/android/app/FragmentHostCallback.java
+++ b/core/java/android/app/FragmentHostCallback.java
@@ -42,9 +42,14 @@
private final Handler mHandler;
final int mWindowAnimations;
final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl();
+ /** The loader managers for individual fragments [i.e. Fragment#getLoaderManager()] */
private ArrayMap<String, LoaderManager> mAllLoaderManagers;
+ /** Whether or not fragment loaders should retain their state */
+ private boolean mRetainLoaders;
+ /** The loader manger for the fragment host [i.e. Activity#getLoaderManager()] */
private LoaderManagerImpl mLoaderManager;
private boolean mCheckedForLoaderManager;
+ /** Whether or not the fragment host loader manager was started */
private boolean mLoadersStarted;
public FragmentHostCallback(Context context, Handler handler, int windowAnimations) {
@@ -166,6 +171,10 @@
return true;
}
+ boolean getRetainLoaders() {
+ return mRetainLoaders;
+ }
+
Activity getActivity() {
return mActivity;
}
@@ -217,6 +226,8 @@
}
void doLoaderStop(boolean retain) {
+ mRetainLoaders = retain;
+
if (mLoaderManager == null) {
return;
}
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 51d6132..696ccdb 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -869,17 +869,6 @@
}
}
- void setRetainLoader(boolean retain) {
- if (mActive != null) {
- for (int i=0; i<mActive.size(); i++) {
- Fragment f = mActive.get(i);
- if (f != null) {
- f.mRetainLoader = retain;
- }
- }
- }
- }
-
void moveToState(Fragment f, int newState, int transit, int transitionStyle,
boolean keepActive) {
if (DEBUG && false) Log.v(TAG, "moveToState: " + f
@@ -2221,6 +2210,7 @@
// This fragment was retained from a previous instance; get it
// going now.
fragment.mInLayout = true;
+ fragment.mHost = mHost;
// If this fragment is newly instantiated (either right now, or
// from last saved state), then give it the attributes to
// initialize itself.
diff --git a/core/java/android/app/FragmentTransaction.java b/core/java/android/app/FragmentTransaction.java
index 876c0f6..0249cb2 100644
--- a/core/java/android/app/FragmentTransaction.java
+++ b/core/java/android/app/FragmentTransaction.java
@@ -1,8 +1,16 @@
package android.app;
-import android.util.Pair;
+import android.annotation.AnimatorRes;
+import android.annotation.IdRes;
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.annotation.StringRes;
+import android.annotation.StyleRes;
import android.view.View;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* API for performing a set of Fragment operations.
*
@@ -21,7 +29,7 @@
/**
* Calls {@link #add(int, Fragment, String)} with a null tag.
*/
- public abstract FragmentTransaction add(int containerViewId, Fragment fragment);
+ public abstract FragmentTransaction add(@IdRes int containerViewId, Fragment fragment);
/**
* Add a fragment to the activity state. This fragment may optionally
@@ -38,12 +46,13 @@
*
* @return Returns the same FragmentTransaction instance.
*/
- public abstract FragmentTransaction add(int containerViewId, Fragment fragment, String tag);
+ public abstract FragmentTransaction add(@IdRes int containerViewId, Fragment fragment,
+ String tag);
/**
* Calls {@link #replace(int, Fragment, String)} with a null tag.
*/
- public abstract FragmentTransaction replace(int containerViewId, Fragment fragment);
+ public abstract FragmentTransaction replace(@IdRes int containerViewId, Fragment fragment);
/**
* Replace an existing fragment that was added to a container. This is
@@ -61,7 +70,8 @@
*
* @return Returns the same FragmentTransaction instance.
*/
- public abstract FragmentTransaction replace(int containerViewId, Fragment fragment, String tag);
+ public abstract FragmentTransaction replace(@IdRes int containerViewId, Fragment fragment,
+ String tag);
/**
* Remove an existing fragment. If it was added to a container, its view
@@ -148,12 +158,18 @@
* with it except that it is appearing or disappearing for some reason. */
public static final int TRANSIT_FRAGMENT_FADE = 3 | TRANSIT_ENTER_MASK;
+ /** @hide */
+ @IntDef({TRANSIT_NONE, TRANSIT_FRAGMENT_OPEN, TRANSIT_FRAGMENT_CLOSE, TRANSIT_FRAGMENT_FADE})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Transit {}
+
/**
* Set specific animation resources to run for the fragments that are
* entering and exiting in this transaction. These animations will not be
* played when popping the back stack.
*/
- public abstract FragmentTransaction setCustomAnimations(int enter, int exit);
+ public abstract FragmentTransaction setCustomAnimations(@AnimatorRes int enter,
+ @AnimatorRes int exit);
/**
* Set specific animation resources to run for the fragments that are
@@ -161,15 +177,15 @@
* and <code>popExit</code> animations will be played for enter/exit
* operations specifically when popping the back stack.
*/
- public abstract FragmentTransaction setCustomAnimations(int enter, int exit,
- int popEnter, int popExit);
+ public abstract FragmentTransaction setCustomAnimations(@AnimatorRes int enter,
+ @AnimatorRes int exit, @AnimatorRes int popEnter, @AnimatorRes int popExit);
/**
* Select a standard transition animation for this transaction. May be
* one of {@link #TRANSIT_NONE}, {@link #TRANSIT_FRAGMENT_OPEN},
- * or {@link #TRANSIT_FRAGMENT_CLOSE}
+ * {@link #TRANSIT_FRAGMENT_CLOSE}, or {@link #TRANSIT_FRAGMENT_FADE}.
*/
- public abstract FragmentTransaction setTransition(int transit);
+ public abstract FragmentTransaction setTransition(@Transit int transit);
/**
* Used with to map a View from a removed or hidden Fragment to a View from a shown
@@ -185,7 +201,7 @@
* Set a custom style resource that will be used for resolving transit
* animations.
*/
- public abstract FragmentTransaction setTransitionStyle(int styleRes);
+ public abstract FragmentTransaction setTransitionStyle(@StyleRes int styleRes);
/**
* Add this transaction to the back stack. This means that the transaction
@@ -194,7 +210,7 @@
*
* @param name An optional name for this back stack state, or null.
*/
- public abstract FragmentTransaction addToBackStack(String name);
+ public abstract FragmentTransaction addToBackStack(@Nullable String name);
/**
* Returns true if this FragmentTransaction is allowed to be added to the back
@@ -218,7 +234,7 @@
*
* @param res A string resource containing the title.
*/
- public abstract FragmentTransaction setBreadCrumbTitle(int res);
+ public abstract FragmentTransaction setBreadCrumbTitle(@StringRes int res);
/**
* Like {@link #setBreadCrumbTitle(int)} but taking a raw string; this
@@ -233,7 +249,7 @@
*
* @param res A string resource containing the title.
*/
- public abstract FragmentTransaction setBreadCrumbShortTitle(int res);
+ public abstract FragmentTransaction setBreadCrumbShortTitle(@StringRes int res);
/**
* Like {@link #setBreadCrumbShortTitle(int)} but taking a raw string; this
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index c26a44c..b69a480 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -123,7 +123,8 @@
public void activitySlept(IBinder token) throws RemoteException;
public void activityDestroyed(IBinder token) throws RemoteException;
public void reportSizeConfigurations(IBinder token, int[] horizontalSizeConfiguration,
- int[] verticalSizeConfigurations) throws RemoteException;
+ int[] verticalSizeConfigurations, int[] smallestWidthConfigurations)
+ throws RemoteException;
public String getCallingPackage(IBinder token) throws RemoteException;
public ComponentName getCallingActivity(IBinder token) throws RemoteException;
public List<IAppTask> getAppTasks(String callingPackage) throws RemoteException;
@@ -143,6 +144,7 @@
public void moveTaskToStack(int taskId, int stackId, boolean toTop) throws RemoteException;
public void moveTaskToDockedStack(int taskId, int createMode, boolean toTop)
throws RemoteException;
+ public boolean moveTopActivityToPinnedStack(int stackId, Rect bounds) throws RemoteException;
public void resizeStack(int stackId, Rect bounds, boolean allowResizeInDockedMode) throws RemoteException;
public void positionTaskInStack(int taskId, int stackId, int position) throws RemoteException;
public List<StackInfo> getAllStackInfos() throws RemoteException;
@@ -390,7 +392,7 @@
public boolean startUserInBackground(int userid) throws RemoteException;
public int stopUser(int userid, IStopUserCallback callback) throws RemoteException;
public UserInfo getCurrentUser() throws RemoteException;
- public boolean isUserRunning(int userid, boolean orStopping) throws RemoteException;
+ public boolean isUserRunning(int userid, int flags) throws RemoteException;
public int[] getRunningUserIds() throws RemoteException;
public boolean removeTask(int taskId) throws RemoteException;
@@ -898,4 +900,5 @@
int MOVE_TASK_TO_DOCKED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 346;
int SUPPRESS_RESIZE_CONFIG_CHANGES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 347;
int REMOVE_STACK = IBinder.FIRST_CALL_TRANSACTION + 348;
+ int MOVE_TOP_ACTIVITY_TO_PINNED_STACK = IBinder.FIRST_CALL_TRANSACTION + 349;
}
diff --git a/core/java/android/app/IAlarmCompleteListener.aidl b/core/java/android/app/IAlarmCompleteListener.aidl
new file mode 100644
index 0000000..9f9ee40
--- /dev/null
+++ b/core/java/android/app/IAlarmCompleteListener.aidl
@@ -0,0 +1,27 @@
+/*
+** Copyright 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 android.app;
+
+import android.os.IBinder;
+
+/**
+ * Callback from app into system process to indicate that processing of
+ * a direct-call alarm has completed.
+ * {@hide}
+ */
+interface IAlarmCompleteListener {
+ void alarmComplete(in IBinder who);
+}
diff --git a/core/java/android/app/IAlarmListener.aidl b/core/java/android/app/IAlarmListener.aidl
new file mode 100644
index 0000000..a110d4d
--- /dev/null
+++ b/core/java/android/app/IAlarmListener.aidl
@@ -0,0 +1,29 @@
+/*
+** Copyright 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 android.app;
+
+import android.app.IAlarmCompleteListener;
+
+/**
+ * System private API for direct alarm callbacks (non-broadcast deliver). See the
+ * AlarmManager#set() variants that take an AlarmReceiver callback object
+ * rather than a PendingIntent.
+ *
+ * {@hide}
+ */
+oneway interface IAlarmListener {
+ void doAlarm(in IAlarmCompleteListener callback);
+}
diff --git a/core/java/android/app/IAlarmManager.aidl b/core/java/android/app/IAlarmManager.aidl
index 327c00b..7b05b49 100644
--- a/core/java/android/app/IAlarmManager.aidl
+++ b/core/java/android/app/IAlarmManager.aidl
@@ -17,7 +17,9 @@
package android.app;
import android.app.AlarmManager;
+import android.app.IAlarmListener;
import android.app.PendingIntent;
+import android.content.Intent;
import android.os.WorkSource;
/**
@@ -27,14 +29,12 @@
*/
interface IAlarmManager {
/** windowLength == 0 means exact; windowLength < 0 means the let the OS decide */
- void set(int type, long triggerAtTime, long windowLength,
- long interval, int flags, in PendingIntent operation, in WorkSource workSource,
- in AlarmManager.AlarmClockInfo alarmClock);
+ void set(String callingPackage, int type, long triggerAtTime, long windowLength,
+ long interval, int flags, in PendingIntent operation, in IAlarmListener listener,
+ String listenerTag, in WorkSource workSource, in AlarmManager.AlarmClockInfo alarmClock);
boolean setTime(long millis);
void setTimeZone(String zone);
- void remove(in PendingIntent operation);
+ void remove(in PendingIntent operation, in IAlarmListener listener);
long getNextWakeFromIdleTime();
AlarmManager.AlarmClockInfo getNextAlarmClock(int userId);
}
-
-
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index 2d78e19..99e8853 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -96,10 +96,10 @@
void bindApplication(String packageName, ApplicationInfo info, List<ProviderInfo> providers,
ComponentName testName, ProfilerInfo profilerInfo, Bundle testArguments,
IInstrumentationWatcher testWatcher, IUiAutomationConnection uiAutomationConnection,
- int debugMode, boolean enableBinderTracking, boolean openGlTrace,
- boolean trackAllocation, boolean restrictedBackupMode, boolean persistent,
- Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services,
- Bundle coreSettings) throws RemoteException;
+ int debugMode, boolean enableBinderTracking, boolean trackAllocation,
+ boolean restrictedBackupMode, boolean persistent, Configuration config,
+ CompatibilityInfo compatInfo, Map<String, IBinder> services, Bundle coreSettings)
+ throws RemoteException;
void scheduleExit() throws RemoteException;
void scheduleSuicide() throws RemoteException;
void scheduleConfigurationChanged(Configuration config) throws RemoteException;
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index e95a35a..30232da 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -83,10 +83,8 @@
int getZenMode();
ZenModeConfig getZenModeConfig();
- boolean setZenModeConfig(in ZenModeConfig config, String reason);
oneway void setZenMode(int mode, in Uri conditionId, String reason);
oneway void notifyConditions(String pkg, in IConditionProvider provider, in Condition[] conditions);
- oneway void requestZenModeConditions(in IConditionListener callback, int relevance);
boolean isNotificationPolicyAccessGranted(String pkg);
NotificationManager.Policy getNotificationPolicy(String pkg);
void setNotificationPolicy(String pkg, in NotificationManager.Policy policy);
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index c2bf28a..891558f 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -31,6 +31,7 @@
import android.content.res.CompatibilityInfo;
import android.content.res.Resources;
import android.os.Bundle;
+import android.os.FileUtils;
import android.os.Handler;
import android.os.IBinder;
import android.os.Process;
@@ -93,6 +94,8 @@
private final String mDataDir;
private final String mLibDir;
private final File mDataDirFile;
+ private final File mDeviceEncryptedDataDirFile;
+ private final File mCredentialEncryptedDataDirFile;
private final ClassLoader mBaseClassLoader;
private final boolean mSecurityViolation;
private final boolean mIncludeCode;
@@ -139,7 +142,9 @@
mOverlayDirs = aInfo.resourceDirs;
mSharedLibraries = aInfo.sharedLibraryFiles;
mDataDir = aInfo.dataDir;
- mDataDirFile = mDataDir != null ? new File(mDataDir) : null;
+ mDataDirFile = FileUtils.newFileOrNull(mDataDir);
+ mDeviceEncryptedDataDirFile = FileUtils.newFileOrNull(aInfo.deviceEncryptedDataDir);
+ mCredentialEncryptedDataDirFile = FileUtils.newFileOrNull(aInfo.credentialEncryptedDataDir);
mLibDir = aInfo.nativeLibraryDir;
mBaseClassLoader = baseLoader;
mSecurityViolation = securityViolation;
@@ -192,6 +197,8 @@
mSharedLibraries = null;
mDataDir = null;
mDataDirFile = null;
+ mDeviceEncryptedDataDirFile = null;
+ mCredentialEncryptedDataDirFile = null;
mLibDir = null;
mBaseClassLoader = null;
mSecurityViolation = false;
@@ -268,7 +275,7 @@
if (!Objects.equals(mPackageName, ActivityThread.currentPackageName())) {
final String isa = VMRuntime.getRuntime().vmInstructionSet();
try {
- ActivityThread.getPackageManager().performDexOptIfNeeded(mPackageName, isa);
+ ActivityThread.getPackageManager().notifyPackageUse(mPackageName);
} catch (RemoteException re) {
// Ignored.
}
@@ -539,6 +546,14 @@
return mDataDirFile;
}
+ public File getDeviceEncryptedDataDirFile() {
+ return mDeviceEncryptedDataDirFile;
+ }
+
+ public File getCredentialEncryptedDataDirFile() {
+ return mCredentialEncryptedDataDirFile;
+ }
+
public AssetManager getAssets(ActivityThread mainThread) {
return getResources(mainThread).getAssets();
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 55aec59..0b77be3 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -62,6 +62,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
/**
* A class that represents how a persistent notification is to be presented to
@@ -250,15 +251,26 @@
public RemoteViews tickerView;
/**
- * The view that will represent this notification in the expanded status bar.
+ * The view that will represent this notification in the notification list (which is pulled
+ * down from the status bar).
+ *
+ * As of N, this field is not used. The notification view is determined by the inputs to
+ * {@link Notification.Builder}; a custom RemoteViews can optionally be
+ * supplied with {@link Notification.Builder#setCustomContentView(RemoteViews)}.
*/
+ @Deprecated
public RemoteViews contentView;
/**
* A large-format version of {@link #contentView}, giving the Notification an
* opportunity to show more detail. The system UI may choose to show this
* instead of the normal content view at its discretion.
+ *
+ * As of N, this field is not used. The expanded notification view is determined by the
+ * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
+ * supplied with {@link Notification.Builder#setCustomBigContentView(RemoteViews)}.
*/
+ @Deprecated
public RemoteViews bigContentView;
@@ -267,7 +279,12 @@
* opportunity to add action buttons to contentView. At its discretion, the system UI may
* choose to show this as a heads-up notification, which will pop up so the user can see
* it without leaving their current activity.
+ *
+ * As of N, this field is not used. The heads-up notification view is determined by the
+ * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
+ * supplied with {@link Notification.Builder#setCustomHeadsUpContentView(RemoteViews)}.
*/
+ @Deprecated
public RemoteViews headsUpContentView;
/**
@@ -866,6 +883,11 @@
public static final String EXTRA_ORIGINATING_USERID = "android.originatingUserId";
/**
+ * @hide
+ */
+ public static final String EXTRA_BUILDER_APPLICATION_INFO = "android.appInfo";
+
+ /**
* Value for {@link #EXTRA_AS_HEADS_UP} that indicates this notification should not be
* displayed in the heads up space.
*
@@ -1362,6 +1384,95 @@
public Notification publicVersion;
/**
+ * Structure to encapsulate a topic that is shown in Notification settings.
+ * It must include an id and label.
+ */
+ public static class Topic implements Parcelable {
+ private final String id;
+ private final CharSequence label;
+
+ public Topic(String id, CharSequence label) {
+ this.id = id;
+ this.label = safeCharSequence(label);
+ }
+
+ private Topic(Parcel in) {
+ if (in.readInt() != 0) {
+ id = in.readString();
+ } else {
+ id = null;
+ }
+ label = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public CharSequence getLabel() {
+ return label;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder(Topic.class.getSimpleName()).append('[')
+ .append("id=").append(id)
+ .append(",label=").append(label)
+ .append(']').toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof Topic)) return false;
+ if (o == this) return true;
+ final Topic other = (Topic) o;
+ return Objects.equals(other.id, id)
+ && Objects.equals(other.label, label);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, label);
+ }
+
+ @Override
+ public Topic clone() {
+ return new Topic(id, label);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ if (id != null) {
+ out.writeInt(1);
+ out.writeString(id);
+ } else {
+ out.writeInt(0);
+ }
+ TextUtils.writeToParcel(label, out, flags);
+ }
+ public static final Parcelable.Creator<Topic> CREATOR =
+ new Parcelable.Creator<Topic>() {
+ public Topic createFromParcel(Parcel in) {
+ return new Topic(in);
+ }
+ public Topic[] newArray(int size) {
+ return new Topic[size];
+ }
+ };
+ }
+
+ private Topic[] topics;
+
+ public Topic[] getTopics() {
+ return topics;
+ }
+
+ /**
* Constructs a Notification object with default values.
* You might want to consider using {@link Builder} instead.
*/
@@ -1487,6 +1598,8 @@
}
color = parcel.readInt();
+
+ topics = parcel.createTypedArray(Topic.CREATOR); // may be null
}
@Override
@@ -1587,6 +1700,13 @@
that.color = this.color;
+ if (this.topics != null) {
+ that.topics = new Topic[this.topics.length];
+ for(int i=0; i<this.topics.length; i++) {
+ that.topics[i] = this.topics[i].clone();
+ }
+ }
+
if (!heavy) {
that.lightenPayload(); // will clean out extras
}
@@ -1608,8 +1728,6 @@
extras.remove(Notification.EXTRA_LARGE_ICON_BIG);
extras.remove(Notification.EXTRA_PICTURE);
extras.remove(Notification.EXTRA_BIG_TEXT);
- // Prevent light notifications from being rebuilt.
- extras.remove(Builder.EXTRA_NEEDS_REBUILD);
}
}
@@ -1759,6 +1877,8 @@
}
parcel.writeInt(color);
+
+ parcel.writeTypedArray(topics, 0); // null ok
}
/**
@@ -1800,21 +1920,13 @@
@Deprecated
public void setLatestEventInfo(Context context,
CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
- Notification.Builder builder = new Notification.Builder(context);
+ if (context.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1){
+ Log.e(TAG, "setLatestEventInfo() is deprecated and you should feel deprecated.",
+ new Throwable());
+ }
- // First, ensure that key pieces of information that may have been set directly
- // are preserved
- builder.setWhen(this.when);
- builder.setSmallIcon(this.icon);
- builder.setPriority(this.priority);
- builder.setTicker(this.tickerText);
- builder.setNumber(this.number);
- builder.setColor(this.color);
- builder.mFlags = this.flags;
- builder.setSound(this.sound, this.audioStreamType);
- builder.setDefaults(this.defaults);
- builder.setVibrate(this.vibrate);
- builder.setDeleteIntent(this.deleteIntent);
+ // ensure that any information already set directly is preserved
+ final Notification.Builder builder = new Notification.Builder(context, this);
// now apply the latestEventInfo fields
if (contentTitle != null) {
@@ -1824,7 +1936,8 @@
builder.setContentText(contentText);
}
builder.setContentIntent(contentIntent);
- builder.buildInto(this);
+
+ builder.build(); // callers expect this notification to be ready to use
}
@Override
@@ -1895,6 +2008,18 @@
sb.append(" publicVersion=");
sb.append(publicVersion.toString());
}
+ if (topics != null) {
+ sb.append("topics=[");
+ int N = topics.length;
+ if (N > 0) {
+ for (int i = 0; i < N-1; i++) {
+ sb.append(topics[i]);
+ sb.append(',');
+ }
+ sb.append(topics[N-1]);
+ }
+ sb.append("]");
+ }
sb.append(")");
return sb.toString();
}
@@ -1967,15 +2092,6 @@
/**
* @hide
*/
- public boolean isValid() {
- // Would like to check for icon!=0 here, too, but NotificationManagerService accepts that
- // for legacy reasons.
- return contentView != null || extras.getBoolean(Builder.EXTRA_REBUILD_CONTENT_VIEW);
- }
-
- /**
- * @hide
- */
public boolean isGroupSummary() {
return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) != 0;
}
@@ -2012,99 +2128,15 @@
private static final int MAX_ACTION_BUTTONS = 3;
private static final float LARGE_TEXT_SCALE = 1.3f;
- /**
- * @hide
- */
- public static final String EXTRA_NEEDS_REBUILD = "android.rebuild";
-
- /**
- * @hide
- */
- public static final String EXTRA_REBUILD_LARGE_ICON = "android.rebuild.largeIcon";
- /**
- * @hide
- */
- public static final String EXTRA_REBUILD_CONTENT_VIEW = "android.rebuild.contentView";
- /**
- * @hide
- */
- public static final String EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT =
- "android.rebuild.contentViewActionCount";
- /**
- * @hide
- */
- public static final String EXTRA_REBUILD_BIG_CONTENT_VIEW
- = "android.rebuild.bigView";
- /**
- * @hide
- */
- public static final String EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT
- = "android.rebuild.bigViewActionCount";
- /**
- * @hide
- */
- public static final String EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW
- = "android.rebuild.hudView";
- /**
- * @hide
- */
- public static final String EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT
- = "android.rebuild.hudViewActionCount";
-
- /**
- * The ApplicationInfo of the package that created the notification, used to create
- * a context to rebuild the notification via a Builder.
- * @hide
- */
- private static final String EXTRA_REBUILD_CONTEXT_APPLICATION_INFO =
- "android.rebuild.applicationInfo";
-
- // Whether to enable stripping (at post time) & rebuilding (at listener receive time) of
- // memory intensive resources.
- private static final boolean STRIP_AND_REBUILD = true;
-
private Context mContext;
-
- private long mWhen;
- private Icon mSmallIcon, mLargeIcon;
- private int mSmallIconLevel;
- private int mNumber;
- private CharSequence mContentTitle;
- private CharSequence mContentText;
- private CharSequence mContentInfo;
- private CharSequence mSubText;
- private PendingIntent mContentIntent;
- private RemoteViews mContentView;
- private PendingIntent mDeleteIntent;
- private PendingIntent mFullScreenIntent;
- private CharSequence mTickerText;
- private RemoteViews mTickerView;
- private Uri mSound;
- private int mAudioStreamType;
- private AudioAttributes mAudioAttributes;
- private long[] mVibrate;
- private int mLedArgb;
- private int mLedOnMs;
- private int mLedOffMs;
- private int mDefaults;
- private int mFlags;
- private int mProgressMax;
- private int mProgress;
- private boolean mProgressIndeterminate;
- private String mCategory;
- private String mGroupKey;
- private String mSortKey;
- private Bundle mExtras;
- private int mPriority;
- private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
- private boolean mUseChronometer;
+ private Notification mN;
+ private Bundle mUserExtras = new Bundle();
private Style mStyle;
- private boolean mShowWhen = true;
- private int mVisibility = VISIBILITY_PRIVATE;
- private Notification mPublicVersion = null;
- private final NotificationColorUtil mColorUtil;
- private ArrayList<String> mPeople;
- private int mColor = COLOR_DEFAULT;
+ private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
+ private ArrayList<String> mPersonList = new ArrayList<String>();
+ private NotificationColorUtil mColorUtil;
+ private boolean mColorUtilInited = false;
+ private List<Topic> mTopics = new ArrayList<>();
/**
* The user that built the notification originally.
@@ -2112,25 +2144,6 @@
private int mOriginatingUserId;
/**
- * Contains extras related to rebuilding during the build phase.
- */
- private Bundle mRebuildBundle = new Bundle();
- /**
- * Contains the notification to rebuild when this Builder is in "rebuild" mode.
- * Null otherwise.
- */
- private Notification mRebuildNotification = null;
-
- /**
- * Whether the build notification has three lines. This is used to make the top padding for
- * both the contracted and expanded layout consistent.
- *
- * <p>
- * This field is only valid during the build phase.
- */
- private boolean mHasThreeLines;
-
- /**
* Constructs a new Builder with the defaults:
*
@@ -2150,61 +2163,67 @@
* object.
*/
public Builder(Context context) {
- /*
- * Important compatibility note!
- * Some apps out in the wild create a Notification.Builder in their Activity subclass
- * constructor for later use. At this point Activities - themselves subclasses of
- * ContextWrapper - do not have their inner Context populated yet. This means that
- * any calls to Context methods from within this constructor can cause NPEs in existing
- * apps. Any data populated from mContext should therefore be populated lazily to
- * preserve compatibility.
- */
- mContext = context;
-
- // Set defaults to match the defaults of a Notification
- mWhen = System.currentTimeMillis();
- mAudioStreamType = STREAM_DEFAULT;
- mAudioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
- mPriority = PRIORITY_DEFAULT;
- mPeople = new ArrayList<String>();
-
- mColorUtil = context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.LOLLIPOP ?
- NotificationColorUtil.getInstance(mContext) : null;
+ this(context, null);
}
/**
- * Creates a Builder for rebuilding the given Notification.
- * <p>
- * Call {@link #rebuild()} to retrieve the rebuilt version of 'n'.
+ * @hide
*/
- private Builder(Context context, Notification n) {
- this(context);
- mRebuildNotification = n;
- restoreFromNotification(n);
+ public Builder(Context context, Notification toAdopt) {
+ mContext = context;
- Style style = null;
- Bundle extras = n.extras;
- String templateClass = extras.getString(EXTRA_TEMPLATE);
- if (!TextUtils.isEmpty(templateClass)) {
- Class<? extends Style> styleClass = getNotificationStyleClass(templateClass);
- if (styleClass == null) {
- Log.d(TAG, "Unknown style class: " + styleClass);
- return;
+ if (toAdopt == null) {
+ mN = new Notification();
+ mN.extras.putBoolean(EXTRA_SHOW_WHEN, true);
+ mN.priority = PRIORITY_DEFAULT;
+ mN.visibility = VISIBILITY_PRIVATE;
+ } else {
+ mN = toAdopt;
+ if (mN.actions != null) {
+ Collections.addAll(mActions, mN.actions);
}
- try {
- Constructor<? extends Style> constructor = styleClass.getConstructor();
- constructor.setAccessible(true);
- style = constructor.newInstance();
- style.restoreFromExtras(extras);
- } catch (Throwable t) {
- Log.e(TAG, "Could not create Style", t);
- return;
+ if (mN.extras.containsKey(EXTRA_PEOPLE)) {
+ Collections.addAll(mPersonList, mN.extras.getStringArray(EXTRA_PEOPLE));
+ }
+
+ if (mN.getTopics() != null) {
+ Collections.addAll(mTopics, mN.getTopics());
+ }
+
+ String templateClass = mN.extras.getString(EXTRA_TEMPLATE);
+ if (!TextUtils.isEmpty(templateClass)) {
+ final Class<? extends Style> styleClass
+ = getNotificationStyleClass(templateClass);
+ if (styleClass == null) {
+ Log.d(TAG, "Unknown style class: " + templateClass);
+ } else {
+ try {
+ final Constructor<? extends Style> ctor = styleClass.getConstructor();
+ ctor.setAccessible(true);
+ final Style style = ctor.newInstance();
+ style.restoreFromExtras(mN.extras);
+
+ if (style != null) {
+ setStyle(style);
+ }
+ } catch (Throwable t) {
+ Log.e(TAG, "Could not create Style", t);
+ }
+ }
+ }
+
+ }
+ }
+
+ private NotificationColorUtil getColorUtil() {
+ if (!mColorUtilInited) {
+ mColorUtilInited = true;
+ if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {
+ mColorUtil = NotificationColorUtil.getInstance(mContext);
}
}
- if (style != null) {
- setStyle(style);
- }
+ return mColorUtil;
}
/**
@@ -2215,7 +2234,7 @@
* @see Notification#when
*/
public Builder setWhen(long when) {
- mWhen = when;
+ mN.when = when;
return this;
}
@@ -2224,7 +2243,7 @@
* in the content view.
*/
public Builder setShowWhen(boolean show) {
- mShowWhen = show;
+ mN.extras.putBoolean(EXTRA_SHOW_WHEN, show);
return this;
}
@@ -2240,7 +2259,7 @@
* @see Notification#when
*/
public Builder setUsesChronometer(boolean b) {
- mUseChronometer = b;
+ mN.extras.putBoolean(EXTRA_SHOW_CHRONOMETER, b);
return this;
}
@@ -2276,7 +2295,7 @@
* @see Notification#iconLevel
*/
public Builder setSmallIcon(@DrawableRes int icon, int level) {
- mSmallIconLevel = level;
+ mN.iconLevel = level;
return setSmallIcon(icon);
}
@@ -2289,7 +2308,10 @@
* @see Notification#icon
*/
public Builder setSmallIcon(Icon icon) {
- mSmallIcon = icon;
+ mN.setSmallIcon(icon);
+ if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
+ mN.icon = icon.getResId();
+ }
return this;
}
@@ -2297,7 +2319,7 @@
* Set the first line of text in the platform notification template.
*/
public Builder setContentTitle(CharSequence title) {
- mContentTitle = safeCharSequence(title);
+ mN.extras.putCharSequence(EXTRA_TITLE, safeCharSequence(title));
return this;
}
@@ -2305,7 +2327,7 @@
* Set the second line of text in the platform notification template.
*/
public Builder setContentText(CharSequence text) {
- mContentText = safeCharSequence(text);
+ mN.extras.putCharSequence(EXTRA_TEXT, safeCharSequence(text));
return this;
}
@@ -2315,7 +2337,7 @@
* same location in the standard template.
*/
public Builder setSubText(CharSequence text) {
- mSubText = safeCharSequence(text);
+ mN.extras.putCharSequence(EXTRA_SUB_TEXT, safeCharSequence(text));
return this;
}
@@ -2325,7 +2347,7 @@
* font size for readability.
*/
public Builder setNumber(int number) {
- mNumber = number;
+ mN.number = number;
return this;
}
@@ -2336,7 +2358,7 @@
* right (to the right of a smallIcon if it has been placed there).
*/
public Builder setContentInfo(CharSequence info) {
- mContentInfo = safeCharSequence(info);
+ mN.extras.putCharSequence(EXTRA_INFO_TEXT, safeCharSequence(info));
return this;
}
@@ -2346,19 +2368,52 @@
* The platform template will represent this using a {@link ProgressBar}.
*/
public Builder setProgress(int max, int progress, boolean indeterminate) {
- mProgressMax = max;
- mProgress = progress;
- mProgressIndeterminate = indeterminate;
+ mN.extras.putInt(EXTRA_PROGRESS, progress);
+ mN.extras.putInt(EXTRA_PROGRESS_MAX, max);
+ mN.extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, indeterminate);
return this;
}
/**
* Supply a custom RemoteViews to use instead of the platform template.
*
- * @see Notification#contentView
+ * Use {@link #setCustomContentView(RemoteViews)} instead.
*/
+ @Deprecated
public Builder setContent(RemoteViews views) {
- mContentView = views;
+ return setCustomContentView(views);
+ }
+
+ /**
+ * Supply custom RemoteViews to use instead of the platform template.
+ *
+ * This will override the layout that would otherwise be constructed by this Builder
+ * object.
+ */
+ public Builder setCustomContentView(RemoteViews contentView) {
+ mN.contentView = contentView;
+ return this;
+ }
+
+ /**
+ * Supply custom RemoteViews to use instead of the platform template in the expanded form.
+ *
+ * This will override the expanded layout that would otherwise be constructed by this
+ * Builder object.
+ */
+ public Builder setCustomBigContentView(RemoteViews contentView) {
+ mN.bigContentView = contentView;
+ return this;
+ }
+
+ /**
+ * Supply custom RemoteViews to use instead of the platform template in the heads up dialog.
+ *
+ * This will override the heads-up layout that would otherwise be constructed by this
+ * Builder object.
+ */
+ public Builder setCustomHeadsUpContentView(RemoteViews contentView) {
+ mN.headsUpContentView = contentView;
return this;
}
@@ -2374,7 +2429,7 @@
* @see Notification#contentIntent Notification.contentIntent
*/
public Builder setContentIntent(PendingIntent intent) {
- mContentIntent = intent;
+ mN.contentIntent = intent;
return this;
}
@@ -2384,7 +2439,7 @@
* @see Notification#deleteIntent
*/
public Builder setDeleteIntent(PendingIntent intent) {
- mDeleteIntent = intent;
+ mN.deleteIntent = intent;
return this;
}
@@ -2409,7 +2464,7 @@
* @see Notification#fullScreenIntent
*/
public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
- mFullScreenIntent = intent;
+ mN.fullScreenIntent = intent;
setFlag(FLAG_HIGH_PRIORITY, highPriority);
return this;
}
@@ -2420,7 +2475,7 @@
* @see Notification#tickerText
*/
public Builder setTicker(CharSequence tickerText) {
- mTickerText = safeCharSequence(tickerText);
+ mN.tickerText = safeCharSequence(tickerText);
return this;
}
@@ -2430,8 +2485,8 @@
*/
@Deprecated
public Builder setTicker(CharSequence tickerText, RemoteViews views) {
- mTickerText = safeCharSequence(tickerText);
- mTickerView = views; // we'll save it for you anyway
+ setTicker(tickerText);
+ // views is ignored
return this;
}
@@ -2454,7 +2509,8 @@
* badge atop the large icon).
*/
public Builder setLargeIcon(Icon icon) {
- mLargeIcon = icon;
+ mN.mLargeIcon = icon;
+ mN.extras.putParcelable(EXTRA_LARGE_ICON, icon);
return this;
}
@@ -2471,8 +2527,8 @@
* @see Notification#sound
*/
public Builder setSound(Uri sound) {
- mSound = sound;
- mAudioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
+ mN.sound = sound;
+ mN.audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
return this;
}
@@ -2489,8 +2545,8 @@
*/
@Deprecated
public Builder setSound(Uri sound, int streamType) {
- mSound = sound;
- mAudioStreamType = streamType;
+ mN.sound = sound;
+ mN.audioStreamType = streamType;
return this;
}
@@ -2505,8 +2561,8 @@
* @see Notification#sound
*/
public Builder setSound(Uri sound, AudioAttributes audioAttributes) {
- mSound = sound;
- mAudioAttributes = audioAttributes;
+ mN.sound = sound;
+ mN.audioAttributes = audioAttributes;
return this;
}
@@ -2523,7 +2579,7 @@
* @see Notification#vibrate
*/
public Builder setVibrate(long[] pattern) {
- mVibrate = pattern;
+ mN.vibrate = pattern;
return this;
}
@@ -2540,9 +2596,15 @@
* @see Notification#ledOffMS
*/
public Builder setLights(@ColorInt int argb, int onMs, int offMs) {
- mLedArgb = argb;
- mLedOnMs = onMs;
- mLedOffMs = offMs;
+ mN.ledARGB = argb;
+ mN.ledOnMS = onMs;
+ mN.ledOffMS = offMs;
+ if (onMs != 0 || offMs != 0) {
+ mN.flags |= FLAG_SHOW_LIGHTS;
+ }
+ if ((mN.defaults & DEFAULT_LIGHTS) != 0) {
+ mN.flags |= FLAG_SHOW_LIGHTS;
+ }
return this;
}
@@ -2610,7 +2672,7 @@
* For all default values, use {@link #DEFAULT_ALL}.
*/
public Builder setDefaults(int defaults) {
- mDefaults = defaults;
+ mN.defaults = defaults;
return this;
}
@@ -2620,7 +2682,7 @@
* @see Notification#priority
*/
public Builder setPriority(@Priority int pri) {
- mPriority = pri;
+ mN.priority = pri;
return this;
}
@@ -2630,7 +2692,7 @@
* @see Notification#category
*/
public Builder setCategory(String category) {
- mCategory = category;
+ mN.category = category;
return this;
}
@@ -2657,7 +2719,7 @@
* @see Notification#EXTRA_PEOPLE
*/
public Builder addPerson(String uri) {
- mPeople.add(uri);
+ mPersonList.add(uri);
return this;
}
@@ -2673,7 +2735,7 @@
* @return this object for method chaining
*/
public Builder setGroup(String groupKey) {
- mGroupKey = groupKey;
+ mN.mGroupKey = groupKey;
return this;
}
@@ -2702,7 +2764,7 @@
* @see String#compareTo(String)
*/
public Builder setSortKey(String sortKey) {
- mSortKey = sortKey;
+ mN.mSortKey = sortKey;
return this;
}
@@ -2715,11 +2777,7 @@
*/
public Builder addExtras(Bundle extras) {
if (extras != null) {
- if (mExtras == null) {
- mExtras = new Bundle(extras);
- } else {
- mExtras.putAll(extras);
- }
+ mUserExtras.putAll(extras);
}
return this;
}
@@ -2737,7 +2795,9 @@
* @see Notification#extras
*/
public Builder setExtras(Bundle extras) {
- mExtras = extras;
+ if (extras != null) {
+ mUserExtras = extras;
+ }
return this;
}
@@ -2752,10 +2812,13 @@
* @see Notification#extras
*/
public Bundle getExtras() {
- if (mExtras == null) {
- mExtras = new Bundle();
- }
- return mExtras;
+ return mUserExtras;
+ }
+
+ private Bundle getAllExtras() {
+ final Bundle saveExtras = (Bundle) mUserExtras.clone();
+ saveExtras.putAll(mN.extras);
+ return saveExtras;
}
/**
@@ -2804,6 +2867,21 @@
}
/**
+ * Alter the complete list of actions attached to this notification.
+ * @see #addAction(Action).
+ *
+ * @param actions
+ * @return
+ */
+ public Builder setActions(Action... actions) {
+ mActions.clear();
+ for (int i = 0; i < actions.length; i++) {
+ mActions.add(actions[i]);
+ }
+ return this;
+ }
+
+ /**
* Add a rich notification style to be applied at build time.
*
* @param style Object responsible for modifying the notification style.
@@ -2813,6 +2891,9 @@
mStyle = style;
if (mStyle != null) {
mStyle.setBuilder(this);
+ mN.extras.putString(EXTRA_TEMPLATE, style.getClass().getName());
+ } else {
+ mN.extras.remove(EXTRA_TEMPLATE);
}
}
return this;
@@ -2827,7 +2908,7 @@
* @return The same Builder.
*/
public Builder setVisibility(int visibility) {
- mVisibility = visibility;
+ mN.visibility = visibility;
return this;
}
@@ -2838,7 +2919,12 @@
* @return The same Builder.
*/
public Builder setPublicVersion(Notification n) {
- mPublicVersion = n;
+ if (n != null) {
+ mN.publicVersion = new Notification();
+ n.cloneInto(mN.publicVersion, /*heavy=*/ true);
+ } else {
+ mN.publicVersion = null;
+ }
return this;
}
@@ -2856,9 +2942,9 @@
*/
public void setFlag(int mask, boolean value) {
if (value) {
- mFlags |= mask;
+ mN.flags |= mask;
} else {
- mFlags &= ~mask;
+ mN.flags &= ~mask;
}
}
@@ -2870,7 +2956,21 @@
* @return The same Builder.
*/
public Builder setColor(@ColorInt int argb) {
- mColor = argb;
+ mN.color = argb;
+ sanitizeColor();
+ return this;
+ }
+
+ /**
+ * Add a topic to this notification. Topics are typically displayed in Notification
+ * settings.
+ * <p>
+ * Every topic must have an id and a textual label.
+ *
+ * @param topic The topic to add.
+ */
+ public Builder addTopic(Topic topic) {
+ mTopics.add(topic);
return this;
}
@@ -2948,7 +3048,6 @@
contentView.setViewVisibility(R.id.overflow_divider, View.GONE);
contentView.setViewVisibility(R.id.progress, View.GONE);
contentView.setViewVisibility(R.id.chronometer, View.GONE);
- contentView.setViewVisibility(R.id.time, View.GONE);
}
private RemoteViews applyStandardTemplate(int resId) {
@@ -2966,39 +3065,43 @@
boolean showLine3 = false;
boolean showLine2 = false;
boolean contentTextInLine2 = false;
+ final Bundle ex = mN.extras;
- if (mLargeIcon != null) {
- contentView.setImageViewIcon(R.id.icon, mLargeIcon);
- processLargeLegacyIcon(mLargeIcon, contentView);
- contentView.setImageViewIcon(R.id.right_icon, mSmallIcon);
+ if (mN.mLargeIcon != null) {
+ contentView.setImageViewIcon(R.id.icon, mN.mLargeIcon);
+ processLargeLegacyIcon(mN.mLargeIcon, contentView);
+ contentView.setImageViewIcon(R.id.right_icon, mN.mSmallIcon);
contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
- processSmallRightIcon(mSmallIcon, contentView);
+ processSmallRightIcon(mN.mSmallIcon, contentView);
} else { // small icon at left
- contentView.setImageViewIcon(R.id.icon, mSmallIcon);
+ contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
contentView.setViewVisibility(R.id.icon, View.VISIBLE);
- processSmallIconAsLarge(mSmallIcon, contentView);
+ processSmallIconAsLarge(mN.mSmallIcon, contentView);
}
- if (mContentTitle != null) {
- contentView.setTextViewText(R.id.title, processLegacyText(mContentTitle));
+ if (ex.getCharSequence(EXTRA_TITLE) != null) {
+ contentView.setTextViewText(R.id.title,
+ processLegacyText(ex.getCharSequence(EXTRA_TITLE)));
}
- if (mContentText != null) {
- contentView.setTextViewText(R.id.text, processLegacyText(mContentText));
+ if (ex.getCharSequence(EXTRA_TEXT) != null) {
+ contentView.setTextViewText(R.id.text,
+ processLegacyText(ex.getCharSequence(EXTRA_TEXT)));
showLine3 = true;
}
- if (mContentInfo != null) {
- contentView.setTextViewText(R.id.info, processLegacyText(mContentInfo));
+ if (ex.getCharSequence(EXTRA_INFO_TEXT) != null) {
+ contentView.setTextViewText(R.id.info,
+ processLegacyText(ex.getCharSequence(EXTRA_INFO_TEXT)));
contentView.setViewVisibility(R.id.info, View.VISIBLE);
showLine3 = true;
- } else if (mNumber > 0) {
+ } else if (mN.number > 0) {
final int tooBig = mContext.getResources().getInteger(
R.integer.status_bar_notification_info_maxnum);
- if (mNumber > tooBig) {
+ if (mN.number > tooBig) {
contentView.setTextViewText(R.id.info, processLegacyText(
mContext.getResources().getString(
R.string.status_bar_notification_info_overflow)));
} else {
NumberFormat f = NumberFormat.getIntegerInstance();
- contentView.setTextViewText(R.id.info, processLegacyText(f.format(mNumber)));
+ contentView.setTextViewText(R.id.info, processLegacyText(f.format(mN.number)));
}
contentView.setViewVisibility(R.id.info, View.VISIBLE);
showLine3 = true;
@@ -3007,10 +3110,12 @@
}
// Need to show three lines?
- if (mSubText != null) {
- contentView.setTextViewText(R.id.text, processLegacyText(mSubText));
- if (mContentText != null) {
- contentView.setTextViewText(R.id.text2, processLegacyText(mContentText));
+ if (ex.getCharSequence(EXTRA_SUB_TEXT) != null) {
+ contentView.setTextViewText(R.id.text,
+ processLegacyText(ex.getCharSequence(EXTRA_SUB_TEXT)));
+ if (ex.getCharSequence(EXTRA_TEXT) != null) {
+ contentView.setTextViewText(R.id.text2,
+ processLegacyText(ex.getCharSequence(EXTRA_TEXT)));
contentView.setViewVisibility(R.id.text2, View.VISIBLE);
showLine2 = true;
contentTextInLine2 = true;
@@ -3019,15 +3124,18 @@
}
} else {
contentView.setViewVisibility(R.id.text2, View.GONE);
- if (hasProgress && (mProgressMax != 0 || mProgressIndeterminate)) {
+ final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
+ final int progress = ex.getInt(EXTRA_PROGRESS, 0);
+ final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
+ if (hasProgress && (max != 0 || ind)) {
contentView.setViewVisibility(R.id.progress, View.VISIBLE);
contentView.setProgressBar(
- R.id.progress, mProgressMax, mProgress, mProgressIndeterminate);
+ R.id.progress, max, progress, ind);
contentView.setProgressBackgroundTintList(
R.id.progress, ColorStateList.valueOf(mContext.getColor(
R.color.notification_progress_background_color)));
- if (mColor != COLOR_DEFAULT) {
- ColorStateList colorStateList = ColorStateList.valueOf(mColor);
+ if (mN.color != COLOR_DEFAULT) {
+ ColorStateList colorStateList = ColorStateList.valueOf(mN.color);
contentView.setProgressTintList(R.id.progress, colorStateList);
contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
}
@@ -3043,20 +3151,21 @@
}
if (showsTimeOrChronometer()) {
- if (mUseChronometer) {
+ if (ex.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
contentView.setLong(R.id.chronometer, "setBase",
- mWhen + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
+ mN.when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
contentView.setBoolean(R.id.chronometer, "setStarted", true);
} else {
contentView.setViewVisibility(R.id.time, View.VISIBLE);
- contentView.setLong(R.id.time, "setTime", mWhen);
+ contentView.setLong(R.id.time, "setTime", mN.when);
}
}
// Adjust padding depending on line count and font size.
- contentView.setViewPadding(R.id.line1, 0, calculateTopPadding(mContext,
- mHasThreeLines, mContext.getResources().getConfiguration().fontScale),
+ contentView.setViewPadding(R.id.line1, 0,
+ calculateTopPadding(mContext, hasThreeLines(),
+ mContext.getResources().getConfiguration().fontScale),
0, 0);
// We want to add badge to first line of text.
@@ -3069,7 +3178,8 @@
// Note getStandardView may hide line 3 again.
contentView.setViewVisibility(R.id.line3, showLine3 ? View.VISIBLE : View.GONE);
- contentView.setViewVisibility(R.id.overflow_divider, showLine3 ? View.VISIBLE : View.GONE);
+ contentView.setViewVisibility(R.id.overflow_divider,
+ showLine3 ? View.VISIBLE : View.GONE);
return contentView;
}
@@ -3078,7 +3188,7 @@
* otherwise
*/
private boolean showsTimeOrChronometer() {
- return mWhen != 0 && mShowWhen;
+ return mN.when != 0 && mN.extras.getBoolean(EXTRA_SHOW_WHEN);
}
/**
@@ -3089,15 +3199,19 @@
* is going to have one or two lines
*/
private boolean hasThreeLines() {
- boolean contentTextInLine2 = mSubText != null && mContentText != null;
+ final CharSequence subText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
+ final CharSequence text = mN.extras.getCharSequence(EXTRA_TEXT);
+ boolean contentTextInLine2 = subText != null && text != null;
boolean hasProgress = mStyle == null || mStyle.hasProgress();
// If we have content text in line 2, badge goes into line 2, or line 3 otherwise
boolean badgeInLine3 = getProfileBadgeDrawable() != null && !contentTextInLine2;
- boolean hasLine3 = mContentText != null || mContentInfo != null || mNumber > 0
- || badgeInLine3;
- boolean hasLine2 = (mSubText != null && mContentText != null) ||
- (hasProgress && mSubText == null
- && (mProgressMax != 0 || mProgressIndeterminate));
+ boolean hasLine3 = text != null || mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null
+ || mN.number > 0 || badgeInLine3;
+ final Bundle ex = mN.extras;
+ final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
+ final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
+ boolean hasLine2 = (subText != null && text != null) ||
+ (hasProgress && subText == null && (max != 0 || ind));
return hasLine2 && hasLine3;
}
@@ -3144,29 +3258,48 @@
return big;
}
- private RemoteViews makeContentView() {
- if (mContentView != null) {
- return mContentView;
- } else {
- return applyStandardTemplate(getBaseLayoutResource());
+ /**
+ * Construct a RemoteViews for the final 1U notification layout. In order:
+ * 1. Custom contentView from the caller
+ * 2. Style's proposed content view
+ * 3. Standard template view
+ */
+ public RemoteViews makeContentView() {
+ if (mN.contentView != null) {
+ return mN.contentView;
+ } else if (mStyle != null) {
+ final RemoteViews styleView = mStyle.makeContentView();
+ if (styleView != null) {
+ return styleView;
+ }
}
+ return applyStandardTemplate(getBaseLayoutResource());
}
- private RemoteViews makeTickerView() {
- if (mTickerView != null) {
- return mTickerView;
- }
- return null; // tickers are not created by default anymore
- }
-
- private RemoteViews makeBigContentView() {
- if (mActions.size() == 0) return null;
+ /**
+ * Construct a RemoteViews for the final big notification layout.
+ */
+ public RemoteViews makeBigContentView() {
+ if (mStyle != null) {
+ final RemoteViews styleView = mStyle.makeBigContentView();
+ if (styleView != null) {
+ return styleView;
+ }
+ } else if (mActions.size() == 0) return null;
return applyStandardTemplateWithActions(getBigBaseLayoutResource());
}
- private RemoteViews makeHeadsUpContentView() {
- if (mActions.size() == 0) return null;
+ /**
+ * Construct a RemoteViews for the final heads-up notification layout.
+ */
+ public RemoteViews makeHeadsUpContentView() {
+ if (mStyle != null) {
+ final RemoteViews styleView = mStyle.makeHeadsUpContentView();
+ if (styleView != null) {
+ return styleView;
+ }
+ } else if (mActions.size() == 0) return null;
return applyStandardTemplateWithActions(getBigBaseLayoutResource());
}
@@ -3193,11 +3326,11 @@
* doesn't create material notifications by itself) app.
*/
private boolean isLegacy() {
- return mColorUtil != null;
+ return getColorUtil() != null;
}
private void processLegacyAction(Action action, RemoteViews button) {
- if (!isLegacy() || mColorUtil.isGrayscaleIcon(mContext, action.getIcon())) {
+ if (!isLegacy() || getColorUtil().isGrayscaleIcon(mContext, action.getIcon())) {
button.setTextViewCompoundDrawablesRelativeColorFilter(R.id.action0, 0,
mContext.getColor(R.color.notification_action_color_filter),
PorterDuff.Mode.MULTIPLY);
@@ -3206,7 +3339,7 @@
private CharSequence processLegacyText(CharSequence charSequence) {
if (isLegacy()) {
- return mColorUtil.invertCharSequenceColors(charSequence);
+ return getColorUtil().invertCharSequenceColors(charSequence);
} else {
return charSequence;
}
@@ -3222,7 +3355,7 @@
PorterDuff.Mode.SRC_ATOP, -1);
applyLargeIconBackground(contentView);
} else {
- if (mColorUtil.isGrayscaleIcon(mContext, largeIcon)) {
+ if (getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
applyLargeIconBackground(contentView);
}
}
@@ -3235,7 +3368,7 @@
// TODO: also check bounds, transparency, that sort of thing.
private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView) {
if (largeIcon != null && isLegacy()
- && mColorUtil.isGrayscaleIcon(mContext, largeIcon)) {
+ && getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
applyLargeIconBackground(contentView);
} else {
removeLargeIconBackground(contentView);
@@ -3277,7 +3410,7 @@
}
final boolean gray = isLegacy()
&& smallIcon.getType() == Icon.TYPE_RESOURCE
- && mColorUtil.isGrayscaleIcon(mContext, smallIcon.getResId());
+ && getColorUtil().isGrayscaleIcon(mContext, smallIcon.getResId());
if (!isLegacy() || gray) {
contentView.setInt(R.id.right_icon,
"setBackgroundResource",
@@ -3293,18 +3426,17 @@
}
}
- private int sanitizeColor() {
- if (mColor != COLOR_DEFAULT) {
- mColor |= 0xFF000000; // no alpha for custom colors
+ private void sanitizeColor() {
+ if (mN.color != COLOR_DEFAULT) {
+ mN.color |= 0xFF000000; // no alpha for custom colors
}
- return mColor;
}
private int resolveColor() {
- if (mColor == COLOR_DEFAULT) {
+ if (mN.color == COLOR_DEFAULT) {
return mContext.getColor(R.color.notification_icon_bg_color);
}
- return mColor;
+ return mN.color;
}
/**
@@ -3312,161 +3444,25 @@
* @hide
*/
public Notification buildUnstyled() {
- Notification n = new Notification();
- n.when = mWhen;
- n.mSmallIcon = mSmallIcon;
- if (mSmallIcon != null && mSmallIcon.getType() == Icon.TYPE_RESOURCE) {
- n.icon = mSmallIcon.getResId();
- }
- n.iconLevel = mSmallIconLevel;
- n.number = mNumber;
-
- n.color = sanitizeColor();
-
- setBuilderContentView(n, makeContentView());
- n.contentIntent = mContentIntent;
- n.deleteIntent = mDeleteIntent;
- n.fullScreenIntent = mFullScreenIntent;
- n.tickerText = mTickerText;
- n.tickerView = makeTickerView();
- n.mLargeIcon = mLargeIcon;
- if (mLargeIcon != null && mLargeIcon.getType() == Icon.TYPE_BITMAP) {
- n.largeIcon = mLargeIcon.getBitmap();
- }
- n.sound = mSound;
- n.audioStreamType = mAudioStreamType;
- n.audioAttributes = mAudioAttributes;
- n.vibrate = mVibrate;
- n.ledARGB = mLedArgb;
- n.ledOnMS = mLedOnMs;
- n.ledOffMS = mLedOffMs;
- n.defaults = mDefaults;
- n.flags = mFlags;
- setBuilderBigContentView(n, makeBigContentView());
- setBuilderHeadsUpContentView(n, makeHeadsUpContentView());
- if (mLedOnMs != 0 || mLedOffMs != 0) {
- n.flags |= FLAG_SHOW_LIGHTS;
- }
- if ((mDefaults & DEFAULT_LIGHTS) != 0) {
- n.flags |= FLAG_SHOW_LIGHTS;
- }
- n.category = mCategory;
- n.mGroupKey = mGroupKey;
- n.mSortKey = mSortKey;
- n.priority = mPriority;
if (mActions.size() > 0) {
- n.actions = new Action[mActions.size()];
- mActions.toArray(n.actions);
+ mN.actions = new Action[mActions.size()];
+ mActions.toArray(mN.actions);
}
- n.visibility = mVisibility;
-
- if (mPublicVersion != null) {
- n.publicVersion = new Notification();
- mPublicVersion.cloneInto(n.publicVersion, true);
+ if (!mPersonList.isEmpty()) {
+ mN.extras.putStringArray(EXTRA_PEOPLE,
+ mPersonList.toArray(new String[mPersonList.size()]));
}
- // Note: If you're adding new fields, also update restoreFromNotitification().
- return n;
+ if (mTopics.size() > 0) {
+ mN.topics = new Topic[mTopics.size()];
+ mTopics.toArray(mN.topics);
+ }
+ return mN;
}
- /**
- * Capture, in the provided bundle, semantic information used in the construction of
- * this Notification object.
- * @hide
- */
- public void populateExtras(Bundle extras) {
- // Store original information used in the construction of this object
- extras.putInt(EXTRA_ORIGINATING_USERID, mOriginatingUserId);
- extras.putParcelable(EXTRA_REBUILD_CONTEXT_APPLICATION_INFO,
- mContext.getApplicationInfo());
- extras.putCharSequence(EXTRA_TITLE, mContentTitle);
- extras.putCharSequence(EXTRA_TEXT, mContentText);
- extras.putCharSequence(EXTRA_SUB_TEXT, mSubText);
- extras.putCharSequence(EXTRA_INFO_TEXT, mContentInfo);
- extras.putParcelable(EXTRA_SMALL_ICON, mSmallIcon);
- extras.putInt(EXTRA_PROGRESS, mProgress);
- extras.putInt(EXTRA_PROGRESS_MAX, mProgressMax);
- extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, mProgressIndeterminate);
- extras.putBoolean(EXTRA_SHOW_CHRONOMETER, mUseChronometer);
- extras.putBoolean(EXTRA_SHOW_WHEN, mShowWhen);
- if (mLargeIcon != null) {
- extras.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
- }
- if (!mPeople.isEmpty()) {
- extras.putStringArray(EXTRA_PEOPLE, mPeople.toArray(new String[mPeople.size()]));
- }
- // NOTE: If you're adding new extras also update restoreFromNotification().
- }
-
-
- /**
- * @hide
- */
- public static void stripForDelivery(Notification n) {
- if (!STRIP_AND_REBUILD) {
- return;
- }
-
- String templateClass = n.extras.getString(EXTRA_TEMPLATE);
- // Only strip views for known Styles because we won't know how to
- // re-create them otherwise.
- boolean stripViews = TextUtils.isEmpty(templateClass) ||
- getNotificationStyleClass(templateClass) != null;
-
- boolean isStripped = false;
-
- if (n.largeIcon != null && n.extras.containsKey(EXTRA_LARGE_ICON)) {
- // TODO: Would like to check for equality here, but if the notification
- // has been cloned, we can't.
- n.largeIcon = null;
- n.extras.putBoolean(EXTRA_REBUILD_LARGE_ICON, true);
- isStripped = true;
- }
- // Get rid of unmodified BuilderRemoteViews.
-
- if (stripViews &&
- n.contentView instanceof BuilderRemoteViews &&
- n.extras.getInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT, -1) ==
- n.contentView.getSequenceNumber()) {
- n.contentView = null;
- n.extras.putBoolean(EXTRA_REBUILD_CONTENT_VIEW, true);
- n.extras.remove(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT);
- isStripped = true;
- }
- if (stripViews &&
- n.bigContentView instanceof BuilderRemoteViews &&
- n.extras.getInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT, -1) ==
- n.bigContentView.getSequenceNumber()) {
- n.bigContentView = null;
- n.extras.putBoolean(EXTRA_REBUILD_BIG_CONTENT_VIEW, true);
- n.extras.remove(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT);
- isStripped = true;
- }
- if (stripViews &&
- n.headsUpContentView instanceof BuilderRemoteViews &&
- n.extras.getInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT, -1) ==
- n.headsUpContentView.getSequenceNumber()) {
- n.headsUpContentView = null;
- n.extras.putBoolean(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW, true);
- n.extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT);
- isStripped = true;
- }
-
- if (isStripped) {
- n.extras.putBoolean(EXTRA_NEEDS_REBUILD, true);
- }
- }
-
- /**
- * @hide
- */
- public static Notification rebuild(Context context, Notification n) {
- Bundle extras = n.extras;
- if (!extras.getBoolean(EXTRA_NEEDS_REBUILD)) return n;
- extras.remove(EXTRA_NEEDS_REBUILD);
-
+ public static Notification.Builder recoverBuilder(Context context, Notification n) {
// Re-create notification context so we can access app resources.
- ApplicationInfo applicationInfo = extras.getParcelable(
- EXTRA_REBUILD_CONTEXT_APPLICATION_INFO);
+ ApplicationInfo applicationInfo = n.extras.getParcelable(
+ EXTRA_BUILDER_APPLICATION_INFO);
Context builderContext;
try {
builderContext = context.createApplicationContext(applicationInfo,
@@ -3476,58 +3472,7 @@
builderContext = context; // try with our context
}
- Builder b = new Builder(builderContext, n);
- return b.rebuild();
- }
-
- /**
- * Rebuilds the notification passed in to the rebuild-constructor
- * {@link #Builder(Context, Notification)}.
- *
- * <p>
- * Throws IllegalStateException when invoked on a Builder that isn't in rebuild mode.
- *
- * @hide
- */
- private Notification rebuild() {
- if (mRebuildNotification == null) {
- throw new IllegalStateException("rebuild() only valid when in 'rebuild' mode.");
- }
- mHasThreeLines = hasThreeLines();
-
- Bundle extras = mRebuildNotification.extras;
-
- if (extras.getBoolean(EXTRA_REBUILD_LARGE_ICON)) {
- mRebuildNotification.largeIcon = extras.getParcelable(EXTRA_LARGE_ICON);
- }
- extras.remove(EXTRA_REBUILD_LARGE_ICON);
-
- if (extras.getBoolean(EXTRA_REBUILD_CONTENT_VIEW)) {
- setBuilderContentView(mRebuildNotification, makeContentView());
- if (mStyle != null) {
- mStyle.populateContentView(mRebuildNotification);
- }
- }
- extras.remove(EXTRA_REBUILD_CONTENT_VIEW);
-
- if (extras.getBoolean(EXTRA_REBUILD_BIG_CONTENT_VIEW)) {
- setBuilderBigContentView(mRebuildNotification, makeBigContentView());
- if (mStyle != null) {
- mStyle.populateBigContentView(mRebuildNotification);
- }
- }
- extras.remove(EXTRA_REBUILD_BIG_CONTENT_VIEW);
-
- if (extras.getBoolean(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW)) {
- setBuilderHeadsUpContentView(mRebuildNotification, makeHeadsUpContentView());
- if (mStyle != null) {
- mStyle.populateHeadsUpContentView(mRebuildNotification);
- }
- }
- extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW);
-
- mHasThreeLines = false;
- return mRebuildNotification;
+ return new Builder(builderContext, n);
}
private static Class<? extends Style> getNotificationStyleClass(String templateClass) {
@@ -3543,87 +3488,15 @@
private void setBuilderContentView(Notification n, RemoteViews contentView) {
n.contentView = contentView;
- if (contentView instanceof BuilderRemoteViews) {
- mRebuildBundle.putInt(Builder.EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT,
- contentView.getSequenceNumber());
- }
}
private void setBuilderBigContentView(Notification n, RemoteViews bigContentView) {
n.bigContentView = bigContentView;
- if (bigContentView instanceof BuilderRemoteViews) {
- mRebuildBundle.putInt(Builder.EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT,
- bigContentView.getSequenceNumber());
- }
}
private void setBuilderHeadsUpContentView(Notification n,
RemoteViews headsUpContentView) {
n.headsUpContentView = headsUpContentView;
- if (headsUpContentView instanceof BuilderRemoteViews) {
- mRebuildBundle.putInt(Builder.EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT,
- headsUpContentView.getSequenceNumber());
- }
- }
-
- private void restoreFromNotification(Notification n) {
-
- // Notification fields.
- mWhen = n.when;
- mSmallIcon = n.mSmallIcon;
- mSmallIconLevel = n.iconLevel;
- mNumber = n.number;
-
- mColor = n.color;
-
- mContentView = n.contentView;
- mDeleteIntent = n.deleteIntent;
- mFullScreenIntent = n.fullScreenIntent;
- mTickerText = n.tickerText;
- mTickerView = n.tickerView;
- mLargeIcon = n.mLargeIcon;
- mSound = n.sound;
- mAudioStreamType = n.audioStreamType;
- mAudioAttributes = n.audioAttributes;
-
- mVibrate = n.vibrate;
- mLedArgb = n.ledARGB;
- mLedOnMs = n.ledOnMS;
- mLedOffMs = n.ledOffMS;
- mDefaults = n.defaults;
- mFlags = n.flags;
-
- mCategory = n.category;
- mGroupKey = n.mGroupKey;
- mSortKey = n.mSortKey;
- mPriority = n.priority;
- mActions.clear();
- if (n.actions != null) {
- Collections.addAll(mActions, n.actions);
- }
- mVisibility = n.visibility;
-
- mPublicVersion = n.publicVersion;
-
- // Extras.
- Bundle extras = n.extras;
- mOriginatingUserId = extras.getInt(EXTRA_ORIGINATING_USERID);
- mContentTitle = extras.getCharSequence(EXTRA_TITLE);
- mContentText = extras.getCharSequence(EXTRA_TEXT);
- mSubText = extras.getCharSequence(EXTRA_SUB_TEXT);
- mContentInfo = extras.getCharSequence(EXTRA_INFO_TEXT);
- mProgress = extras.getInt(EXTRA_PROGRESS);
- mProgressMax = extras.getInt(EXTRA_PROGRESS_MAX);
- mProgressIndeterminate = extras.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
- mUseChronometer = extras.getBoolean(EXTRA_SHOW_CHRONOMETER);
- mShowWhen = extras.getBoolean(EXTRA_SHOW_WHEN);
- if (extras.containsKey(EXTRA_LARGE_ICON)) {
- mLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON);
- }
- if (extras.containsKey(EXTRA_PEOPLE)) {
- mPeople.clear();
- Collections.addAll(mPeople, extras.getStringArray(EXTRA_PEOPLE));
- }
}
/**
@@ -3639,38 +3512,23 @@
* object.
*/
public Notification build() {
- if (mSmallIcon != null) {
- mSmallIcon.convertToAshmem();
+ // first, add any extras from the calling code
+ if (mUserExtras != null) {
+ mN.extras = getAllExtras();
}
- if (mLargeIcon != null) {
- mLargeIcon.convertToAshmem();
- }
+
+ // lazy stuff from mContext; see comment in Builder(Context, Notification)
+ mN.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, mContext.getApplicationInfo());
mOriginatingUserId = mContext.getUserId();
- mHasThreeLines = hasThreeLines();
+ mN.extras.putInt(EXTRA_ORIGINATING_USERID, mOriginatingUserId);
- Notification n = buildUnstyled();
+ buildUnstyled();
if (mStyle != null) {
- mStyle.purgeResources();
- n = mStyle.buildStyled(n);
+ mStyle.buildStyled(mN);
}
- if (mExtras != null) {
- n.extras.putAll(mExtras);
- }
-
- if (mRebuildBundle.size() > 0) {
- n.extras.putAll(mRebuildBundle);
- mRebuildBundle.clear();
- }
-
- populateExtras(n.extras);
- if (mStyle != null) {
- mStyle.addExtras(n.extras);
- }
-
- mHasThreeLines = false;
- return n;
+ return mN;
}
/**
@@ -3766,14 +3624,15 @@
checkBuilder();
// Nasty.
- CharSequence oldBuilderContentTitle = mBuilder.mContentTitle;
+ CharSequence oldBuilderContentTitle =
+ mBuilder.getAllExtras().getCharSequence(EXTRA_TITLE);
if (mBigContentTitle != null) {
mBuilder.setContentTitle(mBigContentTitle);
}
RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId);
- mBuilder.mContentTitle = oldBuilderContentTitle;
+ mBuilder.getAllExtras().putCharSequence(EXTRA_TITLE, oldBuilderContentTitle);
if (mBigContentTitle != null && mBigContentTitle.equals("")) {
contentView.setViewVisibility(R.id.line1, View.GONE);
@@ -3784,7 +3643,7 @@
// The last line defaults to the subtext, but can be replaced by mSummaryText
final CharSequence overflowText =
mSummaryTextSet ? mSummaryText
- : mBuilder.mSubText;
+ : mBuilder.getAllExtras().getCharSequence(EXTRA_SUB_TEXT);
if (overflowText != null) {
contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(overflowText));
contentView.setViewVisibility(R.id.overflow_divider, View.VISIBLE);
@@ -3800,6 +3659,31 @@
}
/**
+ * Construct a Style-specific RemoteViews for the final 1U notification layout.
+ * The default implementation has nothing additional to add.
+ * @hide
+ */
+ public RemoteViews makeContentView() {
+ return null;
+ }
+
+ /**
+ * Construct a Style-specific RemoteViews for the final big notification layout.
+ * @hide
+ */
+ public RemoteViews makeBigContentView() {
+ return null;
+ }
+
+ /**
+ * Construct a Style-specific RemoteViews for the final HUN layout.
+ * @hide
+ */
+ public RemoteViews makeHeadsUpContentView() {
+ return null;
+ }
+
+ /**
* Changes the padding of the first line such that the big and small content view have the
* same top padding.
*
@@ -3807,12 +3691,13 @@
*/
protected void applyTopPadding(RemoteViews contentView) {
int topPadding = Builder.calculateTopPadding(mBuilder.mContext,
- mBuilder.mHasThreeLines,
+ mBuilder.hasThreeLines(),
mBuilder.mContext.getResources().getConfiguration().fontScale);
contentView.setViewPadding(R.id.line1, 0, topPadding, 0, 0);
}
/**
+ * Apply any style-specific extras to this notification before shipping it out.
* @hide
*/
public void addExtras(Bundle extras) {
@@ -3826,6 +3711,7 @@
}
/**
+ * Reconstruct the internal state of this Style object from extras.
* @hide
*/
protected void restoreFromExtras(Bundle extras) {
@@ -3843,10 +3729,7 @@
* @hide
*/
public Notification buildStyled(Notification wip) {
- populateTickerView(wip);
- populateContentView(wip);
- populateBigContentView(wip);
- populateHeadsUpContentView(wip);
+ addExtras(wip.extras);
return wip;
}
@@ -3855,26 +3738,6 @@
*/
public void purgeResources() {}
- // The following methods are split out so we can re-create notification partially.
- /**
- * @hide
- */
- protected void populateTickerView(Notification wip) {}
- /**
- * @hide
- */
- protected void populateContentView(Notification wip) {}
-
- /**
- * @hide
- */
- protected void populateBigContentView(Notification wip) {}
-
- /**
- * @hide
- */
- protected void populateHeadsUpContentView(Notification wip) {}
-
/**
* Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is
* attached to.
@@ -3966,13 +3829,18 @@
return this;
}
+ /** @hide */
+ public static final int MIN_ASHMEM_BITMAP_SIZE = 128 * (1 << 10);
+
/**
* @hide
*/
@Override
public void purgeResources() {
super.purgeResources();
- if (mPicture != null && mPicture.isMutable()) {
+ if (mPicture != null &&
+ mPicture.isMutable() &&
+ mPicture.getAllocationByteCount() >= MIN_ASHMEM_BITMAP_SIZE) {
mPicture = mPicture.createAshmemBitmap();
}
if (mBigLargeIcon != null) {
@@ -3980,29 +3848,33 @@
}
}
- private RemoteViews makeBigContentView() {
- // Replace mLargeIcon with mBigLargeIcon if mBigLargeIconSet
+ /**
+ * @hide
+ */
+ public RemoteViews makeBigContentView() {
+ // Replace mN.mLargeIcon with mBigLargeIcon if mBigLargeIconSet
// This covers the following cases:
// 1. mBigLargeIconSet -> mBigLargeIcon (null or non-null) applies, overrides
- // mLargeIcon
- // 2. !mBigLargeIconSet -> mLargeIcon applies
+ // mN.mLargeIcon
+ // 2. !mBigLargeIconSet -> mN.mLargeIcon applies
Icon oldLargeIcon = null;
if (mBigLargeIconSet) {
- oldLargeIcon = mBuilder.mLargeIcon;
- mBuilder.mLargeIcon = mBigLargeIcon;
+ oldLargeIcon = mBuilder.mN.mLargeIcon;
+ mBuilder.mN.mLargeIcon = mBigLargeIcon;
}
RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
if (mBigLargeIconSet) {
- mBuilder.mLargeIcon = oldLargeIcon;
+ mBuilder.mN.mLargeIcon = oldLargeIcon;
}
contentView.setImageViewBitmap(R.id.big_picture, mPicture);
applyTopPadding(contentView);
- boolean twoTextLines = mBuilder.mSubText != null && mBuilder.mContentText != null;
+ boolean twoTextLines = mBuilder.getAllExtras().getCharSequence(EXTRA_SUB_TEXT) != null
+ && mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT) != null;
mBuilder.addProfileBadge(contentView,
twoTextLines ? R.id.profile_badge_line2 : R.id.profile_badge_line3);
return contentView;
@@ -4033,14 +3905,6 @@
}
mPicture = extras.getParcelable(EXTRA_PICTURE);
}
-
- /**
- * @hide
- */
- @Override
- public void populateBigContentView(Notification wip) {
- mBuilder.setBuilderBigContentView(wip, makeBigContentView());
- }
}
/**
@@ -4120,15 +3984,19 @@
mBigText = extras.getCharSequence(EXTRA_BIG_TEXT);
}
- private RemoteViews makeBigContentView() {
+ /**
+ * @hide
+ */
+ public RemoteViews makeBigContentView() {
// Nasty
- CharSequence oldBuilderContentText = mBuilder.mContentText;
- mBuilder.mContentText = null;
+ CharSequence oldBuilderContentText =
+ mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT);
+ mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
- mBuilder.mContentText = oldBuilderContentText;
+ mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
contentView.setTextViewText(R.id.big_text, mBuilder.processLegacyText(mBigText));
contentView.setViewVisibility(R.id.big_text, View.VISIBLE);
@@ -4147,7 +4015,8 @@
private int calculateMaxLines() {
int lineCount = MAX_LINES;
boolean hasActions = mBuilder.mActions.size() > 0;
- boolean hasSummary = (mSummaryTextSet ? mSummaryText : mBuilder.mSubText) != null;
+ boolean hasSummary = (mSummaryTextSet ? mSummaryText
+ : mBuilder.getAllExtras().getCharSequence(EXTRA_SUB_TEXT)) != null;
if (hasActions) {
lineCount -= LINES_CONSUMED_BY_ACTIONS;
}
@@ -4156,19 +4025,11 @@
}
// If we have less top padding at the top, we can fit less lines.
- if (!mBuilder.mHasThreeLines) {
+ if (!mBuilder.hasThreeLines()) {
lineCount--;
}
return lineCount;
}
-
- /**
- * @hide
- */
- @Override
- public void populateBigContentView(Notification wip) {
- mBuilder.setBuilderBigContentView(wip, makeBigContentView());
- }
}
/**
@@ -4249,16 +4110,18 @@
}
}
- private RemoteViews makeBigContentView() {
+ /**
+ * @hide
+ */
+ public RemoteViews makeBigContentView() {
// Remove the content text so line3 disappears unless you have a summary
-
// Nasty
- CharSequence oldBuilderContentText = mBuilder.mContentText;
- mBuilder.mContentText = null;
+ CharSequence oldBuilderContentText = mBuilder.mN.extras.getCharSequence(EXTRA_TEXT);
+ mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource());
- mBuilder.mContentText = oldBuilderContentText;
+ mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
contentView.setViewVisibility(R.id.text2, View.GONE);
@@ -4302,14 +4165,6 @@
return contentView;
}
-
- /**
- * @hide
- */
- @Override
- public void populateBigContentView(Notification wip) {
- mBuilder.setBuilderBigContentView(wip, makeBigContentView());
- }
}
/**
@@ -4401,16 +4256,16 @@
* @hide
*/
@Override
- public void populateContentView(Notification wip) {
- mBuilder.setBuilderContentView(wip, makeMediaContentView());
+ public RemoteViews makeContentView() {
+ return makeMediaContentView();
}
/**
* @hide
*/
@Override
- public void populateBigContentView(Notification wip) {
- mBuilder.setBuilderBigContentView(wip, makeMediaBigContentView());
+ public RemoteViews makeBigContentView() {
+ return makeMediaBigContentView();
}
/** @hide */
@@ -4524,7 +4379,7 @@
R.color.notification_media_secondary_color);
contentView.setTextColor(R.id.title, primaryColor);
if (mBuilder.showsTimeOrChronometer()) {
- if (mBuilder.mUseChronometer) {
+ if (mBuilder.getAllExtras().getBoolean(EXTRA_SHOW_CHRONOMETER)) {
contentView.setTextColor(R.id.chronometer, secondaryColor);
} else {
contentView.setTextColor(R.id.time, secondaryColor);
@@ -5368,7 +5223,7 @@
/**
* Gets the accent color.
*
- * @see setColor
+ * @see #setColor
*/
@ColorInt
public int getColor() {
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index cb0ff33..07b4d39 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -207,33 +207,7 @@
*/
public void notify(String tag, int id, Notification notification)
{
- int[] idOut = new int[1];
- INotificationManager service = getService();
- String pkg = mContext.getPackageName();
- if (notification.sound != null) {
- notification.sound = notification.sound.getCanonicalUri();
- if (StrictMode.vmFileUriExposureEnabled()) {
- notification.sound.checkFileUriExposed("Notification.sound");
- }
- }
- fixLegacySmallIcon(notification, pkg);
- if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
- if (notification.getSmallIcon() == null) {
- throw new IllegalArgumentException("Invalid notification (no valid small icon): "
- + notification);
- }
- }
- if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
- Notification stripped = notification.clone();
- Builder.stripForDelivery(stripped);
- try {
- service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
- stripped, idOut, UserHandle.myUserId());
- if (id != idOut[0]) {
- Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]);
- }
- } catch (RemoteException e) {
- }
+ notifyAsUser(tag, id, notification, new UserHandle(UserHandle.myUserId()));
}
/**
@@ -251,12 +225,17 @@
}
}
fixLegacySmallIcon(notification, pkg);
+ if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
+ if (notification.getSmallIcon() == null) {
+ throw new IllegalArgumentException("Invalid notification (no valid small icon): "
+ + notification);
+ }
+ }
if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
- Notification stripped = notification.clone();
- Builder.stripForDelivery(stripped);
+ final Notification copy = notification.clone();
try {
service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
- stripped, idOut, user.getIdentifier());
+ copy, idOut, user.getIdentifier());
if (id != idOut[0]) {
Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]);
}
@@ -287,13 +266,7 @@
*/
public void cancel(String tag, int id)
{
- INotificationManager service = getService();
- String pkg = mContext.getPackageName();
- if (localLOGV) Log.v(TAG, pkg + ": cancel(" + id + ")");
- try {
- service.cancelNotificationWithTag(pkg, tag, id, UserHandle.myUserId());
- } catch (RemoteException e) {
- }
+ cancelAsUser(tag, id, new UserHandle(UserHandle.myUserId()));
}
/**
@@ -375,29 +348,6 @@
/**
* @hide
*/
- public boolean setZenModeConfig(ZenModeConfig config, String reason) {
- INotificationManager service = getService();
- try {
- return service.setZenModeConfig(config, reason);
- } catch (RemoteException e) {
- return false;
- }
- }
-
- /**
- * @hide
- */
- public void requestZenModeConditions(IConditionListener listener, int relevance) {
- INotificationManager service = getService();
- try {
- service.requestZenModeConditions(listener, relevance);
- } catch (RemoteException e) {
- }
- }
-
- /**
- * @hide
- */
public int getZenMode() {
INotificationManager service = getService();
try {
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index c42ba65..edafe59 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -432,7 +432,7 @@
* @param intents Array of Intents of the activities to be launched.
* @param flags May be {@link #FLAG_ONE_SHOT}, {@link #FLAG_NO_CREATE},
* {@link #FLAG_CANCEL_CURRENT}, {@link #FLAG_UPDATE_CURRENT},
- * or any of the flags as supported by
+ * {@link #FLAG_IMMUTABLE} or any of the flags as supported by
* {@link Intent#fillIn Intent.fillIn()} to control which unspecified parts
* of the intent that can be supplied when the actual send happens.
*
@@ -502,7 +502,7 @@
* @param intent The Intent to be broadcast.
* @param flags May be {@link #FLAG_ONE_SHOT}, {@link #FLAG_NO_CREATE},
* {@link #FLAG_CANCEL_CURRENT}, {@link #FLAG_UPDATE_CURRENT},
- * or any of the flags as supported by
+ * {@link #FLAG_IMMUTABLE} or any of the flags as supported by
* {@link Intent#fillIn Intent.fillIn()} to control which unspecified parts
* of the intent that can be supplied when the actual send happens.
*
@@ -556,7 +556,7 @@
* @param intent An Intent describing the service to be started.
* @param flags May be {@link #FLAG_ONE_SHOT}, {@link #FLAG_NO_CREATE},
* {@link #FLAG_CANCEL_CURRENT}, {@link #FLAG_UPDATE_CURRENT},
- * or any of the flags as supported by
+ * {@link #FLAG_IMMUTABLE} or any of the flags as supported by
* {@link Intent#fillIn Intent.fillIn()} to control which unspecified parts
* of the intent that can be supplied when the actual send happens.
*
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 9e9d949..4e61103 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -148,6 +148,32 @@
= "android.app.action.PROVISION_MANAGED_PROFILE";
/**
+ * @hide
+ * Activity action: Starts the provisioning flow which sets up a managed user.
+ *
+ * <p>This intent will typically be sent by a mobile device management application (MDM).
+ * Provisioning configures the current user as managed user and sets the MDM as the profile
+ * owner who has full control over the user. Provisioning can only happen before user setup has
+ * been completed. Use {@link #isProvisioningAllowed(String)} to check if provisioning is
+ * allowed.
+ *
+ * This intent should contain the extra {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME},
+ * although specifying only {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME} is also
+ * supported.
+ *
+ * <p> If provisioning fails, the device returns to its previous state.
+ *
+ * <p>If launched with {@link android.app.Activity#startActivityForResult(Intent, int)} a
+ * result code of {@link android.app.Activity#RESULT_OK} implies that the synchronous part of
+ * the provisioning flow was successful, although this doesn't guarantee the full flow will
+ * succeed. Conversely a result code of {@link android.app.Activity#RESULT_CANCELED} implies
+ * that the user backed-out of provisioning, or some precondition for provisioning wasn't met.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_PROVISION_MANAGED_USER
+ = "android.app.action.PROVISION_MANAGED_USER";
+
+ /**
* Activity action: Starts the provisioning flow which sets up a managed device.
* Must be started with {@link android.app.Activity#startActivityForResult(Intent, int)}.
*
@@ -2271,6 +2297,8 @@
* on the device, for this user. After setting this, no applications running as this user
* will be able to access any cameras on the device.
*
+ * <p>If the caller is device owner, then the restriction will be applied to all users.
+ *
* <p>The calling device admin must have requested
* {@link DeviceAdminInfo#USES_POLICY_DISABLE_CAMERA} to be able to call
* this method; if it has not, a security exception will be thrown.
@@ -3635,6 +3663,28 @@
}
/**
+ * Called by a profile or device owner to get user restrictions set with
+ * {@link #addUserRestriction(ComponentName, String)}.
+ * <p>
+ * The target user may have more restrictions set by the system or other device owner / profile
+ * owner. To get all the user restrictions currently set, use
+ * {@link UserManager#getUserRestrictions()}.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ */
+ public Bundle getUserRestrictions(@NonNull ComponentName admin) {
+ Bundle ret = null;
+ if (mService != null) {
+ try {
+ ret = mService.getUserRestrictions(admin);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return ret == null ? new Bundle() : ret;
+ }
+
+ /**
* Called by profile or device owners to hide or unhide packages. When a package is hidden it
* is unavailable for use, but the data and actual package file remain.
*
@@ -4297,4 +4347,23 @@
return PERMISSION_GRANT_STATE_DEFAULT;
}
}
+
+ /**
+ * Returns if provisioning a managed profile or device is possible or not.
+ * @param action One of {@link #ACTION_PROVISION_MANAGED_DEVICE},
+ * {@link #ACTION_PROVISION_MANAGED_PROFILE}.
+ * Note that even if this method returns true, there is a slight possibility that the
+ * provisioning will not be allowed when it is actually initiated because some event has
+ * happened in between.
+ * @return if provisioning a managed profile or device is possible or not.
+ * @throws IllegalArgumentException if the supplied action is not valid.
+ */
+ public boolean isProvisioningAllowed(String action) {
+ try {
+ return mService.isProvisioningAllowed(action);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Failed talking with device policy service", re);
+ return false;
+ }
+ }
}
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index 0a0d77d..4270e16 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -16,6 +16,8 @@
package android.app.admin;
+import android.os.Bundle;
+
import java.util.List;
/**
@@ -69,4 +71,13 @@
* @return true if the uid is an active admin with the given policy.
*/
public abstract boolean isActiveAdminWithPolicy(int uid, int reqPolicy);
+
+ /**
+ * Takes a {@link Bundle} containing "base" user restrictions stored in
+ * {@link com.android.server.pm.UserManagerService}, mixes restrictions set by the device owner
+ * and the profile owner and returns the merged restrictions.
+ *
+ * This method always returns a new {@link Bundle}.
+ */
+ public abstract Bundle getComposedUserRestrictions(int userId, Bundle inBundle);
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index ccaa8cb..95a22ef 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -147,6 +147,7 @@
ComponentName getRestrictionsProvider(int userHandle);
void setUserRestriction(in ComponentName who, in String key, boolean enable);
+ Bundle getUserRestrictions(in ComponentName who);
void addCrossProfileIntentFilter(in ComponentName admin, in IntentFilter filter, int flags);
void clearCrossProfileIntentFilters(in ComponentName admin);
@@ -227,4 +228,5 @@
boolean setPermissionGrantState(in ComponentName admin, String packageName,
String permission, int grantState);
int getPermissionGrantState(in ComponentName admin, String packageName, String permission);
+ boolean isProvisioningAllowed(String action);
}
diff --git a/core/java/android/app/assist/AssistContent.java b/core/java/android/app/assist/AssistContent.java
index cddf47a..39902d7 100644
--- a/core/java/android/app/assist/AssistContent.java
+++ b/core/java/android/app/assist/AssistContent.java
@@ -14,6 +14,7 @@
*/
public class AssistContent implements Parcelable {
private boolean mIsAppProvidedIntent = false;
+ private boolean mIsAppProvidedWebUri = false;
private Intent mIntent;
private String mStructuredData;
private ClipData mClipData;
@@ -39,7 +40,7 @@
Uri uri = intent.getData();
if (uri != null) {
if ("http".equals(uri.getScheme()) || "https".equals(uri.getScheme())) {
- setWebUri(uri);
+ mUri = uri;
}
}
}
@@ -116,6 +117,7 @@
* leave the null and only report the local intent and clip data.
*/
public void setWebUri(Uri uri) {
+ mIsAppProvidedWebUri = true;
mUri = uri;
}
@@ -128,6 +130,16 @@
}
/**
+ * Returns whether or not the current {@link #getWebUri} was explicitly provided in
+ * {@link android.app.Activity#onProvideAssistContent Activity.onProvideAssistContent}. If not,
+ * the Intent was automatically set based on
+ * {@link android.app.Activity#getIntent Activity.getIntent}.
+ */
+ public boolean isAppProvidedWebUri() {
+ return mIsAppProvidedWebUri;
+ }
+
+ /**
* Return Bundle for extra vendor-specific data that can be modified and examined.
*/
public Bundle getExtras() {
diff --git a/core/java/android/app/backup/BackupHelper.java b/core/java/android/app/backup/BackupHelper.java
index 7cbbbc3..3074a8c 100644
--- a/core/java/android/app/backup/BackupHelper.java
+++ b/core/java/android/app/backup/BackupHelper.java
@@ -50,7 +50,11 @@
* new state after performing the backup operation.
* <p class="note">
* <strong>Note:</strong> The helper should not close or seek either the {@code oldState} or
- * the {@code newState} file descriptors.</p>
+ * the {@code newState} file descriptors. It is essential that when reading the helper's
+ * saved state from the {@code oldState} file, no extra content is consumed beyond
+ * what was stored by this helper. If more old state data is read, even accidentally,
+ * it will make it impossible for additional helpers that may be invoked after this one
+ * to properly reconstruct their prior state.</p>
*
* @param oldState An open, read-only {@link android.os.ParcelFileDescriptor} pointing to the
* last backup state provided by the application. May be
diff --git a/core/java/android/app/backup/BlobBackupHelper.java b/core/java/android/app/backup/BlobBackupHelper.java
index cdc62dc..82d0a94 100644
--- a/core/java/android/app/backup/BlobBackupHelper.java
+++ b/core/java/android/app/backup/BlobBackupHelper.java
@@ -20,7 +20,6 @@
import android.util.ArrayMap;
import android.util.Log;
-import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
@@ -42,7 +41,7 @@
*/
public abstract class BlobBackupHelper implements BackupHelper {
private static final String TAG = "BlobBackupHelper";
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final boolean DEBUG = false;
private final int mCurrentBlobVersion;
private final String[] mKeys;
@@ -92,16 +91,21 @@
final ArrayMap<String, Long> state = new ArrayMap<String, Long>();
FileInputStream fis = new FileInputStream(oldStateFd.getFileDescriptor());
- BufferedInputStream bis = new BufferedInputStream(fis);
- DataInputStream in = new DataInputStream(bis);
+ DataInputStream in = new DataInputStream(fis);
try {
int version = in.readInt();
if (version <= mCurrentBlobVersion) {
final int numKeys = in.readInt();
+ if (DEBUG) {
+ Log.i(TAG, " " + numKeys + " keys in state record");
+ }
for (int i = 0; i < numKeys; i++) {
String key = in.readUTF();
long checksum = in.readLong();
+ if (DEBUG) {
+ Log.i(TAG, " key '" + key + "' checksum is " + checksum);
+ }
state.put(key, checksum);
}
} else {
@@ -110,6 +114,9 @@
} catch (EOFException e) {
// Empty file is expected on first backup, so carry on. If the state
// is truncated we just treat it the same way.
+ if (DEBUG) {
+ Log.i(TAG, "Hit EOF reading prior state");
+ }
state.clear();
} catch (Exception e) {
Log.e(TAG, "Error examining prior backup state " + e.getMessage());
@@ -136,8 +143,13 @@
final int N = (state != null) ? state.size() : 0;
out.writeInt(N);
for (int i = 0; i < N; i++) {
- out.writeUTF(state.keyAt(i));
- out.writeLong(state.valueAt(i).longValue());
+ final String key = state.keyAt(i);
+ final long checksum = state.valueAt(i).longValue();
+ if (DEBUG) {
+ Log.i(TAG, " writing key " + key + " checksum = " + checksum);
+ }
+ out.writeUTF(key);
+ out.writeLong(checksum);
}
} catch (IOException e) {
Log.e(TAG, "Unable to write updated state", e);
@@ -226,6 +238,9 @@
@Override
public void performBackup(ParcelFileDescriptor oldStateFd, BackupDataOutput data,
ParcelFileDescriptor newStateFd) {
+ if (DEBUG) {
+ Log.i(TAG, "Performing backup for " + this.getClass().getName());
+ }
final ArrayMap<String, Long> oldState = readOldState(oldStateFd);
final ArrayMap<String, Long> newState = new ArrayMap<String, Long>();
@@ -234,12 +249,16 @@
for (String key : mKeys) {
final byte[] payload = deflate(getBackupPayload(key));
final long checksum = checksum(payload);
+ if (DEBUG) {
+ Log.i(TAG, "Key " + key + " backup checksum is " + checksum);
+ }
newState.put(key, checksum);
Long oldChecksum = oldState.get(key);
- if (oldChecksum == null || checksum != oldChecksum) {
+ if (oldChecksum == null || checksum != oldChecksum.longValue()) {
if (DEBUG) {
- Log.i(TAG, "State has changed for key " + key + ", writing");
+ Log.i(TAG, "Checksum has changed from " + oldChecksum + " to " + checksum
+ + " for key " + key + ", writing");
}
if (payload != null) {
data.writeEntityHeader(key, payload.length);
@@ -258,7 +277,7 @@
Log.w(TAG, "Unable to record notification state: " + e.getMessage());
newState.clear();
} finally {
- // Always recommit the state even if nothing changed
+ // Always rewrite the state even if nothing changed
writeBackupState(newState, newStateFd);
}
}
@@ -291,6 +310,9 @@
@Override
public void writeNewStateDescription(ParcelFileDescriptor newState) {
// Just ensure that we do a full backup the first time after a restore
+ if (DEBUG) {
+ Log.i(TAG, "Writing state description after restore");
+ }
writeBackupState(null, newState);
}
}
diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java
index 948ea1e..498ff81 100644
--- a/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -71,10 +71,11 @@
* Could be hours, could be days, who knows?
*
* @param packageName
+ * @param uidForAppId The uid of the app, which will be used for its app id
* @param userId
* @return
*/
- public abstract boolean isAppIdle(String packageName, int userId);
+ public abstract boolean isAppIdle(String packageName, int uidForAppId, int userId);
/**
* Returns all of the uids for a given user where all packages associating with that uid
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java
index ff4ebee..874026f 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClient.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java
@@ -1059,6 +1059,41 @@
}
/**
+ * Sets whether audio routing is allowed.
+ *
+ * Note: This is an internal function and shouldn't be exposed
+ */
+ public void setAudioRouteAllowed(boolean allowed) {
+ if (VDBG) log("setAudioRouteAllowed");
+ if (mService != null && isEnabled()) {
+ try {
+ mService.setAudioRouteAllowed(allowed);
+ } catch (RemoteException e) {Log.e(TAG, e.toString());}
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ }
+ }
+
+ /**
+ * Returns whether audio routing is allowed.
+ *
+ * Note: This is an internal function and shouldn't be exposed
+ */
+ public boolean getAudioRouteAllowed() {
+ if (VDBG) log("getAudioRouteAllowed");
+ if (mService != null && isEnabled()) {
+ try {
+ return mService.getAudioRouteAllowed();
+ } catch (RemoteException e) {Log.e(TAG, e.toString());}
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ }
+ return false;
+ }
+
+ /**
* Initiates a connection of audio channel.
*
* It setup SCO channel with remote connected Handsfree AG device.
diff --git a/core/java/android/bluetooth/IBluetoothHeadsetClient.aidl b/core/java/android/bluetooth/IBluetoothHeadsetClient.aidl
index e518b7d..79ae4e4 100644
--- a/core/java/android/bluetooth/IBluetoothHeadsetClient.aidl
+++ b/core/java/android/bluetooth/IBluetoothHeadsetClient.aidl
@@ -62,6 +62,8 @@
int getAudioState(in BluetoothDevice device);
boolean connectAudio();
boolean disconnectAudio();
+ void setAudioRouteAllowed(boolean allowed);
+ boolean getAudioRouteAllowed();
Bundle getCurrentAgFeatures(in BluetoothDevice device);
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index e5cfbf3..a0102b6 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -647,20 +647,18 @@
/**
* Open a private file associated with this Context's application package
- * for writing. Creates the file if it doesn't already exist.
- *
- * <p>No permissions are required to invoke this method, since it uses internal
- * storage.
+ * for writing. Creates the file if it doesn't already exist.
+ * <p>
+ * No additional permissions are required for the calling app to read or
+ * write the returned file.
*
* @param name The name of the file to open; can not contain path
- * separators.
- * @param mode Operating mode. Use 0 or {@link #MODE_PRIVATE} for the
- * default operation, {@link #MODE_APPEND} to append to an existing file,
- * {@link #MODE_WORLD_READABLE} and {@link #MODE_WORLD_WRITEABLE} to control
- * permissions.
- *
+ * separators.
+ * @param mode Operating mode. Use 0 or {@link #MODE_PRIVATE} for the
+ * default operation, {@link #MODE_APPEND} to append to an
+ * existing file, {@link #MODE_WORLD_READABLE} and
+ * {@link #MODE_WORLD_WRITEABLE} to control permissions.
* @return The resulting {@link FileOutputStream}.
- *
* @see #MODE_APPEND
* @see #MODE_PRIVATE
* @see #MODE_WORLD_READABLE
@@ -693,6 +691,9 @@
/**
* Returns the absolute path on the filesystem where a file created with
* {@link #openFileOutput} is stored.
+ * <p>
+ * The returned path may change over time if the calling app is moved to an
+ * adopted storage device, so only relative paths should be persisted.
*
* @param name The name of the file for which you would like to get
* its path.
@@ -706,14 +707,16 @@
public abstract File getFileStreamPath(String name);
/**
- * Returns the absolute path to the directory on the filesystem where
- * files created with {@link #openFileOutput} are stored.
- *
- * <p>No permissions are required to read or write to the returned path, since this
- * path is internal storage.
+ * Returns the absolute path to the directory on the filesystem where files
+ * created with {@link #openFileOutput} are stored.
+ * <p>
+ * The returned path may change over time if the calling app is moved to an
+ * adopted storage device, so only relative paths should be persisted.
+ * <p>
+ * No additional permissions are required for the calling app to read or
+ * write files under the returned path.
*
* @return The path of the directory holding application files.
- *
* @see #openFileOutput
* @see #getFileStreamPath
* @see #getDir
@@ -721,18 +724,41 @@
public abstract File getFilesDir();
/**
+ * Return the filesystem directory for storing device-encrypted private app
+ * data. Files stored in this location are typically encrypted with a key
+ * tied to the physical device, and they can be accessed whenever the device
+ * has booted successfully, both <em>before and after</em> the user has
+ * entered their credentials (such as a lock pattern or PIN).
+ */
+ public abstract File getDeviceEncryptedFilesDir();
+
+ /**
+ * Return the filesystem directory for storing credential-encrypted private
+ * app data. Files stored in this location are typically encrypted with a
+ * key tied to user credentials, and they can be accessed
+ * <em>only after</em> the user has entered their credentials (such as a
+ * lock pattern or PIN).
+ *
+ * @hide
+ */
+ @SystemApi
+ public abstract File getCredentialEncryptedFilesDir();
+
+ /**
* Returns the absolute path to the directory on the filesystem similar to
- * {@link #getFilesDir()}. The difference is that files placed under this
- * directory will be excluded from automatic backup to remote storage. See
+ * {@link #getFilesDir()}. The difference is that files placed under this
+ * directory will be excluded from automatic backup to remote storage. See
* {@link android.app.backup.BackupAgent BackupAgent} for a full discussion
* of the automatic backup mechanism in Android.
+ * <p>
+ * The returned path may change over time if the calling app is moved to an
+ * adopted storage device, so only relative paths should be persisted.
+ * <p>
+ * No additional permissions are required for the calling app to read or
+ * write files under the returned path.
*
- * <p>No permissions are required to read or write to the returned path, since this
- * path is internal storage.
- *
- * @return The path of the directory holding application files that will not be
- * automatically backed up to remote storage.
- *
+ * @return The path of the directory holding application files that will not
+ * be automatically backed up to remote storage.
* @see #openFileOutput
* @see #getFileStreamPath
* @see #getDir
@@ -741,200 +767,256 @@
public abstract File getNoBackupFilesDir();
/**
- * Returns the absolute path to the directory on the primary external filesystem
- * (that is somewhere on {@link android.os.Environment#getExternalStorageDirectory()
- * Environment.getExternalStorageDirectory()}) where the application can
- * place persistent files it owns. These files are internal to the
- * applications, and not typically visible to the user as media.
- *
- * <p>This is like {@link #getFilesDir()} in that these
- * files will be deleted when the application is uninstalled, however there
- * are some important differences:
- *
+ * Returns the absolute path to the directory on the primary shared/external
+ * storage device where the application can place persistent files it owns.
+ * These files are internal to the applications, and not typically visible
+ * to the user as media.
+ * <p>
+ * This is like {@link #getFilesDir()} in that these files will be deleted
+ * when the application is uninstalled, however there are some important
+ * differences:
* <ul>
- * <li>External files are not always available: they will disappear if the
- * user mounts the external storage on a computer or removes it. See the
- * APIs on {@link android.os.Environment} for information in the storage state.
- * <li>There is no security enforced with these files. For example, any application
- * holding {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to
+ * <li>Shared storage may not always be available, since removable media can
+ * be ejected by the user. Media state can be checked using
+ * {@link Environment#getExternalStorageState(File)}.
+ * <li>There is no security enforced with these files. For example, any
+ * application holding
+ * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to
* these files.
* </ul>
- *
- * <p>Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions
+ * <p>
+ * If a shared storage device is emulated (as determined by
+ * {@link Environment#isExternalStorageEmulated(File)}), it's contents are
+ * backed by a private user data partition, which means there is little
+ * benefit to storing data here instead of the private directories returned
+ * by {@link #getFilesDir()}, etc.
+ * <p>
+ * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions
* are required to read or write to the returned path; it's always
- * accessible to the calling app. This only applies to paths generated for
- * package name of the calling application. To access paths belonging
- * to other packages, {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE}
- * and/or {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required.
- *
- * <p>On devices with multiple users (as described by {@link UserManager}),
- * each user has their own isolated external storage. Applications only
- * have access to the external storage for the user they're running as.</p>
- *
- * <p>Here is an example of typical code to manipulate a file in
- * an application's private storage:</p>
- *
+ * accessible to the calling app. This only applies to paths generated for
+ * package name of the calling application. To access paths belonging to
+ * other packages,
+ * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} and/or
+ * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required.
+ * <p>
+ * On devices with multiple users (as described by {@link UserManager}),
+ * each user has their own isolated shared storage. Applications only have
+ * access to the shared storage for the user they're running as.
+ * <p>
+ * The returned path may change over time if different shared storage media
+ * is inserted, so only relative paths should be persisted.
+ * <p>
+ * Here is an example of typical code to manipulate a file in an
+ * application's shared storage:
+ * </p>
* {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
* private_file}
- *
- * <p>If you supply a non-null <var>type</var> to this function, the returned
- * file will be a path to a sub-directory of the given type. Though these files
- * are not automatically scanned by the media scanner, you can explicitly
- * add them to the media database with
- * {@link android.media.MediaScannerConnection#scanFile(Context, String[], String[],
- * android.media.MediaScannerConnection.OnScanCompletedListener)
- * MediaScannerConnection.scanFile}.
- * Note that this is not the same as
+ * <p>
+ * If you supply a non-null <var>type</var> to this function, the returned
+ * file will be a path to a sub-directory of the given type. Though these
+ * files are not automatically scanned by the media scanner, you can
+ * explicitly add them to the media database with
+ * {@link android.media.MediaScannerConnection#scanFile(Context, String[], String[], android.media.MediaScannerConnection.OnScanCompletedListener)
+ * MediaScannerConnection.scanFile}. Note that this is not the same as
* {@link android.os.Environment#getExternalStoragePublicDirectory
* Environment.getExternalStoragePublicDirectory()}, which provides
- * directories of media shared by all applications. The
- * directories returned here are
- * owned by the application, and their contents will be removed when the
- * application is uninstalled. Unlike
+ * directories of media shared by all applications. The directories returned
+ * here are owned by the application, and their contents will be removed
+ * when the application is uninstalled. Unlike
* {@link android.os.Environment#getExternalStoragePublicDirectory
- * Environment.getExternalStoragePublicDirectory()}, the directory
- * returned here will be automatically created for you.
- *
- * <p>Here is an example of typical code to manipulate a picture in
- * an application's private storage and add it to the media database:</p>
- *
+ * Environment.getExternalStoragePublicDirectory()}, the directory returned
+ * here will be automatically created for you.
+ * <p>
+ * Here is an example of typical code to manipulate a picture in an
+ * application's shared storage and add it to the media database:
+ * </p>
* {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
* private_picture}
*
- * @param type The type of files directory to return. May be null for
- * the root of the files directory or one of
- * the following Environment constants for a subdirectory:
- * {@link android.os.Environment#DIRECTORY_MUSIC},
- * {@link android.os.Environment#DIRECTORY_PODCASTS},
- * {@link android.os.Environment#DIRECTORY_RINGTONES},
- * {@link android.os.Environment#DIRECTORY_ALARMS},
- * {@link android.os.Environment#DIRECTORY_NOTIFICATIONS},
- * {@link android.os.Environment#DIRECTORY_PICTURES}, or
- * {@link android.os.Environment#DIRECTORY_MOVIES}.
- *
- * @return The path of the directory holding application files
- * on external storage. Returns null if external storage is not currently
- * mounted so it could not ensure the path exists; you will need to call
- * this method again when it is available.
- *
+ * @param type The type of files directory to return. May be {@code null}
+ * for the root of the files directory or one of the following
+ * constants for a subdirectory:
+ * {@link android.os.Environment#DIRECTORY_MUSIC},
+ * {@link android.os.Environment#DIRECTORY_PODCASTS},
+ * {@link android.os.Environment#DIRECTORY_RINGTONES},
+ * {@link android.os.Environment#DIRECTORY_ALARMS},
+ * {@link android.os.Environment#DIRECTORY_NOTIFICATIONS},
+ * {@link android.os.Environment#DIRECTORY_PICTURES}, or
+ * {@link android.os.Environment#DIRECTORY_MOVIES}.
+ * @return the absolute path to application-specific directory. May return
+ * {@code null} if shared storage is not currently available.
* @see #getFilesDir
- * @see android.os.Environment#getExternalStoragePublicDirectory
+ * @see #getExternalFilesDirs(String)
+ * @see Environment#getExternalStorageState(File)
+ * @see Environment#isExternalStorageEmulated(File)
+ * @see Environment#isExternalStorageRemovable(File)
*/
@Nullable
public abstract File getExternalFilesDir(@Nullable String type);
/**
* Returns absolute paths to application-specific directories on all
- * external storage devices where the application can place persistent files
- * it owns. These files are internal to the application, and not typically
- * visible to the user as media.
+ * shared/external storage devices where the application can place
+ * persistent files it owns. These files are internal to the application,
+ * and not typically visible to the user as media.
* <p>
- * This is like {@link #getFilesDir()} in that these files will be deleted when
- * the application is uninstalled, however there are some important differences:
+ * This is like {@link #getFilesDir()} in that these files will be deleted
+ * when the application is uninstalled, however there are some important
+ * differences:
* <ul>
- * <li>External files are not always available: they will disappear if the
- * user mounts the external storage on a computer or removes it.
- * <li>There is no security enforced with these files.
+ * <li>Shared storage may not always be available, since removable media can
+ * be ejected by the user. Media state can be checked using
+ * {@link Environment#getExternalStorageState(File)}.
+ * <li>There is no security enforced with these files. For example, any
+ * application holding
+ * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to
+ * these files.
* </ul>
* <p>
- * External storage devices returned here are considered a permanent part of
- * the device, including both emulated external storage and physical media
- * slots, such as SD cards in a battery compartment. The returned paths do
- * not include transient devices, such as USB flash drives.
+ * If a shared storage device is emulated (as determined by
+ * {@link Environment#isExternalStorageEmulated(File)}), it's contents are
+ * backed by a private user data partition, which means there is little
+ * benefit to storing data here instead of the private directories returned
+ * by {@link #getFilesDir()}, etc.
* <p>
- * An application may store data on any or all of the returned devices. For
+ * Shared storage devices returned here are considered a stable part of the
+ * device, including physical media slots under a protective cover. The
+ * returned paths do not include transient devices, such as USB flash drives
+ * connected to handheld devices.
+ * <p>
+ * An application may store data on any or all of the returned devices. For
* example, an app may choose to store large files on the device with the
* most available space, as measured by {@link StatFs}.
* <p>
- * No permissions are required to read or write to the returned paths; they
- * are always accessible to the calling app. Write access outside of these
- * paths on secondary external storage devices is not available.
+ * No additional permissions are required for the calling app to read or
+ * write files under the returned path. Write access outside of these paths
+ * on secondary external storage devices is not available.
* <p>
- * The first path returned is the same as {@link #getExternalFilesDir(String)}.
- * Returned paths may be {@code null} if a storage device is unavailable.
+ * The returned path may change over time if different shared storage media
+ * is inserted, so only relative paths should be persisted.
*
+ * @param type The type of files directory to return. May be {@code null}
+ * for the root of the files directory or one of the following
+ * constants for a subdirectory:
+ * {@link android.os.Environment#DIRECTORY_MUSIC},
+ * {@link android.os.Environment#DIRECTORY_PODCASTS},
+ * {@link android.os.Environment#DIRECTORY_RINGTONES},
+ * {@link android.os.Environment#DIRECTORY_ALARMS},
+ * {@link android.os.Environment#DIRECTORY_NOTIFICATIONS},
+ * {@link android.os.Environment#DIRECTORY_PICTURES}, or
+ * {@link android.os.Environment#DIRECTORY_MOVIES}.
+ * @return the absolute paths to application-specific directories. Some
+ * individual paths may be {@code null} if that shared storage is
+ * not currently available. The first path returned is the same as
+ * {@link #getExternalFilesDir(String)}.
* @see #getExternalFilesDir(String)
* @see Environment#getExternalStorageState(File)
+ * @see Environment#isExternalStorageEmulated(File)
+ * @see Environment#isExternalStorageRemovable(File)
*/
public abstract File[] getExternalFilesDirs(String type);
/**
- * Return the primary external storage directory where this application's OBB
- * files (if there are any) can be found. Note if the application does not have
- * any OBB files, this directory may not exist.
+ * Return the primary shared/external storage directory where this
+ * application's OBB files (if there are any) can be found. Note if the
+ * application does not have any OBB files, this directory may not exist.
* <p>
- * This is like {@link #getFilesDir()} in that these files will be deleted when
- * the application is uninstalled, however there are some important differences:
+ * This is like {@link #getFilesDir()} in that these files will be deleted
+ * when the application is uninstalled, however there are some important
+ * differences:
* <ul>
- * <li>External files are not always available: they will disappear if the
- * user mounts the external storage on a computer or removes it.
- * <li>There is no security enforced with these files. For example, any application
- * holding {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to
+ * <li>Shared storage may not always be available, since removable media can
+ * be ejected by the user. Media state can be checked using
+ * {@link Environment#getExternalStorageState(File)}.
+ * <li>There is no security enforced with these files. For example, any
+ * application holding
+ * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to
* these files.
* </ul>
* <p>
* Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions
* are required to read or write to the returned path; it's always
- * accessible to the calling app. This only applies to paths generated for
- * package name of the calling application. To access paths belonging
- * to other packages, {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE}
- * and/or {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required.
+ * accessible to the calling app. This only applies to paths generated for
+ * package name of the calling application. To access paths belonging to
+ * other packages,
+ * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} and/or
+ * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required.
* <p>
* On devices with multiple users (as described by {@link UserManager}),
* multiple users may share the same OBB storage location. Applications
* should ensure that multiple instances running under different users don't
* interfere with each other.
+ *
+ * @return the absolute path to application-specific directory. May return
+ * {@code null} if shared storage is not currently available.
+ * @see #getObbDirs()
+ * @see Environment#getExternalStorageState(File)
+ * @see Environment#isExternalStorageEmulated(File)
+ * @see Environment#isExternalStorageRemovable(File)
*/
public abstract File getObbDir();
/**
* Returns absolute paths to application-specific directories on all
- * external storage devices where the application's OBB files (if there are
- * any) can be found. Note if the application does not have any OBB files,
- * these directories may not exist.
+ * shared/external storage devices where the application's OBB files (if
+ * there are any) can be found. Note if the application does not have any
+ * OBB files, these directories may not exist.
* <p>
- * This is like {@link #getFilesDir()} in that these files will be deleted when
- * the application is uninstalled, however there are some important differences:
+ * This is like {@link #getFilesDir()} in that these files will be deleted
+ * when the application is uninstalled, however there are some important
+ * differences:
* <ul>
- * <li>External files are not always available: they will disappear if the
- * user mounts the external storage on a computer or removes it.
- * <li>There is no security enforced with these files.
+ * <li>Shared storage may not always be available, since removable media can
+ * be ejected by the user. Media state can be checked using
+ * {@link Environment#getExternalStorageState(File)}.
+ * <li>There is no security enforced with these files. For example, any
+ * application holding
+ * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to
+ * these files.
* </ul>
* <p>
- * External storage devices returned here are considered a permanent part of
- * the device, including both emulated external storage and physical media
- * slots, such as SD cards in a battery compartment. The returned paths do
- * not include transient devices, such as USB flash drives.
+ * Shared storage devices returned here are considered a stable part of the
+ * device, including physical media slots under a protective cover. The
+ * returned paths do not include transient devices, such as USB flash drives
+ * connected to handheld devices.
* <p>
- * An application may store data on any or all of the returned devices. For
+ * An application may store data on any or all of the returned devices. For
* example, an app may choose to store large files on the device with the
* most available space, as measured by {@link StatFs}.
* <p>
- * No permissions are required to read or write to the returned paths; they
- * are always accessible to the calling app. Write access outside of these
- * paths on secondary external storage devices is not available.
- * <p>
- * The first path returned is the same as {@link #getObbDir()}.
- * Returned paths may be {@code null} if a storage device is unavailable.
+ * No additional permissions are required for the calling app to read or
+ * write files under the returned path. Write access outside of these paths
+ * on secondary external storage devices is not available.
*
+ * @return the absolute paths to application-specific directories. Some
+ * individual paths may be {@code null} if that shared storage is
+ * not currently available. The first path returned is the same as
+ * {@link #getObbDir()}
* @see #getObbDir()
* @see Environment#getExternalStorageState(File)
+ * @see Environment#isExternalStorageEmulated(File)
+ * @see Environment#isExternalStorageRemovable(File)
*/
public abstract File[] getObbDirs();
/**
- * Returns the absolute path to the application specific cache directory
- * on the filesystem. These files will be ones that get deleted first when the
- * device runs low on storage.
- * There is no guarantee when these files will be deleted.
- *
+ * Returns the absolute path to the application specific cache directory on
+ * the filesystem. These files will be ones that get deleted first when the
+ * device runs low on storage. There is no guarantee when these files will
+ * be deleted.
+ * <p>
* <strong>Note: you should not <em>rely</em> on the system deleting these
* files for you; you should always have a reasonable maximum, such as 1 MB,
* for the amount of space you consume with cache files, and prune those
* files when exceeding that space.</strong>
+ * <p>
+ * The returned path may change over time if the calling app is moved to an
+ * adopted storage device, so only relative paths should be persisted.
+ * <p>
+ * Apps require no extra permissions to read or write to the returned path,
+ * since this path lives in their private storage.
*
* @return The path of the directory holding application cache files.
- *
* @see #openFileOutput
* @see #getFileStreamPath
* @see #getDir
@@ -950,6 +1032,9 @@
* This location is optimal for storing compiled or optimized code generated
* by your application at runtime.
* <p>
+ * The returned path may change over time if the calling app is moved to an
+ * adopted storage device, so only relative paths should be persisted.
+ * <p>
* Apps require no extra permissions to read or write to the returned path,
* since this path lives in their private storage.
*
@@ -958,120 +1043,161 @@
public abstract File getCodeCacheDir();
/**
- * Returns the absolute path to the directory on the primary external filesystem
- * (that is somewhere on {@link android.os.Environment#getExternalStorageDirectory()
- * Environment.getExternalStorageDirectory()} where the application can
- * place cache files it owns. These files are internal to the application, and
- * not typically visible to the user as media.
- *
- * <p>This is like {@link #getCacheDir()} in that these
- * files will be deleted when the application is uninstalled, however there
- * are some important differences:
- *
+ * Returns absolute path to application-specific directory on the primary
+ * shared/external storage device where the application can place cache
+ * files it owns. These files are internal to the application, and not
+ * typically visible to the user as media.
+ * <p>
+ * This is like {@link #getCacheDir()} in that these files will be deleted
+ * when the application is uninstalled, however there are some important
+ * differences:
* <ul>
- * <li>The platform does not always monitor the space available in external
- * storage, and thus may not automatically delete these files. Currently
- * the only time files here will be deleted by the platform is when running
- * on {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} or later and
- * {@link android.os.Environment#isExternalStorageEmulated()
- * Environment.isExternalStorageEmulated()} returns true. Note that you should
- * be managing the maximum space you will use for these anyway, just like
- * with {@link #getCacheDir()}.
- * <li>External files are not always available: they will disappear if the
- * user mounts the external storage on a computer or removes it. See the
- * APIs on {@link android.os.Environment} for information in the storage state.
- * <li>There is no security enforced with these files. For example, any application
- * holding {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to
+ * <li>The platform does not always monitor the space available in shared
+ * storage, and thus may not automatically delete these files. Apps should
+ * always manage the maximum space used in this location. Currently the only
+ * time files here will be deleted by the platform is when running on
+ * {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} or later and
+ * {@link Environment#isExternalStorageEmulated(File)} returns true.
+ * <li>Shared storage may not always be available, since removable media can
+ * be ejected by the user. Media state can be checked using
+ * {@link Environment#getExternalStorageState(File)}.
+ * <li>There is no security enforced with these files. For example, any
+ * application holding
+ * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to
* these files.
* </ul>
- *
- * <p>Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions
+ * <p>
+ * If a shared storage device is emulated (as determined by
+ * {@link Environment#isExternalStorageEmulated(File)}), it's contents are
+ * backed by a private user data partition, which means there is little
+ * benefit to storing data here instead of the private directory returned by
+ * {@link #getCacheDir()}.
+ * <p>
+ * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions
* are required to read or write to the returned path; it's always
- * accessible to the calling app. This only applies to paths generated for
- * package name of the calling application. To access paths belonging
- * to other packages, {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE}
- * and/or {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required.
+ * accessible to the calling app. This only applies to paths generated for
+ * package name of the calling application. To access paths belonging to
+ * other packages,
+ * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} and/or
+ * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required.
+ * <p>
+ * On devices with multiple users (as described by {@link UserManager}),
+ * each user has their own isolated shared storage. Applications only have
+ * access to the shared storage for the user they're running as.
+ * <p>
+ * The returned path may change over time if different shared storage media
+ * is inserted, so only relative paths should be persisted.
*
- * <p>On devices with multiple users (as described by {@link UserManager}),
- * each user has their own isolated external storage. Applications only
- * have access to the external storage for the user they're running as.</p>
- *
- * @return The path of the directory holding application cache files
- * on external storage. Returns null if external storage is not currently
- * mounted so it could not ensure the path exists; you will need to call
- * this method again when it is available.
- *
+ * @return the absolute path to application-specific directory. May return
+ * {@code null} if shared storage is not currently available.
* @see #getCacheDir
+ * @see #getExternalCacheDirs()
+ * @see Environment#getExternalStorageState(File)
+ * @see Environment#isExternalStorageEmulated(File)
+ * @see Environment#isExternalStorageRemovable(File)
*/
@Nullable
public abstract File getExternalCacheDir();
/**
* Returns absolute paths to application-specific directories on all
- * external storage devices where the application can place cache files it
- * owns. These files are internal to the application, and not typically
- * visible to the user as media.
+ * shared/external storage devices where the application can place cache
+ * files it owns. These files are internal to the application, and not
+ * typically visible to the user as media.
* <p>
- * This is like {@link #getCacheDir()} in that these files will be deleted when
- * the application is uninstalled, however there are some important differences:
+ * This is like {@link #getCacheDir()} in that these files will be deleted
+ * when the application is uninstalled, however there are some important
+ * differences:
* <ul>
- * <li>External files are not always available: they will disappear if the
- * user mounts the external storage on a computer or removes it.
- * <li>There is no security enforced with these files.
+ * <li>The platform does not always monitor the space available in shared
+ * storage, and thus may not automatically delete these files. Apps should
+ * always manage the maximum space used in this location. Currently the only
+ * time files here will be deleted by the platform is when running on
+ * {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} or later and
+ * {@link Environment#isExternalStorageEmulated(File)} returns true.
+ * <li>Shared storage may not always be available, since removable media can
+ * be ejected by the user. Media state can be checked using
+ * {@link Environment#getExternalStorageState(File)}.
+ * <li>There is no security enforced with these files. For example, any
+ * application holding
+ * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to
+ * these files.
* </ul>
* <p>
- * External storage devices returned here are considered a permanent part of
- * the device, including both emulated external storage and physical media
- * slots, such as SD cards in a battery compartment. The returned paths do
- * not include transient devices, such as USB flash drives.
+ * If a shared storage device is emulated (as determined by
+ * {@link Environment#isExternalStorageEmulated(File)}), it's contents are
+ * backed by a private user data partition, which means there is little
+ * benefit to storing data here instead of the private directory returned by
+ * {@link #getCacheDir()}.
* <p>
- * An application may store data on any or all of the returned devices. For
+ * Shared storage devices returned here are considered a stable part of the
+ * device, including physical media slots under a protective cover. The
+ * returned paths do not include transient devices, such as USB flash drives
+ * connected to handheld devices.
+ * <p>
+ * An application may store data on any or all of the returned devices. For
* example, an app may choose to store large files on the device with the
* most available space, as measured by {@link StatFs}.
* <p>
- * No permissions are required to read or write to the returned paths; they
- * are always accessible to the calling app. Write access outside of these
- * paths on secondary external storage devices is not available.
+ * No additional permissions are required for the calling app to read or
+ * write files under the returned path. Write access outside of these paths
+ * on secondary external storage devices is not available.
* <p>
- * The first path returned is the same as {@link #getExternalCacheDir()}.
- * Returned paths may be {@code null} if a storage device is unavailable.
+ * The returned paths may change over time if different shared storage media
+ * is inserted, so only relative paths should be persisted.
*
+ * @return the absolute paths to application-specific directories. Some
+ * individual paths may be {@code null} if that shared storage is
+ * not currently available. The first path returned is the same as
+ * {@link #getExternalCacheDir()}.
* @see #getExternalCacheDir()
* @see Environment#getExternalStorageState(File)
+ * @see Environment#isExternalStorageEmulated(File)
+ * @see Environment#isExternalStorageRemovable(File)
*/
public abstract File[] getExternalCacheDirs();
/**
* Returns absolute paths to application-specific directories on all
- * external storage devices where the application can place media files.
- * These files are scanned and made available to other apps through
+ * shared/external storage devices where the application can place media
+ * files. These files are scanned and made available to other apps through
* {@link MediaStore}.
* <p>
* This is like {@link #getExternalFilesDirs} in that these files will be
* deleted when the application is uninstalled, however there are some
* important differences:
* <ul>
- * <li>External files are not always available: they will disappear if the
- * user mounts the external storage on a computer or removes it.
- * <li>There is no security enforced with these files.
+ * <li>Shared storage may not always be available, since removable media can
+ * be ejected by the user. Media state can be checked using
+ * {@link Environment#getExternalStorageState(File)}.
+ * <li>There is no security enforced with these files. For example, any
+ * application holding
+ * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to
+ * these files.
* </ul>
* <p>
- * External storage devices returned here are considered a permanent part of
- * the device, including both emulated external storage and physical media
- * slots, such as SD cards in a battery compartment. The returned paths do
- * not include transient devices, such as USB flash drives.
+ * Shared storage devices returned here are considered a stable part of the
+ * device, including physical media slots under a protective cover. The
+ * returned paths do not include transient devices, such as USB flash drives
+ * connected to handheld devices.
* <p>
* An application may store data on any or all of the returned devices. For
* example, an app may choose to store large files on the device with the
* most available space, as measured by {@link StatFs}.
* <p>
- * No permissions are required to read or write to the returned paths; they
- * are always accessible to the calling app. Write access outside of these
- * paths on secondary external storage devices is not available.
+ * No additional permissions are required for the calling app to read or
+ * write files under the returned path. Write access outside of these paths
+ * on secondary external storage devices is not available.
* <p>
- * Returned paths may be {@code null} if a storage device is unavailable.
+ * The returned paths may change over time if different shared storage media
+ * is inserted, so only relative paths should be persisted.
*
+ * @return the absolute paths to application-specific directories. Some
+ * individual paths may be {@code null} if that shared storage is
+ * not currently available.
* @see Environment#getExternalStorageState(File)
+ * @see Environment#isExternalStorageEmulated(File)
+ * @see Environment#isExternalStorageRemovable(File)
*/
public abstract File[] getExternalMediaDirs();
@@ -1094,6 +1220,12 @@
* created through a File object will only be accessible by your own
* application; you can only set the mode of the entire directory, not
* of individual files.
+ * <p>
+ * The returned path may change over time if the calling app is moved to an
+ * adopted storage device, so only relative paths should be persisted.
+ * <p>
+ * Apps require no extra permissions to read or write to the returned path,
+ * since this path lives in their private storage.
*
* @param name Name of the directory to retrieve. This is a directory
* that is created as part of your application data.
@@ -1177,6 +1309,9 @@
/**
* Returns the absolute path on the filesystem where a database created with
* {@link #openOrCreateDatabase} is stored.
+ * <p>
+ * The returned path may change over time if the calling app is moved to an
+ * adopted storage device, so only relative paths should be persisted.
*
* @param name The name of the database for which you would like to get
* its path.
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 8359edf..bec1b37 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -204,6 +204,18 @@
}
@Override
+ public File getDeviceEncryptedFilesDir() {
+ return mBase.getDeviceEncryptedFilesDir();
+ }
+
+ /** {@hide} */
+ @SystemApi
+ @Override
+ public File getCredentialEncryptedFilesDir() {
+ return mBase.getCredentialEncryptedFilesDir();
+ }
+
+ @Override
public File getNoBackupFilesDir() {
return mBase.getNoBackupFilesDir();
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 6740d43..68b77fe 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1658,6 +1658,14 @@
= "android.intent.extra.GET_PERMISSIONS_APP_LABEL_LIST_RESULT";
/**
+ * Boolean list describing if the app is a system app for apps that have one or more runtime
+ * permissions.
+ * @hide
+ */
+ public static final String EXTRA_GET_PERMISSIONS_IS_SYSTEM_APP_LIST_RESULT
+ = "android.intent.extra.GET_PERMISSIONS_IS_SYSTEM_APP_LIST_RESULT";
+
+ /**
* Required extra to be sent with {@link #ACTION_GET_PERMISSIONS_COUNT} broadcasts.
* @hide
*/
@@ -3450,6 +3458,13 @@
public static final String EXTRA_INTENT = "android.intent.extra.INTENT";
/**
+ * An int representing the user id to be used.
+ *
+ * @hide
+ */
+ public static final String EXTRA_USER_ID = "android.intent.extra.USER_ID";
+
+ /**
* An Intent[] describing additional, alternate choices you would like shown with
* {@link #ACTION_CHOOSER}.
*
diff --git a/core/java/android/content/PeriodicSync.java b/core/java/android/content/PeriodicSync.java
index 3efd89a..0441ccc 100644
--- a/core/java/android/content/PeriodicSync.java
+++ b/core/java/android/content/PeriodicSync.java
@@ -21,6 +21,8 @@
import android.os.Parcel;
import android.accounts.Account;
+import java.util.Objects;
+
/**
* Value type that contains information about a periodic sync.
*/
@@ -144,7 +146,9 @@
if (!b2.containsKey(key)) {
return false;
}
- if (!b1.get(key).equals(b2.get(key))) {
+ // Null check. According to ContentResolver#validateSyncExtrasBundle null-valued keys
+ // are allowed in the bundle.
+ if (!Objects.equals(b1.get(key), b2.get(key))) {
return false;
}
}
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index a121b4d..885255f 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -67,7 +67,7 @@
* The launch mode style requested by the activity. From the
* {@link android.R.attr#launchMode} attribute, one of
* {@link #LAUNCH_MULTIPLE},
- * {@link #LAUNCH_SINGLE_TOP}, {@link #LAUNCH_SINGLE_TASK}, or
+ * {@link #LAUNCH_SINGLE_TOP}, {@link #LAUNCH_SINGLE_TASK}, or
* {@link #LAUNCH_SINGLE_INSTANCE}.
*/
public int launchMode;
@@ -140,7 +140,7 @@
* Activity. From the "permission" attribute.
*/
public String permission;
-
+
/**
* The affinity this activity has for another task in the system. The
* string here is the name of the task, often the package name of the
@@ -148,13 +148,13 @@
* {@link android.R.attr#taskAffinity} attribute.
*/
public String taskAffinity;
-
+
/**
* If this is an activity alias, this is the real activity class to run
* for it. Otherwise, this is null.
*/
public String targetActivity;
-
+
/**
* Bit in {@link #flags} indicating whether this activity is able to
* run in multiple processes. If
@@ -292,7 +292,7 @@
/**
* @hide Bit in {@link #flags}: If set, this activity may be launched into an
* owned ActivityContainer such as that within an ActivityView. If not set and
- * this activity is launched into such a container a SecurityExcception will be
+ * this activity is launched into such a container a SecurityException will be
* thrown. Set from the {@link android.R.attr#allowEmbedded} attribute.
*/
public static final int FLAG_ALLOW_EMBEDDED = 0x80000000;
@@ -362,7 +362,7 @@
* the {@link android.R.attr#screenOrientation} attribute.
*/
public static final int SCREEN_ORIENTATION_SENSOR = 4;
-
+
/**
* Constant corresponding to <code>nosensor</code> in
* the {@link android.R.attr#screenOrientation} attribute.
@@ -427,7 +427,7 @@
* The preferred screen orientation this activity would like to run in.
* From the {@link android.R.attr#screenOrientation} attribute, one of
* {@link #SCREEN_ORIENTATION_UNSPECIFIED},
- * {@link #SCREEN_ORIENTATION_LANDSCAPE},
+ * {@link #SCREEN_ORIENTATION_LANDSCAPE},
* {@link #SCREEN_ORIENTATION_PORTRAIT},
* {@link #SCREEN_ORIENTATION_USER},
* {@link #SCREEN_ORIENTATION_BEHIND},
@@ -445,7 +445,7 @@
*/
@ScreenOrientation
public int screenOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
-
+
/**
* Bit in {@link #configChanges} that indicates that the activity
* can itself handle changes to the IMSI MCC. Set from the
@@ -552,7 +552,7 @@
* constant starts at the high bits.
*/
public static final int CONFIG_FONT_SCALE = 0x40000000;
-
+
/** @hide
* Unfortunately the constants for config changes in native code are
* different from ActivityInfo. :( Here are the values we should use for the
@@ -671,6 +671,13 @@
*/
public boolean resizeable;
+ /**
+ * Value indicating if the activity is supports picture-in-picture form of multi-window mode.
+ * See {@link android.R.attr#supportsPictureInPicture}.
+ * @hide
+ */
+ public boolean supportsPip;
+
/** @hide */
public static final int LOCK_TASK_LAUNCH_MODE_DEFAULT = 0;
/** @hide */
@@ -702,7 +709,7 @@
*/
public int lockTaskLaunchMode;
- public InitialLayout initialLayout;
+ public Layout layout;
public ActivityInfo() {
}
@@ -723,8 +730,9 @@
parentActivityName = orig.parentActivityName;
maxRecents = orig.maxRecents;
resizeable = orig.resizeable;
+ supportsPip = orig.supportsPip;
lockTaskLaunchMode = orig.lockTaskLaunchMode;
- initialLayout = orig.initialLayout;
+ layout = orig.layout;
}
/**
@@ -769,12 +777,12 @@
if (uiOptions != 0) {
pw.println(prefix + " uiOptions=0x" + Integer.toHexString(uiOptions));
}
- pw.println(prefix + "resizeable=" + resizeable + " lockTaskLaunchMode="
- + lockTaskLaunchModeToString(lockTaskLaunchMode));
- if (initialLayout != null) {
- pw.println(prefix + "initialLayout=" + initialLayout.width + "|"
- + initialLayout.widthFraction + ", " + initialLayout.height + "|"
- + initialLayout.heightFraction + ", " + initialLayout.gravity);
+ pw.println(prefix + "resizeable=" + resizeable + " supportsPip=" + supportsPip);
+ pw.println(prefix + "lockTaskLaunchMode=" + lockTaskLaunchModeToString(lockTaskLaunchMode));
+ if (layout != null) {
+ pw.println(prefix + "initialLayout=" + layout.width + "|"
+ + layout.widthFraction + ", " + layout.height + "|"
+ + layout.heightFraction + ", " + layout.gravity);
}
super.dumpBack(pw, prefix);
}
@@ -806,14 +814,16 @@
dest.writeInt(persistableMode);
dest.writeInt(maxRecents);
dest.writeInt(resizeable ? 1 : 0);
+ dest.writeInt(supportsPip ? 1 : 0);
dest.writeInt(lockTaskLaunchMode);
- if (initialLayout != null) {
+ if (layout != null) {
dest.writeInt(1);
- dest.writeInt(initialLayout.width);
- dest.writeFloat(initialLayout.widthFraction);
- dest.writeInt(initialLayout.height);
- dest.writeFloat(initialLayout.heightFraction);
- dest.writeInt(initialLayout.gravity);
+ dest.writeInt(layout.width);
+ dest.writeFloat(layout.widthFraction);
+ dest.writeInt(layout.height);
+ dest.writeFloat(layout.heightFraction);
+ dest.writeInt(layout.gravity);
+ dest.writeInt(layout.minimalSize);
} else {
dest.writeInt(0);
}
@@ -846,28 +856,31 @@
persistableMode = source.readInt();
maxRecents = source.readInt();
resizeable = (source.readInt() == 1);
+ supportsPip = (source.readInt() == 1);
lockTaskLaunchMode = source.readInt();
if (source.readInt() == 1) {
- initialLayout = new InitialLayout(source);
+ layout = new Layout(source);
}
}
- public static final class InitialLayout {
- public InitialLayout(int width, float widthFraction, int height, float heightFraction,
- int gravity) {
+ public static final class Layout {
+ public Layout(int width, float widthFraction, int height, float heightFraction, int gravity,
+ int minimalSize) {
this.width = width;
this.widthFraction = widthFraction;
this.height = height;
this.heightFraction = heightFraction;
this.gravity = gravity;
+ this.minimalSize = minimalSize;
}
- InitialLayout(Parcel source) {
+ Layout(Parcel source) {
width = source.readInt();
widthFraction = source.readFloat();
height = source.readInt();
heightFraction = source.readFloat();
gravity = source.readInt();
+ minimalSize = source.readInt();
}
public final int width;
@@ -875,5 +888,6 @@
public final int height;
public final float heightFraction;
public final int gravity;
+ public final int minimalSize;
}
}
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 914945b..9c880d3 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -19,8 +19,12 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
+import android.os.Environment;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.os.storage.StorageManager;
import android.text.TextUtils;
import android.util.Printer;
@@ -459,6 +463,22 @@
public static final int PRIVATE_FLAG_HAS_DOMAIN_URLS = 1<<4;
/**
+ * When set, default data storage directory for given app is pointed at
+ * device-encrypted location.
+ *
+ * @hide
+ */
+ public static final int PRIVATE_FLAG_FORCE_DEVICE_ENCRYPTED = 1 << 5;
+
+ /**
+ * When set, assume that all components under the given app are encryption
+ * aware, unless otherwise specified.
+ *
+ * @hide
+ */
+ public static final int PRIVATE_FLAG_ENCRYPTION_AWARE = 1 << 6;
+
+ /**
* Private/hidden flags. See {@code PRIVATE_FLAG_...} constants.
* {@hide}
*/
@@ -549,11 +569,15 @@
public String[] sharedLibraryFiles;
/**
- * Full path to a directory assigned to the package for its persistent
- * data.
+ * Full path to a directory assigned to the package for its persistent data.
*/
public String dataDir;
+ /** {@hide} */
+ public String deviceEncryptedDataDir;
+ /** {@hide} */
+ public String credentialEncryptedDataDir;
+
/**
* Full path to the directory where native JNI libraries are stored.
*/
@@ -690,6 +714,8 @@
pw.println(prefix + "seinfo=" + seinfo);
}
pw.println(prefix + "dataDir=" + dataDir);
+ pw.println(prefix + "deviceEncryptedDataDir=" + deviceEncryptedDataDir);
+ pw.println(prefix + "credentialEncryptedDataDir=" + credentialEncryptedDataDir);
if (sharedLibraryFiles != null) {
pw.println(prefix + "sharedLibraryFiles=" + Arrays.toString(sharedLibraryFiles));
}
@@ -776,6 +802,8 @@
seinfo = orig.seinfo;
sharedLibraryFiles = orig.sharedLibraryFiles;
dataDir = orig.dataDir;
+ deviceEncryptedDataDir = orig.deviceEncryptedDataDir;
+ credentialEncryptedDataDir = orig.credentialEncryptedDataDir;
uid = orig.uid;
targetSdkVersion = orig.targetSdkVersion;
versionCode = orig.versionCode;
@@ -789,7 +817,6 @@
fullBackupContent = orig.fullBackupContent;
}
-
public String toString() {
return "ApplicationInfo{"
+ Integer.toHexString(System.identityHashCode(this))
@@ -829,6 +856,8 @@
dest.writeString(seinfo);
dest.writeStringArray(sharedLibraryFiles);
dest.writeString(dataDir);
+ dest.writeString(deviceEncryptedDataDir);
+ dest.writeString(credentialEncryptedDataDir);
dest.writeInt(uid);
dest.writeInt(targetSdkVersion);
dest.writeInt(versionCode);
@@ -881,6 +910,8 @@
seinfo = source.readString();
sharedLibraryFiles = source.readStringArray();
dataDir = source.readString();
+ deviceEncryptedDataDir = source.readString();
+ credentialEncryptedDataDir = source.readString();
uid = source.readInt();
targetSdkVersion = source.readInt();
versionCode = source.readInt();
@@ -925,7 +956,31 @@
FLAG_SUPPORTS_SMALL_SCREENS | FLAG_RESIZEABLE_FOR_SCREENS |
FLAG_SUPPORTS_SCREEN_DENSITIES | FLAG_SUPPORTS_XLARGE_SCREENS);
}
-
+
+ /** {@hide} */
+ public void initForUser(int userId) {
+ uid = UserHandle.getUid(userId, UserHandle.getAppId(uid));
+
+ if ("android".equals(packageName)) {
+ dataDir = Environment.getDataSystemDirectory().getAbsolutePath();
+ return;
+ }
+
+ deviceEncryptedDataDir = Environment
+ .getDataUserDeviceEncryptedPackageDirectory(volumeUuid, userId, packageName)
+ .getAbsolutePath();
+ credentialEncryptedDataDir = Environment
+ .getDataUserCredentialEncryptedPackageDirectory(volumeUuid, userId, packageName)
+ .getAbsolutePath();
+
+ if ((privateFlags & PRIVATE_FLAG_FORCE_DEVICE_ENCRYPTED) != 0
+ && SystemProperties.getBoolean(StorageManager.PROP_HAS_FBE, false)) {
+ dataDir = deviceEncryptedDataDir;
+ } else {
+ dataDir = credentialEncryptedDataDir;
+ }
+ }
+
/**
* @hide
*/
@@ -986,6 +1041,11 @@
&& (flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
}
+ /** @hide */
+ public boolean isEncryptionAware() {
+ return (privateFlags & ApplicationInfo.PRIVATE_FLAG_ENCRYPTION_AWARE) != 0;
+ }
+
/**
* @hide
*/
diff --git a/core/java/android/content/pm/AppsQueryHelper.java b/core/java/android/content/pm/AppsQueryHelper.java
index 56b3173..a5a8e3f 100644
--- a/core/java/android/content/pm/AppsQueryHelper.java
+++ b/core/java/android/content/pm/AppsQueryHelper.java
@@ -23,6 +23,8 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.ArraySet;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
import com.android.internal.annotations.VisibleForTesting;
@@ -46,6 +48,14 @@
*/
public static int GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM = 1 << 1;
+ /**
+ * Return all input methods that are marked as default.
+ * <p>When this flag is set, {@code user} specified in
+ * {@link #queryApps(int, boolean, UserHandle)} must be
+ * {@link UserHandle#myUserId user of the current process}.
+ */
+ public static int GET_DEFAULT_IMES = 1 << 2;
+
private final Context mContext;
private List<ApplicationInfo> mAllApps;
@@ -56,13 +66,14 @@
/**
* Return a List of all packages that satisfy a specified criteria.
* @param flags search flags. Use any combination of {@link #GET_NON_LAUNCHABLE_APPS},
- * {@link #GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM}
+ * {@link #GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM} or {@link #GET_DEFAULT_IMES}.
* @param systemAppsOnly if true, only system apps will be returned
* @param user user, whose apps are queried
*/
public List<String> queryApps(int flags, boolean systemAppsOnly, UserHandle user) {
boolean nonLaunchableApps = (flags & GET_NON_LAUNCHABLE_APPS) > 0;
boolean interactAcrossUsers = (flags & GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM) > 0;
+ boolean defaultImes = (flags & GET_DEFAULT_IMES) > 0;
if (mAllApps == null) {
mAllApps = getAllApps(user.getIdentifier());
}
@@ -118,6 +129,33 @@
}
}
+ if (defaultImes) {
+ if (UserHandle.myUserId() != user.getIdentifier()) {
+ throw new IllegalArgumentException("Specified user handle " + user
+ + " is not a user of the current process.");
+ }
+ List<InputMethodInfo> imis = getInputMethodList();
+ int imisSize = imis.size();
+ ArraySet<String> defaultImePackages = new ArraySet<>();
+ for (int i = 0; i < imisSize; i++) {
+ InputMethodInfo imi = imis.get(i);
+ if (imi.isDefault(mContext)) {
+ defaultImePackages.add(imi.getPackageName());
+ }
+ }
+ final int allAppsSize = mAllApps.size();
+ for (int i = 0; i < allAppsSize; i++) {
+ final ApplicationInfo appInfo = mAllApps.get(i);
+ if (systemAppsOnly && !appInfo.isSystemApp()) {
+ continue;
+ }
+ final String packageName = appInfo.packageName;
+ if (defaultImePackages.contains(packageName)) {
+ result.add(packageName);
+ }
+ }
+ }
+
return result;
}
@@ -150,4 +188,12 @@
throw new IllegalStateException("Package manager has died", e);
}
}
+
+ @VisibleForTesting
+ @SuppressWarnings("unchecked")
+ protected List<InputMethodInfo> getInputMethodList() {
+ InputMethodManager imm = (InputMethodManager)
+ mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
+ return imm.getInputMethodList();
+ }
}
diff --git a/core/java/android/content/pm/ComponentInfo.java b/core/java/android/content/pm/ComponentInfo.java
index f27fc2a..ad7ebe5 100644
--- a/core/java/android/content/pm/ComponentInfo.java
+++ b/core/java/android/content/pm/ComponentInfo.java
@@ -63,7 +63,14 @@
* <provider> tag.
*/
public boolean exported = false;
-
+
+ /**
+ * Indicate if this component is aware of encryption lifecycle, and can be
+ * safely run before the user has entered their credentials (such as a lock
+ * pattern or PIN).
+ */
+ public boolean encryptionAware = false;
+
public ComponentInfo() {
}
@@ -74,6 +81,7 @@
descriptionRes = orig.descriptionRes;
enabled = orig.enabled;
exported = orig.exported;
+ encryptionAware = orig.encryptionAware;
}
@Override public CharSequence loadLabel(PackageManager pm) {
@@ -143,7 +151,7 @@
protected void dumpFront(Printer pw, String prefix) {
super.dumpFront(pw, prefix);
pw.println(prefix + "enabled=" + enabled + " exported=" + exported
- + " processName=" + processName);
+ + " encryptionAware=" + encryptionAware + " processName=" + processName);
if (descriptionRes != 0) {
pw.println(prefix + "description=" + descriptionRes);
}
@@ -171,6 +179,7 @@
dest.writeInt(descriptionRes);
dest.writeInt(enabled ? 1 : 0);
dest.writeInt(exported ? 1 : 0);
+ dest.writeInt(encryptionAware ? 1 : 0);
}
protected ComponentInfo(Parcel source) {
@@ -183,6 +192,7 @@
descriptionRes = source.readInt();
enabled = (source.readInt() != 0);
exported = (source.readInt() != 0);
+ encryptionAware = (source.readInt() != 0);
}
/**
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index a5e9faf..6fe1efd 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -63,7 +63,9 @@
boolean isPackageAvailable(String packageName, int userId);
PackageInfo getPackageInfo(String packageName, int flags, int userId);
int getPackageUid(String packageName, int userId);
+ int getPackageUidEtc(String packageName, int flags, int userId);
int[] getPackageGids(String packageName, int userId);
+ int[] getPackageGidsEtc(String packageName, int flags, int userId);
String[] currentToCanonicalPackageNames(in String[] names);
String[] canonicalToCurrentPackageNames(in String[] names);
@@ -418,10 +420,14 @@
boolean hasSystemUidErrors();
/**
- * Ask the package manager to perform boot-time dex-opt of all
- * existing packages.
+ * Ask the package manager to fstrim the disk if needed.
*/
- void performBootDexOpt();
+ void performFstrimIfNeeded();
+
+ /**
+ * Notify the package manager that a package is going to be used.
+ */
+ void notifyPackageUse(String packageName);
/**
* Ask the package manager to perform dex-opt (if needed) on the given
diff --git a/core/java/android/content/pm/InstrumentationInfo.java b/core/java/android/content/pm/InstrumentationInfo.java
index dab0caf..44bdf4e 100644
--- a/core/java/android/content/pm/InstrumentationInfo.java
+++ b/core/java/android/content/pm/InstrumentationInfo.java
@@ -57,11 +57,15 @@
public String[] splitPublicSourceDirs;
/**
- * Full path to a directory assigned to the package for its persistent
- * data.
+ * Full path to a directory assigned to the package for its persistent data.
*/
public String dataDir;
+ /** {@hide} */
+ public String deviceEncryptedDataDir;
+ /** {@hide} */
+ public String credentialEncryptedDataDir;
+
/**
* Full path to the directory where the native JNI libraries are stored.
*
@@ -85,7 +89,11 @@
targetPackage = orig.targetPackage;
sourceDir = orig.sourceDir;
publicSourceDir = orig.publicSourceDir;
+ splitSourceDirs = orig.splitSourceDirs;
+ splitPublicSourceDirs = orig.splitPublicSourceDirs;
dataDir = orig.dataDir;
+ deviceEncryptedDataDir = orig.deviceEncryptedDataDir;
+ credentialEncryptedDataDir = orig.credentialEncryptedDataDir;
nativeLibraryDir = orig.nativeLibraryDir;
handleProfiling = orig.handleProfiling;
functionalTest = orig.functionalTest;
@@ -106,7 +114,11 @@
dest.writeString(targetPackage);
dest.writeString(sourceDir);
dest.writeString(publicSourceDir);
+ dest.writeStringArray(splitSourceDirs);
+ dest.writeStringArray(splitPublicSourceDirs);
dest.writeString(dataDir);
+ dest.writeString(deviceEncryptedDataDir);
+ dest.writeString(credentialEncryptedDataDir);
dest.writeString(nativeLibraryDir);
dest.writeInt((handleProfiling == false) ? 0 : 1);
dest.writeInt((functionalTest == false) ? 0 : 1);
@@ -127,9 +139,26 @@
targetPackage = source.readString();
sourceDir = source.readString();
publicSourceDir = source.readString();
+ splitSourceDirs = source.readStringArray();
+ splitPublicSourceDirs = source.readStringArray();
dataDir = source.readString();
+ deviceEncryptedDataDir = source.readString();
+ credentialEncryptedDataDir = source.readString();
nativeLibraryDir = source.readString();
handleProfiling = source.readInt() != 0;
functionalTest = source.readInt() != 0;
}
+
+ /** {@hide} */
+ public void copyTo(ApplicationInfo ai) {
+ ai.packageName = packageName;
+ ai.sourceDir = sourceDir;
+ ai.publicSourceDir = publicSourceDir;
+ ai.splitSourceDirs = splitSourceDirs;
+ ai.splitPublicSourceDirs = splitPublicSourceDirs;
+ ai.dataDir = dataDir;
+ ai.deviceEncryptedDataDir = deviceEncryptedDataDir;
+ ai.credentialEncryptedDataDir = credentialEncryptedDataDir;
+ ai.nativeLibraryDir = nativeLibraryDir;
+ }
}
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 3283005..d6d395b 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1042,6 +1042,12 @@
}
/** {@hide} */
+ @SystemApi
+ public void setInstallFlagsQuick() {
+ installFlags |= PackageManager.INSTALL_QUICK;
+ }
+
+ /** {@hide} */
public void dump(IndentingPrintWriter pw) {
pw.printPair("mode", mode);
pw.printHexPair("installFlags", installFlags);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 82cbbbe..566de4e 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -43,12 +43,10 @@
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
-import android.os.Environment;
import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.storage.VolumeInfo;
-import android.text.TextUtils;
import android.util.AndroidException;
import com.android.internal.util.ArrayUtils;
@@ -236,6 +234,24 @@
public static final int MATCH_ALL = 0x00020000;
/**
+ * {@link PackageInfo} flag: include components which aren't encryption
+ * aware in the returned info, regardless of the current user state.
+ */
+ public static final int GET_ENCRYPTION_UNAWARE_COMPONENTS = 0x00040000;
+
+ /**
+ * {@link PackageInfo} flag: return components as if the given user is
+ * running with amnesia. This typically limits the component to only those
+ * marked as {@link ComponentInfo#encryptionAware}, unless
+ * {@link #GET_ENCRYPTION_UNAWARE_COMPONENTS} is also specified.
+ * <p>
+ * This flag is for internal use only.
+ *
+ * @hide
+ */
+ public static final int FLAG_USER_RUNNING_WITH_AMNESIA = 0x00080000;
+
+ /**
* Flag for {@link addCrossProfileIntentFilter}: if this flag is set:
* when resolving an intent that matches the {@link CrossProfileIntentFilter}, the current
* profile will be skipped.
@@ -431,6 +447,14 @@
public static final int INSTALL_FORCE_PERMISSION_PROMPT = 0x00000400;
/**
+ * Flag parameter for {@link #installPackage} to indicate that this package is
+ * to be installed quickly.
+ *
+ * @hide
+ */
+ public static final int INSTALL_QUICK = 0x00000800;
+
+ /**
* Flag parameter for
* {@link #setComponentEnabledSetting(android.content.ComponentName, int, int)} to indicate
* that you don't want to kill the app containing the component. Be careful when you set this
@@ -1119,7 +1143,7 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
- * The device has professional audio level of functionality, performance, and acoustics.
+ * The device has professional audio level of functionality and performance.
*/
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_AUDIO_PRO = "android.hardware.audio.pro";
@@ -1702,6 +1726,15 @@
public static final String FEATURE_BACKUP = "android.software.backup";
/**
+ * Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device supports freeform window management.
+ * Windows have title bars and can be moved and resized.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_FREEFORM_WINDOW_MANAGEMENT
+ = "android.software.freeform_window_management";
+
+ /**
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
* The device supports creating secondary users and managed profiles via
* {@link DevicePolicyManager}.
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 1a8602b..838da37 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -24,6 +24,7 @@
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
+import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import android.app.ActivityManager;
import android.content.ComponentName;
@@ -39,6 +40,7 @@
import android.os.Environment;
import android.os.FileUtils;
import android.os.PatternMatcher;
+import android.os.Trace;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -128,7 +130,7 @@
public final String name;
public final int sdkVersion;
public final int fileVersion;
-
+
public NewPermissionInfo(String name, int sdkVersion, int fileVersion) {
this.name = name;
this.sdkVersion = sdkVersion;
@@ -215,10 +217,10 @@
final int iconRes;
final int logoRes;
final int bannerRes;
-
+
String tag;
TypedArray sa;
-
+
ParsePackageItemArgs(Package _owner, String[] _outError,
int _nameRes, int _labelRes, int _iconRes, int _logoRes, int _bannerRes) {
owner = _owner;
@@ -230,14 +232,14 @@
bannerRes = _bannerRes;
}
}
-
+
static class ParseComponentArgs extends ParsePackageItemArgs {
final String[] sepProcesses;
final int processRes;
final int descriptionRes;
final int enabledRes;
int flags;
-
+
ParseComponentArgs(Package _owner, String[] _outError,
int _nameRes, int _labelRes, int _iconRes, int _logoRes, int _bannerRes,
String[] _sepProcesses, int _processRes,
@@ -349,7 +351,7 @@
private ParseComponentArgs mParseActivityAliasArgs;
private ParseComponentArgs mParseServiceArgs;
private ParseComponentArgs mParseProviderArgs;
-
+
/** If set to true, we will only allow package files that exactly match
* the DTD. Otherwise, we try to get as much from the package as we
* can without failing. This should normally be set to false, to
@@ -622,6 +624,8 @@
public final static int PARSE_COLLECT_CERTIFICATES = 1<<8;
public final static int PARSE_TRUSTED_OVERLAY = 1<<9;
public final static int PARSE_ENFORCE_CODE = 1<<10;
+ // TODO: fix b/25118622; remove this entirely once signature processing is quick
+ public final static int PARSE_SKIP_VERIFICATION = 1<<11;
private static final Comparator<String> sSplitNameComparator = new SplitNameComparator();
@@ -902,6 +906,7 @@
}
pkg.volumeUuid = volumeUuid;
+ pkg.applicationInfo.volumeUuid = volumeUuid;
pkg.baseCodePath = apkPath;
pkg.mSignatures = null;
@@ -920,7 +925,6 @@
private void parseSplitApk(Package pkg, int splitIndex, AssetManager assets, int flags)
throws PackageParserException {
final String apkPath = pkg.splitCodePaths[splitIndex];
- final File apkFile = new File(apkPath);
mParseError = PackageManager.INSTALL_SUCCEEDED;
mArchiveSourcePath = apkPath;
@@ -1054,33 +1058,38 @@
/**
* Collect certificates from all the APKs described in the given package,
- * populating {@link Package#mSignatures}. This also asserts that all APK
+ * populating {@link Package#mSignatures}.
+ * <p>Depending upon the parser flags, this may also asserts that all APK
* contents are signed correctly and consistently.
*/
- public void collectCertificates(Package pkg, int flags) throws PackageParserException {
+ public void collectCertificates(Package pkg, int parseFlags) throws PackageParserException {
pkg.mCertificates = null;
pkg.mSignatures = null;
pkg.mSigningKeys = null;
- collectCertificates(pkg, new File(pkg.baseCodePath), flags);
+ collectCertificates(pkg, new File(pkg.baseCodePath), parseFlags);
if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
for (String splitCodePath : pkg.splitCodePaths) {
- collectCertificates(pkg, new File(splitCodePath), flags);
+ collectCertificates(pkg, new File(splitCodePath), parseFlags);
}
}
}
- private static void collectCertificates(Package pkg, File apkFile, int flags)
+ private static void collectCertificates(Package pkg, File apkFile, int parseFlags)
throws PackageParserException {
- final boolean requireCode = ((flags & PARSE_ENFORCE_CODE) != 0)
+ final boolean requireCode = ((parseFlags & PARSE_ENFORCE_CODE) != 0)
&& ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0);
final String apkPath = apkFile.getAbsolutePath();
+ final boolean skipVerification = Build.IS_DEBUGGABLE
+ && ((parseFlags & PARSE_SKIP_VERIFICATION) != 0);
- boolean codeFound = false;
+ boolean codeFound = skipVerification;
StrictJarFile jarFile = null;
try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "strictJarFileCtor");
jarFile = new StrictJarFile(apkPath);
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
// Always verify manifest, regardless of source
final ZipEntry manifestEntry = jarFile.findEntry(ANDROID_MANIFEST_FILENAME);
@@ -1089,11 +1098,12 @@
"Package " + apkPath + " has no manifest");
}
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "buildVerifyList");
final List<ZipEntry> toVerify = new ArrayList<>();
toVerify.add(manifestEntry);
// If we're parsing an untrusted package, verify all contents
- if ((flags & PARSE_IS_SYSTEM) == 0) {
+ if (!skipVerification && (parseFlags & PARSE_IS_SYSTEM) == 0) {
final Iterator<ZipEntry> i = jarFile.iterator();
while (i.hasNext()) {
final ZipEntry entry = i.next();
@@ -1110,6 +1120,7 @@
toVerify.add(entry);
}
}
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
if (!codeFound && requireCode) {
throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
@@ -1119,6 +1130,7 @@
// 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)) {
@@ -1135,6 +1147,9 @@
for (int i=0; i < entryCerts.length; i++) {
pkg.mSigningKeys.add(entryCerts[i][0].getPublicKey());
}
+ if (skipVerification) {
+ break;
+ }
} else {
if (!Signature.areExactMatch(pkg.mSignatures, entrySignatures)) {
throw new PackageParserException(
@@ -1144,6 +1159,7 @@
}
}
}
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
} catch (GeneralSecurityException e) {
throw new PackageParserException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING,
"Failed to collect certificates from " + apkPath, e);
@@ -1199,7 +1215,8 @@
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);
+ // TODO: fix b/25118622; pass in '0' for parse flags
+ collectCertificates(tempPkg, apkFile, flags & PARSE_SKIP_VERIFICATION);
signatures = tempPkg.mSignatures;
} else {
signatures = null;
@@ -1456,7 +1473,7 @@
int supportsXLargeScreens = 1;
int resizeable = 1;
int anyDensity = 1;
-
+
int outerDepth = parser.getDepth();
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
@@ -1612,7 +1629,7 @@
String minCode = null;
int targetVers = 0;
String targetCode = null;
-
+
TypedValue val = sa.peekValue(
com.android.internal.R.styleable.AndroidManifestUsesSdk_minSdkVersion);
if (val != null) {
@@ -1623,7 +1640,7 @@
targetVers = minVers = val.data;
}
}
-
+
val = sa.peekValue(
com.android.internal.R.styleable.AndroidManifestUsesSdk_targetSdkVersion);
if (val != null) {
@@ -1634,7 +1651,7 @@
targetVers = val.data;
}
}
-
+
sa.recycle();
if (minCode != null) {
@@ -1663,7 +1680,7 @@
mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
return null;
}
-
+
if (targetCode != null) {
boolean allowedCodename = false;
for (String codename : SDK_CODENAMES) {
@@ -1730,9 +1747,9 @@
anyDensity);
sa.recycle();
-
+
XmlUtils.skipCurrentTag(parser);
-
+
} else if (tagName.equals("protected-broadcast")) {
sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestProtectedBroadcast);
@@ -1754,12 +1771,12 @@
}
XmlUtils.skipCurrentTag(parser);
-
+
} else if (tagName.equals("instrumentation")) {
if (parseInstrumentation(pkg, res, parser, attrs, outError) == null) {
return null;
}
-
+
} else if (tagName.equals("original-package")) {
sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestOriginalPackage);
@@ -1777,7 +1794,7 @@
sa.recycle();
XmlUtils.skipCurrentTag(parser);
-
+
} else if (tagName.equals("adopt-permissions")) {
sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestOriginalPackage);
@@ -1795,12 +1812,12 @@
}
XmlUtils.skipCurrentTag(parser);
-
+
} else if (tagName.equals("uses-gl-texture")) {
// Just skip this tag
XmlUtils.skipCurrentTag(parser);
continue;
-
+
} else if (tagName.equals("compatible-screens")) {
// Just skip this tag
XmlUtils.skipCurrentTag(parser);
@@ -1808,12 +1825,12 @@
} else if (tagName.equals("supports-input")) {
XmlUtils.skipCurrentTag(parser);
continue;
-
+
} else if (tagName.equals("eat-comment")) {
// Just skip this tag
XmlUtils.skipCurrentTag(parser);
continue;
-
+
} else if (RIGID_PARSER) {
outError[0] = "Bad element under <manifest>: "
+ parser.getName();
@@ -1906,8 +1923,7 @@
return pkg;
}
- private FeatureInfo parseUsesFeature(Resources res, AttributeSet attrs)
- throws XmlPullParserException, IOException {
+ private FeatureInfo parseUsesFeature(Resources res, AttributeSet attrs) {
FeatureInfo fi = new FeatureInfo();
TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestUsesFeature);
@@ -2017,7 +2033,7 @@
}
return proc.intern();
}
-
+
private static String buildProcessName(String pkg, String defProc,
CharSequence procSeq, int flags, String[] separateProcesses,
String[] outError) {
@@ -2226,7 +2242,7 @@
}
sa.recycle();
-
+
if (!parseAllMetaData(res, parser, attrs, "<permission-group>", perm,
outError)) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
@@ -2265,7 +2281,7 @@
if (perm.info.group != null) {
perm.info.group = perm.info.group.intern();
}
-
+
perm.info.descriptionRes = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestPermission_description,
0);
@@ -2296,7 +2312,7 @@
return null;
}
}
-
+
if (!parseAllMetaData(res, parser, attrs, "<permission>", perm,
outError)) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
@@ -2329,7 +2345,7 @@
}
sa.recycle();
-
+
int index = perm.info.name.indexOf('.');
if (index > 0) {
index = perm.info.name.indexOf('.', index+1);
@@ -2371,9 +2387,9 @@
com.android.internal.R.styleable.AndroidManifestInstrumentation_banner);
mParseInstrumentationArgs.tag = "<instrumentation>";
}
-
+
mParseInstrumentationArgs.sa = sa;
-
+
Instrumentation a = new Instrumentation(mParseInstrumentationArgs,
new InstrumentationInfo());
if (outError[0] != null) {
@@ -2616,6 +2632,15 @@
ai.flags |= ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS;
}
+ if (sa.getBoolean(R.styleable.AndroidManifestApplication_forceDeviceEncrypted, false)
+ && (flags & PARSE_IS_SYSTEM) != 0) {
+ ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_FORCE_DEVICE_ENCRYPTED;
+ }
+ if (sa.getBoolean(R.styleable.AndroidManifestApplication_encryptionAware, false)
+ && (flags & PARSE_IS_SYSTEM) != 0) {
+ ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ENCRYPTION_AWARE;
+ }
+
String str;
str = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestApplication_permission, 0);
@@ -2650,10 +2675,10 @@
}
ai.processName = buildProcessName(ai.packageName, null, pname,
flags, mSeparateProcesses, outError);
-
+
ai.enabled = sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_enabled, true);
-
+
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_isGame, false)) {
ai.flags |= ApplicationInfo.FLAG_IS_GAME;
@@ -3022,7 +3047,7 @@
outInfo.icon = iconVal;
outInfo.nonLocalizedLabel = null;
}
-
+
int logoVal = sa.getResourceId(logoRes, 0);
if (logoVal != 0) {
outInfo.logo = logoVal;
@@ -3061,11 +3086,11 @@
R.styleable.AndroidManifestActivity_description,
R.styleable.AndroidManifestActivity_enabled);
}
-
+
mParseActivityArgs.tag = receiver ? "<receiver>" : "<activity>";
mParseActivityArgs.sa = sa;
mParseActivityArgs.flags = flags;
-
+
Activity a = new Activity(mParseActivityArgs, new ActivityInfo());
if (outError[0] != null) {
sa.recycle();
@@ -3204,12 +3229,19 @@
R.styleable.AndroidManifestActivity_resizeableActivity,
owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.N);
+ a.info.supportsPip = a.info.resizeable ? sa.getBoolean(
+ R.styleable.AndroidManifestActivity_supportsPictureInPicture, false) : false;
+
a.info.screenOrientation = sa.getInt(
R.styleable.AndroidManifestActivity_screenOrientation,
ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
a.info.lockTaskLaunchMode =
sa.getInt(R.styleable.AndroidManifestActivity_lockTaskMode, 0);
+
+ a.info.encryptionAware = sa.getBoolean(
+ R.styleable.AndroidManifestActivity_encryptionAware,
+ owner.applicationInfo.isEncryptionAware());
} else {
a.info.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
a.info.configChanges = 0;
@@ -3224,6 +3256,10 @@
setExported = true;
}
}
+
+ a.info.encryptionAware = sa.getBoolean(
+ R.styleable.AndroidManifestActivity_encryptionAware,
+ owner.applicationInfo.isEncryptionAware());
}
sa.recycle();
@@ -3236,7 +3272,7 @@
outError[0] = "Heavy-weight applications can not have receivers in main process";
}
}
-
+
if (outError[0] != null) {
return null;
}
@@ -3282,8 +3318,8 @@
outError)) == null) {
return null;
}
- } else if (!receiver && parser.getName().equals("initial-layout")) {
- parseInitialLayout(res, attrs, a);
+ } else if (!receiver && parser.getName().equals("layout")) {
+ parseLayout(res, attrs, a);
} else {
if (!RIGID_PARSER) {
Slog.w(TAG, "Problem in package " + mArchiveSourcePath + ":");
@@ -3316,41 +3352,44 @@
return a;
}
- private void parseInitialLayout(Resources res, AttributeSet attrs, Activity a) {
+ private void parseLayout(Resources res, AttributeSet attrs, Activity a) {
TypedArray sw = res.obtainAttributes(attrs,
- com.android.internal.R.styleable.AndroidManifestInitialLayout);
+ com.android.internal.R.styleable.AndroidManifestLayout);
int width = -1;
float widthFraction = -1f;
int height = -1;
float heightFraction = -1f;
final int widthType = sw.getType(
- com.android.internal.R.styleable.AndroidManifestInitialLayout_activityWidth);
+ com.android.internal.R.styleable.AndroidManifestLayout_initialWidth);
if (widthType == TypedValue.TYPE_FRACTION) {
widthFraction = sw.getFraction(
- com.android.internal.R.styleable.AndroidManifestInitialLayout_activityWidth,
+ com.android.internal.R.styleable.AndroidManifestLayout_initialWidth,
1, 1, -1);
} else if (widthType == TypedValue.TYPE_DIMENSION) {
width = sw.getDimensionPixelSize(
- com.android.internal.R.styleable.AndroidManifestInitialLayout_activityWidth,
+ com.android.internal.R.styleable.AndroidManifestLayout_initialWidth,
-1);
}
final int heightType = sw.getType(
- com.android.internal.R.styleable.AndroidManifestInitialLayout_activityHeight);
+ com.android.internal.R.styleable.AndroidManifestLayout_initialHeight);
if (heightType == TypedValue.TYPE_FRACTION) {
heightFraction = sw.getFraction(
- com.android.internal.R.styleable.AndroidManifestInitialLayout_activityHeight,
+ com.android.internal.R.styleable.AndroidManifestLayout_initialHeight,
1, 1, -1);
} else if (heightType == TypedValue.TYPE_DIMENSION) {
height = sw.getDimensionPixelSize(
- com.android.internal.R.styleable.AndroidManifestInitialLayout_activityHeight,
+ com.android.internal.R.styleable.AndroidManifestLayout_initialHeight,
-1);
}
int gravity = sw.getInt(
- com.android.internal.R.styleable.AndroidManifestInitialLayout_gravity,
+ com.android.internal.R.styleable.AndroidManifestLayout_gravity,
Gravity.CENTER);
+ int minimalSize = sw.getDimensionPixelSize(
+ com.android.internal.R.styleable.AndroidManifestLayout_minimalSize,
+ -1);
sw.recycle();
- a.info.initialLayout = new ActivityInfo.InitialLayout(width, widthFraction,
- height, heightFraction, gravity);
+ a.info.layout = new ActivityInfo.Layout(width, widthFraction,
+ height, heightFraction, gravity, minimalSize);
}
private Activity parseActivityAlias(Package owner, Resources res,
@@ -3388,10 +3427,10 @@
com.android.internal.R.styleable.AndroidManifestActivityAlias_enabled);
mParseActivityAliasArgs.tag = "<activity-alias>";
}
-
+
mParseActivityAliasArgs.sa = sa;
mParseActivityAliasArgs.flags = flags;
-
+
Activity target = null;
final int NA = owner.activities.size();
@@ -3432,7 +3471,7 @@
info.uiOptions = target.info.uiOptions;
info.parentActivityName = target.info.parentActivityName;
info.maxRecents = target.info.maxRecents;
- info.initialLayout = target.info.initialLayout;
+ info.layout = target.info.layout;
Activity a = new Activity(mParseActivityAliasArgs, info);
if (outError[0] != null) {
@@ -3540,10 +3579,10 @@
com.android.internal.R.styleable.AndroidManifestProvider_enabled);
mParseProviderArgs.tag = "<provider>";
}
-
+
mParseProviderArgs.sa = sa;
mParseProviderArgs.flags = flags;
-
+
Provider p = new Provider(mParseProviderArgs, new ProviderInfo());
if (outError[0] != null) {
sa.recycle();
@@ -3621,6 +3660,10 @@
}
}
+ p.info.encryptionAware = sa.getBoolean(
+ R.styleable.AndroidManifestProvider_encryptionAware,
+ owner.applicationInfo.isEncryptionAware());
+
sa.recycle();
if ((owner.applicationInfo.privateFlags&ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE)
@@ -3632,7 +3675,7 @@
return null;
}
}
-
+
if (cpname == null) {
outError[0] = "<provider> does not include authorities attribute";
return null;
@@ -3675,7 +3718,7 @@
outInfo.metaData, outError)) == null) {
return false;
}
-
+
} else if (parser.getName().equals("grant-uri-permission")) {
TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestGrantUriPermission);
@@ -3699,7 +3742,7 @@
if (str != null) {
pa = new PatternMatcher(str, PatternMatcher.PATTERN_SIMPLE_GLOB);
}
-
+
sa.recycle();
if (pa != null) {
@@ -3746,7 +3789,7 @@
if (writePermission == null) {
writePermission = permission;
}
-
+
boolean havePerm = false;
if (readPermission != null) {
readPermission = readPermission.intern();
@@ -3769,7 +3812,7 @@
return false;
}
}
-
+
String path = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestPathPermission_path, 0);
if (path != null) {
@@ -3852,10 +3895,10 @@
com.android.internal.R.styleable.AndroidManifestService_enabled);
mParseServiceArgs.tag = "<service>";
}
-
+
mParseServiceArgs.sa = sa;
mParseServiceArgs.flags = flags;
-
+
Service s = new Service(mParseServiceArgs, new ServiceInfo());
if (outError[0] != null) {
sa.recycle();
@@ -3901,6 +3944,10 @@
}
}
+ s.info.encryptionAware = sa.getBoolean(
+ R.styleable.AndroidManifestService_encryptionAware,
+ owner.applicationInfo.isEncryptionAware());
+
sa.recycle();
if ((owner.applicationInfo.privateFlags&ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE)
@@ -3912,7 +3959,7 @@
return null;
}
}
-
+
int outerDepth = parser.getDepth();
int type;
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
@@ -3957,7 +4004,7 @@
private boolean parseAllMetaData(Resources res,
XmlPullParser parser, AttributeSet attrs, String tag,
- Component outInfo, String[] outError)
+ Component<?> outInfo, String[] outError)
throws XmlPullParserException, IOException {
int outerDepth = parser.getDepth();
int type;
@@ -4010,7 +4057,7 @@
}
name = name.intern();
-
+
TypedValue v = sa.peekValue(
com.android.internal.R.styleable.AndroidManifestMetaData_resource);
if (v != null && v.resourceId != 0) {
@@ -4152,7 +4199,7 @@
outInfo.icon = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestIntentFilter_icon, 0);
-
+
outInfo.logo = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestIntentFilter_logo, 0);
@@ -4373,7 +4420,7 @@
public ArrayList<String> mOriginalPackages = null;
public String mRealPackage = null;
public ArrayList<String> mAdoptPermissions = null;
-
+
// We store the application meta-data independently to avoid multiple unwanted references
public Bundle mAppMetaData = null;
@@ -4382,7 +4429,7 @@
// The version name declared for this package.
public String mVersionName;
-
+
// The shared user id that this package wants to use.
public String mSharedUserId;
@@ -4604,7 +4651,7 @@
ComponentName componentName;
String componentShortName;
-
+
public Component(Package _owner) {
owner = _owner;
intents = null;
@@ -4636,7 +4683,7 @@
outInfo.icon = iconVal;
outInfo.nonLocalizedLabel = null;
}
-
+
int logoVal = args.sa.getResourceId(args.logoRes, 0);
if (logoVal != 0) {
outInfo.logo = logoVal;
@@ -4676,11 +4723,11 @@
owner.applicationInfo.processName, pname,
args.flags, args.sepProcesses, args.outError);
}
-
+
if (args.descriptionRes != 0) {
outInfo.descriptionRes = args.sa.getResourceId(args.descriptionRes, 0);
}
-
+
outInfo.enabled = args.sa.getBoolean(args.enabledRes, true);
}
@@ -4691,7 +4738,7 @@
componentName = clone.componentName;
componentShortName = clone.componentShortName;
}
-
+
public ComponentName getComponentName() {
if (componentName != null) {
return componentName;
@@ -4716,7 +4763,7 @@
componentShortName = null;
}
}
-
+
public final static class Permission extends Component<IntentInfo> {
public final PermissionInfo info;
public boolean tree;
@@ -4731,7 +4778,7 @@
super(_owner);
info = _info;
}
-
+
public void setPackageName(String packageName) {
super.setPackageName(packageName);
info.packageName = packageName;
@@ -4853,9 +4900,7 @@
// Make shallow copy so we can store the metadata/libraries safely
ApplicationInfo ai = new ApplicationInfo(p.applicationInfo);
- ai.uid = UserHandle.getUid(userId, ai.uid);
- ai.dataDir = Environment.getDataUserPackageDirectory(ai.volumeUuid, userId, ai.packageName)
- .getAbsolutePath();
+ ai.initForUser(userId);
if ((flags & PackageManager.GET_META_DATA) != 0) {
ai.metaData = p.mAppMetaData;
}
@@ -4880,9 +4925,7 @@
// This is only used to return the ResolverActivity; we will just always
// make a copy.
ai = new ApplicationInfo(ai);
- ai.uid = UserHandle.getUid(userId, ai.uid);
- ai.dataDir = Environment.getDataUserPackageDirectory(ai.volumeUuid, userId, ai.packageName)
- .getAbsolutePath();
+ ai.initForUser(userId);
if (state.stopped) {
ai.flags |= ApplicationInfo.FLAG_STOPPED;
} else {
@@ -4922,7 +4965,7 @@
info = _info;
info.applicationInfo = args.owner.applicationInfo;
}
-
+
public void setPackageName(String packageName) {
super.setPackageName(packageName);
info.packageName = packageName;
@@ -4976,7 +5019,7 @@
info = _info;
info.applicationInfo = args.owner.applicationInfo;
}
-
+
public void setPackageName(String packageName) {
super.setPackageName(packageName);
info.packageName = packageName;
@@ -5019,7 +5062,7 @@
info.applicationInfo = args.owner.applicationInfo;
syncable = false;
}
-
+
public Provider(Provider existingProvider) {
super(existingProvider);
this.info = existingProvider.info;
@@ -5070,7 +5113,7 @@
super(args, _info);
info = _info;
}
-
+
public void setPackageName(String packageName) {
super.setPackageName(packageName);
info.packageName = packageName;
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 04c690b..7669053 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -686,13 +686,6 @@
public native final boolean isUpToDate();
/**
- * Change the locale being used by this asset manager. Not for use by
- * applications.
- * {@hide}
- */
- public native final void setLocale(String locale);
-
- /**
* Get the locales that this asset manager contains data for.
*
* <p>On SDK 21 (Android 5.0: Lollipop) and above, Locale strings are valid
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 477b62c..1a19a58 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -1505,21 +1505,27 @@
*
* @hide
*/
- public static String localeToResourceQualifier(Locale loc) {
+ public static String localesToResourceQualifier(LocaleList locs) {
StringBuilder sb = new StringBuilder();
- boolean l = (loc.getLanguage().length() != 0);
- boolean c = (loc.getCountry().length() != 0);
- boolean s = (loc.getScript().length() != 0);
- boolean v = (loc.getVariant().length() != 0);
- // TODO: take script and extensions into account
- if (l) {
- sb.append(loc.getLanguage());
- if (c) {
- sb.append("-r").append(loc.getCountry());
- if (s) {
- sb.append("-s").append(loc.getScript());
- if (v) {
- sb.append("-v").append(loc.getVariant());
+ for (int i = 0; i < locs.size(); i++) {
+ Locale loc = locs.get(i);
+ boolean l = (loc.getLanguage().length() != 0);
+ boolean c = (loc.getCountry().length() != 0);
+ boolean s = (loc.getScript().length() != 0);
+ boolean v = (loc.getVariant().length() != 0);
+ // TODO: take script and extensions into account
+ if (l) {
+ if (sb.length() != 0) {
+ sb.append(",");
+ }
+ sb.append(loc.getLanguage());
+ if (c) {
+ sb.append("-r").append(loc.getCountry());
+ if (s) {
+ sb.append("-s").append(loc.getScript());
+ if (v) {
+ sb.append("-v").append(loc.getVariant());
+ }
}
}
}
@@ -1544,9 +1550,11 @@
}
}
- // TODO: send the whole locale list
- if (config.locale != null && !config.locale.getLanguage().isEmpty()) {
- parts.add(localeToResourceQualifier(config.locale));
+ if (!config.mLocaleList.isEmpty()) {
+ final String resourceQualifier = localesToResourceQualifier(config.mLocaleList);
+ if (!resourceQualifier.isEmpty()) {
+ parts.add(resourceQualifier);
+ }
}
switch (config.screenLayout & Configuration.SCREENLAYOUT_LAYOUTDIR_MASK) {
diff --git a/core/java/android/ddm/DdmHandleViewDebug.java b/core/java/android/ddm/DdmHandleViewDebug.java
index be48633..5539dc9 100644
--- a/core/java/android/ddm/DdmHandleViewDebug.java
+++ b/core/java/android/ddm/DdmHandleViewDebug.java
@@ -16,7 +16,6 @@
package android.ddm;
-import android.opengl.GLUtils;
import android.util.Log;
import android.view.View;
import android.view.ViewDebug;
@@ -41,9 +40,6 @@
* Support for these features are advertised via {@link DdmHandleHello}.
*/
public class DdmHandleViewDebug extends ChunkHandler {
- /** Enable/Disable tracing of OpenGL calls. */
- public static final int CHUNK_VUGL = type("VUGL");
-
/** List {@link ViewRootImpl}'s of this process. */
private static final int CHUNK_VULW = type("VULW");
@@ -97,7 +93,6 @@
private DdmHandleViewDebug() {}
public static void register() {
- DdmServer.registerHandler(CHUNK_VUGL, sInstance);
DdmServer.registerHandler(CHUNK_VULW, sInstance);
DdmServer.registerHandler(CHUNK_VURT, sInstance);
DdmServer.registerHandler(CHUNK_VUOP, sInstance);
@@ -115,9 +110,7 @@
public Chunk handleChunk(Chunk request) {
int type = request.type;
- if (type == CHUNK_VUGL) {
- return handleOpenGlTrace(request);
- } else if (type == CHUNK_VULW) {
+ if (type == CHUNK_VULW) {
return listWindows();
}
@@ -165,12 +158,6 @@
}
}
- private Chunk handleOpenGlTrace(Chunk request) {
- ByteBuffer in = wrapChunk(request);
- GLUtils.setTracingLevel(in.getInt());
- return null; // empty response
- }
-
/** Returns the list of windows owned by this client. */
private Chunk listWindows() {
String[] windowNames = WindowManagerGlobal.getInstance().getViewRootNames();
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 1fc69c0..73bb426 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -3182,8 +3182,8 @@
}
/**
- * Sets GPS processing method. It will store up to 32 characters
- * in JPEG EXIF header.
+ * Sets GPS processing method. The method will be stored in a UTF-8 string up to 31 bytes
+ * long, in the JPEG EXIF header.
*
* @param processing_method The processing method to get this location.
*/
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 30cdfd3..2fe8fb6 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -78,14 +78,14 @@
sSensorModuleInitialized = true;
nativeClassInit();
}
- }
- // initialize the sensor list
- for (int index = 0;;++index) {
- Sensor sensor = new Sensor();
- if (!nativeGetSensorAtIndex(mNativeInstance, sensor, index)) break;
- mFullSensorsList.add(sensor);
- mHandleToSensor.append(sensor.getHandle(), sensor);
+ // initialize the sensor list
+ for (int index = 0;;++index) {
+ Sensor sensor = new Sensor();
+ if (!nativeGetSensorAtIndex(mNativeInstance, sensor, index)) break;
+ mFullSensorsList.add(sensor);
+ mHandleToSensor.append(sensor.getHandle(), sensor);
+ }
}
}
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 1fcfaca..51796eb 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -513,7 +513,7 @@
* {@link CameraManager#registerAvailabilityCallback} to be notified of such availability
* changes.</p>
*
- * @see registerAvailabilityCallback
+ * @see #registerAvailabilityCallback
*/
public static abstract class AvailabilityCallback {
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 35a1d96..f61892e 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -472,13 +472,13 @@
* <li>The maximum available resolution for RAW_SENSOR streams
* will match either the value in
* {@link CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE android.sensor.info.pixelArraySize} or
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.</li>
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.</li>
* <li>All DNG-related optional metadata entries are provided
* by the camera device.</li>
* </ul>
*
- * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
* @see CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
* @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
*/
public static final int REQUEST_AVAILABLE_CAPABILITIES_RAW = 3;
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 3f566eb..67835a0 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -556,6 +556,10 @@
* Set a capture request field to a value. The field definitions can be
* found in {@link CaptureRequest}.
*
+ * <p>Setting a field to {@code null} will remove that field from the capture request.
+ * Unless the field is optional, removing it will likely produce an error from the camera
+ * device when the request is submitted.</p>
+ *
* @param key The metadata field to write.
* @param value The value to set the field to, which must be of a matching
* type to the key.
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index 308a219..6effc0d 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -93,7 +93,8 @@
public static final int RESULT_TIMEOUT = 1;
public static final int RESULT_SOURCE_NOT_AVAILABLE = 2;
public static final int RESULT_TARGET_NOT_AVAILABLE = 3;
- public static final int RESULT_ALREADY_IN_PROGRESS = 4;
+
+ @Deprecated public static final int RESULT_ALREADY_IN_PROGRESS = 4;
public static final int RESULT_EXCEPTION = 5;
public static final int RESULT_INCORRECT_MODE = 6;
public static final int RESULT_COMMUNICATION_FAILED = 7;
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index c8b45c7..174291e 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -61,10 +61,14 @@
// Registers an input devices changed listener.
void registerInputDevicesChangedListener(IInputDevicesChangedListener listener);
+ // Queries whether the device is currently in tablet mode
+ int isInTabletMode();
// Registers a tablet mode change listener
void registerTabletModeChangedListener(ITabletModeChangedListener listener);
// Input device vibrator control.
void vibrate(int deviceId, in long[] pattern, int repeat, IBinder token);
void cancelVibrate(int deviceId, IBinder token);
+
+ void setPointerIconShape(int shapeId);
}
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index bae5757..201afee 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -19,6 +19,7 @@
import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils;
+import android.annotation.IntDef;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.content.Context;
@@ -39,6 +40,8 @@
import android.view.InputDevice;
import android.view.InputEvent;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
@@ -105,13 +108,14 @@
* of a key character map for a particular keyboard layout. The label on the receiver
* is used to name the collection of keyboard layouts provided by this receiver in the
* keyboard layout settings.
- * <pre></code>
+ * <pre><code>
* <?xml version="1.0" encoding="utf-8"?>
* <keyboard-layouts xmlns:android="http://schemas.android.com/apk/res/android">
* <keyboard-layout android:name="keyboard_layout_english_us"
* android:label="@string/keyboard_layout_english_us_label"
* android:keyboardLayout="@raw/keyboard_layout_english_us" />
* </keyboard-layouts>
+ * </pre></code>
* </p><p>
* The <code>android:name</code> attribute specifies an identifier by which
* the keyboard layout will be known in the package.
@@ -179,6 +183,31 @@
*/
public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH = 2; // see InputDispatcher.h
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({SWITCH_STATE_UNKNOWN, SWITCH_STATE_OFF, SWITCH_STATE_ON})
+ public @interface SwitchState {}
+
+ /**
+ * Switch State: Unknown.
+ *
+ * The system has yet to report a valid value for the switch.
+ * @hide
+ */
+ public static final int SWITCH_STATE_UNKNOWN = -1;
+
+ /**
+ * Switch State: Off.
+ * @hide
+ */
+ public static final int SWITCH_STATE_OFF = 0;
+
+ /**
+ * Switch State: On.
+ * @hide
+ */
+ public static final int SWITCH_STATE_ON = 1;
+
private InputManager(IInputManager im) {
mIm = im;
}
@@ -340,6 +369,23 @@
}
/**
+ * Queries whether the device is in tablet mode.
+ *
+ * @return The tablet switch state which is one of {@link #SWITCH_STATE_UNKNOWN},
+ * {@link #SWITCH_STATE_OFF} or {@link #SWITCH_STATE_ON}.
+ * @hide
+ */
+ @SwitchState
+ public int isInTabletMode() {
+ try {
+ return mIm.isInTabletMode();
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Could not get tablet mode state", ex);
+ return SWITCH_STATE_UNKNOWN;
+ }
+ }
+
+ /**
* Register a tablet mode changed listener.
*
* @param listener The listener to register.
@@ -757,6 +803,22 @@
}
}
+ /**
+ * Changes the mouse pointer's icon shape into the specified id.
+ *
+ * @param iconId The id of the pointer graphic, as a value between
+ * {@link PointerIcon.STYLE_ARROW} and {@link PointerIcon.STYLE_GRABBING}.
+ *
+ * @hide
+ */
+ public void setPointerIconShape(int iconId) {
+ try {
+ mIm.setPointerIconShape(iconId);
+ } catch (RemoteException ex) {
+ // Do nothing.
+ }
+ }
+
private void populateInputDevicesLocked() {
if (mInputDevicesChangedListener == null) {
final InputDevicesChangedListener listener = new InputDevicesChangedListener();
diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java
index 32930a7..14bb923 100644
--- a/core/java/android/hardware/radio/RadioManager.java
+++ b/core/java/android/hardware/radio/RadioManager.java
@@ -457,14 +457,16 @@
private final boolean mRds;
private final boolean mTa;
private final boolean mAf;
+ private final boolean mEa;
FmBandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing,
- boolean stereo, boolean rds, boolean ta, boolean af) {
+ boolean stereo, boolean rds, boolean ta, boolean af, boolean ea) {
super(region, type, lowerLimit, upperLimit, spacing);
mStereo = stereo;
mRds = rds;
mTa = ta;
mAf = af;
+ mEa = ea;
}
/** Stereo is supported
@@ -492,6 +494,13 @@
return mAf;
}
+ /** Emergency Announcement is supported
+ * @return {@code true} if Emergency annoucement is supported, {@code false} otherwise.
+ */
+ public boolean isEaSupported() {
+ return mEa;
+ }
+
/* Parcelable implementation */
private FmBandDescriptor(Parcel in) {
super(in);
@@ -499,6 +508,7 @@
mRds = in.readByte() == 1;
mTa = in.readByte() == 1;
mAf = in.readByte() == 1;
+ mEa = in.readByte() == 1;
}
public static final Parcelable.Creator<FmBandDescriptor> CREATOR
@@ -519,6 +529,7 @@
dest.writeByte((byte) (mRds ? 1 : 0));
dest.writeByte((byte) (mTa ? 1 : 0));
dest.writeByte((byte) (mAf ? 1 : 0));
+ dest.writeByte((byte) (mEa ? 1 : 0));
}
@Override
@@ -529,7 +540,8 @@
@Override
public String toString() {
return "FmBandDescriptor [ "+ super.toString() + " mStereo=" + mStereo
- + ", mRds=" + mRds + ", mTa=" + mTa + ", mAf=" + mAf + "]";
+ + ", mRds=" + mRds + ", mTa=" + mTa + ", mAf=" + mAf +
+ ", mEa =" + mEa + "]";
}
@Override
@@ -540,6 +552,7 @@
result = prime * result + (mRds ? 1 : 0);
result = prime * result + (mTa ? 1 : 0);
result = prime * result + (mAf ? 1 : 0);
+ result = prime * result + (mEa ? 1 : 0);
return result;
}
@@ -560,6 +573,8 @@
return false;
if (mAf != other.isAfSupported())
return false;
+ if (mEa != other.isEaSupported())
+ return false;
return true;
}
}
@@ -754,6 +769,7 @@
private final boolean mRds;
private final boolean mTa;
private final boolean mAf;
+ private final boolean mEa;
FmBandConfig(FmBandDescriptor descriptor) {
super((BandDescriptor)descriptor);
@@ -761,15 +777,17 @@
mRds = descriptor.isRdsSupported();
mTa = descriptor.isTaSupported();
mAf = descriptor.isAfSupported();
+ mEa = descriptor.isEaSupported();
}
FmBandConfig(int region, int type, int lowerLimit, int upperLimit, int spacing,
- boolean stereo, boolean rds, boolean ta, boolean af) {
+ boolean stereo, boolean rds, boolean ta, boolean af, boolean ea) {
super(region, type, lowerLimit, upperLimit, spacing);
mStereo = stereo;
mRds = rds;
mTa = ta;
mAf = af;
+ mEa = ea;
}
/** Get stereo enable state
@@ -800,12 +818,21 @@
return mAf;
}
+ /**
+ * Get Emergency announcement enable state
+ * @return the enable state.
+ */
+ public boolean getEa() {
+ return mEa;
+ }
+
private FmBandConfig(Parcel in) {
super(in);
mStereo = in.readByte() == 1;
mRds = in.readByte() == 1;
mTa = in.readByte() == 1;
mAf = in.readByte() == 1;
+ mEa = in.readByte() == 1;
}
public static final Parcelable.Creator<FmBandConfig> CREATOR
@@ -826,6 +853,7 @@
dest.writeByte((byte) (mRds ? 1 : 0));
dest.writeByte((byte) (mTa ? 1 : 0));
dest.writeByte((byte) (mAf ? 1 : 0));
+ dest.writeByte((byte) (mEa ? 1 : 0));
}
@Override
@@ -837,7 +865,7 @@
public String toString() {
return "FmBandConfig [" + super.toString()
+ ", mStereo=" + mStereo + ", mRds=" + mRds + ", mTa=" + mTa
- + ", mAf=" + mAf + "]";
+ + ", mAf=" + mAf + ", mEa =" + mEa + "]";
}
@Override
@@ -848,6 +876,7 @@
result = prime * result + (mRds ? 1 : 0);
result = prime * result + (mTa ? 1 : 0);
result = prime * result + (mAf ? 1 : 0);
+ result = prime * result + (mEa ? 1 : 0);
return result;
}
@@ -868,6 +897,8 @@
return false;
if (mAf != other.mAf)
return false;
+ if (mEa != other.mEa)
+ return false;
return true;
}
@@ -880,6 +911,7 @@
private boolean mRds;
private boolean mTa;
private boolean mAf;
+ private boolean mEa;
/**
* Constructs a new Builder with the defaults from an {@link FmBandDescriptor} .
@@ -893,6 +925,7 @@
mRds = descriptor.isRdsSupported();
mTa = descriptor.isTaSupported();
mAf = descriptor.isAfSupported();
+ mEa = descriptor.isEaSupported();
}
/**
@@ -906,6 +939,7 @@
mRds = config.getRds();
mTa = config.getTa();
mAf = config.getAf();
+ mEa = config.getEa();
}
/**
@@ -917,7 +951,7 @@
FmBandConfig config = new FmBandConfig(mDescriptor.getRegion(),
mDescriptor.getType(), mDescriptor.getLowerLimit(),
mDescriptor.getUpperLimit(), mDescriptor.getSpacing(),
- mStereo, mRds, mTa, mAf);
+ mStereo, mRds, mTa, mAf, mEa);
return config;
}
@@ -956,6 +990,15 @@
mAf = state;
return this;
}
+
+ /** Set Emergency Announcement enable state
+ * @param state The new enable state.
+ * @return the same Builder instance.
+ */
+ public Builder setEa(boolean state) {
+ mEa = state;
+ return this;
+ }
};
}
diff --git a/core/java/android/hardware/radio/RadioMetadata.java b/core/java/android/hardware/radio/RadioMetadata.java
index 8b1851b..b7715da 100644
--- a/core/java/android/hardware/radio/RadioMetadata.java
+++ b/core/java/android/hardware/radio/RadioMetadata.java
@@ -95,11 +95,17 @@
*/
public static final String METADATA_KEY_ART = "android.hardware.radio.metadata.ART";
+ /**
+ * The clock.
+ */
+ public static final String METADATA_KEY_CLOCK = "android.hardware.radio.metadata.CLOCK";
+
private static final int METADATA_TYPE_INVALID = -1;
private static final int METADATA_TYPE_INT = 0;
private static final int METADATA_TYPE_TEXT = 1;
private static final int METADATA_TYPE_BITMAP = 2;
+ private static final int METADATA_TYPE_CLOCK = 3;
private static final ArrayMap<String, Integer> METADATA_KEYS_TYPE;
@@ -116,6 +122,7 @@
METADATA_KEYS_TYPE.put(METADATA_KEY_GENRE, METADATA_TYPE_TEXT);
METADATA_KEYS_TYPE.put(METADATA_KEY_ICON, METADATA_TYPE_BITMAP);
METADATA_KEYS_TYPE.put(METADATA_KEY_ART, METADATA_TYPE_BITMAP);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_CLOCK, METADATA_TYPE_CLOCK);
}
// keep in sync with: system/media/radio/include/system/radio_metadata.h
@@ -131,6 +138,7 @@
private static final int NATIVE_KEY_GENRE = 8;
private static final int NATIVE_KEY_ICON = 9;
private static final int NATIVE_KEY_ART = 10;
+ private static final int NATIVE_KEY_CLOCK = 11;
private static final SparseArray<String> NATIVE_KEY_MAPPING;
@@ -147,6 +155,59 @@
NATIVE_KEY_MAPPING.put(NATIVE_KEY_GENRE, METADATA_KEY_GENRE);
NATIVE_KEY_MAPPING.put(NATIVE_KEY_ICON, METADATA_KEY_ICON);
NATIVE_KEY_MAPPING.put(NATIVE_KEY_ART, METADATA_KEY_ART);
+ NATIVE_KEY_MAPPING.put(NATIVE_KEY_CLOCK, METADATA_KEY_CLOCK);
+ }
+
+ /**
+ * Provides a Clock that can be used to describe time as provided by the Radio.
+ *
+ * The clock is defined by the seconds since epoch at the UTC + 0 timezone
+ * and timezone offset from UTC + 0 represented in number of minutes.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final class Clock implements Parcelable {
+ private final long mUtcEpochSeconds;
+ private final int mTimezoneOffsetMinutes;
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeLong(mUtcEpochSeconds);
+ out.writeInt(mTimezoneOffsetMinutes);
+ }
+
+ public static final Parcelable.Creator<Clock> CREATOR
+ = new Parcelable.Creator<Clock>() {
+ public Clock createFromParcel(Parcel in) {
+ return new Clock(in);
+ }
+
+ public Clock[] newArray(int size) {
+ return new Clock[size];
+ }
+ };
+
+ public Clock(long utcEpochSeconds, int timezoneOffsetMinutes) {
+ mUtcEpochSeconds = utcEpochSeconds;
+ mTimezoneOffsetMinutes = timezoneOffsetMinutes;
+ }
+
+ private Clock(Parcel in) {
+ mUtcEpochSeconds = in.readLong();
+ mTimezoneOffsetMinutes = in.readInt();
+ }
+
+ public long getUtcEpochSeconds() {
+ return mUtcEpochSeconds;
+ }
+
+ public int getTimezoneOffsetMinutes() {
+ return mTimezoneOffsetMinutes;
+ }
}
private final Bundle mBundle;
@@ -212,6 +273,17 @@
return bmp;
}
+ public Clock getClock(String key) {
+ Clock clock = null;
+ try {
+ clock = mBundle.getParcelable(key);
+ } catch (Exception e) {
+ // ignore, value was not a clock.
+ Log.w(TAG, "Failed to retrieve a key as Clock.", e);
+ }
+ return clock;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -389,6 +461,27 @@
}
/**
+ * Put a {@link RadioMetadata.Clock} into the meta data. Custom keys may be used, but if the
+ * METADATA_KEYs defined in this class are used they may only be one of the following:
+ * <ul>
+ * <li>{@link #MEADATA_KEY_CLOCK}</li>
+ * </ul>
+ *
+ * @param utcSecondsSinceEpoch Number of seconds since epoch for UTC + 0 timezone.
+ * @param timezoneOffsetInMinutes Offset of timezone from UTC + 0 in minutes.
+ * @return the same Builder instance.
+ */
+ public Builder putClock(String key, long utcSecondsSinceEpoch, int timezoneOffsetMinutes) {
+ if (!METADATA_KEYS_TYPE.containsKey(key) ||
+ METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_CLOCK) {
+ throw new IllegalArgumentException("The " + key
+ + " key cannot be used to put a RadioMetadata.Clock.");
+ }
+ mBundle.putParcelable(key, new Clock(utcSecondsSinceEpoch, timezoneOffsetMinutes));
+ return this;
+ }
+
+ /**
* Creates a {@link RadioMetadata} instance with the specified fields.
*
* @return a new {@link RadioMetadata} object
@@ -446,4 +539,16 @@
return 0;
}
}
+
+ int putClockFromNative(int nativeKey, long utcEpochSeconds, int timezoneOffsetInMinutes) {
+ Log.d(TAG, "putClockFromNative()");
+ String key = getKeyFromNativeKey(nativeKey);
+ if (!METADATA_KEYS_TYPE.containsKey(key) ||
+ METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_CLOCK) {
+ return -1;
+ }
+ mBundle.putParcelable(key, new RadioMetadata.Clock(
+ utcEpochSeconds, timezoneOffsetInMinutes));
+ return 0;
+ }
}
diff --git a/core/java/android/hardware/radio/RadioModule.java b/core/java/android/hardware/radio/RadioModule.java
index 15916ae..fc7d0d2 100644
--- a/core/java/android/hardware/radio/RadioModule.java
+++ b/core/java/android/hardware/radio/RadioModule.java
@@ -89,6 +89,7 @@
static final int EVENT_METADATA = 4;
static final int EVENT_TA = 5;
static final int EVENT_AF_SWITCH = 6;
+ static final int EVENT_EA = 7;
static final int EVENT_CONTROL = 100;
static final int EVENT_SERVER_DIED = 101;
@@ -170,6 +171,10 @@
callback.onTrafficAnnouncement(msg.arg2 == 1);
}
break;
+ case EVENT_EA:
+ if (callback != null) {
+ callback.onEmergencyAnnouncement(msg.arg2 == 1);
+ }
case EVENT_CONTROL:
if (callback != null) {
callback.onControlChanged(msg.arg2 == 1);
diff --git a/core/java/android/hardware/radio/RadioTuner.java b/core/java/android/hardware/radio/RadioTuner.java
index 376900a..5c82555 100644
--- a/core/java/android/hardware/radio/RadioTuner.java
+++ b/core/java/android/hardware/radio/RadioTuner.java
@@ -281,6 +281,10 @@
*/
public void onTrafficAnnouncement(boolean active) {}
/**
+ * onEmergencyAnnouncement() is called when an emergency annoucement starts and stops.
+ */
+ public void onEmergencyAnnouncement(boolean active) {}
+ /**
* onAntennaState() is called when the antenna is connected or disconnected.
*/
public void onAntennaState(boolean connected) {}
diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java
index b777e8c..d1000ad 100644
--- a/core/java/android/inputmethodservice/KeyboardView.java
+++ b/core/java/android/inputmethodservice/KeyboardView.java
@@ -1,12 +1,12 @@
/*
* Copyright (C) 2008-2009 Google Inc.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -57,7 +57,7 @@
/**
* A view that renders a virtual {@link Keyboard}. It handles rendering of keys and
* detecting key presses and touch movements.
- *
+ *
* @attr ref android.R.styleable#KeyboardView_keyBackground
* @attr ref android.R.styleable#KeyboardView_keyPreviewLayout
* @attr ref android.R.styleable#KeyboardView_keyPreviewOffset
@@ -73,7 +73,7 @@
* Listener for virtual keyboard events.
*/
public interface OnKeyboardActionListener {
-
+
/**
* Called when the user presses a key. This is sent before the {@link #onKey} is called.
* For keys that repeat, this is only called once.
@@ -81,7 +81,7 @@
* key, the value will be zero.
*/
void onPress(int primaryCode);
-
+
/**
* Called when the user releases a key. This is sent after the {@link #onKey} is called.
* For keys that repeat, this is only called once.
@@ -106,22 +106,22 @@
* @param text the sequence of characters to be displayed.
*/
void onText(CharSequence text);
-
+
/**
* Called when the user quickly moves the finger from right to left.
*/
void swipeLeft();
-
+
/**
* Called when the user quickly moves the finger from left to right.
*/
void swipeRight();
-
+
/**
* Called when the user quickly moves the finger from up to down.
*/
void swipeDown();
-
+
/**
* Called when the user quickly moves the finger from down to up.
*/
@@ -131,8 +131,8 @@
private static final boolean DEBUG = false;
private static final int NOT_A_KEY = -1;
private static final int[] KEY_DELETE = { Keyboard.KEYCODE_DELETE };
- private static final int[] LONG_PRESSABLE_STATE_SET = { R.attr.state_long_pressable };
-
+ private static final int[] LONG_PRESSABLE_STATE_SET = { R.attr.state_long_pressable };
+
private Keyboard mKeyboard;
private int mCurrentKeyIndex = NOT_A_KEY;
private int mLabelTextSize;
@@ -141,7 +141,7 @@
private float mShadowRadius;
private int mShadowColor;
private float mBackgroundDimAmount;
-
+
private TextView mPreviewText;
private PopupWindow mPreviewPopup;
private int mPreviewTextSizeLarge;
@@ -162,7 +162,7 @@
/** Listener for {@link OnKeyboardActionListener}. */
private OnKeyboardActionListener mKeyboardActionListener;
-
+
private static final int MSG_SHOW_PREVIEW = 1;
private static final int MSG_REMOVE_PREVIEW = 2;
private static final int MSG_REPEAT = 3;
@@ -171,7 +171,7 @@
private static final int DELAY_BEFORE_PREVIEW = 0;
private static final int DELAY_AFTER_PREVIEW = 70;
private static final int DEBOUNCE_TIME = 70;
-
+
private int mVerticalCorrection;
private int mProximityThreshold;
@@ -187,10 +187,10 @@
private int mStartY;
private boolean mProximityCorrectOn;
-
+
private Paint mPaint;
private Rect mPadding;
-
+
private long mDownTime;
private long mLastMoveTime;
private int mLastKey;
@@ -253,28 +253,7 @@
/** Whether the requirement of a headset to hear passwords if accessibility is enabled is announced. */
private boolean mHeadsetRequiredToHearPasswordsAnnounced;
- Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_SHOW_PREVIEW:
- showKey(msg.arg1);
- break;
- case MSG_REMOVE_PREVIEW:
- mPreviewText.setVisibility(INVISIBLE);
- break;
- case MSG_REPEAT:
- if (repeatKey()) {
- Message repeat = Message.obtain(this, MSG_REPEAT);
- sendMessageDelayed(repeat, REPEAT_INTERVAL);
- }
- break;
- case MSG_LONGPRESS:
- openPopupIfRequired((MotionEvent) msg.obj);
- break;
- }
- }
- };
+ Handler mHandler;
public KeyboardView(Context context, AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.keyboardViewStyle);
@@ -298,7 +277,7 @@
int keyTextSize = 0;
int n = a.getIndexCount();
-
+
for (int i = 0; i < n; i++) {
int attr = a.getIndex(i);
@@ -338,7 +317,7 @@
break;
}
}
-
+
a = mContext.obtainStyledAttributes(
com.android.internal.R.styleable.Theme);
mBackgroundDimAmount = a.getFloat(android.R.styleable.Theme_backgroundDimAmount, 0.5f);
@@ -352,16 +331,16 @@
} else {
mShowPreview = false;
}
-
+
mPreviewPopup.setTouchable(false);
-
+
mPopupKeyboard = new PopupWindow(context);
mPopupKeyboard.setBackgroundDrawable(null);
//mPopupKeyboard.setClippingEnabled(false);
-
+
mPopupParent = this;
//mPredicting = true;
-
+
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setTextSize(keyTextSize);
@@ -380,64 +359,94 @@
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
resetMultiTap();
- initGestureDetector();
}
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ initGestureDetector();
+ if (mHandler == null) {
+ mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_SHOW_PREVIEW:
+ showKey(msg.arg1);
+ break;
+ case MSG_REMOVE_PREVIEW:
+ mPreviewText.setVisibility(INVISIBLE);
+ break;
+ case MSG_REPEAT:
+ if (repeatKey()) {
+ Message repeat = Message.obtain(this, MSG_REPEAT);
+ sendMessageDelayed(repeat, REPEAT_INTERVAL);
+ }
+ break;
+ case MSG_LONGPRESS:
+ openPopupIfRequired((MotionEvent) msg.obj);
+ break;
+ }
+ }
+ };
+ }
+ }
private void initGestureDetector() {
- mGestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
- @Override
- public boolean onFling(MotionEvent me1, MotionEvent me2,
- float velocityX, float velocityY) {
- if (mPossiblePoly) return false;
- final float absX = Math.abs(velocityX);
- final float absY = Math.abs(velocityY);
- float deltaX = me2.getX() - me1.getX();
- float deltaY = me2.getY() - me1.getY();
- int travelX = getWidth() / 2; // Half the keyboard width
- int travelY = getHeight() / 2; // Half the keyboard height
- mSwipeTracker.computeCurrentVelocity(1000);
- final float endingVelocityX = mSwipeTracker.getXVelocity();
- final float endingVelocityY = mSwipeTracker.getYVelocity();
- boolean sendDownKey = false;
- if (velocityX > mSwipeThreshold && absY < absX && deltaX > travelX) {
- if (mDisambiguateSwipe && endingVelocityX < velocityX / 4) {
- sendDownKey = true;
- } else {
- swipeRight();
- return true;
+ if (mGestureDetector == null) {
+ mGestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
+ @Override
+ public boolean onFling(MotionEvent me1, MotionEvent me2,
+ float velocityX, float velocityY) {
+ if (mPossiblePoly) return false;
+ final float absX = Math.abs(velocityX);
+ final float absY = Math.abs(velocityY);
+ float deltaX = me2.getX() - me1.getX();
+ float deltaY = me2.getY() - me1.getY();
+ int travelX = getWidth() / 2; // Half the keyboard width
+ int travelY = getHeight() / 2; // Half the keyboard height
+ mSwipeTracker.computeCurrentVelocity(1000);
+ final float endingVelocityX = mSwipeTracker.getXVelocity();
+ final float endingVelocityY = mSwipeTracker.getYVelocity();
+ boolean sendDownKey = false;
+ if (velocityX > mSwipeThreshold && absY < absX && deltaX > travelX) {
+ if (mDisambiguateSwipe && endingVelocityX < velocityX / 4) {
+ sendDownKey = true;
+ } else {
+ swipeRight();
+ return true;
+ }
+ } else if (velocityX < -mSwipeThreshold && absY < absX && deltaX < -travelX) {
+ if (mDisambiguateSwipe && endingVelocityX > velocityX / 4) {
+ sendDownKey = true;
+ } else {
+ swipeLeft();
+ return true;
+ }
+ } else if (velocityY < -mSwipeThreshold && absX < absY && deltaY < -travelY) {
+ if (mDisambiguateSwipe && endingVelocityY > velocityY / 4) {
+ sendDownKey = true;
+ } else {
+ swipeUp();
+ return true;
+ }
+ } else if (velocityY > mSwipeThreshold && absX < absY / 2 && deltaY > travelY) {
+ if (mDisambiguateSwipe && endingVelocityY < velocityY / 4) {
+ sendDownKey = true;
+ } else {
+ swipeDown();
+ return true;
+ }
}
- } else if (velocityX < -mSwipeThreshold && absY < absX && deltaX < -travelX) {
- if (mDisambiguateSwipe && endingVelocityX > velocityX / 4) {
- sendDownKey = true;
- } else {
- swipeLeft();
- return true;
- }
- } else if (velocityY < -mSwipeThreshold && absX < absY && deltaY < -travelY) {
- if (mDisambiguateSwipe && endingVelocityY > velocityY / 4) {
- sendDownKey = true;
- } else {
- swipeUp();
- return true;
- }
- } else if (velocityY > mSwipeThreshold && absX < absY / 2 && deltaY > travelY) {
- if (mDisambiguateSwipe && endingVelocityY < velocityY / 4) {
- sendDownKey = true;
- } else {
- swipeDown();
- return true;
- }
- }
- if (sendDownKey) {
- detectAndSendKey(mDownKey, mStartX, mStartY, me1.getEventTime());
+ if (sendDownKey) {
+ detectAndSendKey(mDownKey, mStartX, mStartY, me1.getEventTime());
+ }
+ return false;
}
- return false;
- }
- });
+ });
- mGestureDetector.setIsLongpressEnabled(false);
+ mGestureDetector.setIsLongpressEnabled(false);
+ }
}
public void setOnKeyboardActionListener(OnKeyboardActionListener listener) {
@@ -487,7 +496,7 @@
public Keyboard getKeyboard() {
return mKeyboard;
}
-
+
/**
* Sets the state of the shift key of the keyboard, if any.
* @param shifted whether or not to enable the state of the shift key
@@ -520,7 +529,7 @@
/**
* Enables or disables the key feedback popup. This is a popup that shows a magnified
- * version of the depressed key. By default the preview is enabled.
+ * version of the depressed key. By default the preview is enabled.
* @param previewEnabled whether or not to enable the key feedback popup
* @see #isPreviewEnabled()
*/
@@ -536,14 +545,14 @@
public boolean isPreviewEnabled() {
return mShowPreview;
}
-
+
public void setVerticalCorrection(int verticalOffset) {
-
+
}
public void setPopupParent(View v) {
mPopupParent = v;
}
-
+
public void setPopupOffset(int x, int y) {
mMiniKeyboardOffsetX = x;
mMiniKeyboardOffsetY = y;
@@ -569,9 +578,9 @@
return mProximityCorrectOn;
}
- /**
+ /**
* Popup keyboard close button clicked.
- * @hide
+ * @hide
*/
public void onClick(View v) {
dismissPopupKeyboard();
@@ -654,9 +663,9 @@
}
final Canvas canvas = mCanvas;
canvas.clipRect(mDirtyRect, Op.REPLACE);
-
+
if (mKeyboard == null) return;
-
+
final Paint paint = mPaint;
final Drawable keyBackground = mKeyBackground;
final Rect clipRegion = mClipRegion;
@@ -689,15 +698,15 @@
// Switch the character to uppercase if shift is pressed
String label = key.label == null? null : adjustCase(key.label).toString();
-
+
final Rect bounds = keyBackground.getBounds();
- if (key.width != bounds.right ||
+ if (key.width != bounds.right ||
key.height != bounds.bottom) {
keyBackground.setBounds(0, 0, key.width, key.height);
}
canvas.translate(key.x + kbdPaddingLeft, key.y + kbdPaddingTop);
keyBackground.draw(canvas);
-
+
if (label != null) {
// For characters, use large font. For labels like "Done", use small font.
if (label.length() > 1 && key.codes.length < 2) {
@@ -719,12 +728,12 @@
// Turn off drop shadow
paint.setShadowLayer(0, 0, 0, 0);
} else if (key.icon != null) {
- final int drawableX = (key.width - padding.left - padding.right
+ final int drawableX = (key.width - padding.left - padding.right
- key.icon.getIntrinsicWidth()) / 2 + padding.left;
- final int drawableY = (key.height - padding.top - padding.bottom
+ final int drawableY = (key.height - padding.top - padding.bottom
- key.icon.getIntrinsicHeight()) / 2 + padding.top;
canvas.translate(drawableX, drawableY);
- key.icon.setBounds(0, 0,
+ key.icon.setBounds(0, 0,
key.icon.getIntrinsicWidth(), key.icon.getIntrinsicHeight());
key.icon.draw(canvas);
canvas.translate(-drawableX, -drawableY);
@@ -748,7 +757,7 @@
paint.setColor(0xFF00FF00);
canvas.drawCircle((mStartX + mLastX) / 2, (mStartY + mLastY) / 2, 2, paint);
}
-
+
mDrawPending = false;
mDirtyRect.setEmpty();
}
@@ -769,8 +778,8 @@
primaryIndex = nearestKeyIndices[i];
}
- if (((mProximityCorrectOn
- && (dist = key.squaredDistanceFrom(x, y)) < mProximityThreshold)
+ if (((mProximityCorrectOn
+ && (dist = key.squaredDistanceFrom(x, y)) < mProximityThreshold)
|| isInside)
&& key.codes[0] > 32) {
// Find insertion point
@@ -779,9 +788,9 @@
closestKeyDist = dist;
closestKey = nearestKeyIndices[i];
}
-
+
if (allKeys == null) continue;
-
+
for (int j = 0; j < mDistances.length; j++) {
if (mDistances[j] > dist) {
// Make space for nCodes codes
@@ -846,11 +855,11 @@
return adjustCase(key.label);
}
}
-
+
private void showPreview(int keyIndex) {
int oldKeyIndex = mCurrentKeyIndex;
final PopupWindow previewPopup = mPreviewPopup;
-
+
mCurrentKeyIndex = keyIndex;
// Release the old key and press the new key
final Key[] keys = mKeys;
@@ -884,7 +893,7 @@
if (previewPopup.isShowing()) {
if (keyIndex == NOT_A_KEY) {
mHandler.sendMessageDelayed(mHandler
- .obtainMessage(MSG_REMOVE_PREVIEW),
+ .obtainMessage(MSG_REMOVE_PREVIEW),
DELAY_AFTER_PREVIEW);
}
}
@@ -894,20 +903,20 @@
showKey(keyIndex);
} else {
mHandler.sendMessageDelayed(
- mHandler.obtainMessage(MSG_SHOW_PREVIEW, keyIndex, 0),
+ mHandler.obtainMessage(MSG_SHOW_PREVIEW, keyIndex, 0),
DELAY_BEFORE_PREVIEW);
}
}
}
}
-
+
private void showKey(final int keyIndex) {
final PopupWindow previewPopup = mPreviewPopup;
final Key[] keys = mKeys;
if (keyIndex < 0 || keyIndex >= mKeys.length) return;
Key key = keys[keyIndex];
if (key.icon != null) {
- mPreviewText.setCompoundDrawables(null, null, null,
+ mPreviewText.setCompoundDrawables(null, null, null,
key.iconPreview != null ? key.iconPreview : key.icon);
mPreviewText.setText(null);
} else {
@@ -921,9 +930,9 @@
mPreviewText.setTypeface(Typeface.DEFAULT);
}
}
- mPreviewText.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
+ mPreviewText.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
- int popupWidth = Math.max(mPreviewText.getMeasuredWidth(), key.width
+ int popupWidth = Math.max(mPreviewText.getMeasuredWidth(), key.width
+ mPreviewText.getPaddingLeft() + mPreviewText.getPaddingRight());
final int popupHeight = mPreviewHeight;
LayoutParams lp = mPreviewText.getLayoutParams();
@@ -969,7 +978,7 @@
} else {
previewPopup.setWidth(popupWidth);
previewPopup.setHeight(popupHeight);
- previewPopup.showAtLocation(mPopupParent, Gravity.NO_GRAVITY,
+ previewPopup.showAtLocation(mPopupParent, Gravity.NO_GRAVITY,
mPopupPreviewX, mPopupPreviewY);
}
mPreviewText.setVisibility(VISIBLE);
@@ -1030,7 +1039,7 @@
/**
* Requests a redraw of the entire keyboard. Calling {@link #invalidate} is not sufficient
- * because the keyboard renders the keys to an off-screen buffer and an invalidate() only
+ * because the keyboard renders the keys to an off-screen buffer and an invalidate() only
* draws the cached buffer.
* @see #invalidateKey(int)
*/
@@ -1054,10 +1063,10 @@
}
final Key key = mKeys[keyIndex];
mInvalidatedKey = key;
- mDirtyRect.union(key.x + mPaddingLeft, key.y + mPaddingTop,
+ mDirtyRect.union(key.x + mPaddingLeft, key.y + mPaddingTop,
key.x + key.width + mPaddingLeft, key.y + key.height + mPaddingTop);
onBufferDraw();
- invalidate(key.x + mPaddingLeft, key.y + mPaddingTop,
+ invalidate(key.x + mPaddingLeft, key.y + mPaddingTop,
key.x + key.width + mPaddingLeft, key.y + key.height + mPaddingTop);
}
@@ -1070,7 +1079,7 @@
return false;
}
- Key popupKey = mKeys[mCurrentKey];
+ Key popupKey = mKeys[mCurrentKey];
boolean result = onLongPress(popupKey);
if (result) {
mAbortKey = true;
@@ -1105,12 +1114,12 @@
mKeyboardActionListener.onKey(primaryCode, keyCodes);
dismissPopupKeyboard();
}
-
+
public void onText(CharSequence text) {
mKeyboardActionListener.onText(text);
dismissPopupKeyboard();
}
-
+
public void swipeLeft() { }
public void swipeRight() { }
public void swipeUp() { }
@@ -1125,7 +1134,7 @@
//mInputView.setSuggest(mSuggest);
Keyboard keyboard;
if (popupKey.popupCharacters != null) {
- keyboard = new Keyboard(getContext(), popupKeyboardId,
+ keyboard = new Keyboard(getContext(), popupKeyboardId,
popupKey.popupCharacters, -1, getPaddingLeft() + getPaddingRight());
} else {
keyboard = new Keyboard(getContext(), popupKeyboardId);
@@ -1133,9 +1142,9 @@
mMiniKeyboard.setKeyboard(keyboard);
mMiniKeyboard.setPopupParent(this);
mMiniKeyboardContainer.measure(
- MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),
+ MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),
MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST));
-
+
mMiniKeyboardCache.put(popupKey, mMiniKeyboardContainer);
} else {
mMiniKeyboard = (KeyboardView) mMiniKeyboardContainer.findViewById(
@@ -1184,7 +1193,7 @@
@Override
public boolean onTouchEvent(MotionEvent me) {
- // Convert multi-pointer up/down events to single up/down events to
+ // Convert multi-pointer up/down events to single up/down events to
// deal with the typical multi-pointer behavior of two-thumb typing
final int pointerCount = me.getPointerCount();
final int action = me.getAction();
@@ -1250,7 +1259,7 @@
mHandler.removeMessages(MSG_LONGPRESS);
return true;
}
-
+
// Needs to be called after the gesture detector gets a turn, as it may have
// displayed the mini keyboard
if (mMiniKeyboardOnScreen && action != MotionEvent.ACTION_CANCEL) {
@@ -1272,7 +1281,7 @@
mDownTime = me.getEventTime();
mLastMoveTime = mDownTime;
checkMultiTap(eventTime, keyIndex);
- mKeyboardActionListener.onPress(keyIndex != NOT_A_KEY ?
+ mKeyboardActionListener.onPress(keyIndex != NOT_A_KEY ?
mKeys[keyIndex].codes[0] : 0);
if (mCurrentKey >= 0 && mKeys[mCurrentKey].repeatable) {
mRepeatKeyIndex = mCurrentKey;
@@ -1371,11 +1380,11 @@
detectAndSendKey(mCurrentKey, key.x, key.y, mLastTapTime);
return true;
}
-
+
protected void swipeRight() {
mKeyboardActionListener.swipeRight();
}
-
+
protected void swipeLeft() {
mKeyboardActionListener.swipeLeft();
}
@@ -1393,7 +1402,7 @@
mPreviewPopup.dismiss();
}
removeMessages();
-
+
dismissPopupKeyboard();
mBuffer = null;
mCanvas = null;
@@ -1401,9 +1410,11 @@
}
private void removeMessages() {
- mHandler.removeMessages(MSG_REPEAT);
- mHandler.removeMessages(MSG_LONGPRESS);
- mHandler.removeMessages(MSG_SHOW_PREVIEW);
+ if (mHandler != null) {
+ mHandler.removeMessages(MSG_REPEAT);
+ mHandler.removeMessages(MSG_LONGPRESS);
+ mHandler.removeMessages(MSG_SHOW_PREVIEW);
+ }
}
@Override
@@ -1434,7 +1445,7 @@
mLastTapTime = -1;
mInMultiTap = false;
}
-
+
private void checkMultiTap(long eventTime, int keyIndex) {
if (keyIndex == NOT_A_KEY) return;
Key key = mKeys[keyIndex];
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 444548f..ad9058f 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -901,8 +901,12 @@
* Tells the underlying networking system that the caller wants to
* begin using the named feature. The interpretation of {@code feature}
* is completely up to each networking implementation.
- * <p>This method requires the caller to hold the permission
- * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+ *
+ * <p>This method requires the caller to hold either the
+ * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+ * or the ability to modify system settings as determined by
+ * {@link android.provider.Settings.System#canWrite}.</p>
+ *
* @param networkType specifies which network the request pertains to
* @param feature the name of the feature to be used
* @return an integer value representing the outcome of the request.
@@ -952,8 +956,12 @@
* Tells the underlying networking system that the caller is finished
* using the named feature. The interpretation of {@code feature}
* is completely up to each networking implementation.
- * <p>This method requires the caller to hold the permission
- * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+ *
+ * <p>This method requires the caller to hold either the
+ * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+ * or the ability to modify system settings as determined by
+ * {@link android.provider.Settings.System#canWrite}.</p>
+ *
* @param networkType specifies which network the request pertains to
* @param feature the name of the feature that is no longer needed
* @return an integer value representing the outcome of the request.
@@ -1339,8 +1347,12 @@
* Ensure that a network route exists to deliver traffic to the specified
* host via the specified network interface. An attempt to add a route that
* already exists is ignored, but treated as successful.
- * <p>This method requires the caller to hold the permission
- * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+ *
+ * <p>This method requires the caller to hold either the
+ * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+ * or the ability to modify system settings as determined by
+ * {@link android.provider.Settings.System#canWrite}.</p>
+ *
* @param networkType the type of the network over which traffic to the specified
* host is to be routed
* @param hostAddress the IP address of the host to which the route is desired
@@ -1360,8 +1372,12 @@
* Ensure that a network route exists to deliver traffic to the specified
* host via the specified network interface. An attempt to add a route that
* already exists is ignored, but treated as successful.
- * <p>This method requires the caller to hold the permission
- * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+ *
+ * <p>This method requires the caller to hold either the
+ * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+ * or the ability to modify system settings as determined by
+ * {@link android.provider.Settings.System#canWrite}.</p>
+ *
* @param networkType the type of the network over which traffic to the specified
* host is to be routed
* @param hostAddress the IP address of the host to which the route is desired
@@ -1561,6 +1577,13 @@
return (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
}
+ /** {@hide} */
+ public static final void enforceChangePermission(Context context) {
+ int uid = Binder.getCallingUid();
+ Settings.checkAndNoteChangeNetworkStateOperation(context, uid, Settings
+ .getPackageNameForUid(context, uid), true /* throwException */);
+ }
+
/** {@hide */
public static final void enforceTetherChangePermission(Context context) {
if (context.getResources().getStringArray(
@@ -1571,8 +1594,8 @@
android.Manifest.permission.CONNECTIVITY_INTERNAL, "ConnectivityService");
} else {
int uid = Binder.getCallingUid();
- Settings.checkAndNoteChangeNetworkStateOperation(context, uid, Settings
- .getPackageNameForUid(context, uid), true);
+ Settings.checkAndNoteWriteSettingsOperation(context, uid, Settings
+ .getPackageNameForUid(context, uid), true /* throwException */);
}
}
@@ -1677,8 +1700,11 @@
* allowed between the tethered devices and this device, though upstream net
* access will of course fail until an upstream network interface becomes
* active.
- * <p>This method requires the caller to hold the permission
- * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+ *
+ * <p>This method requires the caller to hold either the
+ * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+ * or the ability to modify system settings as determined by
+ * {@link android.provider.Settings.System#canWrite}.</p>
*
* @param iface the interface name to tether.
* @return error a {@code TETHER_ERROR} value indicating success or failure type
@@ -1695,8 +1721,11 @@
/**
* Stop tethering the named interface.
- * <p>This method requires the caller to hold the permission
- * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+ *
+ * <p>This method requires the caller to hold either the
+ * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+ * or the ability to modify system settings as determined by
+ * {@link android.provider.Settings.System#canWrite}.</p>
*
* @param iface the interface name to untether.
* @return error a {@code TETHER_ERROR} value indicating success or failure type
@@ -1796,8 +1825,11 @@
* attempt to switch to Rndis and subsequently tether the resulting
* interface on {@code true} or turn off tethering and switch off
* Rndis on {@code false}.
- * <p>This method requires the caller to hold the permission
- * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+ *
+ * <p>This method requires the caller to hold either the
+ * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+ * or the ability to modify system settings as determined by
+ * {@link android.provider.Settings.System#canWrite}.</p>
*
* @param enable a boolean - {@code true} to enable tethering
* @return error a {@code TETHER_ERROR} value indicating success or failure type
@@ -2467,8 +2499,11 @@
* network may never attain, and whether a network will attain these states
* is unknown prior to bringing up the network so the framework does not
* know how to go about satisfing a request with these capabilities.
- * <p>This method requires the caller to hold the permission
- * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+ *
+ * <p>This method requires the caller to hold either the
+ * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+ * or the ability to modify system settings as determined by
+ * {@link android.provider.Settings.System#canWrite}.</p>
*
* @param request {@link NetworkRequest} describing this request.
* @param networkCallback The {@link NetworkCallback} to be utilized for this
@@ -2490,8 +2525,12 @@
* network is not found within the given time (in milliseconds) the
* {@link NetworkCallback#unavailable} callback is called. The request must
* still be released normally by calling {@link releaseNetworkRequest}.
- * <p>This method requires the caller to hold the permission
- * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+ *
+ * <p>This method requires the caller to hold either the
+ * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+ * or the ability to modify system settings as determined by
+ * {@link android.provider.Settings.System#canWrite}.</p>
+ *
* @param request {@link NetworkRequest} describing this request.
* @param networkCallback The callbacks to be utilized for this request. Note
* the callbacks must not be shared - they uniquely specify
@@ -2564,8 +2603,12 @@
* network may never attain, and whether a network will attain these states
* is unknown prior to bringing up the network so the framework does not
* know how to go about satisfing a request with these capabilities.
- * <p>This method requires the caller to hold the permission
- * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+ *
+ * <p>This method requires the caller to hold either the
+ * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+ * or the ability to modify system settings as determined by
+ * {@link android.provider.Settings.System#canWrite}.</p>
+ *
* @param request {@link NetworkRequest} describing this request.
* @param operation Action to perform when the network is available (corresponds
* to the {@link NetworkCallback#onAvailable} call. Typically
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 6bfa2a4..1bb0fbb 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -119,8 +119,12 @@
//
// For one such example of this, see b/18867306.
//
- // TODO: Remove this special case altogether.
- if (before.isIPv4Provisioned() && !after.isIPv4Provisioned()) {
+ // Additionally, losing IPv6 provisioning can result in TCP
+ // connections getting stuck until timeouts fire and other
+ // baffling failures. Therefore, loss of either IPv4 or IPv6 on a
+ // previously dualstack network is deemed a lost of provisioning.
+ if ((before.isIPv4Provisioned() && !after.isIPv4Provisioned()) ||
+ (before.isIPv6Provisioned() && !after.isIPv6Provisioned())) {
return ProvisioningChange.LOST_PROVISIONING;
}
return ProvisioningChange.STILL_PROVISIONED;
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index 57eef83..b7a411e 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -288,7 +288,8 @@
} else {
final boolean matchesType = (sForceAllNetworkTypes
|| contains(DATA_USAGE_NETWORK_TYPES, ident.mType));
- return matchesType && ArrayUtils.contains(mMatchSubscriberIds, ident.mSubscriberId);
+ return matchesType && !ArrayUtils.isEmpty(mMatchSubscriberIds)
+ && ArrayUtils.contains(mMatchSubscriberIds, ident.mSubscriberId);
}
}
diff --git a/core/java/android/net/SntpClient.java b/core/java/android/net/SntpClient.java
index 7673011..cf9243f 100644
--- a/core/java/android/net/SntpClient.java
+++ b/core/java/android/net/SntpClient.java
@@ -22,6 +22,7 @@
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
+import java.util.Arrays;
/**
* {@hide}
@@ -38,6 +39,7 @@
public class SntpClient
{
private static final String TAG = "SntpClient";
+ private static final boolean DBG = true;
private static final int REFERENCE_TIME_OFFSET = 16;
private static final int ORIGINATE_TIME_OFFSET = 24;
@@ -47,8 +49,14 @@
private static final int NTP_PORT = 123;
private static final int NTP_MODE_CLIENT = 3;
+ private static final int NTP_MODE_SERVER = 4;
+ private static final int NTP_MODE_BROADCAST = 5;
private static final int NTP_VERSION = 3;
+ private static final int NTP_LEAP_NOSYNC = 3;
+ private static final int NTP_STRATUM_DEATH = 0;
+ private static final int NTP_STRATUM_MAX = 15;
+
// Number of seconds between Jan 1, 1900 and Jan 1, 1970
// 70 years plus 17 leap days
private static final long OFFSET_1900_TO_1970 = ((365L * 70L) + 17L) * 24L * 60L * 60L;
@@ -62,6 +70,12 @@
// round trip time in milliseconds
private long mRoundTripTime;
+ private static class InvalidServerReplyException extends Exception {
+ public InvalidServerReplyException(String message) {
+ super(message);
+ }
+ }
+
/**
* Sends an SNTP request to the given host and processes the response.
*
@@ -70,13 +84,23 @@
* @return true if the transaction was successful.
*/
public boolean requestTime(String host, int timeout) {
+ InetAddress address = null;
+ try {
+ address = InetAddress.getByName(host);
+ } catch (Exception e) {
+ if (DBG) Log.d(TAG, "request time failed: " + e);
+ return false;
+ }
+ return requestTime(address, NTP_PORT, timeout);
+ }
+
+ public boolean requestTime(InetAddress address, int port, int timeout) {
DatagramSocket socket = null;
try {
socket = new DatagramSocket();
socket.setSoTimeout(timeout);
- InetAddress address = InetAddress.getByName(host);
byte[] buffer = new byte[NTP_PACKET_SIZE];
- DatagramPacket request = new DatagramPacket(buffer, buffer.length, address, NTP_PORT);
+ DatagramPacket request = new DatagramPacket(buffer, buffer.length, address, port);
// set mode = 3 (client) and version = 3
// mode is in low 3 bits of first byte
@@ -84,8 +108,8 @@
buffer[0] = NTP_MODE_CLIENT | (NTP_VERSION << 3);
// get current time and write it to the request packet
- long requestTime = System.currentTimeMillis();
- long requestTicks = SystemClock.elapsedRealtime();
+ final long requestTime = System.currentTimeMillis();
+ final long requestTicks = SystemClock.elapsedRealtime();
writeTimeStamp(buffer, TRANSMIT_TIME_OFFSET, requestTime);
socket.send(request);
@@ -93,13 +117,21 @@
// read the response
DatagramPacket response = new DatagramPacket(buffer, buffer.length);
socket.receive(response);
- long responseTicks = SystemClock.elapsedRealtime();
- long responseTime = requestTime + (responseTicks - requestTicks);
+ final long responseTicks = SystemClock.elapsedRealtime();
+ final long responseTime = requestTime + (responseTicks - requestTicks);
// extract the results
- long originateTime = readTimeStamp(buffer, ORIGINATE_TIME_OFFSET);
- long receiveTime = readTimeStamp(buffer, RECEIVE_TIME_OFFSET);
- long transmitTime = readTimeStamp(buffer, TRANSMIT_TIME_OFFSET);
+ final byte leap = (byte) ((buffer[0] >> 6) & 0x3);
+ final byte mode = (byte) (buffer[0] & 0x7);
+ final int stratum = (int) (buffer[1] & 0xff);
+ final long originateTime = readTimeStamp(buffer, ORIGINATE_TIME_OFFSET);
+ final long receiveTime = readTimeStamp(buffer, RECEIVE_TIME_OFFSET);
+ final long transmitTime = readTimeStamp(buffer, TRANSMIT_TIME_OFFSET);
+
+ /* do sanity check according to RFC */
+ // TODO: validate originateTime == requestTime.
+ checkValidServerReply(leap, mode, stratum, transmitTime);
+
long roundTripTime = responseTicks - requestTicks - (transmitTime - receiveTime);
// receiveTime = originateTime + transit + skew
// responseTime = transmitTime + transit - skew
@@ -110,8 +142,10 @@
// = (transit + skew - transit + skew)/2
// = (2 * skew)/2 = skew
long clockOffset = ((receiveTime - originateTime) + (transmitTime - responseTime))/2;
- // if (false) Log.d(TAG, "round trip: " + roundTripTime + " ms");
- // if (false) Log.d(TAG, "clock offset: " + clockOffset + " ms");
+ if (DBG) {
+ Log.d(TAG, "round trip: " + roundTripTime + "ms, " +
+ "clock offset: " + clockOffset + "ms");
+ }
// save our results - use the times on this side of the network latency
// (response rather than request time)
@@ -119,7 +153,7 @@
mNtpTimeReference = responseTicks;
mRoundTripTime = roundTripTime;
} catch (Exception e) {
- if (false) Log.d(TAG, "request time failed: " + e);
+ if (DBG) Log.d(TAG, "request time failed: " + e);
return false;
} finally {
if (socket != null) {
@@ -158,6 +192,23 @@
return mRoundTripTime;
}
+ private static void checkValidServerReply(
+ byte leap, byte mode, int stratum, long transmitTime)
+ throws InvalidServerReplyException {
+ if (leap == NTP_LEAP_NOSYNC) {
+ throw new InvalidServerReplyException("unsynchronized server");
+ }
+ if ((mode != NTP_MODE_SERVER) && (mode != NTP_MODE_BROADCAST)) {
+ throw new InvalidServerReplyException("untrusted mode: " + mode);
+ }
+ if ((stratum == NTP_STRATUM_DEATH) || (stratum > NTP_STRATUM_MAX)) {
+ throw new InvalidServerReplyException("untrusted stratum: " + stratum);
+ }
+ if (transmitTime == 0) {
+ throw new InvalidServerReplyException("zero transmitTime");
+ }
+ }
+
/**
* Reads an unsigned 32 bit big endian number from the given offset in the buffer.
*/
@@ -177,20 +228,30 @@
}
/**
- * Reads the NTP time stamp at the given offset in the buffer and returns
+ * Reads the NTP time stamp at the given offset in the buffer and returns
* it as a system time (milliseconds since January 1, 1970).
- */
+ */
private long readTimeStamp(byte[] buffer, int offset) {
long seconds = read32(buffer, offset);
long fraction = read32(buffer, offset + 4);
- return ((seconds - OFFSET_1900_TO_1970) * 1000) + ((fraction * 1000L) / 0x100000000L);
+ // Special case: zero means zero.
+ if (seconds == 0 && fraction == 0) {
+ return 0;
+ }
+ return ((seconds - OFFSET_1900_TO_1970) * 1000) + ((fraction * 1000L) / 0x100000000L);
}
/**
- * Writes system time (milliseconds since January 1, 1970) as an NTP time stamp
+ * Writes system time (milliseconds since January 1, 1970) as an NTP time stamp
* at the given offset in the buffer.
- */
+ */
private void writeTimeStamp(byte[] buffer, int offset, long time) {
+ // Special case: zero means zero.
+ if (time == 0) {
+ Arrays.fill(buffer, offset, offset + 8, (byte) 0x00);
+ return;
+ }
+
long seconds = time / 1000L;
long milliseconds = time - seconds * 1000L;
seconds += OFFSET_1900_TO_1970;
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index bb08be2..2137c3a 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -87,12 +87,21 @@
public static final int TAG_SYSTEM_MEDIA = 0xFFFFFF02;
/**
- * Default tag value for {@link BackupManager} traffic.
+ * Default tag value for {@link BackupManager} backup traffic; that is,
+ * traffic from the device to the storage backend.
*
* @hide
*/
public static final int TAG_SYSTEM_BACKUP = 0xFFFFFF03;
+ /**
+ * Default tag value for {@link BackupManager} restore traffic; that is,
+ * app data retrieved from the storage backend at install time.
+ *
+ * @hide
+ */
+ public static final int TAG_SYSTEM_RESTORE = 0xFFFFFF04;
+
private static INetworkStatsService sStatsService;
private synchronized static INetworkStatsService getStatsService() {
@@ -142,6 +151,16 @@
}
/**
+ * System API for restore-related support components to tag network traffic
+ * appropriately.
+ * @hide
+ */
+ @SystemApi
+ public static void setThreadStatsTagRestore() {
+ setThreadStatsTag(TAG_SYSTEM_RESTORE);
+ }
+
+ /**
* Get the active tag used when accounting {@link Socket} traffic originating
* from the current thread. Only one active tag per thread is supported.
* {@link #tagSocket(Socket)}.
diff --git a/core/java/android/net/http/X509TrustManagerExtensions.java b/core/java/android/net/http/X509TrustManagerExtensions.java
index eb4ceda..25ef8b5 100644
--- a/core/java/android/net/http/X509TrustManagerExtensions.java
+++ b/core/java/android/net/http/X509TrustManagerExtensions.java
@@ -16,6 +16,8 @@
package android.net.http;
+import android.annotation.SystemApi;
+
import com.android.org.conscrypt.TrustManagerImpl;
import java.security.cert.CertificateException;
@@ -80,4 +82,15 @@
public boolean isUserAddedCertificate(X509Certificate cert) {
return mDelegate.isUserAddedCertificate(cert);
}
+
+ /**
+ * Returns {@code true} if the TrustManager uses the same trust configuration for the provided
+ * hostnames.
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean isSameTrustConfiguration(String hostname1, String hostname2) {
+ return true;
+ }
}
diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java
index 1f3e9a7..56cb250 100644
--- a/core/java/android/os/BatteryManager.java
+++ b/core/java/android/os/BatteryManager.java
@@ -108,6 +108,13 @@
*/
public static final String EXTRA_MAX_CHARGING_CURRENT = "max_charging_current";
+ /**
+ * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
+ * Int value set to the maximum charging voltage supported by the charger in micro volts.
+ * {@hide}
+ */
+ public static final String EXTRA_MAX_CHARGING_VOLTAGE = "max_charging_voltage";
+
// values for "status" field in the ACTION_BATTERY_CHANGED Intent
public static final int BATTERY_STATUS_UNKNOWN = 1;
public static final int BATTERY_STATUS_CHARGING = 2;
diff --git a/core/java/android/os/BatteryProperties.java b/core/java/android/os/BatteryProperties.java
index 29e868c..c3e0f24 100644
--- a/core/java/android/os/BatteryProperties.java
+++ b/core/java/android/os/BatteryProperties.java
@@ -23,6 +23,7 @@
public boolean chargerUsbOnline;
public boolean chargerWirelessOnline;
public int maxChargingCurrent;
+ public int maxChargingVoltage;
public int batteryStatus;
public int batteryHealth;
public boolean batteryPresent;
@@ -39,6 +40,7 @@
chargerUsbOnline = other.chargerUsbOnline;
chargerWirelessOnline = other.chargerWirelessOnline;
maxChargingCurrent = other.maxChargingCurrent;
+ maxChargingVoltage = other.maxChargingVoltage;
batteryStatus = other.batteryStatus;
batteryHealth = other.batteryHealth;
batteryPresent = other.batteryPresent;
@@ -58,6 +60,7 @@
chargerUsbOnline = p.readInt() == 1 ? true : false;
chargerWirelessOnline = p.readInt() == 1 ? true : false;
maxChargingCurrent = p.readInt();
+ maxChargingVoltage = p.readInt();
batteryStatus = p.readInt();
batteryHealth = p.readInt();
batteryPresent = p.readInt() == 1 ? true : false;
@@ -72,6 +75,7 @@
p.writeInt(chargerUsbOnline ? 1 : 0);
p.writeInt(chargerWirelessOnline ? 1 : 0);
p.writeInt(maxChargingCurrent);
+ p.writeInt(maxChargingVoltage);
p.writeInt(batteryStatus);
p.writeInt(batteryHealth);
p.writeInt(batteryPresent ? 1 : 0);
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 8e86a53..1aa5c66 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -169,7 +169,7 @@
/**
* Current version of checkin data format.
*/
- static final String CHECKIN_VERSION = "15";
+ static final String CHECKIN_VERSION = "16";
/**
* Old version, we hit 9 and ran out of room, need to remove.
@@ -468,8 +468,8 @@
* @param cluster the index of the CPU cluster.
* @param step the index of the CPU speed. This is not the actual speed of the CPU.
* @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
- * @see PowerProfile.getNumCpuClusters()
- * @see PowerProfile.getNumSpeedStepsInCpuCluster(int)
+ * @see com.android.internal.os.PowerProfile#getNumCpuClusters()
+ * @see com.android.internal.os.PowerProfile#getNumSpeedStepsInCpuCluster(int)
*/
public abstract long getTimeAtCpuSpeed(int cluster, int step, int which);
@@ -1135,14 +1135,15 @@
public static final int STATE2_WIFI_RUNNING_FLAG = 1<<29;
public static final int STATE2_WIFI_ON_FLAG = 1<<28;
public static final int STATE2_FLASHLIGHT_FLAG = 1<<27;
- public static final int STATE2_DEVICE_IDLE_FLAG = 1<<26;
- public static final int STATE2_CHARGING_FLAG = 1<<25;
- public static final int STATE2_PHONE_IN_CALL_FLAG = 1<<24;
- public static final int STATE2_BLUETOOTH_ON_FLAG = 1<<23;
- public static final int STATE2_CAMERA_FLAG = 1<<22;
+ public static final int STATE2_DEVICE_IDLE_SHIFT = 25;
+ public static final int STATE2_DEVICE_IDLE_MASK = 0x3 << STATE2_DEVICE_IDLE_SHIFT;
+ public static final int STATE2_CHARGING_FLAG = 1<<24;
+ public static final int STATE2_PHONE_IN_CALL_FLAG = 1<<23;
+ public static final int STATE2_BLUETOOTH_ON_FLAG = 1<<22;
+ public static final int STATE2_CAMERA_FLAG = 1<<21;
public static final int MOST_INTERESTING_STATES2 =
- STATE2_POWER_SAVE_FLAG | STATE2_WIFI_ON_FLAG | STATE2_DEVICE_IDLE_FLAG
+ STATE2_POWER_SAVE_FLAG | STATE2_WIFI_ON_FLAG | STATE2_DEVICE_IDLE_MASK
| STATE2_CHARGING_FLAG | STATE2_PHONE_IN_CALL_FLAG | STATE2_BLUETOOTH_ON_FLAG;
public static final int SETTLE_TO_ZERO_STATES2 = 0xffff0000 & ~MOST_INTERESTING_STATES2;
@@ -1620,36 +1621,57 @@
public abstract int getPowerSaveModeEnabledCount(int which);
/**
+ * Constant for device idle mode: not active.
+ */
+ public static final int DEVICE_IDLE_MODE_OFF = 0;
+
+ /**
+ * Constant for device idle mode: active in lightweight mode.
+ */
+ public static final int DEVICE_IDLE_MODE_LIGHT = 1;
+
+ /**
+ * Constant for device idle mode: active in full mode.
+ */
+ public static final int DEVICE_IDLE_MODE_FULL = 2;
+
+ /**
* Returns the time in microseconds that device has been in idle mode while
* running on battery.
*
* {@hide}
*/
- public abstract long getDeviceIdleModeEnabledTime(long elapsedRealtimeUs, int which);
+ public abstract long getDeviceIdleModeTime(int mode, long elapsedRealtimeUs, int which);
/**
* Returns the number of times that the devie has gone in to idle mode.
*
* {@hide}
*/
- public abstract int getDeviceIdleModeEnabledCount(int which);
+ public abstract int getDeviceIdleModeCount(int mode, int which);
+
+ /**
+ * Return the longest duration we spent in a particular device idle mode (fully in the
+ * mode, not in idle maintenance etc).
+ */
+ public abstract long getLongestDeviceIdleModeTime(int mode);
/**
* Returns the time in microseconds that device has been in idling while on
- * battery. This is broader than {@link #getDeviceIdleModeEnabledTime} -- it
+ * battery. This is broader than {@link #getDeviceIdleModeTime} -- it
* counts all of the time that we consider the device to be idle, whether or not
* it is currently in the actual device idle mode.
*
* {@hide}
*/
- public abstract long getDeviceIdlingTime(long elapsedRealtimeUs, int which);
+ public abstract long getDeviceIdlingTime(int mode, long elapsedRealtimeUs, int which);
/**
* Returns the number of times that the devie has started idling.
*
* {@hide}
*/
- public abstract int getDeviceIdlingCount(int which);
+ public abstract int getDeviceIdlingCount(int mode, int which);
/**
* Returns the number of times that connectivity state changed.
@@ -1847,7 +1869,10 @@
new BitDescription(HistoryItem.STATE2_WIFI_RUNNING_FLAG, "wifi_running", "Ww"),
new BitDescription(HistoryItem.STATE2_WIFI_ON_FLAG, "wifi", "W"),
new BitDescription(HistoryItem.STATE2_FLASHLIGHT_FLAG, "flashlight", "fl"),
- new BitDescription(HistoryItem.STATE2_DEVICE_IDLE_FLAG, "device_idle", "di"),
+ new BitDescription(HistoryItem.STATE2_DEVICE_IDLE_MASK,
+ HistoryItem.STATE2_DEVICE_IDLE_SHIFT, "device_idle", "di",
+ new String[] { "off", "light", "full", "???" },
+ new String[] { "off", "light", "full", "???" }),
new BitDescription(HistoryItem.STATE2_CHARGING_FLAG, "charging", "ch"),
new BitDescription(HistoryItem.STATE2_PHONE_IN_CALL_FLAG, "phone_in_call", "Pcl"),
new BitDescription(HistoryItem.STATE2_BLUETOOTH_ON_FLAG, "bluetooth", "b"),
@@ -2529,8 +2554,14 @@
final long screenOnTime = getScreenOnTime(rawRealtime, which);
final long interactiveTime = getInteractiveTime(rawRealtime, which);
final long powerSaveModeEnabledTime = getPowerSaveModeEnabledTime(rawRealtime, which);
- final long deviceIdleModeEnabledTime = getDeviceIdleModeEnabledTime(rawRealtime, which);
- final long deviceIdlingTime = getDeviceIdlingTime(rawRealtime, which);
+ final long deviceIdleModeLightTime = getDeviceIdleModeTime(DEVICE_IDLE_MODE_LIGHT,
+ rawRealtime, which);
+ final long deviceIdleModeFullTime = getDeviceIdleModeTime(DEVICE_IDLE_MODE_FULL,
+ rawRealtime, which);
+ final long deviceLightIdlingTime = getDeviceIdlingTime(DEVICE_IDLE_MODE_LIGHT,
+ rawRealtime, which);
+ final long deviceIdlingTime = getDeviceIdlingTime(DEVICE_IDLE_MODE_FULL,
+ rawRealtime, which);
final int connChanges = getNumConnectivityChange(which);
final long phoneOnTime = getPhoneOnTime(rawRealtime, which);
@@ -2613,11 +2644,15 @@
fullWakeLockTimeTotal / 1000, partialWakeLockTimeTotal / 1000,
getMobileRadioActiveTime(rawRealtime, which) / 1000,
getMobileRadioActiveAdjustedTime(which) / 1000, interactiveTime / 1000,
- powerSaveModeEnabledTime / 1000, connChanges, deviceIdleModeEnabledTime / 1000,
- getDeviceIdleModeEnabledCount(which), deviceIdlingTime / 1000,
- getDeviceIdlingCount(which),
+ powerSaveModeEnabledTime / 1000, connChanges, deviceIdleModeFullTime / 1000,
+ getDeviceIdleModeCount(DEVICE_IDLE_MODE_FULL, which), deviceIdlingTime / 1000,
+ getDeviceIdlingCount(DEVICE_IDLE_MODE_FULL, which),
getMobileRadioActiveCount(which),
- getMobileRadioActiveUnknownTime(which) / 1000);
+ getMobileRadioActiveUnknownTime(which) / 1000, deviceIdleModeLightTime / 1000,
+ getDeviceIdleModeCount(DEVICE_IDLE_MODE_LIGHT, which), deviceLightIdlingTime / 1000,
+ getDeviceIdlingCount(DEVICE_IDLE_MODE_LIGHT, which),
+ getLongestDeviceIdleModeTime(DEVICE_IDLE_MODE_LIGHT),
+ getLongestDeviceIdleModeTime(DEVICE_IDLE_MODE_FULL));
// Dump screen brightness stats
Object[] args = new Object[NUM_SCREEN_BRIGHTNESS_BINS];
@@ -3082,8 +3117,14 @@
final long screenOnTime = getScreenOnTime(rawRealtime, which);
final long interactiveTime = getInteractiveTime(rawRealtime, which);
final long powerSaveModeEnabledTime = getPowerSaveModeEnabledTime(rawRealtime, which);
- final long deviceIdleModeEnabledTime = getDeviceIdleModeEnabledTime(rawRealtime, which);
- final long deviceIdlingTime = getDeviceIdlingTime(rawRealtime, which);
+ final long deviceIdleModeLightTime = getDeviceIdleModeTime(DEVICE_IDLE_MODE_LIGHT,
+ rawRealtime, which);
+ final long deviceIdleModeFullTime = getDeviceIdleModeTime(DEVICE_IDLE_MODE_FULL,
+ rawRealtime, which);
+ final long deviceLightIdlingTime = getDeviceIdlingTime(DEVICE_IDLE_MODE_LIGHT,
+ rawRealtime, which);
+ final long deviceIdlingTime = getDeviceIdlingTime(DEVICE_IDLE_MODE_FULL,
+ rawRealtime, which);
final long phoneOnTime = getPhoneOnTime(rawRealtime, which);
final long wifiRunningTime = getGlobalWifiRunningTime(rawRealtime, which);
final long wifiOnTime = getWifiOnTime(rawRealtime, which);
@@ -3127,26 +3168,54 @@
sb.append(")");
pw.println(sb.toString());
}
- if (deviceIdlingTime != 0) {
+ if (deviceLightIdlingTime != 0) {
sb.setLength(0);
sb.append(prefix);
- sb.append(" Device idling: ");
- formatTimeMs(sb, deviceIdlingTime / 1000);
+ sb.append(" Device light idling: ");
+ formatTimeMs(sb, deviceLightIdlingTime / 1000);
sb.append("(");
- sb.append(formatRatioLocked(deviceIdlingTime, whichBatteryRealtime));
- sb.append(") "); sb.append(getDeviceIdlingCount(which));
+ sb.append(formatRatioLocked(deviceLightIdlingTime, whichBatteryRealtime));
+ sb.append(") "); sb.append(getDeviceIdlingCount(DEVICE_IDLE_MODE_LIGHT, which));
sb.append("x");
pw.println(sb.toString());
}
- if (deviceIdleModeEnabledTime != 0) {
+ if (deviceIdleModeLightTime != 0) {
sb.setLength(0);
sb.append(prefix);
- sb.append(" Idle mode time: ");
- formatTimeMs(sb, deviceIdleModeEnabledTime / 1000);
+ sb.append(" Idle mode light time: ");
+ formatTimeMs(sb, deviceIdleModeLightTime / 1000);
sb.append("(");
- sb.append(formatRatioLocked(deviceIdleModeEnabledTime, whichBatteryRealtime));
- sb.append(") "); sb.append(getDeviceIdleModeEnabledCount(which));
+ sb.append(formatRatioLocked(deviceIdleModeLightTime, whichBatteryRealtime));
+ sb.append(") ");
+ sb.append(getDeviceIdleModeCount(DEVICE_IDLE_MODE_LIGHT, which));
sb.append("x");
+ sb.append(" -- longest ");
+ formatTimeMs(sb, getLongestDeviceIdleModeTime(DEVICE_IDLE_MODE_LIGHT));
+ pw.println(sb.toString());
+ }
+ if (deviceIdlingTime != 0) {
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" Device full idling: ");
+ formatTimeMs(sb, deviceIdlingTime / 1000);
+ sb.append("(");
+ sb.append(formatRatioLocked(deviceIdlingTime, whichBatteryRealtime));
+ sb.append(") "); sb.append(getDeviceIdlingCount(DEVICE_IDLE_MODE_FULL, which));
+ sb.append("x");
+ pw.println(sb.toString());
+ }
+ if (deviceIdleModeFullTime != 0) {
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" Idle mode full time: ");
+ formatTimeMs(sb, deviceIdleModeFullTime / 1000);
+ sb.append("(");
+ sb.append(formatRatioLocked(deviceIdleModeFullTime, whichBatteryRealtime));
+ sb.append(") ");
+ sb.append(getDeviceIdleModeCount(DEVICE_IDLE_MODE_FULL, which));
+ sb.append("x");
+ sb.append(" -- longest ");
+ formatTimeMs(sb, getLongestDeviceIdleModeTime(DEVICE_IDLE_MODE_FULL));
pw.println(sb.toString());
}
if (phoneOnTime != 0) {
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 85041ad..f7c8662 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -577,7 +577,7 @@
public static final int KITKAT = 19;
/**
- * Android 4.4W: KitKat for watches, snacks on the run.
+ * June 2014: Android 4.4W. KitKat for watches, snacks on the run.
*
* <p>Applications targeting this or a later release will get these
* new changes in behavior:</p>
@@ -595,7 +595,7 @@
public static final int L = 21;
/**
- * Lollipop. A flat one with beautiful shadows. But still tasty.
+ * November 2014: Lollipop. A flat one with beautiful shadows. But still tasty.
*
* <p>Applications targeting this or a later release will get these
* new changes in behavior:</p>
@@ -626,12 +626,38 @@
public static final int LOLLIPOP = 21;
/**
- * Lollipop with an extra sugar coating on the outside!
+ * March 2015: Lollipop with an extra sugar coating on the outside!
*/
public static final int LOLLIPOP_MR1 = 22;
/**
- * M comes after L.
+ * M is for Marshmallow!
+ *
+ * <p>Applications targeting this or a later release will get these
+ * new changes in behavior:</p>
+ * <ul>
+ * <li> Runtime permissions. Dangerous permissions are no longer granted at
+ * install time, but must be requested by the application at runtime through
+ * {@link android.app.Activity#requestPermissions}.</li>
+ * <li> Bluetooth and Wi-Fi scanning now requires holding the location permission.</li>
+ * <li> {@link android.app.AlarmManager#setTimeZone AlarmManager.setTimeZone} will fail if
+ * the given timezone is non-Olson.</li>
+ * <li> Activity transitions will only return shared
+ * elements mapped in the returned view hierarchy back to the calling activity.</li>
+ * <li> {@link android.view.View} allows a number of behaviors that may break
+ * existing apps: Canvas throws an exception if restore() is called too many times,
+ * widgets may return a hint size when returning UNSPECIFIED measure specs, and it
+ * will respect the attributes {@link android.R.attr#foreground},
+ * {@link android.R.attr#foregroundGravity}, {@link android.R.attr#foregroundTint}, and
+ * {@link android.R.attr#foregroundTintMode}.</li>
+ * <li> {@link android.view.MotionEvent#getButtonState MotionEvent.getButtonState}
+ * will no longer report {@link android.view.MotionEvent#BUTTON_PRIMARY}
+ * and {@link android.view.MotionEvent#BUTTON_SECONDARY} as synonyms for
+ * {@link android.view.MotionEvent#BUTTON_STYLUS_PRIMARY} and
+ * {@link android.view.MotionEvent#BUTTON_STYLUS_SECONDARY}.</li>
+ * <li> {@link android.widget.ScrollView} now respects the layout param margins
+ * when measuring.</li>
+ * </ul>
*/
public static final int M = 23;
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 64d6da5..53627fc 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -35,6 +35,7 @@
private static final String ENV_ANDROID_ROOT = "ANDROID_ROOT";
private static final String ENV_ANDROID_DATA = "ANDROID_DATA";
private static final String ENV_ANDROID_STORAGE = "ANDROID_STORAGE";
+ private static final String ENV_DOWNLOAD_CACHE = "DOWNLOAD_CACHE";
private static final String ENV_OEM_ROOT = "OEM_ROOT";
private static final String ENV_VENDOR_ROOT = "VENDOR_ROOT";
@@ -53,11 +54,10 @@
private static final File DIR_ANDROID_ROOT = getDirectory(ENV_ANDROID_ROOT, "/system");
private static final File DIR_ANDROID_DATA = getDirectory(ENV_ANDROID_DATA, "/data");
private static final File DIR_ANDROID_STORAGE = getDirectory(ENV_ANDROID_STORAGE, "/storage");
+ private static final File DIR_DOWNLOAD_CACHE = getDirectory(ENV_DOWNLOAD_CACHE, "/cache");
private static final File DIR_OEM_ROOT = getDirectory(ENV_OEM_ROOT, "/oem");
private static final File DIR_VENDOR_ROOT = getDirectory(ENV_VENDOR_ROOT, "/vendor");
- private static final String SYSTEM_PROPERTY_EFS_ENABLED = "persist.security.efs.enabled";
-
private static UserEnvironment sCurrentUser;
private static boolean sUserRequired;
@@ -164,34 +164,16 @@
return DIR_VENDOR_ROOT;
}
- /**
- * Gets the system directory available for secure storage.
- * If Encrypted File system is enabled, it returns an encrypted directory (/data/secure/system).
- * Otherwise, it returns the unencrypted /data/system directory.
- * @return File object representing the secure storage system directory.
- * @hide
- */
+ /** {@hide} */
+ @Deprecated
public static File getSystemSecureDirectory() {
- if (isEncryptedFilesystemEnabled()) {
- return new File(SECURE_DATA_DIRECTORY, "system");
- } else {
- return new File(DATA_DIRECTORY, "system");
- }
+ return getDataSystemDirectory();
}
- /**
- * Gets the data directory for secure storage.
- * If Encrypted File system is enabled, it returns an encrypted directory (/data/secure).
- * Otherwise, it returns the unencrypted /data directory.
- * @return File object representing the data directory for secure storage.
- * @hide
- */
+ /** {@hide} */
+ @Deprecated
public static File getSecureDataDirectory() {
- if (isEncryptedFilesystemEnabled()) {
- return SECURE_DATA_DIRECTORY;
- } else {
- return DATA_DIRECTORY;
- }
+ return getDataDirectory();
}
/**
@@ -202,7 +184,7 @@
* @hide
*/
public static File getUserSystemDirectory(int userId) {
- return new File(new File(getSystemSecureDirectory(), "users"), Integer.toString(userId));
+ return new File(new File(getDataSystemDirectory(), "users"), Integer.toString(userId));
}
/**
@@ -217,67 +199,98 @@
}
/**
- * Returns whether the Encrypted File System feature is enabled on the device or not.
- * @return <code>true</code> if Encrypted File System feature is enabled, <code>false</code>
- * if disabled.
- * @hide
- */
- public static boolean isEncryptedFilesystemEnabled() {
- return SystemProperties.getBoolean(SYSTEM_PROPERTY_EFS_ENABLED, false);
- }
-
- private static final File DATA_DIRECTORY
- = getDirectory("ANDROID_DATA", "/data");
-
- /**
- * @hide
- */
- private static final File SECURE_DATA_DIRECTORY
- = getDirectory("ANDROID_SECURE_DATA", "/data/secure");
-
- private static final File DOWNLOAD_CACHE_DIRECTORY = getDirectory("DOWNLOAD_CACHE", "/cache");
-
- /**
* Return the user data directory.
*/
public static File getDataDirectory() {
- return DATA_DIRECTORY;
+ return DIR_ANDROID_DATA;
}
/** {@hide} */
public static File getDataDirectory(String volumeUuid) {
if (TextUtils.isEmpty(volumeUuid)) {
- return new File("/data");
+ return DIR_ANDROID_DATA;
} else {
return new File("/mnt/expand/" + volumeUuid);
}
}
/** {@hide} */
+ public static File getDataSystemDirectory() {
+ return new File(getDataDirectory(), "system");
+ }
+
+ /** {@hide} */
+ public static File getDataSystemCredentialEncryptedDirectory() {
+ return new File(getDataDirectory(), "system_ce");
+ }
+
+ /** {@hide} */
+ public static File getDataSystemCredentialEncryptedDirectory(int userId) {
+ return new File(getDataSystemCredentialEncryptedDirectory(), String.valueOf(userId));
+ }
+
+ /** {@hide} */
public static File getDataAppDirectory(String volumeUuid) {
return new File(getDataDirectory(volumeUuid), "app");
}
/** {@hide} */
+ @Deprecated
public static File getDataUserDirectory(String volumeUuid) {
+ return getDataUserCredentialEncryptedDirectory(volumeUuid);
+ }
+
+ /** {@hide} */
+ @Deprecated
+ public static File getDataUserDirectory(String volumeUuid, int userId) {
+ return getDataUserCredentialEncryptedDirectory(volumeUuid, userId);
+ }
+
+ /** {@hide} */
+ @Deprecated
+ public static File getDataUserPackageDirectory(String volumeUuid, int userId,
+ String packageName) {
+ return getDataUserCredentialEncryptedPackageDirectory(volumeUuid, userId, packageName);
+ }
+
+ /** {@hide} */
+ public static File getDataUserCredentialEncryptedDirectory(String volumeUuid) {
return new File(getDataDirectory(volumeUuid), "user");
}
/** {@hide} */
- public static File getDataUserDirectory(String volumeUuid, int userId) {
- return new File(getDataUserDirectory(volumeUuid), String.valueOf(userId));
+ public static File getDataUserCredentialEncryptedDirectory(String volumeUuid, int userId) {
+ return new File(getDataUserCredentialEncryptedDirectory(volumeUuid),
+ String.valueOf(userId));
}
/** {@hide} */
- public static File getDataUserPackageDirectory(String volumeUuid, int userId,
+ public static File getDataUserCredentialEncryptedPackageDirectory(String volumeUuid, int userId,
String packageName) {
// TODO: keep consistent with installd
- return new File(getDataUserDirectory(volumeUuid, userId), packageName);
+ return new File(getDataUserCredentialEncryptedDirectory(volumeUuid, userId), packageName);
+ }
+
+ /** {@hide} */
+ public static File getDataUserDeviceEncryptedDirectory(String volumeUuid) {
+ return new File(getDataDirectory(volumeUuid), "user_de");
+ }
+
+ /** {@hide} */
+ public static File getDataUserDeviceEncryptedDirectory(String volumeUuid, int userId) {
+ return new File(getDataUserDeviceEncryptedDirectory(volumeUuid), String.valueOf(userId));
+ }
+
+ /** {@hide} */
+ public static File getDataUserDeviceEncryptedPackageDirectory(String volumeUuid, int userId,
+ String packageName) {
+ // TODO: keep consistent with installd
+ return new File(getDataUserDeviceEncryptedDirectory(volumeUuid, userId), packageName);
}
/**
- * Return the primary external storage directory. This directory may not
- * currently be accessible if it has been mounted by the user on their
+ * Return the primary shared/external storage directory. This directory may
+ * not currently be accessible if it has been mounted by the user on their
* computer, has been removed from the device, or some other problem has
* happened. You can determine its current state with
* {@link #getExternalStorageState()}.
@@ -291,12 +304,15 @@
* filesystem on a computer.</em>
* <p>
* On devices with multiple users (as described by {@link UserManager}),
- * each user has their own isolated external storage. Applications only have
- * access to the external storage for the user they're running as.
+ * each user has their own isolated shared storage. Applications only have
+ * access to the shared storage for the user they're running as.
* <p>
- * In devices with multiple "external" storage directories, this directory
- * represents the "primary" external storage that the user will interact
+ * In devices with multiple shared/external storage directories, this
+ * directory represents the primary storage that the user will interact
* with. Access to secondary storage is available through
+ * {@link Context#getExternalFilesDirs(String)},
+ * {@link Context#getExternalCacheDirs()}, and
+ * {@link Context#getExternalMediaDirs()}.
* <p>
* Applications should not directly use this top-level directory, in order
* to avoid polluting the user's root namespace. Any files that are private
@@ -315,8 +331,9 @@
* <p>
* Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, if your
* application only needs to store internal data, consider using
- * {@link Context#getExternalFilesDir(String)} or
- * {@link Context#getExternalCacheDir()}, which require no permissions to
+ * {@link Context#getExternalFilesDir(String)},
+ * {@link Context#getExternalCacheDir()}, or
+ * {@link Context#getExternalMediaDirs()}, which require no permissions to
* read or write.
* <p>
* This path may change between platform versions, so applications should
@@ -325,8 +342,7 @@
* Here is an example of typical code to monitor the state of external
* storage:
* <p>
- * {@sample
- * development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
* monitor_storage}
*
* @see #getExternalStorageState()
@@ -446,32 +462,32 @@
public static String DIRECTORY_DOCUMENTS = "Documents";
/**
- * Get a top-level public external storage directory for placing files of
- * a particular type. This is where the user will typically place and
- * manage their own files, so you should be careful about what you put here
- * to ensure you don't erase their files or get in the way of their own
+ * Get a top-level shared/external storage directory for placing files of a
+ * particular type. This is where the user will typically place and manage
+ * their own files, so you should be careful about what you put here to
+ * ensure you don't erase their files or get in the way of their own
* organization.
- *
- * <p>On devices with multiple users (as described by {@link UserManager}),
- * each user has their own isolated external storage. Applications only
- * have access to the external storage for the user they're running as.</p>
- *
- * <p>Here is an example of typical code to manipulate a picture on
- * the public external storage:</p>
- *
+ * <p>
+ * On devices with multiple users (as described by {@link UserManager}),
+ * each user has their own isolated shared storage. Applications only have
+ * access to the shared storage for the user they're running as.
+ * </p>
+ * <p>
+ * Here is an example of typical code to manipulate a picture on the public
+ * shared storage:
+ * </p>
* {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
* public_picture}
*
- * @param type The type of storage directory to return. Should be one of
- * {@link #DIRECTORY_MUSIC}, {@link #DIRECTORY_PODCASTS},
- * {@link #DIRECTORY_RINGTONES}, {@link #DIRECTORY_ALARMS},
- * {@link #DIRECTORY_NOTIFICATIONS}, {@link #DIRECTORY_PICTURES},
- * {@link #DIRECTORY_MOVIES}, {@link #DIRECTORY_DOWNLOADS}, or
- * {@link #DIRECTORY_DCIM}. May not be null.
- *
- * @return Returns the File path for the directory. Note that this
- * directory may not yet exist, so you must make sure it exists before
- * using it such as with {@link File#mkdirs File.mkdirs()}.
+ * @param type The type of storage directory to return. Should be one of
+ * {@link #DIRECTORY_MUSIC}, {@link #DIRECTORY_PODCASTS},
+ * {@link #DIRECTORY_RINGTONES}, {@link #DIRECTORY_ALARMS},
+ * {@link #DIRECTORY_NOTIFICATIONS}, {@link #DIRECTORY_PICTURES},
+ * {@link #DIRECTORY_MOVIES}, {@link #DIRECTORY_DOWNLOADS}, or
+ * {@link #DIRECTORY_DCIM}. May not be null.
+ * @return Returns the File path for the directory. Note that this directory
+ * may not yet exist, so you must make sure it exists before using
+ * it such as with {@link File#mkdirs File.mkdirs()}.
*/
public static File getExternalStoragePublicDirectory(String type) {
throwIfUserRequired();
@@ -536,7 +552,7 @@
* Return the download/cache content directory.
*/
public static File getDownloadCacheDirectory() {
- return DOWNLOAD_CACHE_DIRECTORY;
+ return DIR_DOWNLOAD_CACHE;
}
/**
@@ -623,7 +639,7 @@
public static final String MEDIA_EJECTING = "ejecting";
/**
- * Returns the current state of the primary "external" storage device.
+ * Returns the current state of the primary shared/external storage media.
*
* @see #getExternalStorageDirectory()
* @return one of {@link #MEDIA_UNKNOWN}, {@link #MEDIA_REMOVED},
@@ -646,8 +662,8 @@
}
/**
- * Returns the current state of the storage device that provides the given
- * path.
+ * Returns the current state of the shared/external storage media at the
+ * given path.
*
* @return one of {@link #MEDIA_UNKNOWN}, {@link #MEDIA_REMOVED},
* {@link #MEDIA_UNMOUNTED}, {@link #MEDIA_CHECKING},
@@ -665,7 +681,8 @@
}
/**
- * Returns whether the primary "external" storage device is removable.
+ * Returns whether the primary shared/external storage media is physically
+ * removable.
*
* @return true if the storage device can be removed (such as an SD card),
* or false if the storage device is built in and cannot be
@@ -678,8 +695,8 @@
}
/**
- * Returns whether the storage device that provides the given path is
- * removable.
+ * Returns whether the shared/external storage media at the given path is
+ * physically removable.
*
* @return true if the storage device can be removed (such as an SD card),
* or false if the storage device is built in and cannot be
@@ -697,9 +714,15 @@
}
/**
- * Returns whether the primary "external" storage device is emulated. If
- * true, data stored on this device will be stored on a portion of the
- * internal storage system.
+ * Returns whether the primary shared/external storage media is emulated.
+ * <p>
+ * The contents of emulated storage devices are backed by a private user
+ * data partition, which means there is little benefit to apps storing data
+ * here instead of the private directories returned by
+ * {@link Context#getFilesDir()}, etc.
+ * <p>
+ * This returns true when emulated storage is backed by either internal
+ * storage or an adopted storage device.
*
* @see DevicePolicyManager#setStorageEncryption(android.content.ComponentName,
* boolean)
@@ -711,9 +734,16 @@
}
/**
- * Returns whether the storage device that provides the given path is
- * emulated. If true, data stored on this device will be stored on a portion
- * of the internal storage system.
+ * Returns whether the shared/external storage media at the given path is
+ * emulated.
+ * <p>
+ * The contents of emulated storage devices are backed by a private user
+ * data partition, which means there is little benefit to apps storing data
+ * here instead of the private directories returned by
+ * {@link Context#getFilesDir()}, etc.
+ * <p>
+ * This returns true when emulated storage is backed by either internal
+ * storage or an adopted storage device.
*
* @throws IllegalArgumentException if the path is not a valid storage
* device.
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index af4c2bc..b73d81e 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -17,6 +17,7 @@
package android.os;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.provider.DocumentsContract.Document;
import android.system.ErrnoException;
import android.system.Os;
@@ -646,4 +647,8 @@
return EMPTY;
}
}
+
+ public static @Nullable File newFileOrNull(@Nullable String path) {
+ return (path != null) ? new File(path) : null;
+ }
}
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index 9fdbec3..dd10df3 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -47,6 +47,7 @@
boolean isPowerSaveMode();
boolean setPowerSaveMode(boolean mode);
boolean isDeviceIdleMode();
+ boolean isLightDeviceIdleMode();
void reboot(boolean confirm, String reason, boolean wait);
void shutdown(boolean confirm, String reason, boolean wait);
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 4c19ddd..b5bbbbb 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -44,8 +44,9 @@
UserInfo getPrimaryUser();
List<UserInfo> getUsers(boolean excludeDying);
List<UserInfo> getProfiles(int userHandle, boolean enabledOnly);
- boolean canAddMoreManagedProfiles(int userId);
+ boolean canAddMoreManagedProfiles(int userId, boolean allowedToRemoveOne);
UserInfo getProfileParent(int userHandle);
+ boolean isSameProfileGroup(int userId, int otherUserId);
UserInfo getUserInfo(int userHandle);
long getUserCreationTime(int userHandle);
boolean isRestricted();
@@ -54,14 +55,12 @@
int getUserHandle(int userSerialNumber);
Bundle getUserRestrictions(int userHandle);
boolean hasUserRestriction(in String restrictionKey, int userHandle);
- void setUserRestrictions(in Bundle restrictions, int userHandle);
void setUserRestriction(String key, boolean value, int userId);
void setSystemControlledUserRestriction(String key, boolean value, int userId);
void setApplicationRestrictions(in String packageName, in Bundle restrictions,
int userHandle);
Bundle getApplicationRestrictions(in String packageName);
Bundle getApplicationRestrictionsForUser(in String packageName, int userHandle);
- void removeRestrictions();
void setDefaultGuestRestrictions(in Bundle restrictions);
Bundle getDefaultGuestRestrictions();
boolean markGuestForDeletion(int userHandle);
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 1273772b..5852f5f 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -16,6 +16,7 @@
package android.os;
+import android.annotation.IntegerRes;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
@@ -42,6 +43,8 @@
import java.util.Map;
import java.util.Set;
+import dalvik.system.VMRuntime;
+
/**
* Container for a message (data and object references) that can
* be sent through an IBinder. A Parcel can contain both flattened data
@@ -193,6 +196,7 @@
* indicating that we're responsible for its lifecycle.
*/
private boolean mOwnsNativeParcelObject;
+ private long mNativeSize;
private RuntimeException mStack;
@@ -244,7 +248,7 @@
private static native int nativeDataAvail(long nativePtr);
private static native int nativeDataPosition(long nativePtr);
private static native int nativeDataCapacity(long nativePtr);
- private static native void nativeSetDataSize(long nativePtr, int size);
+ private static native long nativeSetDataSize(long nativePtr, int size);
private static native void nativeSetDataPosition(long nativePtr, int pos);
private static native void nativeSetDataCapacity(long nativePtr, int size);
@@ -259,7 +263,7 @@
private static native void nativeWriteDouble(long nativePtr, double val);
private static native void nativeWriteString(long nativePtr, String val);
private static native void nativeWriteStrongBinder(long nativePtr, IBinder val);
- private static native void nativeWriteFileDescriptor(long nativePtr, FileDescriptor val);
+ private static native long nativeWriteFileDescriptor(long nativePtr, FileDescriptor val);
private static native byte[] nativeCreateByteArray(long nativePtr);
private static native byte[] nativeReadBlob(long nativePtr);
@@ -272,13 +276,13 @@
private static native FileDescriptor nativeReadFileDescriptor(long nativePtr);
private static native long nativeCreate();
- private static native void nativeFreeBuffer(long nativePtr);
+ private static native long nativeFreeBuffer(long nativePtr);
private static native void nativeDestroy(long nativePtr);
private static native byte[] nativeMarshall(long nativePtr);
- private static native void nativeUnmarshall(
+ private static native long nativeUnmarshall(
long nativePtr, byte[] data, int offset, int length);
- private static native void nativeAppendFrom(
+ private static native long nativeAppendFrom(
long thisNativePtr, long otherNativePtr, int offset, int length);
private static native boolean nativeHasFileDescriptors(long nativePtr);
private static native void nativeWriteInterfaceToken(long nativePtr, String interfaceName);
@@ -390,7 +394,7 @@
* @param size The new number of bytes in the Parcel.
*/
public final void setDataSize(int size) {
- nativeSetDataSize(mNativePtr, size);
+ updateNativeSize(nativeSetDataSize(mNativePtr, size));
}
/**
@@ -442,11 +446,11 @@
* Set the bytes in data to be the raw bytes of this Parcel.
*/
public final void unmarshall(byte[] data, int offset, int length) {
- nativeUnmarshall(mNativePtr, data, offset, length);
+ updateNativeSize(nativeUnmarshall(mNativePtr, data, offset, length));
}
public final void appendFrom(Parcel parcel, int offset, int length) {
- nativeAppendFrom(mNativePtr, parcel.mNativePtr, offset, length);
+ updateNativeSize(nativeAppendFrom(mNativePtr, parcel.mNativePtr, offset, length));
}
/**
@@ -599,7 +603,24 @@
* if {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE} is set.</p>
*/
public final void writeFileDescriptor(FileDescriptor val) {
- nativeWriteFileDescriptor(mNativePtr, val);
+ updateNativeSize(nativeWriteFileDescriptor(mNativePtr, val));
+ }
+
+ private void updateNativeSize(long newNativeSize) {
+ if (mOwnsNativeParcelObject) {
+ if (newNativeSize > Integer.MAX_VALUE) {
+ newNativeSize = Integer.MAX_VALUE;
+ }
+ if (newNativeSize != mNativeSize) {
+ int delta = (int) (newNativeSize - mNativeSize);
+ if (delta > 0) {
+ VMRuntime.getRuntime().registerNativeAllocation(delta);
+ } else {
+ VMRuntime.getRuntime().registerNativeFree(-delta);
+ }
+ mNativeSize = newNativeSize;
+ }
+ }
}
/**
@@ -2545,7 +2566,7 @@
private void freeBuffer() {
if (mOwnsNativeParcelObject) {
- nativeFreeBuffer(mNativePtr);
+ updateNativeSize(nativeFreeBuffer(mNativePtr));
}
}
@@ -2553,6 +2574,7 @@
if (mNativePtr != 0) {
if (mOwnsNativeParcelObject) {
nativeDestroy(mNativePtr);
+ updateNativeSize(0);
}
mNativePtr = 0;
}
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 69974fa..1cffa83 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -910,6 +910,26 @@
}
/**
+ * Returns true if the device is currently in light idle mode. This happens when a device
+ * has had its screen off for a short time, switching it into a batching mode where we
+ * execute jobs, syncs, networking on a batching schedule. You can monitor for changes to
+ * this state with {@link #ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED}.
+ *
+ * @return Returns true if currently in active light device idle mode, else false. This is
+ * when light idle mode restrictions are being actively applied; it will return false if the
+ * device is in a long-term idle mode but currently running a maintenance window where
+ * restrictions have been lifted.
+ * @hide
+ */
+ public boolean isLightDeviceIdleMode() {
+ try {
+ return mService.isLightDeviceIdleMode();
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
* Return whether the given application package name is on the device's power whitelist.
* Apps can be placed on the whitelist through the settings UI invoked by
* {@link android.provider.Settings#ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS}.
@@ -961,6 +981,15 @@
= "android.os.action.DEVICE_IDLE_MODE_CHANGED";
/**
+ * Intent that is broadcast when the state of {@link #isLightDeviceIdleMode()} changes.
+ * This broadcast is only sent to registered receivers.
+ * @hide
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED
+ = "android.os.action.LIGHT_DEVICE_IDLE_MODE_CHANGED";
+
+ /**
* @hide Intent that is broadcast when the set of power save whitelist apps has changed.
* This broadcast is only sent to registered receivers.
*/
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index b6d0fcb..9801e1b 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -148,7 +148,9 @@
public void onLowPowerModeChanged(boolean enabled);
}
- public abstract void setDeviceIdleMode(boolean enabled);
+ public abstract boolean setDeviceIdleMode(boolean enabled);
+
+ public abstract boolean setLightDeviceIdleMode(boolean enabled);
public abstract void setDeviceIdleWhitelist(int[] appids);
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 41de579..4535572 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -341,6 +341,10 @@
} finally {
uncryptFile.close();
}
+ // UNCRYPT_FILE needs to be readable by system server on bootup.
+ if (!UNCRYPT_FILE.setReadable(true, false)) {
+ Log.e(TAG, "Error setting readable for " + UNCRYPT_FILE.getCanonicalPath());
+ }
Log.w(TAG, "!!! REBOOTING TO INSTALL " + filename + " !!!");
// If the package is on the /data partition, write the block map file
@@ -501,6 +505,25 @@
Log.e(TAG, "Error reading recovery log", e);
}
+ if (UNCRYPT_FILE.exists()) {
+ String filename = null;
+ try {
+ filename = FileUtils.readTextFile(UNCRYPT_FILE, 0, null);
+ } catch (IOException e) {
+ Log.e(TAG, "Error reading uncrypt file", e);
+ }
+
+ // Remove the OTA package on /data that has been (possibly
+ // partially) processed. (Bug: 24973532)
+ if (filename != null && filename.startsWith("/data")) {
+ if (UNCRYPT_FILE.delete()) {
+ Log.i(TAG, "Deleted: " + filename);
+ } else {
+ Log.e(TAG, "Can't delete: " + filename);
+ }
+ }
+ }
+
// Delete everything in RECOVERY_DIR except those beginning
// with LAST_PREFIX
String[] names = RECOVERY_DIR.list();
diff --git a/core/java/android/os/ShellCommand.java b/core/java/android/os/ShellCommand.java
index d64273a..cad482b 100644
--- a/core/java/android/os/ShellCommand.java
+++ b/core/java/android/os/ShellCommand.java
@@ -19,8 +19,11 @@
import android.util.Slog;
import com.android.internal.util.FastPrintWriter;
+import java.io.BufferedInputStream;
import java.io.FileDescriptor;
+import java.io.FileInputStream;
import java.io.FileOutputStream;
+import java.io.InputStream;
import java.io.PrintWriter;
/**
@@ -43,8 +46,9 @@
private FastPrintWriter mOutPrintWriter;
private FastPrintWriter mErrPrintWriter;
+ private InputStream mInputStream;
- public void exec(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ public int exec(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ResultReceiver resultReceiver) {
mTarget = target;
mIn = in;
@@ -89,6 +93,7 @@
mResultReceiver.send(res, null);
}
if (DEBUG) Slog.d(TAG, "Finished command " + mCmd + " on " + mTarget);
+ return res;
}
public PrintWriter getOutPrintWriter() {
@@ -110,6 +115,13 @@
return mErrPrintWriter;
}
+ public InputStream getInputStream() {
+ if (mInputStream == null) {
+ mInputStream = new BufferedInputStream(new FileInputStream(mIn));
+ }
+ return mInputStream;
+ }
+
/**
* Return the next option on the command line -- that is an argument that
* starts with '-'. If the next argument is not an option, null is returned.
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index 796addc..95da438 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -270,13 +270,24 @@
* Returns true if this UserHandle refers to the owner user; false otherwise.
* @return true if this UserHandle refers to the owner user; false otherwise.
* @hide
- * TODO: find an alternative to this Api.
+ * @deprecated please use {@link #isSystem()} or check for
+ * {@link android.content.pm.UserInfo#isPrimary()}
+ * {@link android.content.pm.UserInfo#isAdmin()} based on your particular use case.
*/
@SystemApi
public boolean isOwner() {
return this.equals(OWNER);
}
+ /**
+ * @return true if this UserHandle refers to the system user; false otherwise.
+ * @hide
+ */
+ @SystemApi
+ public boolean isSystem() {
+ return this.equals(SYSTEM);
+ }
+
/** @hide */
public UserHandle(int h) {
mHandle = h;
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index d178d20..0a149bb 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -21,6 +21,7 @@
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
import android.content.Context;
import android.content.pm.UserInfo;
import android.content.res.Resources;
@@ -55,7 +56,8 @@
*
* <p/>Key for user restrictions.
* <p/>Type: Boolean
- * @see #setUserRestrictions(Bundle)
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
public static final String DISALLOW_MODIFY_ACCOUNTS = "no_modify_accounts";
@@ -67,7 +69,8 @@
*
* <p/>Key for user restrictions.
* <p/>Type: Boolean
- * @see #setUserRestrictions(Bundle)
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
public static final String DISALLOW_CONFIG_WIFI = "no_config_wifi";
@@ -78,7 +81,8 @@
*
* <p/>Key for user restrictions.
* <p/>Type: Boolean
- * @see #setUserRestrictions(Bundle)
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
public static final String DISALLOW_INSTALL_APPS = "no_install_apps";
@@ -89,7 +93,8 @@
*
* <p/>Key for user restrictions.
* <p/>Type: Boolean
- * @see #setUserRestrictions(Bundle)
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
public static final String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps";
@@ -102,7 +107,8 @@
*
* <p/>Key for user restrictions.
* <p/>Type: Boolean
- * @see #setUserRestrictions(Bundle)
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
public static final String DISALLOW_SHARE_LOCATION = "no_share_location";
@@ -114,7 +120,8 @@
*
* <p/>Key for user restrictions.
* <p/>Type: Boolean
- * @see #setUserRestrictions(Bundle)
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
public static final String DISALLOW_INSTALL_UNKNOWN_SOURCES = "no_install_unknown_sources";
@@ -127,7 +134,8 @@
*
* <p/>Key for user restrictions.
* <p/>Type: Boolean
- * @see #setUserRestrictions(Bundle)
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
public static final String DISALLOW_CONFIG_BLUETOOTH = "no_config_bluetooth";
@@ -139,7 +147,8 @@
*
* <p/>Key for user restrictions.
* <p/>Type: Boolean
- * @see #setUserRestrictions(Bundle)
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
public static final String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer";
@@ -150,7 +159,8 @@
*
* <p/>Key for user restrictions.
* <p/>Type: Boolean
- * @see #setUserRestrictions(Bundle)
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
public static final String DISALLOW_CONFIG_CREDENTIALS = "no_config_credentials";
@@ -163,7 +173,8 @@
*
* <p/>Key for user restrictions.
* <p/>Type: Boolean
- * @see #setUserRestrictions(Bundle)
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
public static final String DISALLOW_REMOVE_USER = "no_remove_user";
@@ -174,7 +185,8 @@
*
* <p/>Key for user restrictions.
* <p/>Type: Boolean
- * @see #setUserRestrictions(Bundle)
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
public static final String DISALLOW_DEBUGGING_FEATURES = "no_debugging_features";
@@ -187,7 +199,8 @@
*
* <p/>Key for user restrictions.
* <p/>Type: Boolean
- * @see #setUserRestrictions(Bundle)
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
public static final String DISALLOW_CONFIG_VPN = "no_config_vpn";
@@ -199,7 +212,8 @@
*
* <p/>Key for user restrictions.
* <p/>Type: Boolean
- * @see #setUserRestrictions(Bundle)
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
public static final String DISALLOW_CONFIG_TETHERING = "no_config_tethering";
@@ -213,7 +227,8 @@
*
* <p/>Key for user restrictions.
* <p/>Type: Boolean
- * @see #setUserRestrictions(Bundle)
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
public static final String DISALLOW_NETWORK_RESET = "no_network_reset";
@@ -227,7 +242,8 @@
*
* <p/>Key for user restrictions.
* <p/>Type: Boolean
- * @see #setUserRestrictions(Bundle)
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
public static final String DISALLOW_FACTORY_RESET = "no_factory_reset";
@@ -241,7 +257,8 @@
*
* <p/>Key for user restrictions.
* <p/>Type: Boolean
- * @see #setUserRestrictions(Bundle)
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
public static final String DISALLOW_ADD_USER = "no_add_user";
@@ -252,7 +269,8 @@
*
* <p/>Key for user restrictions.
* <p/>Type: Boolean
- * @see #setUserRestrictions(Bundle)
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
public static final String ENSURE_VERIFY_APPS = "ensure_verify_apps";
@@ -266,7 +284,8 @@
*
* <p/>Key for user restrictions.
* <p/>Type: Boolean
- * @see #setUserRestrictions(Bundle)
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
public static final String DISALLOW_CONFIG_CELL_BROADCASTS = "no_config_cell_broadcasts";
@@ -280,7 +299,8 @@
*
* <p/>Key for user restrictions.
* <p/>Type: Boolean
- * @see #setUserRestrictions(Bundle)
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
public static final String DISALLOW_CONFIG_MOBILE_NETWORKS = "no_config_mobile_networks";
@@ -300,7 +320,8 @@
*
* <p/>Key for user restrictions.
* <p/>Type: Boolean
- * @see #setUserRestrictions(Bundle)
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
public static final String DISALLOW_APPS_CONTROL = "no_control_apps";
@@ -312,7 +333,8 @@
*
* <p/>Key for user restrictions.
* <p/>Type: Boolean
- * @see #setUserRestrictions(Bundle)
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
public static final String DISALLOW_MOUNT_PHYSICAL_MEDIA = "no_physical_media";
@@ -324,7 +346,8 @@
*
* <p/>Key for user restrictions.
* <p/>Type: Boolean
- * @see #setUserRestrictions(Bundle)
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
public static final String DISALLOW_UNMUTE_MICROPHONE = "no_unmute_microphone";
@@ -336,7 +359,8 @@
*
* <p/>Key for user restrictions.
* <p/>Type: Boolean
- * @see #setUserRestrictions(Bundle)
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
public static final String DISALLOW_ADJUST_VOLUME = "no_adjust_volume";
@@ -350,7 +374,8 @@
*
* <p/>Key for user restrictions.
* <p/>Type: Boolean
- * @see #setUserRestrictions(Bundle)
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
public static final String DISALLOW_OUTGOING_CALLS = "no_outgoing_calls";
@@ -361,7 +386,8 @@
*
* <p/>Key for user restrictions.
* <p/>Type: Boolean
- * @see #setUserRestrictions(Bundle)
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
public static final String DISALLOW_SMS = "no_sms";
@@ -373,7 +399,8 @@
*
* <p/>Key for user restrictions.
* <p/>Type: Boolean
- * @see #setUserRestrictions(Bundle)
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
public static final String DISALLOW_FUN = "no_fun";
@@ -393,7 +420,8 @@
*
* <p/>Key for user restrictions.
* <p/>Type: Boolean
- * @see #setUserRestrictions(Bundle)
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
public static final String DISALLOW_CREATE_WINDOWS = "no_create_windows";
@@ -406,7 +434,8 @@
*
* <p/>Key for user restrictions.
* <p/>Type: Boolean
- * @see #setUserRestrictions(Bundle)
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
public static final String DISALLOW_CROSS_PROFILE_COPY_PASTE = "no_cross_profile_copy_paste";
@@ -417,7 +446,8 @@
*
* <p/>Key for user restrictions.
* <p/>Type: Boolean
- * @see #setUserRestrictions(Bundle)
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
public static final String DISALLOW_OUTGOING_BEAM = "no_outgoing_beam";
@@ -426,7 +456,8 @@
* Hidden user restriction to disallow access to wallpaper manager APIs. This user restriction
* is always set for managed profiles.
* @hide
- * @see #setUserRestrictions(Bundle)
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
public static final String DISALLOW_WALLPAPER = "no_wallpaper";
@@ -438,7 +469,8 @@
*
* <p/>Key for user restrictions.
* <p/>Type: Boolean
- * @see #setUserRestrictions(Bundle)
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
public static final String DISALLOW_SAFE_BOOT = "no_safe_boot";
@@ -447,13 +479,24 @@
* Specifies if a user is not allowed to record audio. This restriction is always enabled for
* background users. The default value is <code>false</code>.
*
- * @see #setUserRestrictions(Bundle)
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
* @hide
*/
public static final String DISALLOW_RECORD_AUDIO = "no_record_audio";
/**
+ * Specifies if a user is not allowed to use the camera.
+ *
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+ * @see #getUserRestrictions()
+ * @hide
+ */
+ public static final String DISALLOW_CAMERA = "no_camera";
+
+ /**
* Allows apps in the parent profile to handle web links from the managed profile.
*
* This user restriction has an effect only in a managed profile.
@@ -466,7 +509,8 @@
*
* <p/>Key for user restrictions.
* <p/>Type: Boolean
- * @see #setUserRestrictions(Bundle)
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
public static final String ALLOW_PARENT_PROFILE_APP_LINKING
@@ -499,14 +543,9 @@
/** @hide */
public static final int PIN_VERIFICATION_SUCCESS = -1;
- private static UserManager sInstance = null;
-
/** @hide */
- public synchronized static UserManager get(Context context) {
- if (sInstance == null) {
- sInstance = (UserManager) context.getSystemService(Context.USER_SERVICE);
- }
- return sInstance;
+ public static UserManager get(Context context) {
+ return (UserManager) context.getSystemService(Context.USER_SERVICE);
}
/** @hide */
@@ -679,7 +718,7 @@
public boolean isUserRunning(UserHandle user) {
try {
return ActivityManagerNative.getDefault().isUserRunning(
- user.getIdentifier(), false);
+ user.getIdentifier(), 0);
} catch (RemoteException e) {
return false;
}
@@ -694,8 +733,9 @@
*/
public boolean isUserRunningOrStopping(UserHandle user) {
try {
+ // TODO: reconcile stopped vs stopping?
return ActivityManagerNative.getDefault().isUserRunning(
- user.getIdentifier(), true);
+ user.getIdentifier(), ActivityManager.FLAG_OR_STOPPED);
} catch (RemoteException e) {
return false;
}
@@ -740,36 +780,23 @@
}
/**
- * Sets all the user-wide restrictions for this user.
- * Requires the MANAGE_USERS permission.
- * @param restrictions the Bundle containing all the restrictions.
- * @deprecated use {@link android.app.admin.DevicePolicyManager#addUserRestriction(
- * android.content.ComponentName, String)} or
- * {@link android.app.admin.DevicePolicyManager#clearUserRestriction(
- * android.content.ComponentName, String)} instead.
+ * This will no longer work. Device owners and profile owners should use
+ * {@link DevicePolicyManager#addUserRestriction(ComponentName, String)} instead.
*/
+ // System apps should use UserManager.setUserRestriction() instead.
@Deprecated
public void setUserRestrictions(Bundle restrictions) {
- setUserRestrictions(restrictions, Process.myUserHandle());
+ throw new UnsupportedOperationException("This method is no longer supported");
}
/**
- * Sets all the user-wide restrictions for the specified user.
- * Requires the MANAGE_USERS permission.
- * @param restrictions the Bundle containing all the restrictions.
- * @param userHandle the UserHandle of the user for whom to set the restrictions.
- * @deprecated use {@link android.app.admin.DevicePolicyManager#addUserRestriction(
- * android.content.ComponentName, String)} or
- * {@link android.app.admin.DevicePolicyManager#clearUserRestriction(
- * android.content.ComponentName, String)} instead.
+ * This will no longer work. Device owners and profile owners should use
+ * {@link DevicePolicyManager#addUserRestriction(ComponentName, String)} instead.
*/
+ // System apps should use UserManager.setUserRestriction() instead.
@Deprecated
public void setUserRestrictions(Bundle restrictions, UserHandle userHandle) {
- try {
- mService.setUserRestrictions(restrictions, userHandle.getIdentifier());
- } catch (RemoteException re) {
- Log.w(TAG, "Could not set user restrictions", re);
- }
+ throw new UnsupportedOperationException("This method is no longer supported");
}
/**
@@ -784,9 +811,7 @@
*/
@Deprecated
public void setUserRestriction(String key, boolean value) {
- Bundle bundle = getUserRestrictions();
- bundle.putBoolean(key, value);
- setUserRestrictions(bundle);
+ setUserRestriction(key, value, Process.myUserHandle());
}
/**
@@ -882,9 +907,8 @@
try {
user = mService.createUser(name, flags);
if (user != null && !user.isAdmin()) {
- Bundle userRestrictions = mService.getUserRestrictions(user.id);
- addDefaultUserRestrictions(userRestrictions);
- mService.setUserRestrictions(userRestrictions, user.id);
+ mService.setUserRestriction(DISALLOW_SMS, true, user.id);
+ mService.setUserRestriction(DISALLOW_OUTGOING_CALLS, true, user.id);
}
} catch (RemoteException re) {
Log.w(TAG, "Could not create a user", re);
@@ -899,27 +923,22 @@
* @hide
*/
public UserInfo createGuest(Context context, String name) {
- UserInfo guest = createUser(name, UserInfo.FLAG_GUEST);
- if (guest != null) {
- Settings.Secure.putStringForUser(context.getContentResolver(),
- Settings.Secure.SKIP_FIRST_USE_HINTS, "1", guest.id);
- try {
- Bundle guestRestrictions = mService.getDefaultGuestRestrictions();
- guestRestrictions.putBoolean(DISALLOW_SMS, true);
- guestRestrictions.putBoolean(DISALLOW_INSTALL_UNKNOWN_SOURCES, true);
- mService.setUserRestrictions(guestRestrictions, guest.id);
- } catch (RemoteException re) {
- Log.w(TAG, "Could not update guest restrictions");
+ UserInfo guest = null;
+ try {
+ guest = mService.createUser(name, UserInfo.FLAG_GUEST);
+ if (guest != null) {
+ Settings.Secure.putStringForUser(context.getContentResolver(),
+ Settings.Secure.SKIP_FIRST_USE_HINTS, "1", guest.id);
+
+ mService.setUserRestriction(DISALLOW_SMS, true, guest.id);
+ mService.setUserRestriction(DISALLOW_INSTALL_UNKNOWN_SOURCES, true, guest.id);
}
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not create a user", re);
}
return guest;
}
- private static void addDefaultUserRestrictions(Bundle restrictions) {
- restrictions.putBoolean(DISALLOW_OUTGOING_CALLS, true);
- restrictions.putBoolean(DISALLOW_SMS, true);
- }
-
/**
* Creates a user with the specified name and options as a profile of another user.
* Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
@@ -1059,13 +1078,15 @@
/**
* Checks whether it's possible to add more managed profiles. Caller must hold the MANAGE_USERS
* permission.
+ * if allowedToRemoveOne is true and if the user already has a managed profile, then return if
+ * we could add a new managed profile to this user after removing the existing one.
*
* @return true if more managed profiles can be added, false if limit has been reached.
* @hide
*/
- public boolean canAddMoreManagedProfiles(int userId) {
+ public boolean canAddMoreManagedProfiles(int userId, boolean allowedToRemoveOne) {
try {
- return mService.canAddMoreManagedProfiles(userId);
+ return mService.canAddMoreManagedProfiles(userId, allowedToRemoveOne);
} catch (RemoteException re) {
Log.w(TAG, "Could not check if we can add more managed profiles", re);
return false;
@@ -1093,6 +1114,22 @@
}
/**
+ * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+ * @param userId one of the two user ids to check.
+ * @param otherUserId one of the two user ids to check.
+ * @return true if the two user ids are in the same profile group.
+ * @hide
+ */
+ public boolean isSameProfileGroup(int userId, int otherUserId) {
+ try {
+ return mService.isSameProfileGroup(userId, otherUserId);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not get user list", re);
+ return false;
+ }
+ }
+
+ /**
* Returns list of the profiles of userHandle including
* userHandle itself.
* Note that this returns only enabled.
@@ -1449,15 +1486,6 @@
return false;
}
- /** @hide */
- public void removeRestrictions() {
- try {
- mService.removeRestrictions();
- } catch (RemoteException re) {
- Log.w(TAG, "Could not change restrictions pin");
- }
- }
-
/**
* @hide
* Set restrictions that should apply to any future guest user that's created.
diff --git a/core/java/android/os/UserManagerInternal.java b/core/java/android/os/UserManagerInternal.java
new file mode 100644
index 0000000..9178ec6
--- /dev/null
+++ b/core/java/android/os/UserManagerInternal.java
@@ -0,0 +1,83 @@
+/*
+ * 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 android.os;
+
+/**
+ * @hide Only for use within the system server.
+ */
+public abstract class UserManagerInternal {
+ public interface UserRestrictionsListener {
+ /**
+ * Called when a user restriction changes.
+ *
+ * @param userId target user id
+ * @param newRestrictions new user restrictions
+ * @param prevRestrictions user restrictions that were previously set
+ */
+ void onUserRestrictionsChanged(int userId, Bundle newRestrictions, Bundle prevRestrictions);
+ }
+
+ /**
+ * Lock that must be held when calling certain methods in this class.
+ *
+ * This is used to avoid dead lock between
+ * {@link com.android.server.pm.UserManagerService} and
+ * {@link com.android.server.devicepolicy.DevicePolicyManagerService}. This lock should not
+ * be newly taken while holding the DPMS lock, which would cause a dead lock. Take this
+ * lock first before taking the DPMS lock to avoid that.
+ */
+ public abstract Object getUserRestrictionsLock();
+
+ /**
+ * Called by {@link com.android.server.devicepolicy.DevicePolicyManagerService} to get
+ * {@link com.android.server.pm.UserManagerService} to update effective user restrictions.
+ *
+ * Must be called while taking the {@link #getUserRestrictionsLock()} lock.
+ */
+ public abstract void updateEffectiveUserRestrictionsLR(int userId);
+
+ /**
+ * Called by {@link com.android.server.devicepolicy.DevicePolicyManagerService} to get
+ * {@link com.android.server.pm.UserManagerService} to update effective user restrictions.
+ *
+ * Must be called while taking the {@link #getUserRestrictionsLock()} lock.
+ */
+ public abstract void updateEffectiveUserRestrictionsForAllUsersLR();
+
+ /**
+ * Returns the "base" user restrictions.
+ *
+ * Used by {@link com.android.server.devicepolicy.DevicePolicyManagerService} for upgrading
+ * from MNC.
+ */
+ public abstract Bundle getBaseUserRestrictions(int userId);
+
+ /**
+ * Called by {@link com.android.server.devicepolicy.DevicePolicyManagerService} for upgrading
+ * from MNC.
+ */
+ public abstract void setBaseUserRestrictionsByDpmsForMigration(int userId,
+ Bundle baseRestrictions);
+
+ /** Return a user restriction. */
+ public abstract boolean getUserRestriction(int userId, String key);
+
+ /** Adds a listener to user restriction changes. */
+ public abstract void addUserRestrictionsListener(UserRestrictionsListener listener);
+
+ /** Remove a {@link UserRestrictionsListener}. */
+ public abstract void removeUserRestrictionsListener(UserRestrictionsListener listener);
+}
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index 01e1dc8..d19c7c9 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -758,6 +758,22 @@
return _result;
}
+ public boolean isConvertibleToFBE() throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ boolean _result;
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ mRemote.transact(Stub.TRANSACTION_isConvertibleToFBE, _data, _reply, 0);
+ _reply.readException();
+ _result = _reply.readInt() != 0;
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ return _result;
+ }
+
public StorageVolume[] getVolumeList(int uid, String packageName, int flags)
throws RemoteException {
Parcel _data = Parcel.obtain();
@@ -1182,14 +1198,14 @@
}
@Override
- public void createNewUserDir(int userHandle, String path) throws RemoteException {
+ public void createUserKey(int userId, int serialNumber) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
- _data.writeInt(userHandle);
- _data.writeString(path);
- mRemote.transact(Stub.TRANSACTION_createNewUserDir, _data, _reply, 0);
+ _data.writeInt(userId);
+ _data.writeInt(serialNumber);
+ mRemote.transact(Stub.TRANSACTION_createUserKey, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
@@ -1198,19 +1214,104 @@
}
@Override
- public void deleteUserKey(int userHandle) throws RemoteException {
+ public void destroyUserKey(int userId) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
- _data.writeInt(userHandle);
- mRemote.transact(Stub.TRANSACTION_deleteUserKey, _data, _reply, 0);
+ _data.writeInt(userId);
+ mRemote.transact(Stub.TRANSACTION_destroyUserKey, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
+
+ @Override
+ public void unlockUserKey(int userId, int serialNumber, byte[] token) throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeInt(userId);
+ _data.writeInt(serialNumber);
+ _data.writeByteArray(token);
+ mRemote.transact(Stub.TRANSACTION_unlockUserKey, _data, _reply, 0);
+ _reply.readException();
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ }
+
+ @Override
+ public void lockUserKey(int userId) throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeInt(userId);
+ mRemote.transact(Stub.TRANSACTION_lockUserKey, _data, _reply, 0);
+ _reply.readException();
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ }
+
+ @Override
+ public boolean isUserKeyUnlocked(int userId) throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ boolean _result;
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeInt(userId);
+ mRemote.transact(Stub.TRANSACTION_isUserKeyUnlocked, _data, _reply, 0);
+ _reply.readException();
+ _result = 0 != _reply.readInt();
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ return _result;
+ }
+
+ @Override
+ public void prepareUserStorage(String volumeUuid, int userId, int serialNumber)
+ throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeString(volumeUuid);
+ _data.writeInt(userId);
+ _data.writeInt(serialNumber);
+ mRemote.transact(Stub.TRANSACTION_prepareUserStorage, _data, _reply, 0);
+ _reply.readException();
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ }
+
+ @Override
+ public boolean isPerUserEncryptionEnabled() throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ boolean _result;
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ mRemote.transact(Stub.TRANSACTION_isPerUserEncryptionEnabled, _data, _reply, 0);
+ _reply.readException();
+ _result = 0 != _reply.readInt();
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ return _result;
+ }
}
private static final String DESCRIPTOR = "IMountService";
@@ -1326,9 +1427,17 @@
static final int TRANSACTION_benchmark = IBinder.FIRST_CALL_TRANSACTION + 59;
static final int TRANSACTION_setDebugFlags = IBinder.FIRST_CALL_TRANSACTION + 60;
- static final int TRANSACTION_createNewUserDir = IBinder.FIRST_CALL_TRANSACTION + 62;
+ static final int TRANSACTION_createUserKey = IBinder.FIRST_CALL_TRANSACTION + 61;
+ static final int TRANSACTION_destroyUserKey = IBinder.FIRST_CALL_TRANSACTION + 62;
- static final int TRANSACTION_deleteUserKey = IBinder.FIRST_CALL_TRANSACTION + 63;
+ static final int TRANSACTION_unlockUserKey = IBinder.FIRST_CALL_TRANSACTION + 63;
+ static final int TRANSACTION_lockUserKey = IBinder.FIRST_CALL_TRANSACTION + 64;
+ static final int TRANSACTION_isUserKeyUnlocked = IBinder.FIRST_CALL_TRANSACTION + 65;
+
+ static final int TRANSACTION_prepareUserStorage = IBinder.FIRST_CALL_TRANSACTION + 66;
+
+ static final int TRANSACTION_isPerUserEncryptionEnabled = IBinder.FIRST_CALL_TRANSACTION + 67;
+ static final int TRANSACTION_isConvertibleToFBE = IBinder.FIRST_CALL_TRANSACTION + 68;
/**
* Cast an IBinder object into an IMountService interface, generating a
@@ -1725,6 +1834,13 @@
reply.writeString(contents);
return true;
}
+ case TRANSACTION_isConvertibleToFBE: {
+ data.enforceInterface(DESCRIPTOR);
+ int resultCode = isConvertibleToFBE() ? 1 : 0;
+ reply.writeNoException();
+ reply.writeInt(resultCode);
+ return true;
+ }
case TRANSACTION_resizeSecureContainer: {
data.enforceInterface(DESCRIPTOR);
String id;
@@ -1885,21 +2001,61 @@
reply.writeNoException();
return true;
}
- case TRANSACTION_createNewUserDir: {
+ case TRANSACTION_createUserKey: {
data.enforceInterface(DESCRIPTOR);
- int userHandle = data.readInt();
- String path = data.readString();
- createNewUserDir(userHandle, path);
+ int userId = data.readInt();
+ int serialNumber = data.readInt();
+ createUserKey(userId, serialNumber);
reply.writeNoException();
return true;
}
- case TRANSACTION_deleteUserKey: {
+ case TRANSACTION_destroyUserKey: {
data.enforceInterface(DESCRIPTOR);
- int userHandle = data.readInt();
- deleteUserKey(userHandle);
+ int userId = data.readInt();
+ destroyUserKey(userId);
reply.writeNoException();
return true;
}
+ case TRANSACTION_unlockUserKey: {
+ data.enforceInterface(DESCRIPTOR);
+ int userId = data.readInt();
+ int serialNumber = data.readInt();
+ byte[] token = data.createByteArray();
+ unlockUserKey(userId, serialNumber, token);
+ reply.writeNoException();
+ return true;
+ }
+ case TRANSACTION_lockUserKey: {
+ data.enforceInterface(DESCRIPTOR);
+ int userId = data.readInt();
+ lockUserKey(userId);
+ reply.writeNoException();
+ return true;
+ }
+ case TRANSACTION_isUserKeyUnlocked: {
+ data.enforceInterface(DESCRIPTOR);
+ int userId = data.readInt();
+ boolean result = isUserKeyUnlocked(userId);
+ reply.writeNoException();
+ reply.writeInt(result ? 1 : 0);
+ return true;
+ }
+ case TRANSACTION_prepareUserStorage: {
+ data.enforceInterface(DESCRIPTOR);
+ String volumeUuid = data.readString();
+ int userId = data.readInt();
+ int serialNumber = data.readInt();
+ prepareUserStorage(volumeUuid, userId, serialNumber);
+ reply.writeNoException();
+ return true;
+ }
+ case TRANSACTION_isPerUserEncryptionEnabled: {
+ data.enforceInterface(DESCRIPTOR);
+ boolean result = isPerUserEncryptionEnabled();
+ reply.writeNoException();
+ reply.writeInt(result ? 1 : 0);
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
}
@@ -2169,6 +2325,8 @@
*/
public String getField(String field) throws RemoteException;
+ public boolean isConvertibleToFBE() throws RemoteException;
+
public int resizeSecureContainer(String id, int sizeMb, String key) throws RemoteException;
/**
@@ -2210,18 +2368,15 @@
public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback)
throws RemoteException;
- /**
- * Creates the user data directory, possibly encrypted
- * @param userHandle Handle of the user whose directory we are creating
- * @param path Path at which to create the directory.
- */
- public void createNewUserDir(int userHandle, String path)
- throws RemoteException;
+ public void createUserKey(int userId, int serialNumber) throws RemoteException;
+ public void destroyUserKey(int userId) throws RemoteException;
- /**
- * Securely delete the user's encryption key
- * @param userHandle Handle of the user whose key we are deleting
- */
- public void deleteUserKey(int userHandle)
- throws RemoteException;
+ public void unlockUserKey(int userId, int serialNumber, byte[] token) throws RemoteException;
+ public void lockUserKey(int userId) throws RemoteException;
+ public boolean isUserKeyUnlocked(int userId) throws RemoteException;
+
+ public void prepareUserStorage(String volumeUuid, int userId, int serialNumber)
+ throws RemoteException;
+
+ public boolean isPerUserEncryptionEnabled() throws RemoteException;
}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 1d92453..27df46d 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -76,7 +76,11 @@
/** {@hide} */
public static final String PROP_HAS_ADOPTABLE = "vold.has_adoptable";
/** {@hide} */
+ public static final String PROP_HAS_FBE = "vold.has_fbe";
+ /** {@hide} */
public static final String PROP_FORCE_ADOPTABLE = "persist.fw.force_adoptable";
+ /** {@hide} */
+ public static final String PROP_EMULATE_FBE = "vold.emulate_fbe";
/** {@hide} */
public static final String UUID_PRIVATE_INTERNAL = null;
@@ -85,6 +89,8 @@
/** {@hide} */
public static final int DEBUG_FORCE_ADOPTABLE = 1 << 0;
+ /** {@hide} */
+ public static final int DEBUG_EMULATE_FBE = 1 << 1;
/** {@hide} */
public static final int FLAG_FOR_WRITE = 1 << 0;
@@ -960,18 +966,63 @@
}
/** {@hide} */
- public void createNewUserDir(int userHandle, File path) {
+ public void createUserKey(int userId, int serialNumber) {
try {
- mMountService.createNewUserDir(userHandle, path.getAbsolutePath());
+ mMountService.createUserKey(userId, serialNumber);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
/** {@hide} */
- public void deleteUserKey(int userHandle) {
+ public void destroyUserKey(int userId) {
try {
- mMountService.deleteUserKey(userHandle);
+ mMountService.destroyUserKey(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /** {@hide} */
+ public void unlockUserKey(int userId, int serialNumber, byte[] token) {
+ try {
+ mMountService.unlockUserKey(userId, serialNumber, token);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /** {@hide} */
+ public void lockUserKey(int userId) {
+ try {
+ mMountService.lockUserKey(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /** {@hide} */
+ public void prepareUserStorage(String volumeUuid, int userId, int serialNumber) {
+ try {
+ mMountService.prepareUserStorage(volumeUuid, userId, serialNumber);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /** {@hide} */
+ public boolean isUserKeyUnlocked(int userId) {
+ try {
+ return mMountService.isUserKeyUnlocked(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /** {@hide} */
+ public boolean isPerUserEncryptionEnabled() {
+ try {
+ return mMountService.isPerUserEncryptionEnabled();
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index 979c828..2445bc2 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -383,6 +383,7 @@
final IntentFilter filter = new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION);
filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
filter.addAction(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED);
+ filter.addAction(AudioManager.STREAM_DEVICES_CHANGED_ACTION);
mContext.registerReceiver(this, filter);
} else {
mContext.unregisterReceiver(this);
@@ -395,13 +396,7 @@
if (AudioManager.VOLUME_CHANGED_ACTION.equals(action)) {
int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
int streamValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1);
- final boolean streamMatch = mNotificationOrRing ? isNotificationOrRing(streamType)
- : (streamType == mStreamType);
- if (mSeekBar != null && streamMatch && streamValue != -1) {
- final boolean muted = mAudioManager.isStreamMute(mStreamType)
- || streamValue == 0;
- mUiHandler.postUpdateSlider(streamValue, mLastAudibleStreamVolume, muted);
- }
+ updateVolumeSlider(streamType, streamValue);
} else if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) {
if (mNotificationOrRing) {
mRingerMode = mAudioManager.getRingerModeInternal();
@@ -409,10 +404,24 @@
if (mAffectedByRingerMode) {
updateSlider();
}
+ } else if (AudioManager.STREAM_DEVICES_CHANGED_ACTION.equals(action)) {
+ int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
+ int streamVolume = mAudioManager.getStreamVolume(streamType);
+ updateVolumeSlider(streamType, streamVolume);
} else if (NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED.equals(action)) {
mZenMode = mNotificationManager.getZenMode();
updateSlider();
}
}
+
+ private void updateVolumeSlider(int streamType, int streamValue) {
+ final boolean streamMatch = mNotificationOrRing ? isNotificationOrRing(streamType)
+ : (streamType == mStreamType);
+ if (mSeekBar != null && streamMatch && streamValue != -1) {
+ final boolean muted = mAudioManager.isStreamMute(mStreamType)
+ || streamValue == 0;
+ mUiHandler.postUpdateSlider(streamValue, mLastAudibleStreamVolume, muted);
+ }
+ }
}
}
diff --git a/core/java/android/print/IPrintSpooler.aidl b/core/java/android/print/IPrintSpooler.aidl
index 7b2cf25..db2bf1a 100644
--- a/core/java/android/print/IPrintSpooler.aidl
+++ b/core/java/android/print/IPrintSpooler.aidl
@@ -46,4 +46,5 @@
void writePrintJobData(in ParcelFileDescriptor fd, in PrintJobId printJobId);
void setClient(IPrintSpoolerClient client);
void setPrintJobCancelling(in PrintJobId printJobId, boolean cancelling);
+ void removeApprovedPrintService(in ComponentName serviceToRemove);
}
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 342f8c7..4b63c36 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -38,7 +38,6 @@
import android.telecom.TelecomManager;
import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
-import android.util.Log;
import com.android.internal.telephony.CallerInfo;
import com.android.internal.telephony.PhoneConstants;
@@ -406,6 +405,14 @@
public static final String SUB_ID = "sub_id";
/**
+ * The post-dial portion of a dialed number, including any digits dialed after a
+ * {@link TelecomManager#DTMF_CHARACTER_PAUSE} or a {@link
+ * TelecomManager#DTMF_CHARACTER_WAIT} and these characters themselves.
+ * <P>Type: TEXT</P>
+ */
+ public static final String POST_DIAL_DIGITS = "post_dial_digits";
+
+ /**
* If a successful call is made that is longer than this duration, update the phone number
* in the ContactsProvider with the normalized version of the number, based on the user's
* current country code.
@@ -436,7 +443,7 @@
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, presentation, callType, features, accountHandle,
+ return addCall(ci, context, number, "", presentation, callType, features, accountHandle,
start, duration, dataUsage, false, false);
}
@@ -466,10 +473,11 @@
* {@hide}
*/
public static Uri addCall(CallerInfo ci, Context context, String number,
- int presentation, int callType, int features, PhoneAccountHandle accountHandle,
- long start, int duration, Long dataUsage, boolean addForAllUsers) {
- return addCall(ci, context, number, presentation, callType, features, accountHandle,
- start, duration, dataUsage, addForAllUsers, false);
+ String postDialDigits, int presentation, int callType, int features,
+ PhoneAccountHandle accountHandle, long start, int duration, Long dataUsage,
+ boolean addForAllUsers) {
+ return addCall(ci, context, number, postDialDigits, presentation, callType, features,
+ accountHandle, start, duration, dataUsage, addForAllUsers, false);
}
/**
@@ -479,6 +487,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 postDialDigits the post-dial digits that were dialed after the number,
+ * if it was outgoing. Otherwise it is ''.
* @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"
@@ -499,8 +509,9 @@
* {@hide}
*/
public static Uri addCall(CallerInfo ci, Context context, String number,
- int presentation, int callType, int features, PhoneAccountHandle accountHandle,
- long start, int duration, Long dataUsage, boolean addForAllUsers, boolean is_read) {
+ String postDialDigits, int presentation, int callType, int features,
+ PhoneAccountHandle accountHandle, long start, int duration, Long dataUsage,
+ boolean addForAllUsers, boolean is_read) {
final ContentResolver resolver = context.getContentResolver();
int numberPresentation = PRESENTATION_ALLOWED;
@@ -551,6 +562,7 @@
ContentValues values = new ContentValues(6);
values.put(NUMBER, number);
+ values.put(POST_DIAL_DIGITS, postDialDigits);
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 241e6db..159ca01 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -231,6 +231,7 @@
* @see #FLAG_SUPPORTS_WRITE
* @see #FLAG_SUPPORTS_DELETE
* @see #FLAG_SUPPORTS_THUMBNAIL
+ * @see #FLAG_SUPPORTS_TYPED_DOCUMENT
* @see #FLAG_DIR_PREFERS_GRID
* @see #FLAG_DIR_PREFERS_LAST_MODIFIED
*/
@@ -347,6 +348,15 @@
public static final int FLAG_SUPPORTS_MOVE = 1 << 8;
/**
+ * Flag indicating that a document can be converted to alternative types.
+ *
+ * @see #COLUMN_FLAGS
+ * @see DocumentsProvider#openTypedDocument(String, String, Bundle,
+ * android.os.CancellationSignal)
+ */
+ public static final int FLAG_SUPPORTS_TYPED_DOCUMENT = 1 << 9;
+
+ /**
* Flag indicating that document titles should be hidden when viewing
* this directory in a larger format grid. For example, a directory
* containing only images may want the image thumbnails to speak for
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 28ac165..f01073b 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -31,6 +31,7 @@
import static android.provider.DocumentsContract.isTreeUri;
import android.annotation.CallSuper;
+import android.content.ClipDescription;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentValues;
@@ -502,6 +503,29 @@
}
/**
+ * Open and return the document in a format matching the specified MIME
+ * type filter.
+ * <p>
+ * A provider may perform a conversion if the documents's MIME type is not
+ * matching the specified MIME type filter.
+ *
+ * @param documentId the document to return.
+ * @param mimeTypeFilter the MIME type filter for the requested format. May
+ * be *\/*, which matches any MIME type.
+ * @param opts extra options from the client. Specific to the content
+ * provider.
+ * @param signal used by the caller to signal if the request should be
+ * cancelled. May be null.
+ * @see Document#FLAG_SUPPORTS_TYPED_DOCUMENT
+ */
+ @SuppressWarnings("unused")
+ public AssetFileDescriptor openTypedDocument(
+ String documentId, String mimeTypeFilter, Bundle opts, CancellationSignal signal)
+ throws FileNotFoundException {
+ throw new UnsupportedOperationException("Typed documents not supported");
+ }
+
+ /**
* Implementation is provided by the parent class. Cannot be overriden.
*
* @see #queryRoots(String[])
@@ -846,34 +870,50 @@
* Implementation is provided by the parent class. Cannot be overriden.
*
* @see #openDocumentThumbnail(String, Point, CancellationSignal)
+ * @see #openTypedDocument(String, String, Bundle, CancellationSignal)
*/
@Override
public final AssetFileDescriptor openTypedAssetFile(Uri uri, String mimeTypeFilter, Bundle opts)
throws FileNotFoundException {
- enforceTree(uri);
- if (opts != null && opts.containsKey(ContentResolver.EXTRA_SIZE)) {
- final Point sizeHint = opts.getParcelable(ContentResolver.EXTRA_SIZE);
- return openDocumentThumbnail(getDocumentId(uri), sizeHint, null);
- } else {
- return super.openTypedAssetFile(uri, mimeTypeFilter, opts);
- }
+ return openTypedAssetFileImpl(uri, mimeTypeFilter, opts, null);
}
/**
* Implementation is provided by the parent class. Cannot be overriden.
*
* @see #openDocumentThumbnail(String, Point, CancellationSignal)
+ * @see #openTypedDocument(String, String, Bundle, CancellationSignal)
*/
@Override
public final AssetFileDescriptor openTypedAssetFile(
Uri uri, String mimeTypeFilter, Bundle opts, CancellationSignal signal)
throws FileNotFoundException {
+ return openTypedAssetFileImpl(uri, mimeTypeFilter, opts, signal);
+ }
+
+ /**
+ * @hide
+ */
+ private final AssetFileDescriptor openTypedAssetFileImpl(
+ Uri uri, String mimeTypeFilter, Bundle opts, CancellationSignal signal)
+ throws FileNotFoundException {
enforceTree(uri);
+ final String documentId = getDocumentId(uri);
if (opts != null && opts.containsKey(ContentResolver.EXTRA_SIZE)) {
final Point sizeHint = opts.getParcelable(ContentResolver.EXTRA_SIZE);
- return openDocumentThumbnail(getDocumentId(uri), sizeHint, signal);
- } else {
- return super.openTypedAssetFile(uri, mimeTypeFilter, opts, signal);
+ return openDocumentThumbnail(documentId, sizeHint, signal);
}
+ if ("*/*".equals(mimeTypeFilter)) {
+ // If they can take anything, the untyped open call is good enough.
+ return openAssetFile(uri, "r");
+ }
+ final String baseType = getType(uri);
+ if (baseType != null && ClipDescription.compareMimeTypes(baseType, mimeTypeFilter)) {
+ // Use old untyped open call if this provider has a type for this
+ // URI and it matches the request.
+ return openAssetFile(uri, "r");
+ }
+ // For any other yet unhandled case, let the provider subclass handle it.
+ return openTypedDocument(documentId, mimeTypeFilter, opts, signal);
}
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d601831..9034cc9 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1434,25 +1434,6 @@
}
/**
- * An app can use this method to check if it is currently allowed to change the network
- * state. In order to be allowed to do so, an app must first declare either the
- * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} or
- * {@link android.Manifest.permission#WRITE_SETTINGS} permission in its manifest. If it
- * is currently disallowed, it can prompt the user to grant it this capability through a
- * management UI by sending an Intent with action
- * {@link android.provider.Settings#ACTION_MANAGE_WRITE_SETTINGS}.
- *
- * @param context A context
- * @return true if the calling app can change the state of network, false otherwise.
- * @hide
- */
- public static boolean canChangeNetworkState(Context context) {
- int uid = Binder.getCallingUid();
- return Settings.isCallingPackageAllowedToChangeNetworkState(context, uid, Settings
- .getPackageNameForUid(context, uid), false);
- }
-
- /**
* System settings, containing miscellaneous system preferences. This
* table holds simple name/value pairs. There are convenience
* functions for accessing individual settings entries.
@@ -5617,17 +5598,10 @@
/**
* Names of the packages that the current user has explicitly allowed to
* manage notification policy configuration, separated by ':'.
- *
- * @hide
*/
public static final String ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES =
"enabled_notification_policy_access_packages";
- /**
- * @hide
- */
- public static final String ENABLED_CONDITION_PROVIDERS = "enabled_condition_providers";
-
/** @hide */
public static final String BAR_SERVICE_COMPONENT = "bar_service_component";
@@ -6296,13 +6270,25 @@
*/
public static final String FORCE_ALLOW_ON_EXTERNAL = "force_allow_on_external";
+ /**
+ * Whether any activity can be resized. When this is true, any
+ * activity, regardless of manifest values, can be resized for multi-window.
+ * (0 = false, 1 = true)
+ * @hide
+ */
+ public static final String DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES
+ = "force_resizable_activities";
+
/**
* Whether user has enabled development settings.
*/
public static final String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled";
/**
- * Whether the device has been provisioned (0 = false, 1 = true)
+ * Whether the device has been provisioned (0 = false, 1 = true).
+ * <p>On a multiuser device with a separate system user, the screen may be locked
+ * as soon as this is set to true and further activities cannot be launched on the
+ * system user unless they are marked to show over keyguard.
*/
public static final String DEVICE_PROVISIONED = "device_provisioned";
@@ -8384,7 +8370,7 @@
* write/modify system settings, as the condition differs for pre-M, M+, and
* privileged/preinstalled apps. If the provided uid does not match the
* callingPackage, a negative result will be returned. The caller is expected to have
- * either WRITE_SETTINGS or CHANGE_NETWORK_STATE permission declared.
+ * the WRITE_SETTINGS permission declared.
*
* Note: if the check is successful, the operation of this app will be updated to the
* current time.
@@ -8400,31 +8386,22 @@
/**
* Performs a strict and comprehensive check of whether a calling package is allowed to
* change the state of network, as the condition differs for pre-M, M+, and
- * privileged/preinstalled apps. If the provided uid does not match the
- * callingPackage, a negative result will be returned. The caller is expected to have
- * either of CHANGE_NETWORK_STATE or WRITE_SETTINGS permission declared.
- * @hide
- */
- public static boolean isCallingPackageAllowedToChangeNetworkState(Context context, int uid,
- String callingPackage, boolean throwException) {
- return isCallingPackageAllowedToPerformAppOpsProtectedOperation(context, uid,
- callingPackage, throwException, AppOpsManager.OP_WRITE_SETTINGS,
- PM_CHANGE_NETWORK_STATE, false);
- }
-
- /**
- * Performs a strict and comprehensive check of whether a calling package is allowed to
- * change the state of network, as the condition differs for pre-M, M+, and
- * privileged/preinstalled apps. If the provided uid does not match the
- * callingPackage, a negative result will be returned. The caller is expected to have
- * either CHANGE_NETWORK_STATE or WRITE_SETTINGS permission declared.
+ * privileged/preinstalled apps. The caller is expected to have either the
+ * CHANGE_NETWORK_STATE or the WRITE_SETTINGS permission declared. Either of these
+ * permissions allow changing network state; WRITE_SETTINGS is a runtime permission and
+ * can be revoked, but (except in M, excluding M MRs), CHANGE_NETWORK_STATE is a normal
+ * permission and cannot be revoked. See http://b/23597341
*
- * Note: if the check is successful, the operation of this app will be updated to the
- * current time.
+ * Note: if the check succeeds because the application holds WRITE_SETTINGS, the operation
+ * of this app will be updated to the current time.
* @hide
*/
public static boolean checkAndNoteChangeNetworkStateOperation(Context context, int uid,
String callingPackage, boolean throwException) {
+ if (context.checkCallingOrSelfPermission(android.Manifest.permission.CHANGE_NETWORK_STATE)
+ == PackageManager.PERMISSION_GRANTED) {
+ return true;
+ }
return isCallingPackageAllowedToPerformAppOpsProtectedOperation(context, uid,
callingPackage, throwException, AppOpsManager.OP_WRITE_SETTINGS,
PM_CHANGE_NETWORK_STATE, true);
diff --git a/core/java/android/security/net/config/ApplicationConfig.java b/core/java/android/security/net/config/ApplicationConfig.java
new file mode 100644
index 0000000..b627641
--- /dev/null
+++ b/core/java/android/security/net/config/ApplicationConfig.java
@@ -0,0 +1,147 @@
+/*
+ * 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 android.security.net.config;
+
+import android.util.Pair;
+import java.util.Locale;
+import java.util.Set;
+import javax.net.ssl.X509TrustManager;
+
+/**
+ * An application's network security configuration.
+ *
+ * <p>{@link #getConfigForHostname(String)} provides a means to obtain network security
+ * configuration to be used for communicating with a specific hostname.</p>
+ *
+ * @hide
+ */
+public final class ApplicationConfig {
+ private static ApplicationConfig sInstance;
+ private static Object sLock = new Object();
+
+ private Set<Pair<Domain, NetworkSecurityConfig>> mConfigs;
+ private NetworkSecurityConfig mDefaultConfig;
+ private X509TrustManager mTrustManager;
+
+ private ConfigSource mConfigSource;
+ private boolean mInitialized;
+ private final Object mLock = new Object();
+
+ public ApplicationConfig(ConfigSource configSource) {
+ mConfigSource = configSource;
+ mInitialized = false;
+ }
+
+ /**
+ * @hide
+ */
+ public boolean hasPerDomainConfigs() {
+ ensureInitialized();
+ return mConfigs != null && !mConfigs.isEmpty();
+ }
+
+ /**
+ * Get the {@link NetworkSecurityConfig} corresponding to the provided hostname.
+ * When matching the most specific matching domain rule will be used, if no match exists
+ * then the default configuration will be returned.
+ *
+ * {@code NetworkSecurityConfig} objects returned by this method can be safely cached for
+ * {@code hostname}. Subsequent calls with the same hostname will always return the same
+ * {@code NetworkSecurityConfig}.
+ *
+ * @return {@link NetworkSecurityConfig} to be used to determine
+ * the network security configuration for connections to {@code hostname}.
+ */
+ public NetworkSecurityConfig getConfigForHostname(String hostname) {
+ ensureInitialized();
+ if (hostname.isEmpty() || mConfigs == null) {
+ return mDefaultConfig;
+ }
+ if (hostname.charAt(0) == '.') {
+ throw new IllegalArgumentException("hostname must not begin with a .");
+ }
+ // Domains are case insensitive.
+ hostname = hostname.toLowerCase(Locale.US);
+ // Normalize hostname by removing trailing . if present, all Domain hostnames are
+ // absolute.
+ if (hostname.charAt(hostname.length() - 1) == '.') {
+ hostname = hostname.substring(0, hostname.length() - 1);
+ }
+ // Find the Domain -> NetworkSecurityConfig entry with the most specific matching
+ // Domain entry for hostname.
+ // TODO: Use a smarter data structure for the lookup.
+ Pair<Domain, NetworkSecurityConfig> bestMatch = null;
+ for (Pair<Domain, NetworkSecurityConfig> entry : mConfigs) {
+ Domain domain = entry.first;
+ NetworkSecurityConfig config = entry.second;
+ // Check for an exact match.
+ if (domain.hostname.equals(hostname)) {
+ return config;
+ }
+ // Otherwise check if the Domain includes sub-domains and that the hostname is a
+ // sub-domain of the Domain.
+ if (domain.subdomainsIncluded
+ && hostname.endsWith(domain.hostname)
+ && hostname.charAt(hostname.length() - domain.hostname.length() - 1) == '.') {
+ if (bestMatch == null) {
+ bestMatch = entry;
+ } else if (domain.hostname.length() > bestMatch.first.hostname.length()) {
+ bestMatch = entry;
+ }
+ }
+ }
+ if (bestMatch != null) {
+ return bestMatch.second;
+ }
+ // If no match was found use the default configuration.
+ return mDefaultConfig;
+ }
+
+ /**
+ * Returns the {@link X509TrustManager} that implements the checking of trust anchors and
+ * certificate pinning based on this configuration.
+ */
+ public X509TrustManager getTrustManager() {
+ ensureInitialized();
+ return mTrustManager;
+ }
+
+ private void ensureInitialized() {
+ synchronized(mLock) {
+ if (mInitialized) {
+ return;
+ }
+ mConfigs = mConfigSource.getPerDomainConfigs();
+ mDefaultConfig = mConfigSource.getDefaultConfig();
+ mConfigSource = null;
+ mTrustManager = new RootTrustManager(this);
+ mInitialized = true;
+ }
+ }
+
+ public static void setDefaultInstance(ApplicationConfig config) {
+ synchronized (sLock) {
+ sInstance = config;
+ }
+ }
+
+ public static ApplicationConfig getDefaultInstance() {
+ synchronized (sLock) {
+ return sInstance;
+ }
+ }
+}
diff --git a/core/java/android/security/net/config/CertificateSource.java b/core/java/android/security/net/config/CertificateSource.java
new file mode 100644
index 0000000..386354d
--- /dev/null
+++ b/core/java/android/security/net/config/CertificateSource.java
@@ -0,0 +1,25 @@
+/*
+ * 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 android.security.net.config;
+
+import java.util.Set;
+import java.security.cert.X509Certificate;
+
+/** @hide */
+public interface CertificateSource {
+ Set<X509Certificate> getCertificates();
+}
diff --git a/core/java/android/security/net/config/CertificatesEntryRef.java b/core/java/android/security/net/config/CertificatesEntryRef.java
new file mode 100644
index 0000000..2ba38c21
--- /dev/null
+++ b/core/java/android/security/net/config/CertificatesEntryRef.java
@@ -0,0 +1,41 @@
+/*
+ * 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 android.security.net.config;
+
+import android.util.ArraySet;
+import java.util.Set;
+import java.security.cert.X509Certificate;
+
+/** @hide */
+public final class CertificatesEntryRef {
+ private final CertificateSource mSource;
+ private final boolean mOverridesPins;
+
+ public CertificatesEntryRef(CertificateSource source, boolean overridesPins) {
+ mSource = source;
+ mOverridesPins = overridesPins;
+ }
+
+ public Set<TrustAnchor> getTrustAnchors() {
+ // TODO: cache this [but handle mutable sources]
+ Set<TrustAnchor> anchors = new ArraySet<TrustAnchor>();
+ for (X509Certificate cert : mSource.getCertificates()) {
+ anchors.add(new TrustAnchor(cert, mOverridesPins));
+ }
+ return anchors;
+ }
+}
diff --git a/core/java/android/security/net/config/ConfigSource.java b/core/java/android/security/net/config/ConfigSource.java
new file mode 100644
index 0000000..4adf265
--- /dev/null
+++ b/core/java/android/security/net/config/ConfigSource.java
@@ -0,0 +1,26 @@
+/*
+ * 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 android.security.net.config;
+
+import android.util.Pair;
+import java.util.Set;
+
+/** @hide */
+public interface ConfigSource {
+ Set<Pair<Domain, NetworkSecurityConfig>> getPerDomainConfigs();
+ NetworkSecurityConfig getDefaultConfig();
+}
diff --git a/core/java/android/security/net/config/Domain.java b/core/java/android/security/net/config/Domain.java
new file mode 100644
index 0000000..5bb727a
--- /dev/null
+++ b/core/java/android/security/net/config/Domain.java
@@ -0,0 +1,57 @@
+/*
+ * 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 android.security.net.config;
+
+import java.util.Locale;
+/** @hide */
+public final class Domain {
+ /**
+ * Lower case hostname for this domain rule.
+ */
+ public final String hostname;
+
+ /**
+ * Whether this domain includes subdomains.
+ */
+ public final boolean subdomainsIncluded;
+
+ public Domain(String hostname, boolean subdomainsIncluded) {
+ if (hostname == null) {
+ throw new NullPointerException("Hostname must not be null");
+ }
+ this.hostname = hostname.toLowerCase(Locale.US);
+ this.subdomainsIncluded = subdomainsIncluded;
+ }
+
+ @Override
+ public int hashCode() {
+ return hostname.hashCode() ^ (subdomainsIncluded ? 1231 : 1237);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+ if (!(other instanceof Domain)) {
+ return false;
+ }
+ Domain otherDomain = (Domain) other;
+ return otherDomain.subdomainsIncluded == this.subdomainsIncluded &&
+ otherDomain.hostname.equals(this.hostname);
+ }
+}
diff --git a/core/java/android/security/net/config/KeyStoreCertificateSource.java b/core/java/android/security/net/config/KeyStoreCertificateSource.java
new file mode 100644
index 0000000..1973ef1
--- /dev/null
+++ b/core/java/android/security/net/config/KeyStoreCertificateSource.java
@@ -0,0 +1,65 @@
+/*
+ * 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 android.security.net.config;
+
+import android.util.ArraySet;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.Enumeration;
+import java.util.Set;
+
+/**
+ * {@link CertificateSource} which provides certificates from trusted certificate entries of a
+ * {@link KeyStore}.
+ */
+class KeyStoreCertificateSource implements CertificateSource {
+ private final Object mLock = new Object();
+ private final KeyStore mKeyStore;
+ private Set<X509Certificate> mCertificates;
+
+ public KeyStoreCertificateSource(KeyStore ks) {
+ mKeyStore = ks;
+ }
+
+ @Override
+ public Set<X509Certificate> getCertificates() {
+ synchronized (mLock) {
+ if (mCertificates != null) {
+ return mCertificates;
+ }
+ try {
+ Set<X509Certificate> certificates = new ArraySet<>(mKeyStore.size());
+ for (Enumeration<String> en = mKeyStore.aliases(); en.hasMoreElements();) {
+ String alias = en.nextElement();
+ if (!mKeyStore.isCertificateEntry(alias)) {
+ continue;
+ }
+ X509Certificate cert = (X509Certificate) mKeyStore.getCertificate(alias);
+ if (cert != null) {
+ certificates.add(cert);
+ }
+ }
+ mCertificates = certificates;
+ return mCertificates;
+ } catch (KeyStoreException e) {
+ throw new RuntimeException("Failed to load certificates from KeyStore", e);
+ }
+ }
+ }
+}
diff --git a/core/java/android/security/net/config/KeyStoreConfigSource.java b/core/java/android/security/net/config/KeyStoreConfigSource.java
new file mode 100644
index 0000000..8d4f098
--- /dev/null
+++ b/core/java/android/security/net/config/KeyStoreConfigSource.java
@@ -0,0 +1,49 @@
+/*
+ * 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 android.security.net.config;
+
+import android.util.Pair;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.util.Set;
+
+/**
+ * {@link ConfigSource} with a single default config based on a {@link KeyStore} and no per domain
+ * configs.
+ */
+class KeyStoreConfigSource implements ConfigSource {
+ private final NetworkSecurityConfig mConfig;
+
+ public KeyStoreConfigSource(KeyStore ks) {
+ mConfig = new NetworkSecurityConfig.Builder()
+ .addCertificatesEntryRef(
+ // Use the KeyStore and do not override pins (of which there are none).
+ new CertificatesEntryRef(new KeyStoreCertificateSource(ks), false))
+ .build();
+ }
+
+ @Override
+ public Set<Pair<Domain, NetworkSecurityConfig>> getPerDomainConfigs() {
+ return null;
+ }
+
+ @Override
+ public NetworkSecurityConfig getDefaultConfig() {
+ return mConfig;
+ }
+}
+
diff --git a/core/java/android/security/net/config/NetworkSecurityConfig.java b/core/java/android/security/net/config/NetworkSecurityConfig.java
new file mode 100644
index 0000000..503854e
--- /dev/null
+++ b/core/java/android/security/net/config/NetworkSecurityConfig.java
@@ -0,0 +1,246 @@
+/*
+ * 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 android.security.net.config;
+
+import android.util.ArraySet;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import javax.net.ssl.X509TrustManager;
+
+/**
+ * @hide
+ */
+public final class NetworkSecurityConfig {
+ /** @hide */
+ public static final boolean DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED = true;
+ /** @hide */
+ public static final boolean DEFAULT_HSTS_ENFORCED = false;
+ public static final NetworkSecurityConfig DEFAULT = getDefaultBuilder().build();
+
+ private final boolean mCleartextTrafficPermitted;
+ private final boolean mHstsEnforced;
+ private final PinSet mPins;
+ private final List<CertificatesEntryRef> mCertificatesEntryRefs;
+ private Set<TrustAnchor> mAnchors;
+ private final Object mAnchorsLock = new Object();
+ private X509TrustManager mTrustManager;
+ private final Object mTrustManagerLock = new Object();
+
+ private NetworkSecurityConfig(boolean cleartextTrafficPermitted, boolean hstsEnforced,
+ PinSet pins, List<CertificatesEntryRef> certificatesEntryRefs) {
+ mCleartextTrafficPermitted = cleartextTrafficPermitted;
+ mHstsEnforced = hstsEnforced;
+ mPins = pins;
+ mCertificatesEntryRefs = certificatesEntryRefs;
+ }
+
+ public Set<TrustAnchor> getTrustAnchors() {
+ synchronized (mAnchorsLock) {
+ if (mAnchors != null) {
+ return mAnchors;
+ }
+ Set<TrustAnchor> anchors = new ArraySet<TrustAnchor>();
+ for (CertificatesEntryRef ref : mCertificatesEntryRefs) {
+ anchors.addAll(ref.getTrustAnchors());
+ }
+ mAnchors = anchors;
+ return anchors;
+ }
+ }
+
+ public boolean isCleartextTrafficPermitted() {
+ return mCleartextTrafficPermitted;
+ }
+
+ public boolean isHstsEnforced() {
+ return mHstsEnforced;
+ }
+
+ public PinSet getPins() {
+ return mPins;
+ }
+
+ public X509TrustManager getTrustManager() {
+ synchronized(mTrustManagerLock) {
+ if (mTrustManager == null) {
+ mTrustManager = new NetworkSecurityTrustManager(this);
+ }
+ return mTrustManager;
+ }
+ }
+
+ void onTrustStoreChange() {
+ synchronized (mAnchorsLock) {
+ mAnchors = null;
+ }
+ }
+
+ /**
+ * Return a {@link Builder} for the default {@code NetworkSecurityConfig}.
+ *
+ * <p>
+ * The default configuration has the following properties:
+ * <ol>
+ * <li>Cleartext traffic is permitted.</li>
+ * <li>HSTS is not enforced.</li>
+ * <li>No certificate pinning is used.</li>
+ * <li>The system and user added trusted certificate stores are trusted for connections.</li>
+ * </ol>
+ *
+ * @hide
+ */
+ public static final Builder getDefaultBuilder() {
+ return new Builder()
+ .setCleartextTrafficPermitted(DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED)
+ .setHstsEnforced(DEFAULT_HSTS_ENFORCED)
+ // System certificate store, does not bypass static pins.
+ .addCertificatesEntryRef(
+ new CertificatesEntryRef(SystemCertificateSource.getInstance(), false))
+ // User certificate store, does not bypass static pins.
+ .addCertificatesEntryRef(
+ new CertificatesEntryRef(UserCertificateSource.getInstance(), false));
+ }
+
+ /**
+ * Builder for creating {@code NetworkSecurityConfig} objects.
+ * @hide
+ */
+ public static final class Builder {
+ private List<CertificatesEntryRef> mCertificatesEntryRefs;
+ private PinSet mPinSet;
+ private boolean mCleartextTrafficPermitted = DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED;
+ private boolean mHstsEnforced = DEFAULT_HSTS_ENFORCED;
+ private boolean mCleartextTrafficPermittedSet = false;
+ private boolean mHstsEnforcedSet = false;
+ private Builder mParentBuilder;
+
+ /**
+ * Sets the parent {@code Builder} for this {@code Builder}.
+ * The parent will be used to determine values not configured in this {@code Builder}
+ * in {@link Builder#build()}, recursively if needed.
+ */
+ public Builder setParent(Builder parent) {
+ // Sanity check to avoid adding loops.
+ Builder current = parent;
+ while (current != null) {
+ if (current == this) {
+ throw new IllegalArgumentException("Loops are not allowed in Builder parents");
+ }
+ current = current.getParent();
+ }
+ mParentBuilder = parent;
+ return this;
+ }
+
+ public Builder getParent() {
+ return mParentBuilder;
+ }
+
+ public Builder setPinSet(PinSet pinSet) {
+ mPinSet = pinSet;
+ return this;
+ }
+
+ private PinSet getEffectivePinSet() {
+ if (mPinSet != null) {
+ return mPinSet;
+ }
+ if (mParentBuilder != null) {
+ return mParentBuilder.getEffectivePinSet();
+ }
+ return PinSet.EMPTY_PINSET;
+ }
+
+ public Builder setCleartextTrafficPermitted(boolean cleartextTrafficPermitted) {
+ mCleartextTrafficPermitted = cleartextTrafficPermitted;
+ mCleartextTrafficPermittedSet = true;
+ return this;
+ }
+
+ private boolean getEffectiveCleartextTrafficPermitted() {
+ if (mCleartextTrafficPermittedSet) {
+ return mCleartextTrafficPermitted;
+ }
+ if (mParentBuilder != null) {
+ return mParentBuilder.getEffectiveCleartextTrafficPermitted();
+ }
+ return DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED;
+ }
+
+ public Builder setHstsEnforced(boolean hstsEnforced) {
+ mHstsEnforced = hstsEnforced;
+ mHstsEnforcedSet = true;
+ return this;
+ }
+
+ private boolean getEffectiveHstsEnforced() {
+ if (mHstsEnforcedSet) {
+ return mHstsEnforced;
+ }
+ if (mParentBuilder != null) {
+ return mParentBuilder.getEffectiveHstsEnforced();
+ }
+ return DEFAULT_HSTS_ENFORCED;
+ }
+
+ public Builder addCertificatesEntryRef(CertificatesEntryRef ref) {
+ if (mCertificatesEntryRefs == null) {
+ mCertificatesEntryRefs = new ArrayList<CertificatesEntryRef>();
+ }
+ mCertificatesEntryRefs.add(ref);
+ return this;
+ }
+
+ public Builder addCertificatesEntryRefs(Collection<? extends CertificatesEntryRef> refs) {
+ if (mCertificatesEntryRefs == null) {
+ mCertificatesEntryRefs = new ArrayList<CertificatesEntryRef>();
+ }
+ mCertificatesEntryRefs.addAll(refs);
+ return this;
+ }
+
+ private List<CertificatesEntryRef> getEffectiveCertificatesEntryRefs() {
+ if (mCertificatesEntryRefs != null) {
+ return mCertificatesEntryRefs;
+ }
+ if (mParentBuilder != null) {
+ return mParentBuilder.getEffectiveCertificatesEntryRefs();
+ }
+ return Collections.<CertificatesEntryRef>emptyList();
+ }
+
+ public boolean hasCertificatesEntryRefs() {
+ return mCertificatesEntryRefs != null;
+ }
+
+ List<CertificatesEntryRef> getCertificatesEntryRefs() {
+ return mCertificatesEntryRefs;
+ }
+
+ public NetworkSecurityConfig build() {
+ boolean cleartextPermitted = getEffectiveCleartextTrafficPermitted();
+ boolean hstsEnforced = getEffectiveHstsEnforced();
+ PinSet pinSet = getEffectivePinSet();
+ List<CertificatesEntryRef> entryRefs = getEffectiveCertificatesEntryRefs();
+ return new NetworkSecurityConfig(cleartextPermitted, hstsEnforced, pinSet, entryRefs);
+ }
+ }
+}
diff --git a/core/java/android/security/net/config/NetworkSecurityConfigProvider.java b/core/java/android/security/net/config/NetworkSecurityConfigProvider.java
new file mode 100644
index 0000000..ca8cdae
--- /dev/null
+++ b/core/java/android/security/net/config/NetworkSecurityConfigProvider.java
@@ -0,0 +1,33 @@
+/*
+ * 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 android.security.net.config;
+
+import java.security.Provider;
+
+/** @hide */
+public final class NetworkSecurityConfigProvider extends Provider {
+
+ private static String PREFIX =
+ NetworkSecurityConfigProvider.class.getPackage().getName() + ".";
+
+ public NetworkSecurityConfigProvider() {
+ // TODO: More clever name than this
+ super("AndroidNSSP", 1.0, "Android Network Security Policy Provider");
+ put("TrustManagerFactory.PKIX", PREFIX + "RootTrustManagerFactorySpi");
+ put("Alg.Alias.TrustManagerFactory.X509", "PKIX");
+ }
+}
diff --git a/core/java/android/security/net/config/NetworkSecurityTrustManager.java b/core/java/android/security/net/config/NetworkSecurityTrustManager.java
new file mode 100644
index 0000000..e69082d
--- /dev/null
+++ b/core/java/android/security/net/config/NetworkSecurityTrustManager.java
@@ -0,0 +1,135 @@
+/*
+ * 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 android.security.net.config;
+
+import com.android.org.conscrypt.TrustManagerImpl;
+
+import android.util.ArrayMap;
+import java.io.IOException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.MessageDigest;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.net.ssl.X509TrustManager;
+
+/**
+ * {@link X509TrustManager} that implements the trust anchor and pinning for a
+ * given {@link NetworkSecurityConfig}.
+ * @hide
+ */
+public class NetworkSecurityTrustManager implements X509TrustManager {
+ // TODO: Replace this with a general X509TrustManager and use duck-typing.
+ private final TrustManagerImpl mDelegate;
+ private final NetworkSecurityConfig mNetworkSecurityConfig;
+
+ public NetworkSecurityTrustManager(NetworkSecurityConfig config) {
+ if (config == null) {
+ throw new NullPointerException("config must not be null");
+ }
+ mNetworkSecurityConfig = config;
+ // TODO: Create our own better KeyStoreImpl
+ try {
+ KeyStore store = KeyStore.getInstance(KeyStore.getDefaultType());
+ store.load(null);
+ int certNum = 0;
+ for (TrustAnchor anchor : mNetworkSecurityConfig.getTrustAnchors()) {
+ store.setEntry(String.valueOf(certNum++),
+ new KeyStore.TrustedCertificateEntry(anchor.certificate),
+ null);
+ }
+ mDelegate = new TrustManagerImpl(store);
+ } catch (GeneralSecurityException | IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void checkClientTrusted(X509Certificate[] chain, String authType)
+ throws CertificateException {
+ throw new CertificateException("Client authentication not supported");
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] certs, String authType)
+ throws CertificateException {
+ List<X509Certificate> trustedChain =
+ mDelegate.checkServerTrusted(certs, authType, (String) null);
+ checkPins(trustedChain);
+ }
+
+ private void checkPins(List<X509Certificate> chain) throws CertificateException {
+ PinSet pinSet = mNetworkSecurityConfig.getPins();
+ if (pinSet.pins.isEmpty()
+ || System.currentTimeMillis() > pinSet.expirationTime
+ || !isPinningEnforced(chain)) {
+ return;
+ }
+ Set<String> pinAlgorithms = pinSet.getPinAlgorithms();
+ Map<String, MessageDigest> digestMap = new ArrayMap<String, MessageDigest>(
+ pinAlgorithms.size());
+ for (int i = chain.size() - 1; i >= 0 ; i--) {
+ X509Certificate cert = chain.get(i);
+ byte[] encodedSPKI = cert.getPublicKey().getEncoded();
+ for (String algorithm : pinAlgorithms) {
+ MessageDigest md = digestMap.get(algorithm);
+ if (md == null) {
+ try {
+ md = MessageDigest.getInstance(algorithm);
+ } catch (GeneralSecurityException e) {
+ throw new RuntimeException(e);
+ }
+ digestMap.put(algorithm, md);
+ }
+ if (pinSet.pins.contains(new Pin(algorithm, md.digest(encodedSPKI)))) {
+ return;
+ }
+ }
+ }
+
+ // TODO: Throw a subclass of CertificateException which indicates a pinning failure.
+ throw new CertificateException("Pin verification failed");
+ }
+
+ private boolean isPinningEnforced(List<X509Certificate> chain) throws CertificateException {
+ if (chain.isEmpty()) {
+ return false;
+ }
+ X509Certificate anchorCert = chain.get(chain.size() - 1);
+ TrustAnchor chainAnchor = null;
+ // TODO: faster lookup
+ for (TrustAnchor anchor : mNetworkSecurityConfig.getTrustAnchors()) {
+ if (anchor.certificate.equals(anchorCert)) {
+ chainAnchor = anchor;
+ break;
+ }
+ }
+ if (chainAnchor == null) {
+ throw new CertificateException("Trusted chain does not end in a TrustAnchor");
+ }
+ return !chainAnchor.overridesPins;
+ }
+
+ @Override
+ public X509Certificate[] getAcceptedIssuers() {
+ return new X509Certificate[0];
+ }
+}
diff --git a/core/java/android/security/net/config/Pin.java b/core/java/android/security/net/config/Pin.java
new file mode 100644
index 0000000..94520e2
--- /dev/null
+++ b/core/java/android/security/net/config/Pin.java
@@ -0,0 +1,78 @@
+/*
+ * 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 android.security.net.config;
+
+import java.util.Arrays;
+
+/** @hide */
+public final class Pin {
+ public final String digestAlgorithm;
+ public final byte[] digest;
+
+ private final int mHashCode;
+
+ public Pin(String digestAlgorithm, byte[] digest) {
+ this.digestAlgorithm = digestAlgorithm;
+ this.digest = digest;
+ mHashCode = Arrays.hashCode(digest) ^ digestAlgorithm.hashCode();
+ }
+
+ /**
+ * @hide
+ */
+ public static boolean isSupportedDigestAlgorithm(String algorithm) {
+ // Currently only SHA-256 is supported. SHA-512 if/once Chromium networking stack
+ // supports it.
+ return "SHA-256".equalsIgnoreCase(algorithm);
+ }
+
+ /**
+ * @hide
+ */
+ public static int getDigestLength(String algorithm) {
+ if ("SHA-256".equalsIgnoreCase(algorithm)) {
+ return 32;
+ }
+ throw new IllegalArgumentException("Unsupported digest algorithm: " + algorithm);
+ }
+
+ @Override
+ public int hashCode() {
+ return mHashCode;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof Pin)) {
+ return false;
+ }
+ Pin other = (Pin) obj;
+ if (other.hashCode() != mHashCode) {
+ return false;
+ }
+ if (!Arrays.equals(digest, other.digest)) {
+ return false;
+ }
+ if (!digestAlgorithm.equals(other.digestAlgorithm)) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/core/java/android/security/net/config/PinSet.java b/core/java/android/security/net/config/PinSet.java
new file mode 100644
index 0000000..d3c975e
--- /dev/null
+++ b/core/java/android/security/net/config/PinSet.java
@@ -0,0 +1,46 @@
+/*
+ * 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 android.security.net.config;
+
+import android.util.ArraySet;
+import java.util.Collections;
+import java.util.Set;
+
+/** @hide */
+public final class PinSet {
+ public static final PinSet EMPTY_PINSET =
+ new PinSet(Collections.<Pin>emptySet(), Long.MAX_VALUE);
+ public final long expirationTime;
+ public final Set<Pin> pins;
+
+ public PinSet(Set<Pin> pins, long expirationTime) {
+ if (pins == null) {
+ throw new NullPointerException("pins must not be null");
+ }
+ this.pins = pins;
+ this.expirationTime = expirationTime;
+ }
+
+ Set<String> getPinAlgorithms() {
+ // TODO: Cache this.
+ Set<String> algorithms = new ArraySet<String>();
+ for (Pin pin : pins) {
+ algorithms.add(pin.digestAlgorithm);
+ }
+ return algorithms;
+ }
+}
diff --git a/core/java/android/security/net/config/ResourceCertificateSource.java b/core/java/android/security/net/config/ResourceCertificateSource.java
new file mode 100644
index 0000000..06dd9d4
--- /dev/null
+++ b/core/java/android/security/net/config/ResourceCertificateSource.java
@@ -0,0 +1,72 @@
+/*
+ * 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 android.security.net.config;
+
+import android.content.Context;
+import android.util.ArraySet;
+import libcore.io.IoUtils;
+import java.io.InputStream;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * {@link CertificateSource} based on certificates contained in an application resource file.
+ * @hide
+ */
+public class ResourceCertificateSource implements CertificateSource {
+ private Set<X509Certificate> mCertificates;
+ private final int mResourceId;
+ private Context mContext;
+ private final Object mLock = new Object();
+
+ public ResourceCertificateSource(int resourceId, Context context) {
+ mResourceId = resourceId;
+ mContext = context.getApplicationContext();
+ }
+
+ @Override
+ public Set<X509Certificate> getCertificates() {
+ synchronized (mLock) {
+ if (mCertificates != null) {
+ return mCertificates;
+ }
+ Set<X509Certificate> certificates = new ArraySet<X509Certificate>();
+ Collection<? extends Certificate> certs;
+ InputStream in = null;
+ try {
+ CertificateFactory factory = CertificateFactory.getInstance("X.509");
+ in = mContext.getResources().openRawResource(mResourceId);
+ certs = factory.generateCertificates(in);
+ } catch (CertificateException e) {
+ throw new RuntimeException("Failed to load trust anchors from id " + mResourceId,
+ e);
+ } finally {
+ IoUtils.closeQuietly(in);
+ }
+ for (Certificate cert : certs) {
+ certificates.add((X509Certificate) cert);
+ }
+ mCertificates = certificates;
+ mContext = null;
+ return mCertificates;
+ }
+ }
+}
diff --git a/core/java/android/security/net/config/RootTrustManager.java b/core/java/android/security/net/config/RootTrustManager.java
new file mode 100644
index 0000000..1338b9f
--- /dev/null
+++ b/core/java/android/security/net/config/RootTrustManager.java
@@ -0,0 +1,74 @@
+/*
+ * 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 android.security.net.config;
+
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.X509TrustManager;
+
+/**
+ * {@link X509TrustManager} 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
+ * {@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 {
+ private final ApplicationConfig mConfig;
+ private static final X509Certificate[] EMPTY_ISSUERS = new X509Certificate[0];
+
+ public RootTrustManager(ApplicationConfig config) {
+ if (config == null) {
+ throw new NullPointerException("config must not be null");
+ }
+ mConfig = config;
+ }
+
+ @Override
+ public void checkClientTrusted(X509Certificate[] chain, String authType)
+ throws CertificateException {
+ throw new CertificateException("Client authentication not supported");
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] certs, String authType)
+ throws CertificateException {
+ if (mConfig.hasPerDomainConfigs()) {
+ throw new CertificateException(
+ "Domain specific configurations require that hostname aware"
+ + " checkServerTrusted(X509Certificate[], String, String) is used");
+ }
+ NetworkSecurityConfig config = mConfig.getConfigForHostname("");
+ config.getTrustManager().checkServerTrusted(certs, authType);
+ }
+
+ public void checkServerTrusted(X509Certificate[] certs, String authType, String hostname)
+ throws CertificateException {
+ NetworkSecurityConfig config = mConfig.getConfigForHostname(hostname);
+ config.getTrustManager().checkServerTrusted(certs, authType);
+ }
+
+ @Override
+ public X509Certificate[] getAcceptedIssuers() {
+ return EMPTY_ISSUERS;
+ }
+}
diff --git a/core/java/android/security/net/config/RootTrustManagerFactorySpi.java b/core/java/android/security/net/config/RootTrustManagerFactorySpi.java
new file mode 100644
index 0000000..0a1fe88
--- /dev/null
+++ b/core/java/android/security/net/config/RootTrustManagerFactorySpi.java
@@ -0,0 +1,75 @@
+/*
+ * 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 android.security.net.config;
+
+import android.util.Pair;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidParameterException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.Provider;
+import java.security.Security;
+import java.util.Set;
+import javax.net.ssl.ManagerFactoryParameters;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.TrustManagerFactorySpi;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/** @hide */
+public class RootTrustManagerFactorySpi extends TrustManagerFactorySpi {
+ private ApplicationConfig mApplicationConfig;
+ private NetworkSecurityConfig mConfig;
+
+ @Override
+ public void engineInit(ManagerFactoryParameters spec)
+ throws InvalidAlgorithmParameterException {
+ if (!(spec instanceof ApplicationConfigParameters)) {
+ throw new InvalidAlgorithmParameterException("Unsupported spec: " + spec + ". Only "
+ + ApplicationConfigParameters.class.getName() + " supported");
+
+ }
+ mApplicationConfig = ((ApplicationConfigParameters) spec).config;
+ }
+
+ @Override
+ public void engineInit(KeyStore ks) throws KeyStoreException {
+ if (ks != null) {
+ mApplicationConfig = new ApplicationConfig(new KeyStoreConfigSource(ks));
+ } else {
+ mApplicationConfig = ApplicationConfig.getDefaultInstance();
+ }
+ }
+
+ @Override
+ public TrustManager[] engineGetTrustManagers() {
+ if (mApplicationConfig == null) {
+ throw new IllegalStateException("TrustManagerFactory not initialized");
+ }
+ return new TrustManager[] { mApplicationConfig.getTrustManager() };
+ }
+
+ @VisibleForTesting
+ public static final class ApplicationConfigParameters implements ManagerFactoryParameters {
+ public final ApplicationConfig config;
+ public ApplicationConfigParameters(ApplicationConfig config) {
+ this.config = config;
+ }
+ }
+}
diff --git a/core/java/android/security/net/config/SystemCertificateSource.java b/core/java/android/security/net/config/SystemCertificateSource.java
new file mode 100644
index 0000000..7649a97
--- /dev/null
+++ b/core/java/android/security/net/config/SystemCertificateSource.java
@@ -0,0 +1,101 @@
+/*
+ * 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 android.security.net.config;
+
+import android.os.Environment;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Set;
+import libcore.io.IoUtils;
+
+/**
+ * {@link CertificateSource} based on the system trusted CA store.
+ * @hide
+ */
+public class SystemCertificateSource implements CertificateSource {
+ private static final SystemCertificateSource INSTANCE = new SystemCertificateSource();
+ private Set<X509Certificate> mSystemCerts = null;
+ private final Object mLock = new Object();
+
+ private SystemCertificateSource() {
+ }
+
+ public static SystemCertificateSource getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public Set<X509Certificate> getCertificates() {
+ // TODO: loading all of these is wasteful, we should instead use a keystore style API.
+ synchronized (mLock) {
+ if (mSystemCerts != null) {
+ return mSystemCerts;
+ }
+ CertificateFactory certFactory;
+ try {
+ certFactory = CertificateFactory.getInstance("X.509");
+ } catch (CertificateException e) {
+ throw new RuntimeException("Failed to obtain X.509 CertificateFactory", e);
+ }
+
+ final String ANDROID_ROOT = System.getenv("ANDROID_ROOT");
+ final File systemCaDir = new File(ANDROID_ROOT + "/etc/security/cacerts");
+ final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
+ final File userRemovedCaDir = new File(configDir, "cacerts-removed");
+ // Sanity check
+ if (!systemCaDir.isDirectory()) {
+ throw new AssertionError(systemCaDir + " is not a directory");
+ }
+
+ Set<X509Certificate> systemCerts = new ArraySet<X509Certificate>();
+ for (String caFile : systemCaDir.list()) {
+ // Skip any CAs in the user's deleted directory.
+ if (new File(userRemovedCaDir, caFile).exists()) {
+ continue;
+ }
+ InputStream is = null;
+ try {
+ is = new BufferedInputStream(
+ new FileInputStream(new File(systemCaDir, caFile)));
+ systemCerts.add((X509Certificate) certFactory.generateCertificate(is));
+ } catch (CertificateException | IOException e) {
+ // Don't rethrow to be consistent with conscrypt's cert loading code.
+ continue;
+ } finally {
+ IoUtils.closeQuietly(is);
+ }
+ }
+ mSystemCerts = systemCerts;
+ return mSystemCerts;
+ }
+ }
+
+ public void onCertificateStorageChange() {
+ synchronized (mLock) {
+ mSystemCerts = null;
+ }
+ }
+}
diff --git a/core/java/android/security/net/config/TrustAnchor.java b/core/java/android/security/net/config/TrustAnchor.java
new file mode 100644
index 0000000..b62d85f
--- /dev/null
+++ b/core/java/android/security/net/config/TrustAnchor.java
@@ -0,0 +1,33 @@
+/*
+ * 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 android.security.net.config;
+
+import java.security.cert.X509Certificate;
+
+/** @hide */
+public final class TrustAnchor {
+ public final X509Certificate certificate;
+ public final boolean overridesPins;
+
+ public TrustAnchor(X509Certificate certificate, boolean overridesPins) {
+ if (certificate == null) {
+ throw new NullPointerException("certificate");
+ }
+ this.certificate = certificate;
+ this.overridesPins = overridesPins;
+ }
+}
diff --git a/core/java/android/security/net/config/UserCertificateSource.java b/core/java/android/security/net/config/UserCertificateSource.java
new file mode 100644
index 0000000..e9d5aa1
--- /dev/null
+++ b/core/java/android/security/net/config/UserCertificateSource.java
@@ -0,0 +1,92 @@
+/*
+ * 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 android.security.net.config;
+
+import android.os.Environment;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Set;
+import libcore.io.IoUtils;
+
+/**
+ * {@link CertificateSource} based on the user-installed trusted CA store.
+ * @hide
+ */
+public class UserCertificateSource implements CertificateSource {
+ private static final UserCertificateSource INSTANCE = new UserCertificateSource();
+ private Set<X509Certificate> mUserCerts = null;
+ private final Object mLock = new Object();
+
+ private UserCertificateSource() {
+ }
+
+ public static UserCertificateSource getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public Set<X509Certificate> getCertificates() {
+ // TODO: loading all of these is wasteful, we should instead use a keystore style API.
+ synchronized (mLock) {
+ if (mUserCerts != null) {
+ return mUserCerts;
+ }
+ CertificateFactory certFactory;
+ try {
+ certFactory = CertificateFactory.getInstance("X.509");
+ } catch (CertificateException e) {
+ throw new RuntimeException("Failed to obtain X.509 CertificateFactory", e);
+ }
+ final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
+ final File userCaDir = new File(configDir, "cacerts-added");
+ Set<X509Certificate> userCerts = new ArraySet<X509Certificate>();
+ // If the user hasn't added any certificates the directory may not exist.
+ if (userCaDir.isDirectory()) {
+ for (String caFile : userCaDir.list()) {
+ InputStream is = null;
+ try {
+ is = new BufferedInputStream(
+ new FileInputStream(new File(userCaDir, caFile)));
+ userCerts.add((X509Certificate) certFactory.generateCertificate(is));
+ } catch (CertificateException | IOException e) {
+ // Don't rethrow to be consistent with conscrypt's cert loading code.
+ continue;
+ } finally {
+ IoUtils.closeQuietly(is);
+ }
+ }
+ }
+ mUserCerts = userCerts;
+ return mUserCerts;
+ }
+ }
+
+ public void onCertificateStorageChange() {
+ synchronized (mLock) {
+ mUserCerts = null;
+ }
+ }
+}
diff --git a/core/java/android/security/net/config/XmlConfigSource.java b/core/java/android/security/net/config/XmlConfigSource.java
new file mode 100644
index 0000000..1706e95
--- /dev/null
+++ b/core/java/android/security/net/config/XmlConfigSource.java
@@ -0,0 +1,387 @@
+package android.security.net.config;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.util.ArraySet;
+import android.util.Base64;
+import android.util.Pair;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+
+/**
+ * {@link ConfigSource} based on an XML configuration file.
+ *
+ * @hide
+ */
+public class XmlConfigSource implements ConfigSource {
+ private static final int CONFIG_BASE = 0;
+ private static final int CONFIG_DOMAIN = 1;
+ private static final int CONFIG_DEBUG = 2;
+
+ private final Object mLock = new Object();
+ private final int mResourceId;
+ private final boolean mDebugBuild;
+
+ private boolean mInitialized;
+ private NetworkSecurityConfig mDefaultConfig;
+ private Set<Pair<Domain, NetworkSecurityConfig>> mDomainMap;
+ private Context mContext;
+
+ public XmlConfigSource(Context context, int resourceId) {
+ this(context, resourceId, false);
+ }
+
+ public XmlConfigSource(Context context, int resourceId, boolean debugBuild) {
+ mResourceId = resourceId;
+ mContext = context;
+ mDebugBuild = debugBuild;
+ }
+
+ public Set<Pair<Domain, NetworkSecurityConfig>> getPerDomainConfigs() {
+ ensureInitialized();
+ return mDomainMap;
+ }
+
+ public NetworkSecurityConfig getDefaultConfig() {
+ ensureInitialized();
+ return mDefaultConfig;
+ }
+
+ private static final String getConfigString(int configType) {
+ switch (configType) {
+ case CONFIG_BASE:
+ return "base-config";
+ case CONFIG_DOMAIN:
+ return "domain-config";
+ case CONFIG_DEBUG:
+ return "debug-overrides";
+ default:
+ throw new IllegalArgumentException("Unknown config type: " + configType);
+ }
+ }
+
+ private void ensureInitialized() {
+ synchronized (mLock) {
+ if (mInitialized) {
+ return;
+ }
+ try (XmlResourceParser parser = mContext.getResources().getXml(mResourceId)) {
+ parseNetworkSecurityConfig(parser);
+ mContext = null;
+ mInitialized = true;
+ } catch (Resources.NotFoundException | XmlPullParserException | IOException
+ | ParserException e) {
+ throw new RuntimeException("Failed to parse XML configuration from "
+ + mContext.getResources().getResourceEntryName(mResourceId), e);
+ }
+ }
+ }
+
+ private Pin parsePin(XmlResourceParser parser)
+ throws IOException, XmlPullParserException, ParserException {
+ String digestAlgorithm = parser.getAttributeValue(null, "digest");
+ if (!Pin.isSupportedDigestAlgorithm(digestAlgorithm)) {
+ throw new ParserException(parser, "Unsupported pin digest algorithm: "
+ + digestAlgorithm);
+ }
+ if (parser.next() != XmlPullParser.TEXT) {
+ throw new ParserException(parser, "Missing pin digest");
+ }
+ String digest = parser.getText();
+ byte[] decodedDigest = null;
+ try {
+ decodedDigest = Base64.decode(digest, 0);
+ } catch (IllegalArgumentException e) {
+ throw new ParserException(parser, "Invalid pin digest", e);
+ }
+ int expectedLength = Pin.getDigestLength(digestAlgorithm);
+ if (decodedDigest.length != expectedLength) {
+ throw new ParserException(parser, "digest length " + decodedDigest.length
+ + " does not match expected length for " + digestAlgorithm + " of "
+ + expectedLength);
+ }
+ if (parser.next() != XmlPullParser.END_TAG) {
+ throw new ParserException(parser, "pin contains additional elements");
+ }
+ return new Pin(digestAlgorithm, decodedDigest);
+ }
+
+ private PinSet parsePinSet(XmlResourceParser parser)
+ throws IOException, XmlPullParserException, ParserException {
+ String expirationDate = parser.getAttributeValue(null, "expiration");
+ long expirationTimestampMilis = Long.MAX_VALUE;
+ if (expirationDate != null) {
+ try {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+ sdf.setLenient(false);
+ Date date = sdf.parse(expirationDate);
+ if (date == null) {
+ throw new ParserException(parser, "Invalid expiration date in pin-set");
+ }
+ expirationTimestampMilis = date.getTime();
+ } catch (ParseException e) {
+ throw new ParserException(parser, "Invalid expiration date in pin-set", e);
+ }
+ }
+
+ int outerDepth = parser.getDepth();
+ Set<Pin> pins = new ArraySet<>();
+ while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+ String tagName = parser.getName();
+ if (tagName.equals("pin")) {
+ pins.add(parsePin(parser));
+ } else {
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ return new PinSet(pins, expirationTimestampMilis);
+ }
+
+ private Domain parseDomain(XmlResourceParser parser, Set<String> seenDomains)
+ throws IOException, XmlPullParserException, ParserException {
+ boolean includeSubdomains =
+ parser.getAttributeBooleanValue(null, "includeSubdomains", false);
+ if (parser.next() != XmlPullParser.TEXT) {
+ throw new ParserException(parser, "Domain name missing");
+ }
+ String domain = parser.getText().toLowerCase(Locale.US);
+ if (parser.next() != XmlPullParser.END_TAG) {
+ throw new ParserException(parser, "domain contains additional elements");
+ }
+ // Domains are matched using a most specific match, so don't allow duplicates.
+ // includeSubdomains isn't relevant here, both android.com + subdomains and android.com
+ // match for android.com equally. Do not allow any duplicates period.
+ if (!seenDomains.add(domain)) {
+ throw new ParserException(parser, domain + " has already been specified");
+ }
+ return new Domain(domain, includeSubdomains);
+ }
+
+ private CertificatesEntryRef parseCertificatesEntry(XmlResourceParser parser,
+ boolean defaultOverridePins)
+ throws IOException, XmlPullParserException, ParserException {
+ boolean overridePins =
+ parser.getAttributeBooleanValue(null, "overridePins", defaultOverridePins);
+ int sourceId = parser.getAttributeResourceValue(null, "src", -1);
+ String sourceString = parser.getAttributeValue(null, "src");
+ CertificateSource source = null;
+ if (sourceString == null) {
+ throw new ParserException(parser, "certificates element missing src attribute");
+ }
+ if (sourceId != -1) {
+ // TODO: Cache ResourceCertificateSources by sourceId
+ source = new ResourceCertificateSource(sourceId, mContext);
+ } else if ("system".equals(sourceString)) {
+ source = SystemCertificateSource.getInstance();
+ } else if ("user".equals(sourceString)) {
+ source = UserCertificateSource.getInstance();
+ } else {
+ throw new ParserException(parser, "Unknown certificates src. "
+ + "Should be one of system|user|@resourceVal");
+ }
+ XmlUtils.skipCurrentTag(parser);
+ return new CertificatesEntryRef(source, overridePins);
+ }
+
+ private Collection<CertificatesEntryRef> parseTrustAnchors(XmlResourceParser parser,
+ boolean defaultOverridePins)
+ throws IOException, XmlPullParserException, ParserException {
+ int outerDepth = parser.getDepth();
+ List<CertificatesEntryRef> anchors = new ArrayList<>();
+ while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+ String tagName = parser.getName();
+ if (tagName.equals("certificates")) {
+ anchors.add(parseCertificatesEntry(parser, defaultOverridePins));
+ } else {
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ return anchors;
+ }
+
+ private List<Pair<NetworkSecurityConfig.Builder, Set<Domain>>> parseConfigEntry(
+ XmlResourceParser parser, Set<String> seenDomains,
+ NetworkSecurityConfig.Builder parentBuilder, int configType)
+ throws IOException, XmlPullParserException, ParserException {
+ List<Pair<NetworkSecurityConfig.Builder, Set<Domain>>> builders = new ArrayList<>();
+ NetworkSecurityConfig.Builder builder = new NetworkSecurityConfig.Builder();
+ builder.setParent(parentBuilder);
+ Set<Domain> domains = new ArraySet<>();
+ boolean seenPinSet = false;
+ boolean seenTrustAnchors = false;
+ boolean defaultOverridePins = configType == CONFIG_DEBUG;
+ String configName = parser.getName();
+ int outerDepth = parser.getDepth();
+ // Add this builder now so that this builder occurs before any of its children. This
+ // makes the final build pass easier.
+ builders.add(new Pair<>(builder, domains));
+ // Parse config attributes. Only set values that are present, config inheritence will
+ // handle the rest.
+ for (int i = 0; i < parser.getAttributeCount(); i++) {
+ String name = parser.getAttributeName(i);
+ if ("hstsEnforced".equals(name)) {
+ builder.setHstsEnforced(
+ parser.getAttributeBooleanValue(i,
+ NetworkSecurityConfig.DEFAULT_HSTS_ENFORCED));
+ } else if ("cleartextTrafficPermitted".equals(name)) {
+ builder.setCleartextTrafficPermitted(
+ parser.getAttributeBooleanValue(i,
+ NetworkSecurityConfig.DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED));
+ }
+ }
+ // Parse the config elements.
+ while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+ String tagName = parser.getName();
+ if ("domain".equals(tagName)) {
+ if (configType != CONFIG_DOMAIN) {
+ throw new ParserException(parser,
+ "domain element not allowed in " + getConfigString(configType));
+ }
+ Domain domain = parseDomain(parser, seenDomains);
+ domains.add(domain);
+ } else if ("trust-anchors".equals(tagName)) {
+ if (seenTrustAnchors) {
+ throw new ParserException(parser,
+ "Multiple trust-anchor elements not allowed");
+ }
+ builder.addCertificatesEntryRefs(
+ parseTrustAnchors(parser, defaultOverridePins));
+ seenTrustAnchors = true;
+ } else if ("pin-set".equals(tagName)) {
+ if (configType != CONFIG_DOMAIN) {
+ throw new ParserException(parser,
+ "pin-set element not allowed in " + getConfigString(configType));
+ }
+ if (seenPinSet) {
+ throw new ParserException(parser, "Multiple pin-set elements not allowed");
+ }
+ builder.setPinSet(parsePinSet(parser));
+ seenPinSet = true;
+ } else if ("domain-config".equals(tagName)) {
+ if (configType != CONFIG_DOMAIN) {
+ throw new ParserException(parser,
+ "Nested domain-config not allowed in " + getConfigString(configType));
+ }
+ builders.addAll(parseConfigEntry(parser, seenDomains, builder, configType));
+ } else {
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ if (configType == CONFIG_DOMAIN && domains.isEmpty()) {
+ throw new ParserException(parser, "No domain elements in domain-config");
+ }
+ return builders;
+ }
+
+ private void addDebugAnchorsIfNeeded(NetworkSecurityConfig.Builder debugConfigBuilder,
+ NetworkSecurityConfig.Builder builder) {
+ if (debugConfigBuilder == null || !debugConfigBuilder.hasCertificatesEntryRefs()) {
+ return;
+ }
+ // Don't add trust anchors if not already present, the builder will inherit the anchors
+ // from its parent, and that's where the trust anchors should be added.
+ if (!builder.hasCertificatesEntryRefs()) {
+ return;
+ }
+
+ builder.addCertificatesEntryRefs(debugConfigBuilder.getCertificatesEntryRefs());
+ }
+
+ private void parseNetworkSecurityConfig(XmlResourceParser parser)
+ throws IOException, XmlPullParserException, ParserException {
+ Set<String> seenDomains = new ArraySet<>();
+ List<Pair<NetworkSecurityConfig.Builder, Set<Domain>>> builders = new ArrayList<>();
+ NetworkSecurityConfig.Builder baseConfigBuilder = null;
+ NetworkSecurityConfig.Builder debugConfigBuilder = null;
+ boolean seenDebugOverrides = false;
+ boolean seenBaseConfig = false;
+
+ XmlUtils.beginDocument(parser, "network-security-config");
+ int outerDepth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+ if ("base-config".equals(parser.getName())) {
+ if (seenBaseConfig) {
+ throw new ParserException(parser, "Only one base-config allowed");
+ }
+ seenBaseConfig = true;
+ baseConfigBuilder =
+ parseConfigEntry(parser, seenDomains, null, CONFIG_BASE).get(0).first;
+ } else if ("domain-config".equals(parser.getName())) {
+ builders.addAll(
+ parseConfigEntry(parser, seenDomains, baseConfigBuilder, CONFIG_DOMAIN));
+ } else if ("debug-overrides".equals(parser.getName())) {
+ if (seenDebugOverrides) {
+ throw new ParserException(parser, "Only one debug-overrides allowed");
+ }
+ if (mDebugBuild) {
+ debugConfigBuilder =
+ parseConfigEntry(parser, seenDomains, null, CONFIG_DEBUG).get(0).first;
+ } else {
+ XmlUtils.skipCurrentTag(parser);
+ }
+ seenDebugOverrides = true;
+ } else {
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+
+ // Use the platform default as the parent of the base config for any values not provided
+ // there. If there is no base config use the platform default.
+ NetworkSecurityConfig.Builder platformDefaultBuilder =
+ NetworkSecurityConfig.getDefaultBuilder();
+ addDebugAnchorsIfNeeded(debugConfigBuilder, platformDefaultBuilder);
+ if (baseConfigBuilder != null) {
+ baseConfigBuilder.setParent(platformDefaultBuilder);
+ addDebugAnchorsIfNeeded(debugConfigBuilder, baseConfigBuilder);
+ } else {
+ baseConfigBuilder = platformDefaultBuilder;
+ }
+ // Build the per-domain config mapping.
+ Set<Pair<Domain, NetworkSecurityConfig>> configs = new ArraySet<>();
+
+ for (Pair<NetworkSecurityConfig.Builder, Set<Domain>> entry : builders) {
+ NetworkSecurityConfig.Builder builder = entry.first;
+ Set<Domain> domains = entry.second;
+ // Set the parent of configs that do not have a parent to the base-config. This can
+ // happen if the base-config comes after a domain-config in the file.
+ // Note that this is safe with regards to children because of the order that
+ // parseConfigEntry returns builders, the parent is always before the children. The
+ // children builders will not have build called until _after_ their parents have their
+ // parent set so everything is consistent.
+ if (builder.getParent() == null) {
+ builder.setParent(baseConfigBuilder);
+ }
+ addDebugAnchorsIfNeeded(debugConfigBuilder, builder);
+ NetworkSecurityConfig config = builder.build();
+ for (Domain domain : domains) {
+ configs.add(new Pair<>(domain, config));
+ }
+ }
+ mDefaultConfig = baseConfigBuilder.build();
+ mDomainMap = configs;
+ }
+
+ public static class ParserException extends Exception {
+
+ public ParserException(XmlPullParser parser, String message, Throwable cause) {
+ super(message + " at: " + parser.getPositionDescription(), cause);
+ }
+
+ public ParserException(XmlPullParser parser, String message) {
+ this(parser, message, null);
+ }
+ }
+}
diff --git a/core/java/android/service/notification/ConditionProviderService.java b/core/java/android/service/notification/ConditionProviderService.java
index c679eda..88bd283 100644
--- a/core/java/android/service/notification/ConditionProviderService.java
+++ b/core/java/android/service/notification/ConditionProviderService.java
@@ -35,7 +35,8 @@
* the {@link android.Manifest.permission#BIND_CONDITION_PROVIDER_SERVICE} permission
* and include an intent filter with the {@link #SERVICE_INTERFACE} action. If you want users to be
* able to create and update conditions for this service to monitor, include the
- * {@link #META_DATA_RULE_TYPE} and {@link #META_DATA_CONFIGURATION_ACTIVITY} tags. For example:</p>
+ * {@link #META_DATA_RULE_TYPE} and {@link #META_DATA_CONFIGURATION_ACTIVITY} tags and request the
+ * {@link android.Manifest.permission#ACCESS_NOTIFICATION_POLICY} permission. For example:</p>
* <pre>
* <service android:name=".MyConditionProvider"
* android:label="@string/service_name"
@@ -167,11 +168,6 @@
}
@Override
- public void onRequestConditions(int relevance) {
- mHandler.obtainMessage(H.ON_REQUEST_CONDITIONS, relevance, 0).sendToTarget();
- }
-
- @Override
public void onSubscribe(Uri conditionId) {
mHandler.obtainMessage(H.ON_SUBSCRIBE, conditionId).sendToTarget();
}
@@ -184,7 +180,6 @@
private final class H extends Handler {
private static final int ON_CONNECTED = 1;
- private static final int ON_REQUEST_CONDITIONS = 2;
private static final int ON_SUBSCRIBE = 3;
private static final int ON_UNSUBSCRIBE = 4;
@@ -197,10 +192,6 @@
name = "onConnected";
onConnected();
break;
- case ON_REQUEST_CONDITIONS:
- name = "onRequestConditions";
- onRequestConditions(msg.arg1);
- break;
case ON_SUBSCRIBE:
name = "onSubscribe";
onSubscribe((Uri)msg.obj);
diff --git a/core/java/android/service/notification/IConditionProvider.aidl b/core/java/android/service/notification/IConditionProvider.aidl
index ada8939..3f3c6b8 100644
--- a/core/java/android/service/notification/IConditionProvider.aidl
+++ b/core/java/android/service/notification/IConditionProvider.aidl
@@ -22,7 +22,6 @@
/** @hide */
oneway interface IConditionProvider {
void onConnected();
- void onRequestConditions(int relevance);
void onSubscribe(in Uri conditionId);
void onUnsubscribe(in Uri conditionId);
}
\ No newline at end of file
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index d424546..7e7b5fc 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -31,6 +31,7 @@
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.graphics.Bitmap;
+import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcel;
@@ -472,9 +473,10 @@
StatusBarNotification sbn = list.get(i);
Notification notification = sbn.getNotification();
try {
- Builder.rebuild(getContext(), notification);
// convert icon metadata to legacy format for older clients
createLegacyIconExtras(notification);
+ // populate remote views for older clients.
+ maybePopulateRemoteViews(notification);
} catch (IllegalArgumentException e) {
if (corruptNotifications == null) {
corruptNotifications = new ArrayList<>(N);
@@ -676,6 +678,18 @@
}
}
+ /**
+ * Populates remote views for pre-N targeting apps.
+ */
+ private void maybePopulateRemoteViews(Notification notification) {
+ if (getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
+ Builder builder = Builder.recoverBuilder(getContext(), notification);
+ notification.contentView = builder.makeContentView();
+ notification.bigContentView = builder.makeBigContentView();
+ notification.headsUpContentView = builder.makeHeadsUpContentView();
+ }
+ }
+
private class INotificationListenerWrapper extends INotificationListener.Stub {
@Override
public void onNotificationPosted(IStatusBarNotificationHolder sbnHolder,
@@ -689,9 +703,10 @@
}
try {
- Notification.Builder.rebuild(getContext(), sbn.getNotification());
+ Notification notification = sbn.getNotification();
// convert icon metadata to legacy format for older clients
createLegacyIconExtras(sbn.getNotification());
+ maybePopulateRemoteViews(sbn.getNotification());
} catch (IllegalArgumentException e) {
// drop corrupt notification
sbn = null;
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index e054a61..82f1b28 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -41,6 +41,7 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
+import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.Objects;
import java.util.UUID;
@@ -68,6 +69,7 @@
public static final int[] MINUTE_BUCKETS = generateMinuteBuckets();
private static final int SECONDS_MS = 1000;
private static final int MINUTES_MS = 60 * SECONDS_MS;
+ private static final int DAY_MINUTES = 24 * 60;
private static final int ZERO_VALUE_MS = 10 * SECONDS_MS;
private static final boolean DEFAULT_ALLOW_CALLS = true;
@@ -561,14 +563,6 @@
return tryParseLong(val, defValue);
}
- public ArraySet<String> getAutomaticRuleNames() {
- final ArraySet<String> rt = new ArraySet<String>();
- for (int i = 0; i < automaticRules.size(); i++) {
- rt.add(automaticRules.valueAt(i).name);
- }
- return rt;
- }
-
@Override
public int describeContents() {
return 0;
@@ -661,40 +655,68 @@
boolean shortVersion) {
final long now = System.currentTimeMillis();
final long millis = minutesFromNow == 0 ? ZERO_VALUE_MS : minutesFromNow * MINUTES_MS;
- return toTimeCondition(context, now + millis, minutesFromNow, now, userHandle,
- shortVersion);
+ return toTimeCondition(context, now + millis, minutesFromNow, userHandle, shortVersion);
}
- public static Condition toTimeCondition(Context context, long time, int minutes, long now,
+ public static Condition toTimeCondition(Context context, long time, int minutes,
int userHandle, boolean shortVersion) {
- final int num, summaryResId, line1ResId;
+ final int num;
+ String summary, line1, line2;
+ final CharSequence formattedTime = getFormattedTime(context, time, userHandle);
+ final Resources res = context.getResources();
if (minutes < 60) {
// display as minutes
num = minutes;
- summaryResId = shortVersion ? R.plurals.zen_mode_duration_minutes_summary_short
+ int summaryResId = shortVersion ? R.plurals.zen_mode_duration_minutes_summary_short
: R.plurals.zen_mode_duration_minutes_summary;
- line1ResId = shortVersion ? R.plurals.zen_mode_duration_minutes_short
+ summary = res.getQuantityString(summaryResId, num, num, formattedTime);
+ int line1ResId = shortVersion ? R.plurals.zen_mode_duration_minutes_short
: R.plurals.zen_mode_duration_minutes;
- } else {
+ line1 = res.getQuantityString(line1ResId, num, num, formattedTime);
+ line2 = res.getString(R.string.zen_mode_until, formattedTime);
+ } else if (minutes < DAY_MINUTES) {
// display as hours
num = Math.round(minutes / 60f);
- summaryResId = shortVersion ? R.plurals.zen_mode_duration_hours_summary_short
+ int summaryResId = shortVersion ? R.plurals.zen_mode_duration_hours_summary_short
: R.plurals.zen_mode_duration_hours_summary;
- line1ResId = shortVersion ? R.plurals.zen_mode_duration_hours_short
+ summary = res.getQuantityString(summaryResId, num, num, formattedTime);
+ int line1ResId = shortVersion ? R.plurals.zen_mode_duration_hours_short
: R.plurals.zen_mode_duration_hours;
+ line1 = res.getQuantityString(line1ResId, num, num, formattedTime);
+ line2 = res.getString(R.string.zen_mode_until, formattedTime);
+ } else {
+ // display as day/time
+ summary = line1 = line2 = res.getString(R.string.zen_mode_until, formattedTime);
}
- final String skeleton = DateFormat.is24HourFormat(context, userHandle) ? "Hm" : "hma";
- final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
- final CharSequence formattedTime = DateFormat.format(pattern, time);
- final Resources res = context.getResources();
- final String summary = res.getQuantityString(summaryResId, num, num, formattedTime);
- final String line1 = res.getQuantityString(line1ResId, num, num, formattedTime);
- final String line2 = res.getString(R.string.zen_mode_until, formattedTime);
final Uri id = toCountdownConditionId(time);
return new Condition(id, summary, line1, line2, 0, Condition.STATE_TRUE,
Condition.FLAG_RELEVANT_NOW);
}
+ public static Condition toNextAlarmCondition(Context context, long now, long alarm,
+ int userHandle) {
+ final CharSequence formattedTime = getFormattedTime(context, alarm, userHandle);
+ final Resources res = context.getResources();
+ final String line1 = res.getString(R.string.zen_mode_alarm, formattedTime);
+ final Uri id = toCountdownConditionId(alarm);
+ return new Condition(id, "", line1, "", 0, Condition.STATE_TRUE,
+ Condition.FLAG_RELEVANT_NOW);
+ }
+
+ private static CharSequence getFormattedTime(Context context, long time, int userHandle) {
+ String skeleton = "EEE " + (DateFormat.is24HourFormat(context, userHandle) ? "Hm" : "hma");
+ GregorianCalendar now = new GregorianCalendar();
+ GregorianCalendar endTime = new GregorianCalendar();
+ endTime.setTimeInMillis(time);
+ if (now.get(Calendar.YEAR) == endTime.get(Calendar.YEAR)
+ && now.get(Calendar.MONTH) == endTime.get(Calendar.MONTH)
+ && now.get(Calendar.DATE) == endTime.get(Calendar.DATE)) {
+ skeleton = DateFormat.is24HourFormat(context, userHandle) ? "Hm" : "hma";
+ }
+ final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
+ return DateFormat.format(pattern, time);
+ }
+
// ==== Built-in system conditions ====
public static final String SYSTEM_AUTHORITY = "android";
@@ -900,11 +922,6 @@
return UUID.randomUUID().toString().replace("-", "");
}
- public static String getConditionLine1(Context context, ZenModeConfig config,
- int userHandle, boolean shortVersion) {
- return getConditionLine(context, config, userHandle, true /*useLine1*/, shortVersion);
- }
-
public static String getConditionSummary(Context context, ZenModeConfig config,
int userHandle, boolean shortVersion) {
return getConditionLine(context, config, userHandle, false /*useLine1*/, shortVersion);
@@ -923,8 +940,8 @@
if (time > 0) {
final long now = System.currentTimeMillis();
final long span = time - now;
- c = toTimeCondition(context,
- time, Math.round(span / (float) MINUTES_MS), now, userHandle, shortVersion);
+ c = toTimeCondition(context, time, Math.round(span / (float) MINUTES_MS),
+ userHandle, shortVersion);
}
final String rt = c == null ? "" : useLine1 ? c.line1 : c.summary;
return TextUtils.isEmpty(rt) ? "" : rt;
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index d598291..d146e5e 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -270,7 +270,7 @@
@Override
public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
- Configuration newConfig) {
+ Configuration newConfig, Rect backDropRect) {
Message msg = mCaller.obtainMessageIO(MSG_WINDOW_RESIZED,
reportDraw ? 1 : 0, outsets);
mCaller.sendMessage(msg);
diff --git a/core/java/android/test/AndroidTestCase.java b/core/java/android/test/AndroidTestCase.java
index 0635559..2ecbfae 100644
--- a/core/java/android/test/AndroidTestCase.java
+++ b/core/java/android/test/AndroidTestCase.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
+import android.test.suitebuilder.annotation.Suppress;
import junit.framework.TestCase;
@@ -44,6 +45,7 @@
super.tearDown();
}
+ @Suppress
public void testAndroidTestCaseSetupProperly() {
assertNotNull("Context is null. setContext should be called before tests are run",
mContext);
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 6ece091..6a33579 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -627,7 +627,9 @@
chooseHt = getParagraphSpans(spanned, paraStart, paraEnd, LineHeightSpan.class);
- if (chooseHt.length != 0) {
+ if (chooseHt.length == 0) {
+ chooseHt = null; // So that out() would not assume it has any contents
+ } else {
if (chooseHtv == null ||
chooseHtv.length < chooseHt.length) {
chooseHtv = ArrayUtils.newUnpaddedIntArray(chooseHt.length);
@@ -810,7 +812,7 @@
v = out(source, here, endPos,
fmAscent, fmDescent, fmTop, fmBottom,
- v, spacingmult, spacingadd, chooseHt,chooseHtv, fm, flags[breakIndex],
+ v, spacingmult, spacingadd, chooseHt, chooseHtv, fm, flags[breakIndex],
needMultiply, chdirs, dir, easy, bufEnd, includepad, trackpad,
chs, widths, paraStart, ellipsize, ellipsizedWidth,
lineWidths[breakIndex], paint, moreChars);
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index de509b2..2459cfa 100644
--- a/core/java/android/text/method/ArrowKeyMovementMethod.java
+++ b/core/java/android/text/method/ArrowKeyMovementMethod.java
@@ -238,6 +238,7 @@
initialScrollY = Touch.getInitialScrollY(widget, buffer);
}
+ boolean wasTouchSelecting = isTouchSelecting(isMouse, buffer);
boolean handled = Touch.onTouchEvent(widget, buffer, event);
if (widget.didTouchFocusSelect() && !isMouse) {
@@ -267,9 +268,9 @@
// Cursor can be active at any location in the text while mouse pointer can start
// selection from a totally different location. Use LAST_TAP_DOWN span to ensure
// text selection will start from mouse pointer location.
+ final int startOffset = buffer.getSpanStart(LAST_TAP_DOWN);
if (isMouse && Touch.isSelectionStarted(buffer)) {
- int offset = buffer.getSpanStart(LAST_TAP_DOWN);
- Selection.setSelection(buffer, offset);
+ Selection.setSelection(buffer, startOffset);
}
if (isTouchSelecting(isMouse, buffer) && handled) {
@@ -284,9 +285,9 @@
// Update selection as we're moving the selection area.
// Get the current touch position
- int offset = widget.getOffsetForPosition(event.getX(), event.getY());
-
- Selection.extendSelection(buffer, offset);
+ final int offset = widget.getOffsetForPosition(event.getX(), event.getY());
+ Selection.setSelection(buffer, Math.min(startOffset, offset),
+ Math.max(startOffset, offset));
return true;
}
} else if (action == MotionEvent.ACTION_UP) {
@@ -300,10 +301,12 @@
return true;
}
- int offset = widget.getOffsetForPosition(event.getX(), event.getY());
- if (isTouchSelecting(isMouse, buffer)) {
+ if (wasTouchSelecting) {
+ final int startOffset = buffer.getSpanStart(LAST_TAP_DOWN);
+ final int endOffset = widget.getOffsetForPosition(event.getX(), event.getY());
+ Selection.setSelection(buffer, Math.min(startOffset, endOffset),
+ Math.max(startOffset, endOffset));
buffer.removeSpan(LAST_TAP_DOWN);
- Selection.extendSelection(buffer, offset);
}
MetaKeyKeyListener.adjustMetaAfterKeypress(buffer);
diff --git a/core/java/android/transition/ChangeBounds.java b/core/java/android/transition/ChangeBounds.java
index c82587b..6d1d893 100644
--- a/core/java/android/transition/ChangeBounds.java
+++ b/core/java/android/transition/ChangeBounds.java
@@ -432,23 +432,24 @@
return anim;
}
} else {
- int startX = (Integer) startValues.values.get(PROPNAME_WINDOW_X);
- int startY = (Integer) startValues.values.get(PROPNAME_WINDOW_Y);
- int endX = (Integer) endValues.values.get(PROPNAME_WINDOW_X);
- int endY = (Integer) endValues.values.get(PROPNAME_WINDOW_Y);
+ sceneRoot.getLocationInWindow(tempLocation);
+ int startX = (Integer) startValues.values.get(PROPNAME_WINDOW_X) - tempLocation[0];
+ int startY = (Integer) startValues.values.get(PROPNAME_WINDOW_Y) - tempLocation[1];
+ int endX = (Integer) endValues.values.get(PROPNAME_WINDOW_X) - tempLocation[0];
+ int endY = (Integer) endValues.values.get(PROPNAME_WINDOW_Y) - tempLocation[1];
// TODO: also handle size changes: check bounds and animate size changes
if (startX != endX || startY != endY) {
- sceneRoot.getLocationInWindow(tempLocation);
- Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),
- Bitmap.Config.ARGB_8888);
+ final int width = view.getWidth();
+ final int height = view.getHeight();
+ Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
view.draw(canvas);
final BitmapDrawable drawable = new BitmapDrawable(bitmap);
+ drawable.setBounds(startX, startY, startX + width, startY + height);
final float transitionAlpha = view.getTransitionAlpha();
view.setTransitionAlpha(0);
sceneRoot.getOverlay().add(drawable);
- Path topLeftPath = getPathMotion().getPath(startX - tempLocation[0],
- startY - tempLocation[1], endX - tempLocation[0], endY - tempLocation[1]);
+ Path topLeftPath = getPathMotion().getPath(startX, startY, endX, endY);
PropertyValuesHolder origin = PropertyValuesHolder.ofObject(
DRAWABLE_ORIGIN_PROPERTY, null, topLeftPath);
ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(drawable, origin);
diff --git a/core/java/android/util/FloatProperty.java b/core/java/android/util/FloatProperty.java
index a67b3cb..4aac196 100644
--- a/core/java/android/util/FloatProperty.java
+++ b/core/java/android/util/FloatProperty.java
@@ -15,18 +15,14 @@
*/
package android.util;
-import android.util.Property;
-
/**
* An implementation of {@link android.util.Property} to be used specifically with fields of type
* <code>float</code>. This type-specific subclass enables performance benefit by allowing
- * calls to a {@link #set(Object, Float) set()} function that takes the primitive
+ * calls to a {@link #setValue(Object, float) setValue()} function that takes the primitive
* <code>float</code> type and avoids autoboxing and other overhead associated with the
* <code>Float</code> class.
*
* @param <T> The class on which the Property is declared.
- *
- * @hide
*/
public abstract class FloatProperty<T> extends Property<T, Float> {
@@ -35,7 +31,7 @@
}
/**
- * A type-specific override of the {@link #set(Object, Float)} that is faster when dealing
+ * A type-specific variant of {@link #set(Object, Float)} that is faster when dealing
* with fields of type <code>float</code>.
*/
public abstract void setValue(T object, float value);
diff --git a/core/java/android/util/IntProperty.java b/core/java/android/util/IntProperty.java
index 17977ca..9e21ced 100644
--- a/core/java/android/util/IntProperty.java
+++ b/core/java/android/util/IntProperty.java
@@ -15,18 +15,14 @@
*/
package android.util;
-import android.util.Property;
-
/**
* An implementation of {@link android.util.Property} to be used specifically with fields of type
* <code>int</code>. This type-specific subclass enables performance benefit by allowing
- * calls to a {@link #set(Object, Integer) set()} function that takes the primitive
+ * calls to a {@link #setValue(Object, int) setValue()} function that takes the primitive
* <code>int</code> type and avoids autoboxing and other overhead associated with the
* <code>Integer</code> class.
*
* @param <T> The class on which the Property is declared.
- *
- * @hide
*/
public abstract class IntProperty<T> extends Property<T, Integer> {
@@ -35,7 +31,7 @@
}
/**
- * A type-specific override of the {@link #set(Object, Integer)} that is faster when dealing
+ * A type-specific variant of {@link #set(Object, Integer)} that is faster when dealing
* with fields of type <code>int</code>.
*/
public abstract void setValue(T object, int value);
diff --git a/core/java/android/util/LocaleList.java b/core/java/android/util/LocaleList.java
index 379651e..0d5c135 100644
--- a/core/java/android/util/LocaleList.java
+++ b/core/java/android/util/LocaleList.java
@@ -37,6 +37,11 @@
*/
public final class LocaleList {
private final Locale[] mList;
+ // This is a comma-separated list of the locales in the LocaleList created at construction time,
+ // basically the result of running each locale's toLanguageTag() method and concatenating them
+ // with commas in between.
+ private final String mStringRepresentation;
+
private static final Locale[] sEmptyList = new Locale[0];
private static final LocaleList sEmptyLocaleList = new LocaleList();
@@ -95,15 +100,9 @@
return sb.toString();
}
+ @NonNull
public String toLanguageTags() {
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < mList.length; ++i) {
- sb.append(mList[i].toLanguageTag());
- if (i < mList.length - 1) {
- sb.append(',');
- }
- }
- return sb.toString();
+ return mStringRepresentation;
}
/**
@@ -112,6 +111,7 @@
*/
public LocaleList() {
mList = sEmptyList;
+ mStringRepresentation = "";
}
/**
@@ -121,9 +121,11 @@
public LocaleList(@Nullable Locale locale) {
if (locale == null) {
mList = sEmptyList;
+ mStringRepresentation = "";
} else {
mList = new Locale[1];
mList[0] = (Locale) locale.clone();
+ mStringRepresentation = locale.toLanguageTag();
}
}
@@ -134,9 +136,11 @@
public LocaleList(@Nullable Locale[] list) {
if (list == null || list.length == 0) {
mList = sEmptyList;
+ mStringRepresentation = "";
} else {
final Locale[] localeList = new Locale[list.length];
final HashSet<Locale> seenLocales = new HashSet<Locale>();
+ final StringBuilder sb = new StringBuilder();
for (int i = 0; i < list.length; ++i) {
final Locale l = list[i];
if (l == null) {
@@ -144,11 +148,17 @@
} else if (seenLocales.contains(l)) {
throw new IllegalArgumentException();
} else {
- seenLocales.add(l);
- localeList[i] = (Locale) l.clone();
+ final Locale localeClone = (Locale) l.clone();
+ localeList[i] = localeClone;
+ sb.append(localeClone.toLanguageTag());
+ if (i < list.length - 1) {
+ sb.append(',');
+ }
+ seenLocales.add(localeClone);
}
}
mList = localeList;
+ mStringRepresentation = sb.toString();
}
}
diff --git a/core/java/android/util/PathParser.java b/core/java/android/util/PathParser.java
index 954dcfb..f099479 100644
--- a/core/java/android/util/PathParser.java
+++ b/core/java/android/util/PathParser.java
@@ -27,21 +27,21 @@
static final String LOGTAG = PathParser.class.getSimpleName();
/**
- * @param pathData The string representing a path, the same as "d" string in svg file.
+ * @param pathString The string representing a path, the same as "d" string in svg file.
* @return the generated Path object.
*/
- public static Path createPathFromPathData(String pathData) {
- Path path = new Path();
- PathDataNode[] nodes = createNodesFromPathData(pathData);
- if (nodes != null) {
- try {
- PathDataNode.nodesToPath(nodes, path);
- } catch (RuntimeException e) {
- throw new RuntimeException("Error in parsing " + pathData, e);
- }
- return path;
+ public static Path createPathFromPathData(String pathString) {
+ if (pathString == null) {
+ throw new IllegalArgumentException("Path string can not be null.");
}
- return null;
+ Path path = new Path();
+ boolean hasValidPathData = nParseStringForPath(path.mNativePath, pathString,
+ pathString.length());
+ if (!hasValidPathData) {
+ throw new IllegalArgumentException("Path string: " + pathString +
+ " does not contain valid path data");
+ }
+ return path;
}
/**
@@ -660,7 +660,7 @@
// and http://www.spaceroots.org/documents/ellipse/node22.html
// Maximum of 45 degrees per cubic Bezier segment
- int numSegments = Math.abs((int) Math.ceil(sweep * 4 / Math.PI));
+ int numSegments = (int) Math.ceil(Math.abs(sweep * 4 / Math.PI));
double eta1 = start;
double cosTheta = Math.cos(theta);
@@ -701,4 +701,7 @@
}
}
}
+
+ private static native boolean nParseStringForPath(long pathPtr, String pathString,
+ int stringLength);
}
diff --git a/core/java/android/util/SparseIntArray.java b/core/java/android/util/SparseIntArray.java
index e5c729d..3b832dd 100644
--- a/core/java/android/util/SparseIntArray.java
+++ b/core/java/android/util/SparseIntArray.java
@@ -19,6 +19,8 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.GrowingArrayUtils;
+import java.util.Arrays;
+
import libcore.util.EmptyArray;
/**
@@ -239,6 +241,18 @@
}
/**
+ * Provides a copy of keys.
+ *
+ * @hide
+ * */
+ public int[] copyKeys() {
+ if (size() == 0) {
+ return null;
+ }
+ return Arrays.copyOf(mKeys, size());
+ }
+
+ /**
* {@inheritDoc}
*
* <p>This implementation composes a string by iterating over its mappings.
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index c1eb80d..37d6757 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -343,15 +343,15 @@
int seconds = (int) Math.floor(duration / 1000);
int days = 0, hours = 0, minutes = 0;
- if (seconds > SECONDS_PER_DAY) {
+ if (seconds >= SECONDS_PER_DAY) {
days = seconds / SECONDS_PER_DAY;
seconds -= days * SECONDS_PER_DAY;
}
- if (seconds > SECONDS_PER_HOUR) {
+ if (seconds >= SECONDS_PER_HOUR) {
hours = seconds / SECONDS_PER_HOUR;
seconds -= hours * SECONDS_PER_HOUR;
}
- if (seconds > SECONDS_PER_MINUTE) {
+ if (seconds >= SECONDS_PER_MINUTE) {
minutes = seconds / SECONDS_PER_MINUTE;
seconds -= minutes * SECONDS_PER_MINUTE;
}
diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java
index c2c247e..d563f51 100644
--- a/core/java/android/view/FocusFinder.java
+++ b/core/java/android/view/FocusFinder.java
@@ -17,6 +17,9 @@
package android.view;
import android.graphics.Rect;
+import android.util.ArrayMap;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import java.util.ArrayList;
import java.util.Collections;
@@ -173,6 +176,7 @@
// Note: This sort is stable.
mSequentialFocusComparator.setRoot(root);
mSequentialFocusComparator.setIsLayoutRtl(root.isLayoutRtl());
+ mSequentialFocusComparator.setFocusables(focusables);
Collections.sort(focusables, mSequentialFocusComparator);
} finally {
mSequentialFocusComparator.recycle();
@@ -598,8 +602,16 @@
+ "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}.");
}
+ private static final boolean isValidId(final int id) {
+ return id != 0 && id != View.NO_ID;
+ }
+
/**
* Sorts views according to their visual layout and geometry for default tab order.
+ * If views are part of a focus chain (nextFocusForwardId), then they are all grouped
+ * together. The head of the chain is used to determine the order of the chain and is
+ * first in the order and the tail of the chain is the last in the order. The views
+ * in the middle of the chain can be arbitrary order.
* This is used for sequential focus traversal.
*/
private static final class SequentialFocusComparator implements Comparator<View> {
@@ -607,9 +619,15 @@
private final Rect mSecondRect = new Rect();
private ViewGroup mRoot;
private boolean mIsLayoutRtl;
+ private final SparseArray<View> mFocusables = new SparseArray<View>();
+ private final SparseBooleanArray mIsConnectedTo = new SparseBooleanArray();
+ private final ArrayMap<View, View> mHeadsOfChains = new ArrayMap<View, View>();
public void recycle() {
mRoot = null;
+ mFocusables.clear();
+ mHeadsOfChains.clear();
+ mIsConnectedTo.clear();
}
public void setRoot(ViewGroup root) {
@@ -620,11 +638,72 @@
mIsLayoutRtl = b;
}
+ public void setFocusables(ArrayList<View> focusables) {
+ for (int i = focusables.size() - 1; i >= 0; i--) {
+ final View view = focusables.get(i);
+ final int id = view.getId();
+ if (isValidId(id)) {
+ mFocusables.put(id, view);
+ }
+ final int nextId = view.getNextFocusForwardId();
+ if (isValidId(nextId)) {
+ mIsConnectedTo.put(nextId, true);
+ }
+ }
+
+ for (int i = focusables.size() - 1; i >= 0; i--) {
+ final View view = focusables.get(i);
+ final int nextId = view.getNextFocusForwardId();
+ if (isValidId(nextId) && !mIsConnectedTo.get(view.getId())) {
+ setHeadOfChain(view);
+ }
+ }
+ }
+
+ private void setHeadOfChain(View head) {
+ for (View view = head; view != null;
+ view = mFocusables.get(view.getNextFocusForwardId())) {
+ final View otherHead = mHeadsOfChains.get(view);
+ if (otherHead != null) {
+ if (otherHead == head) {
+ return; // This view has already had its head set properly
+ }
+ // A hydra -- multi-headed focus chain (e.g. A->C and B->C)
+ // Use the one we've already chosen instead and reset this chain.
+ view = head;
+ head = otherHead;
+ }
+ mHeadsOfChains.put(view, head);
+ }
+ }
+
public int compare(View first, View second) {
if (first == second) {
return 0;
}
+ // Order between views within a chain is immaterial -- next/previous is
+ // within a chain is handled elsewhere.
+ View firstHead = mHeadsOfChains.get(first);
+ View secondHead = mHeadsOfChains.get(second);
+ if (firstHead == secondHead && firstHead != null) {
+ if (first == firstHead) {
+ return -1; // first is the head, it should be first
+ } else if (second == firstHead) {
+ return 1; // second is the head, it should be first
+ } else if (isValidId(first.getNextFocusForwardId())) {
+ return -1; // first is not the end of the chain
+ } else {
+ return 1; // first is end of chain
+ }
+ }
+ if (firstHead != null) {
+ first = firstHead;
+ }
+ if (secondHead != null) {
+ second = secondHead;
+ }
+ // First see if they belong to the same focus chain.
getRect(first, mFirstRect);
getRect(second, mSecondRect);
diff --git a/core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl b/core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl
new file mode 100644
index 0000000..622b9dd
--- /dev/null
+++ b/core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl
@@ -0,0 +1,28 @@
+/*
+ * 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 android.view;
+
+import android.view.AppTransitionAnimationSpec;
+
+/**
+ * A cross-process future to fetch the specifications for app transitions.
+ *
+ * {@hide}
+ */
+interface IAppTransitionAnimationSpecsFuture {
+ AppTransitionAnimationSpec[] get();
+}
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index cc4bcb6..9e478c1 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -47,7 +47,7 @@
void resized(in Rect frame, in Rect overscanInsets, in Rect contentInsets,
in Rect visibleInsets, in Rect stableInsets, in Rect outsets, boolean reportDraw,
- in Configuration newConfig);
+ in Configuration newConfig, in Rect backDropFrame);
void moved(int newX, int newY);
void dispatchAppVisibility(boolean visible);
void dispatchGetNewSurface();
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 7adfa8d..7b5f5ab 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -28,6 +28,7 @@
import android.os.Bundle;
import android.os.IRemoteCallback;
import android.view.IApplicationToken;
+import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.IOnKeyguardExitResult;
import android.view.IRotationWatcher;
import android.view.IWindowSession;
@@ -138,8 +139,17 @@
* @hide
*/
void overridePendingAppTransitionMultiThumb(in AppTransitionAnimationSpec[] specs,
- IRemoteCallback startedCallback, boolean scaleUp);
+ IRemoteCallback startedCallback, IRemoteCallback finishedCallback, boolean scaleUp);
void overridePendingAppTransitionInPlace(String packageName, int anim);
+
+ /**
+ * Like overridePendingAppTransitionMultiThumb, but uses a future to supply the specs. This is
+ * used for recents, where generating the thumbnails of the specs takes a non-trivial amount of
+ * time, so we want to move that off the critical path for starting the new activity.
+ */
+ void overridePendingAppTransitionMultiThumbFuture(
+ IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback startedCallback,
+ boolean scaleUp);
void executeAppTransition();
void setAppStartingWindow(IBinder token, String pkg, int theme,
in CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
@@ -149,6 +159,9 @@
void stopAppFreezingScreen(IBinder token, boolean force);
void removeAppToken(IBinder token);
+ /** Used by system ui to report that recents has shown itself. */
+ void endProlongedAnimations();
+
// Re-evaluate the current orientation from the caller's state.
// If there is a change, the new Configuration is returned and the
// caller must call setNewConfiguration() sometime later.
@@ -201,6 +214,16 @@
*/
void setScreenCaptureDisabled(int userId, boolean disabled);
+ /**
+ * Cancels the window transitions for the given task.
+ */
+ void cancelTaskWindowTransition(int taskId);
+
+ /**
+ * Cancels the thumbnail transitions for the given task.
+ */
+ void cancelTaskThumbnailTransition(int taskId);
+
// These can only be called with the SET_ORIENTATION permission.
/**
* Update the current screen rotation based on the current state of
@@ -309,4 +332,16 @@
* @return The frame statistics or null if the window does not exist.
*/
WindowContentFrameStats getWindowContentFrameStats(IBinder token);
+
+ /**
+ * @return the dock side the current docked stack is at; must be one of the
+ * WindowManagerGlobal.DOCKED_* values
+ */
+ int getDockedStackSide();
+
+ /**
+ * Sets whether we are currently in a drag resize operation where we are changing the docked
+ * stack size.
+ */
+ void setDockedStackResizing(boolean resizing);
}
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 017364a..3fc70cc 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -96,6 +96,31 @@
out Rect outOutsets, out Configuration outConfig, out Surface outSurface);
/**
+ * Position a window relative to it's parent (attached) window without triggering
+ * a full relayout. This action may be deferred until a given frame number
+ * for the parent window appears. This allows for synchronizing movement of a child
+ * to repainting the contents of the parent.
+ *
+ * "width" and "height" correspond to the width and height members of
+ * WindowManager.LayoutParams in the {@link #relayout relayout()} case.
+ * This may differ from the surface buffer size in the
+ * case of {@link LayoutParams#FLAG_SCALED} and {@link #relayout relayout()}
+ * must be used with requestedWidth/height if this must be changed.
+ *
+ * @param window The window being modified. Must be attached to a parent window
+ * or this call will fail.
+ * @param x The new x position
+ * @param y The new y position
+ * @param width The new width
+ * @param height The new 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,
+ long deferTransactionUntilFrame, out Rect outFrame);
+
+ /**
* If a call to relayout() asked to have the surface destroy deferred,
* it must call this once it is okay to destroy that surface.
*/
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index cc4598d..bae51d3 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -766,6 +766,15 @@
}
/**
+ * Sets the current pointer shape.
+ * @param pointerShape the id of the pointer icon.
+ * @hide
+ */
+ public void setPointerShape(int pointerShape) {
+ InputManager.getInstance().setPointerIconShape(pointerShape);
+ }
+
+ /**
* Provides information about the range of values for a particular {@link MotionEvent} axis.
*
* @see InputDevice#getMotionRange(int)
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 1c20cab..55cf56f 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -790,8 +790,14 @@
public static final int KEYCODE_MEDIA_STEP_BACKWARD = 275;
/** Key code constant: put device to sleep unless a wakelock is held. */
public static final int KEYCODE_SOFT_SLEEP = 276;
+ /** Key code constant: Cut key. */
+ public static final int KEYCODE_CUT = 277;
+ /** Key code constant: Copy key. */
+ public static final int KEYCODE_COPY = 278;
+ /** Key code constant: Paste key. */
+ public static final int KEYCODE_PASTE = 279;
- private static final int LAST_KEYCODE = KEYCODE_SOFT_SLEEP;
+ private static final int LAST_KEYCODE = KEYCODE_PASTE;
// NOTE: If you add a new keycode here you must also add it to:
// isSystem()
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index e200bef..aa29636 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -547,9 +547,9 @@
// Don't retain static reference on context.
mConstructorArgs[0] = lastContext;
mConstructorArgs[1] = null;
- }
- Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
return result;
}
diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java
index cf35ce5..88e9a95 100644
--- a/core/java/android/view/PointerIcon.java
+++ b/core/java/android/view/PointerIcon.java
@@ -48,6 +48,12 @@
/** Style constant: Null icon. It has no bitmap. */
public static final int STYLE_NULL = 0;
+ /** Style constant: no icons are specified. If all views uses this, then falls back
+ * to the default style, but this is helpful to distinguish a view explicitly want
+ * to have the default icon.
+ */
+ public static final int STYLE_NOT_SPECIFIED = 1;
+
/** Style constant: Arrow icon. (Default mouse pointer) */
public static final int STYLE_ARROW = 1000;
@@ -60,12 +66,74 @@
/** {@hide} Style constant: Spot anchor icon for touchpads. */
public static final int STYLE_SPOT_ANCHOR = 2002;
+ // Style constants for additional predefined icons for mice.
+ /** Style constant: context-menu. */
+ public static final int STYLE_CONTEXT_MENU = 1001;
+
+ /** Style constant: hand. */
+ public static final int STYLE_HAND = 1002;
+
+ /** Style constant: help. */
+ public static final int STYLE_HELP = 1003;
+
+ /** Style constant: wait. */
+ public static final int STYLE_WAIT = 1004;
+
+ /** Style constant: cell. */
+ public static final int STYLE_CELL = 1006;
+
+ /** Style constant: crosshair. */
+ public static final int STYLE_CROSSHAIR = 1007;
+
+ /** Style constant: text. */
+ public static final int STYLE_TEXT = 1008;
+
+ /** Style constant: vertical-text. */
+ public static final int STYLE_VERTICAL_TEXT = 1009;
+
+ /** Style constant: alias (indicating an alias of/shortcut to something is
+ * to be created. */
+ public static final int STYLE_ALIAS = 1010;
+
+ /** Style constant: copy. */
+ public static final int STYLE_COPY = 1011;
+
+ /** Style constant: no-drop. */
+ public static final int STYLE_NO_DROP = 1012;
+
+ /** Style constant: all-scroll. */
+ public static final int STYLE_ALL_SCROLL = 1013;
+
+ /** Style constant: horizontal double arrow mainly for resizing. */
+ public static final int STYLE_HORIZONTAL_DOUBLE_ARROW = 1014;
+
+ /** Style constant: vertical double arrow mainly for resizing. */
+ public static final int STYLE_VERTICAL_DOUBLE_ARROW = 1015;
+
+ /** Style constant: diagonal double arrow -- top-right to bottom-left. */
+ public static final int STYLE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW = 1016;
+
+ /** Style constant: diagonal double arrow -- top-left to bottom-right. */
+ public static final int STYLE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW = 1017;
+
+ /** Style constant: zoom-in. */
+ public static final int STYLE_ZOOM_IN = 1018;
+
+ /** Style constant: zoom-out. */
+ public static final int STYLE_ZOOM_OUT = 1019;
+
+ /** Style constant: grab. */
+ public static final int STYLE_GRAB = 1020;
+
+ /** Style constant: grabbing. */
+ public static final int STYLE_GRABBING = 1021;
+
// OEM private styles should be defined starting at this range to avoid
// conflicts with any system styles that may be defined in the future.
private static final int STYLE_OEM_FIRST = 10000;
- // The default pointer icon.
- private static final int STYLE_DEFAULT = STYLE_ARROW;
+ /** {@hide} The default pointer icon. */
+ public static final int STYLE_DEFAULT = STYLE_ARROW;
private static final PointerIcon gNullIcon = new PointerIcon(STYLE_NULL);
@@ -434,6 +502,49 @@
return com.android.internal.R.styleable.Pointer_pointerIconSpotTouch;
case STYLE_SPOT_ANCHOR:
return com.android.internal.R.styleable.Pointer_pointerIconSpotAnchor;
+ case STYLE_HAND:
+ return com.android.internal.R.styleable.Pointer_pointerIconHand;
+ case STYLE_CONTEXT_MENU:
+ return com.android.internal.R.styleable.Pointer_pointerIconContextMenu;
+ case STYLE_HELP:
+ return com.android.internal.R.styleable.Pointer_pointerIconHelp;
+ case STYLE_WAIT:
+ // falls back to the default icon because no animation support.
+ return com.android.internal.R.styleable.Pointer_pointerIconArrow;
+ case STYLE_CELL:
+ return com.android.internal.R.styleable.Pointer_pointerIconCell;
+ case STYLE_CROSSHAIR:
+ return com.android.internal.R.styleable.Pointer_pointerIconCrosshair;
+ case STYLE_TEXT:
+ return com.android.internal.R.styleable.Pointer_pointerIconText;
+ case STYLE_VERTICAL_TEXT:
+ return com.android.internal.R.styleable.Pointer_pointerIconVerticalText;
+ case STYLE_ALIAS:
+ return com.android.internal.R.styleable.Pointer_pointerIconAlias;
+ case STYLE_COPY:
+ return com.android.internal.R.styleable.Pointer_pointerIconCopy;
+ case STYLE_ALL_SCROLL:
+ return com.android.internal.R.styleable.Pointer_pointerIconAllScroll;
+ case STYLE_NO_DROP:
+ return com.android.internal.R.styleable.Pointer_pointerIconNodrop;
+ case STYLE_HORIZONTAL_DOUBLE_ARROW:
+ return com.android.internal.R.styleable.Pointer_pointerIconHorizontalDoubleArrow;
+ case STYLE_VERTICAL_DOUBLE_ARROW:
+ return com.android.internal.R.styleable.Pointer_pointerIconVerticalDoubleArrow;
+ case STYLE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW:
+ return com.android.internal.R.styleable.
+ Pointer_pointerIconTopRightDiagonalDoubleArrow;
+ case STYLE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW:
+ return com.android.internal.R.styleable.
+ Pointer_pointerIconTopLeftDiagonalDoubleArrow;
+ case STYLE_ZOOM_IN:
+ return com.android.internal.R.styleable.Pointer_pointerIconZoomIn;
+ case STYLE_ZOOM_OUT:
+ return com.android.internal.R.styleable.Pointer_pointerIconZoomOut;
+ case STYLE_GRAB:
+ return com.android.internal.R.styleable.Pointer_pointerIconGrab;
+ case STYLE_GRABBING:
+ return com.android.internal.R.styleable.Pointer_pointerIconGrabbing;
default:
return 0;
}
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index 771856f..88bffb5 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -198,8 +198,8 @@
* @see #isValid()
*/
public void end(DisplayListCanvas canvas) {
- long renderNodeData = canvas.finishRecording();
- nSetDisplayListData(mNativeRenderNode, renderNodeData);
+ long displayList = canvas.finishRecording();
+ nSetDisplayList(mNativeRenderNode, displayList);
canvas.recycle();
mValid = true;
}
@@ -209,10 +209,10 @@
* during destruction of hardware resources, to ensure that we do not hold onto
* obsolete resources after related resources are gone.
*/
- public void destroyDisplayListData() {
+ public void discardDisplayList() {
if (!mValid) return;
- nSetDisplayListData(mNativeRenderNode, 0);
+ nSetDisplayList(mNativeRenderNode, 0);
mValid = false;
}
@@ -781,7 +781,7 @@
private static native long nCreate(String name);
private static native void nDestroyRenderNode(long renderNode);
- private static native void nSetDisplayListData(long renderNode, long newData);
+ private static native void nSetDisplayList(long renderNode, long newData);
// Matrix
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
index 2a3756d..7747580 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -336,9 +336,6 @@
return mUnscaledDuration;
}
- /**
- * @hide
- */
@Override
public long getTotalDuration() {
return mUnscaledDuration + mUnscaledStartDelay;
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 6de4d3e..394660f 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -56,6 +56,8 @@
private static native int nativeGetWidth(long nativeObject);
private static native int nativeGetHeight(long nativeObject);
+ private static native long nativeGetNextFrameNumber(long nativeObject);
+
public static final Parcelable.Creator<Surface> CREATOR =
new Parcelable.Creator<Surface>() {
@Override
@@ -220,6 +222,18 @@
}
/**
+ * Returns the next frame number which will be dequeued for rendering.
+ * Intended for use with SurfaceFlinger's deferred transactions API.
+ *
+ * @hide
+ */
+ public long getNextFrameNumber() {
+ synchronized (mLock) {
+ return nativeGetNextFrameNumber(mNativeObject);
+ }
+ }
+
+ /**
* Returns true if the consumer of this Surface is running behind the producer.
*
* @return True if the consumer is more than one buffer ahead of the producer.
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index bcf9b2c..b58c68f 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -81,6 +81,9 @@
private static native boolean nativeSetActiveConfig(IBinder displayToken, int id);
private static native void nativeSetDisplayPowerMode(
IBinder displayToken, int mode);
+ private static native void nativeDeferTransactionUntil(long nativeObject,
+ IBinder handle, long frame);
+ private static native IBinder nativeGetHandle(long nativeObject);
private final CloseGuard mCloseGuard = CloseGuard.get();
@@ -358,6 +361,14 @@
nativeCloseTransaction();
}
+ public void deferTransactionUntil(IBinder handle, long frame) {
+ nativeDeferTransactionUntil(mNativeObject, handle, frame);
+ }
+
+ public IBinder getHandle() {
+ return nativeGetHandle(mNativeObject);
+ }
+
/** flag the transaction as an animation */
public static void setAnimationTransaction() {
nativeSetAnimationTransaction();
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index db68c29..589c0dc 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -157,10 +157,10 @@
long mLastLockTime = 0;
boolean mVisible = false;
- int mLeft = -1;
- int mTop = -1;
- int mWidth = -1;
- int mHeight = -1;
+ int mWindowSpaceLeft = -1;
+ int mWindowSpaceTop = -1;
+ int mWindowSpaceWidth = -1;
+ int mWindowSpaceHeight = -1;
int mFormat = -1;
final Rect mSurfaceFrame = new Rect();
int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1;
@@ -445,33 +445,33 @@
getLocationInWindow(mLocation);
final boolean creating = mWindow == null;
final boolean formatChanged = mFormat != mRequestedFormat;
- final boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
+ final boolean sizeChanged = mWindowSpaceWidth != myWidth || mWindowSpaceHeight != myHeight;
final boolean visibleChanged = mVisible != mRequestedVisible;
- final boolean layoutSizeChanged = getWidth() != mLayout.width || getHeight() != mLayout.height;
+ final boolean layoutSizeChanged = getWidth() != mLayout.width
+ || getHeight() != mLayout.height;
+ final boolean positionChanged = mWindowSpaceLeft != mLocation[0] || mWindowSpaceTop != mLocation[1];
if (force || creating || formatChanged || sizeChanged || visibleChanged
- || mLeft != mLocation[0] || mTop != mLocation[1]
- || mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded || layoutSizeChanged) {
-
+ || mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded) {
if (DEBUG) Log.i(TAG, "Changes: creating=" + creating
+ " format=" + formatChanged + " size=" + sizeChanged
+ " visible=" + visibleChanged
- + " left=" + (mLeft != mLocation[0])
- + " top=" + (mTop != mLocation[1]));
+ + " left=" + (mWindowSpaceLeft != mLocation[0])
+ + " top=" + (mWindowSpaceTop != mLocation[1]));
try {
final boolean visible = mVisible = mRequestedVisible;
- mLeft = mLocation[0];
- mTop = mLocation[1];
- mWidth = myWidth;
- mHeight = myHeight;
+ mWindowSpaceLeft = mLocation[0];
+ mWindowSpaceTop = mLocation[1];
+ mWindowSpaceWidth = myWidth;
+ mWindowSpaceHeight = myHeight;
mFormat = mRequestedFormat;
// Scaling/Translate window's layout here because mLayout is not used elsewhere.
// Places the window relative
- mLayout.x = mLeft;
- mLayout.y = mTop;
+ mLayout.x = mWindowSpaceLeft;
+ mLayout.y = mWindowSpaceTop;
mLayout.width = getWidth();
mLayout.height = getHeight();
if (mTranslator != null) {
@@ -486,6 +486,14 @@
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
;
+ if (!creating && !force && !mUpdateWindowNeeded) {
+ mLayout.privateFlags |=
+ WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY;
+ } else {
+ mLayout.privateFlags &=
+ ~WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY;
+ }
+
if (!getContext().getResources().getCompatibilityInfo().supportsScreen()) {
mLayout.privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
@@ -517,7 +525,7 @@
if (DEBUG) Log.i(TAG, "Cur surface: " + mSurface);
relayoutResult = mSession.relayout(
- mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
+ mWindow, mWindow.mSeq, mLayout, mWindowSpaceWidth, mWindowSpaceHeight,
visible ? VISIBLE : GONE,
WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY,
mWinFrame, mOverscanInsets, mContentInsets,
@@ -616,11 +624,30 @@
mSession.performDeferredDestroy(mWindow);
}
} catch (RemoteException ex) {
+ Log.e(TAG, "Exception from relayout", ex);
}
if (DEBUG) Log.v(
TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
" w=" + mLayout.width + " h=" + mLayout.height +
", frame=" + mSurfaceFrame);
+ } else if (positionChanged || layoutSizeChanged) { // Only the position has changed
+ mWindowSpaceLeft = mLocation[0];
+ mWindowSpaceTop = mLocation[1];
+ // For our size changed check, we keep mLayout.width and mLayout.height
+ // in view local space.
+ mLocation[0] = mLayout.width = getWidth();
+ mLocation[1] = mLayout.height = getHeight();
+
+ transformFromViewToWindowSpace(mLocation);
+
+ try {
+ mSession.repositionChild(mWindow, mWindowSpaceLeft, mWindowSpaceTop,
+ mLocation[0], mLocation[1],
+ viewRoot != null ? viewRoot.getNextFrameNumber() : -1,
+ mWinFrame);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Exception from relayout", ex);
+ }
}
}
@@ -658,7 +685,7 @@
@Override
public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
- Configuration newConfig) {
+ Configuration newConfig, Rect backDropRect) {
SurfaceView surfaceView = mSurfaceView.get();
if (surfaceView != null) {
if (DEBUG) Log.v(
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 7752ed8..66b05a2 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2415,6 +2415,7 @@
* 1 PFLAG3_SCROLL_INDICATOR_START
* 1 PFLAG3_SCROLL_INDICATOR_END
* 1 PFLAG3_ASSIST_BLOCKED
+ * 1111111 PFLAG3_POINTER_ICON_MASK
* |-------|-------|-------|-------|
*/
@@ -2611,6 +2612,36 @@
static final int PFLAG3_ASSIST_BLOCKED = 0x4000;
/**
+ * The mask for use with private flags indicating bits used for pointer icon shapes.
+ */
+ static final int PFLAG3_POINTER_ICON_MASK = 0x7f8000;
+
+ /**
+ * Left-shift used for pointer icon shape values in private flags.
+ */
+ static final int PFLAG3_POINTER_ICON_LSHIFT = 15;
+
+ /**
+ * Value indicating {@link PointerIcon.STYLE_NOT_SPECIFIED}.
+ */
+ private static final int PFLAG3_POINTER_ICON_NOT_SPECIFIED = 0 << PFLAG3_POINTER_ICON_LSHIFT;
+
+ /**
+ * Value indicating {@link PointerIcon.STYLE_NULL}.
+ */
+ private static final int PFLAG3_POINTER_ICON_NULL = 1 << PFLAG3_POINTER_ICON_LSHIFT;
+
+ /**
+ * Value incicating {@link PointerIcon.STYLE_CUSTOM}.
+ */
+ private static final int PFLAG3_POINTER_ICON_CUSTOM = 2 << PFLAG3_POINTER_ICON_LSHIFT;
+
+ /**
+ * The base value for other pointer icon shapes.
+ */
+ private static final int PFLAG3_POINTER_ICON_VALUE_START = 3 << PFLAG3_POINTER_ICON_LSHIFT;
+
+ /**
* Always allow a user to over-scroll this view, provided it is a
* view that can scroll.
*
@@ -3520,6 +3551,11 @@
ListenerInfo mListenerInfo;
+ // Temporary values used to hold (x,y) coordinates when delegating from the
+ // two-arg performLongClick() method to the legacy no-arg version.
+ private float mLongClickX = Float.NaN;
+ private float mLongClickY = Float.NaN;
+
/**
* The application environment this view lives in.
* This field should be made private, so it is hidden from the SDK.
@@ -5341,21 +5377,60 @@
}
/**
- * Call this view's OnLongClickListener, if it is defined. Invokes the context menu if the
- * OnLongClickListener did not consume the event.
+ * Calls this view's OnLongClickListener, if it is defined. Invokes the
+ * context menu if the OnLongClickListener did not consume the event.
*
- * @return True if one of the above receivers consumed the event, false otherwise.
+ * @return {@code true} if one of the above receivers consumed the event,
+ * {@code false} otherwise
*/
public boolean performLongClick() {
+ return performLongClickInternal(mLongClickX, mLongClickY);
+ }
+
+ /**
+ * Calls this view's OnLongClickListener, if it is defined. Invokes the
+ * context menu if the OnLongClickListener did not consume the event,
+ * anchoring it to an (x,y) coordinate.
+ *
+ * @param x x coordinate of the anchoring touch event, or {@link Float#NaN}
+ * to disable anchoring
+ * @param y y coordinate of the anchoring touch event, or {@link Float#NaN}
+ * to disable anchoring
+ * @return {@code true} if one of the above receivers consumed the event,
+ * {@code false} otherwise
+ */
+ public boolean performLongClick(float x, float y) {
+ mLongClickX = x;
+ mLongClickY = y;
+ final boolean handled = performLongClick();
+ mLongClickX = Float.NaN;
+ mLongClickY = Float.NaN;
+ return handled;
+ }
+
+ /**
+ * Calls this view's OnLongClickListener, if it is defined. Invokes the
+ * context menu if the OnLongClickListener did not consume the event,
+ * optionally anchoring it to an (x,y) coordinate.
+ *
+ * @param x x coordinate of the anchoring touch event, or {@link Float#NaN}
+ * to disable anchoring
+ * @param y y coordinate of the anchoring touch event, or {@link Float#NaN}
+ * to disable anchoring
+ * @return {@code true} if one of the above receivers consumed the event,
+ * {@code false} otherwise
+ */
+ private boolean performLongClickInternal(float x, float y) {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
boolean handled = false;
- ListenerInfo li = mListenerInfo;
+ final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnLongClickListener != null) {
handled = li.mOnLongClickListener.onLongClick(View.this);
}
if (!handled) {
- handled = showContextMenu();
+ final boolean isAnchored = !Float.isNaN(x) && !Float.isNaN(y);
+ handled = isAnchored ? showContextMenu(x, y) : showContextMenu();
}
if (handled) {
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
@@ -5392,7 +5467,7 @@
* @hide
*/
protected boolean performButtonActionOnTouchDown(MotionEvent event) {
- if (event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE &&
+ if (event.isFromSource(InputDevice.SOURCE_MOUSE) &&
(event.getButtonState() & MotionEvent.BUTTON_SECONDARY) != 0) {
showContextMenu(event.getX(), event.getY());
mPrivateFlags |= PFLAG_CANCEL_NEXT_UP_EVENT;
@@ -10002,32 +10077,36 @@
* KeyEvent.Callback.onKeyDown()}: perform press of the view
* when {@link KeyEvent#KEYCODE_DPAD_CENTER} or {@link KeyEvent#KEYCODE_ENTER}
* is released, if the view is enabled and clickable.
+ * <p>
+ * Key presses in software keyboards will generally NOT trigger this
+ * listener, although some may elect to do so in some situations. Do not
+ * rely on this to catch software key presses.
*
- * <p>Key presses in software keyboards will generally NOT trigger this listener,
- * although some may elect to do so in some situations. Do not rely on this to
- * catch software key presses.
- *
- * @param keyCode A key code that represents the button pressed, from
- * {@link android.view.KeyEvent}.
- * @param event The KeyEvent object that defines the button action.
+ * @param keyCode a key code that represents the button pressed, from
+ * {@link android.view.KeyEvent}
+ * @param event the KeyEvent object that defines the button action
*/
public boolean onKeyDown(int keyCode, KeyEvent event) {
- boolean result = false;
-
if (KeyEvent.isConfirmKey(keyCode)) {
if ((mViewFlags & ENABLED_MASK) == DISABLED) {
return true;
}
- // Long clickable items don't necessarily have to be clickable
- if (((mViewFlags & CLICKABLE) == CLICKABLE ||
- (mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) &&
- (event.getRepeatCount() == 0)) {
- setPressed(true);
- checkForLongClick(0);
+
+ // Long clickable items don't necessarily have to be clickable.
+ if (((mViewFlags & CLICKABLE) == CLICKABLE
+ || (mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
+ && (event.getRepeatCount() == 0)) {
+ // For the purposes of menu anchoring and drawable hotspots,
+ // key events are considered to be at the center of the view.
+ final float x = getWidth() / 2f;
+ final float y = getHeight() / 2f;
+ setPressed(true, x, y);
+ checkForLongClick(0, x, y);
return true;
}
}
- return result;
+
+ return false;
}
/**
@@ -10555,7 +10634,7 @@
} else {
// Not inside a scrolling container, so show the feedback right away
setPressed(true, x, y);
- checkForLongClick(0);
+ checkForLongClick(0, x, y);
}
break;
@@ -12889,6 +12968,10 @@
mPrivateFlags |= PFLAG_DIRTY;
+ // Release any resources in-case we don't end up drawing again
+ // as anything cached is no longer valid
+ resetDisplayList();
+
if (invalidateCache) {
mPrivateFlags |= PFLAG_INVALIDATED;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
@@ -15385,11 +15468,11 @@
private void resetDisplayList() {
if (mRenderNode.isValid()) {
- mRenderNode.destroyDisplayListData();
+ mRenderNode.discardDisplayList();
}
if (mBackgroundRenderNode != null && mBackgroundRenderNode.isValid()) {
- mBackgroundRenderNode.destroyDisplayListData();
+ mBackgroundRenderNode.discardDisplayList();
}
}
@@ -18415,21 +18498,29 @@
* must be an array of two integers. After the method returns, the array
* contains the x and y location in that order.</p>
*
- * @param location an array of two integers in which to hold the coordinates
+ * @param outWindowSpace an array of two integers in which to hold the coordinates
*/
- public void getLocationInWindow(@Size(2) int[] location) {
- if (location == null || location.length < 2) {
- throw new IllegalArgumentException("location must be an array of two integers");
+ public void getLocationInWindow(@Size(2) int[] outWindowSpace) {
+ outWindowSpace[0] = 0;
+ outWindowSpace[1] = 0;
+
+ transformFromViewToWindowSpace(outWindowSpace);
+ }
+
+ void transformFromViewToWindowSpace(@Size(2) int[] inOutLocation) {
+ if (inOutLocation == null || inOutLocation.length < 2) {
+ throw new IllegalArgumentException("inOutLocation must be an array of two integers");
}
if (mAttachInfo == null) {
// When the view is not attached to a window, this method does not make sense
- location[0] = location[1] = 0;
+ inOutLocation[0] = inOutLocation[1] = 0;
return;
}
- float[] position = mAttachInfo.mTmpTransformLocation;
- position[0] = position[1] = 0.0f;
+ float position[] = mAttachInfo.mTmpTransformLocation;
+ position[0] = inOutLocation[0];
+ position[1] = inOutLocation[1];
if (!hasIdentityMatrix()) {
getMatrix().mapPoints(position);
@@ -18461,8 +18552,8 @@
position[1] -= vr.mCurScrollY;
}
- location[0] = (int) (position[0] + 0.5f);
- location[1] = (int) (position[1] + 0.5f);
+ inOutLocation[0] = (int) (position[0] + 0.5f);
+ inOutLocation[1] = (int) (position[1] + 0.5f);
}
/**
@@ -19992,13 +20083,14 @@
}
}
- private void checkForLongClick(int delayOffset) {
+ private void checkForLongClick(int delayOffset, float x, float y) {
if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) {
mHasPerformedLongPress = false;
if (mPendingCheckForLongPress == null) {
mPendingCheckForLongPress = new CheckForLongPress();
}
+ mPendingCheckForLongPress.setAnchor(x, y);
mPendingCheckForLongPress.rememberWindowAttachCount();
postDelayed(mPendingCheckForLongPress,
ViewConfiguration.getLongPressTimeout() - delayOffset);
@@ -20995,6 +21087,44 @@
}
}
+ /** @hide */
+ public int getPointerShape(MotionEvent event, float x, float y) {
+ final int value = (mPrivateFlags3 & PFLAG3_POINTER_ICON_MASK);
+ switch (value) {
+ case PFLAG3_POINTER_ICON_NOT_SPECIFIED:
+ return PointerIcon.STYLE_NOT_SPECIFIED;
+ case PFLAG3_POINTER_ICON_NULL:
+ return PointerIcon.STYLE_NULL;
+ case PFLAG3_POINTER_ICON_CUSTOM:
+ return PointerIcon.STYLE_CUSTOM;
+ default:
+ return ((value - PFLAG3_POINTER_ICON_VALUE_START) >> PFLAG3_POINTER_ICON_LSHIFT)
+ + PointerIcon.STYLE_ARROW;
+ }
+ }
+
+ /** @hide */
+ public void setPointerShape(int pointerShape) {
+ int newValue;
+ if (pointerShape == PointerIcon.STYLE_NOT_SPECIFIED) {
+ newValue = PFLAG3_POINTER_ICON_NOT_SPECIFIED;
+ } else if (pointerShape == PointerIcon.STYLE_NULL) {
+ newValue = PFLAG3_POINTER_ICON_NULL;
+ } else if (pointerShape == PointerIcon.STYLE_CUSTOM) {
+ newValue = PFLAG3_POINTER_ICON_CUSTOM;
+ } else if (pointerShape >= PointerIcon.STYLE_ARROW
+ && pointerShape <= PointerIcon.STYLE_GRABBING) {
+ newValue = ((pointerShape - PointerIcon.STYLE_ARROW) << PFLAG3_POINTER_ICON_LSHIFT)
+ + PFLAG3_POINTER_ICON_VALUE_START;
+ } else {
+ Log.w(VIEW_LOG_TAG, "Invalid pointer shape " + pointerShape + " is specified.");
+ return;
+ }
+ if (newValue != (mPrivateFlags3 & PFLAG3_POINTER_ICON_MASK)) {
+ mPrivateFlags3 = (mPrivateFlags3 & ~PFLAG3_POINTER_ICON_MASK) | newValue;
+ }
+ }
+
//
// Properties
//
@@ -21349,17 +21479,24 @@
private final class CheckForLongPress implements Runnable {
private int mOriginalWindowAttachCount;
+ private float mX;
+ private float mY;
@Override
public void run() {
if (isPressed() && (mParent != null)
&& mOriginalWindowAttachCount == mWindowAttachCount) {
- if (performLongClick()) {
+ if (performLongClick(mX, mY)) {
mHasPerformedLongPress = true;
}
}
}
+ public void setAnchor(float x, float y) {
+ mX = x;
+ mY = y;
+ }
+
public void rememberWindowAttachCount() {
mOriginalWindowAttachCount = mWindowAttachCount;
}
@@ -21373,7 +21510,7 @@
public void run() {
mPrivateFlags &= ~PFLAG_PREPRESSED;
setPressed(true, x, y);
- checkForLongClick(ViewConfiguration.getTapTimeout());
+ checkForLongClick(ViewConfiguration.getTapTimeout(), x, y);
}
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 475ce2f..db978a6 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1715,6 +1715,36 @@
return false;
}
+ /** @hide */
+ @Override
+ public int getPointerShape(MotionEvent event, float x, float y) {
+ // Check what the child under the pointer says about the pointer.
+ final int childrenCount = mChildrenCount;
+ if (childrenCount != 0) {
+ final ArrayList<View> preorderedList = buildOrderedChildList();
+ final boolean customOrder = preorderedList == null
+ && isChildrenDrawingOrderEnabled();
+ final View[] children = mChildren;
+ for (int i = childrenCount - 1; i >= 0; i--) {
+ final int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;
+ final View child = (preorderedList == null)
+ ? children[childIndex] : preorderedList.get(childIndex);
+ PointF point = getLocalPoint();
+ if (isTransformedTouchPointInView(x, y, child, point)) {
+ final int pointerShape = child.getPointerShape(event, point.x, point.y);
+ if (pointerShape != PointerIcon.STYLE_NOT_SPECIFIED) {
+ return pointerShape;
+ }
+ break;
+ }
+ }
+ }
+
+ // The pointer is not a child or the child has no preferences, returning the default
+ // implementation.
+ return super.getPointerShape(event, x, y);
+ }
+
/**
* {@inheritDoc}
*/
@@ -4405,6 +4435,10 @@
}
}
}
+
+ if (mCurrentDragStartEvent != null && child.getVisibility() == VISIBLE) {
+ notifyChildOfDragStart(child);
+ }
}
private void addInArray(View child, int index) {
@@ -4606,7 +4640,6 @@
}
private void removeViewInternal(int index, View view) {
-
if (mTransition != null) {
mTransition.removeChild(this, view);
}
@@ -4657,6 +4690,10 @@
mTransientIndices.set(i, oldIndex - 1);
}
}
+
+ if (mCurrentDragStartEvent != null) {
+ mChildrenInterestedInDrag.remove(view);
+ }
}
/**
@@ -4699,12 +4736,17 @@
}
private void removeViewsInternal(int start, int count) {
+ final int end = start + count;
+
+ if (start < 0 || count < 0 || end > mChildrenCount) {
+ throw new IndexOutOfBoundsException();
+ }
+
final View focused = mFocused;
final boolean detach = mAttachInfo != null;
boolean clearChildFocus = false;
final View[] children = mChildren;
- final int end = start + count;
for (int i = start; i < end; i++) {
final View view = children[i];
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 3d10289..d80c6a3 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -37,6 +37,7 @@
import android.graphics.drawable.Drawable;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
+import android.hardware.input.InputManager;
import android.media.AudioManager;
import android.os.Binder;
import android.os.Build;
@@ -286,6 +287,7 @@
final Rect mPendingStableInsets = new Rect();
final Rect mPendingContentInsets = new Rect();
final Rect mPendingOutsets = new Rect();
+ final Rect mPendingBackDropFrame = new Rect();
final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets
= new ViewTreeObserver.InternalInsetsInfo();
@@ -324,6 +326,8 @@
private long mFpsPrevTime = -1;
private int mFpsNumFrames;
+ private int mPointerIconShape = PointerIcon.STYLE_NOT_SPECIFIED;
+
/**
* see {@link #playSoundEffect(int)}
*/
@@ -1550,6 +1554,10 @@
frame.height() < desiredWindowHeight && frame.height() != mHeight));
windowShouldResize |= mDragResizing;
+ // If the backdrop frame doesn't equal to a frame, we are starting a resize operation, so
+ // force it to be resized.
+ windowShouldResize |= !mPendingBackDropFrame.equals(mWinFrame);
+
// Determine whether to compute insets.
// If there are no inset listeners remaining then we may still need to compute
// insets in case the old insets were non-empty and must be reset.
@@ -1730,7 +1738,7 @@
& WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING) != 0;
if (mDragResizing != dragResizing) {
if (dragResizing) {
- startDragResizing(frame);
+ startDragResizing(mPendingBackDropFrame);
} else {
// We shouldn't come here, but if we come we should end the resize.
endDragResizing();
@@ -3266,6 +3274,7 @@
mPendingStableInsets.set((Rect) args.arg6);
mPendingVisibleInsets.set((Rect) args.arg3);
mPendingOutsets.set((Rect) args.arg7);
+ mPendingBackDropFrame.set((Rect) args.arg8);
args.recycle();
@@ -3291,6 +3300,8 @@
mWinFrame.top = t;
mWinFrame.bottom = t + h;
+ mPendingBackDropFrame.set(mWinFrame);
+
if (mView != null) {
forceLayout(mView);
}
@@ -4168,6 +4179,36 @@
private int processPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
+ if (event.getPointerCount() == 1
+ && event.isFromSource(InputDevice.SOURCE_MOUSE)) {
+ if (event.getActionMasked() == MotionEvent.ACTION_HOVER_ENTER
+ || event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT) {
+ // Other apps or the window manager may change the icon shape outside of
+ // this app, therefore the icon shape has to be reset on enter/exit event.
+ mPointerIconShape = PointerIcon.STYLE_NOT_SPECIFIED;
+ }
+
+ final float x = event.getX();
+ final float y = event.getY();
+ if (event.getActionMasked() != MotionEvent.ACTION_HOVER_EXIT
+ && x >= 0 && x < mView.getWidth() && y >= 0 && y < mView.getHeight()) {
+ int pointerShape = mView.getPointerShape(event, x, y);
+ if (pointerShape == PointerIcon.STYLE_NOT_SPECIFIED) {
+ pointerShape = PointerIcon.STYLE_DEFAULT;
+ }
+
+ if (mPointerIconShape != pointerShape) {
+ mPointerIconShape = pointerShape;
+ final InputDevice inputDevice = event.getDevice();
+ if (inputDevice != null) {
+ inputDevice.setPointerShape(pointerShape);
+ }
+ }
+ } else if (event.getActionMasked() == MotionEvent.ACTION_HOVER_MOVE) {
+ mPointerIconShape = PointerIcon.STYLE_NOT_SPECIFIED;
+ }
+ }
+
mAttachInfo.mUnbufferedDispatchRequested = false;
boolean handled = mView.dispatchPointerEvent(event);
if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) {
@@ -5614,18 +5655,19 @@
public void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets,
Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
- Configuration newConfig) {
+ Configuration newConfig, Rect backDropFrame) {
if (DEBUG_LAYOUT) Log.v(TAG, "Resizing " + this + ": frame=" + frame.toShortString()
+ " contentInsets=" + contentInsets.toShortString()
+ " visibleInsets=" + visibleInsets.toShortString()
- + " reportDraw=" + reportDraw);
+ + " reportDraw=" + reportDraw
+ + " backDropFrame=" + backDropFrame);
// Tell all listeners that we are resizing the window so that the chrome can get
// updated as fast as possible on a separate thread,
if (mDragResizing) {
synchronized (mWindowCallbacks) {
for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
- mWindowCallbacks.get(i).onWindowSizeIsChanging(frame);
+ mWindowCallbacks.get(i).onWindowSizeIsChanging(backDropFrame);
}
}
}
@@ -5646,6 +5688,7 @@
args.arg5 = sameProcessCall ? new Rect(overscanInsets) : overscanInsets;
args.arg6 = sameProcessCall ? new Rect(stableInsets) : stableInsets;
args.arg7 = sameProcessCall ? new Rect(outsets) : outsets;
+ args.arg8 = sameProcessCall ? new Rect(backDropFrame) : backDropFrame;
msg.obj = args;
mHandler.sendMessage(msg);
}
@@ -6574,6 +6617,20 @@
}
}
+ long getNextFrameNumber() {
+ long frameNumber = -1;
+ if (mSurfaceHolder != null) {
+ mSurfaceHolder.mSurfaceLock.lock();
+ }
+ if (mSurface.isValid()) {
+ frameNumber = mSurface.getNextFrameNumber();
+ }
+ if (mSurfaceHolder != null) {
+ mSurfaceHolder.mSurfaceLock.unlock();
+ }
+ return frameNumber;
+ }
+
class TakenSurfaceHolder extends BaseSurfaceHolder {
@Override
public boolean onAllowLockCanvas() {
@@ -6630,11 +6687,11 @@
@Override
public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
- Configuration newConfig) {
+ Configuration newConfig, Rect backDropFrame) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.dispatchResized(frame, overscanInsets, contentInsets,
- visibleInsets, stableInsets, outsets, reportDraw, newConfig);
+ visibleInsets, stableInsets, outsets, reportDraw, newConfig, backDropFrame);
}
}
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 13544851..8259372 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -552,8 +552,8 @@
/**
* Called to move the window and its activity/task to a different stack container.
* For example, a window can move between
- * {@link android.app.ActivityManager#FULLSCREEN_WORKSPACE_STACK_ID} stack and
- * {@link android.app.ActivityManager#FREEFORM_WORKSPACE_STACK_ID} stack.
+ * {@link android.app.ActivityManager.StackId#FULLSCREEN_WORKSPACE_STACK_ID} stack and
+ * {@link android.app.ActivityManager.StackId#FREEFORM_WORKSPACE_STACK_ID} stack.
*
* @param stackId stack Id to change to.
*/
@@ -666,7 +666,7 @@
void adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp) {
CharSequence curTitle = wp.getTitle();
if (wp.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
- wp.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
+ wp.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
if (wp.token == null) {
View decor = peekDecorView();
if (decor != null) {
@@ -674,24 +674,38 @@
}
}
if (curTitle == null || curTitle.length() == 0) {
- String title;
+ final StringBuilder title = new StringBuilder(32);
if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA) {
- title="Media";
+ title.append("Media");
} else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY) {
- title="MediaOvr";
+ title.append("MediaOvr");
} else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
- title="Panel";
+ title.append("Panel");
} else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL) {
- title="SubPanel";
+ title.append("SubPanel");
} else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL) {
- title="AboveSubPanel";
+ title.append("AboveSubPanel");
} else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG) {
- title="AtchDlg";
+ title.append("AtchDlg");
} else {
- title=Integer.toString(wp.type);
+ title.append(wp.type);
}
if (mAppName != null) {
- title += ":" + mAppName;
+ title.append(":").append(mAppName);
+ }
+ wp.setTitle(title);
+ }
+ } else if (wp.type >= WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW &&
+ wp.type <= WindowManager.LayoutParams.LAST_SYSTEM_WINDOW) {
+ // We don't set the app token to this system window because the life cycles should be
+ // independent. If an app creates a system window and then the app goes to the stopped
+ // state, the system window should not be affected (can still show and receive input
+ // events).
+ if (curTitle == null || curTitle.length() == 0) {
+ final StringBuilder title = new StringBuilder(32);
+ title.append("Sys").append(wp.type);
+ if (mAppName != null) {
+ title.append(":").append(mAppName);
}
wp.setTitle(title);
}
@@ -1142,6 +1156,27 @@
public abstract void setContentView(View view);
/**
+ * Install a view in the decoration (title) area, to be shown when
+ * the window is in multi-window mode. This view will be placed
+ * next to the window controls.
+ *
+ * The view may be restored to defaults by passing null.
+ *
+ * @param view The desired view to display in window decorations.
+ */
+ public abstract void setDecorView(View view);
+
+ /**
+ * Convenience for
+ * {@link #setDecorView(View)}
+ * to set the custom window decoration from a layout resource. The layout will be inflated
+ * adding all top level views to the decoration
+ *
+ * @param layoutResID Resource ID to be inflated.
+ */
+ public abstract void setDecorView(@LayoutRes int layoutResID);
+
+ /**
* 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 hierarchy.
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 92e473d..edf4297 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -50,6 +50,18 @@
* @see android.content.Context#WINDOW_SERVICE
*/
public interface WindowManager extends ViewManager {
+
+ /** @hide */
+ int DOCKED_INVALID = -1;
+ /** @hide */
+ int DOCKED_LEFT = 1;
+ /** @hide */
+ int DOCKED_TOP = 2;
+ /** @hide */
+ int DOCKED_RIGHT = 3;
+ /** @hide */
+ int DOCKED_BOTTOM = 4;
+
/**
* Exception that is thrown when trying to add view whose
* {@link LayoutParams} {@link LayoutParams#token}
@@ -1136,6 +1148,16 @@
public static final int PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT = 0x00001000;
/**
+ * Flag indicating that the x, y, width, and height members should be
+ * ignored (and thus their previous value preserved). For example
+ * because they are being managed externally through repositionChild.
+ *
+ * {@hide}
+ */
+ public static final int PRIVATE_FLAG_PRESERVE_GEOMETRY = 0x00002000;
+
+
+ /**
* Control flags that are private to the platform.
* @hide
*/
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index ab99b9e..43d643e 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -71,7 +71,7 @@
/**
* The window is being resized by dragging one of the window corners,
- * in this case the surface would be fullsreen-sized. The client should
+ * in this case the surface would be fullscreen-sized. The client should
* render to the actual frame location (instead of (0,curScrollY)).
*/
public static final int RELAYOUT_RES_DRAG_RESIZING = 0x8;
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index aaf6052..7e45bbb 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -22,6 +22,7 @@
import android.content.pm.ActivityInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
+import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Bundle;
@@ -38,7 +39,7 @@
* instance of it is created by the window manager when it starts up, and allows
* customization of window layering, special window types, key dispatching, and
* layout.
- *
+ *
* <p>Because this provides deep interaction with the system window manager,
* specific methods on this interface can be called from a variety of contexts
* with various restrictions on what they can do. These are encoded through
@@ -47,9 +48,9 @@
* is attached to a method, then it is not called with any locks and may be
* called from the main window manager thread or another thread calling into
* the window manager.
- *
+ *
* <p>The current suffixes are:
- *
+ *
* <dl>
* <dt> Ti <dd> Called from the input thread. This is the thread that
* collects pending input events and dispatches them to the appropriate window.
@@ -71,7 +72,7 @@
* acquired by the window manager while it holds the window lock, so this is
* even more restrictive than <var>Lw</var>.
* </dl>
- *
+ *
* @hide
*/
public interface WindowManagerPolicy {
@@ -138,7 +139,7 @@
* Perform standard frame computation. The result can be obtained with
* getFrame() if so desired. Must be called with the window manager
* lock held.
- *
+ *
* @param parentFrame The frame of the parent container this window
* is in, used for computing its basic position.
* @param displayFrame The frame of the overall display in which this
@@ -168,18 +169,18 @@
/**
* Retrieve the current frame of the window that has been assigned by
* the window manager. Must be called with the window manager lock held.
- *
+ *
* @return Rect The rectangle holding the window frame.
*/
public Rect getFrameLw();
/**
- * Retrieve the current frame of the window that is actually shown.
+ * Retrieve the current position of the window that is actually shown.
* Must be called with the window manager lock held.
- *
- * @return Rect The rectangle holding the shown window frame.
+ *
+ * @return Point The point holding the shown window position.
*/
- public RectF getShownFrameLw();
+ public Point getShownPositionLw();
/**
* Retrieve the frame of the display that this window was last
@@ -206,7 +207,7 @@
* account for screen decorations such as a status bar or soft
* keyboard. Must be called with the
* window manager lock held.
- *
+ *
* @return Rect The rectangle holding the content frame.
*/
public Rect getContentFrameLw();
@@ -218,7 +219,7 @@
* content frame to account for transient UI elements blocking it
* such as an input method's candidates UI. Must be called with the
* window manager lock held.
- *
+ *
* @return Rect The rectangle holding the visible frame.
*/
public Rect getVisibleFrameLw();
@@ -229,12 +230,12 @@
* layout of other windows.
*/
public boolean getGivenInsetsPendingLw();
-
+
/**
* Retrieve the insets given by this window's client for the content
* area of windows behind it. Must be called with the
* window manager lock held.
- *
+ *
* @return Rect The left, top, right, and bottom insets, relative
* to the window's frame, of the actual contents.
*/
@@ -244,7 +245,7 @@
* Retrieve the insets given by this window's client for the visible
* area of windows behind it. Must be called with the
* window manager lock held.
- *
+ *
* @return Rect The left, top, right, and bottom insets, relative
* to the window's frame, of the actual visible area.
*/
@@ -252,7 +253,7 @@
/**
* Retrieve the current LayoutParams of the window.
- *
+ *
* @return WindowManager.LayoutParams The window's internal LayoutParams
* instance.
*/
@@ -298,12 +299,12 @@
public boolean isVoiceInteraction();
/**
- * Return true if, at any point, the application token associated with
- * this window has actually displayed any windows. This is most useful
- * with the "starting up" window to determine if any windows were
- * displayed when it is closed.
- *
- * @return Returns true if one or more windows have been displayed,
+ * Return true if, at any point, the application token associated with
+ * this window has actually displayed any windows. This is most useful
+ * with the "starting up" window to determine if any windows were
+ * displayed when it is closed.
+ *
+ * @return Returns true if one or more windows have been displayed,
* else false.
*/
public boolean hasAppShownWindows();
@@ -314,17 +315,17 @@
* that will remove the surface.
*/
boolean isVisibleLw();
-
+
/**
* Like {@link #isVisibleLw}, but also counts a window that is currently
* "hidden" behind the keyguard as visible. This allows us to apply
* things like window flags that impact the keyguard.
*/
boolean isVisibleOrBehindKeyguardLw();
-
+
/**
- * Is this window currently visible to the user on-screen? It is
- * displayed either if it is visible or it is currently running an
+ * Is this window currently visible to the user on-screen? It is
+ * displayed either if it is visible or it is currently running an
* animation before no longer being visible. Must be called with the
* window manager lock held.
*/
@@ -334,7 +335,7 @@
* Return true if this window (or a window it is attached to, but not
* considering its app token) is currently animating.
*/
- public boolean isAnimatingLw();
+ boolean isAnimatingLw();
/**
* Is this window considered to be gone for purposes of layout?
@@ -350,7 +351,7 @@
boolean isDrawnLw();
/**
- * Returns true if this window has been shown on screen at some time in
+ * Returns true if this window has been shown on screen at some time in
* the past. Must be called with the window manager lock held.
*/
public boolean hasDrawnLw();
@@ -362,7 +363,7 @@
* Returns true if {@link #showLw} was last called for the window.
*/
public boolean hideLw(boolean doAnimation);
-
+
/**
* Can be called to undo the effect of {@link #hideLw}, allowing a
* window to be shown as long as the window manager and client would
@@ -479,7 +480,7 @@
// NOTE: screen off reasons are in order of significance, with more
// important ones lower than less important ones.
-
+
/** Screen turned off because of a device admin */
public final int OFF_BECAUSE_OF_ADMIN = 1;
/** Screen turned off because of power button */
@@ -498,10 +499,10 @@
/** When not otherwise specified by the activity's screenOrientation, rotation is set by
* the user. */
public final int USER_ROTATION_LOCKED = 1;
-
+
/**
* Perform initialization of the policy.
- *
+ *
* @param context The system context we are running in.
*/
public void init(Context context, IWindowManager windowManager,
@@ -526,11 +527,11 @@
/**
* Check permissions when adding a window.
- *
+ *
* @param attrs The window's LayoutParams.
* @param outAppOp First element will be filled with the app op corresponding to
* this window, or OP_NONE.
- *
+ *
* @return {@link WindowManagerGlobal#ADD_OKAY} if the add can proceed;
* else an error code, usually
* {@link WindowManagerGlobal#ADD_PERMISSION_DENIED}, to abort the add.
@@ -551,21 +552,21 @@
* Sanitize the layout parameters coming from a client. Allows the policy
* to do things like ensure that windows of a specific type can't take
* input focus.
- *
+ *
* @param attrs The window layout parameters to be modified. These values
* are modified in-place.
*/
public void adjustWindowParamsLw(WindowManager.LayoutParams attrs);
-
+
/**
* After the window manager has computed the current configuration based
* on its knowledge of the display and input devices, it gives the policy
* a chance to adjust the information contained in it. If you want to
* leave it as-is, simply do nothing.
- *
+ *
* <p>This method may be called by any thread in the window manager, but
* no internal locks in the window manager will be held.
- *
+ *
* @param config The Configuration being computed, for you to change as
* desired.
* @param keyboardPresence Flags that indicate whether internal or external
@@ -575,13 +576,13 @@
*/
public void adjustConfigurationLw(Configuration config, int keyboardPresence,
int navigationPresence);
-
+
/**
* Assign a window type to a layer. Allows you to control how different
* kinds of windows are ordered on-screen.
- *
+ *
* @param type The type of window being assigned.
- *
+ *
* @return int An arbitrary integer used to order windows, with lower
* numbers below higher ones.
*/
@@ -591,9 +592,9 @@
* Return how to Z-order sub-windows in relation to the window they are
* attached to. Return positive to have them ordered in front, negative for
* behind.
- *
+ *
* @param type The sub-window type code.
- *
+ *
* @return int Layer in relation to the attached window, where positive is
* above and negative is below.
*/
@@ -669,19 +670,19 @@
* manager (using the normal window manager APIs) that will be shown until
* the application displays its own window. This is called without the
* window manager locked so that you can call back into it.
- *
+ *
* @param appToken Token of the application being started.
- * @param packageName The name of the application package being started.
+ * @param packageName The name of the application package being started.
* @param theme Resource defining the application's overall visual theme.
* @param nonLocalizedLabel The default title label of the application if
* no data is found in the resource.
* @param labelRes The resource ID the application would like to use as its name.
* @param icon The resource ID the application would like to use as its icon.
* @param windowFlags Window layout flags.
- *
+ *
* @return Optionally you can return the View that was used to create the
* window, for easy removal in removeStartingWindow.
- *
+ *
* @see #removeStartingWindow
*/
public View addStartingWindow(IBinder appToken, String packageName,
@@ -694,15 +695,15 @@
* that application. You should at this point remove the window from the
* window manager. This is called without the window manager locked so
* that you can call back into it.
- *
+ *
* <p>Note: due to the nature of these functions not being called with the
* window manager locked, you must be prepared for this function to be
* called multiple times and/or an initial time with a null View window
* even if you previously returned one.
- *
+ *
* @param appToken Token of the application that has started.
* @param window Window View that was returned by createStartingWindow.
- *
+ *
* @see #addStartingWindow
*/
public void removeStartingWindow(IBinder appToken, View window);
@@ -711,10 +712,10 @@
* Prepare for a window being added to the window manager. You can throw an
* exception here to prevent the window being added, or do whatever setup
* you need to keep track of the window.
- *
+ *
* @param win The window being added.
- * @param attrs The window's LayoutParams.
- *
+ * @param attrs The window's LayoutParams.
+ *
* @return {@link WindowManagerGlobal#ADD_OKAY} if the add can proceed, else an
* error code to abort the add.
*/
@@ -724,7 +725,7 @@
/**
* Called when a window is being removed from a window manager. Must not
* throw an exception -- clean up as much as possible.
- *
+ *
* @param win The window being removed.
*/
public void removeWindowLw(WindowState win);
@@ -733,12 +734,12 @@
* Control the animation to run when a window's state changes. Return a
* non-0 number to force the animation to a specific resource ID, or 0
* to use the default animation.
- *
+ *
* @param win The window that is changing.
* @param transit What is happening to the window: {@link #TRANSIT_ENTER},
* {@link #TRANSIT_EXIT}, {@link #TRANSIT_SHOW}, or
* {@link #TRANSIT_HIDE}.
- *
+ *
* @return Resource ID of the actual animation to use, or 0 for none.
*/
public int selectAnimationLw(WindowState win, int transit);
@@ -747,8 +748,8 @@
* Determine the animation to run for a rotation transition based on the
* top fullscreen windows {@link WindowManager.LayoutParams#rotationAnimation}
* and whether it is currently fullscreen and frontmost.
- *
- * @param anim The exiting animation resource id is stored in anim[0], the
+ *
+ * @param anim The exiting animation resource id is stored in anim[0], the
* entering animation resource id is stored in anim[1].
*/
public void selectRotationAnimationLw(int anim[]);
@@ -814,7 +815,7 @@
* <p>Allows you to define
* behavior for keys that can not be overridden by applications.
* This method is called from the input thread, with no locks held.
- *
+ *
* @param win The window that currently has focus. This is where the key
* event will normally go.
* @param event The key event.
@@ -832,7 +833,7 @@
*
* <p>Allows you to define default global behavior for keys that were not handled
* by applications. This method is called from the input thread, with no locks held.
- *
+ *
* @param win The window that currently has focus. This is where the key
* event will normally go.
* @param event The key event.
@@ -844,7 +845,7 @@
/**
* Called when layout of the windows is about to start.
- *
+ *
* @param isDefaultDisplay true if window is on {@link Display#DEFAULT_DISPLAY}.
* @param displayWidth The current full width of the screen.
* @param displayHeight The current full height of the screen.
@@ -872,7 +873,7 @@
* Called for each window attached to the window manager as layout is
* proceeding. The implementation of this function must take care of
* setting the window's frame, either here or in finishLayout().
- *
+ *
* @param win The window being positioned.
* @param attached For sub-windows, the window it is attached to; this
* window will already have had layoutWindow() called on it
@@ -880,7 +881,7 @@
*/
public void layoutWindowLw(WindowState win, WindowState attached);
-
+
/**
* Return the insets for the areas covered by system windows. These values
* are computed on the most recent layout, so they are not guaranteed to
@@ -912,10 +913,10 @@
static final int FINISH_LAYOUT_REDO_WALLPAPER = 0x0004;
/** Need to recompute animations */
static final int FINISH_LAYOUT_REDO_ANIM = 0x0008;
-
+
/**
* Called following layout of all windows before each window has policy applied.
- *
+ *
* @param displayWidth The current full width of the screen.
* @param displayHeight The current full height of the screen.
*/
@@ -936,7 +937,7 @@
* to each window. If in this function you do
* something that may have modified the animation state of another window,
* be sure to return non-zero in order to perform another pass through layout.
- *
+ *
* @return Return any bit set of {@link #FINISH_LAYOUT_REDO_LAYOUT},
* {@link #FINISH_LAYOUT_REDO_CONFIG}, {@link #FINISH_LAYOUT_REDO_WALLPAPER},
* or {@link #FINISH_LAYOUT_REDO_ANIM}.
@@ -1150,7 +1151,7 @@
* the system should go into safe mode.
*/
public void setSafeMode(boolean safeMode);
-
+
/**
* Called when the system is mostly done booting.
*/
@@ -1184,14 +1185,14 @@
* this point the display is active.
*/
public void enableScreenAfterBoot();
-
+
public void setCurrentOrientationLw(@ActivityInfo.ScreenOrientation int newOrientation);
-
+
/**
* Call from application to perform haptic feedback on its window.
*/
public boolean performHapticFeedbackLw(WindowState win, int effectId, boolean always);
-
+
/**
* Called when we have started keeping the screen on because a window
* requesting this has become visible.
@@ -1205,21 +1206,21 @@
public void keepScreenOnStoppedLw();
/**
- * Gets the current user rotation mode.
+ * Gets the current user rotation mode.
*
* @return The rotation mode.
*
* @see WindowManagerPolicy#USER_ROTATION_LOCKED
- * @see WindowManagerPolicy#USER_ROTATION_FREE
+ * @see WindowManagerPolicy#USER_ROTATION_FREE
*/
@UserRotationMode
public int getUserRotationMode();
/**
- * Inform the policy that the user has chosen a preferred orientation ("rotation lock").
+ * Inform the policy that the user has chosen a preferred orientation ("rotation lock").
*
* @param mode One of {@link WindowManagerPolicy#USER_ROTATION_LOCKED} or
- * {@link WindowManagerPolicy#USER_ROTATION_FREE}.
+ * {@link WindowManagerPolicy#USER_ROTATION_FREE}.
* @param rotation One of {@link Surface#ROTATION_0}, {@link Surface#ROTATION_90},
* {@link Surface#ROTATION_180}, {@link Surface#ROTATION_270}.
*/
diff --git a/core/java/android/view/accessibility/AccessibilityCache.java b/core/java/android/view/accessibility/AccessibilityCache.java
index d0dde00..1da305f 100644
--- a/core/java/android/view/accessibility/AccessibilityCache.java
+++ b/core/java/android/view/accessibility/AccessibilityCache.java
@@ -43,6 +43,8 @@
private long mAccessibilityFocus = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
private long mInputFocus = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
+ private boolean mIsAllWindowsCached;
+
private final SparseArray<AccessibilityWindowInfo> mWindowCache =
new SparseArray<>();
@@ -52,6 +54,24 @@
private final SparseArray<AccessibilityWindowInfo> mTempWindowArray =
new SparseArray<>();
+ public void setWindows(List<AccessibilityWindowInfo> windows) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Set windows");
+ }
+ clearWindowCache();
+ if (windows == null) {
+ return;
+ }
+ final int windowCount = windows.size();
+ for (int i = 0; i < windowCount; i++) {
+ final AccessibilityWindowInfo window = windows.get(i);
+ addWindow(window);
+ }
+ mIsAllWindowsCached = true;
+ }
+ }
+
public void addWindow(AccessibilityWindowInfo window) {
synchronized (mLock) {
if (DEBUG) {
@@ -186,6 +206,10 @@
public List<AccessibilityWindowInfo> getWindows() {
synchronized (mLock) {
+ if (!mIsAllWindowsCached) {
+ return null;
+ }
+
final int windowCount = mWindowCache.size();
if (windowCount > 0) {
// Careful to return the windows in a decreasing layer order.
@@ -280,12 +304,7 @@
if (DEBUG) {
Log.i(LOG_TAG, "clear()");
}
- final int windowCount = mWindowCache.size();
- for (int i = windowCount - 1; i >= 0; i--) {
- AccessibilityWindowInfo window = mWindowCache.valueAt(i);
- window.recycle();
- mWindowCache.removeAt(i);
- }
+ clearWindowCache();
final int nodesForWindowCount = mNodeCache.size();
for (int i = 0; i < nodesForWindowCount; i++) {
final int windowId = mNodeCache.keyAt(i);
@@ -297,6 +316,16 @@
}
}
+ private void clearWindowCache() {
+ final int windowCount = mWindowCache.size();
+ for (int i = windowCount - 1; i >= 0; i--) {
+ AccessibilityWindowInfo window = mWindowCache.valueAt(i);
+ window.recycle();
+ mWindowCache.removeAt(i);
+ }
+ mIsAllWindowsCached = false;
+ }
+
private void clearNodesForWindowLocked(int windowId) {
if (DEBUG) {
Log.i(LOG_TAG, "clearNodesForWindowLocked(" + windowId + ")");
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index b49cbc6..1406fbd 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -228,11 +228,7 @@
windows = connection.getWindows();
Binder.restoreCallingIdentity(identityToken);
if (windows != null) {
- final int windowCount = windows.size();
- for (int i = 0; i < windowCount; i++) {
- AccessibilityWindowInfo window = windows.get(i);
- sAccessibilityCache.addWindow(window);
- }
+ sAccessibilityCache.setWindows(windows);
return windows;
}
} else {
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 1b17736..1735e1b 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -3083,8 +3083,32 @@
return "ACTION_PASTE";
case ACTION_SET_SELECTION:
return "ACTION_SET_SELECTION";
+ case ACTION_EXPAND:
+ return "ACTION_EXPAND";
+ case ACTION_COLLAPSE:
+ return "ACTION_COLLAPSE";
+ case ACTION_DISMISS:
+ return "ACTION_DISMISS";
+ case ACTION_SET_TEXT:
+ return "ACTION_SET_TEXT";
+ case R.id.accessibilityActionShowOnScreen:
+ return "ACTION_SHOW_ON_SCREEN";
+ case R.id.accessibilityActionScrollToPosition:
+ return "ACTION_SCROLL_TO_POSITION";
+ case R.id.accessibilityActionScrollUp:
+ return "ACTION_SCROLL_UP";
+ case R.id.accessibilityActionScrollLeft:
+ return "ACTION_SCROLL_LEFT";
+ case R.id.accessibilityActionScrollDown:
+ return "ACTION_SCROLL_DOWN";
+ case R.id.accessibilityActionScrollRight:
+ return "ACTION_SCROLL_RIGHT";
+ case R.id.accessibilityActionSetProgress:
+ return "ACTION_SET_PROGRESS";
+ case R.id.accessibilityActionContextClick:
+ return "ACTION_CONTEXT_CLICK";
default:
- return"ACTION_UNKNOWN";
+ return "ACTION_UNKNOWN";
}
}
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 11d0f4a..4ac547a 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -429,7 +429,8 @@
public void dump(Printer pw, String prefix) {
pw.println(prefix + "mId=" + mId
- + " mSettingsActivityName=" + mSettingsActivityName);
+ + " mSettingsActivityName=" + mSettingsActivityName
+ + " mSupportsSwitchingToNextInputMethod=" + mSupportsSwitchingToNextInputMethod);
pw.println(prefix + "mIsDefaultResId=0x"
+ Integer.toHexString(mIsDefaultResId));
pw.println(prefix + "Service:");
diff --git a/core/java/android/webkit/WebResourceRequest.java b/core/java/android/webkit/WebResourceRequest.java
index 0760d2b..ab93505 100644
--- a/core/java/android/webkit/WebResourceRequest.java
+++ b/core/java/android/webkit/WebResourceRequest.java
@@ -40,6 +40,13 @@
boolean isForMainFrame();
/**
+ * Gets whether the request was a result of a server-side redirect.
+ *
+ * @return whether the request was a result of a server-side redirect.
+ */
+ boolean isRedirect();
+
+ /**
* Gets whether a gesture (such as a click) was associated with the request.
* For security reasons in certain situations this method may return false even though the
* sequence of events which caused the request to be created was initiated by a user gesture.
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index f62b4cc..9442226 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -1363,4 +1363,50 @@
* offscreen but attached to a window.
*/
public abstract boolean getOffscreenPreRaster();
+
+ /**
+ * Disables the action mode menu items according to {@code menuItems} flag.
+ * @param menuItems an integer field flag for the menu items to be disabled.
+ */
+ public abstract void setDisabledActionModeMenuItems(int menuItems);
+
+ /**
+ * Gets the action mode menu items that are disabled, expressed in an integer field flag.
+ * The default value is {@link #MENU_ITEM_NONE}
+ *
+ * @return all the disabled menu item flags combined with OR.
+ */
+ public abstract int getDisabledActionModeMenuItems();
+
+ /**
+ * Used with {@link #setDisabledActionModeMenuItems}.
+ *
+ * No menu items should be disabled.
+ */
+ public static final int MENU_ITEM_NONE = 0;
+
+ /**
+ * Used with {@link #setDisabledActionModeMenuItems}.
+ *
+ * Disable menu item "Share".
+ */
+ public static final int MENU_ITEM_SHARE = 1 << 0;
+
+ /**
+ * Used with {@link #setDisabledActionModeMenuItems}.
+ *
+ * Disable menu item "Web Search".
+ */
+ public static final int MENU_ITEM_WEB_SEARCH = 1 << 1;
+
+ /**
+ * Used with {@link #setDisabledActionModeMenuItems}.
+ *
+ * Disable all the action mode menu items for text processing.
+ * By default WebView searches for activities that are able to handle
+ * {@link android.content.Intent#ACTION_PROCESS_TEXT} and show them in the
+ * action mode menu. If this flag is set via {@link
+ * #setDisabledActionModeMenuItems}, these menu items will be disabled.
+ */
+ public static final int MENU_ITEM_PROCESS_TEXT = 1 << 2;
}
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index f2bb55a..0e5034d 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -38,12 +38,42 @@
* @param url The url to be loaded.
* @return True if the host application wants to leave the current WebView
* and handle the url itself, otherwise return false.
+ * @deprecated Use {@link #shouldOverrideUrlLoading(WebView, WebResourceRequest)
+ * shouldOverrideUrlLoading(WebView, WebResourceRequest)} instead.
*/
+ @Deprecated
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return false;
}
/**
+ * Give the host application a chance to take over the control when a new
+ * url is about to be loaded in the current WebView. If WebViewClient is not
+ * provided, by default WebView will ask Activity Manager to choose the
+ * proper handler for the url. If WebViewClient is provided, return true
+ * means the host application handles the url, while return false means the
+ * current WebView handles the url.
+ *
+ * <p>Notes:
+ * <ul>
+ * <li>This method is not called for requests using the POST "method".</li>
+ * <li>This method is also called for subframes with non-http schemes, thus it is
+ * strongly disadvised to unconditionally call {@link WebView#loadUrl(String)}
+ * with the request's url from inside the method and then return true,
+ * as this will make WebView to attempt loading a non-http url, and thus fail.</li>
+ * </ul>
+ * </p>
+ *
+ * @param view The WebView that is initiating the callback.
+ * @param request Object containing the details of the request.
+ * @return True if the host application wants to leave the current WebView
+ * and handle the url itself, otherwise return false.
+ */
+ public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
+ return shouldOverrideUrlLoading(view, request.getUrl().toString());
+ }
+
+ /**
* Notify the host application that a page has started loading. This method
* is called once for each main frame load so a page with iframes or
* framesets will call onPageStarted one time for the main frame. This also
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 584deff..229011d 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -96,27 +96,49 @@
public MissingWebViewPackageException(Exception e) { super(e); }
}
- public static String getWebViewPackageName() {
- return AppGlobals.getInitialApplication().getString(
- com.android.internal.R.string.config_webViewPackageName);
+ /** @hide */
+ public static String[] getWebViewPackageNames() {
+ return AppGlobals.getInitialApplication().getResources().getStringArray(
+ com.android.internal.R.array.config_webViewPackageNames);
}
- private static PackageInfo fetchPackageInfo() {
+ // TODO (gsennton) remove when committing webview xts test change
+ public static String getWebViewPackageName() {
+ String[] webViewPackageNames = getWebViewPackageNames();
+ return webViewPackageNames[webViewPackageNames.length-1];
+ }
+
+ /**
+ * Return the package info of the first package in the webview priority list that contains
+ * webview.
+ *
+ * @hide
+ */
+ public static PackageInfo findPreferredWebViewPackage() {
PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
- try {
- return pm.getPackageInfo(getWebViewPackageName(), PackageManager.GET_META_DATA);
- } catch (PackageManager.NameNotFoundException e) {
- throw new MissingWebViewPackageException(e);
+
+ for (String packageName : getWebViewPackageNames()) {
+ try {
+ PackageInfo packageInfo = pm.getPackageInfo(packageName,
+ PackageManager.GET_META_DATA);
+ ApplicationInfo applicationInfo = packageInfo.applicationInfo;
+
+ // If the correct flag is set the package contains webview.
+ if (getWebViewLibrary(applicationInfo) != null) {
+ return packageInfo;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ }
}
+ throw new MissingWebViewPackageException("Could not find a loadable WebView package");
}
// throws MissingWebViewPackageException
private static ApplicationInfo getWebViewApplicationInfo() {
- if (sPackageInfo == null) {
- return fetchPackageInfo().applicationInfo;
- } else {
+ if (sPackageInfo == null)
+ return findPreferredWebViewPackage().applicationInfo;
+ else
return sPackageInfo.applicationInfo;
- }
}
private static String getWebViewLibrary(ApplicationInfo ai) {
@@ -134,7 +156,12 @@
* name is the same as the one providing the webview.
*/
public static int loadWebViewNativeLibraryFromPackage(String packageName) {
- sPackageInfo = fetchPackageInfo();
+ try {
+ sPackageInfo = findPreferredWebViewPackage();
+ } catch (MissingWebViewPackageException e) {
+ return LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES;
+ }
+
if (packageName != null && packageName.equals(sPackageInfo.packageName)) {
return loadNativeLibrary();
}
@@ -180,7 +207,7 @@
private static Class<WebViewFactoryProvider> getProviderClass() {
try {
// First fetch the package info so we can log the webview package version.
- sPackageInfo = fetchPackageInfo();
+ sPackageInfo = findPreferredWebViewPackage();
Log.i(LOGTAG, "Loading " + sPackageInfo.packageName + " version " +
sPackageInfo.versionName + " (code " + sPackageInfo.versionCode + ")");
@@ -221,7 +248,7 @@
Context webViewContext = initialApplication.createPackageContext(
sPackageInfo.packageName,
Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
- initialApplication.getAssets().addAssetPath(
+ initialApplication.getAssets().addAssetPathAsSharedLibrary(
webViewContext.getApplicationInfo().sourceDir);
ClassLoader clazzLoader = webViewContext.getClassLoader();
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "Class.forName()");
diff --git a/core/java/android/widget/ActionMenuPresenter.java b/core/java/android/widget/ActionMenuPresenter.java
index 2dd84e3..5eea252 100644
--- a/core/java/android/widget/ActionMenuPresenter.java
+++ b/core/java/android/widget/ActionMenuPresenter.java
@@ -788,7 +788,7 @@
// Not a submenu, but treat it like one.
super.onSubMenuSelected(null);
} else {
- mMenu.close(false);
+ mMenu.close(false /* closeAllMenus */);
}
}
@@ -981,7 +981,7 @@
@Override
public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
if (menu instanceof SubMenuBuilder) {
- ((SubMenuBuilder) menu).getRootMenu().close(false);
+ menu.getRootMenu().close(false /* closeAllMenus */);
}
final Callback cb = getCallback();
if (cb != null) {
diff --git a/core/java/android/widget/AdapterViewFlipper.java b/core/java/android/widget/AdapterViewFlipper.java
index a105b40..18d7470 100644
--- a/core/java/android/widget/AdapterViewFlipper.java
+++ b/core/java/android/widget/AdapterViewFlipper.java
@@ -21,7 +21,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.TypedArray;
-import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
@@ -112,7 +111,7 @@
// home screen. Therefore, we register the receiver as the current
// user not the one the context is for.
getContext().registerReceiverAsUser(mReceiver, android.os.Process.myUserHandle(),
- filter, null, mHandler);
+ filter, null, getHandler());
if (mAutoStart) {
@@ -194,9 +193,8 @@
// if the flipper is currently flipping automatically, and showNext() is called
// we should we should make sure to reset the timer
if (mRunning) {
- mHandler.removeMessages(FLIP_MSG);
- Message msg = mHandler.obtainMessage(FLIP_MSG);
- mHandler.sendMessageDelayed(msg, mFlipInterval);
+ removeCallbacks(mFlipRunnable);
+ postDelayed(mFlipRunnable, mFlipInterval);
}
super.showNext();
}
@@ -210,9 +208,8 @@
// if the flipper is currently flipping automatically, and showPrevious() is called
// we should we should make sure to reset the timer
if (mRunning) {
- mHandler.removeMessages(FLIP_MSG);
- Message msg = mHandler.obtainMessage(FLIP_MSG);
- mHandler.sendMessageDelayed(msg, mFlipInterval);
+ removeCallbacks(mFlipRunnable);
+ postDelayed(mFlipRunnable, mFlipInterval);
}
super.showPrevious();
}
@@ -241,10 +238,9 @@
if (running != mRunning) {
if (running) {
showOnly(mWhichChild, flipNow);
- Message msg = mHandler.obtainMessage(FLIP_MSG);
- mHandler.sendMessageDelayed(msg, mFlipInterval);
+ postDelayed(mFlipRunnable, mFlipInterval);
} else {
- mHandler.removeMessages(FLIP_MSG);
+ removeCallbacks(mFlipRunnable);
}
mRunning = running;
}
@@ -277,15 +273,11 @@
return mAutoStart;
}
- private final int FLIP_MSG = 1;
-
- private final Handler mHandler = new Handler() {
+ private final Runnable mFlipRunnable = new Runnable() {
@Override
- public void handleMessage(Message msg) {
- if (msg.what == FLIP_MSG) {
- if (mRunning) {
- showNext();
- }
+ public void run() {
+ if (mRunning) {
+ showNext();
}
}
};
diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java
index 45eee34..7f5e2133 100644
--- a/core/java/android/widget/AnalogClock.java
+++ b/core/java/android/widget/AnalogClock.java
@@ -56,7 +56,6 @@
private boolean mAttached;
- private final Handler mHandler = new Handler();
private float mMinutes;
private float mHour;
private boolean mChanged;
@@ -121,7 +120,7 @@
// home screen. Therefore, we register the receiver as the current
// user not the one the context is for.
getContext().registerReceiverAsUser(mIntentReceiver,
- android.os.Process.myUserHandle(), filter, null, mHandler);
+ android.os.Process.myUserHandle(), filter, null, getHandler());
}
// NOTE: It's safe to do these after registering the receiver since the receiver always runs
diff --git a/core/java/android/widget/Chronometer.java b/core/java/android/widget/Chronometer.java
index ebb54ff..4d707e3 100644
--- a/core/java/android/widget/Chronometer.java
+++ b/core/java/android/widget/Chronometer.java
@@ -73,8 +73,6 @@
private OnChronometerTickListener mOnChronometerTickListener;
private StringBuilder mRecycle = new StringBuilder(8);
- private static final int TICK_WHAT = 2;
-
/**
* Initialize this Chronometer object.
* Sets the base to the current time.
@@ -259,20 +257,21 @@
if (running) {
updateText(SystemClock.elapsedRealtime());
dispatchChronometerTick();
- mHandler.sendMessageDelayed(Message.obtain(mHandler, TICK_WHAT), 1000);
+ postDelayed(mTickRunnable, 1000);
} else {
- mHandler.removeMessages(TICK_WHAT);
+ removeCallbacks(mTickRunnable);
}
mRunning = running;
}
}
-
- private Handler mHandler = new Handler() {
- public void handleMessage(Message m) {
+
+ private final Runnable mTickRunnable = new Runnable() {
+ @Override
+ public void run() {
if (mRunning) {
updateText(SystemClock.elapsedRealtime());
dispatchChronometerTick();
- sendMessageDelayed(Message.obtain(this, TICK_WHAT), 1000);
+ postDelayed(mTickRunnable, 1000);
}
}
};
diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java
index 7e542c9..cbb6109 100755
--- a/core/java/android/widget/DatePickerCalendarDelegate.java
+++ b/core/java/android/widget/DatePickerCalendarDelegate.java
@@ -171,7 +171,7 @@
// Set up year picker view.
mYearPickerView = (YearPickerView) mAnimator.findViewById(R.id.date_picker_year_picker);
mYearPickerView.setRange(mMinDate, mMaxDate);
- mYearPickerView.setDate(mCurrentDate.getTimeInMillis());
+ mYearPickerView.setYear(mCurrentDate.get(Calendar.YEAR));
mYearPickerView.setOnYearSelectedListener(mOnYearSelectedListener);
// Set up content descriptions.
@@ -267,6 +267,9 @@
// Automatically switch to day picker.
setCurrentView(VIEW_MONTH_DAY);
+
+ // Switch focus back to the year text.
+ mHeaderYear.requestFocus();
}
};
@@ -344,7 +347,18 @@
mAnimator.announceForAccessibility(mSelectDay);
break;
case VIEW_YEAR:
- mYearPickerView.setDate(mCurrentDate.getTimeInMillis());
+ final int year = mCurrentDate.get(Calendar.YEAR);
+ mYearPickerView.setYear(year);
+ mYearPickerView.post(new Runnable() {
+ @Override
+ public void run() {
+ mYearPickerView.requestFocus();
+ final View selected = mYearPickerView.getSelectedView();
+ if (selected != null) {
+ selected.requestFocus();
+ }
+ }
+ });
if (mCurrentView != viewIndex) {
mHeaderMonthDay.setActivated(false);
diff --git a/core/java/android/widget/DayPickerPagerAdapter.java b/core/java/android/widget/DayPickerPagerAdapter.java
index 8fe8252..f5840dc 100644
--- a/core/java/android/widget/DayPickerPagerAdapter.java
+++ b/core/java/android/widget/DayPickerPagerAdapter.java
@@ -287,6 +287,14 @@
return null;
}
+ SimpleMonthView getView(Object object) {
+ if (object == null) {
+ return null;
+ }
+ final ViewHolder holder = (ViewHolder) object;
+ return holder.calendar;
+ }
+
private final OnDayClickListener mOnDayClickListener = new OnDayClickListener() {
@Override
public void onDayClick(SimpleMonthView view, Calendar day) {
diff --git a/core/java/android/widget/DayPickerViewPager.java b/core/java/android/widget/DayPickerViewPager.java
index bb6e3a4..5f0ae29 100644
--- a/core/java/android/widget/DayPickerViewPager.java
+++ b/core/java/android/widget/DayPickerViewPager.java
@@ -16,12 +16,18 @@
package android.widget;
-import com.android.internal.widget.ViewPager;
-
+import android.annotation.Nullable;
import android.content.Context;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+
+import com.android.internal.util.Predicate;
+import com.android.internal.widget.PagerAdapter;
+import com.android.internal.widget.ViewPager;
import java.util.ArrayList;
@@ -134,4 +140,37 @@
mMatchParentChildren.clear();
}
+
+ @Override
+ protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) {
+ if (predicate.apply(this)) {
+ return this;
+ }
+
+ // Always try the selected view first.
+ final DayPickerPagerAdapter adapter = (DayPickerPagerAdapter) getAdapter();
+ final SimpleMonthView current = adapter.getView(getCurrent());
+ if (current != childToSkip && current != null) {
+ final View v = current.findViewByPredicate(predicate);
+ if (v != null) {
+ return v;
+ }
+ }
+
+ final int len = getChildCount();
+ for (int i = 0; i < len; i++) {
+ final View child = getChildAt(i);
+
+ if (child != childToSkip && child != current) {
+ final View v = child.findViewByPredicate(predicate);
+
+ if (v != null) {
+ return v;
+ }
+ }
+ }
+
+ return null;
+ }
+
}
diff --git a/core/java/android/widget/DigitalClock.java b/core/java/android/widget/DigitalClock.java
index 9e442f9..1df1643 100644
--- a/core/java/android/widget/DigitalClock.java
+++ b/core/java/android/widget/DigitalClock.java
@@ -60,18 +60,18 @@
if (mCalendar == null) {
mCalendar = Calendar.getInstance();
}
-
- mFormatChangeObserver = new FormatChangeObserver();
- getContext().getContentResolver().registerContentObserver(
- Settings.System.CONTENT_URI, true, mFormatChangeObserver);
-
- setFormat();
}
@Override
protected void onAttachedToWindow() {
mTickerStopped = false;
super.onAttachedToWindow();
+
+ mFormatChangeObserver = new FormatChangeObserver();
+ getContext().getContentResolver().registerContentObserver(
+ Settings.System.CONTENT_URI, true, mFormatChangeObserver);
+ setFormat();
+
mHandler = new Handler();
/**
@@ -95,6 +95,8 @@
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mTickerStopped = true;
+ getContext().getContentResolver().unregisterContentObserver(
+ mFormatChangeObserver);
}
private void setFormat() {
diff --git a/core/java/android/widget/DropDownListView.java b/core/java/android/widget/DropDownListView.java
index c869ccb..2fb2101 100644
--- a/core/java/android/widget/DropDownListView.java
+++ b/core/java/android/widget/DropDownListView.java
@@ -127,13 +127,15 @@
}
@Override
- protected boolean shouldShowSelector() {
- View selectedView = getSelectedView();
- return selectedView != null && selectedView.isEnabled() || super.shouldShowSelector();
+ boolean shouldShowSelector() {
+ return isHovered() || super.shouldShowSelector();
}
@Override
public boolean onHoverEvent(MotionEvent ev) {
+ // Allow the super class to handle hover state management first.
+ final boolean handled = super.onHoverEvent(ev);
+
final int action = ev.getActionMasked();
if (action == MotionEvent.ACTION_HOVER_ENTER
|| action == MotionEvent.ACTION_HOVER_MOVE) {
@@ -154,26 +156,11 @@
// Do not cancel the selected position if the selection is visible by other reasons.
if (!super.shouldShowSelector()) {
setSelectedPositionInt(INVALID_POSITION);
+ setNextSelectedPositionInt(INVALID_POSITION);
}
}
- return super.onHoverEvent(ev);
- }
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- final int x = (int) event.getX();
- final int y = (int) event.getY();
- final int position = pointToPosition(x, y);
- if (position == INVALID_POSITION) {
- return super.onTouchEvent(event);
- }
-
- if (position != mSelectedPosition) {
- setSelectedPositionInt(position);
- setNextSelectedPositionInt(position);
- }
-
- return super.onTouchEvent(event);
+ return handled;
}
/**
diff --git a/core/java/android/widget/EditText.java b/core/java/android/widget/EditText.java
index d21a5f7..e31bbe9 100644
--- a/core/java/android/widget/EditText.java
+++ b/core/java/android/widget/EditText.java
@@ -128,6 +128,15 @@
/** @hide */
@Override
+ public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfoInternal(info);
+ if (isEnabled()) {
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SET_TEXT);
+ }
+ }
+
+ /** @hide */
+ @Override
public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
switch (action) {
case AccessibilityNodeInfo.ACTION_SET_TEXT: {
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index b1566a7..2983053 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -16,12 +16,6 @@
package android.widget;
-import java.text.BreakIterator;
-import java.util.Arrays;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-
import android.R;
import android.annotation.Nullable;
import android.app.PendingIntent;
@@ -45,7 +39,6 @@
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
-import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.ParcelableParcel;
@@ -113,6 +106,12 @@
import com.android.internal.util.Preconditions;
import com.android.internal.widget.EditableInputConnection;
+import java.text.BreakIterator;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+
/**
* Helper class used by TextView to handle editable text views.
@@ -392,7 +391,7 @@
mTextView.removeCallbacks(mShowFloatingToolbar);
- destroyDisplayListsData();
+ discardTextDisplayLists();
if (mSpellChecker != null) {
mSpellChecker.closeSession();
@@ -408,13 +407,13 @@
mTemporaryDetach = false;
}
- private void destroyDisplayListsData() {
+ private void discardTextDisplayLists() {
if (mTextRenderNodes != null) {
for (int i = 0; i < mTextRenderNodes.length; i++) {
RenderNode displayList = mTextRenderNodes[i] != null
? mTextRenderNodes[i].renderNode : null;
if (displayList != null && displayList.isValid()) {
- displayList.destroyDisplayListData();
+ displayList.discardDisplayList();
}
}
}
@@ -1005,8 +1004,7 @@
}
if (!handled && mTextActionMode != null) {
- // TODO: Fix dragging in extracted mode.
- if (touchPositionIsInSelection() && !mTextView.isInExtractedMode()) {
+ if (touchPositionIsInSelection()) {
// Start a drag
final int start = mTextView.getSelectionStart();
final int end = mTextView.getSelectionEnd();
@@ -2072,14 +2070,14 @@
if (shouldBlink()) {
mShowCursor = SystemClock.uptimeMillis();
if (mBlink == null) mBlink = new Blink();
- mBlink.removeCallbacks(mBlink);
- mBlink.postAtTime(mBlink, mShowCursor + BLINK);
+ mTextView.removeCallbacks(mBlink);
+ mTextView.postDelayed(mBlink, BLINK);
} else {
- if (mBlink != null) mBlink.removeCallbacks(mBlink);
+ if (mBlink != null) mTextView.removeCallbacks(mBlink);
}
}
- private class Blink extends Handler implements Runnable {
+ private class Blink implements Runnable {
private boolean mCancelled;
public void run() {
@@ -2087,20 +2085,20 @@
return;
}
- removeCallbacks(Blink.this);
+ mTextView.removeCallbacks(this);
if (shouldBlink()) {
if (mTextView.getLayout() != null) {
mTextView.invalidateCursorPath();
}
- postAtTime(this, SystemClock.uptimeMillis() + BLINK);
+ mTextView.postDelayed(this, BLINK);
}
}
void cancel() {
if (!mCancelled) {
- removeCallbacks(Blink.this);
+ mTextView.removeCallbacks(this);
mCancelled = true;
}
}
@@ -4275,10 +4273,14 @@
positionAtCursorOffset(offset, false);
}
+ /**
+ * @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);
- mInWord = !getWordIteratorWithText().isBoundary(offset);
+ mInWord = (offset != -1) && !getWordIteratorWithText().isBoundary(offset);
}
@Override
@@ -4511,10 +4513,14 @@
positionAtCursorOffset(offset, false);
}
+ /**
+ * @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);
- mInWord = !getWordIteratorWithText().isBoundary(offset);
+ mInWord = (offset != -1) && !getWordIteratorWithText().isBoundary(offset);
}
@Override
@@ -4881,9 +4887,8 @@
mEndHandle.showAtLocation(endOffset);
// No longer the first dragging motion, reset.
- if (!(mTextView.isInExtractedMode())) {
- startSelectionActionMode();
- }
+ startSelectionActionMode();
+
mDragAcceleratorActive = false;
mStartOffset = -1;
mSwitchedLines = false;
diff --git a/core/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java
index b187c1c..9ebbe36 100644
--- a/core/java/android/widget/Gallery.java
+++ b/core/java/android/widget/Gallery.java
@@ -201,9 +201,6 @@
public Gallery(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
-
- mGestureDetector = new GestureDetector(context, this);
- mGestureDetector.setIsLongpressEnabled(true);
final TypedArray a = context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.Gallery, defStyleAttr, defStyleRes);
@@ -236,6 +233,16 @@
mGroupFlags |= FLAG_SUPPORT_STATIC_TRANSFORMATIONS;
}
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ if (mGestureDetector == null) {
+ mGestureDetector = new GestureDetector(getContext(), this);
+ mGestureDetector.setIsLongpressEnabled(true);
+ }
+ }
+
/**
* Whether or not to callback on any {@link #getOnItemSelectedListener()}
* while the items are being flinged. If false, only the final selected item
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 245c7332..6e90baf 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -232,6 +232,8 @@
if (w != mDrawableWidth || h != mDrawableHeight) {
mDrawableWidth = w;
mDrawableHeight = h;
+ // updates the matrix, which is dependent on the bounds
+ configureBounds();
}
}
/* we invalidate the whole view in this case because it's very
diff --git a/core/java/android/widget/MediaController.java b/core/java/android/widget/MediaController.java
index ff2f22b..8008637 100644
--- a/core/java/android/widget/MediaController.java
+++ b/core/java/android/widget/MediaController.java
@@ -20,8 +20,6 @@
import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.media.AudioManager;
-import android.os.Handler;
-import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
@@ -83,8 +81,6 @@
private boolean mShowing;
private boolean mDragging;
private static final int sDefaultTimeout = 3000;
- private static final int FADE_OUT = 1;
- private static final int SHOW_PROGRESS = 2;
private final boolean mUseFastForward;
private boolean mFromXml;
private boolean mListenersSet;
@@ -373,12 +369,11 @@
// cause the progress bar to be updated even if mShowing
// was already true. This happens, for example, if we're
// paused with the progress bar showing the user hits play.
- mHandler.sendEmptyMessage(SHOW_PROGRESS);
+ post(mShowProgress);
if (timeout != 0 && !mAccessibilityManager.isTouchExplorationEnabled()) {
- mHandler.removeMessages(FADE_OUT);
- Message msg = mHandler.obtainMessage(FADE_OUT);
- mHandler.sendMessageDelayed(msg, timeout);
+ removeCallbacks(mFadeOut);
+ postDelayed(mFadeOut, timeout);
}
}
@@ -395,7 +390,7 @@
if (mShowing) {
try {
- mHandler.removeMessages(SHOW_PROGRESS);
+ removeCallbacks(mShowProgress);
mWindowManager.removeView(mDecor);
} catch (IllegalArgumentException ex) {
Log.w("MediaController", "already removed");
@@ -404,21 +399,19 @@
}
}
- private final Handler mHandler = new Handler() {
+ private final Runnable mFadeOut = new Runnable() {
@Override
- public void handleMessage(Message msg) {
- int pos;
- switch (msg.what) {
- case FADE_OUT:
- hide();
- break;
- case SHOW_PROGRESS:
- pos = setProgress();
- if (!mDragging && mShowing && mPlayer.isPlaying()) {
- msg = obtainMessage(SHOW_PROGRESS);
- sendMessageDelayed(msg, 1000 - (pos % 1000));
- }
- break;
+ public void run() {
+ hide();
+ }
+ };
+
+ private final Runnable mShowProgress = new Runnable() {
+ @Override
+ public void run() {
+ int pos = setProgress();
+ if (!mDragging && mShowing && mPlayer.isPlaying()) {
+ postDelayed(mShowProgress, 1000 - (pos % 1000));
}
}
};
@@ -587,7 +580,7 @@
// the seekbar and b) once the user is done dragging the thumb
// we will post one of these messages to the queue again and
// this ensures that there will be exactly one message queued up.
- mHandler.removeMessages(SHOW_PROGRESS);
+ removeCallbacks(mShowProgress);
}
@Override
@@ -615,7 +608,7 @@
// Ensure that progress is properly updated in the future,
// the call to show() does not guarantee this because it is a
// no-op if we are already showing.
- mHandler.sendEmptyMessage(SHOW_PROGRESS);
+ post(mShowProgress);
}
};
diff --git a/core/java/android/widget/MenuItemHoverListener.java b/core/java/android/widget/MenuItemHoverListener.java
index 87c5c85..13f0e6a 100644
--- a/core/java/android/widget/MenuItemHoverListener.java
+++ b/core/java/android/widget/MenuItemHoverListener.java
@@ -2,6 +2,9 @@
import com.android.internal.view.menu.MenuBuilder;
+import android.annotation.NonNull;
+import android.view.MenuItem;
+
/**
* An interface notified when a menu item is hovered. Useful for cases when hover should trigger
* some behavior at a higher level, like managing the opening and closing of submenus.
@@ -9,5 +12,22 @@
* @hide
*/
public interface MenuItemHoverListener {
- public void onItemHovered(MenuBuilder menu, int position);
+ /**
+ * Called when hover exits a menu item.
+ * <p>
+ * If hover is moving to another item, this method will be called before
+ * {@link #onItemHoverEnter(MenuBuilder, MenuItem)} for the newly-hovered item.
+ *
+ * @param menu the item's parent menu
+ * @param item the hovered menu item
+ */
+ void onItemHoverExit(@NonNull MenuBuilder menu, @NonNull MenuItem item);
+
+ /**
+ * Called when hover enters a menu item.
+ *
+ * @param menu the item's parent menu
+ * @param item the hovered menu item
+ */
+ void onItemHoverEnter(@NonNull MenuBuilder menu, @NonNull MenuItem item);
}
diff --git a/core/java/android/widget/MenuPopupWindow.java b/core/java/android/widget/MenuPopupWindow.java
index 1fb62d0..85e26d0 100644
--- a/core/java/android/widget/MenuPopupWindow.java
+++ b/core/java/android/widget/MenuPopupWindow.java
@@ -16,12 +16,14 @@
package android.widget;
+import android.annotation.NonNull;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.transition.Transition;
import android.util.AttributeSet;
import android.view.KeyEvent;
+import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
@@ -72,10 +74,18 @@
}
@Override
- public void onItemHovered(MenuBuilder menu, int position) {
+ public void onItemHoverEnter(@NonNull MenuBuilder menu, @NonNull MenuItem item) {
// Forward up the chain
if (mHoverListener != null) {
- mHoverListener.onItemHovered(menu, position);
+ mHoverListener.onItemHoverEnter(menu, item);
+ }
+ }
+
+ @Override
+ public void onItemHoverExit(@NonNull MenuBuilder menu, @NonNull MenuItem item) {
+ // Forward up the chain
+ if (mHoverListener != null) {
+ mHoverListener.onItemHoverExit(menu, item);
}
}
@@ -87,6 +97,7 @@
final int mRetreatKey;
private MenuItemHoverListener mHoverListener;
+ private MenuItem mHoveredMenuItem;
public MenuDropDownListView(Context context, boolean hijackFocus) {
super(context, hijackFocus);
@@ -115,8 +126,7 @@
public boolean onKeyDown(int keyCode, KeyEvent event) {
ListMenuItemView selectedItem = (ListMenuItemView) getSelectedView();
if (selectedItem != null && keyCode == mAdvanceKey) {
- if (selectedItem.isEnabled() &&
- ((ListMenuItemView) selectedItem).getItemData().hasSubMenu()) {
+ if (selectedItem.isEnabled() && selectedItem.getItemData().hasSubMenu()) {
performItemClick(
selectedItem,
getSelectedItemPosition(),
@@ -127,7 +137,8 @@
setSelectedPositionInt(INVALID_POSITION);
setNextSelectedPositionInt(INVALID_POSITION);
- ((MenuAdapter) getAdapter()).getAdapterMenu().close();
+ // Close only the top-level menu.
+ ((MenuAdapter) getAdapter()).getAdapterMenu().close(false /* closeAllMenus */);
return true;
}
return super.onKeyDown(keyCode, event);
@@ -135,36 +146,49 @@
@Override
public boolean onHoverEvent(MotionEvent ev) {
- boolean dispatchHover = false;
- final int position = pointToPosition((int) ev.getX(), (int) ev.getY());
+ // Dispatch any changes in hovered item index to the listener.
+ if (mHoverListener != null) {
+ // The adapter may be wrapped. Adjust the index if necessary.
+ final int headersCount;
+ final MenuAdapter menuAdapter;
+ final ListAdapter adapter = getAdapter();
+ if (adapter instanceof HeaderViewListAdapter) {
+ final HeaderViewListAdapter headerAdapter = (HeaderViewListAdapter) adapter;
+ headersCount = headerAdapter.getHeadersCount();
+ menuAdapter = (MenuAdapter) headerAdapter.getWrappedAdapter();
+ } else {
+ headersCount = 0;
+ menuAdapter = (MenuAdapter) adapter;
+ }
- final int action = ev.getActionMasked();
- if (action == MotionEvent.ACTION_HOVER_ENTER
- || action == MotionEvent.ACTION_HOVER_MOVE) {
- if (position != INVALID_POSITION && position != mSelectedPosition) {
- final View hoveredItem = getChildAt(position - getFirstVisiblePosition());
- if (hoveredItem.isEnabled()) {
- dispatchHover = true;
+ // Find the menu item for the view at the event coordinates.
+ MenuItem menuItem = null;
+ if (ev.getAction() != MotionEvent.ACTION_HOVER_EXIT) {
+ final int position = pointToPosition((int) ev.getX(), (int) ev.getY());
+ if (position != INVALID_POSITION) {
+ final int itemPosition = position - headersCount;
+ if (itemPosition >= 0 && itemPosition < menuAdapter.getCount()) {
+ menuItem = menuAdapter.getItem(itemPosition);
+ }
+ }
+ }
+
+ final MenuItem oldMenuItem = mHoveredMenuItem;
+ if (oldMenuItem != menuItem) {
+ final MenuBuilder menu = menuAdapter.getAdapterMenu();
+ if (oldMenuItem != null) {
+ mHoverListener.onItemHoverExit(menu, oldMenuItem);
+ }
+
+ mHoveredMenuItem = menuItem;
+
+ if (menuItem != null) {
+ mHoverListener.onItemHoverEnter(menu, menuItem);
}
}
}
- boolean superVal = super.onHoverEvent(ev);
-
- if (dispatchHover && mHoverListener != null) {
- ListAdapter adapter = getAdapter();
- MenuAdapter menuAdapter;
- if (adapter instanceof HeaderViewListAdapter) {
- menuAdapter = (MenuAdapter) ((HeaderViewListAdapter) adapter)
- .getWrappedAdapter();
- } else {
- menuAdapter = (MenuAdapter) adapter;
- }
-
- mHoverListener.onItemHovered(menuAdapter.getAdapterMenu(), position);
- }
-
- return superVal;
+ return super.onHoverEvent(ev);
}
}
}
\ No newline at end of file
diff --git a/core/java/android/widget/QuickContactBadge.java b/core/java/android/widget/QuickContactBadge.java
index e241d4c..8c15cde 100644
--- a/core/java/android/widget/QuickContactBadge.java
+++ b/core/java/android/widget/QuickContactBadge.java
@@ -96,10 +96,16 @@
com.android.internal.R.styleable.Theme_quickContactBadgeOverlay);
styledAttributes.recycle();
+ setOnClickListener(this);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
if (!isInEditMode()) {
mQueryHandler = new QueryHandler(mContext.getContentResolver());
}
- setOnClickListener(this);
}
@Override
diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java
index e9325ef..6edce91 100644
--- a/core/java/android/widget/SimpleMonthView.java
+++ b/core/java/android/widget/SimpleMonthView.java
@@ -16,6 +16,7 @@
package android.widget;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
@@ -34,8 +35,10 @@
import android.util.IntArray;
import android.util.MathUtils;
import android.util.StateSet;
+import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewParent;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
@@ -61,11 +64,14 @@
private static final String DEFAULT_TITLE_FORMAT = "MMMMy";
private static final String DAY_OF_WEEK_FORMAT = "EEEEE";
+ private static final int SELECTED_HIGHLIGHT_ALPHA = 0xB0;
+
private final TextPaint mMonthPaint = new TextPaint();
private final TextPaint mDayOfWeekPaint = new TextPaint();
private final TextPaint mDayPaint = new TextPaint();
private final Paint mDaySelectorPaint = new Paint();
private final Paint mDayHighlightPaint = new Paint();
+ private final Paint mDayHighlightSelectorPaint = new Paint();
private final Calendar mCalendar = Calendar.getInstance();
private final Calendar mDayOfWeekLabelCalendar = Calendar.getInstance();
@@ -130,7 +136,9 @@
private ColorStateList mDayTextColor;
- private int mTouchedItem = -1;
+ private int mHighlightedDay = -1;
+ private int mPreviouslyHighlightedDay = -1;
+ private boolean mIsTouchHighlighted = false;
public SimpleMonthView(Context context) {
this(context, null);
@@ -268,6 +276,9 @@
mDayHighlightPaint.setAntiAlias(true);
mDayHighlightPaint.setStyle(Style.FILL);
+ mDayHighlightSelectorPaint.setAntiAlias(true);
+ mDayHighlightSelectorPaint.setStyle(Style.FILL);
+
mDayPaint.setAntiAlias(true);
mDayPaint.setTextSize(dayTextSize);
mDayPaint.setTypeface(Typeface.create(dayTypeface, 0));
@@ -296,6 +307,8 @@
final int activatedColor = dayBackgroundColor.getColorForState(
StateSet.get(StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_ACTIVATED), 0);
mDaySelectorPaint.setColor(activatedColor);
+ mDayHighlightSelectorPaint.setColor(activatedColor);
+ mDayHighlightSelectorPaint.setAlpha(SELECTED_HIGHLIGHT_ALPHA);
invalidate();
}
@@ -326,8 +339,10 @@
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
final int touchedItem = getDayAtLocation(x, y);
- if (mTouchedItem != touchedItem) {
- mTouchedItem = touchedItem;
+ mIsTouchHighlighted = true;
+ if (mHighlightedDay != touchedItem) {
+ mHighlightedDay = touchedItem;
+ mPreviouslyHighlightedDay = touchedItem;
invalidate();
}
if (action == MotionEvent.ACTION_DOWN && touchedItem < 0) {
@@ -342,7 +357,8 @@
// Fall through.
case MotionEvent.ACTION_CANCEL:
// Reset touched day on stream end.
- mTouchedItem = -1;
+ mHighlightedDay = -1;
+ mIsTouchHighlighted = false;
invalidate();
break;
}
@@ -350,6 +366,228 @@
}
@Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ // We need to handle focus change within the SimpleMonthView because we are simulating
+ // multiple Views. The arrow keys will move between days until there is no space (no
+ // day to the left, top, right, or bottom). Focus forward and back jumps out of the
+ // SimpleMonthView, skipping over other SimpleMonthViews in the parent ViewPager
+ // to the next focusable View in the hierarchy.
+ boolean focusChanged = false;
+ switch (event.getKeyCode()) {
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ if (event.hasNoModifiers()) {
+ focusChanged = moveOneDay(isLayoutRtl());
+ }
+ break;
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ if (event.hasNoModifiers()) {
+ focusChanged = moveOneDay(!isLayoutRtl());
+ }
+ break;
+ case KeyEvent.KEYCODE_DPAD_UP:
+ if (event.hasNoModifiers()) {
+ ensureFocusedDay();
+ if (mHighlightedDay > 7) {
+ mHighlightedDay -= 7;
+ focusChanged = true;
+ }
+ }
+ break;
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ if (event.hasNoModifiers()) {
+ ensureFocusedDay();
+ if (mHighlightedDay <= mDaysInMonth - 7) {
+ mHighlightedDay += 7;
+ focusChanged = true;
+ }
+ }
+ break;
+ case KeyEvent.KEYCODE_DPAD_CENTER:
+ case KeyEvent.KEYCODE_ENTER:
+ if (mHighlightedDay != -1) {
+ onDayClicked(mHighlightedDay);
+ return true;
+ }
+ break;
+ case KeyEvent.KEYCODE_TAB: {
+ int focusChangeDirection = 0;
+ if (event.hasNoModifiers()) {
+ focusChangeDirection = View.FOCUS_FORWARD;
+ } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {
+ focusChangeDirection = View.FOCUS_BACKWARD;
+ }
+ if (focusChangeDirection != 0) {
+ final ViewParent parent = getParent();
+ // move out of the ViewPager next/previous
+ View nextFocus = this;
+ do {
+ nextFocus = nextFocus.focusSearch(focusChangeDirection);
+ } while (nextFocus != null && nextFocus != this &&
+ nextFocus.getParent() == parent);
+ if (nextFocus != null) {
+ nextFocus.requestFocus();
+ return true;
+ }
+ }
+ break;
+ }
+ }
+ if (focusChanged) {
+ invalidate();
+ return true;
+ } else {
+ return super.onKeyDown(keyCode, event);
+ }
+ }
+
+ private boolean moveOneDay(boolean positive) {
+ ensureFocusedDay();
+ boolean focusChanged = false;
+ if (positive) {
+ if (!isLastDayOfWeek(mHighlightedDay) && mHighlightedDay < mDaysInMonth) {
+ mHighlightedDay++;
+ focusChanged = true;
+ }
+ } else {
+ if (!isFirstDayOfWeek(mHighlightedDay) && mHighlightedDay > 1) {
+ mHighlightedDay--;
+ focusChanged = true;
+ }
+ }
+ return focusChanged;
+ }
+
+ @Override
+ protected void onFocusChanged(boolean gainFocus, @FocusDirection int direction,
+ @Nullable Rect previouslyFocusedRect) {
+ if (gainFocus) {
+ // If we've gained focus through arrow keys, we should find the day closest
+ // to the focus rect. If we've gained focus through forward/back, we should
+ // focus on the selected day if there is one.
+ final int offset = findDayOffset();
+ switch(direction) {
+ case View.FOCUS_RIGHT: {
+ int row = findClosestRow(previouslyFocusedRect);
+ mHighlightedDay = row == 0 ? 1 : (row * DAYS_IN_WEEK) - offset + 1;
+ break;
+ }
+ case View.FOCUS_LEFT: {
+ int row = findClosestRow(previouslyFocusedRect) + 1;
+ mHighlightedDay = Math.min(mDaysInMonth, (row * DAYS_IN_WEEK) - offset);
+ break;
+ }
+ case View.FOCUS_DOWN: {
+ final int col = findClosestColumn(previouslyFocusedRect);
+ final int day = col - offset + 1;
+ mHighlightedDay = day < 1 ? day + DAYS_IN_WEEK : day;
+ break;
+ }
+ case View.FOCUS_UP: {
+ final int col = findClosestColumn(previouslyFocusedRect);
+ final int maxWeeks = (offset + mDaysInMonth) / DAYS_IN_WEEK;
+ final int day = col - offset + (DAYS_IN_WEEK * maxWeeks) + 1;
+ mHighlightedDay = day > mDaysInMonth ? day - DAYS_IN_WEEK : day;
+ break;
+ }
+ }
+ ensureFocusedDay();
+ invalidate();
+ }
+ super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+ }
+
+ /**
+ * Returns the row (0 indexed) closest to previouslyFocusedRect or center if null.
+ */
+ private int findClosestRow(@Nullable Rect previouslyFocusedRect) {
+ if (previouslyFocusedRect == null) {
+ return 3;
+ } else {
+ int centerY = previouslyFocusedRect.centerY();
+
+ final TextPaint p = mDayPaint;
+ final int headerHeight = mMonthHeight + mDayOfWeekHeight;
+ final int rowHeight = mDayHeight;
+
+ // Text is vertically centered within the row height.
+ final float halfLineHeight = (p.ascent() + p.descent()) / 2f;
+ final int rowCenter = headerHeight + rowHeight / 2;
+
+ centerY -= rowCenter - halfLineHeight;
+ int row = Math.round(centerY / (float) rowHeight);
+ final int maxDay = findDayOffset() + mDaysInMonth;
+ final int maxRows = (maxDay / DAYS_IN_WEEK) - ((maxDay % DAYS_IN_WEEK == 0) ? 1 : 0);
+
+ row = MathUtils.constrain(row, 0, maxRows);
+ return row;
+ }
+ }
+
+ /**
+ * Returns the column (0 indexed) closest to the previouslyFocusedRect or center if null.
+ * The 0 index is related to the first day of the week.
+ */
+ private int findClosestColumn(@Nullable Rect previouslyFocusedRect) {
+ if (previouslyFocusedRect == null) {
+ return DAYS_IN_WEEK / 2;
+ } else {
+ int centerX = previouslyFocusedRect.centerX() - mPaddingLeft;
+ final int columnFromLeft =
+ MathUtils.constrain(centerX / mCellWidth, 0, DAYS_IN_WEEK - 1);
+ return isLayoutRtl() ? DAYS_IN_WEEK - columnFromLeft - 1: columnFromLeft;
+ }
+ }
+
+ @Override
+ public void getFocusedRect(Rect r) {
+ if (mHighlightedDay > 0) {
+ getBoundsForDay(mHighlightedDay, r);
+ } else {
+ super.getFocusedRect(r);
+ }
+ }
+
+ @Override
+ protected void onFocusLost() {
+ if (!mIsTouchHighlighted) {
+ // Unhighlight a day.
+ mPreviouslyHighlightedDay = mHighlightedDay;
+ mHighlightedDay = -1;
+ invalidate();
+ }
+ super.onFocusLost();
+ }
+
+ /**
+ * Ensure some day is highlighted. If a day isn't highlighted, it chooses the selected day,
+ * if possible, or the first day of the month if not.
+ */
+ private void ensureFocusedDay() {
+ if (mHighlightedDay != -1) {
+ return;
+ }
+ if (mPreviouslyHighlightedDay != -1) {
+ mHighlightedDay = mPreviouslyHighlightedDay;
+ return;
+ }
+ if (mActivatedDay != -1) {
+ mHighlightedDay = mActivatedDay;
+ return;
+ }
+ mHighlightedDay = 1;
+ }
+
+ private boolean isFirstDayOfWeek(int day) {
+ final int offset = findDayOffset();
+ return (offset + day - 1) % DAYS_IN_WEEK == 0;
+ }
+
+ private boolean isLastDayOfWeek(int day) {
+ final int offset = findDayOffset();
+ return (offset + day) % DAYS_IN_WEEK == 0;
+ }
+
+ @Override
protected void onDraw(Canvas canvas) {
final int paddingLeft = getPaddingLeft();
final int paddingTop = getPaddingTop();
@@ -432,12 +670,15 @@
}
final boolean isDayActivated = mActivatedDay == day;
+ final boolean isDayHighlighted = mHighlightedDay == day;
if (isDayActivated) {
stateMask |= StateSet.VIEW_STATE_ACTIVATED;
// Adjust the circle to be centered on the row.
- canvas.drawCircle(colCenterRtl, rowCenter, mDaySelectorRadius, mDaySelectorPaint);
- } else if (mTouchedItem == day) {
+ final Paint paint = isDayHighlighted ? mDayHighlightSelectorPaint :
+ mDaySelectorPaint;
+ canvas.drawCircle(colCenterRtl, rowCenter, mDaySelectorRadius, paint);
+ } else if (isDayHighlighted) {
stateMask |= StateSet.VIEW_STATE_PRESSED;
if (isDayEnabled) {
diff --git a/core/java/android/widget/SlidingDrawer.java b/core/java/android/widget/SlidingDrawer.java
index 9c44236..a55e77d 100644
--- a/core/java/android/widget/SlidingDrawer.java
+++ b/core/java/android/widget/SlidingDrawer.java
@@ -93,7 +93,6 @@
private static final float MAXIMUM_MAJOR_VELOCITY = 200.0f;
private static final float MAXIMUM_ACCELERATION = 2000.0f;
private static final int VELOCITY_UNITS = 1000;
- private static final int MSG_ANIMATE = 1000;
private static final int ANIMATION_FRAME_DURATION = 1000 / 60;
private static final int EXPANDED_FULL_OPEN = -10001;
@@ -123,7 +122,6 @@
private OnDrawerCloseListener mOnDrawerCloseListener;
private OnDrawerScrollListener mOnDrawerScrollListener;
- private final Handler mHandler = new SlidingHandler();
private float mAnimatedAcceleration;
private float mAnimatedVelocity;
private float mAnimationPosition;
@@ -553,8 +551,8 @@
mAnimationLastTime = now;
mCurrentAnimationTime = now + ANIMATION_FRAME_DURATION;
mAnimating = true;
- mHandler.removeMessages(MSG_ANIMATE);
- mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurrentAnimationTime);
+ removeCallbacks(mSlidingRunnable);
+ postDelayed(mSlidingRunnable, ANIMATION_FRAME_DURATION);
stopTracking();
}
@@ -569,7 +567,7 @@
(mVertical ? getHeight() - mHandleHeight : getWidth() - mHandleWidth);
moveHandle((int) mAnimationPosition);
mAnimating = true;
- mHandler.removeMessages(MSG_ANIMATE);
+ removeCallbacks(mSlidingRunnable);
long now = SystemClock.uptimeMillis();
mAnimationLastTime = now;
mCurrentAnimationTime = now + ANIMATION_FRAME_DURATION;
@@ -577,7 +575,7 @@
} else {
if (mAnimating) {
mAnimating = false;
- mHandler.removeMessages(MSG_ANIMATE);
+ removeCallbacks(mSlidingRunnable);
}
moveHandle(position);
}
@@ -709,8 +707,7 @@
} else {
moveHandle((int) mAnimationPosition);
mCurrentAnimationTime += ANIMATION_FRAME_DURATION;
- mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE),
- mCurrentAnimationTime);
+ postDelayed(mSlidingRunnable, ANIMATION_FRAME_DURATION);
}
}
}
@@ -974,13 +971,10 @@
}
}
- private class SlidingHandler extends Handler {
- public void handleMessage(Message m) {
- switch (m.what) {
- case MSG_ANIMATE:
- doAnimation();
- break;
- }
+ private final Runnable mSlidingRunnable = new Runnable() {
+ @Override
+ public void run() {
+ doAnimation();
}
- }
+ };
}
diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java
index bcde315..ff10287 100644
--- a/core/java/android/widget/TextClock.java
+++ b/core/java/android/widget/TextClock.java
@@ -137,7 +137,13 @@
private boolean mShowCurrentUserTime;
- private final ContentObserver mFormatChangeObserver = new ContentObserver(new Handler()) {
+ private ContentObserver mFormatChangeObserver;
+ private class FormatChangeObserver extends ContentObserver {
+
+ public FormatChangeObserver(Handler handler) {
+ super(handler);
+ }
+
@Override
public void onChange(boolean selfChange) {
chooseFormat();
@@ -553,13 +559,18 @@
}
private void registerObserver() {
- final ContentResolver resolver = getContext().getContentResolver();
- if (mShowCurrentUserTime) {
- resolver.registerContentObserver(Settings.System.CONTENT_URI, true,
- mFormatChangeObserver, UserHandle.USER_ALL);
- } else {
- resolver.registerContentObserver(Settings.System.CONTENT_URI, true,
- mFormatChangeObserver);
+ if (isAttachedToWindow()) {
+ if (mFormatChangeObserver == null) {
+ mFormatChangeObserver = new FormatChangeObserver(getHandler());
+ }
+ final ContentResolver resolver = getContext().getContentResolver();
+ if (mShowCurrentUserTime) {
+ resolver.registerContentObserver(Settings.System.CONTENT_URI, true,
+ mFormatChangeObserver, UserHandle.USER_ALL);
+ } else {
+ resolver.registerContentObserver(Settings.System.CONTENT_URI, true,
+ mFormatChangeObserver);
+ }
}
}
@@ -568,8 +579,10 @@
}
private void unregisterObserver() {
- final ContentResolver resolver = getContext().getContentResolver();
- resolver.unregisterContentObserver(mFormatChangeObserver);
+ if (mFormatChangeObserver != null) {
+ final ContentResolver resolver = getContext().getContentResolver();
+ resolver.unregisterContentObserver(mFormatChangeObserver);
+ }
}
private void onTimeChanged() {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 0712052..c54a574 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -16,6 +16,8 @@
package android.widget;
+import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
+
import android.R;
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
@@ -116,15 +118,16 @@
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.MotionEvent;
+import android.view.PointerIcon;
import android.view.View;
-import android.view.ViewParent;
-import android.view.ViewStructure;
import android.view.ViewConfiguration;
import android.view.ViewDebug;
import android.view.ViewGroup.LayoutParams;
-import android.view.ViewRootImpl;
-import android.view.ViewTreeObserver;
import android.view.ViewHierarchyEncoder;
+import android.view.ViewParent;
+import android.view.ViewRootImpl;
+import android.view.ViewStructure;
+import android.view.ViewTreeObserver;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -152,8 +155,6 @@
import java.util.ArrayList;
import java.util.Locale;
-import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
-
/**
* Displays text to the user and optionally allows them to edit it. A TextView
* is a complete text editor, however the basic class is configured to not
@@ -309,6 +310,14 @@
*/
static final int PROCESS_TEXT_REQUEST_CODE = 100;
+ /**
+ * Return code of {@link #doKeyDown}.
+ */
+ private static final int KEY_EVENT_NOT_HANDLED = 0;
+ private static final int KEY_EVENT_HANDLED = -1;
+ private static final int KEY_DOWN_HANDLED_BY_KEY_LISTENER = 1;
+ private static final int KEY_DOWN_HANDLED_BY_MOVEMENT_METHOD = 2;
+
// System wide time for last cut, copy or text changed action.
static long sLastCutCopyOrTextChangedTime;
@@ -1477,7 +1486,12 @@
}
}
}
+ } else if (mText instanceof Spannable) {
+ // Reset the selection.
+ stopTextActionMode();
+ Selection.setSelection((Spannable) mText, getSelectionStart(), getSelectionEnd());
}
+
if (mEditor.hasSelectionController()) {
mEditor.startSelectionActionMode();
}
@@ -5281,14 +5295,6 @@
mEditor.mCreatedWithASelection = false;
}
- // Phone specific code (there is no ExtractEditText on tablets).
- // ExtractEditText does not call onFocus when it is displayed, and mHasSelectionOnFocus can
- // not be set. Do the test here instead.
- if (isInExtractedMode() && hasSelection() && mEditor != null
- && mEditor.mTextActionMode == null && isShown() && hasWindowFocus()) {
- mEditor.startSelectionActionMode();
- }
-
unregisterForPreDraw();
return true;
@@ -5905,6 +5911,17 @@
return mLayout != null ? mLayout.getHeight() : 0;
}
+ /**
+ * @hide
+ */
+ @Override
+ public int getPointerShape(MotionEvent event, float x, float y) {
+ if (isTextSelectable() || isTextEditable()) {
+ return PointerIcon.STYLE_TEXT;
+ }
+ return super.getPointerShape(event, x, y);
+ }
+
@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
// Note: If the IME is in fullscreen mode and IMS#mExtractEditText is in text action mode,
@@ -5946,8 +5963,8 @@
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
- int which = doKeyDown(keyCode, event, null);
- if (which == 0) {
+ final int which = doKeyDown(keyCode, event, null);
+ if (which == KEY_EVENT_NOT_HANDLED) {
return super.onKeyDown(keyCode, event);
}
@@ -5957,13 +5974,12 @@
@Override
public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
KeyEvent down = KeyEvent.changeAction(event, KeyEvent.ACTION_DOWN);
-
- int which = doKeyDown(keyCode, down, event);
- if (which == 0) {
+ final int which = doKeyDown(keyCode, down, event);
+ if (which == KEY_EVENT_NOT_HANDLED) {
// Go through default dispatching.
return super.onKeyMultiple(keyCode, repeatCount, event);
}
- if (which == -1) {
+ if (which == KEY_EVENT_HANDLED) {
// Consumed the whole thing.
return true;
}
@@ -5976,7 +5992,7 @@
// It would be nice if those interfaces had an onKeyMultiple() method,
// but adding that is a more complicated change.
KeyEvent up = KeyEvent.changeAction(event, KeyEvent.ACTION_UP);
- if (which == 1) {
+ if (which == KEY_DOWN_HANDLED_BY_KEY_LISTENER) {
// mEditor and mEditor.mInput are not null from doKeyDown
mEditor.mKeyListener.onKeyUp(this, (Editable)mText, keyCode, up);
while (--repeatCount > 0) {
@@ -5985,7 +6001,7 @@
}
hideErrorIfUnchanged();
- } else if (which == 2) {
+ } else if (which == KEY_DOWN_HANDLED_BY_MOVEMENT_METHOD) {
// mMovement is not null from doKeyDown
mMovement.onKeyUp(this, (Spannable)mText, keyCode, up);
while (--repeatCount > 0) {
@@ -6042,7 +6058,7 @@
private int doKeyDown(int keyCode, KeyEvent event, KeyEvent otherEvent) {
if (!isEnabled()) {
- return 0;
+ return KEY_EVENT_NOT_HANDLED;
}
// If this is the initial keydown, we don't want to prevent a movement away from this view.
@@ -6069,7 +6085,7 @@
this, EditorInfo.IME_NULL, event)) {
mEditor.mInputContentType.enterDown = true;
// We are consuming the enter key for them.
- return -1;
+ return KEY_EVENT_HANDLED;
}
}
@@ -6079,9 +6095,9 @@
if ((event.getFlags() & KeyEvent.FLAG_EDITOR_ACTION) != 0
|| shouldAdvanceFocusOnEnter()) {
if (hasOnClickListeners()) {
- return 0;
+ return KEY_EVENT_NOT_HANDLED;
}
- return -1;
+ return KEY_EVENT_HANDLED;
}
}
break;
@@ -6089,7 +6105,7 @@
case KeyEvent.KEYCODE_DPAD_CENTER:
if (event.hasNoModifiers()) {
if (shouldAdvanceFocusOnEnter()) {
- return 0;
+ return KEY_EVENT_NOT_HANDLED;
}
}
break;
@@ -6097,7 +6113,7 @@
case KeyEvent.KEYCODE_TAB:
if (event.hasNoModifiers() || event.hasModifiers(KeyEvent.META_SHIFT_ON)) {
if (shouldAdvanceFocusOnTab()) {
- return 0;
+ return KEY_EVENT_NOT_HANDLED;
}
}
break;
@@ -6106,7 +6122,31 @@
case KeyEvent.KEYCODE_BACK:
if (mEditor != null && mEditor.mTextActionMode != null) {
stopTextActionMode();
- return -1;
+ return KEY_EVENT_HANDLED;
+ }
+ break;
+
+ case KeyEvent.KEYCODE_CUT:
+ if (event.hasNoModifiers() && canCut()) {
+ if (onTextContextMenuItem(ID_CUT)) {
+ return KEY_EVENT_HANDLED;
+ }
+ }
+ break;
+
+ case KeyEvent.KEYCODE_COPY:
+ if (event.hasNoModifiers() && canCopy()) {
+ if (onTextContextMenuItem(ID_COPY)) {
+ return KEY_EVENT_HANDLED;
+ }
+ }
+ break;
+
+ case KeyEvent.KEYCODE_PASTE:
+ if (event.hasNoModifiers() && canPaste()) {
+ if (onTextContextMenuItem(ID_PASTE)) {
+ return KEY_EVENT_HANDLED;
+ }
}
break;
}
@@ -6121,7 +6161,7 @@
hideErrorIfUnchanged();
doDown = false;
if (handled) {
- return -1;
+ return KEY_EVENT_HANDLED;
}
} catch (AbstractMethodError e) {
// onKeyOther was added after 1.0, so if it isn't
@@ -6137,7 +6177,7 @@
keyCode, event);
endBatchEdit();
hideErrorIfUnchanged();
- if (handled) return 1;
+ if (handled) return KEY_DOWN_HANDLED_BY_KEY_LISTENER;
}
}
@@ -6152,7 +6192,7 @@
otherEvent);
doDown = false;
if (handled) {
- return -1;
+ return KEY_EVENT_HANDLED;
}
} catch (AbstractMethodError e) {
// onKeyOther was added after 1.0, so if it isn't
@@ -6164,12 +6204,13 @@
if (event.getRepeatCount() == 0 && !KeyEvent.isModifierKey(keyCode)) {
mPreventDefaultMovement = true;
}
- return 2;
+ return KEY_DOWN_HANDLED_BY_MOVEMENT_METHOD;
}
}
}
- return mPreventDefaultMovement && !KeyEvent.isModifierKey(keyCode) ? -1 : 0;
+ return mPreventDefaultMovement && !KeyEvent.isModifierKey(keyCode) ?
+ KEY_EVENT_HANDLED : KEY_EVENT_NOT_HANDLED;
}
/**
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index 986c0f8..8e5af79 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -29,6 +29,8 @@
import java.util.Locale;
+import libcore.icu.LocaleData;
+
/**
* A widget for selecting the time of day, in either 24-hour or AM/PM mode.
* <p>
@@ -303,6 +305,16 @@
void onValidationChanged(boolean valid);
}
+ static String[] getAmPmStrings(Context context) {
+ final Locale locale = context.getResources().getConfiguration().locale;
+ final LocaleData d = LocaleData.get(locale);
+
+ final String[] result = new String[2];
+ result[0] = d.amPm[0].length() > 4 ? d.narrowAm : d.amPm[0];
+ result[1] = d.amPm[1].length() > 4 ? d.narrowPm : d.amPm[1];
+ return result;
+ }
+
/**
* An abstract class which can be used as a start for TimePicker implementations
*/
diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java
index 6d41c1d..4dc5fd3e 100644
--- a/core/java/android/widget/TimePickerClockDelegate.java
+++ b/core/java/android/widget/TimePickerClockDelegate.java
@@ -29,21 +29,22 @@
import android.text.format.DateUtils;
import android.text.style.TtsSpan;
import android.util.AttributeSet;
-import android.util.Log;
import android.util.StateSet;
import android.view.HapticFeedbackConstants;
-import android.view.KeyCharacterMap;
-import android.view.KeyEvent;
import android.view.LayoutInflater;
+import android.view.MotionEvent;
import android.view.View;
import android.view.View.AccessibilityDelegate;
+import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import com.android.internal.R;
+import com.android.internal.widget.NumericTextView;
+import com.android.internal.widget.NumericTextView.OnValueChangedListener;
-import java.util.ArrayList;
import java.util.Calendar;
import java.util.Locale;
@@ -52,7 +53,12 @@
*/
class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate implements
RadialTimePickerView.OnValueSelectedListener {
- private static final String TAG = "TimePickerClockDelegate";
+ /**
+ * Delay in milliseconds before valid but potentially incomplete, for
+ * example "1" but not "12", keyboard edits are propagated from the
+ * hour / minute fields to the radial picker.
+ */
+ private static final long DELAY_COMMIT_MILLIS = 2000;
// Index used by RadialPickerLayout
private static final int HOUR_INDEX = 0;
@@ -61,9 +67,6 @@
// NOT a real index for the purpose of what's showing.
private static final int AMPM_INDEX = 2;
- // Also NOT a real index, just used for keyboard mode.
- private static final int ENABLE_PICKER_INDEX = 3;
-
private static final int[] ATTRS_TEXT_COLOR = new int[] {R.attr.textColor};
private static final int[] ATTRS_DISABLED_ALPHA = new int[] {R.attr.disabledAlpha};
@@ -74,18 +77,14 @@
private static final int HOURS_IN_HALF_DAY = 12;
- private final View mHeaderView;
- private final TextView mHourView;
- private final TextView mMinuteView;
+ private final NumericTextView mHourView;
+ private final NumericTextView mMinuteView;
private final View mAmPmLayout;
- private final CheckedTextView mAmLabel;
- private final CheckedTextView mPmLabel;
+ private final RadioButton mAmLabel;
+ private final RadioButton mPmLabel;
private final RadialTimePickerView mRadialTimePickerView;
private final TextView mSeparatorView;
- private final String mAmText;
- private final String mPmText;
-
private boolean mIsEnabled = true;
private boolean mAllowAutoAdvance;
private int mInitialHourOfDay;
@@ -93,20 +92,14 @@
private boolean mIs24HourView;
private boolean mIsAmPmAtStart;
- // For hardware IME input.
- private char mPlaceholderText;
- private String mDoublePlaceholderText;
- private String mDeletedKeyFormat;
- private boolean mInKbMode;
- private ArrayList<Integer> mTypedTimes = new ArrayList<Integer>();
- private Node mLegalTimesTree;
- private int mAmKeyCode;
- private int mPmKeyCode;
-
// Accessibility strings.
private String mSelectHours;
private String mSelectMinutes;
+ // Localization data.
+ private boolean mHourFormatShowLeadingZero;
+ private boolean mHourFormatStartsAtZero;
+
// Most recent time announcement values for accessibility.
private CharSequence mLastAnnouncedText;
private boolean mLastAnnouncedIsHour;
@@ -127,43 +120,42 @@
mSelectHours = res.getString(R.string.select_hours);
mSelectMinutes = res.getString(R.string.select_minutes);
- String[] amPmStrings = TimePickerSpinnerDelegate.getAmPmStrings(context);
- mAmText = amPmStrings[0];
- mPmText = amPmStrings[1];
-
final int layoutResourceId = a.getResourceId(R.styleable.TimePicker_internalLayout,
R.layout.time_picker_material);
final View mainView = inflater.inflate(layoutResourceId, delegator);
-
- mHeaderView = mainView.findViewById(R.id.time_header);
+ final View headerView = mainView.findViewById(R.id.time_header);
+ headerView.setOnTouchListener(new NearestTouchDelegate());
// Set up hour/minute labels.
- mHourView = (TextView) mainView.findViewById(R.id.hours);
+ mHourView = (NumericTextView) mainView.findViewById(R.id.hours);
mHourView.setOnClickListener(mClickListener);
+ mHourView.setOnFocusChangeListener(mFocusListener);
+ mHourView.setOnDigitEnteredListener(mDigitEnteredListener);
mHourView.setAccessibilityDelegate(
new ClickActionDelegate(context, R.string.select_hours));
mSeparatorView = (TextView) mainView.findViewById(R.id.separator);
- mMinuteView = (TextView) mainView.findViewById(R.id.minutes);
+ mMinuteView = (NumericTextView) mainView.findViewById(R.id.minutes);
mMinuteView.setOnClickListener(mClickListener);
+ mMinuteView.setOnFocusChangeListener(mFocusListener);
+ mMinuteView.setOnDigitEnteredListener(mDigitEnteredListener);
mMinuteView.setAccessibilityDelegate(
new ClickActionDelegate(context, R.string.select_minutes));
-
- // Now that we have text appearances out of the way, make sure the hour
- // and minute views are correctly sized.
- mHourView.setMinWidth(computeStableWidth(mHourView, 24));
- mMinuteView.setMinWidth(computeStableWidth(mMinuteView, 60));
-
- final SpannableStringBuilder amLabel = new SpannableStringBuilder()
- .append(amPmStrings[0], new TtsSpan.VerbatimBuilder(amPmStrings[0]).build(), 0);
+ mMinuteView.setRange(0, 59);
// Set up AM/PM labels.
mAmPmLayout = mainView.findViewById(R.id.ampm_layout);
- mAmLabel = (CheckedTextView) mAmPmLayout.findViewById(R.id.am_label);
+ mAmPmLayout.setOnTouchListener(new NearestTouchDelegate());
+
+ final String[] amPmStrings = TimePicker.getAmPmStrings(context);
+ mAmLabel = (RadioButton) mAmPmLayout.findViewById(R.id.am_label);
mAmLabel.setText(obtainVerbatim(amPmStrings[0]));
mAmLabel.setOnClickListener(mClickListener);
- mPmLabel = (CheckedTextView) mAmPmLayout.findViewById(R.id.pm_label);
+ ensureMinimumTextWidth(mAmLabel);
+
+ mPmLabel = (RadioButton) mAmPmLayout.findViewById(R.id.pm_label);
mPmLabel.setText(obtainVerbatim(amPmStrings[1]));
mPmLabel.setOnClickListener(mClickListener);
+ ensureMinimumTextWidth(mPmLabel);
// For the sake of backwards compatibility, attempt to extract the text
// color from the header time text appearance. If it's set, we'll let
@@ -195,7 +187,7 @@
// Set up header background, if available.
if (a.hasValueOrEmpty(R.styleable.TimePicker_headerBackground)) {
- mHeaderView.setBackground(a.getDrawable(R.styleable.TimePicker_headerBackground));
+ headerView.setBackground(a.getDrawable(R.styleable.TimePicker_headerBackground));
}
a.recycle();
@@ -207,18 +199,66 @@
mAllowAutoAdvance = true;
- // Set up for keyboard mode.
- mDoublePlaceholderText = res.getString(R.string.time_placeholder);
- mDeletedKeyFormat = res.getString(R.string.deleted_key);
- mPlaceholderText = mDoublePlaceholderText.charAt(0);
- mAmKeyCode = mPmKeyCode = -1;
- generateLegalTimesTree();
+ // Updates mHourFormat variables used below.
+ updateHourFormat(mCurrentLocale, mIs24HourView);
- // Initialize with current time
- final Calendar calendar = Calendar.getInstance(mCurrentLocale);
- final int currentHour = calendar.get(Calendar.HOUR_OF_DAY);
- final int currentMinute = calendar.get(Calendar.MINUTE);
- initialize(currentHour, currentMinute, false /* 12h */, HOUR_INDEX);
+ // Update hour text field.
+ final int minHour = mHourFormatStartsAtZero ? 0 : 1;
+ final int maxHour = (mIs24HourView ? 23 : 11) + minHour;
+ mHourView.setRange(minHour, maxHour);
+ mHourView.setShowLeadingZeroes(mHourFormatShowLeadingZero);
+
+ // Initialize with current time.
+ mTempCalendar = Calendar.getInstance(mCurrentLocale);
+ final int currentHour = mTempCalendar.get(Calendar.HOUR_OF_DAY);
+ final int currentMinute = mTempCalendar.get(Calendar.MINUTE);
+ initialize(currentHour, currentMinute, mIs24HourView, HOUR_INDEX);
+ }
+
+ /**
+ * Ensures that a TextView is wide enough to contain its text without
+ * wrapping or clipping. Measures the specified view and sets the minimum
+ * width to the view's desired width.
+ *
+ * @param v the text view to measure
+ */
+ private static void ensureMinimumTextWidth(TextView v) {
+ v.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ // Set both the TextView and the View version of minimum
+ // width because they are subtly different.
+ final int minWidth = v.getMeasuredWidth();
+ v.setMinWidth(minWidth);
+ v.setMinimumWidth(minWidth);
+ }
+
+ /**
+ * Determines how the hour should be formatted and updates member variables
+ * related to hour formatting.
+ *
+ * @param locale the locale in which the view is displayed
+ * @param is24Hour whether the view is in 24-hour (hour-of-day) mode
+ */
+ private void updateHourFormat(Locale locale, boolean is24Hour) {
+ final String bestDateTimePattern = DateFormat.getBestDateTimePattern(
+ locale, is24Hour ? "Hm" : "hm");
+ final int lengthPattern = bestDateTimePattern.length();
+ boolean showLeadingZero = false;
+ char hourFormat = '\0';
+
+ for (int i = 0; i < lengthPattern; i++) {
+ final char c = bestDateTimePattern.charAt(i);
+ if (c == 'H' || c == 'h' || c == 'K' || c == 'k') {
+ hourFormat = c;
+ if (i + 1 < lengthPattern && c == bestDateTimePattern.charAt(i + 1)) {
+ showLeadingZero = true;
+ }
+ break;
+ }
+ }
+
+ mHourFormatShowLeadingZero = showLeadingZero;
+ mHourFormatStartsAtZero = hourFormat == 'K' || hourFormat == 'H';
}
private static final CharSequence obtainVerbatim(String text) {
@@ -290,51 +330,24 @@
}
}
- private int computeStableWidth(TextView v, int maxNumber) {
- int maxWidth = 0;
-
- for (int i = 0; i < maxNumber; i++) {
- final String text = String.format("%02d", i);
- v.setText(text);
- v.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
-
- final int width = v.getMeasuredWidth();
- if (width > maxWidth) {
- maxWidth = width;
- }
- }
-
- return maxWidth;
- }
-
private void initialize(int hourOfDay, int minute, boolean is24HourView, int index) {
mInitialHourOfDay = hourOfDay;
mInitialMinute = minute;
mIs24HourView = is24HourView;
- mInKbMode = false;
updateUI(index);
}
private void setupListeners() {
- mHeaderView.setOnKeyListener(mKeyListener);
- mHeaderView.setOnFocusChangeListener(mFocusListener);
- mHeaderView.setFocusable(true);
-
mRadialTimePickerView.setOnValueSelectedListener(this);
}
private void updateUI(int index) {
- // Update RadialPicker values
- updateRadialPicker(index);
- // Enable or disable the AM/PM view.
updateHeaderAmPm();
- // Update Hour and Minutes
updateHeaderHour(mInitialHourOfDay, false);
- // Update time separator
updateHeaderSeparator();
- // Update Minutes
updateHeaderMinute(mInitialMinute, false);
- // Invalidate everything
+ updateRadialPicker(index);
+
mDelegator.invalidate();
}
@@ -447,14 +460,11 @@
if (is24HourView == mIs24HourView) {
return;
}
+
mIs24HourView = is24HourView;
- generateLegalTimesTree();
- int hour = mRadialTimePickerView.getCurrentHour();
- mInitialHourOfDay = hour;
- updateHeaderHour(hour, false);
- updateHeaderAmPm();
- updateRadialPicker(mRadialTimePickerView.getCurrentItemShowing());
- mDelegator.invalidate();
+ mInitialHourOfDay = getCurrentHour();
+
+ updateUI(mRadialTimePickerView.getCurrentItemShowing());
}
/**
@@ -499,20 +509,14 @@
@Override
public Parcelable onSaveInstanceState(Parcelable superState) {
return new SavedState(superState, getCurrentHour(), getCurrentMinute(),
- is24HourView(), inKbMode(), getTypedTimes(), getCurrentItemShowing());
+ is24HourView(), getCurrentItemShowing());
}
@Override
public void onRestoreInstanceState(Parcelable state) {
- SavedState ss = (SavedState) state;
- setInKbMode(ss.inKbMode());
- setTypedTimes(ss.getTypesTimes());
+ final SavedState ss = (SavedState) state;
initialize(ss.getHour(), ss.getMinute(), ss.is24HourMode(), ss.getCurrentItemShowing());
mRadialTimePickerView.invalidate();
- if (mInKbMode) {
- tryStartingKbMode(-1);
- mHourView.invalidate();
- }
}
@Override
@@ -543,33 +547,6 @@
}
/**
- * Set whether in keyboard mode or not.
- *
- * @param inKbMode True means in keyboard mode.
- */
- private void setInKbMode(boolean inKbMode) {
- mInKbMode = inKbMode;
- }
-
- /**
- * @return true if in keyboard mode
- */
- private boolean inKbMode() {
- return mInKbMode;
- }
-
- private void setTypedTimes(ArrayList<Integer> typeTimes) {
- mTypedTimes = typeTimes;
- }
-
- /**
- * @return an array of typed times
- */
- private ArrayList<Integer> getTypedTimes() {
- return mTypedTimes;
- }
-
- /**
* @return the index of the current item showing
*/
private int getCurrentItemShowing() {
@@ -595,19 +572,14 @@
private final int mHour;
private final int mMinute;
private final boolean mIs24HourMode;
- private final boolean mInKbMode;
- private final ArrayList<Integer> mTypedTimes;
private final int mCurrentItemShowing;
private SavedState(Parcelable superState, int hour, int minute, boolean is24HourMode,
- boolean isKbMode, ArrayList<Integer> typedTimes,
- int currentItemShowing) {
+ int currentItemShowing) {
super(superState);
mHour = hour;
mMinute = minute;
mIs24HourMode = is24HourMode;
- mInKbMode = isKbMode;
- mTypedTimes = typedTimes;
mCurrentItemShowing = currentItemShowing;
}
@@ -616,8 +588,6 @@
mHour = in.readInt();
mMinute = in.readInt();
mIs24HourMode = (in.readInt() == 1);
- mInKbMode = (in.readInt() == 1);
- mTypedTimes = in.readArrayList(getClass().getClassLoader());
mCurrentItemShowing = in.readInt();
}
@@ -633,14 +603,6 @@
return mIs24HourMode;
}
- public boolean inKbMode() {
- return mInKbMode;
- }
-
- public ArrayList<Integer> getTypesTimes() {
- return mTypedTimes;
- }
-
public int getCurrentItemShowing() {
return mCurrentItemShowing;
}
@@ -651,13 +613,11 @@
dest.writeInt(mHour);
dest.writeInt(mMinute);
dest.writeInt(mIs24HourMode ? 1 : 0);
- dest.writeInt(mInKbMode ? 1 : 0);
- dest.writeList(mTypedTimes);
dest.writeInt(mCurrentItemShowing);
}
@SuppressWarnings({"unused", "hiding"})
- public static final Parcelable.Creator<SavedState> CREATOR = new Creator<SavedState>() {
+ public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
@@ -703,12 +663,6 @@
case AMPM_INDEX:
updateAmPmLabelStates(newValue);
break;
- case ENABLE_PICKER_INDEX:
- if (!isTypedTimeFullyLegal()) {
- mTypedTimes.clear();
- }
- finishKbMode();
- break;
}
if (mOnTimeChangedListener != null) {
@@ -716,61 +670,41 @@
}
}
- private void updateHeaderHour(int value, boolean announce) {
- final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mCurrentLocale,
- (mIs24HourView) ? "Hm" : "hm");
- final int lengthPattern = bestDateTimePattern.length();
- boolean hourWithTwoDigit = false;
- char hourFormat = '\0';
- // Check if the returned pattern is single or double 'H', 'h', 'K', 'k'. We also save
- // the hour format that we found.
- for (int i = 0; i < lengthPattern; i++) {
- final char c = bestDateTimePattern.charAt(i);
- if (c == 'H' || c == 'h' || c == 'K' || c == 'k') {
- hourFormat = c;
- if (i + 1 < lengthPattern && c == bestDateTimePattern.charAt(i + 1)) {
- hourWithTwoDigit = true;
- }
- break;
- }
+ /**
+ * Converts hour-of-day (0-23) time into a localized hour number.
+ *
+ * @param hourOfDay the hour-of-day (0-23)
+ * @return a localized hour number
+ */
+ private int getLocalizedHour(int hourOfDay) {
+ if (!mIs24HourView) {
+ // Convert to hour-of-am-pm.
+ hourOfDay %= 12;
}
- final String format;
- if (hourWithTwoDigit) {
- format = "%02d";
- } else {
- format = "%d";
+
+ if (!mHourFormatStartsAtZero && hourOfDay == 0) {
+ // Convert to clock-hour (either of-day or of-am-pm).
+ hourOfDay = mIs24HourView ? 24 : 12;
}
- if (mIs24HourView) {
- // 'k' means 1-24 hour
- if (hourFormat == 'k' && value == 0) {
- value = 24;
- }
- } else {
- // 'K' means 0-11 hour
- value = modulo12(value, hourFormat == 'K');
- }
- CharSequence text = String.format(format, value);
- mHourView.setText(text);
+
+ return hourOfDay;
+ }
+
+ private void updateHeaderHour(int hourOfDay, boolean announce) {
+ final int localizedHour = getLocalizedHour(hourOfDay);
+ mHourView.setValue(localizedHour);
+
if (announce) {
- tryAnnounceForAccessibility(text, true);
+ tryAnnounceForAccessibility(mHourView.getText(), true);
}
}
- private void tryAnnounceForAccessibility(CharSequence text, boolean isHour) {
- if (mLastAnnouncedIsHour != isHour || !text.equals(mLastAnnouncedText)) {
- // TODO: Find a better solution, potentially live regions?
- mDelegator.announceForAccessibility(text);
- mLastAnnouncedText = text;
- mLastAnnouncedIsHour = isHour;
- }
- }
+ private void updateHeaderMinute(int minuteOfHour, boolean announce) {
+ mMinuteView.setValue(minuteOfHour);
- private static int modulo12(int n, boolean startWithZero) {
- int value = n % 12;
- if (value == 0 && !startWithZero) {
- value = 12;
+ if (announce) {
+ tryAnnounceForAccessibility(mMinuteView.getText(), false);
}
- return value;
}
/**
@@ -812,14 +746,12 @@
return -1;
}
- private void updateHeaderMinute(int value, boolean announceForAccessibility) {
- if (value == 60) {
- value = 0;
- }
- final CharSequence text = String.format(mCurrentLocale, "%02d", value);
- mMinuteView.setText(text);
- if (announceForAccessibility) {
- tryAnnounceForAccessibility(text, false);
+ private void tryAnnounceForAccessibility(CharSequence text, boolean isHour) {
+ if (mLastAnnouncedIsHour != isHour || !text.equals(mLastAnnouncedText)) {
+ // TODO: Find a better solution, potentially live regions?
+ mDelegator.announceForAccessibility(text);
+ mLastAnnouncedText = text;
+ mLastAnnouncedIsHour = isHour;
}
}
@@ -848,477 +780,82 @@
mRadialTimePickerView.setAmOrPm(amOrPm);
}
- /**
- * For keyboard mode, processes key events.
- *
- * @param keyCode the pressed key.
- *
- * @return true if the key was successfully processed, false otherwise.
- */
- private boolean processKeyUp(int keyCode) {
- if (keyCode == KeyEvent.KEYCODE_DEL) {
- if (mInKbMode) {
- if (!mTypedTimes.isEmpty()) {
- int deleted = deleteLastTypedKey();
- String deletedKeyStr;
- if (deleted == getAmOrPmKeyCode(AM)) {
- deletedKeyStr = mAmText;
- } else if (deleted == getAmOrPmKeyCode(PM)) {
- deletedKeyStr = mPmText;
- } else {
- deletedKeyStr = String.format("%d", getValFromKeyCode(deleted));
+ private final OnValueChangedListener mDigitEnteredListener = new OnValueChangedListener() {
+ @Override
+ public void onValueChanged(NumericTextView view, int value,
+ boolean isValid, boolean isFinished) {
+ final Runnable commitCallback;
+ final View nextFocusTarget;
+ if (view == mHourView) {
+ commitCallback = mCommitHour;
+ nextFocusTarget = view.isFocused() ? mMinuteView : null;
+ } else if (view == mMinuteView) {
+ commitCallback = mCommitMinute;
+ nextFocusTarget = null;
+ } else {
+ return;
+ }
+
+ view.removeCallbacks(commitCallback);
+
+ if (isValid) {
+ if (isFinished) {
+ // Done with hours entry, make visual updates
+ // immediately and move to next focus if needed.
+ commitCallback.run();
+
+ if (nextFocusTarget != null) {
+ nextFocusTarget.requestFocus();
}
- mDelegator.announceForAccessibility(
- String.format(mDeletedKeyFormat, deletedKeyStr));
- updateDisplay(true);
- }
- }
- } else if (keyCode == KeyEvent.KEYCODE_0 || keyCode == KeyEvent.KEYCODE_1
- || keyCode == KeyEvent.KEYCODE_2 || keyCode == KeyEvent.KEYCODE_3
- || keyCode == KeyEvent.KEYCODE_4 || keyCode == KeyEvent.KEYCODE_5
- || keyCode == KeyEvent.KEYCODE_6 || keyCode == KeyEvent.KEYCODE_7
- || keyCode == KeyEvent.KEYCODE_8 || keyCode == KeyEvent.KEYCODE_9
- || (!mIs24HourView &&
- (keyCode == getAmOrPmKeyCode(AM) || keyCode == getAmOrPmKeyCode(PM)))) {
- if (!mInKbMode) {
- if (mRadialTimePickerView == null) {
- // Something's wrong, because time picker should definitely not be null.
- Log.e(TAG, "Unable to initiate keyboard mode, TimePicker was null.");
- return true;
- }
- mTypedTimes.clear();
- tryStartingKbMode(keyCode);
- return true;
- }
- // We're already in keyboard mode.
- if (addKeyIfLegal(keyCode)) {
- updateDisplay(false);
- }
- return true;
- }
- return false;
- }
-
- /**
- * Try to start keyboard mode with the specified key.
- *
- * @param keyCode The key to use as the first press. Keyboard mode will not be started if the
- * key is not legal to start with. Or, pass in -1 to get into keyboard mode without a starting
- * key.
- */
- private void tryStartingKbMode(int keyCode) {
- if (keyCode == -1 || addKeyIfLegal(keyCode)) {
- mInKbMode = true;
- onValidationChanged(false);
- updateDisplay(false);
- mRadialTimePickerView.setInputEnabled(false);
- }
- }
-
- private boolean addKeyIfLegal(int keyCode) {
- // If we're in 24hour mode, we'll need to check if the input is full. If in AM/PM mode,
- // we'll need to see if AM/PM have been typed.
- if ((mIs24HourView && mTypedTimes.size() == 4) ||
- (!mIs24HourView && isTypedTimeFullyLegal())) {
- return false;
- }
-
- mTypedTimes.add(keyCode);
- if (!isTypedTimeLegalSoFar()) {
- deleteLastTypedKey();
- return false;
- }
-
- int val = getValFromKeyCode(keyCode);
- mDelegator.announceForAccessibility(String.format("%d", val));
- // Automatically fill in 0's if AM or PM was legally entered.
- if (isTypedTimeFullyLegal()) {
- if (!mIs24HourView && mTypedTimes.size() <= 3) {
- mTypedTimes.add(mTypedTimes.size() - 1, KeyEvent.KEYCODE_0);
- mTypedTimes.add(mTypedTimes.size() - 1, KeyEvent.KEYCODE_0);
- }
- onValidationChanged(true);
- }
-
- return true;
- }
-
- /**
- * Traverse the tree to see if the keys that have been typed so far are legal as is,
- * or may become legal as more keys are typed (excluding backspace).
- */
- private boolean isTypedTimeLegalSoFar() {
- Node node = mLegalTimesTree;
- for (int keyCode : mTypedTimes) {
- node = node.canReach(keyCode);
- if (node == null) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Check if the time that has been typed so far is completely legal, as is.
- */
- private boolean isTypedTimeFullyLegal() {
- if (mIs24HourView) {
- // For 24-hour mode, the time is legal if the hours and minutes are each legal. Note:
- // getEnteredTime() will ONLY call isTypedTimeFullyLegal() when NOT in 24hour mode.
- int[] values = getEnteredTime(null);
- return (values[0] >= 0 && values[1] >= 0 && values[1] < 60);
- } else {
- // For AM/PM mode, the time is legal if it contains an AM or PM, as those can only be
- // legally added at specific times based on the tree's algorithm.
- return (mTypedTimes.contains(getAmOrPmKeyCode(AM)) ||
- mTypedTimes.contains(getAmOrPmKeyCode(PM)));
- }
- }
-
- private int deleteLastTypedKey() {
- int deleted = mTypedTimes.remove(mTypedTimes.size() - 1);
- if (!isTypedTimeFullyLegal()) {
- onValidationChanged(false);
- }
- return deleted;
- }
-
- /**
- * Get out of keyboard mode. If there is nothing in typedTimes, revert to TimePicker's time.
- */
- private void finishKbMode() {
- mInKbMode = false;
- if (!mTypedTimes.isEmpty()) {
- int values[] = getEnteredTime(null);
- mRadialTimePickerView.setCurrentHour(values[0]);
- mRadialTimePickerView.setCurrentMinute(values[1]);
- if (!mIs24HourView) {
- mRadialTimePickerView.setAmOrPm(values[2]);
- }
- mTypedTimes.clear();
- }
- updateDisplay(false);
- mRadialTimePickerView.setInputEnabled(true);
- }
-
- /**
- * Update the hours, minutes, and AM/PM displays with the typed times. If the typedTimes is
- * empty, either show an empty display (filled with the placeholder text), or update from the
- * timepicker's values.
- *
- * @param allowEmptyDisplay if true, then if the typedTimes is empty, use the placeholder text.
- * Otherwise, revert to the timepicker's values.
- */
- private void updateDisplay(boolean allowEmptyDisplay) {
- if (!allowEmptyDisplay && mTypedTimes.isEmpty()) {
- int hour = mRadialTimePickerView.getCurrentHour();
- int minute = mRadialTimePickerView.getCurrentMinute();
- updateHeaderHour(hour, false);
- updateHeaderMinute(minute, false);
- if (!mIs24HourView) {
- updateAmPmLabelStates(hour < 12 ? AM : PM);
- }
- setCurrentItemShowing(mRadialTimePickerView.getCurrentItemShowing(), true, true);
- onValidationChanged(true);
- } else {
- boolean[] enteredZeros = {false, false};
- int[] values = getEnteredTime(enteredZeros);
- String hourFormat = enteredZeros[0] ? "%02d" : "%2d";
- String minuteFormat = (enteredZeros[1]) ? "%02d" : "%2d";
- String hourStr = (values[0] == -1) ? mDoublePlaceholderText :
- String.format(hourFormat, values[0]).replace(' ', mPlaceholderText);
- String minuteStr = (values[1] == -1) ? mDoublePlaceholderText :
- String.format(minuteFormat, values[1]).replace(' ', mPlaceholderText);
- mHourView.setText(hourStr);
- mHourView.setActivated(false);
- mMinuteView.setText(minuteStr);
- mMinuteView.setActivated(false);
- if (!mIs24HourView) {
- updateAmPmLabelStates(values[2]);
- }
- }
- }
-
- private int getValFromKeyCode(int keyCode) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_0:
- return 0;
- case KeyEvent.KEYCODE_1:
- return 1;
- case KeyEvent.KEYCODE_2:
- return 2;
- case KeyEvent.KEYCODE_3:
- return 3;
- case KeyEvent.KEYCODE_4:
- return 4;
- case KeyEvent.KEYCODE_5:
- return 5;
- case KeyEvent.KEYCODE_6:
- return 6;
- case KeyEvent.KEYCODE_7:
- return 7;
- case KeyEvent.KEYCODE_8:
- return 8;
- case KeyEvent.KEYCODE_9:
- return 9;
- default:
- return -1;
- }
- }
-
- /**
- * Get the currently-entered time, as integer values of the hours and minutes typed.
- *
- * @param enteredZeros A size-2 boolean array, which the caller should initialize, and which
- * may then be used for the caller to know whether zeros had been explicitly entered as either
- * hours of minutes. This is helpful for deciding whether to show the dashes, or actual 0's.
- *
- * @return A size-3 int array. The first value will be the hours, the second value will be the
- * minutes, and the third will be either AM or PM.
- */
- private int[] getEnteredTime(boolean[] enteredZeros) {
- int amOrPm = -1;
- int startIndex = 1;
- if (!mIs24HourView && isTypedTimeFullyLegal()) {
- int keyCode = mTypedTimes.get(mTypedTimes.size() - 1);
- if (keyCode == getAmOrPmKeyCode(AM)) {
- amOrPm = AM;
- } else if (keyCode == getAmOrPmKeyCode(PM)){
- amOrPm = PM;
- }
- startIndex = 2;
- }
- int minute = -1;
- int hour = -1;
- for (int i = startIndex; i <= mTypedTimes.size(); i++) {
- int val = getValFromKeyCode(mTypedTimes.get(mTypedTimes.size() - i));
- if (i == startIndex) {
- minute = val;
- } else if (i == startIndex+1) {
- minute += 10 * val;
- if (enteredZeros != null && val == 0) {
- enteredZeros[1] = true;
- }
- } else if (i == startIndex+2) {
- hour = val;
- } else if (i == startIndex+3) {
- hour += 10 * val;
- if (enteredZeros != null && val == 0) {
- enteredZeros[0] = true;
+ } else {
+ // May still be making changes. Postpone visual
+ // updates to prevent distracting the user.
+ view.postDelayed(commitCallback, DELAY_COMMIT_MILLIS);
}
}
}
+ };
- return new int[] { hour, minute, amOrPm };
- }
+ private final Runnable mCommitHour = new Runnable() {
+ @Override
+ public void run() {
+ setCurrentHour(mHourView.getValue());
+ }
+ };
- /**
- * Get the keycode value for AM and PM in the current language.
- */
- private int getAmOrPmKeyCode(int amOrPm) {
- // Cache the codes.
- if (mAmKeyCode == -1 || mPmKeyCode == -1) {
- // Find the first character in the AM/PM text that is unique.
- final KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
- final CharSequence amText = mAmText.toLowerCase(mCurrentLocale);
- final CharSequence pmText = mPmText.toLowerCase(mCurrentLocale);
- final int N = Math.min(amText.length(), pmText.length());
- for (int i = 0; i < N; i++) {
- final char amChar = amText.charAt(i);
- final char pmChar = pmText.charAt(i);
- if (amChar != pmChar) {
- // There should be 4 events: a down and up for both AM and PM.
- final KeyEvent[] events = kcm.getEvents(new char[] { amChar, pmChar });
- if (events != null && events.length == 4) {
- mAmKeyCode = events[0].getKeyCode();
- mPmKeyCode = events[2].getKeyCode();
- } else {
- Log.e(TAG, "Unable to find keycodes for AM and PM.");
- }
- break;
+ private final Runnable mCommitMinute = new Runnable() {
+ @Override
+ public void run() {
+ setCurrentMinute(mMinuteView.getValue());
+ }
+ };
+
+ private final View.OnFocusChangeListener mFocusListener = new View.OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean focused) {
+ if (focused) {
+ switch (v.getId()) {
+ case R.id.am_label:
+ setAmOrPm(AM);
+ break;
+ case R.id.pm_label:
+ setAmOrPm(PM);
+ break;
+ case R.id.hours:
+ setCurrentItemShowing(HOUR_INDEX, true, true);
+ break;
+ case R.id.minutes:
+ setCurrentItemShowing(MINUTE_INDEX, true, true);
+ break;
+ default:
+ // Failed to handle this click, don't vibrate.
+ return;
}
+
+ tryVibrate();
}
}
-
- if (amOrPm == AM) {
- return mAmKeyCode;
- } else if (amOrPm == PM) {
- return mPmKeyCode;
- }
-
- return -1;
- }
-
- /**
- * Create a tree for deciding what keys can legally be typed.
- */
- private void generateLegalTimesTree() {
- // Create a quick cache of numbers to their keycodes.
- final int k0 = KeyEvent.KEYCODE_0;
- final int k1 = KeyEvent.KEYCODE_1;
- final int k2 = KeyEvent.KEYCODE_2;
- final int k3 = KeyEvent.KEYCODE_3;
- final int k4 = KeyEvent.KEYCODE_4;
- final int k5 = KeyEvent.KEYCODE_5;
- final int k6 = KeyEvent.KEYCODE_6;
- final int k7 = KeyEvent.KEYCODE_7;
- final int k8 = KeyEvent.KEYCODE_8;
- final int k9 = KeyEvent.KEYCODE_9;
-
- // The root of the tree doesn't contain any numbers.
- mLegalTimesTree = new Node();
- if (mIs24HourView) {
- // We'll be re-using these nodes, so we'll save them.
- Node minuteFirstDigit = new Node(k0, k1, k2, k3, k4, k5);
- Node minuteSecondDigit = new Node(k0, k1, k2, k3, k4, k5, k6, k7, k8, k9);
- // The first digit must be followed by the second digit.
- minuteFirstDigit.addChild(minuteSecondDigit);
-
- // The first digit may be 0-1.
- Node firstDigit = new Node(k0, k1);
- mLegalTimesTree.addChild(firstDigit);
-
- // When the first digit is 0-1, the second digit may be 0-5.
- Node secondDigit = new Node(k0, k1, k2, k3, k4, k5);
- firstDigit.addChild(secondDigit);
- // We may now be followed by the first minute digit. E.g. 00:09, 15:58.
- secondDigit.addChild(minuteFirstDigit);
-
- // When the first digit is 0-1, and the second digit is 0-5, the third digit may be 6-9.
- Node thirdDigit = new Node(k6, k7, k8, k9);
- // The time must now be finished. E.g. 0:55, 1:08.
- secondDigit.addChild(thirdDigit);
-
- // When the first digit is 0-1, the second digit may be 6-9.
- secondDigit = new Node(k6, k7, k8, k9);
- firstDigit.addChild(secondDigit);
- // We must now be followed by the first minute digit. E.g. 06:50, 18:20.
- secondDigit.addChild(minuteFirstDigit);
-
- // The first digit may be 2.
- firstDigit = new Node(k2);
- mLegalTimesTree.addChild(firstDigit);
-
- // When the first digit is 2, the second digit may be 0-3.
- secondDigit = new Node(k0, k1, k2, k3);
- firstDigit.addChild(secondDigit);
- // We must now be followed by the first minute digit. E.g. 20:50, 23:09.
- secondDigit.addChild(minuteFirstDigit);
-
- // When the first digit is 2, the second digit may be 4-5.
- secondDigit = new Node(k4, k5);
- firstDigit.addChild(secondDigit);
- // We must now be followd by the last minute digit. E.g. 2:40, 2:53.
- secondDigit.addChild(minuteSecondDigit);
-
- // The first digit may be 3-9.
- firstDigit = new Node(k3, k4, k5, k6, k7, k8, k9);
- mLegalTimesTree.addChild(firstDigit);
- // We must now be followed by the first minute digit. E.g. 3:57, 8:12.
- firstDigit.addChild(minuteFirstDigit);
- } else {
- // We'll need to use the AM/PM node a lot.
- // Set up AM and PM to respond to "a" and "p".
- Node ampm = new Node(getAmOrPmKeyCode(AM), getAmOrPmKeyCode(PM));
-
- // The first hour digit may be 1.
- Node firstDigit = new Node(k1);
- mLegalTimesTree.addChild(firstDigit);
- // We'll allow quick input of on-the-hour times. E.g. 1pm.
- firstDigit.addChild(ampm);
-
- // When the first digit is 1, the second digit may be 0-2.
- Node secondDigit = new Node(k0, k1, k2);
- firstDigit.addChild(secondDigit);
- // Also for quick input of on-the-hour times. E.g. 10pm, 12am.
- secondDigit.addChild(ampm);
-
- // When the first digit is 1, and the second digit is 0-2, the third digit may be 0-5.
- Node thirdDigit = new Node(k0, k1, k2, k3, k4, k5);
- secondDigit.addChild(thirdDigit);
- // The time may be finished now. E.g. 1:02pm, 1:25am.
- thirdDigit.addChild(ampm);
-
- // When the first digit is 1, the second digit is 0-2, and the third digit is 0-5,
- // the fourth digit may be 0-9.
- Node fourthDigit = new Node(k0, k1, k2, k3, k4, k5, k6, k7, k8, k9);
- thirdDigit.addChild(fourthDigit);
- // The time must be finished now. E.g. 10:49am, 12:40pm.
- fourthDigit.addChild(ampm);
-
- // When the first digit is 1, and the second digit is 0-2, the third digit may be 6-9.
- thirdDigit = new Node(k6, k7, k8, k9);
- secondDigit.addChild(thirdDigit);
- // The time must be finished now. E.g. 1:08am, 1:26pm.
- thirdDigit.addChild(ampm);
-
- // When the first digit is 1, the second digit may be 3-5.
- secondDigit = new Node(k3, k4, k5);
- firstDigit.addChild(secondDigit);
-
- // When the first digit is 1, and the second digit is 3-5, the third digit may be 0-9.
- thirdDigit = new Node(k0, k1, k2, k3, k4, k5, k6, k7, k8, k9);
- secondDigit.addChild(thirdDigit);
- // The time must be finished now. E.g. 1:39am, 1:50pm.
- thirdDigit.addChild(ampm);
-
- // The hour digit may be 2-9.
- firstDigit = new Node(k2, k3, k4, k5, k6, k7, k8, k9);
- mLegalTimesTree.addChild(firstDigit);
- // We'll allow quick input of on-the-hour-times. E.g. 2am, 5pm.
- firstDigit.addChild(ampm);
-
- // When the first digit is 2-9, the second digit may be 0-5.
- secondDigit = new Node(k0, k1, k2, k3, k4, k5);
- firstDigit.addChild(secondDigit);
-
- // When the first digit is 2-9, and the second digit is 0-5, the third digit may be 0-9.
- thirdDigit = new Node(k0, k1, k2, k3, k4, k5, k6, k7, k8, k9);
- secondDigit.addChild(thirdDigit);
- // The time must be finished now. E.g. 2:57am, 9:30pm.
- thirdDigit.addChild(ampm);
- }
- }
-
- /**
- * Simple node class to be used for traversal to check for legal times.
- * mLegalKeys represents the keys that can be typed to get to the node.
- * mChildren are the children that can be reached from this node.
- */
- private class Node {
- private int[] mLegalKeys;
- private ArrayList<Node> mChildren;
-
- public Node(int... legalKeys) {
- mLegalKeys = legalKeys;
- mChildren = new ArrayList<Node>();
- }
-
- public void addChild(Node child) {
- mChildren.add(child);
- }
-
- public boolean containsKey(int key) {
- for (int i = 0; i < mLegalKeys.length; i++) {
- if (mLegalKeys[i] == key) {
- return true;
- }
- }
- return false;
- }
-
- public Node canReach(int key) {
- if (mChildren == null) {
- return null;
- }
- for (Node child : mChildren) {
- if (child.containsKey(key)) {
- return child;
- }
- }
- return null;
- }
- }
+ };
private final View.OnClickListener mClickListener = new View.OnClickListener() {
@Override
@@ -1347,28 +884,55 @@
}
};
- private final View.OnKeyListener mKeyListener = new View.OnKeyListener() {
- @Override
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- if (event.getAction() == KeyEvent.ACTION_UP) {
- return processKeyUp(keyCode);
+ /**
+ * Delegates unhandled touches in a view group to the nearest child view.
+ */
+ private static class NearestTouchDelegate implements View.OnTouchListener {
+ private View mInitialTouchTarget;
+
+ @Override
+ public boolean onTouch(View view, MotionEvent motionEvent) {
+ final int actionMasked = motionEvent.getActionMasked();
+ if (actionMasked == MotionEvent.ACTION_DOWN) {
+ mInitialTouchTarget = findNearestChild((ViewGroup) view,
+ (int) motionEvent.getX(), (int) motionEvent.getY());
+ }
+
+ final View child = mInitialTouchTarget;
+ if (child == null) {
+ return false;
+ }
+
+ final float offsetX = view.getScrollX() - child.getLeft();
+ final float offsetY = view.getScrollY() - child.getTop();
+ motionEvent.offsetLocation(offsetX, offsetY);
+ final boolean handled = child.dispatchTouchEvent(motionEvent);
+ motionEvent.offsetLocation(-offsetX, -offsetY);
+
+ if (actionMasked == MotionEvent.ACTION_UP
+ || actionMasked == MotionEvent.ACTION_CANCEL) {
+ mInitialTouchTarget = null;
+ }
+
+ return handled;
}
- return false;
- }
- };
- private final View.OnFocusChangeListener mFocusListener = new View.OnFocusChangeListener() {
- @Override
- public void onFocusChange(View v, boolean hasFocus) {
- if (!hasFocus && mInKbMode && isTypedTimeFullyLegal()) {
- finishKbMode();
+ private View findNearestChild(ViewGroup v, int x, int y) {
+ View bestChild = null;
+ int bestDist = Integer.MAX_VALUE;
- if (mOnTimeChangedListener != null) {
- mOnTimeChangedListener.onTimeChanged(mDelegator,
- mRadialTimePickerView.getCurrentHour(),
- mRadialTimePickerView.getCurrentMinute());
+ for (int i = 0, count = v.getChildCount(); i < count; i++) {
+ final View child = v.getChildAt(i);
+ final int dX = x - (child.getLeft() + child.getWidth() / 2);
+ final int dY = y - (child.getTop() + child.getHeight() / 2);
+ final int dist = dX * dX + dY * dY;
+ if (bestDist > dist) {
+ bestChild = child;
+ bestDist = dist;
}
}
+
+ return bestChild;
}
- };
+ }
}
diff --git a/core/java/android/widget/ViewFlipper.java b/core/java/android/widget/ViewFlipper.java
index 94e7ba1..65af7aa 100644
--- a/core/java/android/widget/ViewFlipper.java
+++ b/core/java/android/widget/ViewFlipper.java
@@ -96,7 +96,7 @@
// home screen. Therefore, we register the receiver as the current
// user not the one the context is for.
getContext().registerReceiverAsUser(mReceiver, android.os.Process.myUserHandle(),
- filter, null, mHandler);
+ filter, null, getHandler());
if (mAutoStart) {
// Automatically start when requested
@@ -173,10 +173,9 @@
if (running != mRunning) {
if (running) {
showOnly(mWhichChild, flipNow);
- Message msg = mHandler.obtainMessage(FLIP_MSG);
- mHandler.sendMessageDelayed(msg, mFlipInterval);
+ postDelayed(mFlipRunnable, mFlipInterval);
} else {
- mHandler.removeMessages(FLIP_MSG);
+ removeCallbacks(mFlipRunnable);
}
mRunning = running;
}
@@ -209,17 +208,12 @@
return mAutoStart;
}
- private final int FLIP_MSG = 1;
-
- private final Handler mHandler = new Handler() {
+ private final Runnable mFlipRunnable = new Runnable() {
@Override
- public void handleMessage(Message msg) {
- if (msg.what == FLIP_MSG) {
- if (mRunning) {
- showNext();
- msg = obtainMessage(FLIP_MSG);
- sendMessageDelayed(msg, mFlipInterval);
- }
+ public void run() {
+ if (mRunning) {
+ showNext();
+ postDelayed(mFlipRunnable, mFlipInterval);
}
}
};
diff --git a/core/java/android/widget/YearPickerView.java b/core/java/android/widget/YearPickerView.java
index 89e59f9..96624d2 100644
--- a/core/java/android/widget/YearPickerView.java
+++ b/core/java/android/widget/YearPickerView.java
@@ -38,8 +38,6 @@
private OnYearSelectedListener mOnYearSelectedListener;
- private long mCurrentTimeMillis;
-
public YearPickerView(Context context, AttributeSet attrs) {
this(context, attrs, R.attr.listViewStyle);
}
@@ -79,10 +77,6 @@
mOnYearSelectedListener = listener;
}
- public void setDate(long currentTimeMillis) {
- mCurrentTimeMillis = currentTimeMillis;
- }
-
/**
* Sets the currently selected year. Jumps immediately to the new year.
*
diff --git a/core/java/android/widget/ZoomButton.java b/core/java/android/widget/ZoomButton.java
index 0255bdb..7d4f8ed 100644
--- a/core/java/android/widget/ZoomButton.java
+++ b/core/java/android/widget/ZoomButton.java
@@ -17,7 +17,6 @@
package android.widget;
import android.content.Context;
-import android.os.Handler;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -26,12 +25,11 @@
public class ZoomButton extends ImageButton implements OnLongClickListener {
- private final Handler mHandler;
private final Runnable mRunnable = new Runnable() {
public void run() {
if (hasOnClickListeners() && mIsInLongpress && isEnabled()) {
callOnClick();
- mHandler.postDelayed(this, mZoomSpeed);
+ postDelayed(this, mZoomSpeed);
}
}
};
@@ -53,7 +51,6 @@
public ZoomButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- mHandler = new Handler();
setOnLongClickListener(this);
}
@@ -72,7 +69,7 @@
public boolean onLongClick(View v) {
mIsInLongpress = true;
- mHandler.post(mRunnable);
+ post(mRunnable);
return true;
}
diff --git a/core/java/com/android/internal/alsa/AlsaCardsParser.java b/core/java/com/android/internal/alsa/AlsaCardsParser.java
index 67ee085..17e8c9c 100644
--- a/core/java/com/android/internal/alsa/AlsaCardsParser.java
+++ b/core/java/com/android/internal/alsa/AlsaCardsParser.java
@@ -94,6 +94,12 @@
public String textFormat() {
return mCardName + " : " + mCardDescription;
}
+
+ public void log(int listIndex) {
+ Slog.d(TAG, "" + listIndex +
+ " [" + mCardNum + " " + mCardName + " : " + mCardDescription +
+ " usb:" + mIsUsb);
+ }
}
public AlsaCardsParser() {}
@@ -169,9 +175,41 @@
// return -1 if none found
public int getDefaultUsbCard() {
+ // save the current list of devices
+ ArrayList<AlsaCardsParser.AlsaCardRecord> prevRecs = mCardRecords;
+ if (DEBUG) {
+ LogDevices("Previous Devices:", prevRecs);
+ }
+
+ // get the new list of devices
+ scan();
+ if (DEBUG) {
+ LogDevices("Current Devices:", mCardRecords);
+ }
+
+ // Calculate the difference between the old and new device list
+ ArrayList<AlsaCardRecord> newRecs = getNewCardRecords(prevRecs);
+ if (DEBUG) {
+ LogDevices("New Devices:", newRecs);
+ }
+
// Choose the most-recently added EXTERNAL card
+ // Check recently added devices
+ for (AlsaCardRecord rec : newRecs) {
+ if (DEBUG) {
+ Slog.d(TAG, rec.mCardName + " card:" + rec.mCardNum + " usb:" + rec.mIsUsb);
+ }
+ if (rec.mIsUsb) {
+ // Found it
+ return rec.mCardNum;
+ }
+ }
+
// or return the first added EXTERNAL card?
- for (AlsaCardRecord rec : mCardRecords) {
+ for (AlsaCardRecord rec : prevRecs) {
+ if (DEBUG) {
+ Slog.d(TAG, rec.mCardName + " card:" + rec.mCardNum + " usb:" + rec.mIsUsb);
+ }
if (rec.mIsUsb) {
return rec.mCardNum;
}
@@ -183,11 +221,17 @@
public int getDefaultCard() {
// return an external card if possible
int card = getDefaultUsbCard();
+ if (DEBUG) {
+ Slog.d(TAG, "getDefaultCard() default usb card:" + card);
+ }
if (card < 0 && getNumCardRecords() > 0) {
// otherwise return the (internal) card with the highest number
card = getCardRecordAt(getNumCardRecords() - 1).mCardNum;
}
+ if (DEBUG) {
+ Slog.d(TAG, " returns card:" + card);
+ }
return card;
}
@@ -222,4 +266,13 @@
}
}
}
+
+ static public void LogDevices(String caption, ArrayList<AlsaCardRecord> deviceList) {
+ Slog.d(TAG, caption + " ----------------");
+ int listIndex = 0;
+ for (AlsaCardRecord device : deviceList) {
+ device.log(listIndex++);
+ }
+ Slog.d(TAG, "----------------");
+ }
}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 255e23a..b7b7400 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -373,6 +373,11 @@
int targetsToQuery = 0;
for (int i = 0, N = adapter.getDisplayResolveInfoCount(); i < N; i++) {
final DisplayResolveInfo dri = adapter.getDisplayResolveInfo(i);
+ if (adapter.getScore(dri) == 0) {
+ // A score of 0 means the app hasn't been used in some time;
+ // don't query it as it's not likely to be relevant.
+ continue;
+ }
final ActivityInfo ai = dri.getResolveInfo().activityInfo;
final Bundle md = ai.metaData;
final String serviceName = md != null ? convertServiceName(ai.packageName,
@@ -920,7 +925,7 @@
@Override
public int compare(ChooserTarget lhs, ChooserTarget rhs) {
// Descending order
- return (int) Math.signum(lhs.getScore() - rhs.getScore());
+ return (int) Math.signum(rhs.getScore() - lhs.getScore());
}
}
@@ -1017,7 +1022,15 @@
final RowScale rs = new RowScale(ChooserRowAdapter.this, 0.f, 1.f)
.setInterpolator(mInterpolator);
mServiceTargetScale[i] = rs;
- rs.startAnimation();
+ }
+
+ // Start the animations in a separate loop.
+ // The process of starting animations will result in
+ // binding views to set up initial values, and we must
+ // have ALL of the new RowScale objects created above before
+ // we get started.
+ for (int i = oldRCount; i < rcount; i++) {
+ mServiceTargetScale[i].startAnimation();
}
}
@@ -1097,17 +1110,19 @@
for (int i = 0; i < mColumnCount; i++) {
final View v = mChooserListAdapter.createView(row);
+ final int column = i;
v.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
- startSelected(holder.itemIndex, false, true);
+ startSelected(holder.itemIndices[column], false, true);
}
});
v.setOnLongClickListener(new OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
showAppDetails(
- mChooserListAdapter.resolveInfoForPosition(holder.itemIndex, true));
+ mChooserListAdapter.resolveInfoForPosition(
+ holder.itemIndices[column], true));
return true;
}
});
@@ -1165,8 +1180,8 @@
final View v = holder.cells[i];
if (start + i <= end) {
v.setVisibility(View.VISIBLE);
- holder.itemIndex = start + i;
- mChooserListAdapter.bindView(holder.itemIndex, v);
+ holder.itemIndices[i] = start + i;
+ mChooserListAdapter.bindView(holder.itemIndices[i], v);
} else {
v.setVisibility(View.GONE);
}
@@ -1197,11 +1212,12 @@
final View[] cells;
final ViewGroup row;
int measuredRowHeight;
- int itemIndex;
+ int[] itemIndices;
public RowViewHolder(ViewGroup row, int cellCount) {
this.row = row;
this.cells = new View[cellCount];
+ this.itemIndices = new int[cellCount];
}
public void measure() {
@@ -1389,7 +1405,7 @@
final View v = mChooserRowAdapter.getView(pos, mCachedView, mListView);
int height = ((RowViewHolder) (v.getTag())).measuredRowHeight;
- offset += (int) (height * mChooserRowAdapter.getRowScale(pos) * chooserTargetRows);
+ offset += (int) (height * mChooserRowAdapter.getRowScale(pos));
if (vt >= 0) {
mCachedViewType = vt;
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 3cddbf6..3b9b8db 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -117,7 +117,7 @@
void noteWifiRadioPowerState(int powerState, long timestampNs);
void noteNetworkInterfaceType(String iface, int type);
void noteNetworkStatsEnabled();
- void noteDeviceIdleMode(boolean enabled, String activeReason, int activeUid);
+ void noteDeviceIdleMode(int mode, String activeReason, int activeUid);
void setBatteryState(int status, int health, int plugType, int level, int temp, int volt);
long getAwakeTimeBattery();
long getAwakeTimePlugged();
diff --git a/core/java/com/android/internal/logging/MetricsConstants.java b/core/java/com/android/internal/logging/MetricsConstants.java
index b90cb36..6cc8ba9 100644
--- a/core/java/com/android/internal/logging/MetricsConstants.java
+++ b/core/java/com/android/internal/logging/MetricsConstants.java
@@ -262,6 +262,24 @@
public static final int TUNER_DEMO_MODE_ON = 236;
public static final int TUNER_BATTERY_PERCENTAGE = 237;
public static final int FUELGAUGE_INACTIVE_APPS = 238;
+ public static final int ACTION_ASSIST_LONG_PRESS = 239;
+ public static final int FINGERPRINT_ENROLLING = 240;
+ public static final int FINGERPRINT_FIND_SENSOR = 241;
+ public static final int FINGERPRINT_ENROLL_FINISH = 242;
+ public static final int FINGERPRINT_ENROLL_INTRO = 243;
+ public static final int FINGERPRINT_ENROLL_ONBOARD = 244;
+ public static final int FINGERPRINT_ENROLL_SIDECAR = 245;
+ public static final int FINGERPRINT_ENROLLING_SETUP = 246;
+ public static final int FINGERPRINT_FIND_SENSOR_SETUP = 247;
+ public static final int FINGERPRINT_ENROLL_FINISH_SETUP = 248;
+ public static final int FINGERPRINT_ENROLL_INTRO_SETUP = 249;
+ public static final int FINGERPRINT_ENROLL_ONBOARD_SETUP = 250;
+ public static final int ACTION_FINGERPRINT_ENROLL = 251;
+ public static final int ACTION_FINGERPRINT_AUTH = 252;
+ public static final int ACTION_FINGERPRINT_DELETE = 253;
+ public static final int ACTION_FINGERPRINT_RENAME = 254;
+ public static final int ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE = 255;
+ public static final int ACTION_WIGGLE_CAMERA_GESTURE = 256;
// These constants must match those in the analytic pipeline, do not edit.
// Add temporary values to the top of MetricsLogger instead.
diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java
index c25db65..2c5e50c 100644
--- a/core/java/com/android/internal/logging/MetricsLogger.java
+++ b/core/java/com/android/internal/logging/MetricsLogger.java
@@ -27,25 +27,6 @@
*/
public class MetricsLogger implements MetricsConstants {
// Temporary constants go here, to await migration to MetricsConstants.
- // next value is 239;
- public static final int ACTION_ASSIST_LONG_PRESS = 239;
- public static final int FINGERPRINT_ENROLLING = 240;
- public static final int FINGERPRINT_FIND_SENSOR = 241;
- public static final int FINGERPRINT_ENROLL_FINISH = 242;
- public static final int FINGERPRINT_ENROLL_INTRO = 243;
- public static final int FINGERPRINT_ENROLL_ONBOARD = 244;
- public static final int FINGERPRINT_ENROLL_SIDECAR = 245;
- public static final int FINGERPRINT_ENROLLING_SETUP = 246;
- public static final int FINGERPRINT_FIND_SENSOR_SETUP = 247;
- public static final int FINGERPRINT_ENROLL_FINISH_SETUP = 248;
- public static final int FINGERPRINT_ENROLL_INTRO_SETUP = 249;
- public static final int FINGERPRINT_ENROLL_ONBOARD_SETUP = 250;
- public static final int ACTION_FINGERPRINT_ENROLL = 251;
- public static final int ACTION_FINGERPRINT_AUTH = 252;
- public static final int ACTION_FINGERPRINT_DELETE = 253;
- public static final int ACTION_FINGERPRINT_RENAME = 254;
- public static final int ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE = 255;
- public static final int ACTION_WIGGLE_CAMERA_GESTURE = 256;
public static final int QS_LOCK_TILE = 257;
public static final int QS_USER_TILE = 258;
public static final int QS_BATTERY_TILE = 259;
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 64b7768..f73df00 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -105,7 +105,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 132 + (USE_OLD_HISTORY ? 1000 : 0);
+ private static final int VERSION = 135 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS = 2000;
@@ -338,8 +338,15 @@
boolean mDeviceIdling;
StopwatchTimer mDeviceIdlingTimer;
- boolean mDeviceIdleModeEnabled;
- StopwatchTimer mDeviceIdleModeEnabledTimer;
+ boolean mDeviceLightIdling;
+ StopwatchTimer mDeviceLightIdlingTimer;
+
+ int mDeviceIdleMode;
+ long mLastIdleTimeStart;
+ long mLongestLightIdleTime;
+ long mLongestFullIdleTime;
+ StopwatchTimer mDeviceIdleModeLightTimer;
+ StopwatchTimer mDeviceIdleModeFullTimer;
boolean mPhoneOn;
StopwatchTimer mPhoneOnTimer;
@@ -3219,42 +3226,69 @@
}
}
- public void noteDeviceIdleModeLocked(boolean enabled, String activeReason, int activeUid) {
+ public void noteDeviceIdleModeLocked(int mode, String activeReason, int activeUid) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
- boolean nowIdling = enabled;
- if (mDeviceIdling && !enabled && activeReason == null) {
+ boolean nowIdling = mode == DEVICE_IDLE_MODE_FULL;
+ if (mDeviceIdling && !nowIdling && activeReason == null) {
// We don't go out of general idling mode until explicitly taken out of
// device idle through going active or significant motion.
nowIdling = true;
}
+ boolean nowLightIdling = mode == DEVICE_IDLE_MODE_LIGHT;
+ if (mDeviceLightIdling && !nowLightIdling && !nowIdling && activeReason == null) {
+ // We don't go out of general light idling mode until explicitly taken out of
+ // device idle through going active or significant motion.
+ nowLightIdling = true;
+ }
+ if (activeReason != null && (mDeviceIdling || mDeviceLightIdling)) {
+ addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_ACTIVE,
+ activeReason, activeUid);
+ }
if (mDeviceIdling != nowIdling) {
mDeviceIdling = nowIdling;
int stepState = nowIdling ? STEP_LEVEL_MODE_DEVICE_IDLE : 0;
mModStepMode |= (mCurStepMode&STEP_LEVEL_MODE_DEVICE_IDLE) ^ stepState;
mCurStepMode = (mCurStepMode&~STEP_LEVEL_MODE_DEVICE_IDLE) | stepState;
- if (enabled) {
+ if (nowIdling) {
mDeviceIdlingTimer.startRunningLocked(elapsedRealtime);
} else {
mDeviceIdlingTimer.stopRunningLocked(elapsedRealtime);
}
}
- if (mDeviceIdleModeEnabled != enabled) {
- mDeviceIdleModeEnabled = enabled;
- addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_ACTIVE,
- activeReason != null ? activeReason : "", activeUid);
- if (enabled) {
- mHistoryCur.states2 |= HistoryItem.STATE2_DEVICE_IDLE_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "Device idle mode enabled to: "
- + Integer.toHexString(mHistoryCur.states2));
- mDeviceIdleModeEnabledTimer.startRunningLocked(elapsedRealtime);
+ if (mDeviceLightIdling != nowLightIdling) {
+ mDeviceLightIdling = nowLightIdling;
+ if (nowLightIdling) {
+ mDeviceLightIdlingTimer.startRunningLocked(elapsedRealtime);
} else {
- mHistoryCur.states2 &= ~HistoryItem.STATE2_DEVICE_IDLE_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "Device idle mode disabled to: "
- + Integer.toHexString(mHistoryCur.states2));
- mDeviceIdleModeEnabledTimer.stopRunningLocked(elapsedRealtime);
+ mDeviceLightIdlingTimer.stopRunningLocked(elapsedRealtime);
}
+ }
+ if (mDeviceIdleMode != mode) {
+ mHistoryCur.states2 = (mHistoryCur.states2 & ~HistoryItem.STATE2_DEVICE_IDLE_MASK)
+ | (mode << HistoryItem.STATE2_DEVICE_IDLE_SHIFT);
+ if (DEBUG_HISTORY) Slog.v(TAG, "Device idle mode changed to: "
+ + Integer.toHexString(mHistoryCur.states2));
addHistoryRecordLocked(elapsedRealtime, uptime);
+ long lastDuration = elapsedRealtime - mLastIdleTimeStart;
+ mLastIdleTimeStart = elapsedRealtime;
+ if (mDeviceIdleMode == DEVICE_IDLE_MODE_LIGHT) {
+ if (lastDuration > mLongestLightIdleTime) {
+ mLongestLightIdleTime = lastDuration;
+ }
+ mDeviceIdleModeLightTimer.stopRunningLocked(elapsedRealtime);
+ } else if (mDeviceIdleMode == DEVICE_IDLE_MODE_FULL) {
+ if (lastDuration > mLongestFullIdleTime) {
+ mLongestFullIdleTime = lastDuration;
+ }
+ mDeviceIdleModeFullTimer.stopRunningLocked(elapsedRealtime);
+ }
+ if (mode == DEVICE_IDLE_MODE_LIGHT) {
+ mDeviceIdleModeLightTimer.startRunningLocked(elapsedRealtime);
+ } else if (mode == DEVICE_IDLE_MODE_FULL) {
+ mDeviceIdleModeFullTimer.startRunningLocked(elapsedRealtime);
+ }
+ mDeviceIdleMode = mode;
}
}
@@ -4140,20 +4174,55 @@
return mPowerSaveModeEnabledTimer.getCountLocked(which);
}
- @Override public long getDeviceIdleModeEnabledTime(long elapsedRealtimeUs, int which) {
- return mDeviceIdleModeEnabledTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
+ @Override public long getDeviceIdleModeTime(int mode, long elapsedRealtimeUs,
+ int which) {
+ switch (mode) {
+ case DEVICE_IDLE_MODE_LIGHT:
+ return mDeviceIdleModeLightTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
+ case DEVICE_IDLE_MODE_FULL:
+ return mDeviceIdleModeFullTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
+ }
+ return 0;
}
- @Override public int getDeviceIdleModeEnabledCount(int which) {
- return mDeviceIdleModeEnabledTimer.getCountLocked(which);
+ @Override public int getDeviceIdleModeCount(int mode, int which) {
+ switch (mode) {
+ case DEVICE_IDLE_MODE_LIGHT:
+ return mDeviceIdleModeLightTimer.getCountLocked(which);
+ case DEVICE_IDLE_MODE_FULL:
+ return mDeviceIdleModeFullTimer.getCountLocked(which);
+ }
+ return 0;
}
- @Override public long getDeviceIdlingTime(long elapsedRealtimeUs, int which) {
- return mDeviceIdlingTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
+ @Override public long getLongestDeviceIdleModeTime(int mode) {
+ switch (mode) {
+ case DEVICE_IDLE_MODE_LIGHT:
+ return mLongestLightIdleTime;
+ case DEVICE_IDLE_MODE_FULL:
+ return mLongestFullIdleTime;
+ }
+ return 0;
}
- @Override public int getDeviceIdlingCount(int which) {
- return mDeviceIdlingTimer.getCountLocked(which);
+ @Override public long getDeviceIdlingTime(int mode, long elapsedRealtimeUs, int which) {
+ switch (mode) {
+ case DEVICE_IDLE_MODE_LIGHT:
+ return mDeviceLightIdlingTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
+ case DEVICE_IDLE_MODE_FULL:
+ return mDeviceIdlingTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
+ }
+ return 0;
+ }
+
+ @Override public int getDeviceIdlingCount(int mode, int which) {
+ switch (mode) {
+ case DEVICE_IDLE_MODE_LIGHT:
+ return mDeviceLightIdlingTimer.getCountLocked(which);
+ case DEVICE_IDLE_MODE_FULL:
+ return mDeviceIdlingTimer.getCountLocked(which);
+ }
+ return 0;
}
@Override public int getNumConnectivityChange(int which) {
@@ -6855,7 +6924,9 @@
}
mInteractiveTimer = new StopwatchTimer(null, -10, null, mOnBatteryTimeBase);
mPowerSaveModeEnabledTimer = new StopwatchTimer(null, -2, null, mOnBatteryTimeBase);
- mDeviceIdleModeEnabledTimer = new StopwatchTimer(null, -11, null, mOnBatteryTimeBase);
+ mDeviceIdleModeLightTimer = new StopwatchTimer(null, -11, null, mOnBatteryTimeBase);
+ mDeviceIdleModeFullTimer = new StopwatchTimer(null, -14, null, mOnBatteryTimeBase);
+ mDeviceLightIdlingTimer = new StopwatchTimer(null, -15, null, mOnBatteryTimeBase);
mDeviceIdlingTimer = new StopwatchTimer(null, -12, null, mOnBatteryTimeBase);
mPhoneOnTimer = new StopwatchTimer(null, -3, null, mOnBatteryTimeBase);
for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
@@ -7484,7 +7555,11 @@
}
mInteractiveTimer.reset(false);
mPowerSaveModeEnabledTimer.reset(false);
- mDeviceIdleModeEnabledTimer.reset(false);
+ mLongestLightIdleTime = 0;
+ mLongestFullIdleTime = 0;
+ mDeviceIdleModeLightTimer.reset(false);
+ mDeviceIdleModeFullTimer.reset(false);
+ mDeviceLightIdlingTimer.reset(false);
mDeviceIdlingTimer.reset(false);
mPhoneOnTimer.reset(false);
mAudioOnTimer.reset(false);
@@ -9224,7 +9299,11 @@
mInteractiveTimer.readSummaryFromParcelLocked(in);
mPhoneOn = false;
mPowerSaveModeEnabledTimer.readSummaryFromParcelLocked(in);
- mDeviceIdleModeEnabledTimer.readSummaryFromParcelLocked(in);
+ mLongestLightIdleTime = in.readLong();
+ mLongestFullIdleTime = in.readLong();
+ mDeviceIdleModeLightTimer.readSummaryFromParcelLocked(in);
+ mDeviceIdleModeFullTimer.readSummaryFromParcelLocked(in);
+ mDeviceLightIdlingTimer.readSummaryFromParcelLocked(in);
mDeviceIdlingTimer.readSummaryFromParcelLocked(in);
mPhoneOnTimer.readSummaryFromParcelLocked(in);
for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
@@ -9558,7 +9637,11 @@
}
mInteractiveTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
mPowerSaveModeEnabledTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
- mDeviceIdleModeEnabledTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ out.writeLong(mLongestLightIdleTime);
+ out.writeLong(mLongestFullIdleTime);
+ mDeviceIdleModeLightTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ mDeviceIdleModeFullTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ mDeviceLightIdlingTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
mDeviceIdlingTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
mPhoneOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
@@ -9892,7 +9975,11 @@
mInteractiveTimer = new StopwatchTimer(null, -10, null, mOnBatteryTimeBase, in);
mPhoneOn = false;
mPowerSaveModeEnabledTimer = new StopwatchTimer(null, -2, null, mOnBatteryTimeBase, in);
- mDeviceIdleModeEnabledTimer = new StopwatchTimer(null, -11, null, mOnBatteryTimeBase, in);
+ mLongestLightIdleTime = in.readLong();
+ mLongestFullIdleTime = in.readLong();
+ mDeviceIdleModeLightTimer = new StopwatchTimer(null, -14, null, mOnBatteryTimeBase, in);
+ mDeviceIdleModeFullTimer = new StopwatchTimer(null, -11, null, mOnBatteryTimeBase, in);
+ mDeviceLightIdlingTimer = new StopwatchTimer(null, -15, null, mOnBatteryTimeBase, in);
mDeviceIdlingTimer = new StopwatchTimer(null, -12, null, mOnBatteryTimeBase, in);
mPhoneOnTimer = new StopwatchTimer(null, -3, null, mOnBatteryTimeBase, in);
for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
@@ -10053,7 +10140,11 @@
}
mInteractiveTimer.writeToParcel(out, uSecRealtime);
mPowerSaveModeEnabledTimer.writeToParcel(out, uSecRealtime);
- mDeviceIdleModeEnabledTimer.writeToParcel(out, uSecRealtime);
+ out.writeLong(mLongestLightIdleTime);
+ out.writeLong(mLongestFullIdleTime);
+ mDeviceIdleModeLightTimer.writeToParcel(out, uSecRealtime);
+ mDeviceIdleModeFullTimer.writeToParcel(out, uSecRealtime);
+ mDeviceLightIdlingTimer.writeToParcel(out, uSecRealtime);
mDeviceIdlingTimer.writeToParcel(out, uSecRealtime);
mPhoneOnTimer.writeToParcel(out, uSecRealtime);
for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
@@ -10188,8 +10279,12 @@
mInteractiveTimer.logState(pr, " ");
pr.println("*** Power save mode timer:");
mPowerSaveModeEnabledTimer.logState(pr, " ");
- pr.println("*** Device idle mode timer:");
- mDeviceIdleModeEnabledTimer.logState(pr, " ");
+ pr.println("*** Device idle mode light timer:");
+ mDeviceIdleModeLightTimer.logState(pr, " ");
+ pr.println("*** Device idle mode full timer:");
+ mDeviceIdleModeFullTimer.logState(pr, " ");
+ pr.println("*** Device light idling timer:");
+ mDeviceLightIdlingTimer.logState(pr, " ");
pr.println("*** Device idling timer:");
mDeviceIdlingTimer.logState(pr, " ");
pr.println("*** Phone timer:");
diff --git a/core/java/com/android/internal/os/InstallerConnection.java b/core/java/com/android/internal/os/InstallerConnection.java
index 13d046e..830da79 100644
--- a/core/java/com/android/internal/os/InstallerConnection.java
+++ b/core/java/com/android/internal/os/InstallerConnection.java
@@ -21,6 +21,8 @@
import android.os.SystemClock;
import android.util.Slog;
+import com.android.internal.util.Preconditions;
+
import libcore.io.IoUtils;
import libcore.io.Streams;
@@ -42,11 +44,22 @@
private OutputStream mOut;
private LocalSocket mSocket;
+ private volatile Object mWarnIfHeld;
+
private final byte buf[] = new byte[1024];
public InstallerConnection() {
}
+ /**
+ * Yell loudly if someone tries making future calls while holding a lock on
+ * the given object.
+ */
+ public void setWarnIfHeld(Object warnIfHeld) {
+ Preconditions.checkState(mWarnIfHeld == null);
+ mWarnIfHeld = Preconditions.checkNotNull(warnIfHeld);
+ }
+
public synchronized String transact(String cmd) {
if (!connect()) {
Slog.e(TAG, "connection failed");
@@ -84,6 +97,11 @@
}
public int execute(String cmd) {
+ if (mWarnIfHeld != null && Thread.holdsLock(mWarnIfHeld)) {
+ Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName() + " is holding 0x"
+ + Integer.toHexString(System.identityHashCode(mWarnIfHeld)), new Throwable());
+ }
+
String res = transact(cmd);
try {
return Integer.parseInt(res);
diff --git a/core/java/com/android/internal/os/SomeArgs.java b/core/java/com/android/internal/os/SomeArgs.java
index c05e0d8..8fb56d4 100644
--- a/core/java/com/android/internal/os/SomeArgs.java
+++ b/core/java/com/android/internal/os/SomeArgs.java
@@ -47,6 +47,7 @@
public Object arg5;
public Object arg6;
public Object arg7;
+ public Object arg8;
public int argi1;
public int argi2;
public int argi3;
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index aaa89df..f78d8d8 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -70,6 +70,7 @@
private static final String TAG = "Zygote";
private static final String PROPERTY_DISABLE_OPENGL_PRELOADING = "ro.zygote.disable_gl_preload";
+ private static final String PROPERTY_RUNNING_IN_CONTAINER = "ro.boot.container";
private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
@@ -503,7 +504,6 @@
private static boolean startSystemServer(String abiList, String socketName)
throws MethodAndArgsCaller, RuntimeException {
long capabilities = posixCapabilitiesAsBits(
- OsConstants.CAP_BLOCK_SUSPEND,
OsConstants.CAP_KILL,
OsConstants.CAP_NET_ADMIN,
OsConstants.CAP_NET_BIND_SERVICE,
@@ -515,11 +515,15 @@
OsConstants.CAP_SYS_TIME,
OsConstants.CAP_SYS_TTY_CONFIG
);
+ /* Containers run without this capability, so avoid setting it in that case */
+ if (!SystemProperties.getBoolean(PROPERTY_RUNNING_IN_CONTAINER, false)) {
+ capabilities |= posixCapabilitiesAsBits(OsConstants.CAP_BLOCK_SUSPEND);
+ }
/* Hardcoded command line to start the system server */
String args[] = {
"--setuid=1000",
"--setgid=1000",
- "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1032,3001,3002,3003,3006,3007",
+ "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1032,3001,3002,3003,3006,3007,3009",
"--capabilities=" + capabilities + "," + capabilities,
"--nice-name=system_server",
"--runtime-args",
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index ab372d3..83f810f 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -16,11 +16,8 @@
package com.android.internal.policy;
-import static android.app.ActivityManager.FIRST_DYNAMIC_STACK_ID;
-import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.HOME_STACK_ID;
-import static android.app.ActivityManager.INVALID_STACK_ID;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.view.View.MeasureSpec.AT_MOST;
import static android.view.View.MeasureSpec.EXACTLY;
import static android.view.View.MeasureSpec.getMode;
@@ -30,10 +27,9 @@
import android.animation.Animator;
import android.animation.ObjectAnimator;
+import android.app.ActivityManager.StackId;
import android.app.ActivityManagerNative;
import android.app.SearchManager;
-import android.content.ContextWrapper;
-import android.content.res.Resources;
import android.os.Build;
import android.os.UserHandle;
@@ -125,6 +121,7 @@
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
+import android.widget.LinearLayout;
import android.widget.ImageView;
import android.widget.PopupWindow;
import android.widget.ProgressBar;
@@ -471,6 +468,30 @@
}
@Override
+ public void setDecorView(int layoutResID) {
+ View v = mLayoutInflater.inflate(layoutResID, null);
+ setDecorView(v);
+ }
+
+ @Override
+ public void setDecorView(View view) {
+ if (mContentParent == null) {
+ installDecor();
+ }
+
+ LinearLayout clientDecorPlaceholder =
+ (LinearLayout) findViewById(R.id.client_decor_placeholder);
+
+ if (clientDecorPlaceholder != null) {
+ clientDecorPlaceholder.removeAllViews();
+
+ if (view != null) {
+ clientDecorPlaceholder.addView(view);
+ }
+ }
+ }
+
+ @Override
public void addContentView(View view, ViewGroup.LayoutParams params) {
if (mContentParent == null) {
installDecor();
@@ -714,9 +735,9 @@
if (mWorkspaceId != workspaceId) {
mWorkspaceId = workspaceId;
// We might have to change the kind of surface before we do anything else.
- mNonClientDecorView.phoneWindowUpdated(hasNonClientDecor(mWorkspaceId),
- nonClientDecorHasShadow(mWorkspaceId));
- mDecor.enableNonClientDecor(hasNonClientDecor(workspaceId));
+ mNonClientDecorView.phoneWindowUpdated(StackId.hasWindowDecor(mWorkspaceId),
+ StackId.hasWindowShadow(mWorkspaceId));
+ mDecor.enableNonClientDecor(StackId.hasWindowDecor(workspaceId));
}
}
}
@@ -2383,7 +2404,7 @@
private PhoneWindow mWindow;
- public DecorView(Context context, int featureId) {
+ private DecorView(Context context, int featureId, PhoneWindow window) {
super(context);
mFeatureId = featureId;
@@ -2394,6 +2415,8 @@
mBarEnterExitDuration = context.getResources().getInteger(
R.integer.dock_enter_exit_duration);
+
+ setWindow(window);
}
public void setBackgroundFallback(int resId) {
@@ -3177,16 +3200,16 @@
int vis = show ? VISIBLE : INVISIBLE;
visibilityChanged = state.targetVisibility != vis;
state.targetVisibility = vis;
+ LayoutParams lp = (LayoutParams) view.getLayoutParams();
+ if (lp.height != resolvedHeight || lp.width != resolvedWidth
+ || lp.gravity != resolvedGravity || lp.rightMargin != rightMargin) {
+ lp.height = resolvedHeight;
+ lp.width = resolvedWidth;
+ lp.gravity = resolvedGravity;
+ lp.rightMargin = rightMargin;
+ view.setLayoutParams(lp);
+ }
if (show) {
- LayoutParams lp = (LayoutParams) view.getLayoutParams();
- if (lp.height != resolvedHeight || lp.width != resolvedWidth
- || lp.gravity != resolvedGravity || lp.rightMargin != rightMargin) {
- lp.height = resolvedHeight;
- lp.width = resolvedWidth;
- lp.gravity = resolvedGravity;
- lp.rightMargin = rightMargin;
- view.setLayoutParams(lp);
- }
view.setBackgroundColor(color);
}
}
@@ -3705,13 +3728,14 @@
}
/**
- * Returns true if the Window is free floating and has a shadow. Note that non overlapping
- * windows do not have a shadow since it could not be seen anyways (a small screen / tablet
+ * Returns true if the Window is free floating and has a shadow (although at some times
+ * it might not be displaying it, e.g. during a resize). Note that non overlapping windows
+ * do not have a shadow since it could not be seen anyways (a small screen / tablet
* "tiles" the windows side by side but does not overlap them).
* @return Returns true when the window has a shadow created by the non client decor.
**/
private boolean windowHasShadow() {
- return windowHasNonClientDecor() && getElevation() > 0;
+ return windowHasNonClientDecor() && StackId.hasWindowShadow(mWindow.mWorkspaceId);
}
void setWindow(PhoneWindow phoneWindow) {
@@ -3846,22 +3870,7 @@
context.setTheme(mTheme);
}
}
- return new DecorView(context, featureId);
- }
-
- protected void setFeatureFromAttrs(int featureId, TypedArray attrs,
- int drawableAttr, int alphaAttr) {
- Drawable d = attrs.getDrawable(drawableAttr);
- if (d != null) {
- requestFeature(featureId);
- setFeatureDefaultDrawable(featureId, d);
- }
- if ((getFeatures() & (1 << featureId)) != 0) {
- int alpha = attrs.getInt(alphaAttr, -1);
- if (alpha >= 0) {
- setFeatureDrawableAlpha(featureId, alpha);
- }
- }
+ return new DecorView(context, featureId, this);
}
protected ViewGroup generateLayout(DecorView decor) {
@@ -4210,7 +4219,7 @@
mWorkspaceId = getWorkspaceId();
// Only a non floating application window on one of the allowed workspaces can get a non
// client decor.
- if (!isFloating() && isApplication && mWorkspaceId < FIRST_DYNAMIC_STACK_ID) {
+ if (!isFloating() && isApplication && StackId.isStaticStack(mWorkspaceId)) {
// Dependent on the brightness of the used title we either use the
// dark or the light button frame.
if (nonClientDecorView == null) {
@@ -4226,11 +4235,13 @@
R.layout.non_client_decor_light, null);
}
}
- nonClientDecorView.setPhoneWindow(this, hasNonClientDecor(mWorkspaceId),
- nonClientDecorHasShadow(mWorkspaceId));
+ nonClientDecorView.setPhoneWindow(this, StackId.hasWindowDecor(mWorkspaceId),
+ StackId.hasWindowShadow(mWorkspaceId), getResizingBackgroundDrawable(),
+ mDecor.getContext().getDrawable(R.drawable.non_client_decor_title_focused));
}
// Tell the decor if it has a visible non client decor.
- mDecor.enableNonClientDecor(nonClientDecorView != null && hasNonClientDecor(mWorkspaceId));
+ mDecor.enableNonClientDecor(
+ nonClientDecorView != null&& StackId.hasWindowDecor(mWorkspaceId));
return nonClientDecorView;
}
@@ -4244,7 +4255,6 @@
mForceDecorInstall = false;
if (mDecor == null) {
mDecor = generateDecor(-1);
- mDecor.setWindow(this);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
@@ -5403,24 +5413,6 @@
return workspaceId;
}
- /**
- * Determines if the window should show a non client decor for the workspace it is in.
- * @param workspaceId The Id of the workspace which contains this window.
- * @Return Returns true if the window should show a non client decor.
- **/
- private boolean hasNonClientDecor(int workspaceId) {
- return workspaceId == FREEFORM_WORKSPACE_STACK_ID;
- }
-
- /**
- * Determines if the window should show a shadow or not, dependent on the workspace.
- * @param workspaceId The Id of the workspace which contains this window.
- * @Return Returns true if the window should show a shadow.
- **/
- private boolean nonClientDecorHasShadow(int workspaceId) {
- return workspaceId == FREEFORM_WORKSPACE_STACK_ID;
- }
-
@Override
public void setTheme(int resid) {
mTheme = resid;
@@ -5431,4 +5423,31 @@
}
}
}
+
+ /**
+ * Returns the color used to fill areas the app has not rendered content to yet when the user
+ * is resizing the window of an activity in multi-window mode.
+ * */
+ private Drawable getResizingBackgroundDrawable() {
+ final Context context = mDecor.getContext();
+
+ if (mBackgroundResource != 0) {
+ final Drawable drawable = context.getDrawable(mBackgroundResource);
+ if (drawable != null) {
+ return drawable;
+ }
+ }
+
+ if (mBackgroundFallbackResource != 0) {
+ final Drawable fallbackDrawable = context.getDrawable(mBackgroundFallbackResource);
+ if (fallbackDrawable != null) {
+ return fallbackDrawable;
+ }
+ }
+
+ // We shouldn't really get here as the background fallback should be always available since
+ // it is defaulted by the system.
+ Log.w(TAG, "Failed to find background drawable for PhoneWindow=" + this);
+ return null;
+ }
}
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index f190d8c..e8970bc 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -144,6 +144,13 @@
}
/**
+ * Checks if given array is null or has zero elements.
+ */
+ public static boolean isEmpty(byte[] array) {
+ return array == null || array.length == 0;
+ }
+
+ /**
* Checks that value is present as at least one of the elements of the array.
* @param array the array to check in
* @param value the value to check for
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index 07bfce7..8699843 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -35,7 +35,8 @@
@Override
public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets,
- Rect stableInsets, Rect outsets, boolean reportDraw, Configuration newConfig) {
+ Rect stableInsets, Rect outsets, boolean reportDraw, Configuration newConfig,
+ Rect backDropFrame) {
if (reportDraw) {
try {
mSession.finishDrawing(this);
diff --git a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
index 293e2ad..e9b8447 100644
--- a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
+++ b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
@@ -5,28 +5,33 @@
import java.util.ArrayList;
import java.util.List;
+import android.annotation.AttrRes;
import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StyleRes;
import android.content.Context;
-import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Parcelable;
+import android.os.SystemClock;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
+import android.view.MenuItem;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.View.OnAttachStateChangeListener;
import android.view.View.OnKeyListener;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
-import android.widget.DropDownListView;
+import android.widget.AbsListView;
import android.widget.FrameLayout;
-import android.widget.MenuItemHoverListener;
+import android.widget.HeaderViewListAdapter;
import android.widget.ListAdapter;
+import android.widget.MenuItemHoverListener;
import android.widget.ListView;
import android.widget.MenuPopupWindow;
-import android.widget.MenuPopupWindow.MenuDropDownListView;
import android.widget.PopupWindow;
import android.widget.PopupWindow.OnDismissListener;
import android.widget.TextView;
@@ -47,6 +52,10 @@
private static final int HORIZ_POSITION_LEFT = 0;
private static final int HORIZ_POSITION_RIGHT = 1;
+ /**
+ * Delay between hovering over a menu item with a mouse and receiving
+ * side-effects (ex. opening a sub-menu or closing unrelated menus).
+ */
private static final int SUBMENU_TIMEOUT_MS = 200;
private final Context mContext;
@@ -54,9 +63,14 @@
private final int mPopupStyleAttr;
private final int mPopupStyleRes;
private final boolean mOverflowOnly;
- private final int mLayoutDirection;
private final Handler mSubMenuHoverHandler;
+ /**
+ * List of open menus. The first item is the root menu and each
+ * subsequent item is a direct submenu of the previous item.
+ */
+ private final List<CascadingMenuInfo> mAddedMenus = new ArrayList<>();
+
private final OnGlobalLayoutListener mGlobalLayoutListener = new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
@@ -66,8 +80,8 @@
dismiss();
} else if (isShowing()) {
// Recompute window sizes and positions.
- for (MenuPopupWindow popup : mPopupWindows) {
- popup.show();
+ for (CascadingMenuInfo info : mAddedMenus) {
+ info.window.show();
}
}
}
@@ -94,13 +108,23 @@
private final MenuItemHoverListener mMenuItemHoverListener = new MenuItemHoverListener() {
@Override
- public void onItemHovered(MenuBuilder menu, int position) {
- int menuIndex = -1;
- for (int i = 0; i < mListViews.size(); i++) {
- final MenuDropDownListView view = (MenuDropDownListView) mListViews.get(i);
- final MenuAdapter adapter = toMenuAdapter(view.getAdapter());
+ public void onItemHoverExit(@NonNull MenuBuilder menu, @NonNull MenuItem item) {
+ // If the mouse moves between two windows, hover enter/exit pairs
+ // may be received out of order. So, instead of canceling all
+ // pending runnables, only cancel runnables for the host menu.
+ mSubMenuHoverHandler.removeCallbacksAndMessages(menu);
+ }
- if (adapter.getAdapterMenu() == menu) {
+ @Override
+ public void onItemHoverEnter(
+ @NonNull final MenuBuilder menu, @NonNull final MenuItem item) {
+ // Something new was hovered, cancel all scheduled runnables.
+ mSubMenuHoverHandler.removeCallbacksAndMessages(null);
+
+ // Find the position of the hovered menu within the added menus.
+ int menuIndex = -1;
+ for (int i = 0, count = mAddedMenus.size(); i < count; i++) {
+ if (menu == mAddedMenus.get(i).menu) {
menuIndex = i;
break;
}
@@ -110,82 +134,43 @@
return;
}
- final MenuDropDownListView view = (MenuDropDownListView) mListViews.get(menuIndex);
- final ListMenuItemView selectedItemView = (ListMenuItemView) view.getSelectedView();
-
- if (selectedItemView != null && selectedItemView.isEnabled()
- && selectedItemView.getItemData().hasSubMenu()) {
- // If the currently selected item corresponds to a submenu, schedule to open the
- // submenu on a timeout.
-
- mSubMenuHoverHandler.removeCallbacksAndMessages(null);
- mSubMenuHoverHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- // Make sure the submenu item is still the one selected.
- if (view.getSelectedView() == selectedItemView
- && selectedItemView.isEnabled()
- && selectedItemView.getItemData().hasSubMenu()) {
- // Close any other submenus that might be open at the current or
- // a deeper level.
- int nextIndex = mListViews.indexOf(view) + 1;
- if (nextIndex < mListViews.size()) {
- MenuAdapter nextSubMenuAdapter =
- toMenuAdapter(mListViews.get(nextIndex).getAdapter());
- // Disable exit animation, to prevent overlapping fading out
- // submenus.
- mPopupWindows.get(nextIndex).setExitTransition(null);
- nextSubMenuAdapter.getAdapterMenu().close();
- }
-
- // Then open the selected submenu.
- view.performItemClick(
- selectedItemView,
- view.getSelectedItemPosition(),
- view.getSelectedItemId());
- }
- }
- }, SUBMENU_TIMEOUT_MS);
- } else if (menuIndex + 1 < mListViews.size()) {
- // If the currently selected item does NOT corresponds to a submenu, check if there
- // is a submenu already open that is one level deeper. If so, schedule to close it
- // on a timeout.
-
- final MenuDropDownListView nextView =
- (MenuDropDownListView) mListViews.get(menuIndex + 1);
- final MenuAdapter nextAdapter = toMenuAdapter(nextView.getAdapter());
-
- mSubMenuHoverHandler.removeCallbacksAndMessages(null);
- mSubMenuHoverHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- // Make sure the menu wasn't already closed by something else and that
- // it wasn't re-hovered by the user since this was scheduled.
- int nextMenuIndex = mListViews.indexOf(nextView);
-
- if (nextMenuIndex != -1 && nextView.getSelectedView() == null) {
- // Disable exit animation, to prevent overlapping fading out submenus.
- for (int i = nextMenuIndex; i < mPopupWindows.size(); i++) {
- final MenuPopupWindow popupWindow = mPopupWindows.get(i);
- popupWindow.setExitTransition(null);
- popupWindow.setAnimationStyle(0);
- }
- nextAdapter.getAdapterMenu().close();
- }
- }
- }, SUBMENU_TIMEOUT_MS);
+ final CascadingMenuInfo nextInfo;
+ final int nextIndex = menuIndex + 1;
+ if (nextIndex < mAddedMenus.size()) {
+ nextInfo = mAddedMenus.get(nextIndex);
+ } else {
+ nextInfo = null;
}
+
+ final Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ // Close any other submenus that might be open at the
+ // current or a deeper level.
+ if (nextInfo != null) {
+ // Disable exit animations to prevent overlapping
+ // fading out submenus.
+ mShouldCloseImmediately = true;
+ nextInfo.menu.close(false /* closeAllMenus */);
+ mShouldCloseImmediately = false;
+ }
+
+ // Then open the selected submenu, if there is one.
+ if (item.isEnabled() && item.hasSubMenu()) {
+ menu.performItemAction(item, 0);
+ }
+ }
+ };
+ final long uptimeMillis = SystemClock.uptimeMillis() + SUBMENU_TIMEOUT_MS;
+ mSubMenuHoverHandler.postAtTime(runnable, menu, uptimeMillis);
}
};
+ private int mRawDropDownGravity = Gravity.NO_GRAVITY;
private int mDropDownGravity = Gravity.NO_GRAVITY;
private View mAnchorView;
private View mShownAnchorView;
- private List<DropDownListView> mListViews;
- private List<MenuPopupWindow> mPopupWindows;
private int mLastPosition;
- private List<Integer> mPositions;
- private List<int[]> mOffsets;
private int mInitXOffset;
private int mInitYOffset;
private boolean mForceShowIcon;
@@ -194,13 +179,16 @@
private ViewTreeObserver mTreeObserver;
private PopupWindow.OnDismissListener mOnDismissListener;
+ /** Whether popup menus should disable exit animations when closing. */
+ private boolean mShouldCloseImmediately;
+
/**
* Initializes a new cascading-capable menu popup.
*
- * @param parent A parent view to get the {@link android.view.View#getWindowToken()} token from.
+ * @param anchor A parent view to get the {@link android.view.View#getWindowToken()} token from.
*/
- public CascadingMenuPopup(Context context, View anchor, int popupStyleAttr,
- int popupStyleRes, boolean overflowOnly) {
+ public CascadingMenuPopup(@NonNull Context context, @NonNull View anchor,
+ @AttrRes int popupStyleAttr, @StyleRes int popupStyleRes, boolean overflowOnly) {
mContext = Preconditions.checkNotNull(context);
mAnchorView = Preconditions.checkNotNull(anchor);
mPopupStyleAttr = popupStyleAttr;
@@ -208,18 +196,12 @@
mOverflowOnly = overflowOnly;
mForceShowIcon = false;
+ mLastPosition = getInitialMenuPosition();
final Resources res = context.getResources();
- final Configuration config = res.getConfiguration();
- mLayoutDirection = config.getLayoutDirection();
- mLastPosition = getInitialMenuPosition();
mMenuMaxWidth = Math.max(res.getDisplayMetrics().widthPixels / 2,
res.getDimensionPixelSize(com.android.internal.R.dimen.config_prefDialogWidth));
- mPopupWindows = new ArrayList<MenuPopupWindow>();
- mListViews = new ArrayList<DropDownListView>();
- mOffsets = new ArrayList<int[]>();
- mPositions = new ArrayList<Integer>();
mSubMenuHoverHandler = new Handler();
}
@@ -246,28 +228,28 @@
return;
}
- // Show any menus that have been added via #addMenu(MenuBuilder) but which have not yet been
- // shown.
- // In a typical use case, #addMenu(MenuBuilder) would be called once, followed by a call to
- // this #show() method -- which would actually show the popup on the screen.
- for (int i = 0; i < mPopupWindows.size(); i++) {
- MenuPopupWindow popupWindow = mPopupWindows.get(i);
+ // Show any menus that have been added via #addMenu(MenuBuilder) but
+ // which have not yet been shown. In a typical use case,
+ // #addMenu(MenuBuilder) would be called once, followed by a call to
+ // this #show() method -- which would actually show the popup on the
+ // screen.
+ for (int i = 0, count = mAddedMenus.size(); i < count; i++) {
+ final CascadingMenuInfo info = mAddedMenus.get(i);
+ final MenuPopupWindow popupWindow = info.window;
popupWindow.show();
- DropDownListView listView = (DropDownListView) popupWindow.getListView();
- mListViews.add(listView);
- MenuBuilder menu = toMenuAdapter(listView.getAdapter()).getAdapterMenu();
+ final MenuBuilder menu = info.menu;
if (i == 0 && mShowTitle && menu.getHeaderTitle() != null) {
FrameLayout titleItemView =
(FrameLayout) LayoutInflater.from(mContext).inflate(
com.android.internal.R.layout.popup_menu_header_item_layout,
- listView,
+ info.getListView(),
false);
TextView titleView = (TextView) titleItemView.findViewById(
com.android.internal.R.id.title);
titleView.setText(menu.getHeaderTitle());
titleItemView.setEnabled(false);
- listView.addHeaderView(titleItemView, null, false);
+ info.getListView().addHeaderView(titleItemView, null, false);
// Update to show the title.
popupWindow.show();
@@ -287,12 +269,19 @@
@Override
public void dismiss() {
- // Need to make another list to avoid a concurrent modification exception, as #onDismiss
- // may clear mPopupWindows while we are iterating.
- List<MenuPopupWindow> popupWindows = new ArrayList<MenuPopupWindow>(mPopupWindows);
- for (MenuPopupWindow popupWindow : popupWindows) {
- if (popupWindow != null && popupWindow.isShowing()) {
- popupWindow.dismiss();
+ // Need to make another list to avoid a concurrent modification
+ // exception, as #onDismiss may clear mPopupWindows while we are
+ // iterating. Remove from the last added menu so that the callbacks
+ // are received in order from foreground to background.
+ final int length = mAddedMenus.size();
+ if (length > 0) {
+ final CascadingMenuInfo[] addedMenus =
+ mAddedMenus.toArray(new CascadingMenuInfo[length]);
+ for (int i = length - 1; i >= 0; i--) {
+ final CascadingMenuInfo info = addedMenus[i];
+ if (info.window.isShowing()) {
+ info.window.dismiss();
+ }
}
}
}
@@ -312,7 +301,8 @@
*/
@HorizPosition
private int getInitialMenuPosition() {
- return mLayoutDirection == View.LAYOUT_DIRECTION_RTL ? HORIZ_POSITION_LEFT :
+ final int layoutDirection = mAnchorView.getLayoutDirection();
+ return layoutDirection == View.LAYOUT_DIRECTION_RTL ? HORIZ_POSITION_LEFT :
HORIZ_POSITION_RIGHT;
}
@@ -325,7 +315,7 @@
*/
@HorizPosition
private int getNextMenuPosition(int nextMenuWidth) {
- ListView lastListView = mListViews.get(mListViews.size() - 1);
+ ListView lastListView = mAddedMenus.get(mAddedMenus.size() - 1).getListView();
final int[] screenLocation = new int[2];
lastListView.getLocationOnScreen(screenLocation);
@@ -350,76 +340,159 @@
@Override
public void addMenu(MenuBuilder menu) {
- boolean addSubMenu = mListViews.size() > 0;
-
menu.addMenuPresenter(this, mContext);
- MenuPopupWindow popupWindow = createPopupWindow();
-
- MenuAdapter adapter = new MenuAdapter(menu, LayoutInflater.from(mContext), mOverflowOnly);
+ final LayoutInflater inflater = LayoutInflater.from(mContext);
+ final MenuAdapter adapter = new MenuAdapter(menu, inflater, mOverflowOnly);
adapter.setForceShowIcon(mForceShowIcon);
+ final int menuWidth = measureIndividualMenuWidth(adapter, null, mContext, mMenuMaxWidth);
+ final MenuPopupWindow popupWindow = createPopupWindow();
popupWindow.setAdapter(adapter);
+ popupWindow.setWidth(menuWidth);
+ popupWindow.setDropDownGravity(mDropDownGravity);
- int menuWidth = measureIndividualMenuWidth(adapter, null, mContext, mMenuMaxWidth);
+ final CascadingMenuInfo parentInfo;
+ final View parentView;
+ if (mAddedMenus.size() > 0) {
+ parentInfo = mAddedMenus.get(mAddedMenus.size() - 1);
+ parentView = findParentViewForSubmenu(parentInfo, menu);
+ } else {
+ parentInfo = null;
+ parentView = null;
+ }
- int x = 0;
- int y = 0;
-
- if (addSubMenu) {
+ final int x;
+ final int y;
+ if (parentView != null) {
+ // This menu is a cascading submenu anchored to a parent view.
popupWindow.setTouchModal(false);
popupWindow.setEnterTransition(null);
- ListView lastListView = mListViews.get(mListViews.size() - 1);
- @HorizPosition int nextMenuPosition = getNextMenuPosition(menuWidth);
- boolean showOnRight = nextMenuPosition == HORIZ_POSITION_RIGHT;
+ final @HorizPosition int nextMenuPosition = getNextMenuPosition(menuWidth);
+ final boolean showOnRight = nextMenuPosition == HORIZ_POSITION_RIGHT;
mLastPosition = nextMenuPosition;
- int[] lastLocation = new int[2];
- lastListView.getLocationOnScreen(lastLocation);
+ final int[] tempLocation = new int[2];
- int[] lastOffset = mOffsets.get(mOffsets.size() - 1);
+ // This popup menu will be positioned relative to the top-left edge
+ // of the view representing its parent menu.
+ parentView.getLocationInWindow(tempLocation);
+ final int parentOffsetLeft = parentInfo.window.getHorizontalOffset() + tempLocation[0];
+ final int parentOffsetTop = parentInfo.window.getVerticalOffset() + tempLocation[1];
- // Note: By now, mDropDownGravity is the absolute gravity, so this should work in both
- // LTR and RTL.
+ // By now, mDropDownGravity is the resolved absolute gravity, so
+ // this should work in both LTR and RTL.
if ((mDropDownGravity & Gravity.RIGHT) == Gravity.RIGHT) {
if (showOnRight) {
- x = lastOffset[0] + menuWidth;
+ x = parentOffsetLeft + menuWidth;
} else {
- x = lastOffset[0] - lastListView.getWidth();
+ x = parentOffsetLeft - parentView.getWidth();
}
} else {
if (showOnRight) {
- x = lastOffset[0] + lastListView.getWidth();
+ x = parentOffsetLeft + parentView.getWidth();
} else {
- x = lastOffset[0] - menuWidth;
+ x = parentOffsetLeft - menuWidth;
}
}
- y = lastOffset[1] + lastListView.getSelectedView().getTop() -
- lastListView.getChildAt(0).getTop();
+ y = parentOffsetTop;
} else {
x = mInitXOffset;
y = mInitYOffset;
}
- popupWindow.setWidth(menuWidth);
popupWindow.setHorizontalOffset(x);
popupWindow.setVerticalOffset(y);
- mPopupWindows.add(popupWindow);
+
+ final CascadingMenuInfo menuInfo = new CascadingMenuInfo(popupWindow, menu, mLastPosition);
+ mAddedMenus.add(menuInfo);
// NOTE: This case handles showing submenus once the CascadingMenuPopup has already
// been shown via a call to its #show() method. If it hasn't yet been show()n, then
// we deliberately do not yet show the popupWindow, as #show() will do that later.
if (isShowing()) {
popupWindow.show();
- DropDownListView listView = (DropDownListView) popupWindow.getListView();
- mListViews.add(listView);
+ }
+ }
+
+ /**
+ * Returns the menu item within the specified parent menu that owns
+ * specified submenu.
+ *
+ * @param parent the parent menu
+ * @param submenu the submenu for which the index should be returned
+ * @return the menu item that owns the submenu, or {@code null} if not
+ * present
+ */
+ private MenuItem findMenuItemForSubmenu(
+ @NonNull MenuBuilder parent, @NonNull MenuBuilder submenu) {
+ for (int i = 0, count = parent.size(); i < count; i++) {
+ final MenuItem item = parent.getItem(i);
+ if (item.hasSubMenu() && submenu == item.getSubMenu()) {
+ return item;
+ }
}
- int[] offsets = {x, y};
- mOffsets.add(offsets);
- mPositions.add(mLastPosition);
+ return null;
+ }
+
+ /**
+ * Attempts to find the view for the menu item that owns the specified
+ * submenu.
+ *
+ * @param parentInfo info for the parent menu
+ * @param submenu the submenu whose parent view should be obtained
+ * @return the parent view, or {@code null} if one could not be found
+ */
+ @Nullable
+ private View findParentViewForSubmenu(
+ @NonNull CascadingMenuInfo parentInfo, @NonNull MenuBuilder submenu) {
+ final MenuItem owner = findMenuItemForSubmenu(parentInfo.menu, submenu);
+ if (owner == null) {
+ // Couldn't find the submenu owner.
+ return null;
+ }
+
+ // The adapter may be wrapped. Adjust the index if necessary.
+ final int headersCount;
+ final MenuAdapter menuAdapter;
+ final ListView listView = parentInfo.getListView();
+ final ListAdapter listAdapter = listView.getAdapter();
+ if (listAdapter instanceof HeaderViewListAdapter) {
+ final HeaderViewListAdapter headerAdapter = (HeaderViewListAdapter) listAdapter;
+ headersCount = headerAdapter.getHeadersCount();
+ menuAdapter = (MenuAdapter) headerAdapter.getWrappedAdapter();
+ } else {
+ headersCount = 0;
+ menuAdapter = (MenuAdapter) listAdapter;
+ }
+
+ // Find the index within the menu adapter's data set of the menu item.
+ int ownerPosition = AbsListView.INVALID_POSITION;
+ for (int i = 0, count = menuAdapter.getCount(); i < count; i++) {
+ if (owner == menuAdapter.getItem(i)) {
+ ownerPosition = i;
+ break;
+ }
+ }
+ if (ownerPosition == AbsListView.INVALID_POSITION) {
+ // Couldn't find the owner within the menu adapter.
+ return null;
+ }
+
+ // Adjust the index for the adapter used to display views.
+ ownerPosition += headersCount;
+
+ // Adjust the index for the visible views.
+ final int ownerViewPosition = ownerPosition - listView.getFirstVisiblePosition();
+ if (ownerViewPosition < 0 || ownerViewPosition >= listView.getChildCount()) {
+ // Not visible on screen.
+ return null;
+ }
+
+ return listView.getChildAt(ownerViewPosition);
}
/**
@@ -427,7 +500,7 @@
*/
@Override
public boolean isShowing() {
- return mPopupWindows.size() > 0 && mPopupWindows.get(0).isShowing();
+ return mAddedMenus.size() > 0 && mAddedMenus.get(0).window.isShowing();
}
/**
@@ -435,28 +508,28 @@
*/
@Override
public void onDismiss() {
- int dismissedIndex = -1;
- for (int i = 0; i < mPopupWindows.size(); i++) {
- if (!mPopupWindows.get(i).isShowing()) {
- dismissedIndex = i;
+ // The dismiss listener doesn't pass the calling window, so walk
+ // through the stack to figure out which one was just dismissed.
+ CascadingMenuInfo dismissedInfo = null;
+ for (int i = 0, count = mAddedMenus.size(); i < count; i++) {
+ final CascadingMenuInfo info = mAddedMenus.get(i);
+ if (!info.window.isShowing()) {
+ dismissedInfo = info;
break;
}
}
- if (dismissedIndex != -1) {
- for (int i = dismissedIndex; i < mListViews.size(); i++) {
- ListView view = mListViews.get(i);
- ListAdapter adapter = view.getAdapter();
- MenuAdapter menuAdapter = toMenuAdapter(adapter);
- menuAdapter.mAdapterMenu.close();
- }
+ // Close all menus starting from the dismissed menu, passing false
+ // since we are manually closing only a subset of windows.
+ if (dismissedInfo != null) {
+ dismissedInfo.menu.close(false);
}
}
@Override
public void updateMenuView(boolean cleared) {
- for (ListView view : mListViews) {
- toMenuAdapter(view.getAdapter()).notifyDataSetChanged();
+ for (CascadingMenuInfo info : mAddedMenus) {
+ toMenuAdapter(info.getListView().getAdapter()).notifyDataSetChanged();
}
}
@@ -468,16 +541,17 @@
@Override
public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
// Don't allow double-opening of the same submenu.
- for (ListView view : mListViews) {
- if (toMenuAdapter(view.getAdapter()).mAdapterMenu.equals(subMenu)) {
+ for (CascadingMenuInfo info : mAddedMenus) {
+ if (subMenu == info.menu) {
// Just re-focus that one.
- view.requestFocus();
+ info.getListView().requestFocus();
return true;
}
}
if (subMenu.hasVisibleItems()) {
- this.addMenu(subMenu);
+ addMenu(subMenu);
+
if (mPresenterCallback != null) {
mPresenterCallback.onOpenSubMenu(subMenu);
}
@@ -486,52 +560,62 @@
return false;
}
+ /**
+ * Finds the index of the specified menu within the list of added menus.
+ *
+ * @param menu the menu to find
+ * @return the index of the menu, or {@code -1} if not present
+ */
+ private int findIndexOfAddedMenu(@NonNull MenuBuilder menu) {
+ for (int i = 0, count = mAddedMenus.size(); i < count; i++) {
+ final CascadingMenuInfo info = mAddedMenus.get(i);
+ if (menu == info.menu) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
@Override
public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
- int menuIndex = -1;
- boolean wasSelected = false;
-
- for (int i = 0; i < mListViews.size(); i++) {
- ListView view = mListViews.get(i);
- MenuAdapter adapter = toMenuAdapter(view.getAdapter());
-
- if (menuIndex == -1 && menu == adapter.mAdapterMenu) {
- menuIndex = i;
- wasSelected = view.getSelectedView() != null;
- }
-
- // Once the menu has been found, remove it and all submenus beneath it from the
- // container view. Also remove the presenter.
- if (menuIndex != -1) {
- adapter.mAdapterMenu.removeMenuPresenter(this);
- }
+ final int menuIndex = findIndexOfAddedMenu(menu);
+ if (menuIndex < 0) {
+ return;
}
- // Then, actually remove the views for these [sub]menu(s) from our list of views.
- if (menuIndex != -1) {
- for (int i = menuIndex; i < mPopupWindows.size(); i++) {
- mPopupWindows.get(i).dismiss();
- }
- mPopupWindows.subList(menuIndex, mPopupWindows.size()).clear();
- mListViews.subList(menuIndex, mListViews.size()).clear();
- mOffsets.subList(menuIndex, mOffsets.size()).clear();
-
- mPositions.subList(menuIndex, mPositions.size()).clear();
- if (mPositions.size() > 0) {
- mLastPosition = mPositions.get(mPositions.size() - 1);
- } else {
- mLastPosition = getInitialMenuPosition();
- }
+ // Recursively close descendant menus.
+ final int nextMenuIndex = menuIndex + 1;
+ if (nextMenuIndex < mAddedMenus.size()) {
+ final CascadingMenuInfo childInfo = mAddedMenus.get(nextMenuIndex);
+ childInfo.menu.close(false /* closeAllMenus */);
}
- if (mListViews.size() == 0 || wasSelected) {
+ // Close the target menu.
+ final CascadingMenuInfo info = mAddedMenus.remove(menuIndex);
+ info.menu.removeMenuPresenter(this);
+ if (mShouldCloseImmediately) {
+ // Disable all exit animations.
+ info.window.setExitTransition(null);
+ info.window.setAnimationStyle(0);
+ }
+ info.window.dismiss();
+
+ final int count = mAddedMenus.size();
+ if (count > 0) {
+ mLastPosition = mAddedMenus.get(count - 1).position;
+ } else {
+ mLastPosition = getInitialMenuPosition();
+ }
+
+ if (count == 0) {
+ // This was the last window. Clean up.
dismiss();
- if (mPresenterCallback != null) {
- mPresenterCallback.onCloseMenu(menu, allMenusAreClosing);
- }
- }
- if (mPopupWindows.size() == 0) {
+ if (mPresenterCallback != null) {
+ mPresenterCallback.onCloseMenu(menu, true);
+ }
+
if (mTreeObserver != null) {
if (mTreeObserver.isAlive()) {
mTreeObserver.removeGlobalOnLayoutListener(mGlobalLayoutListener);
@@ -539,9 +623,16 @@
mTreeObserver = null;
}
mShownAnchorView.removeOnAttachStateChangeListener(mAttachStateChangeListener);
- // If every [sub]menu was dismissed, that means the whole thing was dismissed, so notify
- // the owner.
+
+ // If every [sub]menu was dismissed, that means the whole thing was
+ // dismissed, so notify the owner.
mOnDismissListener.onDismiss();
+ } else if (allMenusAreClosing) {
+ // Close all menus starting from the root. This will recursively
+ // close any remaining menus, so we don't need to propagate the
+ // "closeAllMenus" flag. The last window will clean up.
+ final CascadingMenuInfo rootInfo = mAddedMenus.get(0);
+ rootInfo.menu.close(false /* closeAllMenus */);
}
}
@@ -561,12 +652,22 @@
@Override
public void setGravity(int dropDownGravity) {
- mDropDownGravity = Gravity.getAbsoluteGravity(dropDownGravity, mLayoutDirection);
+ if (mRawDropDownGravity != dropDownGravity) {
+ mRawDropDownGravity = dropDownGravity;
+ mDropDownGravity = Gravity.getAbsoluteGravity(
+ dropDownGravity, mAnchorView.getLayoutDirection());
+ }
}
@Override
- public void setAnchorView(View anchor) {
- mAnchorView = anchor;
+ public void setAnchorView(@NonNull View anchor) {
+ if (mAnchorView != anchor) {
+ mAnchorView = anchor;
+
+ // Gravity resolution may have changed, update from raw gravity.
+ mDropDownGravity = Gravity.getAbsoluteGravity(
+ mRawDropDownGravity, mAnchorView.getLayoutDirection());
+ }
}
@Override
@@ -576,7 +677,7 @@
@Override
public ListView getListView() {
- return mListViews.size() > 0 ? mListViews.get(mListViews.size() - 1) : null;
+ return mAddedMenus.isEmpty() ? null : mAddedMenus.get(mAddedMenus.size() - 1).getListView();
}
@Override
@@ -593,4 +694,21 @@
public void setShowTitle(boolean showTitle) {
mShowTitle = showTitle;
}
+
+ private static class CascadingMenuInfo {
+ public final MenuPopupWindow window;
+ public final MenuBuilder menu;
+ public final int position;
+
+ public CascadingMenuInfo(@NonNull MenuPopupWindow window, @NonNull MenuBuilder menu,
+ int position) {
+ this.window = window;
+ this.menu = menu;
+ this.position = position;
+ }
+
+ public ListView getListView() {
+ return window.getListView();
+ }
+ }
}
\ No newline at end of file
diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java
index 1673928..465d775 100644
--- a/core/java/com/android/internal/view/menu/MenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/MenuBuilder.java
@@ -794,7 +794,7 @@
}
if ((flags & FLAG_ALWAYS_PERFORM_CLOSE) != 0) {
- close(true);
+ close(true /* closeAllMenus */);
}
return handled;
@@ -910,10 +910,12 @@
final boolean providerHasSubMenu = provider != null && provider.hasSubMenu();
if (itemImpl.hasCollapsibleActionView()) {
invoked |= itemImpl.expandActionView();
- if (invoked) close(true);
+ if (invoked) {
+ close(true /* closeAllMenus */);
+ }
} else if (itemImpl.hasSubMenu() || providerHasSubMenu) {
if (!mShowCascadingMenus) {
- close(false);
+ close(false /* closeAllMenus */);
}
if (!itemImpl.hasSubMenu()) {
@@ -925,10 +927,12 @@
provider.onPrepareSubMenu(subMenu);
}
invoked |= dispatchSubMenuSelected(subMenu, preferredPresenter);
- if (!invoked) close(true);
+ if (!invoked) {
+ close(true /* closeAllMenus */);
+ }
} else {
if ((flags & FLAG_PERFORM_NO_CLOSE) == 0) {
- close(true);
+ close(true /* closeAllMenus */);
}
}
@@ -936,15 +940,14 @@
}
/**
- * Closes the visible menu.
- *
- * @param allMenusAreClosing Whether the menus are completely closing (true),
- * or whether there is another menu coming in this menu's place
- * (false). For example, if the menu is closing because a
- * sub menu is about to be shown, <var>allMenusAreClosing</var>
- * is false.
+ * Closes the menu.
+ *
+ * @param closeAllMenus {@code true} if all displayed menus and submenus
+ * should be completely closed (as when a menu item is
+ * selected) or {@code false} if only this menu should
+ * be closed
*/
- public final void close(boolean allMenusAreClosing) {
+ public final void close(boolean closeAllMenus) {
if (mIsClosing) return;
mIsClosing = true;
@@ -953,7 +956,7 @@
if (presenter == null) {
mPresenters.remove(ref);
} else {
- presenter.onCloseMenu(this, allMenusAreClosing);
+ presenter.onCloseMenu(this, closeAllMenus);
}
}
mIsClosing = false;
@@ -961,7 +964,7 @@
/** {@inheritDoc} */
public void close() {
- close(true);
+ close(true /* closeAllMenus */);
}
/**
diff --git a/core/java/com/android/internal/view/menu/MenuDialogHelper.java b/core/java/com/android/internal/view/menu/MenuDialogHelper.java
index 5c8e057..b9e0e40 100644
--- a/core/java/com/android/internal/view/menu/MenuDialogHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuDialogHelper.java
@@ -111,7 +111,7 @@
if (decor != null) {
KeyEvent.DispatcherState ds = decor.getKeyDispatcherState();
if (ds != null && ds.isTracking(event)) {
- mMenu.close(true);
+ mMenu.close(true /* closeAllMenus */);
dialog.dismiss();
return true;
}
diff --git a/core/java/com/android/internal/view/menu/MenuPresenter.java b/core/java/com/android/internal/view/menu/MenuPresenter.java
index f207b98..c847c15 100644
--- a/core/java/com/android/internal/view/menu/MenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/MenuPresenter.java
@@ -97,8 +97,10 @@
* closing. Presenter implementations should close the representation
* of the menu indicated as necessary and notify a registered callback.
*
- * @param menu Menu or submenu that is closing.
- * @param allMenusAreClosing True if all associated menus are closing.
+ * @param menu the menu or submenu that is closing
+ * @param allMenusAreClosing {@code true} if all displayed menus and
+ * submenus are closing, {@code false} if only
+ * the specified menu is closing
*/
public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing);
diff --git a/core/java/com/android/internal/widget/NonClientDecorView.java b/core/java/com/android/internal/widget/NonClientDecorView.java
index ed7492a..de6e228 100644
--- a/core/java/com/android/internal/widget/NonClientDecorView.java
+++ b/core/java/com/android/internal/widget/NonClientDecorView.java
@@ -16,9 +16,11 @@
package com.android.internal.widget;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+
import android.content.Context;
-import android.graphics.Color;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.os.Looper;
import android.os.RemoteException;
import android.util.AttributeSet;
@@ -52,7 +54,7 @@
* <li>The resize handles which allow to resize the window.</li>
* </ul>
* After creating the view, the function
- * {@link #setPhoneWindow(PhoneWindow owner, boolean windowHasShadow)} needs to be called to make
+ * {@link #setPhoneWindow} needs to be called to make
* the connection to it's owning PhoneWindow.
* Note: At this time the application can change various attributes of the DecorView which
* will break things (in settle/unexpected ways):
@@ -97,6 +99,9 @@
// The resize frame renderer.
private ResizeFrameThread mFrameRendererThread = null;
+ private Drawable mResizingBackgroundDrawable;
+ private Drawable mCaptionBackgroundDrawable;
+
public NonClientDecorView(Context context) {
super(context);
}
@@ -133,10 +138,13 @@
}
}
- public void setPhoneWindow(PhoneWindow owner, boolean showDecor, boolean windowHasShadow) {
+ public void setPhoneWindow(PhoneWindow owner, boolean showDecor, boolean windowHasShadow,
+ Drawable resizingBackgroundDrawable, Drawable captionBackgroundDrawableDrawable) {
mOwner = owner;
mWindowHasShadow = windowHasShadow;
mShowDecor = showDecor;
+ mResizingBackgroundDrawable = resizingBackgroundDrawable;
+ mCaptionBackgroundDrawable = captionBackgroundDrawableDrawable;
updateCaptionVisibility();
if (mWindowHasShadow) {
initializeElevation();
@@ -325,8 +333,7 @@
Window.WindowControllerCallback callback = mOwner.getWindowControllerCallback();
if (callback != null) {
try {
- callback.changeWindowStack(
- android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID);
+ callback.changeWindowStack(FULLSCREEN_WORKSPACE_STACK_ID);
} catch (RemoteException ex) {
Log.e(TAG, "Cannot change task workspace.");
}
@@ -365,7 +372,7 @@
@Override
public void onRequestDraw(boolean reportNextDraw) {
if (mFrameRendererThread != null) {
- mFrameRendererThread.onRequsetDraw(reportNextDraw);
+ mFrameRendererThread.onRequestDraw(reportNextDraw);
} else if (reportNextDraw) {
// If render thread is gone, just report immediately.
if (isAttachedToWindow()) {
@@ -421,8 +428,7 @@
// The render nodes for the multi threaded renderer.
private ThreadedRenderer mRenderer;
- private RenderNode mFrameNode;
- private RenderNode mBackdropNode;
+ private RenderNode mFrameAndBackdropNode;
private final Rect mOldTargetRect = new Rect();
private final Rect mNewTargetRect = new Rect();
@@ -443,13 +449,11 @@
setName("ResizeFrame");
mRenderer = renderer;
- // Create the render nodes for our frame and backdrop which can be resized independently
- // from the content.
- mFrameNode = RenderNode.create("FrameNode", null);
- mBackdropNode = RenderNode.create("BackdropNode", null);
+ // Create a render node for the content and frame backdrop
+ // which can be resized independently from the content.
+ mFrameAndBackdropNode = RenderNode.create("FrameAndBackdropNode", null);
- mRenderer.addRenderNode(mFrameNode, false);
- mRenderer.addRenderNode(mBackdropNode, true);
+ mRenderer.addRenderNode(mFrameAndBackdropNode, true);
// Set the initial bounds and draw once so that we do not get a broken frame.
mTargetRect.set(initialBounds);
@@ -497,10 +501,9 @@
// Invalidate the current content bounds.
mRenderer.setContentDrawBounds(0, 0, 0, 0);
- // Remove the render nodes again
+ // Remove the render node again
// (see comment above - better to do that only once).
- mRenderer.removeRenderNode(mFrameNode);
- mRenderer.removeRenderNode(mBackdropNode);
+ mRenderer.removeRenderNode(mFrameAndBackdropNode);
mRenderer = null;
@@ -514,7 +517,12 @@
public void run() {
try {
Looper.prepare();
- mChoreographer = Choreographer.getInstance();
+ synchronized (this) {
+ mChoreographer = Choreographer.getInstance();
+
+ // Draw at least once.
+ mChoreographer.postFrameCallback(this);
+ }
Looper.loop();
} finally {
releaseRenderer();
@@ -568,7 +576,7 @@
mRenderer.setContentDrawBounds(
mLastXOffset,
- mLastYOffset + mLastCaptionHeight,
+ mLastYOffset,
mLastXOffset + mLastContentWidth,
mLastYOffset + mLastCaptionHeight + mLastContentHeight);
// If this was the first call and changeWindowSizeLocked got already called prior
@@ -577,7 +585,7 @@
}
}
- public void onRequsetDraw(boolean reportNextDraw) {
+ public void onRequestDraw(boolean reportNextDraw) {
synchronized (this) {
mReportNextDraw = reportNextDraw;
mOldTargetRect.set(0, 0, 0, 0);
@@ -616,30 +624,20 @@
final int width = newBounds.width();
final int height = newBounds.height();
- // Produce the draw calls.
- // TODO(skuhne): Create a separate caption view which draws this. If the shadow should
- // be resized while the window resizes, this hierarchy needs to have the elevation.
- // That said - it is probably no good idea to draw the shadow every time since it costs
- // a considerable time which we should rather spend for resizing the content and it does
- // barely show while the entire screen is moving.
- mFrameNode.setLeftTopRightBottom(left, top, left + width, top + mLastCaptionHeight);
- DisplayListCanvas canvas = mFrameNode.start(width, height);
- canvas.drawColor(Color.BLACK);
- mFrameNode.end(canvas);
+ mFrameAndBackdropNode.setLeftTopRightBottom(left, top, left + width, top + height);
- mBackdropNode.setLeftTopRightBottom(left, top + mLastCaptionHeight, left + width,
- top + height);
+ // Draw the caption and content backdrops in to our render node.
+ DisplayListCanvas canvas = mFrameAndBackdropNode.start(width, height);
+ mCaptionBackgroundDrawable.setBounds(0, 0, left + width, top + mLastCaptionHeight);
+ mCaptionBackgroundDrawable.draw(canvas);
// The backdrop: clear everything with the background. Clipping is done elsewhere.
- canvas = mBackdropNode.start(width, height - mLastCaptionHeight);
- // TODO(skuhne): mOwner.getDecorView().mBackgroundFallback.draw(..) - or similar.
- // Note: This might not work (calculator for example uses a transparent background).
- canvas.drawColor(0xff808080);
- mBackdropNode.end(canvas);
+ mResizingBackgroundDrawable.setBounds(0, mLastCaptionHeight, left + width, top + height);
+ mResizingBackgroundDrawable.draw(canvas);
+ mFrameAndBackdropNode.end(canvas);
- // We need to render both rendered nodes explicitly.
- mRenderer.drawRenderNode(mFrameNode);
- mRenderer.drawRenderNode(mBackdropNode);
+ // We need to render the node explicitly
+ mRenderer.drawRenderNode(mFrameAndBackdropNode);
reportDrawIfNeeded();
}
diff --git a/core/java/com/android/internal/widget/NumericTextView.java b/core/java/com/android/internal/widget/NumericTextView.java
new file mode 100644
index 0000000..27c5834
--- /dev/null
+++ b/core/java/com/android/internal/widget/NumericTextView.java
@@ -0,0 +1,328 @@
+/*
+ * 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.internal.widget;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.util.StateSet;
+import android.view.KeyEvent;
+import android.widget.TextView;
+
+/**
+ * Extension of TextView that can handle displaying and inputting a range of
+ * numbers.
+ * <p>
+ * Clients of this view should never call {@link #setText(CharSequence)} or
+ * {@link #setHint(CharSequence)} directly. Instead, they should call
+ * {@link #setValue(int)} to modify the currently displayed value.
+ */
+public class NumericTextView extends TextView {
+ private static final int RADIX = 10;
+ private static final double LOG_RADIX = Math.log(RADIX);
+
+ private int mMinValue = 0;
+ private int mMaxValue = 99;
+
+ /** Number of digits in the maximum value. */
+ private int mMaxCount = 2;
+
+ private boolean mShowLeadingZeroes = true;
+
+ private int mValue;
+
+ /** Number of digits entered during editing mode. */
+ private int mCount;
+
+ /** Used to restore the value after an aborted edit. */
+ private int mPreviousValue;
+
+ private OnValueChangedListener mListener;
+
+ public NumericTextView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ // Generate the hint text color based on disabled state.
+ final int textColorDisabled = getTextColors().getColorForState(StateSet.get(0), 0);
+ setHintTextColor(textColorDisabled);
+
+ setFocusable(true);
+ }
+
+ @Override
+ protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
+ super.onFocusChanged(focused, direction, previouslyFocusedRect);
+
+ if (focused) {
+ mPreviousValue = mValue;
+ mValue = 0;
+ mCount = 0;
+
+ // Transfer current text to hint.
+ setHint(getText());
+ setText("");
+ } else {
+ if (mCount == 0) {
+ // No digits were entered, revert to previous value.
+ mValue = mPreviousValue;
+
+ setText(getHint());
+ setHint("");
+ }
+
+ // Ensure the committed value is within range.
+ if (mValue < mMinValue) {
+ mValue = mMinValue;
+ }
+
+ setValue(mValue);
+
+ if (mListener != null) {
+ mListener.onValueChanged(this, mValue, true, true);
+ }
+ }
+ }
+
+ /**
+ * Sets the currently displayed value.
+ * <p>
+ * The specified {@code value} must be within the range specified by
+ * {@link #setRange(int, int)} (e.g. between {@link #getRangeMinimum()}
+ * and {@link #getRangeMaximum()}).
+ *
+ * @param value the value to display
+ */
+ public final void setValue(int value) {
+ if (mValue != value) {
+ mValue = value;
+
+ updateDisplayedValue();
+ }
+ }
+
+ /**
+ * Returns the currently displayed value.
+ * <p>
+ * If the value is currently being edited, returns the live value which may
+ * not be within the range specified by {@link #setRange(int, int)}.
+ *
+ * @return the currently displayed value
+ */
+ public final int getValue() {
+ return mValue;
+ }
+
+ /**
+ * Sets the valid range (inclusive).
+ *
+ * @param minValue the minimum valid value (inclusive)
+ * @param maxValue the maximum valid value (inclusive)
+ */
+ public final void setRange(int minValue, int maxValue) {
+ if (mMinValue != minValue) {
+ mMinValue = minValue;
+ }
+
+ if (mMaxValue != maxValue) {
+ mMaxValue = maxValue;
+ mMaxCount = 1 + (int) (Math.log(maxValue) / LOG_RADIX);
+
+ updateMinimumWidth();
+ updateDisplayedValue();
+ }
+ }
+
+ /**
+ * @return the minimum value value (inclusive)
+ */
+ public final int getRangeMinimum() {
+ return mMinValue;
+ }
+
+ /**
+ * @return the maximum value value (inclusive)
+ */
+ public final int getRangeMaximum() {
+ return mMaxValue;
+ }
+
+ /**
+ * Sets whether this view shows leading zeroes.
+ * <p>
+ * When leading zeroes are shown, the displayed value will be padded
+ * with zeroes to the width of the maximum value as specified by
+ * {@link #setRange(int, int)} (see also {@link #getRangeMaximum()}.
+ * <p>
+ * For example, with leading zeroes shown, a maximum of 99 and value of
+ * 9 would display "09". A maximum of 100 and a value of 9 would display
+ * "009". With leading zeroes hidden, both cases would show "9".
+ *
+ * @param showLeadingZeroes {@code true} to show leading zeroes,
+ * {@code false} to hide them
+ */
+ public final void setShowLeadingZeroes(boolean showLeadingZeroes) {
+ if (mShowLeadingZeroes != showLeadingZeroes) {
+ mShowLeadingZeroes = showLeadingZeroes;
+
+ updateDisplayedValue();
+ }
+ }
+
+ public final boolean getShowLeadingZeroes() {
+ return mShowLeadingZeroes;
+ }
+
+ /**
+ * Computes the display value and updates the text of the view.
+ * <p>
+ * This method should be called whenever the current value or display
+ * properties (leading zeroes, max digits) change.
+ */
+ private void updateDisplayedValue() {
+ final String format;
+ if (mShowLeadingZeroes) {
+ format = "%0" + mMaxCount + "d";
+ } else {
+ format = "%d";
+ }
+
+ // Always use String.format() rather than Integer.toString()
+ // to obtain correctly localized values.
+ setText(String.format(format, mValue));
+ }
+
+ /**
+ * Computes the minimum width in pixels required to display all possible
+ * values and updates the minimum width of the view.
+ * <p>
+ * This method should be called whenever the maximum value changes.
+ */
+ private void updateMinimumWidth() {
+ final CharSequence previousText = getText();
+ int maxWidth = 0;
+
+ for (int i = 0; i < mMaxValue; i++) {
+ setText(String.format("%0" + mMaxCount + "d", i));
+ measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ final int width = getMeasuredWidth();
+ if (width > maxWidth) {
+ maxWidth = width;
+ }
+ }
+
+ setText(previousText);
+ setMinWidth(maxWidth);
+ setMinimumWidth(maxWidth);
+ }
+
+ public final void setOnDigitEnteredListener(OnValueChangedListener listener) {
+ mListener = listener;
+ }
+
+ public final OnValueChangedListener getOnDigitEnteredListener() {
+ return mListener;
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ return isKeyCodeNumeric(keyCode)
+ || (keyCode == KeyEvent.KEYCODE_DEL)
+ || super.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
+ return isKeyCodeNumeric(keyCode)
+ || (keyCode == KeyEvent.KEYCODE_DEL)
+ || super.onKeyMultiple(keyCode, repeatCount, event);
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ return handleKeyUp(keyCode)
+ || super.onKeyUp(keyCode, event);
+ }
+
+ private boolean handleKeyUp(int keyCode) {
+ if (keyCode == KeyEvent.KEYCODE_DEL) {
+ // Backspace removes the least-significant digit, if available.
+ if (mCount > 0) {
+ mValue /= RADIX;
+ mCount--;
+ }
+ } else if (isKeyCodeNumeric(keyCode)) {
+ if (mCount < mMaxCount) {
+ final int keyValue = numericKeyCodeToInt(keyCode);
+ final int newValue = mValue * RADIX + keyValue;
+ if (newValue <= mMaxValue) {
+ mValue = newValue;
+ mCount++;
+ }
+ }
+ } else {
+ return false;
+ }
+
+ final String formattedValue;
+ if (mCount > 0) {
+ // If the user types 01, we should always show the leading 0 even if
+ // getShowLeadingZeroes() is false. Preserve typed leading zeroes by
+ // using the number of digits entered as the format width.
+ formattedValue = String.format("%0" + mCount + "d", mValue);
+ } else {
+ formattedValue = "";
+ }
+
+ setText(formattedValue);
+
+ if (mListener != null) {
+ final boolean isValid = mValue >= mMinValue;
+ final boolean isFinished = mCount >= mMaxCount || mValue * RADIX > mMaxValue;
+ mListener.onValueChanged(this, mValue, isValid, isFinished);
+ }
+
+ return true;
+ }
+
+ private static boolean isKeyCodeNumeric(int keyCode) {
+ return keyCode == KeyEvent.KEYCODE_0 || keyCode == KeyEvent.KEYCODE_1
+ || keyCode == KeyEvent.KEYCODE_2 || keyCode == KeyEvent.KEYCODE_3
+ || keyCode == KeyEvent.KEYCODE_4 || keyCode == KeyEvent.KEYCODE_5
+ || keyCode == KeyEvent.KEYCODE_6 || keyCode == KeyEvent.KEYCODE_7
+ || keyCode == KeyEvent.KEYCODE_8 || keyCode == KeyEvent.KEYCODE_9;
+ }
+
+ private static int numericKeyCodeToInt(int keyCode) {
+ return keyCode - KeyEvent.KEYCODE_0;
+ }
+
+ public interface OnValueChangedListener {
+ /**
+ * Called when the value displayed by {@code view} changes.
+ *
+ * @param view the view whose value changed
+ * @param value the new value
+ * @param isValid {@code true} if the value is valid (e.g. within the
+ * range specified by {@link #setRange(int, int)}),
+ * {@code false} otherwise
+ * @param isFinished {@code true} if the no more digits may be entered,
+ * {@code false} if more digits may be entered
+ */
+ void onValueChanged(NumericTextView view, int value, boolean isValid, boolean isFinished);
+ }
+}
diff --git a/core/java/com/android/internal/widget/ViewPager.java b/core/java/com/android/internal/widget/ViewPager.java
index 6217d6e..948a6bb 100644
--- a/core/java/com/android/internal/widget/ViewPager.java
+++ b/core/java/com/android/internal/widget/ViewPager.java
@@ -1095,7 +1095,16 @@
View child = getChildAt(i);
ii = infoForChild(child);
if (ii != null && ii.position == mCurItem) {
- if (child.requestFocus(focusDirection)) {
+ final Rect focusRect;
+ if (currentFocused == null) {
+ focusRect = null;
+ } else {
+ focusRect = mTempRect;
+ currentFocused.getFocusedRect(mTempRect);
+ offsetDescendantRectToMyCoords(currentFocused, mTempRect);
+ offsetRectIntoDescendantCoords(child, mTempRect);
+ }
+ if (child.requestFocus(focusDirection, focusRect)) {
break;
}
}
@@ -1321,6 +1330,11 @@
}
}
+ public Object getCurrent() {
+ final ItemInfo itemInfo = infoForPosition(getCurrentItem());
+ return itemInfo == null ? null : itemInfo.object;
+ }
+
@Override
public void removeView(View view) {
if (mInLayout) {
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 20d00b0..4d648ce 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -89,6 +89,7 @@
android_util_Binder.cpp \
android_util_EventLog.cpp \
android_util_Log.cpp \
+ android_util_PathParser.cpp \
android_util_Process.cpp \
android_util_StringBlock.cpp \
android_util_XmlBlock.cpp \
@@ -189,6 +190,7 @@
external/pdfium/core/include/fpdfdoc \
external/pdfium/fpdfsdk/include \
external/pdfium/public \
+ external/skia/include/private \
external/skia/src/core \
external/skia/src/effects \
external/skia/src/images \
@@ -270,7 +272,7 @@
# -Wno-c++11-extensions: Clang warns about Skia using the C++11 override keyword, but this project
# is not being compiled with that level. Remove once this has changed.
-LOCAL_CFLAGS += -Wno-c++11-extensions
+LOCAL_CLANG_CFLAGS += -Wno-c++11-extensions
# b/22414716: thread_local (android/graphics/Paint.cpp) and Clang don't like each other at the
# moment.
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index ffc69a9..815d330 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -110,6 +110,7 @@
extern int register_android_content_AssetManager(JNIEnv* env);
extern int register_android_util_EventLog(JNIEnv* env);
extern int register_android_util_Log(JNIEnv* env);
+extern int register_android_util_PathParser(JNIEnv* env);
extern int register_android_content_StringBlock(JNIEnv* env);
extern int register_android_content_XmlBlock(JNIEnv* env);
extern int register_android_graphics_Canvas(JNIEnv* env);
@@ -888,21 +889,22 @@
profileMaxStackDepth,
"-Xprofile-max-stack-depth:");
- /*
- * Tracing options.
- */
- property_get("dalvik.vm.method-trace", propBuf, "false");
- if (strcmp(propBuf, "true") == 0) {
- addOption("-Xmethod-trace");
- parseRuntimeOption("dalvik.vm.method-trace-file",
- methodTraceFileBuf,
- "-Xmethod-trace-file:");
- parseRuntimeOption("dalvik.vm.method-trace-file-siz",
- methodTraceFileSizeBuf,
- "-Xmethod-trace-file-size:");
- property_get("dalvik.vm.method-trace-stream", propBuf, "false");
+ // Trace files are stored in /data/misc/trace which is writable only in debug mode.
+ property_get("ro.debuggable", propBuf, "0");
+ if (strcmp(propBuf, "1") == 0) {
+ property_get("dalvik.vm.method-trace", propBuf, "false");
if (strcmp(propBuf, "true") == 0) {
- addOption("-Xmethod-trace-stream");
+ addOption("-Xmethod-trace");
+ parseRuntimeOption("dalvik.vm.method-trace-file",
+ methodTraceFileBuf,
+ "-Xmethod-trace-file:");
+ parseRuntimeOption("dalvik.vm.method-trace-file-siz",
+ methodTraceFileSizeBuf,
+ "-Xmethod-trace-file-size:");
+ property_get("dalvik.vm.method-trace-stream", propBuf, "false");
+ if (strcmp(propBuf, "true") == 0) {
+ addOption("-Xmethod-trace-stream");
+ }
}
}
@@ -1299,6 +1301,7 @@
REG_JNI(register_android_os_SystemClock),
REG_JNI(register_android_util_EventLog),
REG_JNI(register_android_util_Log),
+ REG_JNI(register_android_util_PathParser),
REG_JNI(register_android_content_AssetManager),
REG_JNI(register_android_content_StringBlock),
REG_JNI(register_android_content_XmlBlock),
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index e6c7c2b..703a9bd 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -28,6 +28,7 @@
#include <cutils/ashmem.h>
#define DEBUG_PARCEL 0
+#define ASHMEM_BITMAP_MIN_SIZE (128 * (1 << 10))
namespace android {
@@ -993,7 +994,7 @@
// Map the bitmap in place from the ashmem region if possible otherwise copy.
Bitmap* nativeBitmap;
- if (blob.fd() >= 0 && (blob.isMutable() || !isMutable)) {
+ if (blob.fd() >= 0 && (blob.isMutable() || !isMutable) && (size >= ASHMEM_BITMAP_MIN_SIZE)) {
#if DEBUG_PARCEL
ALOGD("Bitmap.createFromParcel: mapped contents of %s bitmap from %s blob "
"(fds %s)",
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 28bc7fe..ecaf951 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -165,7 +165,7 @@
virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
const SkImageInfo& info = bitmap->info();
- if (info.fColorType == kUnknown_SkColorType) {
+ if (info.colorType() == kUnknown_SkColorType) {
ALOGW("unable to reuse a bitmap as the target has an unknown bitmap configuration");
return false;
}
diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp
index 2df3a46..3cdf640 100644
--- a/core/jni/android/graphics/BitmapRegionDecoder.cpp
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -39,14 +39,14 @@
using namespace android;
-class SkBitmapRegionDecoder {
+class BitmapRegionDecoder {
public:
- SkBitmapRegionDecoder(SkImageDecoder* decoder, int width, int height) {
+ BitmapRegionDecoder(SkImageDecoder* decoder, int width, int height) {
fDecoder = decoder;
fWidth = width;
fHeight = height;
}
- ~SkBitmapRegionDecoder() {
+ ~BitmapRegionDecoder() {
delete fDecoder;
}
@@ -91,7 +91,7 @@
return nullObjectReturn("decoder->buildTileIndex returned false");
}
- SkBitmapRegionDecoder *bm = new SkBitmapRegionDecoder(decoder, width, height);
+ BitmapRegionDecoder *bm = new BitmapRegionDecoder(decoder, width, height);
return GraphicsJNI::createBitmapRegionDecoder(env, bm);
}
@@ -166,7 +166,7 @@
*/
static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle,
jint start_x, jint start_y, jint width, jint height, jobject options) {
- SkBitmapRegionDecoder *brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
+ BitmapRegionDecoder *brd = reinterpret_cast<BitmapRegionDecoder*>(brdHandle);
jobject tileBitmap = NULL;
SkImageDecoder *decoder = brd->getDecoder();
int sampleSize = 1;
@@ -245,17 +245,17 @@
}
static jint nativeGetHeight(JNIEnv* env, jobject, jlong brdHandle) {
- SkBitmapRegionDecoder *brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
+ BitmapRegionDecoder *brd = reinterpret_cast<BitmapRegionDecoder*>(brdHandle);
return static_cast<jint>(brd->getHeight());
}
static jint nativeGetWidth(JNIEnv* env, jobject, jlong brdHandle) {
- SkBitmapRegionDecoder *brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
+ BitmapRegionDecoder *brd = reinterpret_cast<BitmapRegionDecoder*>(brdHandle);
return static_cast<jint>(brd->getWidth());
}
static void nativeClean(JNIEnv* env, jobject, jlong brdHandle) {
- SkBitmapRegionDecoder *brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
+ BitmapRegionDecoder *brd = reinterpret_cast<BitmapRegionDecoder*>(brdHandle);
delete brd;
}
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 93259e7..ed44019 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -439,7 +439,7 @@
return env->CallIntMethod(javaBitmap, gBitmap_getAllocationByteCountMethodID);
}
-jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap)
+jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, BitmapRegionDecoder* bitmap)
{
SkASSERT(bitmap != NULL);
@@ -486,7 +486,7 @@
android::Bitmap* GraphicsJNI::allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
SkColorTable* ctable) {
const SkImageInfo& info = bitmap->info();
- if (info.fColorType == kUnknown_SkColorType) {
+ if (info.colorType() == kUnknown_SkColorType) {
doThrowIAE(env, "unknown bitmap configuration");
return NULL;
}
@@ -538,7 +538,7 @@
bool GraphicsJNI::allocatePixels(JNIEnv* env, SkBitmap* bitmap, SkColorTable* ctable) {
const SkImageInfo& info = bitmap->info();
- if (info.fColorType == kUnknown_SkColorType) {
+ if (info.colorType() == kUnknown_SkColorType) {
doThrowIAE(env, "unknown bitmap configuration");
return NULL;
}
@@ -581,7 +581,7 @@
int fd;
const SkImageInfo& info = bitmap->info();
- if (info.fColorType == kUnknown_SkColorType) {
+ if (info.colorType() == kUnknown_SkColorType) {
doThrowIAE(env, "unknown bitmap configuration");
return nullptr;
}
@@ -625,7 +625,7 @@
android::Bitmap* GraphicsJNI::mapAshmemPixelRef(JNIEnv* env, SkBitmap* bitmap,
SkColorTable* ctable, int fd, void* addr, bool readOnly) {
const SkImageInfo& info = bitmap->info();
- if (info.fColorType == kUnknown_SkColorType) {
+ if (info.colorType() == kUnknown_SkColorType) {
doThrowIAE(env, "unknown bitmap configuration");
return nullptr;
}
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index bcd834b..90f8291 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -12,7 +12,7 @@
#include <Canvas.h>
#include <jni.h>
-class SkBitmapRegionDecoder;
+class BitmapRegionDecoder;
class SkCanvas;
namespace android {
@@ -90,7 +90,7 @@
static jobject createRegion(JNIEnv* env, SkRegion* region);
- static jobject createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap);
+ static jobject createBitmapRegionDecoder(JNIEnv* env, BitmapRegionDecoder* bitmap);
static android::Bitmap* allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
SkColorTable* ctable);
diff --git a/core/jni/android/graphics/MinikinSkia.cpp b/core/jni/android/graphics/MinikinSkia.cpp
index 4649b07..8ac5d46 100644
--- a/core/jni/android/graphics/MinikinSkia.cpp
+++ b/core/jni/android/graphics/MinikinSkia.cpp
@@ -32,16 +32,6 @@
SkSafeUnref(mTypeface);
}
-bool MinikinFontSkia::GetGlyph(uint32_t codepoint, uint32_t *glyph) const {
- SkPaint paint;
- paint.setTypeface(mTypeface);
- paint.setTextEncoding(SkPaint::kUTF32_TextEncoding);
- uint16_t glyph16;
- paint.textToGlyphs(&codepoint, sizeof(codepoint), &glyph16);
- *glyph = glyph16;
- return !!glyph16;
-}
-
static void MinikinFontSkia_SetSkiaPaint(const MinikinFont* font, SkPaint* skPaint, const MinikinPaint& paint) {
skPaint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
skPaint->setTextSize(paint.size);
diff --git a/core/jni/android/graphics/MinikinSkia.h b/core/jni/android/graphics/MinikinSkia.h
index a06428d..8f469ba 100644
--- a/core/jni/android/graphics/MinikinSkia.h
+++ b/core/jni/android/graphics/MinikinSkia.h
@@ -28,8 +28,6 @@
~MinikinFontSkia();
- bool GetGlyph(uint32_t codepoint, uint32_t *glyph) const;
-
float GetHorizontalAdvance(uint32_t glyph_id,
const MinikinPaint &paint) const;
@@ -54,4 +52,4 @@
} // namespace android
-#endif // _ANDROID_GRAPHICS_MINIKIN_SKIA_H_
\ No newline at end of file
+#endif // _ANDROID_GRAPHICS_MINIKIN_SKIA_H_
diff --git a/core/jni/android/graphics/MinikinUtils.cpp b/core/jni/android/graphics/MinikinUtils.cpp
index d1780d6..7fd288a 100644
--- a/core/jni/android/graphics/MinikinUtils.cpp
+++ b/core/jni/android/graphics/MinikinUtils.cpp
@@ -33,11 +33,11 @@
FontStyle resolved = resolvedFace->fStyle;
/* Prepare minikin FontStyle */
- const std::string& lang = paint->getTextLocale();
- FontLanguage minikinLang(lang.c_str(), lang.size());
+ const std::string& langs = paint->getTextLocales();
+ FontLanguages minikinLangs(langs.c_str(), langs.size());
FontVariant minikinVariant = (paint->getFontVariant() == VARIANT_ELEGANT) ? VARIANT_ELEGANT
: VARIANT_COMPACT;
- FontStyle minikinStyle(minikinLang, minikinVariant, resolved.getWeight(), resolved.getItalic());
+ FontStyle minikinStyle(minikinLangs, minikinVariant, resolved.getWeight(), resolved.getItalic());
/* Prepare minikin Paint */
// Note: it would be nice to handle fractional size values (it would improve smooth zoom
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 314e4b6..b50046f 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -28,6 +28,7 @@
#include "SkBlurDrawLooper.h"
#include "SkColorFilter.h"
#include "SkMaskFilter.h"
+#include "SkPath.h"
#include "SkRasterizer.h"
#include "SkShader.h"
#include "SkTypeface.h"
@@ -43,12 +44,10 @@
#include "Paint.h"
#include "TypefaceImpl.h"
-#include <vector>
+#include <cassert>
+#include <cstring>
#include <memory>
-
-// temporary for debugging
-#include <Caches.h>
-#include <utils/Log.h>
+#include <vector>
namespace android {
@@ -71,12 +70,12 @@
paint->setTextEncoding(Paint::kGlyphID_TextEncoding);
}
-struct LocaleCacheEntry {
- std::string javaLocale;
- std::string languageTag;
+struct LocalesCacheEntry {
+ std::string javaLocales;
+ std::string languageTags;
};
-static thread_local LocaleCacheEntry sSingleEntryLocaleCache;
+static thread_local LocalesCacheEntry sSingleEntryLocalesCache;
namespace PaintGlue {
enum MoveOpt {
@@ -360,17 +359,57 @@
output[0] = '\0';
}
- static void setTextLocale(JNIEnv* env, jobject clazz, jlong objHandle, jstring locale) {
- Paint* obj = reinterpret_cast<Paint*>(objHandle);
- ScopedUtfChars localeChars(env, locale);
- if (sSingleEntryLocaleCache.javaLocale != localeChars.c_str()) {
- sSingleEntryLocaleCache.javaLocale = localeChars.c_str();
- char langTag[ULOC_FULLNAME_CAPACITY];
- toLanguageTag(langTag, ULOC_FULLNAME_CAPACITY, localeChars.c_str());
- sSingleEntryLocaleCache.languageTag = langTag;
+ static void toLanguageTags(std::string* output, const char* locales) {
+ if (output == NULL) {
+ return;
+ }
+ if (locales == NULL) {
+ output->clear();
+ return;
}
- obj->setTextLocale(sSingleEntryLocaleCache.languageTag);
+ char langTag[ULOC_FULLNAME_CAPACITY];
+ const char* commaLoc = strchr(locales, ',');
+ if (commaLoc == NULL) {
+ assert(locales[0] != '\0'); // the string should not be empty
+ toLanguageTag(langTag, ULOC_FULLNAME_CAPACITY, locales);
+ *output = langTag;
+ return;
+ }
+
+ size_t len = strlen(locales);
+ char locale[len];
+ output->clear();
+ output->reserve(len);
+ const char* lastStart = locales;
+ do {
+ assert(lastStart > commaLoc); // the substring should not be empty
+ strncpy(locale, lastStart, commaLoc - lastStart);
+ locale[commaLoc - lastStart] = '\0';
+ toLanguageTag(langTag, ULOC_FULLNAME_CAPACITY, locale);
+ if (langTag[0] != '\0') {
+ output->append(langTag);
+ output->push_back(',');
+ }
+ lastStart = commaLoc + 1;
+ commaLoc = strchr(lastStart, ',');
+ } while (commaLoc != NULL);
+ assert(lastStart[0] != '\0'); // the final substring should not be empty
+ toLanguageTag(langTag, ULOC_FULLNAME_CAPACITY, lastStart);
+ if (langTag[0] != '\0') {
+ output->append(langTag);
+ }
+ }
+
+ static void setTextLocales(JNIEnv* env, jobject clazz, jlong objHandle, jstring locales) {
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ ScopedUtfChars localesChars(env, locales);
+ if (sSingleEntryLocalesCache.javaLocales != localesChars.c_str()) {
+ sSingleEntryLocalesCache.javaLocales = localesChars.c_str();
+ toLanguageTags(&sSingleEntryLocalesCache.languageTags, localesChars.c_str());
+ }
+
+ obj->setTextLocales(sSingleEntryLocalesCache.languageTags);
}
static jboolean isElegantTextHeight(JNIEnv* env, jobject, jlong paintHandle) {
@@ -952,7 +991,7 @@
{"nSetRasterizer","!(JJ)J", (void*) PaintGlue::setRasterizer},
{"nGetTextAlign","!(J)I", (void*) PaintGlue::getTextAlign},
{"nSetTextAlign","!(JI)V", (void*) PaintGlue::setTextAlign},
- {"nSetTextLocale","!(JLjava/lang/String;)V", (void*) PaintGlue::setTextLocale},
+ {"nSetTextLocales","!(JLjava/lang/String;)V", (void*) PaintGlue::setTextLocales},
{"nIsElegantTextHeight","!(J)Z", (void*) PaintGlue::isElegantTextHeight},
{"nSetElegantTextHeight","!(JZ)V", (void*) PaintGlue::setElegantTextHeight},
{"nGetTextSize","!(J)F", (void*) PaintGlue::getTextSize},
diff --git a/core/jni/android/graphics/Paint.h b/core/jni/android/graphics/Paint.h
index 6df22ff..7a34bc2 100644
--- a/core/jni/android/graphics/Paint.h
+++ b/core/jni/android/graphics/Paint.h
@@ -53,12 +53,12 @@
return mFontFeatureSettings;
}
- void setTextLocale(const std::string &textLocale) {
- mTextLocale = textLocale;
+ void setTextLocales(const std::string &textLocales) {
+ mTextLocales = textLocales;
}
- const std::string& getTextLocale() const {
- return mTextLocale;
+ const std::string& getTextLocales() const {
+ return mTextLocales;
}
void setFontVariant(FontVariant variant) {
@@ -80,7 +80,7 @@
private:
float mLetterSpacing = 0;
std::string mFontFeatureSettings;
- std::string mTextLocale;
+ std::string mTextLocales;
FontVariant mFontVariant;
uint32_t mHyphenEdit = 0;
};
diff --git a/core/jni/android/graphics/PaintImpl.cpp b/core/jni/android/graphics/PaintImpl.cpp
index da85018..d5a0972 100644
--- a/core/jni/android/graphics/PaintImpl.cpp
+++ b/core/jni/android/graphics/PaintImpl.cpp
@@ -23,12 +23,12 @@
namespace android {
Paint::Paint() : SkPaint(),
- mLetterSpacing(0), mFontFeatureSettings(), mTextLocale(), mFontVariant(VARIANT_DEFAULT) {
+ mLetterSpacing(0), mFontFeatureSettings(), mTextLocales(), mFontVariant(VARIANT_DEFAULT) {
}
Paint::Paint(const Paint& paint) : SkPaint(paint),
mLetterSpacing(paint.mLetterSpacing), mFontFeatureSettings(paint.mFontFeatureSettings),
- mTextLocale(paint.mTextLocale), mFontVariant(paint.mFontVariant),
+ mTextLocales(paint.mTextLocales), mFontVariant(paint.mFontVariant),
mHyphenEdit(paint.mHyphenEdit) {
}
@@ -39,7 +39,7 @@
SkPaint::operator=(other);
mLetterSpacing = other.mLetterSpacing;
mFontFeatureSettings = other.mFontFeatureSettings;
- mTextLocale = other.mTextLocale;
+ mTextLocales = other.mTextLocales;
mFontVariant = other.mFontVariant;
mHyphenEdit = other.mHyphenEdit;
return *this;
@@ -49,7 +49,7 @@
return static_cast<const SkPaint&>(a) == static_cast<const SkPaint&>(b)
&& a.mLetterSpacing == b.mLetterSpacing
&& a.mFontFeatureSettings == b.mFontFeatureSettings
- && a.mTextLocale == b.mTextLocale
+ && a.mTextLocales == b.mTextLocales
&& a.mFontVariant == b.mFontVariant
&& a.mHyphenEdit == b.mHyphenEdit;
}
diff --git a/core/jni/android/opengl/util.cpp b/core/jni/android/opengl/util.cpp
index e045f5f..d4735ec 100644
--- a/core/jni/android/opengl/util.cpp
+++ b/core/jni/android/opengl/util.cpp
@@ -643,12 +643,6 @@
// ---------------------------------------------------------------------------
-extern void setGLDebugLevel(int level);
-void setTracingLevel(JNIEnv *env, jclass clazz, jint level)
-{
- setGLDebugLevel(level);
-}
-
static int checkFormat(SkColorType colorType, int format, int type)
{
switch(colorType) {
@@ -1103,7 +1097,6 @@
{ "native_getType", "(Landroid/graphics/Bitmap;)I", (void*) util_getType },
{ "native_texImage2D", "(IIILandroid/graphics/Bitmap;II)I", (void*)util_texImage2D },
{ "native_texSubImage2D", "(IIIILandroid/graphics/Bitmap;II)I", (void*)util_texSubImage2D },
- { "setTracingLevel", "(I)V", (void*)setTracingLevel },
};
static const JNINativeMethod gEtc1Methods[] = {
diff --git a/core/jni/android_hardware_Radio.cpp b/core/jni/android_hardware_Radio.cpp
index b9dd77a..ec6471e 100644
--- a/core/jni/android_hardware_Radio.cpp
+++ b/core/jni/android_hardware_Radio.cpp
@@ -93,6 +93,7 @@
jfieldID mRds;
jfieldID mTa;
jfieldID mAf;
+ jfieldID mEa;
} gRadioFmBandConfigFields;
static const char* const kRadioAmBandConfigClassPathName =
@@ -117,6 +118,7 @@
jmethodID putIntFromNative;
jmethodID putStringFromNative;
jmethodID putBitmapFromNative;
+ jmethodID putClockFromNative;
} gRadioMetadataMethods;
static Mutex gLock;
@@ -171,7 +173,8 @@
nBandconfig->band.fm.stereo,
nBandconfig->band.fm.rds != RADIO_RDS_NONE,
nBandconfig->band.fm.ta,
- nBandconfig->band.fm.af);
+ nBandconfig->band.fm.af,
+ nBandconfig->band.fm.ea);
} else if (nBandconfig->band.type == RADIO_BAND_AM) {
*jBandDescriptor = env->NewObject(gRadioAmBandDescriptorClass, gRadioAmBandDescriptorCstor,
nBandconfig->region, nBandconfig->band.type,
@@ -205,7 +208,8 @@
nBandconfig->band.fm.stereo,
nBandconfig->band.fm.rds != RADIO_RDS_NONE,
nBandconfig->band.fm.ta,
- nBandconfig->band.fm.af);
+ nBandconfig->band.fm.af,
+ nBandconfig->band.fm.ea);
} else if (nBandconfig->band.type == RADIO_BAND_AM) {
*jBandConfig = env->NewObject(gRadioAmBandConfigClass, gRadioAmBandConfigCstor,
nBandconfig->region, nBandconfig->band.type,
@@ -284,6 +288,18 @@
}
env->DeleteLocalRef(jData);
} break;
+ case RADIO_METADATA_TYPE_CLOCK: {
+ ALOGV("%s RADIO_METADATA_TYPE_CLOCK %d", __FUNCTION__, key);
+ radio_metadata_clock_t *clock = (radio_metadata_clock_t *) value;
+ jStatus =
+ env->CallIntMethod(*jMetadata,
+ gRadioMetadataMethods.putClockFromNative,
+ key, (jint) clock->utc_seconds_since_epoch,
+ (jint) clock->timezone_offset_in_minutes);
+ if (jStatus == 0) {
+ jCount++;
+ }
+ } break;
}
}
return jCount;
@@ -351,6 +367,7 @@
nBandconfig->region);
nBandconfig->band.fm.ta = env->GetBooleanField(jBandConfig, gRadioFmBandConfigFields.mTa);
nBandconfig->band.fm.af = env->GetBooleanField(jBandConfig, gRadioFmBandConfigFields.mAf);
+ nBandconfig->band.fm.ea = env->GetBooleanField(jBandConfig, gRadioFmBandConfigFields.mEa);
} else if (env->IsInstanceOf(jBandConfig, gRadioAmBandConfigClass)) {
nBandconfig->band.am.stereo =
env->GetBooleanField(jBandConfig, gRadioAmBandConfigFields.mStereo);
@@ -518,6 +535,7 @@
break;
case RADIO_EVENT_ANTENNA:
case RADIO_EVENT_TA:
+ case RADIO_EVENT_EA:
case RADIO_EVENT_CONTROL:
jArg2 = event->on ? 1 : 0;
break;
@@ -878,7 +896,7 @@
jclass fmBandDescriptorClass = FindClassOrDie(env, kRadioFmBandDescriptorClassPathName);
gRadioFmBandDescriptorClass = MakeGlobalRefOrDie(env, fmBandDescriptorClass);
gRadioFmBandDescriptorCstor = GetMethodIDOrDie(env, fmBandDescriptorClass, "<init>",
- "(IIIIIZZZZ)V");
+ "(IIIIIZZZZZ)V");
jclass amBandDescriptorClass = FindClassOrDie(env, kRadioAmBandDescriptorClassPathName);
gRadioAmBandDescriptorClass = MakeGlobalRefOrDie(env, amBandDescriptorClass);
@@ -894,11 +912,13 @@
jclass fmBandConfigClass = FindClassOrDie(env, kRadioFmBandConfigClassPathName);
gRadioFmBandConfigClass = MakeGlobalRefOrDie(env, fmBandConfigClass);
gRadioFmBandConfigCstor = GetMethodIDOrDie(env, fmBandConfigClass, "<init>",
- "(IIIIIZZZZ)V");
+ "(IIIIIZZZZZ)V");
gRadioFmBandConfigFields.mStereo = GetFieldIDOrDie(env, fmBandConfigClass, "mStereo", "Z");
gRadioFmBandConfigFields.mRds = GetFieldIDOrDie(env, fmBandConfigClass, "mRds", "Z");
gRadioFmBandConfigFields.mTa = GetFieldIDOrDie(env, fmBandConfigClass, "mTa", "Z");
gRadioFmBandConfigFields.mAf = GetFieldIDOrDie(env, fmBandConfigClass, "mAf", "Z");
+ gRadioFmBandConfigFields.mEa =
+ GetFieldIDOrDie(env, fmBandConfigClass, "mEa", "Z");
jclass amBandConfigClass = FindClassOrDie(env, kRadioAmBandConfigClassPathName);
@@ -924,6 +944,9 @@
gRadioMetadataMethods.putBitmapFromNative = GetMethodIDOrDie(env, metadataClass,
"putBitmapFromNative",
"(I[B)I");
+ gRadioMetadataMethods.putClockFromNative = GetMethodIDOrDie(env, metadataClass,
+ "putClockFromNative",
+ "(IJI)I");
RegisterMethodsOrDie(env, kRadioManagerClassPathName, gMethods, NELEM(gMethods));
diff --git a/core/jni/android_hardware_camera2_DngCreator.cpp b/core/jni/android_hardware_camera2_DngCreator.cpp
index 738a62a..cb0abb6 100644
--- a/core/jni/android_hardware_camera2_DngCreator.cpp
+++ b/core/jni/android_hardware_camera2_DngCreator.cpp
@@ -1660,10 +1660,11 @@
uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
if (entry2.count > 0 && entry2.count == lsmWidth * lsmHeight * 4) {
+ // GainMap rectangle is relative to the active area origin.
err = builder.addGainMapsForMetadata(lsmWidth,
lsmHeight,
- ymin,
- xmin,
+ 0,
+ 0,
height,
width,
opcodeCfaLayout,
diff --git a/core/jni/android_opengl_EGL14.cpp b/core/jni/android_opengl_EGL14.cpp
index 568473c..c8b1784 100644
--- a/core/jni/android_opengl_EGL14.cpp
+++ b/core/jni/android_opengl_EGL14.cpp
@@ -184,51 +184,43 @@
jint _minorRemaining;
EGLint *minor = (EGLint *) 0;
- if (!major_ref) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "major == null";
- goto exit;
+ if (major_ref) {
+ if (majorOffset < 0) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "majorOffset < 0";
+ goto exit;
+ }
+ _majorRemaining = _env->GetArrayLength(major_ref) - majorOffset;
+ if (_majorRemaining < 1) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "length - majorOffset < 1 < needed";
+ goto exit;
+ }
+ major_base = (EGLint *)
+ _env->GetIntArrayElements(major_ref, (jboolean *)0);
+ major = major_base + majorOffset;
}
- if (majorOffset < 0) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "majorOffset < 0";
- goto exit;
- }
- _majorRemaining = _env->GetArrayLength(major_ref) - majorOffset;
- if (_majorRemaining < 1) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "length - majorOffset < 1 < needed";
- goto exit;
- }
- major_base = (EGLint *)
- _env->GetIntArrayElements(major_ref, (jboolean *)0);
- major = major_base + majorOffset;
- if (!minor_ref) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "minor == null";
- goto exit;
+ if (minor_ref) {
+ if (minorOffset < 0) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "minorOffset < 0";
+ goto exit;
+ }
+ _minorRemaining = _env->GetArrayLength(minor_ref) - minorOffset;
+ if (_minorRemaining < 1) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "length - minorOffset < 1 < needed";
+ goto exit;
+ }
+ minor_base = (EGLint *)
+ _env->GetIntArrayElements(minor_ref, (jboolean *)0);
+ minor = minor_base + minorOffset;
}
- if (minorOffset < 0) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "minorOffset < 0";
- goto exit;
- }
- _minorRemaining = _env->GetArrayLength(minor_ref) - minorOffset;
- if (_minorRemaining < 1) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "length - minorOffset < 1 < needed";
- goto exit;
- }
- minor_base = (EGLint *)
- _env->GetIntArrayElements(minor_ref, (jboolean *)0);
- minor = minor_base + minorOffset;
_returnValue = eglInitialize(
(EGLDisplay)dpy_native,
@@ -289,26 +281,22 @@
jint _num_configRemaining;
EGLint *num_config = (EGLint *) 0;
- if (!configs_ref) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "configs == null";
- goto exit;
+ if (configs_ref) {
+ if (configsOffset < 0) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "configsOffset < 0";
+ goto exit;
+ }
+ _configsRemaining = _env->GetArrayLength(configs_ref) - configsOffset;
+ if (_configsRemaining < config_size) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "length - configsOffset < config_size < needed";
+ goto exit;
+ }
+ configs = new EGLConfig[_configsRemaining];
}
- if (configsOffset < 0) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "configsOffset < 0";
- goto exit;
- }
- _configsRemaining = _env->GetArrayLength(configs_ref) - configsOffset;
- if (_configsRemaining < config_size) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "length - configsOffset < config_size < needed";
- goto exit;
- }
- configs = new EGLConfig[_configsRemaining];
if (!num_config_ref) {
_exception = 1;
@@ -401,26 +389,22 @@
goto exit;
}
- if (!configs_ref) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "configs == null";
- goto exit;
+ if (configs_ref) {
+ if (configsOffset < 0) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "configsOffset < 0";
+ goto exit;
+ }
+ _configsRemaining = _env->GetArrayLength(configs_ref) - configsOffset;
+ if (_configsRemaining < config_size) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "length - configsOffset < config_size < needed";
+ goto exit;
+ }
+ configs = new EGLConfig[_configsRemaining];
}
- if (configsOffset < 0) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "configsOffset < 0";
- goto exit;
- }
- _configsRemaining = _env->GetArrayLength(configs_ref) - configsOffset;
- if (_configsRemaining < config_size) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "length - configsOffset < config_size < needed";
- goto exit;
- }
- configs = new EGLConfig[_configsRemaining];
if (!num_config_ref) {
_exception = 1;
@@ -546,18 +530,32 @@
EGLint *attrib_list = (EGLint *) 0;
android::sp<ANativeWindow> window;
- if (!attrib_list_ref) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "attrib_list == null";
- goto exit;
+ if (attrib_list_ref) {
+ if (offset < 0) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "offset < 0";
+ goto exit;
+ }
+ _remaining = _env->GetArrayLength(attrib_list_ref) - offset;
+ attrib_list_base = (EGLint *)
+ _env->GetIntArrayElements(attrib_list_ref, (jboolean *)0);
+ attrib_list = attrib_list_base + offset;
+ attrib_list_sentinel = 0;
+ for (int i = _remaining - 1; i >= 0; i--) {
+ if (*((EGLint*)(attrib_list + i)) == EGL_NONE){
+ attrib_list_sentinel = 1;
+ break;
+ }
+ }
+ if (attrib_list_sentinel == 0) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "attrib_list must contain EGL_NONE!";
+ goto exit;
+ }
}
- if (offset < 0) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "offset < 0";
- goto exit;
- }
+
if (win == NULL) {
not_valid_surface:
_exception = 1;
@@ -571,24 +569,6 @@
if (window == NULL)
goto not_valid_surface;
- _remaining = _env->GetArrayLength(attrib_list_ref) - offset;
- attrib_list_base = (EGLint *)
- _env->GetIntArrayElements(attrib_list_ref, (jboolean *)0);
- attrib_list = attrib_list_base + offset;
- attrib_list_sentinel = 0;
- for (int i = _remaining - 1; i >= 0; i--) {
- if (*((EGLint*)(attrib_list + i)) == EGL_NONE){
- attrib_list_sentinel = 1;
- break;
- }
- }
- if (attrib_list_sentinel == 0) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "attrib_list must contain EGL_NONE!";
- goto exit;
- }
-
_returnValue = eglCreateWindowSurface(
(EGLDisplay)dpy_native,
(EGLConfig)config_native,
@@ -703,34 +683,30 @@
jint _remaining;
EGLint *attrib_list = (EGLint *) 0;
- if (!attrib_list_ref) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "attrib_list == null";
- goto exit;
- }
- if (offset < 0) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "offset < 0";
- goto exit;
- }
- _remaining = _env->GetArrayLength(attrib_list_ref) - offset;
- attrib_list_base = (EGLint *)
- _env->GetIntArrayElements(attrib_list_ref, (jboolean *)0);
- attrib_list = attrib_list_base + offset;
- attrib_list_sentinel = false;
- for (int i = _remaining - 1; i >= 0; i--) {
- if (attrib_list[i] == EGL_NONE){
- attrib_list_sentinel = true;
- break;
+ if (attrib_list_ref) {
+ if (offset < 0) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "offset < 0";
+ goto exit;
}
- }
- if (attrib_list_sentinel == false) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "attrib_list must contain EGL_NONE!";
- goto exit;
+ _remaining = _env->GetArrayLength(attrib_list_ref) - offset;
+ attrib_list_base = (EGLint *)
+ _env->GetIntArrayElements(attrib_list_ref, (jboolean *)0);
+ attrib_list = attrib_list_base + offset;
+ attrib_list_sentinel = false;
+ for (int i = _remaining - 1; i >= 0; i--) {
+ if (attrib_list[i] == EGL_NONE){
+ attrib_list_sentinel = true;
+ break;
+ }
+ }
+ if (attrib_list_sentinel == false) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "attrib_list must contain EGL_NONE!";
+ goto exit;
+ }
}
_returnValue = eglCreatePbufferSurface(
@@ -882,34 +858,30 @@
jint _remaining;
EGLint *attrib_list = (EGLint *) 0;
- if (!attrib_list_ref) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "attrib_list == null";
- goto exit;
- }
- if (offset < 0) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "offset < 0";
- goto exit;
- }
- _remaining = _env->GetArrayLength(attrib_list_ref) - offset;
- attrib_list_base = (EGLint *)
- _env->GetIntArrayElements(attrib_list_ref, (jboolean *)0);
- attrib_list = attrib_list_base + offset;
- attrib_list_sentinel = false;
- for (int i = _remaining - 1; i >= 0; i--) {
- if (attrib_list[i] == EGL_NONE){
- attrib_list_sentinel = true;
- break;
+ if (attrib_list_ref) {
+ if (offset < 0) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "offset < 0";
+ goto exit;
}
- }
- if (attrib_list_sentinel == false) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "attrib_list must contain EGL_NONE!";
- goto exit;
+ _remaining = _env->GetArrayLength(attrib_list_ref) - offset;
+ attrib_list_base = (EGLint *)
+ _env->GetIntArrayElements(attrib_list_ref, (jboolean *)0);
+ attrib_list = attrib_list_base + offset;
+ attrib_list_sentinel = false;
+ for (int i = _remaining - 1; i >= 0; i--) {
+ if (attrib_list[i] == EGL_NONE){
+ attrib_list_sentinel = true;
+ break;
+ }
+ }
+ if (attrib_list_sentinel == false) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "attrib_list must contain EGL_NONE!";
+ goto exit;
+ }
}
_returnValue = eglCreatePbufferFromClientBuffer(
diff --git a/core/jni/android_opengl_GLES20.cpp b/core/jni/android_opengl_GLES20.cpp
index b9f61a9..ac3bf7a 100644
--- a/core/jni/android_opengl_GLES20.cpp
+++ b/core/jni/android_opengl_GLES20.cpp
@@ -1783,22 +1783,18 @@
jint _nameRemaining;
char *name = (char *) 0;
- if (!length_ref) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "length == null";
- goto exit;
+ if (length_ref) {
+ if (lengthOffset < 0) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "lengthOffset < 0";
+ goto exit;
+ }
+ _lengthRemaining = _env->GetArrayLength(length_ref) - lengthOffset;
+ length_base = (GLsizei *)
+ _env->GetIntArrayElements(length_ref, (jboolean *)0);
+ length = length_base + lengthOffset;
}
- if (lengthOffset < 0) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "lengthOffset < 0";
- goto exit;
- }
- _lengthRemaining = _env->GetArrayLength(length_ref) - lengthOffset;
- length_base = (GLsizei *)
- _env->GetIntArrayElements(length_ref, (jboolean *)0);
- length = length_base + lengthOffset;
if (!size_ref) {
_exception = 1;
@@ -2111,22 +2107,18 @@
jint _nameRemaining;
char *name = (char *) 0;
- if (!length_ref) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "length == null";
- goto exit;
+ if (length_ref) {
+ if (lengthOffset < 0) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "lengthOffset < 0";
+ goto exit;
+ }
+ _lengthRemaining = _env->GetArrayLength(length_ref) - lengthOffset;
+ length_base = (GLsizei *)
+ _env->GetIntArrayElements(length_ref, (jboolean *)0);
+ length = length_base + lengthOffset;
}
- if (lengthOffset < 0) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "lengthOffset < 0";
- goto exit;
- }
- _lengthRemaining = _env->GetArrayLength(length_ref) - lengthOffset;
- length_base = (GLsizei *)
- _env->GetIntArrayElements(length_ref, (jboolean *)0);
- length = length_base + lengthOffset;
if (!size_ref) {
_exception = 1;
@@ -2434,28 +2426,24 @@
jint _shadersRemaining;
GLuint *shaders = (GLuint *) 0;
- if (!count_ref) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "count == null";
- goto exit;
+ if (count_ref) {
+ if (countOffset < 0) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "countOffset < 0";
+ goto exit;
+ }
+ _countRemaining = _env->GetArrayLength(count_ref) - countOffset;
+ if (_countRemaining < 1) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "length - countOffset < 1 < needed";
+ goto exit;
+ }
+ count_base = (GLsizei *)
+ _env->GetIntArrayElements(count_ref, (jboolean *)0);
+ count = count_base + countOffset;
}
- if (countOffset < 0) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "countOffset < 0";
- goto exit;
- }
- _countRemaining = _env->GetArrayLength(count_ref) - countOffset;
- if (_countRemaining < 1) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "length - countOffset < 1 < needed";
- goto exit;
- }
- count_base = (GLsizei *)
- _env->GetIntArrayElements(count_ref, (jboolean *)0);
- count = count_base + countOffset;
if (!shaders_ref) {
_exception = 1;
@@ -2526,20 +2514,18 @@
goto exit;
}
}
- if (shaders_buf) {
- shaders = (GLuint *)getPointer(_env, shaders_buf, (jarray*)&_shadersArray, &_shadersRemaining, &_shadersBufferOffset);
- if (_shadersRemaining < maxcount) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "remaining() < maxcount < needed";
- goto exit;
- }
+ shaders = (GLuint *)getPointer(_env, shaders_buf, (jarray*)&_shadersArray, &_shadersRemaining, &_shadersBufferOffset);
+ if (_shadersRemaining < maxcount) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "remaining() < maxcount < needed";
+ goto exit;
}
if (count_buf && count == NULL) {
char * _countBase = (char *)_env->GetIntArrayElements(_countArray, (jboolean *) 0);
count = (GLsizei *) (_countBase + _countBufferOffset);
}
- if (shaders_buf && shaders == NULL) {
+ if (shaders == NULL) {
char * _shadersBase = (char *)_env->GetIntArrayElements(_shadersArray, (jboolean *) 0);
shaders = (GLuint *) (_shadersBase + _shadersBufferOffset);
}
@@ -3262,22 +3248,18 @@
jint _sourceRemaining;
char *source = (char *) 0;
- if (!length_ref) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "length == null";
- goto exit;
+ if (length_ref) {
+ if (lengthOffset < 0) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "lengthOffset < 0";
+ goto exit;
+ }
+ _lengthRemaining = _env->GetArrayLength(length_ref) - lengthOffset;
+ length_base = (GLsizei *)
+ _env->GetIntArrayElements(length_ref, (jboolean *)0);
+ length = length_base + lengthOffset;
}
- if (lengthOffset < 0) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "lengthOffset < 0";
- goto exit;
- }
- _lengthRemaining = _env->GetArrayLength(length_ref) - lengthOffset;
- length_base = (GLsizei *)
- _env->GetIntArrayElements(length_ref, (jboolean *)0);
- length = length_base + lengthOffset;
if (!source_ref) {
_exception = 1;
diff --git a/core/jni/android_opengl_GLES30.cpp b/core/jni/android_opengl_GLES30.cpp
index 8eb5044..c9b5af7 100644
--- a/core/jni/android_opengl_GLES30.cpp
+++ b/core/jni/android_opengl_GLES30.cpp
@@ -501,8 +501,10 @@
jint _remaining;
GLvoid *pixels = (GLvoid *) 0;
- pixels = (GLvoid *)getPointer(_env, pixels_buf, (jarray*)&_array, &_remaining, &_bufferOffset);
- if (pixels == NULL) {
+ if (pixels_buf) {
+ pixels = (GLvoid *)getPointer(_env, pixels_buf, (jarray*)&_array, &_remaining, &_bufferOffset);
+ }
+ if (pixels_buf && pixels == NULL) {
char * _pixelsBase = (char *)_env->GetPrimitiveArrayCritical(_array, (jboolean *) 0);
pixels = (GLvoid *) (_pixelsBase + _bufferOffset);
}
@@ -1910,22 +1912,18 @@
jint _nameRemaining;
char *name = (char *) 0;
- if (!length_ref) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "length == null";
- goto exit;
+ if (length_ref) {
+ if (lengthOffset < 0) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "lengthOffset < 0";
+ goto exit;
+ }
+ _lengthRemaining = _env->GetArrayLength(length_ref) - lengthOffset;
+ length_base = (GLsizei *)
+ _env->GetIntArrayElements(length_ref, (jboolean *)0);
+ length = length_base + lengthOffset;
}
- if (lengthOffset < 0) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "lengthOffset < 0";
- goto exit;
- }
- _lengthRemaining = _env->GetArrayLength(length_ref) - lengthOffset;
- length_base = (GLsizei *)
- _env->GetIntArrayElements(length_ref, (jboolean *)0);
- length = length_base + lengthOffset;
if (!size_ref) {
_exception = 1;
@@ -3591,22 +3589,18 @@
jint _nameRemaining;
GLchar* _name = (GLchar*)0;
- if (!length_ref) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "length == null";
- goto exit;
+ if (length_ref) {
+ if (lengthOffset < 0) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "lengthOffset < 0";
+ goto exit;
+ }
+ _lengthRemaining = _env->GetArrayLength(length_ref) - lengthOffset;
+ _length_base = (GLsizei*)_env->GetIntArrayElements(
+ length_ref, (jboolean*)0);
+ _length = _length_base + lengthOffset;
}
- if (lengthOffset < 0) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "lengthOffset < 0";
- goto exit;
- }
- _lengthRemaining = _env->GetArrayLength(length_ref) - lengthOffset;
- _length_base = (GLsizei*)_env->GetIntArrayElements(
- length_ref, (jboolean*)0);
- _length = _length_base + lengthOffset;
if (!name_ref) {
_exception = 1;
@@ -3900,22 +3894,18 @@
jint _valuesRemaining;
GLint *values = (GLint *) 0;
- if (!length_ref) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "length == null";
- goto exit;
+ if (length_ref) {
+ if (lengthOffset < 0) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "lengthOffset < 0";
+ goto exit;
+ }
+ _lengthRemaining = _env->GetArrayLength(length_ref) - lengthOffset;
+ length_base = (GLsizei *)
+ _env->GetIntArrayElements(length_ref, (jboolean *)0);
+ length = length_base + lengthOffset;
}
- if (lengthOffset < 0) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "lengthOffset < 0";
- goto exit;
- }
- _lengthRemaining = _env->GetArrayLength(length_ref) - lengthOffset;
- length_base = (GLsizei *)
- _env->GetIntArrayElements(length_ref, (jboolean *)0);
- length = length_base + lengthOffset;
if (!values_ref) {
_exception = 1;
@@ -3969,9 +3959,11 @@
jint _valuesRemaining;
GLint *values = (GLint *) 0;
- length = (GLsizei *)getPointer(_env, length_buf, (jarray*)&_lengthArray, &_lengthRemaining, &_lengthBufferOffset);
+ if (length_buf) {
+ length = (GLsizei *)getPointer(_env, length_buf, (jarray*)&_lengthArray, &_lengthRemaining, &_lengthBufferOffset);
+ }
values = (GLint *)getPointer(_env, values_buf, (jarray*)&_valuesArray, &_valuesRemaining, &_valuesBufferOffset);
- if (length == NULL) {
+ if (length_buf && length == NULL) {
char * _lengthBase = (char *)_env->GetIntArrayElements(_lengthArray, (jboolean *) 0);
length = (GLsizei *) (_lengthBase + _lengthBufferOffset);
}
@@ -4772,22 +4764,18 @@
jint _binaryRemaining;
GLvoid *binary = (GLvoid *) 0;
- if (!length_ref) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "length == null";
- goto exit;
+ if (length_ref) {
+ if (lengthOffset < 0) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "lengthOffset < 0";
+ goto exit;
+ }
+ _lengthRemaining = _env->GetArrayLength(length_ref) - lengthOffset;
+ length_base = (GLsizei *)
+ _env->GetIntArrayElements(length_ref, (jboolean *)0);
+ length = length_base + lengthOffset;
}
- if (lengthOffset < 0) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "lengthOffset < 0";
- goto exit;
- }
- _lengthRemaining = _env->GetArrayLength(length_ref) - lengthOffset;
- length_base = (GLsizei *)
- _env->GetIntArrayElements(length_ref, (jboolean *)0);
- length = length_base + lengthOffset;
if (!binaryFormat_ref) {
_exception = 1;
@@ -4853,10 +4841,12 @@
jint _binaryRemaining;
GLvoid *binary = (GLvoid *) 0;
- length = (GLsizei *)getPointer(_env, length_buf, (jarray*)&_lengthArray, &_lengthRemaining, &_lengthBufferOffset);
+ if (length_buf) {
+ length = (GLsizei *)getPointer(_env, length_buf, (jarray*)&_lengthArray, &_lengthRemaining, &_lengthBufferOffset);
+ }
binaryFormat = (GLenum *)getPointer(_env, binaryFormat_buf, (jarray*)&_binaryFormatArray, &_binaryFormatRemaining, &_binaryFormatBufferOffset);
binary = (GLvoid *)getPointer(_env, binary_buf, (jarray*)&_binaryArray, &_binaryRemaining, &_binaryBufferOffset);
- if (length == NULL) {
+ if (length_buf && length == NULL) {
char * _lengthBase = (char *)_env->GetIntArrayElements(_lengthArray, (jboolean *) 0);
length = (GLsizei *) (_lengthBase + _lengthBufferOffset);
}
diff --git a/core/jni/android_opengl_GLES31.cpp b/core/jni/android_opengl_GLES31.cpp
index e427388..5751add 100644
--- a/core/jni/android_opengl_GLES31.cpp
+++ b/core/jni/android_opengl_GLES31.cpp
@@ -714,22 +714,18 @@
_env->GetIntArrayElements(props_ref, (jboolean *)0);
props = props_base + propsOffset;
- if (!length_ref) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "length == null";
- goto exit;
+ if (length_ref) {
+ if (lengthOffset < 0) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "lengthOffset < 0";
+ goto exit;
+ }
+ _lengthRemaining = _env->GetArrayLength(length_ref) - lengthOffset;
+ length_base = (GLsizei *)
+ _env->GetIntArrayElements(length_ref, (jboolean *)0);
+ length = length_base + lengthOffset;
}
- if (lengthOffset < 0) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "lengthOffset < 0";
- goto exit;
- }
- _lengthRemaining = _env->GetArrayLength(length_ref) - lengthOffset;
- length_base = (GLsizei *)
- _env->GetIntArrayElements(length_ref, (jboolean *)0);
- length = length_base + lengthOffset;
if (!params_ref) {
_exception = 1;
@@ -795,13 +791,15 @@
GLint *params = (GLint *) 0;
props = (GLenum *)getPointer(_env, props_buf, (jarray*)&_propsArray, &_propsRemaining, &_propsBufferOffset);
- length = (GLsizei *)getPointer(_env, length_buf, (jarray*)&_lengthArray, &_lengthRemaining, &_lengthBufferOffset);
+ if (length_buf) {
+ length = (GLsizei *)getPointer(_env, length_buf, (jarray*)&_lengthArray, &_lengthRemaining, &_lengthBufferOffset);
+ }
params = (GLint *)getPointer(_env, params_buf, (jarray*)&_paramsArray, &_paramsRemaining, &_paramsBufferOffset);
if (props == NULL) {
char * _propsBase = (char *)_env->GetIntArrayElements(_propsArray, (jboolean *) 0);
props = (GLenum *) (_propsBase + _propsBufferOffset);
}
- if (length == NULL) {
+ if (length_buf && length == NULL) {
char * _lengthBase = (char *)_env->GetIntArrayElements(_lengthArray, (jboolean *) 0);
length = (GLsizei *) (_lengthBase + _lengthBufferOffset);
}
diff --git a/core/jni/android_opengl_GLES31Ext.cpp b/core/jni/android_opengl_GLES31Ext.cpp
index 180c693..5be7be0 100644
--- a/core/jni/android_opengl_GLES31Ext.cpp
+++ b/core/jni/android_opengl_GLES31Ext.cpp
@@ -646,13 +646,9 @@
const char * _exceptionMessage = NULL;
const char* _nativelabel = 0;
- if (!label) {
- _exception = 1;
- _exceptionType = "java/lang/IllegalArgumentException";
- _exceptionMessage = "label == null";
- goto exit;
+ if (label) {
+ _nativelabel = _env->GetStringUTFChars(label, 0);
}
- _nativelabel = _env->GetStringUTFChars(label, 0);
glObjectLabelKHR(
(GLenum)identifier,
@@ -660,8 +656,6 @@
(GLsizei)length,
(GLchar *)_nativelabel
);
-
-exit:
if (_nativelabel) {
_env->ReleaseStringUTFChars(label, _nativelabel);
}
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 097bbac..1ee7ea8 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -294,7 +294,9 @@
// This is the regular Dalvik heap.
whichHeap = HEAP_DALVIK;
subHeap = HEAP_DALVIK_NORMAL;
- } else if (strstr(name, "/dev/ashmem/dalvik-large object space") == name) {
+ } else if (strstr(name, "/dev/ashmem/dalvik-large object space") == name ||
+ strstr(name, "/dev/ashmem/dalvik-free list large object space")
+ == name) {
whichHeap = HEAP_DALVIK;
subHeap = HEAP_DALVIK_LARGE;
} else if (strstr(name, "/dev/ashmem/dalvik-non moving space") == name) {
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index a14afa0..0a8ae2b 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -114,7 +114,7 @@
return parcel ? parcel->dataCapacity() : 0;
}
-static void android_os_Parcel_setDataSize(JNIEnv* env, jclass clazz, jlong nativePtr, jint size)
+static jlong android_os_Parcel_setDataSize(JNIEnv* env, jclass clazz, jlong nativePtr, jint size)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL) {
@@ -122,7 +122,9 @@
if (err != NO_ERROR) {
signalExceptionForError(env, clazz, err);
}
+ return parcel->getOpenAshmemSize();
}
+ return 0;
}
static void android_os_Parcel_setDataPosition(JNIEnv* env, jclass clazz, jlong nativePtr, jint pos)
@@ -304,7 +306,7 @@
}
}
-static void android_os_Parcel_writeFileDescriptor(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
+static jlong android_os_Parcel_writeFileDescriptor(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL) {
@@ -313,7 +315,9 @@
if (err != NO_ERROR) {
signalExceptionForError(env, clazz, err);
}
+ return parcel->getOpenAshmemSize();
}
+ return 0;
}
static jbyteArray android_os_Parcel_createByteArray(JNIEnv* env, jclass clazz, jlong nativePtr)
@@ -546,12 +550,14 @@
return reinterpret_cast<jlong>(parcel);
}
-static void android_os_Parcel_freeBuffer(JNIEnv* env, jclass clazz, jlong nativePtr)
+static jlong android_os_Parcel_freeBuffer(JNIEnv* env, jclass clazz, jlong nativePtr)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL) {
parcel->freeData();
+ return parcel->getOpenAshmemSize();
}
+ return 0;
}
static void android_os_Parcel_destroy(JNIEnv* env, jclass clazz, jlong nativePtr)
@@ -589,12 +595,12 @@
return ret;
}
-static void android_os_Parcel_unmarshall(JNIEnv* env, jclass clazz, jlong nativePtr,
- jbyteArray data, jint offset, jint length)
+static jlong android_os_Parcel_unmarshall(JNIEnv* env, jclass clazz, jlong nativePtr,
+ jbyteArray data, jint offset, jint length)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel == NULL || length < 0) {
- return;
+ return 0;
}
jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(data, 0);
@@ -608,24 +614,26 @@
env->ReleasePrimitiveArrayCritical(data, array, 0);
}
+ return parcel->getOpenAshmemSize();
}
-static void android_os_Parcel_appendFrom(JNIEnv* env, jclass clazz, jlong thisNativePtr,
- jlong otherNativePtr, jint offset, jint length)
+static jlong android_os_Parcel_appendFrom(JNIEnv* env, jclass clazz, jlong thisNativePtr,
+ jlong otherNativePtr, jint offset, jint length)
{
Parcel* thisParcel = reinterpret_cast<Parcel*>(thisNativePtr);
if (thisParcel == NULL) {
- return;
+ return 0;
}
Parcel* otherParcel = reinterpret_cast<Parcel*>(otherNativePtr);
if (otherParcel == NULL) {
- return;
+ return thisParcel->getOpenAshmemSize();
}
status_t err = thisParcel->appendFrom(otherParcel, offset, length);
if (err != NO_ERROR) {
signalExceptionForError(env, clazz, err);
}
+ return thisParcel->getOpenAshmemSize();
}
static jboolean android_os_Parcel_hasFileDescriptors(JNIEnv* env, jclass clazz, jlong nativePtr)
@@ -714,33 +722,33 @@
// ----------------------------------------------------------------------------
static const JNINativeMethod gParcelMethods[] = {
- {"nativeDataSize", "(J)I", (void*)android_os_Parcel_dataSize},
- {"nativeDataAvail", "(J)I", (void*)android_os_Parcel_dataAvail},
- {"nativeDataPosition", "(J)I", (void*)android_os_Parcel_dataPosition},
- {"nativeDataCapacity", "(J)I", (void*)android_os_Parcel_dataCapacity},
- {"nativeSetDataSize", "(JI)V", (void*)android_os_Parcel_setDataSize},
- {"nativeSetDataPosition", "(JI)V", (void*)android_os_Parcel_setDataPosition},
- {"nativeSetDataCapacity", "(JI)V", (void*)android_os_Parcel_setDataCapacity},
+ {"nativeDataSize", "!(J)I", (void*)android_os_Parcel_dataSize},
+ {"nativeDataAvail", "!(J)I", (void*)android_os_Parcel_dataAvail},
+ {"nativeDataPosition", "!(J)I", (void*)android_os_Parcel_dataPosition},
+ {"nativeDataCapacity", "!(J)I", (void*)android_os_Parcel_dataCapacity},
+ {"nativeSetDataSize", "!(JI)J", (void*)android_os_Parcel_setDataSize},
+ {"nativeSetDataPosition", "!(JI)V", (void*)android_os_Parcel_setDataPosition},
+ {"nativeSetDataCapacity", "!(JI)V", (void*)android_os_Parcel_setDataCapacity},
- {"nativePushAllowFds", "(JZ)Z", (void*)android_os_Parcel_pushAllowFds},
- {"nativeRestoreAllowFds", "(JZ)V", (void*)android_os_Parcel_restoreAllowFds},
+ {"nativePushAllowFds", "!(JZ)Z", (void*)android_os_Parcel_pushAllowFds},
+ {"nativeRestoreAllowFds", "!(JZ)V", (void*)android_os_Parcel_restoreAllowFds},
{"nativeWriteByteArray", "(J[BII)V", (void*)android_os_Parcel_writeNative},
{"nativeWriteBlob", "(J[BII)V", (void*)android_os_Parcel_writeBlob},
- {"nativeWriteInt", "(JI)V", (void*)android_os_Parcel_writeInt},
- {"nativeWriteLong", "(JJ)V", (void*)android_os_Parcel_writeLong},
- {"nativeWriteFloat", "(JF)V", (void*)android_os_Parcel_writeFloat},
- {"nativeWriteDouble", "(JD)V", (void*)android_os_Parcel_writeDouble},
+ {"nativeWriteInt", "!(JI)V", (void*)android_os_Parcel_writeInt},
+ {"nativeWriteLong", "!(JJ)V", (void*)android_os_Parcel_writeLong},
+ {"nativeWriteFloat", "!(JF)V", (void*)android_os_Parcel_writeFloat},
+ {"nativeWriteDouble", "!(JD)V", (void*)android_os_Parcel_writeDouble},
{"nativeWriteString", "(JLjava/lang/String;)V", (void*)android_os_Parcel_writeString},
{"nativeWriteStrongBinder", "(JLandroid/os/IBinder;)V", (void*)android_os_Parcel_writeStrongBinder},
- {"nativeWriteFileDescriptor", "(JLjava/io/FileDescriptor;)V", (void*)android_os_Parcel_writeFileDescriptor},
+ {"nativeWriteFileDescriptor", "(JLjava/io/FileDescriptor;)J", (void*)android_os_Parcel_writeFileDescriptor},
{"nativeCreateByteArray", "(J)[B", (void*)android_os_Parcel_createByteArray},
{"nativeReadBlob", "(J)[B", (void*)android_os_Parcel_readBlob},
- {"nativeReadInt", "(J)I", (void*)android_os_Parcel_readInt},
- {"nativeReadLong", "(J)J", (void*)android_os_Parcel_readLong},
- {"nativeReadFloat", "(J)F", (void*)android_os_Parcel_readFloat},
- {"nativeReadDouble", "(J)D", (void*)android_os_Parcel_readDouble},
+ {"nativeReadInt", "!(J)I", (void*)android_os_Parcel_readInt},
+ {"nativeReadLong", "!(J)J", (void*)android_os_Parcel_readLong},
+ {"nativeReadFloat", "!(J)F", (void*)android_os_Parcel_readFloat},
+ {"nativeReadDouble", "!(J)D", (void*)android_os_Parcel_readDouble},
{"nativeReadString", "(J)Ljava/lang/String;", (void*)android_os_Parcel_readString},
{"nativeReadStrongBinder", "(J)Landroid/os/IBinder;", (void*)android_os_Parcel_readStrongBinder},
{"nativeReadFileDescriptor", "(J)Ljava/io/FileDescriptor;", (void*)android_os_Parcel_readFileDescriptor},
@@ -751,13 +759,13 @@
{"clearFileDescriptor", "(Ljava/io/FileDescriptor;)V", (void*)android_os_Parcel_clearFileDescriptor},
{"nativeCreate", "()J", (void*)android_os_Parcel_create},
- {"nativeFreeBuffer", "(J)V", (void*)android_os_Parcel_freeBuffer},
+ {"nativeFreeBuffer", "(J)J", (void*)android_os_Parcel_freeBuffer},
{"nativeDestroy", "(J)V", (void*)android_os_Parcel_destroy},
{"nativeMarshall", "(J)[B", (void*)android_os_Parcel_marshall},
- {"nativeUnmarshall", "(J[BII)V", (void*)android_os_Parcel_unmarshall},
- {"nativeAppendFrom", "(JJII)V", (void*)android_os_Parcel_appendFrom},
- {"nativeHasFileDescriptors", "(J)Z", (void*)android_os_Parcel_hasFileDescriptors},
+ {"nativeUnmarshall", "(J[BII)J", (void*)android_os_Parcel_unmarshall},
+ {"nativeAppendFrom", "(JJII)J", (void*)android_os_Parcel_appendFrom},
+ {"nativeHasFileDescriptors", "!(J)Z", (void*)android_os_Parcel_hasFileDescriptors},
{"nativeWriteInterfaceToken", "(JLjava/lang/String;)V", (void*)android_os_Parcel_writeInterfaceToken},
{"nativeEnforceInterface", "(JLjava/lang/String;)V", (void*)android_os_Parcel_enforceInterface},
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index c94bc64..90606a35 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -578,22 +578,6 @@
return am->isUpToDate() ? JNI_TRUE : JNI_FALSE;
}
-static void android_content_AssetManager_setLocale(JNIEnv* env, jobject clazz,
- jstring locale)
-{
- ScopedUtfChars locale8(env, locale);
- if (locale8.c_str() == NULL) {
- return;
- }
-
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return;
- }
-
- am->setLocale(locale8.c_str());
-}
-
static jobjectArray android_content_AssetManager_getLocales(JNIEnv* env, jobject clazz)
{
Vector<String8> locales;
@@ -2156,9 +2140,9 @@
(void*) android_content_AssetManager_readAsset },
{ "seekAsset", "(JJI)J",
(void*) android_content_AssetManager_seekAsset },
- { "getAssetLength", "(J)J",
+ { "getAssetLength", "!(J)J",
(void*) android_content_AssetManager_getAssetLength },
- { "getAssetRemainingLength", "(J)J",
+ { "getAssetRemainingLength", "!(J)J",
(void*) android_content_AssetManager_getAssetRemainingLength },
{ "addAssetPathNative", "(Ljava/lang/String;Z)I",
(void*) android_content_AssetManager_addAssetPath },
@@ -2168,31 +2152,29 @@
(void*) android_content_AssetManager_isUpToDate },
// Resources.
- { "setLocale", "(Ljava/lang/String;)V",
- (void*) android_content_AssetManager_setLocale },
{ "getLocales", "()[Ljava/lang/String;",
(void*) android_content_AssetManager_getLocales },
{ "getSizeConfigurations", "()[Landroid/content/res/Configuration;",
(void*) android_content_AssetManager_getSizeConfigurations },
- { "setConfiguration", "(IILjava/lang/String;IIIIIIIIIIIIII)V",
+ { "setConfiguration", "!(IILjava/lang/String;IIIIIIIIIIIIII)V",
(void*) android_content_AssetManager_setConfiguration },
- { "getResourceIdentifier","(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
+ { "getResourceIdentifier","!(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
(void*) android_content_AssetManager_getResourceIdentifier },
- { "getResourceName","(I)Ljava/lang/String;",
+ { "getResourceName","!(I)Ljava/lang/String;",
(void*) android_content_AssetManager_getResourceName },
- { "getResourcePackageName","(I)Ljava/lang/String;",
+ { "getResourcePackageName","!(I)Ljava/lang/String;",
(void*) android_content_AssetManager_getResourcePackageName },
- { "getResourceTypeName","(I)Ljava/lang/String;",
+ { "getResourceTypeName","!(I)Ljava/lang/String;",
(void*) android_content_AssetManager_getResourceTypeName },
- { "getResourceEntryName","(I)Ljava/lang/String;",
+ { "getResourceEntryName","!(I)Ljava/lang/String;",
(void*) android_content_AssetManager_getResourceEntryName },
- { "loadResourceValue","(ISLandroid/util/TypedValue;Z)I",
+ { "loadResourceValue","!(ISLandroid/util/TypedValue;Z)I",
(void*) android_content_AssetManager_loadResourceValue },
- { "loadResourceBagValue","(IILandroid/util/TypedValue;Z)I",
+ { "loadResourceBagValue","!(IILandroid/util/TypedValue;Z)I",
(void*) android_content_AssetManager_loadResourceBagValue },
- { "getStringBlockCount","()I",
+ { "getStringBlockCount","!()I",
(void*) android_content_AssetManager_getStringBlockCount },
- { "getNativeStringBlock","(I)J",
+ { "getNativeStringBlock","!(I)J",
(void*) android_content_AssetManager_getNativeStringBlock },
{ "getCookieName","(I)Ljava/lang/String;",
(void*) android_content_AssetManager_getCookieName },
@@ -2210,21 +2192,21 @@
(void*) android_content_AssetManager_copyTheme },
{ "clearTheme", "(J)V",
(void*) android_content_AssetManager_clearTheme },
- { "loadThemeAttributeValue", "(JILandroid/util/TypedValue;Z)I",
+ { "loadThemeAttributeValue", "!(JILandroid/util/TypedValue;Z)I",
(void*) android_content_AssetManager_loadThemeAttributeValue },
- { "getThemeChangingConfigurations", "(J)I",
+ { "getThemeChangingConfigurations", "!(J)I",
(void*) android_content_AssetManager_getThemeChangingConfigurations },
{ "dumpTheme", "(JILjava/lang/String;Ljava/lang/String;)V",
(void*) android_content_AssetManager_dumpTheme },
- { "applyStyle","(JIIJ[I[I[I)Z",
+ { "applyStyle","!(JIIJ[I[I[I)Z",
(void*) android_content_AssetManager_applyStyle },
- { "resolveAttrs","(JII[I[I[I[I)Z",
+ { "resolveAttrs","!(JII[I[I[I[I)Z",
(void*) android_content_AssetManager_resolveAttrs },
- { "retrieveAttributes","(J[I[I[I)Z",
+ { "retrieveAttributes","!(J[I[I[I)Z",
(void*) android_content_AssetManager_retrieveAttributes },
- { "getArraySize","(I)I",
+ { "getArraySize","!(I)I",
(void*) android_content_AssetManager_getArraySize },
- { "retrieveArray","(I[I)I",
+ { "retrieveArray","!(I[I)I",
(void*) android_content_AssetManager_retrieveArray },
// XML files.
@@ -2234,11 +2216,11 @@
// Arrays.
{ "getArrayStringResource","(I)[Ljava/lang/String;",
(void*) android_content_AssetManager_getArrayStringResource },
- { "getArrayStringInfo","(I)[I",
+ { "getArrayStringInfo","!(I)[I",
(void*) android_content_AssetManager_getArrayStringInfo },
- { "getArrayIntResource","(I)[I",
+ { "getArrayIntResource","!(I)[I",
(void*) android_content_AssetManager_getArrayIntResource },
- { "getStyleAttributes","(I)[I",
+ { "getStyleAttributes","!(I)[I",
(void*) android_content_AssetManager_getStyleAttributes },
// Bookkeeping.
diff --git a/core/jni/android_util_PathParser.cpp b/core/jni/android_util_PathParser.cpp
new file mode 100644
index 0000000..bf6ffc54
--- /dev/null
+++ b/core/jni/android_util_PathParser.cpp
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+#include "jni.h"
+
+#include <PathParser.h>
+#include <SkPath.h>
+
+#include "core_jni_helpers.h"
+
+namespace android {
+
+static bool parseStringForPath(JNIEnv* env, jobject, jlong skPathHandle, jstring inputPathStr,
+ jint strLength) {
+ const char* pathString = env->GetStringUTFChars(inputPathStr, NULL);
+ SkPath* skPath = reinterpret_cast<SkPath*>(skPathHandle);
+ bool hasValidData = android::uirenderer::PathParser::parseStringForSkPath(skPath, pathString,
+ strLength);
+ env->ReleaseStringUTFChars(inputPathStr, pathString);
+ return hasValidData;
+}
+
+static const JNINativeMethod gMethods[] = {
+ {"nParseStringForPath", "(JLjava/lang/String;I)Z", (void*)parseStringForPath}
+};
+
+int register_android_util_PathParser(JNIEnv* env) {
+ return RegisterMethodsOrDie(env, "android/util/PathParser", gMethods, NELEM(gMethods));
+}
+};
diff --git a/core/jni/android_util_XmlBlock.cpp b/core/jni/android_util_XmlBlock.cpp
index 7ae51c8..a15c23c 100644
--- a/core/jni/android_util_XmlBlock.cpp
+++ b/core/jni/android_util_XmlBlock.cpp
@@ -372,37 +372,37 @@
(void*) android_content_XmlBlock_nativeGetStringBlock },
{ "nativeCreateParseState", "(J)J",
(void*) android_content_XmlBlock_nativeCreateParseState },
- { "nativeNext", "(J)I",
+ { "nativeNext", "!(J)I",
(void*) android_content_XmlBlock_nativeNext },
- { "nativeGetNamespace", "(J)I",
+ { "nativeGetNamespace", "!(J)I",
(void*) android_content_XmlBlock_nativeGetNamespace },
- { "nativeGetName", "(J)I",
+ { "nativeGetName", "!(J)I",
(void*) android_content_XmlBlock_nativeGetName },
- { "nativeGetText", "(J)I",
+ { "nativeGetText", "!(J)I",
(void*) android_content_XmlBlock_nativeGetText },
- { "nativeGetLineNumber", "(J)I",
+ { "nativeGetLineNumber", "!(J)I",
(void*) android_content_XmlBlock_nativeGetLineNumber },
- { "nativeGetAttributeCount", "(J)I",
+ { "nativeGetAttributeCount", "!(J)I",
(void*) android_content_XmlBlock_nativeGetAttributeCount },
- { "nativeGetAttributeNamespace","(JI)I",
+ { "nativeGetAttributeNamespace","!(JI)I",
(void*) android_content_XmlBlock_nativeGetAttributeNamespace },
- { "nativeGetAttributeName", "(JI)I",
+ { "nativeGetAttributeName", "!(JI)I",
(void*) android_content_XmlBlock_nativeGetAttributeName },
- { "nativeGetAttributeResource", "(JI)I",
+ { "nativeGetAttributeResource", "!(JI)I",
(void*) android_content_XmlBlock_nativeGetAttributeResource },
- { "nativeGetAttributeDataType", "(JI)I",
+ { "nativeGetAttributeDataType", "!(JI)I",
(void*) android_content_XmlBlock_nativeGetAttributeDataType },
- { "nativeGetAttributeData", "(JI)I",
+ { "nativeGetAttributeData", "!(JI)I",
(void*) android_content_XmlBlock_nativeGetAttributeData },
- { "nativeGetAttributeStringValue", "(JI)I",
+ { "nativeGetAttributeStringValue", "!(JI)I",
(void*) android_content_XmlBlock_nativeGetAttributeStringValue },
- { "nativeGetAttributeIndex", "(JLjava/lang/String;Ljava/lang/String;)I",
+ { "nativeGetAttributeIndex", "!(JLjava/lang/String;Ljava/lang/String;)I",
(void*) android_content_XmlBlock_nativeGetAttributeIndex },
- { "nativeGetIdAttribute", "(J)I",
+ { "nativeGetIdAttribute", "!(J)I",
(void*) android_content_XmlBlock_nativeGetIdAttribute },
- { "nativeGetClassAttribute", "(J)I",
+ { "nativeGetClassAttribute", "!(J)I",
(void*) android_content_XmlBlock_nativeGetClassAttribute },
- { "nativeGetStyleAttribute", "(J)I",
+ { "nativeGetStyleAttribute", "!(J)I",
(void*) android_content_XmlBlock_nativeGetStyleAttribute },
{ "nativeDestroyParseState", "(J)V",
(void*) android_content_XmlBlock_nativeDestroyParseState },
diff --git a/core/jni/android_view_DisplayListCanvas.cpp b/core/jni/android_view_DisplayListCanvas.cpp
index b64acc3..9b41eb3 100644
--- a/core/jni/android_view_DisplayListCanvas.cpp
+++ b/core/jni/android_view_DisplayListCanvas.cpp
@@ -151,10 +151,10 @@
// not in the emulator
return JNI_TRUE;
}
- // In the emulator this property will be set to 1 when hardware GLES is
+ // In the emulator this property will be set > 0 when OpenGL ES 2.0 is
// enabled, 0 otherwise. On old emulator versions it will be undefined.
property_get("ro.kernel.qemu.gles", prop, "0");
- return atoi(prop) == 1 ? JNI_TRUE : JNI_FALSE;
+ return atoi(prop) > 0 ? JNI_TRUE : JNI_FALSE;
}
// ----------------------------------------------------------------------------
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 81d46c3..0245d38 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -768,94 +768,94 @@
"(JJ[Landroid/view/MotionEvent$PointerCoords;I)V",
(void*)android_view_MotionEvent_nativeAddBatch },
{ "nativeGetDeviceId",
- "(J)I",
+ "!(J)I",
(void*)android_view_MotionEvent_nativeGetDeviceId },
{ "nativeGetSource",
- "(J)I",
+ "!(J)I",
(void*)android_view_MotionEvent_nativeGetSource },
{ "nativeSetSource",
- "(JI)I",
+ "!(JI)I",
(void*)android_view_MotionEvent_nativeSetSource },
{ "nativeGetAction",
- "(J)I",
+ "!(J)I",
(void*)android_view_MotionEvent_nativeGetAction },
{ "nativeSetAction",
- "(JI)V",
+ "!(JI)V",
(void*)android_view_MotionEvent_nativeSetAction },
{ "nativeGetActionButton",
- "(J)I",
+ "!(J)I",
(void*)android_view_MotionEvent_nativeGetActionButton},
{ "nativeSetActionButton",
- "(JI)V",
+ "!(JI)V",
(void*)android_view_MotionEvent_nativeSetActionButton},
{ "nativeIsTouchEvent",
- "(J)Z",
+ "!(J)Z",
(void*)android_view_MotionEvent_nativeIsTouchEvent },
{ "nativeGetFlags",
- "(J)I",
+ "!(J)I",
(void*)android_view_MotionEvent_nativeGetFlags },
{ "nativeSetFlags",
- "(JI)V",
+ "!(JI)V",
(void*)android_view_MotionEvent_nativeSetFlags },
{ "nativeGetEdgeFlags",
- "(J)I",
+ "!(J)I",
(void*)android_view_MotionEvent_nativeGetEdgeFlags },
{ "nativeSetEdgeFlags",
- "(JI)V",
+ "!(JI)V",
(void*)android_view_MotionEvent_nativeSetEdgeFlags },
{ "nativeGetMetaState",
- "(J)I",
+ "!(J)I",
(void*)android_view_MotionEvent_nativeGetMetaState },
{ "nativeGetButtonState",
- "(J)I",
+ "!(J)I",
(void*)android_view_MotionEvent_nativeGetButtonState },
{ "nativeSetButtonState",
- "(JI)V",
+ "!(JI)V",
(void*)android_view_MotionEvent_nativeSetButtonState },
{ "nativeOffsetLocation",
- "(JFF)V",
+ "!(JFF)V",
(void*)android_view_MotionEvent_nativeOffsetLocation },
{ "nativeGetXOffset",
- "(J)F",
+ "!(J)F",
(void*)android_view_MotionEvent_nativeGetXOffset },
{ "nativeGetYOffset",
- "(J)F",
+ "!(J)F",
(void*)android_view_MotionEvent_nativeGetYOffset },
{ "nativeGetXPrecision",
- "(J)F",
+ "!(J)F",
(void*)android_view_MotionEvent_nativeGetXPrecision },
{ "nativeGetYPrecision",
- "(J)F",
+ "!(J)F",
(void*)android_view_MotionEvent_nativeGetYPrecision },
{ "nativeGetDownTimeNanos",
- "(J)J",
+ "!(J)J",
(void*)android_view_MotionEvent_nativeGetDownTimeNanos },
{ "nativeSetDownTimeNanos",
- "(JJ)V",
+ "!(JJ)V",
(void*)android_view_MotionEvent_nativeSetDownTimeNanos },
{ "nativeGetPointerCount",
- "(J)I",
+ "!(J)I",
(void*)android_view_MotionEvent_nativeGetPointerCount },
{ "nativeGetPointerId",
- "(JI)I",
+ "!(JI)I",
(void*)android_view_MotionEvent_nativeGetPointerId },
{ "nativeGetToolType",
- "(JI)I",
+ "!(JI)I",
(void*)android_view_MotionEvent_nativeGetToolType },
{ "nativeFindPointerIndex",
- "(JI)I",
+ "!(JI)I",
(void*)android_view_MotionEvent_nativeFindPointerIndex },
{ "nativeGetHistorySize",
- "(J)I",
+ "!(J)I",
(void*)android_view_MotionEvent_nativeGetHistorySize },
{ "nativeGetEventTimeNanos",
- "(JI)J",
+ "!(JI)J",
(void*)android_view_MotionEvent_nativeGetEventTimeNanos },
{ "nativeGetRawAxisValue",
- "(JIII)F",
+ "!(JIII)F",
(void*)android_view_MotionEvent_nativeGetRawAxisValue },
{ "nativeGetAxisValue",
- "(JIII)F",
+ "!(JIII)F",
(void*)android_view_MotionEvent_nativeGetAxisValue },
{ "nativeGetPointerCoords",
"(JIILandroid/view/MotionEvent$PointerCoords;)V",
@@ -864,7 +864,7 @@
"(JILandroid/view/MotionEvent$PointerProperties;)V",
(void*)android_view_MotionEvent_nativeGetPointerProperties },
{ "nativeScale",
- "(JF)V",
+ "!(JF)V",
(void*)android_view_MotionEvent_nativeScale },
{ "nativeTransform",
"(JLandroid/graphics/Matrix;)V",
diff --git a/core/jni/android_view_PointerIcon.h b/core/jni/android_view_PointerIcon.h
index 3bfd645..86f288d 100644
--- a/core/jni/android_view_PointerIcon.h
+++ b/core/jni/android_view_PointerIcon.h
@@ -31,6 +31,27 @@
POINTER_ICON_STYLE_CUSTOM = -1,
POINTER_ICON_STYLE_NULL = 0,
POINTER_ICON_STYLE_ARROW = 1000,
+ POINTER_ICON_STYLE_CONTEXT_MENU = 1001,
+ POINTER_ICON_STYLE_HAND = 1002,
+ POINTER_ICON_STYLE_HELP = 1003,
+ POINTER_ICON_STYLE_WAIT = 1004,
+ POINTER_ICON_STYLE_CELL = 1006,
+ POINTER_ICON_STYLE_CROSSHAIR = 1007,
+ POINTER_ICON_STYLE_TEXT = 1008,
+ POINTER_ICON_STYLE_VERTICAL_TEXT = 1009,
+ POINTER_ICON_STYLE_ALIAS = 1010,
+ POINTER_ICON_STYLE_COPY = 1011,
+ POINTER_ICON_STYLE_NO_DROP = 1012,
+ POINTER_ICON_STYLE_ALL_SCROLL = 1013,
+ POINTER_ICON_STYLE_HORIZONTAL_DOUBLE_ARROW = 1014,
+ POINTER_ICON_STYLE_VERTICAL_DOUBLE_ARROW = 1015,
+ POINTER_ICON_STYLE_TOP_RIGHT_DOUBLE_ARROW = 1016,
+ POINTER_ICON_STYLE_TOP_LEFT_DOUBLE_ARROW = 1017,
+ POINTER_ICON_STYLE_ZOOM_IN = 1018,
+ POINTER_ICON_STYLE_ZOOM_OUT = 1019,
+ POINTER_ICON_STYLE_GRAB = 1020,
+ POINTER_ICON_STYLE_GRABBING = 1021,
+
POINTER_ICON_STYLE_SPOT_HOVER = 2000,
POINTER_ICON_STYLE_SPOT_TOUCH = 2001,
POINTER_ICON_STYLE_SPOT_ANCHOR = 2002,
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 388d365..b1d4e26 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -71,10 +71,10 @@
renderNode->decStrong(0);
}
-static void android_view_RenderNode_setDisplayListData(JNIEnv* env,
- jobject clazz, jlong renderNodePtr, jlong newDataPtr) {
+static void android_view_RenderNode_setDisplayList(JNIEnv* env,
+ jobject clazz, jlong renderNodePtr, jlong displayListPtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
- DisplayListData* newData = reinterpret_cast<DisplayListData*>(newDataPtr);
+ DisplayList* newData = reinterpret_cast<DisplayList*>(displayListPtr);
renderNode->setStagingDisplayList(newData);
}
@@ -468,11 +468,11 @@
const char* const kClassPathName = "android/view/RenderNode";
static const JNINativeMethod gMethods[] = {
- { "nCreate", "(Ljava/lang/String;)J", (void*) android_view_RenderNode_create },
- { "nDestroyRenderNode", "(J)V", (void*) android_view_RenderNode_destroyRenderNode },
- { "nSetDisplayListData", "(JJ)V", (void*) android_view_RenderNode_setDisplayListData },
- { "nOutput", "(J)V", (void*) android_view_RenderNode_output },
- { "nGetDebugSize", "(J)I", (void*) android_view_RenderNode_getDebugSize },
+ { "nCreate", "(Ljava/lang/String;)J", (void*) android_view_RenderNode_create },
+ { "nDestroyRenderNode", "(J)V", (void*) android_view_RenderNode_destroyRenderNode },
+ { "nSetDisplayList", "(JJ)V", (void*) android_view_RenderNode_setDisplayList },
+ { "nOutput", "(J)V", (void*) android_view_RenderNode_output },
+ { "nGetDebugSize", "(J)I", (void*) android_view_RenderNode_getDebugSize },
{ "nSetLayerType", "!(JI)Z", (void*) android_view_RenderNode_setLayerType },
{ "nSetLayerPaint", "!(JJ)Z", (void*) android_view_RenderNode_setLayerPaint },
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 24055e7..ff51e4e 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -313,12 +313,11 @@
return 0;
}
+
SkImageInfo info = SkImageInfo::Make(outBuffer.width, outBuffer.height,
convertPixelFormat(outBuffer.format),
- kPremul_SkAlphaType);
- if (outBuffer.format == PIXEL_FORMAT_RGBX_8888) {
- info.fAlphaType = kOpaque_SkAlphaType;
- }
+ outBuffer.format == PIXEL_FORMAT_RGBX_8888 ?
+ kOpaque_SkAlphaType : kPremul_SkAlphaType);
SkBitmap bitmap;
ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
@@ -461,6 +460,10 @@
anw->query(anw, NATIVE_WINDOW_HEIGHT, &value);
return value;
}
+static jlong nativeGetNextFrameNumber(JNIEnv *env, jclass clazz, jlong nativeObject) {
+ Surface* surface = reinterpret_cast<Surface*>(nativeObject);
+ return surface->getNextFrameNumber();
+}
namespace uirenderer {
@@ -537,6 +540,7 @@
(void*)nativeWriteToParcel },
{"nativeGetWidth", "(J)I", (void*)nativeGetWidth },
{"nativeGetHeight", "(J)I", (void*)nativeGetHeight },
+ {"nativeGetNextFrameNumber", "(J)J", (void*)nativeGetNextFrameNumber },
// HWUI context
{"nHwuiCreate", "(JJ)J", (void*) hwui::create },
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 65ebb663..1dfe40a 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -138,35 +138,36 @@
return NULL;
}
- SkImageInfo screenshotInfo;
- screenshotInfo.fWidth = screenshot->getWidth();
- screenshotInfo.fHeight = screenshot->getHeight();
-
+ SkColorType colorType;
+ SkAlphaType alphaType;
switch (screenshot->getFormat()) {
case PIXEL_FORMAT_RGBX_8888: {
- screenshotInfo.fColorType = kRGBA_8888_SkColorType;
- screenshotInfo.fAlphaType = kOpaque_SkAlphaType;
+ colorType = kRGBA_8888_SkColorType;
+ alphaType = kOpaque_SkAlphaType;
break;
}
case PIXEL_FORMAT_RGBA_8888: {
- screenshotInfo.fColorType = kRGBA_8888_SkColorType;
- screenshotInfo.fAlphaType = kPremul_SkAlphaType;
+ colorType = kRGBA_8888_SkColorType;
+ alphaType = kPremul_SkAlphaType;
break;
}
case PIXEL_FORMAT_RGB_565: {
- screenshotInfo.fColorType = kRGB_565_SkColorType;
- screenshotInfo.fAlphaType = kOpaque_SkAlphaType;
+ colorType = kRGB_565_SkColorType;
+ alphaType = kOpaque_SkAlphaType;
break;
}
default: {
return NULL;
}
}
+ SkImageInfo screenshotInfo = SkImageInfo::Make(screenshot->getWidth(),
+ screenshot->getHeight(),
+ colorType, alphaType);
const size_t rowBytes =
screenshot->getStride() * android::bytesPerPixel(screenshot->getFormat());
- if (!screenshotInfo.fWidth || !screenshotInfo.fHeight) {
+ if (!screenshotInfo.width() || !screenshotInfo.height()) {
return NULL;
}
@@ -570,6 +571,21 @@
return JNI_TRUE;
}
+
+static void nativeDeferTransactionUntil(JNIEnv* env, jclass clazz, jlong nativeObject,
+ jobject handleObject, jlong frameNumber) {
+ auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
+ sp<IBinder> handle = ibinderForJavaObject(env, handleObject);
+
+ ctrl->deferTransactionUntil(handle, frameNumber);
+}
+
+static jobject nativeGetHandle(JNIEnv* env, jclass clazz, jlong nativeObject) {
+ auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
+
+ return javaObjectForIBinder(env, ctrl->getHandle());
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod sSurfaceControlMethods[] = {
@@ -637,6 +653,10 @@
(void*)nativeGetAnimationFrameStats },
{"nativeSetDisplayPowerMode", "(Landroid/os/IBinder;I)V",
(void*)nativeSetDisplayPowerMode },
+ {"nativeDeferTransactionUntil", "(JLandroid/os/IBinder;J)V",
+ (void*)nativeDeferTransactionUntil },
+ {"nativeGetHandle", "(J)Landroid/os/IBinder;",
+ (void*)nativeGetHandle }
};
int register_android_view_SurfaceControl(JNIEnv* env)
diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp
index b736a17..e185281 100644
--- a/core/jni/android_view_TextureView.cpp
+++ b/core/jni/android_view_TextureView.cpp
@@ -72,29 +72,25 @@
// FIXME: consider exporting this to share (e.g. android_view_Surface.cpp)
static inline SkImageInfo convertPixelFormat(const ANativeWindow_Buffer& buffer) {
- SkImageInfo info;
- info.fWidth = buffer.width;
- info.fHeight = buffer.height;
+ SkColorType colorType = kUnknown_SkColorType;
+ SkAlphaType alphaType = kOpaque_SkAlphaType;
switch (buffer.format) {
case WINDOW_FORMAT_RGBA_8888:
- info.fColorType = kN32_SkColorType;
- info.fAlphaType = kPremul_SkAlphaType;
+ colorType = kN32_SkColorType;
+ alphaType = kPremul_SkAlphaType;
break;
case WINDOW_FORMAT_RGBX_8888:
- info.fColorType = kN32_SkColorType;
- info.fAlphaType = kOpaque_SkAlphaType;
+ colorType = kN32_SkColorType;
+ alphaType = kOpaque_SkAlphaType;
break;
case WINDOW_FORMAT_RGB_565:
- info.fColorType = kRGB_565_SkColorType;
- info.fAlphaType = kOpaque_SkAlphaType;
+ colorType = kRGB_565_SkColorType;
+ alphaType = kOpaque_SkAlphaType;
break;
default:
- info.fColorType = kUnknown_SkColorType;
- // switch to kUnknown_SkAlphaType when its in skia
- info.fAlphaType = kOpaque_SkAlphaType;
break;
}
- return info;
+ return SkImageInfo::Make(buffer.width, buffer.height, colorType, alphaType);
}
/**
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 148e7c1..6bdf71b 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -241,7 +241,6 @@
<protected-broadcast android:name="android.intent.action.DREAMING_STARTED" />
<protected-broadcast android:name="android.intent.action.DREAMING_STOPPED" />
<protected-broadcast android:name="android.intent.action.ANY_DATA_STATE" />
- <protected-broadcast android:name="android.intent.action.DATA_CONNECTION_CONNECTED_TO_PROVISIONING_APN" />
<protected-broadcast android:name="com.android.server.WifiManager.action.START_SCAN" />
<protected-broadcast android:name="com.android.server.WifiManager.action.START_PNO" />
@@ -1477,7 +1476,7 @@
<permission android:name="android.permission.SYSTEM_ALERT_WINDOW"
android:label="@string/permlab_systemAlertWindow"
android:description="@string/permdesc_systemAlertWindow"
- android:protectionLevel="signature|preinstalled|appop|pre23" />
+ android:protectionLevel="signature|preinstalled|appop|pre23|development" />
<!-- ================================== -->
<!-- Permissions affecting the system wallpaper -->
@@ -1684,6 +1683,10 @@
<permission android:name="android.permission.MOUNT_FORMAT_FILESYSTEMS"
android:protectionLevel="system|signature" />
+ <!-- @hide -->
+ <permission android:name="android.permission.STORAGE_INTERNAL"
+ android:protectionLevel="signature" />
+
<!-- Allows access to ASEC non-destructive API calls
@hide -->
<permission android:name="android.permission.ASEC_ACCESS"
@@ -1715,12 +1718,12 @@
android:protectionLevel="signature|privileged" />
<!-- Allows applications to change network connectivity state.
- <p>Protection level: signature
+ <p>Protection level: normal
-->
<permission android:name="android.permission.CHANGE_NETWORK_STATE"
android:description="@string/permdesc_changeNetworkState"
android:label="@string/permlab_changeNetworkState"
- android:protectionLevel="signature|preinstalled|appop|pre23" />
+ android:protectionLevel="normal" />
<!-- Allows an application to clear the caches of all installed
applications on the device.
@@ -2075,10 +2078,11 @@
<permission android:name="android.permission.SET_KEYBOARD_LAYOUT"
android:protectionLevel="signature" />
- <!-- Allows an application to monitor changes in tablet mode.
+ <!-- Allows an application to query tablet mode state and monitor changes
+ in it.
<p>Not for use by third-party applications.
@hide -->
- <permission android:name="android.permission.TABLET_MODE_LISTENER"
+ <permission android:name="android.permission.TABLET_MODE"
android:protectionLevel="signature" />
<!-- Allows an application to request installing packages. Apps
@@ -2127,7 +2131,7 @@
<!-- Allows an application to grant specific permissions.
@hide -->
<permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS"
- android:protectionLevel="signature|installer" />
+ android:protectionLevel="signature|installer|verifier" />
<!-- Allows an app that has this permission and the permissions to install packages
to request certain runtime permissions to be granted at installation.
@@ -2702,7 +2706,9 @@
android:killAfterRestore="false"
android:icon="@drawable/ic_launcher_android"
android:supportsRtl="true"
- android:theme="@style/Theme.Material.DayNight.DarkActionBar">
+ android:theme="@style/Theme.Material.DayNight.DarkActionBar"
+ android:forceDeviceEncrypted="true"
+ android:encryptionAware="true">
<activity android:name="com.android.internal.app.ChooserActivity"
android:theme="@style/Theme.DeviceDefault.Resolver"
android:finishOnCloseSystemDialogs="true"
diff --git a/core/res/res/color/primary_text_secondary_when_activated_material_inverse.xml b/core/res/res/color/primary_text_secondary_when_activated_material_inverse.xml
index baa8958..905fd4d 100644
--- a/core/res/res/color/primary_text_secondary_when_activated_material_inverse.xml
+++ b/core/res/res/color/primary_text_secondary_when_activated_material_inverse.xml
@@ -1,8 +1,28 @@
<?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
+ -->
+
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
+ android:state_enabled="false"
+ android:color="?attr/textColorSecondaryInverse"
+ android:alpha="?attr/disabledAlpha" />
+ <item
android:state_activated="true"
android:color="?attr/textColorPrimaryInverse" />
<item
android:color="?attr/textColorSecondaryInverse" />
-</selector>
\ No newline at end of file
+</selector>
diff --git a/core/res/res/drawable-mdpi/pointer_alias.png b/core/res/res/drawable-mdpi/pointer_alias.png
new file mode 100644
index 0000000..8f61a39
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_alias.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_all_scroll.png b/core/res/res/drawable-mdpi/pointer_all_scroll.png
new file mode 100644
index 0000000..a897ef4
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_all_scroll.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_cell.png b/core/res/res/drawable-mdpi/pointer_cell.png
new file mode 100644
index 0000000..b521389
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_cell.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_context_menu.png b/core/res/res/drawable-mdpi/pointer_context_menu.png
new file mode 100644
index 0000000..4e1ba4e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_context_menu.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_copy.png b/core/res/res/drawable-mdpi/pointer_copy.png
new file mode 100644
index 0000000..7d41036
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_copy.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_crosshair.png b/core/res/res/drawable-mdpi/pointer_crosshair.png
new file mode 100644
index 0000000..8a06c77
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_crosshair.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_grab.png b/core/res/res/drawable-mdpi/pointer_grab.png
new file mode 100644
index 0000000..0e0ea2e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_grab.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_grabbing.png b/core/res/res/drawable-mdpi/pointer_grabbing.png
new file mode 100644
index 0000000..9deb64c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_grabbing.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_hand.png b/core/res/res/drawable-mdpi/pointer_hand.png
new file mode 100644
index 0000000..c614d9e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_hand.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_help.png b/core/res/res/drawable-mdpi/pointer_help.png
new file mode 100644
index 0000000..d54b2b1
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_help.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_horizontal_double_arrow.png b/core/res/res/drawable-mdpi/pointer_horizontal_double_arrow.png
new file mode 100644
index 0000000..a2951a9
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_horizontal_double_arrow.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_nodrop.png b/core/res/res/drawable-mdpi/pointer_nodrop.png
new file mode 100644
index 0000000..ad13c66
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_nodrop.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_text.png b/core/res/res/drawable-mdpi/pointer_text.png
new file mode 100644
index 0000000..34d1698
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_text.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_top_left_diagonal_double_arrow.png b/core/res/res/drawable-mdpi/pointer_top_left_diagonal_double_arrow.png
new file mode 100644
index 0000000..b0cd92c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_top_left_diagonal_double_arrow.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_top_right_diagonal_double_arrow.png b/core/res/res/drawable-mdpi/pointer_top_right_diagonal_double_arrow.png
new file mode 100644
index 0000000..f8d3527
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_top_right_diagonal_double_arrow.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_vertical_double_arrow.png b/core/res/res/drawable-mdpi/pointer_vertical_double_arrow.png
new file mode 100644
index 0000000..48c9379
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_vertical_double_arrow.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_vertical_text.png b/core/res/res/drawable-mdpi/pointer_vertical_text.png
new file mode 100644
index 0000000..9fcbcba
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_vertical_text.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_zoom_in.png b/core/res/res/drawable-mdpi/pointer_zoom_in.png
new file mode 100644
index 0000000..17c4e66
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_zoom_in.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_zoom_out.png b/core/res/res/drawable-mdpi/pointer_zoom_out.png
new file mode 100644
index 0000000..742f705
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_zoom_out.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_alias.png b/core/res/res/drawable-xhdpi/pointer_alias.png
new file mode 100644
index 0000000..fe0fd25
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_alias.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_all_scroll.png b/core/res/res/drawable-xhdpi/pointer_all_scroll.png
new file mode 100644
index 0000000..e2374ec
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_all_scroll.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_cell.png b/core/res/res/drawable-xhdpi/pointer_cell.png
new file mode 100644
index 0000000..4ca09e3
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_cell.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_context_menu.png b/core/res/res/drawable-xhdpi/pointer_context_menu.png
new file mode 100644
index 0000000..05a59f8
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_context_menu.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_copy.png b/core/res/res/drawable-xhdpi/pointer_copy.png
new file mode 100644
index 0000000..f28a3e9
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_copy.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_crosshair.png b/core/res/res/drawable-xhdpi/pointer_crosshair.png
new file mode 100644
index 0000000..86c649c
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_crosshair.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_grab.png b/core/res/res/drawable-xhdpi/pointer_grab.png
new file mode 100644
index 0000000..b5c28ba
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_grab.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_grabbing.png b/core/res/res/drawable-xhdpi/pointer_grabbing.png
new file mode 100644
index 0000000..6aba589
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_grabbing.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_hand.png b/core/res/res/drawable-xhdpi/pointer_hand.png
new file mode 100644
index 0000000..486cb24
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_hand.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_help.png b/core/res/res/drawable-xhdpi/pointer_help.png
new file mode 100644
index 0000000..98a6632
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_help.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_horizontal_double_arrow.png b/core/res/res/drawable-xhdpi/pointer_horizontal_double_arrow.png
new file mode 100644
index 0000000..299ae11
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_horizontal_double_arrow.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_nodrop.png b/core/res/res/drawable-xhdpi/pointer_nodrop.png
new file mode 100644
index 0000000..c56bfbb
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_nodrop.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_text.png b/core/res/res/drawable-xhdpi/pointer_text.png
new file mode 100644
index 0000000..0cebeae
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_text.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_top_left_diagonal_double_arrow.png b/core/res/res/drawable-xhdpi/pointer_top_left_diagonal_double_arrow.png
new file mode 100644
index 0000000..5454a8b
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_top_left_diagonal_double_arrow.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_top_right_diagonal_double_arrow.png b/core/res/res/drawable-xhdpi/pointer_top_right_diagonal_double_arrow.png
new file mode 100644
index 0000000..a4268e4
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_top_right_diagonal_double_arrow.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_vertical_double_arrow.png b/core/res/res/drawable-xhdpi/pointer_vertical_double_arrow.png
new file mode 100644
index 0000000..95ca954
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_vertical_double_arrow.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_vertical_text.png b/core/res/res/drawable-xhdpi/pointer_vertical_text.png
new file mode 100644
index 0000000..a07d091
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_vertical_text.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_zoom_in.png b/core/res/res/drawable-xhdpi/pointer_zoom_in.png
new file mode 100644
index 0000000..9c29fcb
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_zoom_in.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_zoom_out.png b/core/res/res/drawable-xhdpi/pointer_zoom_out.png
new file mode 100644
index 0000000..710552b
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_zoom_out.png
Binary files differ
diff --git a/core/res/res/drawable/pointer_alias_icon.xml b/core/res/res/drawable/pointer_alias_icon.xml
new file mode 100644
index 0000000..8ba9301
--- /dev/null
+++ b/core/res/res/drawable/pointer_alias_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+ android:bitmap="@drawable/pointer_alias"
+ android:hotSpotX="8dp"
+ android:hotSpotY="6dp" />
diff --git a/core/res/res/drawable/pointer_all_scroll_icon.xml b/core/res/res/drawable/pointer_all_scroll_icon.xml
new file mode 100644
index 0000000..e946948
--- /dev/null
+++ b/core/res/res/drawable/pointer_all_scroll_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+ android:bitmap="@drawable/pointer_all_scroll"
+ android:hotSpotX="11dp"
+ android:hotSpotY="11dp" />
diff --git a/core/res/res/drawable/pointer_cell_icon.xml b/core/res/res/drawable/pointer_cell_icon.xml
new file mode 100644
index 0000000..da1e320
--- /dev/null
+++ b/core/res/res/drawable/pointer_cell_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+ android:bitmap="@drawable/pointer_cell"
+ android:hotSpotX="11dp"
+ android:hotSpotY="11dp" />
diff --git a/core/res/res/drawable/pointer_context_menu_icon.xml b/core/res/res/drawable/pointer_context_menu_icon.xml
new file mode 100644
index 0000000..330b627
--- /dev/null
+++ b/core/res/res/drawable/pointer_context_menu_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+ android:bitmap="@drawable/pointer_context_menu"
+ android:hotSpotX="4dp"
+ android:hotSpotY="4dp" />
diff --git a/core/res/res/drawable/pointer_copy_icon.xml b/core/res/res/drawable/pointer_copy_icon.xml
new file mode 100644
index 0000000..e299db5
--- /dev/null
+++ b/core/res/res/drawable/pointer_copy_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+ android:bitmap="@drawable/pointer_copy"
+ android:hotSpotX="9dp"
+ android:hotSpotY="9dp" />
diff --git a/core/res/res/drawable/pointer_crosshair_icon.xml b/core/res/res/drawable/pointer_crosshair_icon.xml
new file mode 100644
index 0000000..3b96a8a
--- /dev/null
+++ b/core/res/res/drawable/pointer_crosshair_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+ android:bitmap="@drawable/pointer_crosshair"
+ android:hotSpotX="12dp"
+ android:hotSpotY="12dp" />
diff --git a/core/res/res/drawable/pointer_grab_icon.xml b/core/res/res/drawable/pointer_grab_icon.xml
new file mode 100644
index 0000000..d437b3a
--- /dev/null
+++ b/core/res/res/drawable/pointer_grab_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+ android:bitmap="@drawable/pointer_grab"
+ android:hotSpotX="8dp"
+ android:hotSpotY="5dp" />
diff --git a/core/res/res/drawable/pointer_grabbing_icon.xml b/core/res/res/drawable/pointer_grabbing_icon.xml
new file mode 100644
index 0000000..38f4c3a
--- /dev/null
+++ b/core/res/res/drawable/pointer_grabbing_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+ android:bitmap="@drawable/pointer_grabbing"
+ android:hotSpotX="9dp"
+ android:hotSpotY="9dp" />
diff --git a/core/res/res/drawable/pointer_hand_icon.xml b/core/res/res/drawable/pointer_hand_icon.xml
new file mode 100644
index 0000000..3d90b88
--- /dev/null
+++ b/core/res/res/drawable/pointer_hand_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+ android:bitmap="@drawable/pointer_hand"
+ android:hotSpotX="9dp"
+ android:hotSpotY="4dp" />
diff --git a/core/res/res/drawable/pointer_help_icon.xml b/core/res/res/drawable/pointer_help_icon.xml
new file mode 100644
index 0000000..49ae554
--- /dev/null
+++ b/core/res/res/drawable/pointer_help_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+ android:bitmap="@drawable/pointer_help"
+ android:hotSpotX="4dp"
+ android:hotSpotY="4dp" />
diff --git a/core/res/res/drawable/pointer_horizontal_double_arrow_icon.xml b/core/res/res/drawable/pointer_horizontal_double_arrow_icon.xml
new file mode 100644
index 0000000..5a5ad9e
--- /dev/null
+++ b/core/res/res/drawable/pointer_horizontal_double_arrow_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+ android:bitmap="@drawable/pointer_horizontal_double_arrow"
+ android:hotSpotX="11dp"
+ android:hotSpotY="12dp" />
diff --git a/core/res/res/drawable/pointer_nodrop_icon.xml b/core/res/res/drawable/pointer_nodrop_icon.xml
new file mode 100644
index 0000000..955b40f
--- /dev/null
+++ b/core/res/res/drawable/pointer_nodrop_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+ android:bitmap="@drawable/pointer_nodrop"
+ android:hotSpotX="9dp"
+ android:hotSpotY="9dp" />
diff --git a/core/res/res/drawable/pointer_text_icon.xml b/core/res/res/drawable/pointer_text_icon.xml
new file mode 100644
index 0000000..d948c89
--- /dev/null
+++ b/core/res/res/drawable/pointer_text_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+ android:bitmap="@drawable/pointer_text"
+ android:hotSpotX="12dp"
+ android:hotSpotY="12dp" />
diff --git a/core/res/res/drawable/pointer_top_left_diagonal_double_arrow_icon.xml b/core/res/res/drawable/pointer_top_left_diagonal_double_arrow_icon.xml
new file mode 100644
index 0000000..de5efe2
--- /dev/null
+++ b/core/res/res/drawable/pointer_top_left_diagonal_double_arrow_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+ android:bitmap="@drawable/pointer_top_left_diagonal_double_arrow"
+ android:hotSpotX="11dp"
+ android:hotSpotY="11dp" />
diff --git a/core/res/res/drawable/pointer_top_right_diagonal_double_arrow_icon.xml b/core/res/res/drawable/pointer_top_right_diagonal_double_arrow_icon.xml
new file mode 100644
index 0000000..e87b526
--- /dev/null
+++ b/core/res/res/drawable/pointer_top_right_diagonal_double_arrow_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+ android:bitmap="@drawable/pointer_top_right_diagonal_double_arrow"
+ android:hotSpotX="12dp"
+ android:hotSpotY="11dp" />
diff --git a/core/res/res/drawable/pointer_vertical_double_arrow_icon.xml b/core/res/res/drawable/pointer_vertical_double_arrow_icon.xml
new file mode 100644
index 0000000..5759079
--- /dev/null
+++ b/core/res/res/drawable/pointer_vertical_double_arrow_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+ android:bitmap="@drawable/pointer_vertical_double_arrow"
+ android:hotSpotX="11dp"
+ android:hotSpotY="12dp" />
diff --git a/core/res/res/drawable/pointer_vertical_text_icon.xml b/core/res/res/drawable/pointer_vertical_text_icon.xml
new file mode 100644
index 0000000..3b48dc8
--- /dev/null
+++ b/core/res/res/drawable/pointer_vertical_text_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+ android:bitmap="@drawable/pointer_vertical_text"
+ android:hotSpotX="12dp"
+ android:hotSpotY="11dp" />
diff --git a/core/res/res/drawable/pointer_zoom_in_icon.xml b/core/res/res/drawable/pointer_zoom_in_icon.xml
new file mode 100644
index 0000000..e2dcb49
--- /dev/null
+++ b/core/res/res/drawable/pointer_zoom_in_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+ android:bitmap="@drawable/pointer_zoom_in"
+ android:hotSpotX="10dp"
+ android:hotSpotY="10dp" />
diff --git a/core/res/res/drawable/pointer_zoom_out_icon.xml b/core/res/res/drawable/pointer_zoom_out_icon.xml
new file mode 100644
index 0000000..b805df3
--- /dev/null
+++ b/core/res/res/drawable/pointer_zoom_out_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+ android:bitmap="@drawable/pointer_zoom_out"
+ android:hotSpotX="10dp"
+ android:hotSpotY="10dp" />
diff --git a/core/res/res/drawable/spinner_background_material.xml b/core/res/res/drawable/spinner_background_material.xml
index d37f5b7..c2a2a26 100644
--- a/core/res/res/drawable/spinner_background_material.xml
+++ b/core/res/res/drawable/spinner_background_material.xml
@@ -17,18 +17,18 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
android:paddingMode="stack"
android:paddingStart="0dp"
- android:paddingEnd="24dp"
+ android:paddingEnd="48dp"
android:paddingLeft="0dp"
android:paddingRight="0dp">
<item
- android:gravity="end|center_vertical"
- android:width="24dp"
- android:height="24dp"
+ android:gravity="end|fill_vertical"
+ android:width="48dp"
android:drawable="@drawable/control_background_40dp_material" />
<item
android:drawable="@drawable/ic_spinner_caret"
android:gravity="end|center_vertical"
android:width="24dp"
- android:height="24dp" />
+ android:height="24dp"
+ android:end="12dp" />
</layer-list>
diff --git a/core/res/res/drawable/time_picker_editable_background.xml b/core/res/res/drawable/time_picker_editable_background.xml
new file mode 100644
index 0000000..72e863b
--- /dev/null
+++ b/core/res/res/drawable/time_picker_editable_background.xml
@@ -0,0 +1,32 @@
+<?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
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true">
+ <layer-list>
+ <item android:gravity="bottom"
+ android:height="10dp">
+ <shape android:shape="line"
+ android:tint="?attr/textColorPrimaryInverse">
+ <stroke android:color="@color/white"
+ android:width="2dp" />
+ </shape>
+ </item>
+ </layer-list>
+ </item>
+ <item android:drawable="@color/transparent" />
+</selector>
diff --git a/core/res/res/layout-land/time_picker_material.xml b/core/res/res/layout-land/time_picker_material.xml
index bb347cb..7a0c38f 100644
--- a/core/res/res/layout-land/time_picker_material.xml
+++ b/core/res/res/layout-land/time_picker_material.xml
@@ -51,15 +51,17 @@
<!-- The hour should always be to the left of the separator,
regardless of the current locale's layout direction. -->
- <TextView
+ <com.android.internal.widget.NumericTextView
android:id="@+id/hours"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
+ android:background="@drawable/time_picker_editable_background"
android:singleLine="true"
android:ellipsize="none"
android:gravity="right"
- android:includeFontPadding="false" />
+ android:focusable="true"
+ android:nextFocusForward="@+id/minutes" />
<TextView
android:id="@+id/separator"
@@ -71,57 +73,56 @@
<!-- The minutes should always be to the right of the separator,
regardless of the current locale's layout direction. -->
- <TextView
+ <com.android.internal.widget.NumericTextView
android:id="@+id/minutes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
+ android:background="@drawable/time_picker_editable_background"
android:singleLine="true"
android:ellipsize="none"
android:gravity="left"
- android:includeFontPadding="false" />
+ android:focusable="true"
+ android:nextFocusForward="@+id/am_label" />
</LinearLayout>
- <!-- The layout alignment of this view will switch between toRightOf
- @id/minutes and toLeftOf @id/hours depending on the locale. -->
- <LinearLayout
+ <RadioGroup
android:id="@+id/ampm_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/time_layout"
android:layout_centerHorizontal="true"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp"
android:orientation="vertical"
android:layoutDirection="locale">
-
- <CheckedTextView
+ <RadioButton
android:id="@+id/am_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:minHeight="48dp"
- android:minWidth="48dp"
- android:gravity="bottom"
+ android:padding="8dp"
+ android:layout_marginBottom="-8dp"
android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
- android:paddingStart="@dimen/timepicker_ampm_horizontal_padding"
- android:paddingEnd="@dimen/timepicker_ampm_horizontal_padding"
- android:paddingTop="4dp"
- android:paddingBottom="6dp"
android:lines="1"
- android:ellipsize="none" />
-
- <CheckedTextView
+ android:ellipsize="none"
+ android:focusable="true"
+ android:background="?android:attr/selectableItemBackground"
+ android:includeFontPadding="false"
+ android:nextFocusForward="@+id/pm_label"
+ android:button="@null" />
+ <RadioButton
android:id="@+id/pm_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:minHeight="48dp"
- android:minWidth="48dp"
- android:gravity="top"
+ android:padding="8dp"
android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
- android:paddingStart="@dimen/timepicker_ampm_horizontal_padding"
- android:paddingEnd="@dimen/timepicker_ampm_horizontal_padding"
android:lines="1"
android:ellipsize="none"
- android:includeFontPadding="false" />
- </LinearLayout>
+ android:focusable="true"
+ android:background="?android:attr/selectableItemBackground"
+ android:includeFontPadding="false"
+ android:button="@null" />
+ </RadioGroup>
</RelativeLayout>
<ViewStub
diff --git a/core/res/res/layout/date_picker_header_material.xml b/core/res/res/layout/date_picker_header_material.xml
index a4388f6..821b588 100644
--- a/core/res/res/layout/date_picker_header_material.xml
+++ b/core/res/res/layout/date_picker_header_material.xml
@@ -19,19 +19,27 @@
android:id="@+id/date_picker_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingBottom="18dp"
android:paddingStart="?attr/dialogPreferredPadding"
android:paddingEnd="?attr/dialogPreferredPadding"
- android:orientation="vertical">
+ android:paddingTop="16dp"
+ android:paddingBottom="18dp"
+ android:orientation="vertical"
+ android:clipToPadding="false"
+ android:clipChildren="false">
- <!-- Top padding should stay on this view so that
- the touch target is a bit larger. -->
<TextView
android:id="@+id/date_picker_header_year"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:paddingTop="16dp"
- android:textAppearance="@style/TextAppearance.Material.DatePicker.YearLabel" />
+ android:focusable="true"
+ android:layout_marginStart="-8dp"
+ android:layout_marginEnd="-8dp"
+ android:layout_marginTop="-8dp"
+ android:layout_marginBottom="-8dp"
+ android:padding="8dp"
+ android:background="?attr/selectableItemBackground"
+ android:textAppearance="@style/TextAppearance.Material.DatePicker.YearLabel"
+ android:nextFocusForward="@+id/prev" />
<TextView
android:id="@+id/date_picker_header_date"
diff --git a/core/res/res/layout/date_picker_month_item_material.xml b/core/res/res/layout/date_picker_month_item_material.xml
index cb79cee..52f7b8e 100644
--- a/core/res/res/layout/date_picker_month_item_material.xml
+++ b/core/res/res/layout/date_picker_month_item_material.xml
@@ -21,4 +21,5 @@
android:layout_height="wrap_content"
android:paddingStart="@dimen/day_picker_padding_horizontal"
android:paddingEnd="@dimen/day_picker_padding_horizontal"
- android:paddingTop="@dimen/day_picker_padding_top" />
+ android:paddingTop="@dimen/day_picker_padding_top"
+ android:focusable="true"/>
diff --git a/core/res/res/layout/day_picker_content_material.xml b/core/res/res/layout/day_picker_content_material.xml
index b582d74..d77e8dc 100644
--- a/core/res/res/layout/day_picker_content_material.xml
+++ b/core/res/res/layout/day_picker_content_material.xml
@@ -33,7 +33,9 @@
android:src="@drawable/ic_chevron_start"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/date_picker_prev_month_button"
- android:visibility="invisible" />
+ android:visibility="invisible"
+ android:nextFocusForward="@+id/next"
+ android:nextFocusDown="@+id/month_view" />
<ImageButton
android:id="@+id/next"
@@ -44,6 +46,8 @@
android:src="@drawable/ic_chevron_end"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/date_picker_next_month_button"
- android:visibility="invisible" />
+ android:visibility="invisible"
+ android:nextFocusForward="@+id/month_view"
+ android:nextFocusDown="@+id/month_view"/>
</FrameLayout>
diff --git a/core/res/res/layout/docked_stack_divider.xml b/core/res/res/layout/docked_stack_divider.xml
deleted file mode 100644
index aa6e68d..0000000
--- a/core/res/res/layout/docked_stack_divider.xml
+++ /dev/null
@@ -1,21 +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.
--->
-
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="@dimen/docked_stack_divider_thickness"
- android:layout_height="match_parent"
- android:background="@android:color/black"
- />
diff --git a/core/res/res/layout/non_client_decor_dark.xml b/core/res/res/layout/non_client_decor_dark.xml
index 112f4b7..40b8960 100644
--- a/core/res/res/layout/non_client_decor_dark.xml
+++ b/core/res/res/layout/non_client_decor_dark.xml
@@ -29,10 +29,11 @@
android:background="@drawable/non_client_decor_title"
android:focusable="false"
android:descendantFocusability="blocksDescendants" >
- <TextView
+ <LinearLayout
+ android:id="@+id/client_decor_placeholder"
android:layout_width="0dp"
android:layout_height="match_parent"
- android:layout_weight="1" />
+ android:layout_weight="1"/>
<Button
android:id="@+id/maximize_window"
android:layout_width="32dp"
diff --git a/core/res/res/layout/non_client_decor_light.xml b/core/res/res/layout/non_client_decor_light.xml
index 5dd79c7..c75d526 100644
--- a/core/res/res/layout/non_client_decor_light.xml
+++ b/core/res/res/layout/non_client_decor_light.xml
@@ -29,10 +29,11 @@
android:background="@drawable/non_client_decor_title"
android:focusable="false"
android:descendantFocusability="blocksDescendants" >
- <TextView
+ <LinearLayout
+ android:id="@+id/client_decor_placeholder"
android:layout_width="0dp"
android:layout_height="match_parent"
- android:layout_weight="1" />
+ android:layout_weight="1"/>
<Button
android:id="@+id/maximize_window"
android:layout_width="32dp"
diff --git a/core/res/res/layout/time_picker_header_material.xml b/core/res/res/layout/time_picker_header_material.xml
index acdc509..7019ced 100644
--- a/core/res/res/layout/time_picker_header_material.xml
+++ b/core/res/res/layout/time_picker_header_material.xml
@@ -21,23 +21,24 @@
android:id="@+id/time_header"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="horizontal"
- android:padding="@dimen/timepicker_separator_padding"
android:paddingStart="16dp"
android:paddingEnd="16dp">
<!-- The hour should always be to the left of the separator,
regardless of the current locale's layout direction. -->
- <TextView
+ <com.android.internal.widget.NumericTextView
android:id="@+id/hours"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@+id/separator"
android:layout_alignBaseline="@+id/separator"
android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
+ android:background="@drawable/time_picker_editable_background"
android:singleLine="true"
android:ellipsize="none"
- android:gravity="right" />
+ android:gravity="right"
+ android:focusable="true"
+ android:nextFocusForward="@+id/minutes" />
<TextView
android:id="@+id/separator"
@@ -51,50 +52,57 @@
<!-- The minutes should always be to the left of the separator,
regardless of the current locale's layout direction. -->
- <TextView
+ <com.android.internal.widget.NumericTextView
android:id="@+id/minutes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/separator"
android:layout_alignBaseline="@+id/separator"
android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
+ android:background="@drawable/time_picker_editable_background"
android:singleLine="true"
android:ellipsize="none"
- android:gravity="left" />
+ android:gravity="left"
+ android:focusable="true"
+ android:nextFocusForward="@+id/am_label" />
<!-- The layout alignment of this view will switch between toRightOf
@id/minutes and toLeftOf @id/hours depending on the locale. -->
- <LinearLayout
+ <RadioGroup
android:id="@+id/ampm_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/minutes"
android:layout_alignBaseline="@+id/minutes"
- android:paddingStart="8dp"
- android:paddingEnd="8dp"
+ android:paddingStart="4dp"
+ android:paddingEnd="4dp"
android:orientation="vertical"
android:baselineAlignedChildIndex="1">
- <CheckedTextView
+ <RadioButton
android:id="@+id/am_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:minWidth="48dp"
- android:minHeight="48dp"
- android:gravity="bottom"
- android:paddingTop="@dimen/timepicker_am_top_padding"
+ android:padding="8dp"
+ android:layout_marginBottom="-8dp"
android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
android:lines="1"
- android:ellipsize="none" />
- <CheckedTextView
+ android:ellipsize="none"
+ android:focusable="true"
+ android:background="?android:attr/selectableItemBackground"
+ android:includeFontPadding="false"
+ android:nextFocusForward="@+id/pm_label"
+ android:button="@null" />
+ <RadioButton
android:id="@+id/pm_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:minWidth="48dp"
- android:minHeight="48dp"
- android:gravity="top"
- android:paddingTop="@dimen/timepicker_pm_top_padding"
+ android:padding="8dp"
android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
android:lines="1"
- android:ellipsize="none" />
- </LinearLayout>
+ android:ellipsize="none"
+ android:focusable="true"
+ android:background="?android:attr/selectableItemBackground"
+ android:includeFontPadding="false"
+ android:button="@null" />
+ </RadioGroup>
</RelativeLayout>
diff --git a/core/res/res/values-af-watch/strings.xml b/core/res/res/values-af-watch/strings.xml
index ee43129..b9a1a9f 100644
--- a/core/res/res/values-af-watch/strings.xml
+++ b/core/res/res/values-af-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Program <xliff:g id="NUMBER_0">%1$d</xliff:g> van <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Sensors"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"verkry toegang tot jou kontakte"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"verkry toegang tot hierdie horlosie se ligging"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"verkry toegang tot jou kalender"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"stuur en bekyk SMS-boodskappe"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"verkry toegang tot foto\'s, media en lêers op jou horlosie"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"neem oudio op"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"neem foto\'s en neem video op"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"maak en bestuur foonoproepe"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"verkry toegang tot sensordata oor jou lewenstekens"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"wees die statusbalk"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"verkry toegang tot liggaamsensors (soos hartklopmonitors)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"verkry toegang tot presiese ligging (GPS- en netwerkgegrond)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"verkry toegang tot benaderde ligging (netwerkgegrond)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"stuur bevele na die SIM"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"verkry volle netwerktoegang"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"bestuur profiel- en toesteleienaars"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"verander WiMAX-status"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"ontvang Android Straal-oordragstatus"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"roeteer media-uitvoer"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"lees installeersessies"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"versoek installeerpakkette"</string>
</resources>
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 2f1627b..f8b88a7 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Sluit persoonlike data soos kredietkaartnommers en wagwoorde in."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"deaktiveer of verander statusbalk"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Laat die program toe om die statusbalk te deaktiveer en stelselikone by te voeg of te verwyder."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"statusbalk"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"wees die statusbalk"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Laat die program toe om die statusbalk te wees."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"vou statusbalk in of uit"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Laat die program toe om die statusbalk uit te vou of in te vou."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Laat die program toe om WAP-boodskappe te ontvang en te verwerk. Hierdie toestemming sluit ook in dat boodskappe wat na jou toestel gestuur is, gemonitor of uitgevee kan word, sonder dat jy dit gesien het."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"haal lopende programme op"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Laat die program toe om inligting oor die huidig- en onlangslopende take op te haal. Dit kan moontlik die program toelaat om inligting oor watter programme op die toestel gebruik word, te ontdek."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Bestuur profiel- en toesteleienaars"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"bestuur profiel- en toesteleienaars"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Laat programme toe om die profieleienaars en die toesteleienaar te stel."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"herrangskik lopende programme"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Laat die program toe om take na die voorgrond of agtergrond te skuif. Die program kan dit moontlik sonder jou insette doen."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Laat die program toe om jou tablet se oproeprekord, insluitende data oor inkomende en uitgaande oproepe, te verander. Kwaadwillige programme kan dit gebruik om jou oproeprekord uit te vee of te verander."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Laat die program toe om jou TV se oproeprekord te wysig, insluitend data oor inkomende en uitgaande oproepe. Kwaadwillige programme kan dit gebruik om jou oproeprekord uit te vee of te wysig."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Laat die program toe om jou foon se oproeprekord, insluitende data oor inkomende en uitgaande oproepe, te verander. Kwaadwillige programme kan dit gebruik om jou oproeprekord uit te vee of te verander."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"liggaamsensors (soos hartklopmonitors)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"verkry toegang tot liggaamsensors (soos hartklopmonitors)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Laat die program toe om toegang te verkry tot data van sensors af wat jou fisieke toestand, soos jou polsslag, monitor."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"lees kalenderafsprake plus vertroulike inligting"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Laat die program toe om alle kalendergebeure wat op jou tablet gestoor is, insluitend dié van vriende en medewerkers, te lees. Dit kan moontlik die program toelaat om jou kalenderdata te deel of te stoor, ongeag van vertroulikheid of sensitiwiteit."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Laat die program toe om gebeure wat op jou foon aangepas kan word, by te voeg, te verwyder of te verander, insluitend dié van vriende en medewerkers. Dit kan moontlik die program toelaat om boodskappe wat lyk of dit van kalendereienaars af kom, te stuur, of om gebeure sonder die eienaar se kennis aan te pas."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"Kry toegang tot ekstra liggingverskaffer-bevele"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Gee die program toegang tot ekstra liggingverskaffer-bevele. Dit kan die program dalk toelaat om in te meng met die werking van die GPS of ander liggingbronne."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"presiese ligging (GPS en netwerkgebaseer)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"verkry toegang tot presiese ligging (GPS- en netwerkgegrond)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Laat die program toe om jou presiese ligging te kry met behulp van die globaleposisioneringstelsel (GPS) of netwerkliggingbronne soos seltorings en Wi-Fi. Hierdie liggingdienste moet aangeskakel en beskikbaar wees aan jou toestel vir die program om dit te gebruik. Programme kan dit gebruik om te bepaal waar jy is en kan batterylewe opgebruik."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"benaderde ligging (netwerkgebaseer)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"verkry toegang tot benaderde ligging (netwerkgegrond)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Laat die program toe om jou benaderde ligging te kry. Hierdie ligging word verkry deur liggingdienste met gebruik van netwerkliggingbronne soos seltorings en Wi-Fi. Hierdie liggingdienste moet aangeskakel en beskikbaar wees aan jou toestel vir die program om dit te gebruik. Programme kan dit gebruik om te bepaal waar omtrent jy is."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"verander jou klankinstellings"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Laat die program toe om globale klankinstellings soos volume en watter luidspreker vir uitvoer gebruik word, te verander."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"neem klank op"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Laat die program toe om klank met die mikrofoon op te neem. Hierdie toestemming laat die program toe om klank te eniger tyd, sonder jou bevestiging, op te neem."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"sim-kommunikasie"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"stuur bevele na die SIM"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Laat die program toe om bevele na die SIM te stuur. Dit is baie gevaarlik."</string>
<string name="permlab_camera" msgid="3616391919559751192">"neem foto\'s en video\'s"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Laat die program toe om foto\'s en video\'s met die kamera te neem. Hierdie toestemming laat die program toe om die kamera te eniger tyd sonder jou bevestiging te gebruik."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Laat die program toe om die lys van rekeninge wat aan die foon bekend is, te kry. Dit kan moontlik enige rekeninge wat geskep is deur programme wat jy geïnstalleer het, insluit."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"bekyk netwerkverbindings"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Laat die program toe om inligting oor netwerkverbindings, soos watter netwerke bestaan en gekoppel is, te sien."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"volle netwerktoegang"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"verkry volle netwerktoegang"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Laat die program toe om netwerksokke te skep en gepasmaakte netwerkprotokolle te gebruik. Die blaaier en ander programme verskaf reeds die middele waardeur data na die internet gestuur kan word, so hierdie toestemming word nie vereis om data na die internet te stuur nie."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"verander netwerkverbinding"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Laat die program toe om die status van netwerkkonnektiwiteit te verander."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Laat die program toe om die plaaslike Bluetooth-foon op te stel en te ontdek en met afgeleë toestelle saam te bind."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"koppel aan en ontkoppel van WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Laat die program toe om te bepaal of WiMAX geaktiveer is en of enige WiMAX-netwerke gekoppel is."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Verander WiMAX-status"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"verander WiMAX-status"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Laat die program toe om die tablet aan WiMAX-netwerke te koppel en daarvan te ontkoppel."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Laat die program toe om die TV te koppel aan en die TV van WiMAX-netwerke af te ontkoppel."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Laat die program toe om die foon aan WiMAX-netwerke te koppel en daarvan te ontkoppel."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Laat die program toe om die kalibrasieparameters van die raakskerm te wysig. Dit behoort nooit vir normale programme nodig te wees nie."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"gaan in by DRM-sertifikate"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Laat \'n program toe om DRM-sertifikate op te stel en te gebruik. Behoort nooit vir normale programme nodig te wees nie."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Ontvang Android Straal-oordragstatus"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"ontvang Android Straal-oordragstatus"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Laat hierdie program toe om inligting oor huidige Android Straal-oordragte te ontvang."</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"verwyder DRM-sertifikate"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Laat \'n program toe om DRM-sertifikate te verwyder. Behoort nooit vir gewone programme nodig te wees nie."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Verkeerde PIN-kode."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Om te ontsluit, druk Kieslys dan 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Noodnommer"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Geen diens nie."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Geen diens nie"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Skerm gesluit."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Druk kieslys om oop te sluit of maak noodoproep."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Druk kieslys om oop te maak."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Sien die handleiding of kontak kliëntediens."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM-kaart is gesluit."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Ontsluit tans SIM-kaart…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Jy het jou ontsluitpatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer verkeerd geteken. \n\nProbeer weer oor <xliff:g id="NUMBER_1">%d</xliff:g> sekondes."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Jy het jou wagwoord <xliff:g id="NUMBER_0">%d</xliff:g> keer verkeerd ingevoer. \n\nProbeer weer oor <xliff:g id="NUMBER_1">%d</xliff:g> sekondes."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Jy het jou wagwoord <xliff:g id="NUMBER_0">%d</xliff:g> keer verkeerd ingevoer. \n\nProbeer weer oor <xliff:g id="NUMBER_1">%d</xliff:g> sekondes."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Jy het jou ontsluitpatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer verkeerd geteken. Na <xliff:g id="NUMBER_1">%d</xliff:g> nóg onsuksesvolle pogings sal jy gevra word om jou tablet te ontsluit met gebruik van jou Google-aanmelddetails.\n\n Probeer weer oor <xliff:g id="NUMBER_2">%d</xliff:g> sekondes."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Jy het jou ontsluitpatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer verkeerd getrek. Na nog <xliff:g id="NUMBER_1">%d</xliff:g> onsuksesvolle pogings sal jy gevra word om jou TV met jou Google-aanmelding te ontsluit.\n\n Probeer oor <xliff:g id="NUMBER_2">%d</xliff:g> sekondes weer."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Jy het jou ontsluitpatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer verkeerd geteken. Na nóg <xliff:g id="NUMBER_1">%d</xliff:g> onsuksesvolle pogings sal jy gevra word om jou foon te ontsluit met gebruik van jou Google-aanmelddetails.\n\n Probeer weer oor <xliff:g id="NUMBER_2">%d</xliff:g> sekondes."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Jy het <xliff:g id="NUMBER_0">%d</xliff:g> keer probeer om die tablet verkeerde te ontsluit. Na nog <xliff:g id="NUMBER_1">%d</xliff:g> onsuksesvolle poging, sal die tablet terug gestel word nia die fabrieksverstek en alle gebruikerdata sal verlore wees."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Jy het die TV <xliff:g id="NUMBER_0">%d</xliff:g> keer verkeerd probeer ontsluit. Na nog <xliff:g id="NUMBER_1">%d</xliff:g> onsuksesvolle pogings sal die TV na die verstekfabriekinstellings teruggestel word en sal alle gebruikerdata verloor word."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Jy het <xliff:g id="NUMBER_0">%d</xliff:g> keer probeer om die foon verkeerde te ontsluit. Na nog <xliff:g id="NUMBER_1">%d</xliff:g> onsuksesvolle poging sal die foon terug gestel word na die fabrieksverstek en alle gebruikerdata sal verlore wees."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Jy het jou ontsluitpatroon <xliff:g id="NUMBER_0">%1$d</xliff:g> keer verkeerd geteken. \n\nProbeer weer oor <xliff:g id="NUMBER_1">%2$d</xliff:g> sekondes."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Jy het jou wagwoord <xliff:g id="NUMBER_0">%1$d</xliff:g> keer verkeerd ingevoer. \n\nProbeer weer oor <xliff:g id="NUMBER_1">%2$d</xliff:g> sekondes."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Jy het jou wagwoord <xliff:g id="NUMBER_0">%1$d</xliff:g> keer verkeerd ingevoer. \n\nProbeer weer oor <xliff:g id="NUMBER_1">%2$d</xliff:g> sekondes."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Jy het jou ontsluitpatroon <xliff:g id="NUMBER_0">%1$d</xliff:g> keer verkeerd geteken. Na <xliff:g id="NUMBER_1">%2$d</xliff:g> nóg onsuksesvolle pogings sal jy gevra word om jou tablet te ontsluit met gebruik van jou Google-aanmelddetails.\n\n Probeer weer oor <xliff:g id="NUMBER_2">%3$d</xliff:g> sekondes."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Jy het jou ontsluitpatroon <xliff:g id="NUMBER_0">%1$d</xliff:g> keer verkeerd getrek. Na nog <xliff:g id="NUMBER_1">%2$d</xliff:g> onsuksesvolle pogings sal jy gevra word om jou TV met jou Google-aanmelding te ontsluit.\n\n Probeer oor <xliff:g id="NUMBER_2">%3$d</xliff:g> sekondes weer."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Jy het jou ontsluitpatroon <xliff:g id="NUMBER_0">%1$d</xliff:g> keer verkeerd geteken. Na nóg <xliff:g id="NUMBER_1">%2$d</xliff:g> onsuksesvolle pogings sal jy gevra word om jou foon te ontsluit met gebruik van jou Google-aanmelddetails.\n\n Probeer weer oor <xliff:g id="NUMBER_2">%3$d</xliff:g> sekondes."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Jy het <xliff:g id="NUMBER_0">%1$d</xliff:g> keer probeer om die tablet verkeerde te ontsluit. Na nog <xliff:g id="NUMBER_1">%2$d</xliff:g> onsuksesvolle poging, sal die tablet terug gestel word nia die fabrieksverstek en alle gebruikerdata sal verlore wees."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Jy het die TV <xliff:g id="NUMBER_0">%1$d</xliff:g> keer verkeerd probeer ontsluit. Na nog <xliff:g id="NUMBER_1">%2$d</xliff:g> onsuksesvolle pogings sal die TV na die verstekfabriekinstellings teruggestel word en sal alle gebruikerdata verloor word."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Jy het <xliff:g id="NUMBER_0">%1$d</xliff:g> keer probeer om die foon verkeerde te ontsluit. Na nog <xliff:g id="NUMBER_1">%2$d</xliff:g> onsuksesvolle poging sal die foon terug gestel word na die fabrieksverstek en alle gebruikerdata sal verlore wees."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Jy het die tablet <xliff:g id="NUMBER">%d</xliff:g> keer verkeerd probeer ontsluit. Die tablet sal nou terug gestel word na die fabrieksverstek."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Jy het die TV <xliff:g id="NUMBER">%d</xliff:g> keer verkeerd probeer ontsluit. Die TV sal nou na die verstekfabriekinstellings teruggestel word."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Jy <xliff:g id="NUMBER">%d</xliff:g> keer probeer om die foon verkeerd te ontsluit. Die foon sal nou terug gestel word na die fabrieksvertek."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"woord"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"skakel"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"reël"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Fabriektoets het gefaal"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Die FACTORY_TEST-handeling word net ondersteun vir pakkette wat in /system/app geïnstalleer is."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Geen pakket is gevind wat die FACTORY_TEST-handeling bied nie."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Formateer tans …"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Nie ingevoeg nie"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Geen passende aktiwiteite gevind nie."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Roeteer media-uitvoer"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"roeteer media-uitvoer"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Laat \'n program toe om media-uitvoere na ander eksterne toestelle te roeteer."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Lees installasiesessies"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"lees installeersessies"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Laat \'n program toe om installasiesessies te lees. Dit laat dit toe om besonderhede van aktiewe pakketinstallasies te sien."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Versoek installeer-pakette"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"versoek installeerpakkette"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Laat \'n program toe om te versoek dat pakkette geïnstalleer word."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Raak twee keer vir zoembeheer"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Kon nie legstuk byvoeg nie."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Ongeldige gebruikernaam of wagwoord."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Het jy jou gebruikernaam of wagwoord vergeet?\nBesoek "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Kontroleer tans rekening..."</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Jy het jou PIN <xliff:g id="NUMBER_0">%d</xliff:g> keer verkeerd ingetik. \n\nProbeer weer oor <xliff:g id="NUMBER_1">%d</xliff:g> sekondes."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Jy het <xliff:g id="NUMBER_0">%d</xliff:g> keer jou wagwoord verkeerdelik getik. \n\nProbeer weer oor <xliff:g id="NUMBER_1">%d</xliff:g> sekondes."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Jy het jou ontsluitpatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer verkeerdelik geteken. \n\nProbeer weer oor <xliff:g id="NUMBER_1">%d</xliff:g> sekondes."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Jy het <xliff:g id="NUMBER_0">%d</xliff:g> keer verkeerdelik gepoog om die tablet te ontsluit. Na nog <xliff:g id="NUMBER_1">%d</xliff:g> onsuksesvolle pogings, sal die tablet na die fabrieksverstek teruggestel word en al die gebruikerdata sal verlore wees."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Jy het die TV <xliff:g id="NUMBER_0">%d</xliff:g> keer verkeerd probeer ontsluit. Na nog <xliff:g id="NUMBER_1">%d</xliff:g> onsuksesvolle pogings sal die TV na die verstekfabriekinstellings teruggestel word en sal alle gebruikerdata verloor word."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Jy het <xliff:g id="NUMBER_0">%d</xliff:g> keer verkeerdelik gepoog om die foon te ontsluit. Na nog <xliff:g id="NUMBER_1">%d</xliff:g> onsuksesvolle pogings, sal die foon na die fabrieksverstek teruggestel word en al die gebruikerdata sal verlore wees."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Jy het jou PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> keer verkeerd ingetik. \n\nProbeer weer oor <xliff:g id="NUMBER_1">%2$d</xliff:g> sekondes."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Jy het <xliff:g id="NUMBER_0">%1$d</xliff:g> keer jou wagwoord verkeerdelik getik. \n\nProbeer weer oor <xliff:g id="NUMBER_1">%2$d</xliff:g> sekondes."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Jy het jou ontsluitpatroon <xliff:g id="NUMBER_0">%1$d</xliff:g> keer verkeerdelik geteken. \n\nProbeer weer oor <xliff:g id="NUMBER_1">%2$d</xliff:g> sekondes."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Jy het <xliff:g id="NUMBER_0">%1$d</xliff:g> keer verkeerdelik gepoog om die tablet te ontsluit. Na nog <xliff:g id="NUMBER_1">%2$d</xliff:g> onsuksesvolle pogings, sal die tablet na die fabrieksverstek teruggestel word en al die gebruikerdata sal verlore wees."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Jy het die TV <xliff:g id="NUMBER_0">%1$d</xliff:g> keer verkeerd probeer ontsluit. Na nog <xliff:g id="NUMBER_1">%2$d</xliff:g> onsuksesvolle pogings sal die TV na die verstekfabriekinstellings teruggestel word en sal alle gebruikerdata verloor word."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Jy het <xliff:g id="NUMBER_0">%1$d</xliff:g> keer verkeerdelik gepoog om die foon te ontsluit. Na nog <xliff:g id="NUMBER_1">%2$d</xliff:g> onsuksesvolle pogings, sal die foon na die fabrieksverstek teruggestel word en al die gebruikerdata sal verlore wees."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Jy het <xliff:g id="NUMBER">%d</xliff:g> keer verkeerdelik gepoog om die tablet te ontsluit. Die tablet sal nou na fabrieksverstek teruggestel word."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Jy het die TV <xliff:g id="NUMBER">%d</xliff:g> keer verkeerd probeer ontsluit. Die TV sal nou na die verstekfabriekinstellings teruggestel word."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Jy het <xliff:g id="NUMBER">%d</xliff:g> keer verkeerdelik gepoog om die foon te ontsluit. Die foon sal nou na fabrieksverstek teruggestel word."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Jy het jou ontsluitpatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer verkeerdelik geteken. Na nog <xliff:g id="NUMBER_1">%d</xliff:g> onsuksesvolle pogings, sal jy gevra word om jou tablet te ontsluit deur middel van \'n e-posrekening.\n\n Probeer weer oor <xliff:g id="NUMBER_2">%d</xliff:g> sekondes."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Jy het jou ontsluitpatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer verkeerd getrek. Na nog <xliff:g id="NUMBER_1">%d</xliff:g> onsuksesvolle pogings sal jy gevra word om jou TV met \'n e-posrekening te ontsluit.\n\n Probeer oor <xliff:g id="NUMBER_2">%d</xliff:g> sekondes weer."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Jy het jou ontsluitpatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer verkeerdelik geteken. Na nog <xliff:g id="NUMBER_1">%d</xliff:g> onsuksesvolle pogings, sal jy gevra word om jou foon te ontsluit deur middel van \'n e-posrekening.\n\n Probeer weer oor <xliff:g id="NUMBER_2">%d</xliff:g> sekondes."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Jy het jou ontsluitpatroon <xliff:g id="NUMBER_0">%1$d</xliff:g> keer verkeerdelik geteken. Na nog <xliff:g id="NUMBER_1">%2$d</xliff:g> onsuksesvolle pogings, sal jy gevra word om jou tablet te ontsluit deur middel van \'n e-posrekening.\n\n Probeer weer oor <xliff:g id="NUMBER_2">%3$d</xliff:g> sekondes."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Jy het jou ontsluitpatroon <xliff:g id="NUMBER_0">%1$d</xliff:g> keer verkeerd getrek. Na nog <xliff:g id="NUMBER_1">%2$d</xliff:g> onsuksesvolle pogings sal jy gevra word om jou TV met \'n e-posrekening te ontsluit.\n\n Probeer oor <xliff:g id="NUMBER_2">%3$d</xliff:g> sekondes weer."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Jy het jou ontsluitpatroon <xliff:g id="NUMBER_0">%1$d</xliff:g> keer verkeerdelik geteken. Na nog <xliff:g id="NUMBER_1">%2$d</xliff:g> onsuksesvolle pogings, sal jy gevra word om jou foon te ontsluit deur middel van \'n e-posrekening.\n\n Probeer weer oor <xliff:g id="NUMBER_2">%3$d</xliff:g> sekondes."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Verwyder"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Verhoog volume bo aanbevole vlak?\n\nOm lang tydperke teen hoë volume te luister, kan jou gehoor beskadig."</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">1 uur lank</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Tot <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Tot <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (volgende wekker)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Totdat jy dit afskakel"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Totdat jy Moenie steur nie afskakel"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-am-watch/strings.xml b/core/res/res/values-am-watch/strings.xml
index ee6ed57..c73ebf9 100644
--- a/core/res/res/values-am-watch/strings.xml
+++ b/core/res/res/values-am-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_0">%1$d</xliff:g> መተግበሪያ ከ<xliff:g id="NUMBER_1">%2$d</xliff:g>።"</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"አነፍናፊዎች"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"የእርስዎ እውቂያዎች ላይ ይድረሱባቸው"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"የዚህን ሰዓት መገኛ አካባቢ ይድረሱባቸው"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"የእርስዎን ቀን መቁጠሪያ ይድረሱበት"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"የኤስኤምኤስ መልዕክቶችን ይላኩና ይመልከቱ"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"በእርስዎ ሰዓት ላይ ፎቶዎችን፣ ሚዲያ እና ፋይሎችን ይድረሱባቸው"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"ኦዲዮ ይቅዱ"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"ስዕሎች ያንሱ እና ቪዲዮ ይቅረጹ"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"የስልክ ጥሪዎች ያድርጉ እና ያስተዳድሩ"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"ስለአስፈላጊ ምልክቶችዎ ያሉ የዳሳሽ ውሂብ ይድረሱ"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"የሁነታ አሞሌ ይሁን"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"የሰውነት ዳሳሾችን ይድረሱባቸው (እንደ የልብ ምት መከታተያዎች)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"ትክክለኛ አካባቢ ይድረሱበት (በጂ ፒ ኤስ እና አውታረ መረብ ላይ የተመሠረተ)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"ግምታዊ አካባቢን ይድረሱበት (በአውታረ መረብ ላይ የተመሰረተ)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"ወደ ሲሙ ትዕዛዞችን ላክ"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"ሙሉ የአውታረ መረብ መዳረሻ"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"የመገለጫ እና የመሣሪያ ባለቤቶችን ያስተዳድሩ"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"የWiMAX ሁኔታ ይለውጡ"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"የAndroid Beam ሽግግር ሁኔታን ይቀበሉ"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"የሚዲያ ውፅአት መንገድ"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"የመጫን ክፍለ ጊዜዎችን አንብብ"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"የጭነት ጥቅሎችን ጠይቅ"</string>
</resources>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 5d94a69..d9246bc 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"እንደ የክሬዲት ካርድ ቁጥሮች እና የይለፍ ቃላት ያሉ የግል ውሂብ ያካትታል።"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"የሁኔቴ አሞሌ አቦዝን ወይም ቀይር"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"የስርዓት አዶዎችን ወደ ሁኔታ አሞሌ ላለማስቻል ወይም ለማከል እና ለማስወገድ ለመተግበሪያው ይፈቅዳሉ፡፡"</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"ኹናቴ አሞሌ"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"የሁኔታ አሞሌ መሆን"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"የኹናቴ አሞሌ እንዲሆን ለመተግበሪያው ይፈቅዳሉ።"</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"የሁኔታ አሞሌ ዘርጋ/ሰብስብ"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"የሁኔታ አሞሌን ለመዝረጋት እና ለመሰብሰብ ለመተግበሪያው ይፈቅዳሉ።"</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"መተግበሪያው የWAP መልዕክቶችን እንዲያነብ እና እንዲያካሂድ ይፈቅዳል። ይህ ፈቃድ የተላኩልዎን መልዕክቶች ለእርስዎ ሳያሳይዎ የመቆጣጠር ወይም የመሰረዝ ብቃትን ያጠቃልላል።"</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"አሂድ መተግበሪያዎችን ሰርስረው ያውጡ"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"መተግበሪያው በአሁኑ ጊዜና በቅርቡ እየተካሄዱ ስላሉ ተግባሮችን መረጃ ሰርስሮ እንዲያወጣ ይፈቅድለታል። ይህ መተግበሪያው በመሳሪያው ላይ የትኛዎቹ መተግበሪያዎች ጥቅም ላይ ስለመዋላቸው መረጃ እንዲያገኝ ሊፈቅድለት ይችላል።"</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"የመገለጫ እና የመሣሪያ ባለቤቶችን ያስተዳድሩ"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"የመገለጫ እና የመሣሪያ ባለቤቶችን ማቀናበር"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"የመገለጫ ባለቤቶችን እና የመሣሪያውን ባለቤት መተግበሪያዎች እንዲያዋቅሩ ይፈቅዳል።"</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"አሂድ ትግበራዎችን ድጋሚ ደርድር"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"መተግበሪያው ተግባሮችን ወደ ቅድመ ገጹ እና ወደ ዳራው እንዲያንቀሳቅስ ይፈቅድለታል። መተግበሪያው ይህንን ያላንተ ግብዓት ሊያደርግ ይችላል።"</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"ስለ ገቢ እና ወጪ ጥሪዎችን ውሂብ ጨምሮ፣ የጡባዊተኮህን ምዝግብ ማስታወሻ ለመቀየር ለመተግበሪያው ይፈቅዳል። ይሄንን ተንኮል አዘል መተግበሪያዎች የስልክህን ምዝግብ ማስታወሻ ለመሰረዝ ወይም ለመለወጥ ሊጠቀሙበት ይችላሉ።"</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"መተግበሪያው ስለገቢ እና ወጪ ጥሪዎች ያለ ውሂብም ጨምሮ የቴሌቪዥንዎ ምዝግብ ማስታወሻ እንዲይቀርይ ያስችለዋል። ተንኮል-አዘል መተግበሪያዎች ይህን ተጠቅመው የስልክዎን ምዝግብ ማስታወሻ ሊደመስሱ ወይም ሊቀይሩ ይችላሉ።"</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"ስለ ገቢ እና ወጪ ጥሪዎችን ውሂብ ጨምሮ፣ የስልክህን ምዝግብ ማስታወሻ ለመቀየር ለመተግበሪያው ይፈቅዳል። ይሄንን ተንኮል አዘል መተግበሪያዎች የስልክህን ምዝግብ ማስታወሻ ለመሰረዝ ወይም ለመለወጥ ሊጠቀሙበት ይችላሉ።"</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"የሰውነት መመርመሪያዎች (እንደ የልብ ምት መቆጣጠሪያዎች)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"የሰውነት ዳሳሾችን መድረስ (እንደ የልብ ምት መከታተያዎች ያሉ)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"እንደ የእርስዎ የልብ ምት የመሳሰሉ ያሉበትን አካላዊ ሁኔታ ከሚቆጣጠሩ ሰውነት ዳሳሾች ውሂብ ላይ እንዲደርስ ለመተግበሪያው ይፈቅደለታል።"</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"የቀን መቁጠሪያ ክስተቶች ተጨማሪ ሚስጥራዊ መረጃ አንብብ"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"መተግበሪያው የጓደኞችን ወይም የስራ ባልደረቦችን ጨምሮ ሁሉንም በጡባዊ ቱኮህ ላይ የተከማቹ የቀን መቁጠሪያ ክስተቶች እንዲያነብ ይፈቅድለታል። ይህ መተግበሪያው የቀን መቁጠሪያ ውሂብህን ሚስጥራዊቱን ከግምት ሳያስገባ እንዲያጋራ ወይም እንዲያስቀምጥ ሊፈቅድለት ይችላል።"</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"መተግበሪያው የጓደኞችዎን እና የስራ ባልደረቦችዎን ጨምሮ በስልክዎ ላይ ሊያስተካክሏቸው የሚችሏቸውን ክስተቶች እንዲያክል፣ እንዲያስወግድ፣ እንዲለውጥ ይፈቅድለታል። ይህ መተግበሪያው ከቀን መቁጠሪያ ባለቤቶች የመጡ የሚመስሉ መልዕክቶችን እንዲልክ ወይም ያለባለቤቱ እውቀት ክስተቶችን እንዲያስተካክል ሊፈቅድለት ይችላል።"</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"ተጨማሪ ሥፍራ አቅራቢ ትዕዛዞችን ድረስ።"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"መተግበሪያው ተጨማሪ የአካባቢ አቅራቢ ትእዛዞችን እንዲደርስ ይፈቅድለታል። ይሄ መተግበሪያው በጂፒኤስ ወይም ሌላ የአካባቢ ምንጮች ስራ ላይ ጣልቃ እንዲገባ ሊፈቅድለት ይችላል።"</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"ትክክለኛ አካባቢ (በጂ ፒ ኤስ እና አውታረ መረብ ላይ የተመሠረተ)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"ትክክለኛውን አካባቢ መድረስ (በጂፒኤስ እና አውታረ መረብ ላይ የተመሠረተ)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"መተግበሪያው የእርስዎን አለምአቀፍ የመሬት አቀማመጥ ስርዓትን (ጂ ፒ ኤስ) ወይም እንደ የተንቀሳቃሽ ስልክ ማማዎች እና Wi-Fi ያሉ የአውታረ መረብ አካባቢ ምንጮችን ተጠቅሞ ትክክለኛ አካባቢዎትን እንዲያውቅ ያስችለዋል። መተግበሪያው እነዚህ የአካባቢ አገልግሎቶችን እንዲጠቀምባቸው እነሱ ሊበሩ እና ለመሣሪያዎ የሚገኙ መሆን አለባቸው። መተግበሪያዎች እርስዎ የት እንዳሉ ለማወቅ ይህንን ሊጠቀሙበት ይችላሉ፣ እና ተጨማሪ ባትሪ ሊፈጁ ይችላሉ።"</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"ግምታዊ አካባቢ (በአውታረ መረብ ላይ የተመሰረተ)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"ግምታዊ አካባቢን መድረስ (በአውታረ መረብ ላይ የተመሰረተ)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"መተግበሪያው ግምታዊ አካባቢዎትን እንዲያገኝ ያሽችለዋል። ይህ አካባቢ እንደ የተንቀሳቃሽ ስልክ ማማዎች እና Wi-Fi ያሉ የአውታረ መረብ አካባቢ ምንጮችን በመጠቀም የሚገኝ ነው። መተግበሪያው እነዚህ የአካባቢ አገልግሎቶችን እንዲጠቀምባቸው እነሱ ሊበሩ እና ለመሣሪያዎ የሚገኙ መሆን አለባቸው። መተግበሪያዎች እርስዎ የት እንዳሉ ለማወቅ ይህንን ሊጠቀሙበት ይችላሉ።"</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"የድምፅ ቅንብሮችን ለውጥ"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"መተግበሪያው አንደ የድምጽ መጠን እና ለውጽአት የትኛውን የድምጽ ማጉያ ጥቅም ላይ እንደዋለ የመሳሰሉ ሁለንተናዊ የድምጽ ቅንብሮችን እንዲያስተካክል ይፈቅድለታል።"</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"ኦዲዮ ይቅዱ"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"መተግበሪያው ድምጽን በማይክሮፎን እንዲቀዳ ይፈቅድለታል። ይህ ፈቃድ መተግበሪያው ያላንተ ማረጋገጫ በማንኛውም ጊዜ ድምጽ እንዲቀዳ ይፈቅድለታል።"</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"የሲም ግንኙነት"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"ወደ ሲሙ ትዕዛዞችን መላክ"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"መተግበሪያው ትዕዛዞችን ወደ ሲሙ እንዲልክ ያስችለዋል። ይሄ በጣማ አደገኛ ነው።"</string>
<string name="permlab_camera" msgid="3616391919559751192">"ፎቶዎች እና ቪዲዮዎች ያንሱ"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"መተግበሪያው በካሜራው ፎቶዎችንና ቪዲዮዎችን እንዲያነሳ ይፈቅድለታል። ይህ ፈቃድ መተግበሪያው ካሜራውን በማንኛውም ጊዜ ያላንተ ማረጋገጫ እንዲጠቀም ይፈቅድለታል።"</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"መተግበሪያው በስልኩ የሚታወቁትን መለያዎች ዝርዝር እንዲያገኝ ይፈቅድለታል። ይህ በጫንዋቸው ማናቸውም መተግበሪያዎች የተፈጠሩ መለያዎችን ሊያጠቃልል ይችላል።"</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"የአውታረ መረብ ግንኙነቶችን ይመልከቱ"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"መተግበሪያው እንደ የትኛዎቹ አውታረ መረቦች እንዳሉ እና እንደተገናኙ ያሉ የአውታረ መረብ ግንኙነቶች መረጃዎችን እንዲያይ ይፈቅድለታል።"</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"ሙሉ የአውታረ መረብ መዳረሻ"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"ሙሉ የአውታረ መረብ መዳረሻ"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"መተግበሪያው የአውታረ መረብ መሰኪያዎችን እንዲፈጥር እና ብጁ የአውታረ መረብ ፕሮቶኮሎችን እንዲጠቀም ይፈቅድለታል። አሳሹ እና ሌሎች መተግበሪያዎች ውሂብ ወደ በይነመረብ የመላኪያ መንገዶችን ስለሚያቀርቡውሂብ ወደ በይነመረብ ለመላክ ይህ ፍቃድ አያስፈልግም።"</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"የአውታረ መረብ ተያያዥነትን ለውጥ"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"የእውታረ መረቡን ግንኙነት ሁኔታ ለመለወጥ ለመተግበሪያው ይፈቅዳሉ።"</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"የአካባቢውን ብሉቱዝ ጡባዊ ለማዋቀር እና አግኝቶ ከሩቅ መሣሪያዎች ጋር ለማጣመር ለመተግበሪያው ይፈቅዳሉ።"</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"ከWiMAX ጋር ይገናኙ እና ያላቅቁ"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"መተግበሪያው WiMAX እንደነቃ እና ስለማናቸውም የተገናኙ የWiMAX አውታረ መረቦች መረጃ እንዲወስን ይፈቅድለታል።"</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"የWiMAX ሁኔታ ለውጥ"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"የWiMAX ሁኔታ ለውጥ"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"መተግበሪያው ጡባዊ ተኮውን ከWiMAX አውታረ መረብ ጋር እንዲያገናኝና እንዲያለያይ ይፈቅድለታል።"</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"መተግበሪያው ቴሌቪዥኑን ከWiMAX አውታረ መረቦች ጋር እንዲያገናኝ እና እንዲያላቀቅ ያስችለዋል።"</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"መተግበሪያው ስልኩን ከWiMAX አውታረ መረብ ጋር እንዲያገናኝና እንዲያለያይ ይፈቅድለታል።"</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"መተግበሪያው የማያ ንካ የማስተካከያ ልኬቶቹን እንዲቀይር ያስችለዋል። ለመደበኛ መተግበሪያዎች በጭራሽ ሊያስፈልግ አይገባም።"</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"የDRM የምስክር ወረቀቶች ላይ ይድረሱ"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"አንድ መተግበሪያ የDRM የምስክር ወረቀቶችን እንዲሰጥና እንዲጠቀም ያስችላል። ለመደበኛ መተግበሪያዎች በፍጹም አስፈላጊ አይሆንም።"</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"የAndroid Beam ሽግግር ሁኔታን ይቀበሉ"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"የAndroid Beam ማስተላለፍ ሁኔታን መቀበል"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"ይም መተግበሪያ ስለአሁን የAndroid Beam ሽግግሮች መረጃ እንዲቀበል ይፈቅዳል"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"የDRM እውቅና ማረጋገጫዎችን ያስወግዳል"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"አንድ መተግበሪያ የDRM እውቅና ማረጋገጫዎችን እንዲያስወግድ ያስችለዋል። ለመደበኛ መተግበሪያዎች በጭራሽ ሊያስፈልግ አይገባም።"</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"ትክክል ያልሆነ ፒን ኮድ።"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"ለመክፈት፣ምናሌ ተጫን ከዛ 0"</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"የአደጋ ጊዜቁጥር"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"ከአገልግሎት መስጫ ክልል ውጪ"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"ምንም አገልግሎት የለም"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"ማሳያ መቆለፊያ።"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"ለመክፈት ምናሌ ተጫንወይም የአደጋ ጊዜ ጥሪ አድርግ።"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"ለመክፈት ምናሌ ተጫን"</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"እባክህ የተጠቃሚ መመሪያን ተመልከት ወይም የደንበኞች አገልግሎትአግኝ።"</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM ካርድ ተዘግቷል።"</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"የSIM ካርድ በመክፈት ላይ..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"የመክፈቻ ስርዓተ ጥለቱን <xliff:g id="NUMBER_0">%d</xliff:g>ጊዜ በስህተት ስለውታል።\n\nእባክህ እንደገና ከ<xliff:g id="NUMBER_1">%d</xliff:g>ሰከንዶች በኋላ ሞክር።"</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"<xliff:g id="NUMBER_0">%d</xliff:g>ጊዚያቶች የይለፍ ቃልህን በስህተት ተይበኻል፡፡በ<xliff:g id="NUMBER_1">%d</xliff:g> ሰኮንዶች ውስጥ \n\nእንደገና ሞክር፡፡"</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"<xliff:g id="NUMBER_0">%d</xliff:g>ጊዚያቶች ፒንህን በስህተት ተይበኻል፡፡በ\nሰኮንዶች ውስጥ \n<xliff:g id="NUMBER_1">%d</xliff:g>እንደገና ሞክር፡፡"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"የመክፈቻ ስርዓተ ጥለቱን <xliff:g id="NUMBER_0">%d</xliff:g>ጊዜ በስህተት ስለውታል።ከ<xliff:g id="NUMBER_1">%d</xliff:g>የበለጠ ያልተሳካ ሙከራ በኋላ፣ የGoogle መግቢያዎን ተጠቅመው ስልኩን እንዲከከፍቱ ይጠየቃሉ።\n\nእባክዎ እንደገና <xliff:g id="NUMBER_2">%d</xliff:g>ከሰከንዶች በኋላ ይሞክሩ።"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"የእርስዎን ስርዓተ ጥለት <xliff:g id="NUMBER_0">%d</xliff:g> ጊዜ በትክክል አልሳሉም። ከ<xliff:g id="NUMBER_1">%d</xliff:g> ተጨማሪ ያልተሳኩ ሙከራዎች በኋላ የGoogle መግቢያዎን ተጠቅመው የእርስዎን ቴሌቪዥን እንዲያስከፍቱ ይጠየቃሉ።\n\n በ<xliff:g id="NUMBER_2">%d</xliff:g> ሰከንዶች ውስጥ እንደገና ይሞክሩ።"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"የመክፈቻ ስርዓተ ጥለቱን<xliff:g id="NUMBER_0">%d</xliff:g>ጊዜ በስህተት ስለውታል።ከ<xliff:g id="NUMBER_1">%d</xliff:g> የበለጠ ያልተሳካ ሙከራ በኋላ፣ የGoogle መግቢያዎን ተጠቅመው ስልኩን እንዲከፍቱ ይጠየቃሉ።\n\nእባክዎ እንደገና ከ<xliff:g id="NUMBER_2">%d</xliff:g> ሰከንዶች በኋላ ይሞክሩ።"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"ይህን tablet <xliff:g id="NUMBER_0">%d</xliff:g> ጊዜ ያህል በስህተት ለማስከፈት ሞክረሃል፡፡ ከ <xliff:g id="NUMBER_1">%d</xliff:g> በላይ ያልተሳኩ ሙከራዎች በኋላ፣ ይህ tablet አሁን በፋብሪካ ነባሪ ቅንጅት ዳግም ይቀናበርና ሁሉም የተጠቃሚው ውሂብ ይጠፋል፡፡"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"ቴሌቪዥኑን <xliff:g id="NUMBER_0">%d</xliff:g> ጊዜ ባልተሳካ ሁኔታ ለማስከፈት ሞክረዋል። ከ<xliff:g id="NUMBER_1">%d</xliff:g> ተጨማሪ ያልተሳኩ ሙከራዎች በኋላ ቴሌቪዥኑ ወደ የፋብሪካ ነባሪ ዳግም ይጀመርና ሁሉም የተጠቃሚ ውሂብ ይጠፋል።"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"ይህን ስልክ <xliff:g id="NUMBER_0">%d</xliff:g> ጊዜ ያህል በስህተት ለማስከፈት ሞክረሃል፡፡ ከ <xliff:g id="NUMBER_1">%d</xliff:g> በላይ ያልተሳኩ ሙከራዎች በኋላ፣ ይህ ስልክ በፋብሪካ ነባሪ ቅንጅት ዳግም ይቀናበርና ሁሉም የተጠቃሚው ውሂብ ይጠፋል፡፡"</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"የመክፈቻ ስርዓተ ጥለቱን <xliff:g id="NUMBER_0">%1$d</xliff:g>ጊዜ በስህተት ስለውታል።\n\nእባክህ እንደገና ከ<xliff:g id="NUMBER_1">%2$d</xliff:g>ሰከንዶች በኋላ ሞክር።"</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"<xliff:g id="NUMBER_0">%1$d</xliff:g>ጊዚያቶች የይለፍ ቃልህን በስህተት ተይበኻል፡፡በ<xliff:g id="NUMBER_1">%2$d</xliff:g> ሰኮንዶች ውስጥ \n\nእንደገና ሞክር፡፡"</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"<xliff:g id="NUMBER_0">%1$d</xliff:g>ጊዚያቶች ፒንህን በስህተት ተይበኻል፡፡በ\nሰኮንዶች ውስጥ \n<xliff:g id="NUMBER_1">%2$d</xliff:g>እንደገና ሞክር፡፡"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"የመክፈቻ ስርዓተ ጥለቱን <xliff:g id="NUMBER_0">%1$d</xliff:g>ጊዜ በስህተት ስለውታል።ከ<xliff:g id="NUMBER_1">%2$d</xliff:g>የበለጠ ያልተሳካ ሙከራ በኋላ፣ የGoogle መግቢያዎን ተጠቅመው ስልኩን እንዲከከፍቱ ይጠየቃሉ።\n\nእባክዎ እንደገና <xliff:g id="NUMBER_2">%3$d</xliff:g>ከሰከንዶች በኋላ ይሞክሩ።"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"የእርስዎን ስርዓተ ጥለት <xliff:g id="NUMBER_0">%1$d</xliff:g> ጊዜ በትክክል አልሳሉም። ከ<xliff:g id="NUMBER_1">%2$d</xliff:g> ተጨማሪ ያልተሳኩ ሙከራዎች በኋላ የGoogle መግቢያዎን ተጠቅመው የእርስዎን ቴሌቪዥን እንዲያስከፍቱ ይጠየቃሉ።\n\n በ<xliff:g id="NUMBER_2">%3$d</xliff:g> ሰከንዶች ውስጥ እንደገና ይሞክሩ።"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"የመክፈቻ ስርዓተ ጥለቱን<xliff:g id="NUMBER_0">%1$d</xliff:g>ጊዜ በስህተት ስለውታል።ከ<xliff:g id="NUMBER_1">%2$d</xliff:g> የበለጠ ያልተሳካ ሙከራ በኋላ፣ የGoogle መግቢያዎን ተጠቅመው ስልኩን እንዲከፍቱ ይጠየቃሉ።\n\nእባክዎ እንደገና ከ<xliff:g id="NUMBER_2">%3$d</xliff:g> ሰከንዶች በኋላ ይሞክሩ።"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"ይህን tablet <xliff:g id="NUMBER_0">%1$d</xliff:g> ጊዜ ያህል በስህተት ለማስከፈት ሞክረሃል፡፡ ከ <xliff:g id="NUMBER_1">%2$d</xliff:g> በላይ ያልተሳኩ ሙከራዎች በኋላ፣ ይህ tablet አሁን በፋብሪካ ነባሪ ቅንጅት ዳግም ይቀናበርና ሁሉም የተጠቃሚው ውሂብ ይጠፋል፡፡"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"ቴሌቪዥኑን <xliff:g id="NUMBER_0">%1$d</xliff:g> ጊዜ ባልተሳካ ሁኔታ ለማስከፈት ሞክረዋል። ከ<xliff:g id="NUMBER_1">%2$d</xliff:g> ተጨማሪ ያልተሳኩ ሙከራዎች በኋላ ቴሌቪዥኑ ወደ የፋብሪካ ነባሪ ዳግም ይጀመርና ሁሉም የተጠቃሚ ውሂብ ይጠፋል።"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"ይህን ስልክ <xliff:g id="NUMBER_0">%1$d</xliff:g> ጊዜ ያህል በስህተት ለማስከፈት ሞክረሃል፡፡ ከ <xliff:g id="NUMBER_1">%2$d</xliff:g> በላይ ያልተሳኩ ሙከራዎች በኋላ፣ ይህ ስልክ በፋብሪካ ነባሪ ቅንጅት ዳግም ይቀናበርና ሁሉም የተጠቃሚው ውሂብ ይጠፋል፡፡"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"ይህን tablet <xliff:g id="NUMBER">%d</xliff:g> ጊዜ ያህል በስህተት ለማስከፈት ሞክረሃል፡፡ ይህ tablet አሁን በፋብሪካ ነባሪ ቅንጅት ዳግም ይቀናበራል፡፡"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"ቴሌቪዥኑን <xliff:g id="NUMBER">%d</xliff:g> ጊዜ ባልተሳካ ሁኔታ ለማስከፈት ሞክረዋል። ቴሌቪዥኑ አሁን ወደ ፋብሪካ ነባሪ ዳግም ይጀመራል።"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"ስልኩን <xliff:g id="NUMBER">%d</xliff:g> ጊዜ ያህል በስህተት ለማስከፈት ሞክረሃል፡፡ ስልኩ አሁን በፋብሪካ ነባሪ ቅንጅት ዳግም ይቀናበራል፡፡"</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"ቃል"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"አገናኝ"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"መስመር"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"የፋብሪካሙከራ ተስኗል"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"የፋብሪካ_ ሙከራ ርምጃበ/system/app አካታች ውስጥ የተጫነ ብቻ ተደግፏል።"</string>
<string name="factorytest_no_action" msgid="872991874799998561">"የፋብሪካ_ሙከራ ርምጃ የሚያቀርብምንም አካታች አልተገኘም።"</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"በመቅረጽ ላይ…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"አልገባም"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"ምንም ተመሳሳይ እንቅስቃሴዎች አልተገኙም።"</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"የሚዲያ ውፅአት መንገድ"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"የሚዲያ ውፅዓት ማዛወር"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"አንድ መተግበሪያ የሚዲያ ውፅአትን ወደ ሌላ ውጫዊ መሳሪያ እንዲመራ ይፈቅድለታል።"</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"የመጫን ክፍለ ጊዜዎችን አንብብ"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"የመጫን ክፍለ ጊዜዎችን ማንበብ"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"መተግበሪያው የመጫን ክፍለ ጊዜዎችን እንዲያነብ ይፈቅድለታል። ይህም ስለ ገቢር የጥቅል ጭነቶች ዝርዝር መረጃን እንዲያይ ይፈቅድለታል።"</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"የጭነት ጥቅሎችን ጠይቅ"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"የጭነት ጥቅሎችን መጠየቅ"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"መተግበሪያ የጥቅሎች መጫንን እንዲጠይቅ ይፈቅዳል።"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"ለአጉላ መቆጣጠሪያ ሁለት ጊዜ ነካ አድርግ"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"ምግብር ማከል አልተቻለም።"</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"ልክ ያልሆነ የተጠቃሚ ስም ወይም የይለፍ ቃል።"</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"የተጠቃሚ ስምዎን ወይም የይለፍ ቃልዎን ረሱት?\n"<b>"google.com/accounts/recovery"</b>"ይጎብኙ።"</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"መለያውን በማረጋገጥ ላይ…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"ፒንዎን <xliff:g id="NUMBER_0">%d</xliff:g> ጊዜ በትክክል አልተየቡም። \n\nበ<xliff:g id="NUMBER_1">%d</xliff:g> ሰኮንዶች ውስጥ እንደገና ይሞክሩ።"</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"የይለፍ ቃልዎን <xliff:g id="NUMBER_0">%d</xliff:g> ጊዜ ትክክል ባልሆነ መንገድ ተይበዋል።\n\nበ<xliff:g id="NUMBER_1">%d</xliff:g> ሰኮንዶች ውስጥ እንደገና ይሞክሩ።"</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"የመክፈቻ ስርዓተ ጥለትዎን <xliff:g id="NUMBER_0">%d</xliff:g> ጊዜ በትክክል አልሳሉትም። \n\n ከ<xliff:g id="NUMBER_1">%d</xliff:g> ሰከንዶች በኋላ እንደገና ይሞክሩ።"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"ጡባዊ ቱኮውን <xliff:g id="NUMBER_0">%d</xliff:g> ጊዜ ትክክል ባልሆነ መንገድ ለመክፈት ሞክረዋል። ከ<xliff:g id="NUMBER_1">%d</xliff:g> ተጨማሪ ያልተሳኩ ሙከራዎች በኋላ ጡባዊ ቱኮው በፋብሪካ ነባሪ ቅንብር ዳግም ይጀመርና ሁሉም የተጠቃሚ ውሂብ ይጠፋል።"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"ቴሌቪዥኑን <xliff:g id="NUMBER_0">%d</xliff:g> ጊዜ ባልተሳካ ሁኔታ ለማስከፈት ሞክረዋል። ከ<xliff:g id="NUMBER_1">%d</xliff:g> ተጨማሪ ያልተሳኩ ሙከራዎች በኋላ ቴሌቪዥኑ ወደ የፋብሪካ ነባሪ ዳግም ይጀመርና ሁሉም የተጠቃሚ ውሂብ ይጠፋል።"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"ስልኩን <xliff:g id="NUMBER_0">%d</xliff:g> ጊዜ ትክክል ባልሆነ መንገድ ለመክፈት ሞክረዋል። ከ<xliff:g id="NUMBER_1">%d</xliff:g> ተጨማሪ ያልተሳኩ ሙከራዎች በኋላ ስልኩ በፋብሪካ ነባሪ ቅንብር ዳግም ይጀመርና ሁሉም የተጠቃሚ ውሂብ ይጠፋል።"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"ፒንዎን <xliff:g id="NUMBER_0">%1$d</xliff:g> ጊዜ በትክክል አልተየቡም። \n\nበ<xliff:g id="NUMBER_1">%2$d</xliff:g> ሰኮንዶች ውስጥ እንደገና ይሞክሩ።"</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"የይለፍ ቃልዎን <xliff:g id="NUMBER_0">%1$d</xliff:g> ጊዜ ትክክል ባልሆነ መንገድ ተይበዋል።\n\nበ<xliff:g id="NUMBER_1">%2$d</xliff:g> ሰኮንዶች ውስጥ እንደገና ይሞክሩ።"</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"የመክፈቻ ስርዓተ ጥለትዎን <xliff:g id="NUMBER_0">%1$d</xliff:g> ጊዜ በትክክል አልሳሉትም። \n\n ከ<xliff:g id="NUMBER_1">%2$d</xliff:g> ሰከንዶች በኋላ እንደገና ይሞክሩ።"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"ጡባዊ ቱኮውን <xliff:g id="NUMBER_0">%1$d</xliff:g> ጊዜ ትክክል ባልሆነ መንገድ ለመክፈት ሞክረዋል። ከ<xliff:g id="NUMBER_1">%2$d</xliff:g> ተጨማሪ ያልተሳኩ ሙከራዎች በኋላ ጡባዊ ቱኮው በፋብሪካ ነባሪ ቅንብር ዳግም ይጀመርና ሁሉም የተጠቃሚ ውሂብ ይጠፋል።"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"ቴሌቪዥኑን <xliff:g id="NUMBER_0">%1$d</xliff:g> ጊዜ ባልተሳካ ሁኔታ ለማስከፈት ሞክረዋል። ከ<xliff:g id="NUMBER_1">%2$d</xliff:g> ተጨማሪ ያልተሳኩ ሙከራዎች በኋላ ቴሌቪዥኑ ወደ የፋብሪካ ነባሪ ዳግም ይጀመርና ሁሉም የተጠቃሚ ውሂብ ይጠፋል።"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"ስልኩን <xliff:g id="NUMBER_0">%1$d</xliff:g> ጊዜ ትክክል ባልሆነ መንገድ ለመክፈት ሞክረዋል። ከ<xliff:g id="NUMBER_1">%2$d</xliff:g> ተጨማሪ ያልተሳኩ ሙከራዎች በኋላ ስልኩ በፋብሪካ ነባሪ ቅንብር ዳግም ይጀመርና ሁሉም የተጠቃሚ ውሂብ ይጠፋል።"</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"ጡባዊ ቱኮዎን <xliff:g id="NUMBER">%d</xliff:g> ጊዜ ትክክል ባልሆነ መንገድ ለመክፈት ሞክረዋል። ጡባዊ ቱኮዎ አሁን በፋብሪካ ነባሪ ቅንብር ዳግም ይጀመራል።"</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"ቴሌቪዥኑን <xliff:g id="NUMBER">%d</xliff:g> ጊዜ ባልተሳካ ሁኔታ ለማስከፈት ሞክረዋል። ቴሌቪዥኑ አሁን ወደ ፋብሪካ ነባሪ ዳግም ይጀመራል።"</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"ስልኩን <xliff:g id="NUMBER">%d</xliff:g> ጊዜ ትክክል ባልሆነ መንገድ ለመክፈት ሞክረዋል። ስልኩ አሁን በፋብሪካ ነባሪ ቅንብር ዳግም ይጀመራል።"</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"የመክፈቻ ስርዓተ ጥለቱን <xliff:g id="NUMBER_0">%d</xliff:g> ጊዜ በትክክል አልሳሉትም። ከ<xliff:g id="NUMBER_1">%d</xliff:g> ተጨማሪ ያልተሳኩ ሙከራዎች በኋላ የኢሜይል መለያ ተጠቅመው ጡባዊ ቱኮዎን እንዲከፍቱ ይጠየቃሉ።\n\n ከ<xliff:g id="NUMBER_2">%d</xliff:g> ከሰከንዶች በኋላ እንደገና ይሞክሩ።"</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"ስርዓተ ጥለትዎን <xliff:g id="NUMBER_0">%d</xliff:g> ጊዜ በትክክል አልሳሉም። ከ<xliff:g id="NUMBER_1">%d</xliff:g> ተጨማሪ ያልተሳኩ ሙከራዎች በኋላ የኢሜይል መለያ ተጠቅመው የእርስዎን ቴሌቪዥን እንዲያስከፍቱ ይጠየቃሉ።\n\n በ<xliff:g id="NUMBER_2">%d</xliff:g> ሰከንዶች ውስጥ እንደገና ይሞክሩ።"</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"የመክፈቻ ስርዓተ ጥለቱን <xliff:g id="NUMBER_0">%d</xliff:g> ጊዜ በትክክል አልሳሉትም። ከ<xliff:g id="NUMBER_1">%d</xliff:g> ተጨማሪ ያልተሳኩ ሙከራዎች በኋላ የኢሜይል መለያ ተጠቅመው ስልክዎን እንዲከፍቱ ይጠየቃሉ።\n\nእባክዎ ከ<xliff:g id="NUMBER_2">%d</xliff:g> ሰከንዶች በኋላ እንደገና ይሞክሩ።"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"የመክፈቻ ስርዓተ ጥለቱን <xliff:g id="NUMBER_0">%1$d</xliff:g> ጊዜ በትክክል አልሳሉትም። ከ<xliff:g id="NUMBER_1">%2$d</xliff:g> ተጨማሪ ያልተሳኩ ሙከራዎች በኋላ የኢሜይል መለያ ተጠቅመው ጡባዊ ቱኮዎን እንዲከፍቱ ይጠየቃሉ።\n\n ከ<xliff:g id="NUMBER_2">%3$d</xliff:g> ከሰከንዶች በኋላ እንደገና ይሞክሩ።"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"ስርዓተ ጥለትዎን <xliff:g id="NUMBER_0">%1$d</xliff:g> ጊዜ በትክክል አልሳሉም። ከ<xliff:g id="NUMBER_1">%2$d</xliff:g> ተጨማሪ ያልተሳኩ ሙከራዎች በኋላ የኢሜይል መለያ ተጠቅመው የእርስዎን ቴሌቪዥን እንዲያስከፍቱ ይጠየቃሉ።\n\n በ<xliff:g id="NUMBER_2">%3$d</xliff:g> ሰከንዶች ውስጥ እንደገና ይሞክሩ።"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"የመክፈቻ ስርዓተ ጥለቱን <xliff:g id="NUMBER_0">%1$d</xliff:g> ጊዜ በትክክል አልሳሉትም። ከ<xliff:g id="NUMBER_1">%2$d</xliff:g> ተጨማሪ ያልተሳኩ ሙከራዎች በኋላ የኢሜይል መለያ ተጠቅመው ስልክዎን እንዲከፍቱ ይጠየቃሉ።\n\nእባክዎ ከ<xliff:g id="NUMBER_2">%3$d</xliff:g> ሰከንዶች በኋላ እንደገና ይሞክሩ።"</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"አስወግድ"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"ድምጹ ከሚመከረው መጠን በላይ ከፍ ይበል?\n\nበከፍተኛ ድምጽ ለረጅም ጊዜ ማዳመጥ ጆሮዎን ሊጎዳው ይችላል።"</string>
@@ -1470,6 +1468,7 @@
<item quantity="other">ለ%d ሰዓት</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"እስከ <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> ድረስ"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"እስከ <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (ቀጣይ ማንቂያ)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"ይህን እስኪያጠፉት ድረስ"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"አትረብሽን እስኪያጠፉ ድረስ"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-ar-watch/strings.xml b/core/res/res/values-ar-watch/strings.xml
index af6a164..79dd730 100644
--- a/core/res/res/values-ar-watch/strings.xml
+++ b/core/res/res/values-ar-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"التطبيق <xliff:g id="NUMBER_0">%1$d</xliff:g> من <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"أجهزة الاستشعار"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"الوصول إلى جهات الاتصال التابعة لك"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"الوصول إلى موقع هذه الساعة"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"الوصول إلى تقويمك"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"إرسال رسائل قصيرة SMS وعرضها"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"الوصول إلى الصور والوسائط والملفات على ساعتك"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"تسجيل الصوت"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"التقاط صور وتسجيل فيديو"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"إجراء مكالمات هاتفية وإدارتها"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"الوصول إلى بيانات المستشعر حول علاماتك الحيوية"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"العمل كشريط للحالة"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"الوصول إلى أجهزة استشعار الجسم (مثل شاشات معدل ضربات القلب)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"الوصول إلى الموقع الدقيق (استنادًا إلى نظام تحديد المواقع العالمي \"GPS\" والشبكة)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"الوصول إلى الموقع التقريبي (استنادًا إلى الشبكة)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"إرسال أوامر إلى شريحة SIM"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"حق الوصول بالكامل إلى الشبكة"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"إدارة المالكين لكل من الملف الشخصي والجهاز"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"تغيير حالة WiMAX"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"تلقي حالة نقل شعاع Android"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"توجيه إخراج الوسائط"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"قراءة جلسات التثبيت"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"طلب حزم التثبيت"</string>
</resources>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 209e1dc..c62fb2c 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -258,7 +258,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"يتضمن بيانات شخصية مثل أرقام بطاقات الائتمان وكلمات المرور."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"تعطيل شريط الحالة أو تعديله"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"للسماح للتطبيق بتعطيل شريط الحالة أو إضافة رموز نظام وإزالتها."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"شريط الحالة"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"العمل كشريط للحالة"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"للسماح للتطبيق بأن يكون شريط الحالة."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"توسيع/تصغير شريط الحالة"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"للسماح للتطبيق بتوسيع شريط الحالة أو تصغيره."</string>
@@ -286,7 +286,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"للسماح للتطبيق بتلقي رسائل WAP ومعالجتها. ويتضمن هذا الإذن إمكانية مراقبة الرسائل التي يتم إرسالها إليك أو حذفها بدون عرضها لك."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"استرداد التطبيقات التي قيد التشغيل"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"للسماح للتطبيق باسترداد معلومات حول المهام التي يجري تشغيلها حاليًا والتي تم تشغيلها مؤخرًا. وقد يسمح هذا للتطبيق باكتشاف معلومات حول التطبيقات المستخدمة على الجهاز."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"إدارة المالكين لكل من الملف الشخصي والجهاز"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"إدارة الملف الشخصي ومالكي الجهاز"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"للسماح للتطبيقات بتعيين مالكي الملف الشخصي ومالك الجهاز."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"إعادة ترتيب التطبيقات قيد التشغيل"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"للسماح للتطبيق بنقل المهام إلى المقدمة والخلفية. وقد يجري التطبيق ذلك بدون إذنك."</string>
@@ -328,7 +328,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"للسماح للتطبيق بتعديل سجل مكالمات الجهاز اللوحي، بما في ذلك البيانات عن المكالمات الواردة والصادرة. وربما تستخدم التطبيقات الضارة هذا لمسح سجل المكالمات أو تعديله."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"يتيح للتطبيق تعديل سجل مكالمات التلفزيون، بما في ذلك البيانات عن المكالمات الواردة والصادرة. وربما تستخدم التطبيقات الضارة هذا لمسح سجل المكالمات أو تعديله."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"للسماح للتطبيق بتعديل سجل مكالمات الهاتف، بما في ذلك البيانات عن المكالمات الواردة والصادرة. وربما تستخدم التطبيقات الضارة هذا لمحو سجل المكالمات أو تعديله."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"أجهزة استشعار الجسم (مثل شاشات معدل ضربات القلب)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"الوصول إلى أجهزة استشعار الجسم (مثل شاشات معدل ضربات القلب)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"للسماح للتطبيق بالدخول إلى البيانات من المستشعرات التي تراقب الحالة البدنية، مثل معدل نبضات القلب."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"قراءة أحداث التقويم بالإضافة إلى المعلومات السرية"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"للسماح للتطبيق بقراءة جميع أحداث التقويم المخزنة على الجهاز اللوحي، بما في ذلك أحداث التقويم التابعة للأصدقاء أو زملاء العمل. وقد يتيح هذا للتطبيق مشاركة بيانات التقويم أو حفظها، بغض النظر عن مدى سرية البيانات أو حساسيتها."</string>
@@ -340,15 +340,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"للسماح للتطبيق بإضافة أو إزالة أو تغيير الأحداث التي يمكنك تعديلها على هاتفك، بما في ذلك أحداث الأصدقاء أو زملاء العمل. وقد يتيح هذا للتطبيق إرسال رسائل يبدو أنها واردة من أصحاب التقويم أو تعديل الأحداث بدون معرفة المالكين."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"الدخول إلى المزيد من أوامر موفر الموقع"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"للسماح للتطبيق بالدخول إلى أوامر إضافية لموفر الموقع. قد يتيح هذا للتطبيق التداخل مع تشغيل تقنية نظام تحديد المواقع العالمي (GPS) أو مصادر الموقع الأخرى."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"الموقع الدقيق (مستند إلى نظام تحديد المواقع العالمي والشبكة)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"الوصول إلى الموقع الدقيق (استنادًا إلى نظام تحديد المواقع العالمي \"GPS\" والشبكة)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"للسماح للتطبيق بتحديد موقعك بدقة وهذا باستخدام نظام تحديد المواقع العالمي (GPS) أو مصادر المواقع التي تستخدم الشبكات مثل أبراج الجوال أو تقنية Wi-Fi. يتعين توفر خدمات المواقع هذه وتشغيلها على جهازك للتطبيق كي يستخدمها. وقد تستخدم التطبيقات هذا لتحديد موقعك وقد تستهلك مزيدًا من طاقة البطارية."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"الموقع التقريبي (مستند إلى الشبكة)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"الوصول إلى الموقع التقريبي (استنادًا إلى الشبكة)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"للسماح للتطبيق بتحديد موقعك التقريبي الذي يستمد من خدمات الموقع باستخدام مصادر المواقع التي تستخدم الشبكات مثل أبراج الجوال وتقنية Wi-Fi. يتعين توفر خدمات المواقع هذه وتشغيلها على جهازك للتطبيق كي يستخدمها. وقد تستخدم التطبيقات هذا لتحديد موقعك التقريبي."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"تغيير إعداداتك الصوتية"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"للسماح للتطبيق بتعديل إعدادات الصوت العامة مثل مستوى الصوت وأي السماعات يتم استخدامها للاستماع."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"تسجيل الصوت"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"للسماح للتطبيق بتسجيل الصوت باستخدام الميكروفون. ويتيح هذا الإذن للتطبيق تسجيل الصوت في أي وقت وبدون موافقة منك."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"اتصالات SIM"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"إرسال أوامر إلى شريحة SIM"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"السماح للتطبيق بإرسال أوامر إلى شريحة SIM. وهذا أمر بالغ الخطورة."</string>
<string name="permlab_camera" msgid="3616391919559751192">"التقاط صور ومقاطع فيديو"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"للسماح للتطبيق بالتقاط صور ومقاطع فيديو من خلال الكاميرا. ويتيح هذا الإذن للتطبيق استخدام الكاميرا في أي وقت وبدون موافقة منك."</string>
@@ -386,7 +386,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"للسماح للتطبيق بالحصول على قائمة بالحسابات التي يعرفها الهاتف. وقد يتضمن ذلك أية حسابات تم إنشاؤها بواسطة التطبيقات التي ثبتها."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"عرض اتصالات الشبكة"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"للسماح للتطبيق بعرض معلومات حول اتصالات الشبكة كعرض معلومات عن الشبكات المتوفرة والشبكات المتصلة."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"إمكانية دخول كاملة إلى الشبكة"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"حق الوصول الكامل إلى الشبكة"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"للسماح للتطبيق بإنشاء مقابس شبكات واستخدام بروتوكولات شبكات مخصصة. ويوفر المتصفح وتطبيقات أخرى طرقًا لإرسال البيانات إلى الإنترنت، ولذلك لا يعد هذا الإذن مطلوبًا لإرسال البيانات إلى الإنترنت."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"تغيير اتصال الشبكة"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"للسماح للتطبيق بتغيير حالة اتصال الشبكة."</string>
@@ -406,7 +406,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"للسماح للتطبيق بتهيئة هاتف البلوتوث المحلي، واكتشاف أجهزة التحكم عن بعد والاقتران بها."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"الاتصال بـشبكة WiMAX وقطع الاتصال بها"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"للسماح للتطبيق بتحديد ما إذا تم تمكين WiMAX وتحديد معلومات حول أية شبكات WiMAX متصلة."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"تغيير حالة WiMAX"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"تغيير حالة WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"للسماح للتطبيق بتوصيل الجهاز اللوحي بشبكات WiMAX وقطع اتصاله بها."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"يتيح للتطبيق توصيل التلفزيون أو إلغاء توصيله بشبكات WiMAX."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"للسماح للتطبيق بتوصيل الهاتف بشبكات WiMAX وقطع اتصاله بها."</string>
@@ -489,7 +489,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"يتيح للتطبيق إمكانية تعديل معلمات المعايرة في شاشة اللمس. يجب عدم اللجوء إليه مع التطبيقات العادية."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"الدخول إلى شهادات DRM"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"للسماح لأحد التطبيقات بتقديم شهادات DRM واستخدامها. لا يجب أن يكون ذلك لازمًا مطلقًا مع التطبيقات العادية."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"تلقي حالة نقل شعاع Android"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"تلقي حالة نقل شعاع Android"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"السماح لهذا التطبيق بتلقي معلومات حول عمليات نقل شعاع Android الحالية"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"إزالة شهادات DRM"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"للسماح لأحد التطبيقات بإزالة شهادات DRM. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
@@ -654,7 +654,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"رمز PIN غير صحيح."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"لإلغاء التأمين، اضغط على \"القائمة\" ثم على 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"رقم الطوارئ"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"لا تتوفر خدمة"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"لا خدمة"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"الشاشة مؤمّنة."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"اضغط على \"القائمة\" لإلغاء التأمين أو إجراء اتصال بالطوارئ."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"اضغط على \"القائمة\" لإلغاء التأمين."</string>
@@ -686,15 +686,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"راجع دليل المستخدم أو اتصل بخدمة العملاء."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"شريحة SIM مؤمّنة."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"جارٍ إلغاء تأمين شريحة SIM…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"لقد رسمت نقش إلغاء التأمين بطريقة غير صحيحة <xliff:g id="NUMBER_0">%d</xliff:g> مرة.\n\nالرجاء إعادة المحاولة خلال <xliff:g id="NUMBER_1">%d</xliff:g> ثانية."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"لقد كتبت كلمة المرور <xliff:g id="NUMBER_0">%d</xliff:g> مرة بشكل غير صحيح. \n\nأعد المحاولة خلال <xliff:g id="NUMBER_1">%d</xliff:g> ثانية."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"لقد كتبت رمز PIN <xliff:g id="NUMBER_0">%d</xliff:g> مرة بشكل غير صحيح. \n\nأعد المحاولة خلال <xliff:g id="NUMBER_1">%d</xliff:g> ثانية."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"لقد رسمت نقش إلغاء التأمين بشكل غير صحيح <xliff:g id="NUMBER_0">%d</xliff:g> مرة. بعد <xliff:g id="NUMBER_1">%d</xliff:g> من المحاولات غير الناجحة الأخرى، ستُطالب بإلغاء تأمين الجهاز اللوحي باستخدام معلومات تسجيل الدخول إلى Google.\n\n أعد المحاولة خلال <xliff:g id="NUMBER_2">%d</xliff:g> ثانية."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"لديك <xliff:g id="NUMBER_0">%d</xliff:g> من محاولات رسم نقش إلغاء القفل غير الصحيحة. بعد <xliff:g id="NUMBER_1">%d</xliff:g> من المحاولات غير الناجحة الأخرى، سيُطلب منك إلغاء قفل التلفزيون من خلال تسجيل الدخول إلى Google.\n\n يمكنك إعادة التجربة خلال <xliff:g id="NUMBER_2">%d</xliff:g> ثانية."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"لقد رسمت نقش إلغاء التأمين بشكل غير صحيح <xliff:g id="NUMBER_0">%d</xliff:g> مرة. بعد <xliff:g id="NUMBER_1">%d</xliff:g> من المحاولات غير الناجحة الأخرى، ستُطالب بإلغاء تأمين الهاتف باستخدام معلومات تسجيل الدخول إلى Google.\n\n أعد المحاولة خلال <xliff:g id="NUMBER_2">%d</xliff:g> ثانية."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"لقد حاولت إلغاء تأمين الجهاز اللوحي <xliff:g id="NUMBER_0">%d</xliff:g> من المرات. بعد <xliff:g id="NUMBER_1">%d</xliff:g> من المحاولات غير الناجحة، ستتم إعادة تعيين الجهاز اللوحي إلى الإعدادات الافتراضية للمصنع وسيتم فقد جميع بيانات المستخدم."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"لديك <xliff:g id="NUMBER_0">%d</xliff:g> من محاولات إلغاء قفل التلفزيون غير الصحيحة. بعد <xliff:g id="NUMBER_1">%d</xliff:g> من المحاولات غير الناجحة الأخرى، ستتم إعادة ضبط التلفزيون على إعدادات المصنع الافتراضية وستفقد جميع بيانات المستخدم."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"لقد حاولت إلغاء تأمين الهاتف <xliff:g id="NUMBER_0">%d</xliff:g> من المرات. بعد <xliff:g id="NUMBER_1">%d</xliff:g> من المحاولات غير الناجحة، ستتم إعادة تعيين الهاتف إلى الإعدادات الافتراضية للمصنع وسيتم فقد جميع بيانات المستخدم."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"لقد رسمت نقش إلغاء التأمين بطريقة غير صحيحة <xliff:g id="NUMBER_0">%1$d</xliff:g> مرة.\n\nالرجاء إعادة المحاولة خلال <xliff:g id="NUMBER_1">%2$d</xliff:g> ثانية."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"لقد كتبت كلمة المرور <xliff:g id="NUMBER_0">%1$d</xliff:g> مرة بشكل غير صحيح. \n\nأعد المحاولة خلال <xliff:g id="NUMBER_1">%2$d</xliff:g> ثانية."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"لقد كتبت رمز PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> مرة بشكل غير صحيح. \n\nأعد المحاولة خلال <xliff:g id="NUMBER_1">%2$d</xliff:g> ثانية."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"لقد رسمت نقش إلغاء التأمين بشكل غير صحيح <xliff:g id="NUMBER_0">%1$d</xliff:g> مرة. بعد <xliff:g id="NUMBER_1">%2$d</xliff:g> من المحاولات غير الناجحة الأخرى، ستُطالب بإلغاء تأمين الجهاز اللوحي باستخدام معلومات تسجيل الدخول إلى Google.\n\n أعد المحاولة خلال <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانية."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"لديك <xliff:g id="NUMBER_0">%1$d</xliff:g> من محاولات رسم نقش إلغاء القفل غير الصحيحة. بعد <xliff:g id="NUMBER_1">%2$d</xliff:g> من المحاولات غير الناجحة الأخرى، سيُطلب منك إلغاء قفل التلفزيون من خلال تسجيل الدخول إلى Google.\n\n يمكنك إعادة التجربة خلال <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانية."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"لقد رسمت نقش إلغاء التأمين بشكل غير صحيح <xliff:g id="NUMBER_0">%1$d</xliff:g> مرة. بعد <xliff:g id="NUMBER_1">%2$d</xliff:g> من المحاولات غير الناجحة الأخرى، ستُطالب بإلغاء تأمين الهاتف باستخدام معلومات تسجيل الدخول إلى Google.\n\n أعد المحاولة خلال <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانية."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"لقد حاولت إلغاء تأمين الجهاز اللوحي <xliff:g id="NUMBER_0">%1$d</xliff:g> من المرات. بعد <xliff:g id="NUMBER_1">%2$d</xliff:g> من المحاولات غير الناجحة، ستتم إعادة تعيين الجهاز اللوحي إلى الإعدادات الافتراضية للمصنع وسيتم فقد جميع بيانات المستخدم."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"لديك <xliff:g id="NUMBER_0">%1$d</xliff:g> من محاولات إلغاء قفل التلفزيون غير الصحيحة. بعد <xliff:g id="NUMBER_1">%2$d</xliff:g> من المحاولات غير الناجحة الأخرى، ستتم إعادة ضبط التلفزيون على إعدادات المصنع الافتراضية وستفقد جميع بيانات المستخدم."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"لقد حاولت إلغاء تأمين الهاتف <xliff:g id="NUMBER_0">%1$d</xliff:g> من المرات. بعد <xliff:g id="NUMBER_1">%2$d</xliff:g> من المحاولات غير الناجحة، ستتم إعادة تعيين الهاتف إلى الإعدادات الافتراضية للمصنع وسيتم فقد جميع بيانات المستخدم."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"لقد حاولت إلغاء تأمين الجهاز اللوحي <xliff:g id="NUMBER">%d</xliff:g> من المرات بشكل غير صحيح. سيتم الآن إعادة تعيين الجهاز اللوحي إلى الإعدادات الافتراضية للمصنع."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"لديك <xliff:g id="NUMBER">%d</xliff:g> من محاولات إلغاء قفل التلفزيون غير الصحيحة. ستتم الآن إعادة ضبط التلفزيون على إعدادات المصنع الافتراضية."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"لقد حاولت إلغاء تأمين الهاتف <xliff:g id="NUMBER">%d</xliff:g> من المرات بشكل غير صحيح. سيتم الآن إعادة تعيين الهاتف إلى الإعدادات الافتراضية للمصنع."</string>
@@ -746,8 +746,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"كلمة"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"رابط"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"سطر"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"أخفق اختبار المصنع"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"إجراء FACTORY_TEST غير متاح سوى للحزم المثبتة في /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"لم يتم العثور على أية حزمة توفر إجراء FACTORY_TEST."</string>
@@ -1101,11 +1099,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"جارٍ التهيئة…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"لم يتم الإدخال"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"لم يتم العثور على أي أنشطة متطابقة."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"توجيه إخراج الوسائط"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"توجيه إخراج الوسائط"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"للسماح للتطبيق بتوجيه إخراج الوسائط إلى أجهزة خارجية أخرى."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"قراءة جلسات التثبيت"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"قراءة جلسات التثبيت"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"للسماح لأحد التطبيقات بقراءة جلسات التثبيت. ويسمح لك هذا بالاطلاع على تفاصيل بشأن عمليات تثبيت الحزم النشطة."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"طلب تثبيت الحزم"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"طلب حزم التثبيت"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"للسماح لتطبيق ما بطلب تثبيت الحزم."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"المس مرتين للتحكم في التكبير/التصغير"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"تعذرت إضافة أداة."</string>
@@ -1317,18 +1315,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"اسم مستخدم غير صحيح أو كلمة مرور غير صالحة."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"هل نسيت اسم المستخدم أو كلمة المرور؟\nانتقل إلى "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"جارٍ فحص الحساب…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"لقد كتبت رمز PIN بشكل غير صحيح <xliff:g id="NUMBER_0">%d</xliff:g> مرة. \n\nأعد المحاولة خلال <xliff:g id="NUMBER_1">%d</xliff:g> ثانية."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"لقد كتبت كلمة المرور بشكل غير صحيح <xliff:g id="NUMBER_0">%d</xliff:g> مرة. \n\nأعد المحاولة خلال <xliff:g id="NUMBER_1">%d</xliff:g> ثانية."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"لقد رسمت نقش إلغاء التأمين بطريقة غير صحيحة <xliff:g id="NUMBER_0">%d</xliff:g> مرة. \n\nأعد المحاولة خلال <xliff:g id="NUMBER_1">%d</xliff:g> ثانية."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"لقد حاولت إلغاء تأمين الجهاز اللوحي بشكل غير صحيح <xliff:g id="NUMBER_0">%d</xliff:g> مرة. بعد إجراء <xliff:g id="NUMBER_1">%d</xliff:g> من المحاولات غير الناجحة الأخرى، ستتم إعادة تعيين الجهاز اللوحي على الإعدادات الافتراضية للمصنع وسيتم فقد جميع بيانات المستخدم."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"لديك <xliff:g id="NUMBER_0">%d</xliff:g> من محاولات إلغاء قفل التلفزيون غير الصحيحة. بعد <xliff:g id="NUMBER_1">%d</xliff:g> من المحاولات غير الناجحة الأخرى، ستتم إعادة ضبط التلفزيون على إعدادات المصنع الافتراضية وستفقد جميع بيانات المستخدم."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"لقد حاولت إلغاء تأمين الهاتف بشكل غير صحيح <xliff:g id="NUMBER_0">%d</xliff:g> مرة. بعد إجراء <xliff:g id="NUMBER_1">%d</xliff:g> من المحاولات غير الناجحة الأخرى، ستتم إعادة تعيين الهاتف على الإعدادات الافتراضية للمصنع وسيتم فقد جميع بيانات المستخدم."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"لقد كتبت رمز PIN بشكل غير صحيح <xliff:g id="NUMBER_0">%1$d</xliff:g> مرة. \n\nأعد المحاولة خلال <xliff:g id="NUMBER_1">%2$d</xliff:g> ثانية."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"لقد كتبت كلمة المرور بشكل غير صحيح <xliff:g id="NUMBER_0">%1$d</xliff:g> مرة. \n\nأعد المحاولة خلال <xliff:g id="NUMBER_1">%2$d</xliff:g> ثانية."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"لقد رسمت نقش إلغاء التأمين بطريقة غير صحيحة <xliff:g id="NUMBER_0">%1$d</xliff:g> مرة. \n\nأعد المحاولة خلال <xliff:g id="NUMBER_1">%2$d</xliff:g> ثانية."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"لقد حاولت إلغاء تأمين الجهاز اللوحي بشكل غير صحيح <xliff:g id="NUMBER_0">%1$d</xliff:g> مرة. بعد إجراء <xliff:g id="NUMBER_1">%2$d</xliff:g> من المحاولات غير الناجحة الأخرى، ستتم إعادة تعيين الجهاز اللوحي على الإعدادات الافتراضية للمصنع وسيتم فقد جميع بيانات المستخدم."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"لديك <xliff:g id="NUMBER_0">%1$d</xliff:g> من محاولات إلغاء قفل التلفزيون غير الصحيحة. بعد <xliff:g id="NUMBER_1">%2$d</xliff:g> من المحاولات غير الناجحة الأخرى، ستتم إعادة ضبط التلفزيون على إعدادات المصنع الافتراضية وستفقد جميع بيانات المستخدم."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"لقد حاولت إلغاء تأمين الهاتف بشكل غير صحيح <xliff:g id="NUMBER_0">%1$d</xliff:g> مرة. بعد إجراء <xliff:g id="NUMBER_1">%2$d</xliff:g> من المحاولات غير الناجحة الأخرى، ستتم إعادة تعيين الهاتف على الإعدادات الافتراضية للمصنع وسيتم فقد جميع بيانات المستخدم."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"لقد حاولت إلغاء تأمين الجهاز اللوحي بشكل غير صحيح <xliff:g id="NUMBER">%d</xliff:g> مرة. سيتم الآن إعادة تعيين الجهاز اللوحي على الإعدادات الافتراضية للمصنع."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"لديك <xliff:g id="NUMBER">%d</xliff:g> من محاولات إلغاء قفل التلفزيون غير الصحيحة. ستتم الآن إعادة ضبط التلفزيون على إعدادات المصنع الافتراضية."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"لقد حاولت إلغاء تأمين الهاتف بشكل غير صحيح <xliff:g id="NUMBER">%d</xliff:g> مرة. سيتم الآن إعادة تعيين الهاتف على الإعدادات الافتراضية للمصنع."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"لقد رسمت نقش إلغاء التأمين بشكل غير صحيح <xliff:g id="NUMBER_0">%d</xliff:g> مرة. بعد إجراء <xliff:g id="NUMBER_1">%d</xliff:g> من المحاولات غير الناجحة الأخرى، ستطالَب بإلغاء تأمين الجهاز اللوحي باستخدام معلومات حساب بريد إلكتروني.\n\n أعد المحاولة خلال <xliff:g id="NUMBER_2">%d</xliff:g> ثانية."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"لقد رسمت نقش إلغاء القفل بشكل غير صحيح عدد <xliff:g id="NUMBER_0">%d</xliff:g> من المرات. بعد <xliff:g id="NUMBER_1">%d</xliff:g> من المحاولات غير الناجحة، سيُطلب منك إلغاء قفل التلفزيون باستخدام حساب بريد إلكتروني.\n\n يمكنك إعادة التجربة خلال <xliff:g id="NUMBER_2">%d</xliff:g> ثانية."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"لقد رسمت نقش إلغاء التأمين بشكل غير صحيح <xliff:g id="NUMBER_0">%d</xliff:g> مرة. بعد إجراء <xliff:g id="NUMBER_1">%d</xliff:g> من المحاولات غير الناجحة الأخرى، ستُطالب بإلغاء تأمين الهاتف باستخدام حساب بريد إلكتروني لإلغاء تأمين الهاتف.\n\n أعد المحاولة خلال <xliff:g id="NUMBER_2">%d</xliff:g> ثانية."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"لقد رسمت نقش إلغاء التأمين بشكل غير صحيح <xliff:g id="NUMBER_0">%1$d</xliff:g> مرة. بعد إجراء <xliff:g id="NUMBER_1">%2$d</xliff:g> من المحاولات غير الناجحة الأخرى، ستطالَب بإلغاء تأمين الجهاز اللوحي باستخدام معلومات حساب بريد إلكتروني.\n\n أعد المحاولة خلال <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانية."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"لقد رسمت نقش إلغاء القفل بشكل غير صحيح عدد <xliff:g id="NUMBER_0">%1$d</xliff:g> من المرات. بعد <xliff:g id="NUMBER_1">%2$d</xliff:g> من المحاولات غير الناجحة، سيُطلب منك إلغاء قفل التلفزيون باستخدام حساب بريد إلكتروني.\n\n يمكنك إعادة التجربة خلال <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانية."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"لقد رسمت نقش إلغاء التأمين بشكل غير صحيح <xliff:g id="NUMBER_0">%1$d</xliff:g> مرة. بعد إجراء <xliff:g id="NUMBER_1">%2$d</xliff:g> من المحاولات غير الناجحة الأخرى، ستُطالب بإلغاء تأمين الهاتف باستخدام حساب بريد إلكتروني لإلغاء تأمين الهاتف.\n\n أعد المحاولة خلال <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانية."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"إزالة"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"هل تريد رفع مستوى الصوت فوق المستوى الموصى به؟\n\nقد يضر سماع صوت عالٍ لفترات طويلة بسمعك."</string>
@@ -1538,6 +1536,7 @@
<item quantity="one">لمدة ساعة</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"حتى <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"حتى <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (التنبيه التالي)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"لحين تعطيل هذا الإعداد"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"حتى يتم تعطيل \"الرجاء عدم الإزعاج\""</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-az-rAZ-watch/strings.xml b/core/res/res/values-az-rAZ-watch/strings.xml
index 5a84880..9200718 100644
--- a/core/res/res/values-az-rAZ-watch/strings.xml
+++ b/core/res/res/values-az-rAZ-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Tətbiq <xliff:g id="NUMBER_0">%1$d</xliff:g>/<xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Sensorlar"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"kontaktlarınıza daxil olun"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"bu saatın məkanına giriş"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"təqvimə daxil olun"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"göndərin və SMS mesajlarına baxın"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"Saatınızda foto, media və fayllara daxil olun"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"səsi qeydə alın"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"şəkil çəkin və video yazın"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"telefon zəngləri edin və onları idarə edin"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"Həyati əlamətlər haqqında sensor dataya daxil olun"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"status paneli edin"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"bədən sensorlarına (ürək döyüntüsü monitorları kimi) giriş"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"dəqiq məkana (GPS və şəbəkə əsasında) giriş"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"təxmini məkana (şəbəkə əsaslı) giriş"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"əmrləri SIM\'ə göndərin"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"tam şəbəkə girişi var"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"profil və cihaz sahiblərini idarə edin"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"WiMAX vəziyyətini dəyişin"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"Android Beam transfer statusunu əldə edin"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"media çıxışını yönləndirin"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"quraşdırma sessiyalarını oxuyun"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"paketləri quraşdırma sorğusu"</string>
</resources>
diff --git a/core/res/res/values-az-rAZ/strings.xml b/core/res/res/values-az-rAZ/strings.xml
index 16d123b..db91232 100644
--- a/core/res/res/values-az-rAZ/strings.xml
+++ b/core/res/res/values-az-rAZ/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Kredit kartı nömrələri və parollar kimi şəxsi məlumatlar daxildir."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"status panelini deaktivləşdir və ya dəyişdir"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Tətbiqə status panelini deaktiv etməyə və ya sistem ikonalarını əlavə etmək və ya silmək imkanı verir."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"status paneli"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"status paneli edin"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Tətbiqə status paneli olmağa imkan verir."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"status panelini genişlətmək və ya yığmaq"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Tətbiqə status panelini genişləndirməyə və ya yox etməyə imkan verir."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Tətbiqə WAP mesajlar göndərmək və ya qəbul etmək imkanı verir. Buna mesajları izləmək və Sizə xəbər vermədən silmək imkanları da daxildir."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"işlənən tətbiqlər əldə etmək"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Tətbiqə hazırda və az öncə işləyən tapşırıqlar haqqında ətraflı məlumat əldə etməyə imkan verir. Bu da cihazda hansı tətbiqlərin istifadə olunması haqqında məlumatların əldə edilməsinə imkan verir."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Profil və cihaz sahiblərini idarə edin"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"profil və cihaz sahiblərini idarə edin"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Profil və cihaz sahiblərini təyin etmək üçün tətbiqə icazə verin."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"işlənən tətbiqlərin sırasını dəyişmək"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Tətbiqə tapşırıqları ön plandan arxa plana keçirməyə imkan verir. Tətbiq bunu Sizin daxiletməniz olmadan da edə bilər."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Tətbiqə planşetinizdəki zəng jurnalını, həmçinin gedən və gələn zənglərin siyahısını dəyişməyə imkan verir. Zərərli tətbiqlər bundan istifadə edərək, zəng jurnalınıza dəyişiklik edə bilər."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Proqrama TV-nizin zəng jurnalını, o cümlədən daxil olan və çıxan zənglərlə bağlı məlumatları dəyişdirmək imkanı verə bilər. Zərərli proqramlar zəng jurnalınızı silmək və ya dəyişdirmək üçün bundan istifadə edə bilər."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Tətbiqə sizin daxil olan və gedən zənglər daxil olmaqla telefon zəngi loqlarınızı redaktə etmək icazəsi verir. Zərərli tətbiqlər bundan telefon loqlarınızı silmək və ya redaktə etmək üçün istifadə edə bilər."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"bədən sensorları (ürək döyüntüsü sayı kimi)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"bədən sensorlarına (ürək döyüntüsü monitorları kimi) giriş"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Tətbiqə ürək döyüntüsü kimi fiziki durumunuzu izləməyən sensorların datasına daxil olmağa icazə verir."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"təqvim tədbirlərini və konfidensial məlumatları oxuyur"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Tətbiqə dostlarınızın və əməkdaşlarınızın planşetinizdə yerləşən kalendar tədbirlərini oxumağa icazə verir. Bu tətbiqə konfidensiallıq və ya həssaslıqdan asılı olmayaraq sizin kalendar məlumatlarınızı paylaşmaq və ya saxlamağa imkan yaradır."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Dostlarınız və həmkarlarınıza məxsus olanlar da daxil olmaqla, tətbiqə telefonunuzdakı tədbirləri dəyişməyə, tədbir əlavə etməyə və ya silməyə imkan verir. Bu, tədbirə Sizin adınızdan və Sizdən xəbərsiz, təqvim sahibi kimi mesaj göndərmək imkanı verir."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"əlavə məkan provayderi əmrlərinə çıxış"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Tətbiqə ekstra məkan provayder əmrlərinə girişə imkan verir. Bu, tətbiqə GPS və ya digər lokal mənbələrlə əməliyyata müdaxiləyə imkan verə bilər."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"dəqiq yeri (GPS və şəbəkə-əsaslı)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"dəqiq məkana (GPS və şəbəkə əsasında) giriş"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Qlobal Pozisiya Sistemini və ya şəbəkə qüllələri və Wi-Fi kimi şəbəkə məkanını istifadə edərək tətbiqə Sizin dəqiq yerinizi təyin etməyə imkan verir. Bu məkan xidmətləri aktivləşdirilməlidirlər ki, Siz tətbiqi istifadə edən zaman tətbiq onları istifadə edə bilsin. Tətbiqlər Sizin harada olmağınızı bunun vasitəsilə təyin edəcək, eyni zamanda, bu xidmət əlavə batareya enerjisi apara bilər."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"təxmini məkan (şəbəkə əsaslı)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"təxmini məkana (şəbəkə əsaslı) giriş"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Tətbiqə təxmini yerinizi almaq üçün imkan verir. Bu yer, yerləşmə xidmətləri tərəfindən mobil qüllələr və Wi-Fi kimi şəbəkə yerləşmə mənbələrdən istifadə etməklə əldə edilir. Bu yerləşmə xidmətləri tətbiqin onlardan istifadəsi üçün açıq və cihazınızın onları istifadəsi üçün mövcud olmalıdır. Tətbiqlər bundan sizin təxminən harada olduğunuzu müəyyənləşdirmək üçün istifadə edə bilər."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"audio ayarlarınızı dəyişir"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Tətbiqə səs və hansı spikerin çıxış üçün istifadə olunduğu kimi qlobal səs ayarlarını dəyişdirməyə imkan verir."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"səs yaz"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Tətbiqə mikrofonla audio yazmaq icazəsi verir. İcazə tətbiqə sizin təsdiqiniz olmadan istənilən zaman səs yazma izni verir."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"sim rabitəsi"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"əmrləri SIM\'ə göndərin"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Tətbiqə SIM-ə əmrlər göndərməyə imkan verir. Bu, çox təhlükəlidir."</string>
<string name="permlab_camera" msgid="3616391919559751192">"şəkil və video çəkmək"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Tətbiqə kamera ilə şəkil və video çəkməyə imkan yaradır. Bu icazə tətbiqə sizin təsdiqiniz olmadan kameradan istifadə icazəsi verir."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Tətbiqə telefonda olan hesabların siyahısını əldə etməyə imkan verir. Buna quraşdırdığınız istənilən tətbiq tərəfindən yaradılan hesablar da aiddir."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"şəbəkə bağlantılarına baxmaq"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Tətbiqə mövcud olan və qoşulan şəbəkələr kimi qoşulmalar haqqında məlumatı görməyə icazə verir."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"tam şəbəkə girişi"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"tam şəbəkə girişi var"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Tətbiqə şəbəkə soketlərini yaratmağa və fərdi şəbəkə protokollarını istifadə etməyə imkan verir. Brauzer və digər tətbiqlər datanın internetə ötürülməsini təmin edən vəsaitlər verir, ona görə də datanın internetə gönrədilməsi üçün bu icazə tələb olunmur."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"şəbəkə bağlantısını dəyişir"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Tətbiqə şəbəkə vəziyyətini dəyişməyə icazə verir."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Tətbiqə lokal Bluetooth telefonunu konfiqurə etməyə və uzaq cihazları kəşf etmək və onlara qoşulmaq icazəsi verir."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"WiMAX\'a qoşul və bağlantını kəs"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Tətbiqə WiMAX mövcudluğu və qoşulmuş WiMAX şəbəkələrini təyin etməyə icazə verir."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX vəziyyətini dəyişir"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"WiMAX vəziyyətini dəyişir"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Tətbiqə planşeti WiMAX şəbəkələrinə qoşmaq və onlardan ayırmaq icazəsi verir."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Proqrama TV-ni WiMAX şəbəkələrinə qoşmaq və onlarla əlaqəni kəsmək imkanı verir."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Tətbiqə telefonu WiMAX şəbəkəsinə qoşmağa və ya WiMAX şəbəkəsindən ayırmağa imkan verir."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Sensor ekran parametr kalibrasiyalarını dəyişmək üçün tətbiqə icazə verin. Normal tətbiqlər belə şeyləri istəmir."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"DRM sertifikatlarına giriş"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Tətbiqə DRM sertifikatları təmin etməyə və işlətməyə icazə verir. Normal tətbiqlər tərəfindən heç vaxt istənilməməlidir."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Android Beam transfer statusunu əldə edin"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"Android Beam transfer statusunu əldə edin"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Bu tətbiqə hazırkı Android Beam transferləri haqqında məlumat əldə etməyə icazə verir"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"Müəllif hüquqlarının qorunması üçün texniki vasitələr sertifikatlarını silin"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Tətbiqə müəllif hüquqlarının qorunması üçün texniki vasitələr sertifikatlarını silməyə icazə verir. Normal tətbiqlər tərəfindən heç vaxt tələb edilmir."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Yanlış PIN kodu."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Kilidi açmaq üçün Menyu, sonra 0 basın."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Təcili nömrə"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Xidmət yoxdur."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Xidmət yoxdur"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Ekran kilidlənib."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Təcili zəng kilidini açmaq və ya yerləşdirmək üçün Menyu düyməsinə basın."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Kilidi açmaq üçün Menyu düyməsinə basın."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"İstifadəçi Təlimatlarına baxın və ya Müştəri Xidmətlərinə müraciət edin."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM kart kilidlənib."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"SİM kartın kilidi açılır..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Siz kilid modelini <xliff:g id="NUMBER_0">%d</xliff:g> dəfə yanlış çəkdiniz. \n\n <xliff:g id="NUMBER_1">%d</xliff:g> saniyə içində yenidən sınayın."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Şifrənizi <xliff:g id="NUMBER_0">%d</xliff:g> dəfə yanlış daxil etdiniz.\n\n <xliff:g id="NUMBER_1">%d</xliff:g> saniyə ərzində yenidən yoxlayın"</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Siz PIN nömrənizi <xliff:g id="NUMBER_0">%d</xliff:g> dəfə yanlış daxil etdiniz. \n \n <xliff:g id="NUMBER_1">%d</xliff:g> saniyə içində təkrar sınayın."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Siz kilidi açmaq üçün şablonu <xliff:g id="NUMBER_0">%d</xliff:g> dəfə səhv çəkdiniz. <xliff:g id="NUMBER_1">%d</xliff:g> daha uğursuz cəhddən sonra planşetin kilidini Google hesabınıza daxil olmaqla açmağınız istəniləcək.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> saniyə ərzində bir daha yoxlayın."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Siz kilidaçma nümunənizi <xliff:g id="NUMBER_0">%d</xliff:g> dəfə səhv daxil etdiniz. Daha <xliff:g id="NUMBER_1">%d</xliff:g> uğursuz cəhddən sonra sizdən Google girişindən istifadə etməklə TV-nizin kilidini açmağınız istəniləcəkdir.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> saniyəyə yenidən cəhd edin."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Siz kilidi açmaq üçün şablonu <xliff:g id="NUMBER_0">%d</xliff:g> dəfə səhv çəkdiniz. <xliff:g id="NUMBER_1">%d</xliff:g> daha uğursuz cəhddən sonra planşetin kilidini Google hesabınıza daxil olmaqla açmağınız tələb olunacaq.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> saniyə ərzində bir daha yoxlayın."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Siz planşet kilidini açmaq üçün <xliff:g id="NUMBER_0">%d</xliff:g> dəfə uğursuz cəhd etmisiniz. <xliff:g id="NUMBER_1">%d</xliff:g> dəfə də uğursuz cəhd etsəniz, planşet fabrik ayarlarına sıfırlanacaq və bütün məlumatlarınız itəcək."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Siz TV <xliff:g id="NUMBER_0">%d</xliff:g> dəfə TV-nizin kilidini açmaq üçün səhv cəhdlər etdiniz. Daha <xliff:g id="NUMBER_1">%d</xliff:g> uğursuz cəhddən sonra TV standart parametrlərə qbərpa olunacaq və bütün istifadəçi məlumatları itəcəkdir."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Siz telefon kilidini açmaq üçün <xliff:g id="NUMBER_0">%d</xliff:g> dəfə uğursuz cəhd etmisiniz. <xliff:g id="NUMBER_1">%d</xliff:g> dəfə də uğursuz cəhd etsəniz, telefon zavod ayarlarına sıfırlanacaq və bütün məlumatlarınız itəcək."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Siz kilid modelini <xliff:g id="NUMBER_0">%1$d</xliff:g> dəfə yanlış çəkdiniz. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> saniyə içində yenidən sınayın."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Şifrənizi <xliff:g id="NUMBER_0">%1$d</xliff:g> dəfə yanlış daxil etdiniz.\n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> saniyə ərzində yenidən yoxlayın"</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Siz PIN nömrənizi <xliff:g id="NUMBER_0">%1$d</xliff:g> dəfə yanlış daxil etdiniz. \n \n <xliff:g id="NUMBER_1">%2$d</xliff:g> saniyə içində təkrar sınayın."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Siz kilidi açmaq üçün şablonu <xliff:g id="NUMBER_0">%1$d</xliff:g> dəfə səhv çəkdiniz. <xliff:g id="NUMBER_1">%2$d</xliff:g> daha uğursuz cəhddən sonra planşetin kilidini Google hesabınıza daxil olmaqla açmağınız istəniləcək.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> saniyə ərzində bir daha yoxlayın."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Siz kilidaçma nümunənizi <xliff:g id="NUMBER_0">%1$d</xliff:g> dəfə səhv daxil etdiniz. Daha <xliff:g id="NUMBER_1">%2$d</xliff:g> uğursuz cəhddən sonra sizdən Google girişindən istifadə etməklə TV-nizin kilidini açmağınız istəniləcəkdir.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> saniyəyə yenidən cəhd edin."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Siz kilidi açmaq üçün şablonu <xliff:g id="NUMBER_0">%1$d</xliff:g> dəfə səhv çəkdiniz. <xliff:g id="NUMBER_1">%2$d</xliff:g> daha uğursuz cəhddən sonra planşetin kilidini Google hesabınıza daxil olmaqla açmağınız tələb olunacaq.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> saniyə ərzində bir daha yoxlayın."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Siz planşet kilidini açmaq üçün <xliff:g id="NUMBER_0">%1$d</xliff:g> dəfə uğursuz cəhd etmisiniz. <xliff:g id="NUMBER_1">%2$d</xliff:g> dəfə də uğursuz cəhd etsəniz, planşet fabrik ayarlarına sıfırlanacaq və bütün məlumatlarınız itəcək."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Siz TV <xliff:g id="NUMBER_0">%1$d</xliff:g> dəfə TV-nizin kilidini açmaq üçün səhv cəhdlər etdiniz. Daha <xliff:g id="NUMBER_1">%2$d</xliff:g> uğursuz cəhddən sonra TV standart parametrlərə qbərpa olunacaq və bütün istifadəçi məlumatları itəcəkdir."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Siz telefon kilidini açmaq üçün <xliff:g id="NUMBER_0">%1$d</xliff:g> dəfə uğursuz cəhd etmisiniz. <xliff:g id="NUMBER_1">%2$d</xliff:g> dəfə də uğursuz cəhd etsəniz, telefon zavod ayarlarına sıfırlanacaq və bütün məlumatlarınız itəcək."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Siz planşetin kilidini açmaq üçün <xliff:g id="NUMBER">%d</xliff:g> yanlış cəhd etmisiniz. Planşet artıq defolt zavod halına sıfırlanacaq."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Siz <xliff:g id="NUMBER">%d</xliff:g> dəfə TV-nizin kilidini açmaq üçün səhv cəhdlər etdiniz. TV indi standart parametrlərə bərpa olunacaqdır."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Siz telefonun kilidini açmaq üçün <xliff:g id="NUMBER">%d</xliff:g> dəfə səhv cəhd etdiniz. Telefonunuz indi zavod nizamlarına yenilənəcək."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"söz"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"link"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"xətt"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Zavod testi alınmadı"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Bu FACTORY_TEST fəaliyyəti yalnızca/sistemdə/tətbiqdə quraşdırılmış paketlər üçün dəstəklənir."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"FACTORY_TEST əməliyyatını təsdiqləyən heç bir paket tapılmadı."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Format edilir..."</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Daxil edilməyib"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Uyğun gələn fəaliyyət tapılmadı."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Media çıxışını yönləndirir"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"media çıxışını yönləndirin"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Tətbiqə media çıxışını digər xarici cihazlara yönləndirmək imkanı verir."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Quraşdırma sessiyalarını oxuyun"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"quraşdırma sessiyalarını oxuyun"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Tətbiqə quraşdırma sessiyalarını oxumağa yardım edir. Bu da aktiv paket quraşdırmaları haqqında məlumatları görməyə imkan verir."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Pektləri quraşdırma sorğusu"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"paketləri quraşdırma sorğusu"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Tətbiqə paketləri quraşdırma sorğusu göndərməyə icazə verir."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Zoom nəzarəti üçün iki dəfə toxunun"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Widget əlavə edilə bilmədi."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Yanlış istifadəçi adı və ya parol."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"İstifadəçi adınızı və ya parolunuzu unutmusunuz?\n "<b>"google.com/accounts/recovery"</b>" linkinə daxil olun."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Hesab yoxlanılır..."</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"PIN kodunuzu <xliff:g id="NUMBER_0">%d</xliff:g> dəfə yanlış daxil etdiniz.\n\n <xliff:g id="NUMBER_1">%d</xliff:g> saniyə ərzində yenidən yoxlayın"</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Şifrənizi <xliff:g id="NUMBER_0">%d</xliff:g> dəfə yanlış daxil etdiniz. \n\n <xliff:g id="NUMBER_1">%d</xliff:g> saniyə ərzində yenidən yoxlayın."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Modelinizi <xliff:g id="NUMBER_0">%d</xliff:g> dəfə yanlış çəkmisiniz.\n\n <xliff:g id="NUMBER_1">%d</xliff:g> saniyə ərzində yenidən yoxlayın"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Siz planşet kilidini açmaq üçün <xliff:g id="NUMBER_0">%d</xliff:g> dəfə uğursuz cəhd etmisiniz. <xliff:g id="NUMBER_1">%d</xliff:g> dəfə də uğursuz cəhd etsəniz, planşet fabrik ayarlarına sıfırlanacaq və bütün məlumatlarınız itəcək."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Siz <xliff:g id="NUMBER_0">%d</xliff:g> dəfə TV-nizin kilidini açmaq üçün səhv cəhdlər etdiniz. Daha <xliff:g id="NUMBER_1">%d</xliff:g> uğursuz cəhddən sonra TV standart parametrlərə bərpa olunacaq və bütün istifadəçi məlumatları itəcəkdir."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Siz telefon kilidini açmaq üçün <xliff:g id="NUMBER_0">%d</xliff:g> dəfə uğursuz cəhd etmisiniz. <xliff:g id="NUMBER_1">%d</xliff:g> dəfə də uğursuz cəhd etsəniz, telefon fabrik ayarlarına sıfırlanacaq və bütün məlumatlarınız itəcək."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"PIN kodunuzu <xliff:g id="NUMBER_0">%1$d</xliff:g> dəfə yanlış daxil etdiniz.\n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> saniyə ərzində yenidən yoxlayın"</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Şifrənizi <xliff:g id="NUMBER_0">%1$d</xliff:g> dəfə yanlış daxil etdiniz. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> saniyə ərzində yenidən yoxlayın."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Modelinizi <xliff:g id="NUMBER_0">%1$d</xliff:g> dəfə yanlış çəkmisiniz.\n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> saniyə ərzində yenidən yoxlayın"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Siz planşet kilidini açmaq üçün <xliff:g id="NUMBER_0">%1$d</xliff:g> dəfə uğursuz cəhd etmisiniz. <xliff:g id="NUMBER_1">%2$d</xliff:g> dəfə də uğursuz cəhd etsəniz, planşet fabrik ayarlarına sıfırlanacaq və bütün məlumatlarınız itəcək."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Siz <xliff:g id="NUMBER_0">%1$d</xliff:g> dəfə TV-nizin kilidini açmaq üçün səhv cəhdlər etdiniz. Daha <xliff:g id="NUMBER_1">%2$d</xliff:g> uğursuz cəhddən sonra TV standart parametrlərə bərpa olunacaq və bütün istifadəçi məlumatları itəcəkdir."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Siz telefon kilidini açmaq üçün <xliff:g id="NUMBER_0">%1$d</xliff:g> dəfə uğursuz cəhd etmisiniz. <xliff:g id="NUMBER_1">%2$d</xliff:g> dəfə də uğursuz cəhd etsəniz, telefon fabrik ayarlarına sıfırlanacaq və bütün məlumatlarınız itəcək."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Siz planşet kilidini açmaq üçün <xliff:g id="NUMBER">%d</xliff:g> dəfə uğursuz cəhd etmisiniz. Planşet fabrik ayarlarına sıfırlanacaq."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Siz <xliff:g id="NUMBER">%d</xliff:g> dəfə TV-nizin kilidini açmaq üçün səhv cəhdlər etdiniz. TV indi standart parametrlərə bərpa olunacaqdır."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Siz telefonun kilidini açmaq üçün <xliff:g id="NUMBER">%d</xliff:g> yanlış cəhd etmisiniz. Telefon artıq defolt zavod halına sıfırlanacaq."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Siz kilidi açmaq üçün şablonu <xliff:g id="NUMBER_0">%d</xliff:g> dəfə səhv çəkdiniz. <xliff:g id="NUMBER_1">%d</xliff:g> daha uğursuz cəhddən sonra planşetinizin kilidini e-poçt hesabınızla açmaq tələb olunacaq.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> saniyə ərzində bir daha yoxlayın."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Siz kilidaçma nümunənizi <xliff:g id="NUMBER_0">%d</xliff:g> dəfə səhv daxil etdiniz. Daha <xliff:g id="NUMBER_1">%d</xliff:g> uğursuz cəhddən sonra sizdən e-poçt hesabından istifadə etməklə TV-nizin kilidini açmaq soruşulacaqdır.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> saniyəyə yenidən cəhd edin."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Siz artıq modeli <xliff:g id="NUMBER_0">%d</xliff:g> dəfə yanlış daxil etmisiniz.<xliff:g id="NUMBER_1">%d</xliff:g> dəfə də yanlış daxil etsəniz, telefonun kilidinin açılması üçün elektron poçt ünvanınız tələb olunacaq.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> saniyə ərzində yenidən cəhd edin."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Siz kilidi açmaq üçün şablonu <xliff:g id="NUMBER_0">%1$d</xliff:g> dəfə səhv çəkdiniz. <xliff:g id="NUMBER_1">%2$d</xliff:g> daha uğursuz cəhddən sonra planşetinizin kilidini e-poçt hesabınızla açmaq tələb olunacaq.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> saniyə ərzində bir daha yoxlayın."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Siz kilidaçma nümunənizi <xliff:g id="NUMBER_0">%1$d</xliff:g> dəfə səhv daxil etdiniz. Daha <xliff:g id="NUMBER_1">%2$d</xliff:g> uğursuz cəhddən sonra sizdən e-poçt hesabından istifadə etməklə TV-nizin kilidini açmaq soruşulacaqdır.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> saniyəyə yenidən cəhd edin."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Siz artıq modeli <xliff:g id="NUMBER_0">%1$d</xliff:g> dəfə yanlış daxil etmisiniz.<xliff:g id="NUMBER_1">%2$d</xliff:g> dəfə də yanlış daxil etsəniz, telefonun kilidinin açılması üçün elektron poçt ünvanınız tələb olunacaq.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> saniyə ərzində yenidən cəhd edin."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" - "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Yığışdır"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Səsin həcmi tövsiyə olunan səviyyədən artıq olsun?\n\nYüksək səsi uzun zaman dinləmək eşitmə qabiliyyətinizə zərər vura bilər."</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">1 saat üçün</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Saat <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> qədər"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> radəsinə qədər (növbəti siqnal)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Bunu söndürənə kimi"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"\"Narahat etməyin\" seçiminini deaktiv edənə kimi"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-bg-watch/strings.xml b/core/res/res/values-bg-watch/strings.xml
index 5eb2a02..1cd56eb 100644
--- a/core/res/res/values-bg-watch/strings.xml
+++ b/core/res/res/values-bg-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Прилож. <xliff:g id="NUMBER_0">%1$d</xliff:g> от <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Сензори"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"достъп до контактите ви"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"достъп до местоположението на този часовник"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"достъп до календара ви"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"изпращане и преглед на SMS съобщения"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"достъп до снимките, мултимедията и файловете на часовника ви"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"записване на звук"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"правене на снимки и записване на видеоклипове"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"извършване и управление на телефонни обаждания"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"достъп до сензорните данни за жизнените ви показатели"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"изпълняване на ролята на лента на състоянието"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"достъп до сензорите за тяло (например пулсомери)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"достъп до точното местоположение (въз основа на GPS и мрежата)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"достъп до приблизителното местоположение (въз основа на мрежата)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"изпращане на команди до SIM картата"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"пълен достъп до мрежата"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"управление на собствениците на потребителските профили и устройствата"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"промяна на състоянието на WiMAX мрежата"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"получаване на състоянието на прехвърлянията чрез Android Лъч"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"маршрутизиране на извеждането на мултимедия"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"четене на сесии за инсталиране"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"заявка на пакети за инсталиране"</string>
</resources>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 5427659..fcab6d0 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Включва лични данни, като например номера на кредитни карти и пароли."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"деактивиране или промяна на лентата на състоянието"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Разрешава на приложението да деактивира лентата на състоянието или да добавя и премахва системни икони."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"лента на състоянието"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"изпълняване на ролята на лента на състоянието"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Разрешава на приложението да бъде лентата на състоянието."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"разгъване или свиване на лентата на състоянието"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Разрешава на приложението да разгъва или свива лентата на състоянието."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Разрешава на приложението да получава и обработва WAP съобщения. Това разрешение включва възможността да наблюдава или изтрива изпратените до вас, без да ви ги покаже."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"извличане на изпълняваните приложения"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Разрешава на приложението да извлича информация за задачите, изпълнявани понастоящем и неотдавна. Това може да му позволи да открива данни за това, кои приложения се използват на устройството."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Управление на собствениците на потребителските профили и на устройствата"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"управление на собствениците на потребителските профили и устройствата"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Разрешава на приложенията да задават собствениците на потребителските профили и собственика на устройствата."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"пренареждане на изпълняваните приложения"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Разрешава на приложението да прехвърля задачи на преден и на заден план. То може да направи това без вашето потвърждение."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Разрешава на приложението да променя списъка с обаждания на таблета ви, включително данните за входящите и изходящите обаждания. Злонамерените приложения могат да използват това, за да изтрият или променят този списък."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Разрешава на приложението да променя списъка с обажданията на телевизора ви, включително данните за входящите и изходящите повиквания. Злонамерените приложения може да използват това, за да изтрият или променят списъка с обажданията ви."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Разрешава на приложението да променя списъка с обаждания на телефона ви, включително данните за входящите и изходящите обаждания. Злонамерените приложения могат да използват това, за да изтрият или променят този списък."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"телесни сензори (като монитори за сърдечния ритъм)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"достъп до телесните сензори (напр. пулсомери)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Разрешава на приложението да осъществява достъп до данните от сензорите, които следят физическото ви състояние, като например сърдечния ви ритъм."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"четене на събития от календари плюс поверителна информация"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Разрешава на приложението да чете всички събития от календари, съхранени на таблета ви, включително тези на приятели или колеги. Това може да му позволи да споделя или запазва данните от календара ви независимо от поверителността."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Разрешава на приложението да добавя, премахва и променя събития, които можете да променяте на телефона си, включително тези на приятели или колеги. Това може да му позволи да изпраща съобщения, които изглежда, че идват от собствениците на календарите, или да променя събития без знанието на собствениците."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"достъп до допълнителни команди на доставчика на местоположение"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Разрешава на приложението достъп до допълнителни команди на доставчика на местоположение. Това може да позволи на приложението да смущава работата на GPS или на другите източници на местоположение."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"точно местоположение (основано на GPS и мрежата)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"достъп до точното местоположение (въз основа на GPS и мрежата)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Разрешава на приложението да получи точното ви местоположение посредством системата GPS или съответните мрежови източници, като клетъчни кули и Wi-Fi. Тези услуги за местоположение трябва да са включени и налице на устройството ви, за да могат да се използват от приложението. Приложенията може да ползват това, за да определят къде се намирате, и да изразходват повече енергия от батерията."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"приблизително местоположение (основано на мрежата)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"достъп до приблизителното местоположение (въз основа на мрежата)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Разрешава на приложението да получи приблизителното ви местоположение. То се извлича от услугите за местоположение посредством съответните мрежови източници, като клетъчни кули и Wi-Fi. Тези услуги трябва да са включени и налице на устройството ви, за да могат да се използват от приложението. Приложенията може да ползват това, за да определят къде приблизително се намирате."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"промяна на настройките ви за звука"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Разрешава на приложението да променя глобалните настройки за звука, като например силата и това, кой високоговорител се използва за изход."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"запис на звук"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Разрешава на приложението да записва звук с микрофона. Това разрешение му позволява да го прави по всяко време без потвърждение от ваша страна."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"комуникация със SIM картата"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"изпращане на команди до SIM картата"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Разрешава на приложението да изпраща команди до SIM картата. Това е много опасно."</string>
<string name="permlab_camera" msgid="3616391919559751192">"правене на снимки и видеоклипове"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Разрешава на приложението да прави снимки и видеоклипове с камерата. Това разрешение му позволява да я използва по всяко време без потвърждение от ваша страна."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Разрешава на приложението да получава списъка с профили, известни на телефона. Това може да включва и създадените от инсталирани от вас приложения."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"преглед на връзките с мрежата"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Разрешава на приложението да вижда информация за връзките с мрежата, като например съществуващите и свързаните мрежи."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"пълен достъп до мрежата"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"пълен достъп до мрежата"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Разрешава на приложението да създава мрежови сокети и да използва персонализирани мрежови протоколи. Браузърът и другите приложения предоставят средства за изпращане на данни до интернет, така че това разрешение не се изисква за тази цел."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"промяна на връзката с мрежата"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Разрешава на приложението да променя състоянието на връзката с мрежата."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Разрешава на приложението да конфигурира локалния телефон с Bluetooth, както и да открива и да се сдвоява с отдалечени устройства."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"установяване и прекратяване на връзката с WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Разрешава на приложението да определя дали WiMAX мрежата е активирана, както и информация за всички такива мрежи, които са свързани."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Промяна на състоянието на WiMAX мрежата"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"промяна на състоянието на WiMAX мрежата"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Разрешава на приложението да свързва таблета към WiMAX мрежа и да прекратява връзката му с нея."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Разрешава на приложението да установява и прекратява връзката на телевизора с WiMAX мрежи."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Разрешава на приложението да свързва телефона към WiMAX мрежа и да прекратява връзката му с нея."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Разрешава на приложението да променя параметрите на калибриране на сензорния екран. Нормалните приложения би трябвало никога да не се нуждаят от това."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"достъп до сертификатите за управление на цифровите права (DRM)"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Разрешава на приложението да обезпечава и използва сертификатите за управление на цифровите права (DRM). Нормалните приложения би трябвало никога да не се нуждаят от това."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Получаване на състоянието на прехвърлянията чрез Android Лъч"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"получаване на състоянието на прехвърлянията чрез Android Лъч"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Разрешава на това приложение да получава информация относно текущите прехвърляния чрез Android Лъч"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"премахване на сертификатите за управление на цифровите права (DRM)"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Разрешава на приложението да премахва сертификатите за управление на цифровите права (DRM). Нормалните приложения би трябвало никога да се нуждаят от това."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Неправилен ПИН код."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"За да отключите, натиснете „Меню“ и после 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Спешен номер"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Няма покритие."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Няма покритие"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Екранът е заключен."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Натиснете „Меню“, за да отключите или да извършите спешно обаждане."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Натиснете „Меню“, за да отключите."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Вижте ръководството за потребителя или се свържете с отдела за поддръжка на клиенти."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM картата е заключена."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"SIM картата се отключва..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Начертахте неправилно фигурата си за отключване <xliff:g id="NUMBER_0">%d</xliff:g> пъти. \n\nОпитайте отново след <xliff:g id="NUMBER_1">%d</xliff:g> секунди."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Въведохте неправилно паролата си <xliff:g id="NUMBER_0">%d</xliff:g> пъти. \n\nОпитайте отново след <xliff:g id="NUMBER_1">%d</xliff:g> секунди."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Въведохте неправилно ПИН кода си <xliff:g id="NUMBER_0">%d</xliff:g> пъти. \n\nОпитайте отново след <xliff:g id="NUMBER_1">%d</xliff:g> секунди."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Начертахте неправилно фигурата си за отключване <xliff:g id="NUMBER_0">%d</xliff:g> пъти. След още <xliff:g id="NUMBER_1">%d</xliff:g> неуспешни опита ще бъдете помолени да отключите таблета посредством данните си за вход в Google.\n\nОпитайте отново след <xliff:g id="NUMBER_2">%d</xliff:g> секунди."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Неправилно начертахте фигурата си за отключване <xliff:g id="NUMBER_0">%d</xliff:g> пъти. След още <xliff:g id="NUMBER_1">%d</xliff:g> неуспешни опита ще бъдете помолени да отключите телевизора си посредством данните си за вход в Google.\n\n Опитайте отново след <xliff:g id="NUMBER_2">%d</xliff:g> секунди."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Начертахте неправилно фигурата си за отключване <xliff:g id="NUMBER_0">%d</xliff:g> пъти. След още <xliff:g id="NUMBER_1">%d</xliff:g> неуспешни опита ще бъдете помолени да отключите телефона посредством данните си за вход в Google.\n\n Опитайте отново след <xliff:g id="NUMBER_2">%d</xliff:g> секунди."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Направихте опит да отключите таблета неправилно <xliff:g id="NUMBER_0">%d</xliff:g> пъти. След още <xliff:g id="NUMBER_1">%d</xliff:g> неуспешни опита ще бъдат възстановени стандартните му фабрични настройки и всички потребителски данни ще бъдат заличени."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Опитахте да отключите телевизора <xliff:g id="NUMBER_0">%d</xliff:g> пъти. След още <xliff:g id="NUMBER_1">%d</xliff:g> неуспешни опита ще бъдат възстановени фабричните му настройки и всички потребителски данни ще бъдат заличени."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Направихте опит да отключите телефона неправилно <xliff:g id="NUMBER_0">%d</xliff:g> пъти. След още <xliff:g id="NUMBER_1">%d</xliff:g> неуспешни опита ще бъдат възстановени стандартните му фабрични настройки и всички потребителски данни ще бъдат заличени."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Начертахте неправилно фигурата си за отключване <xliff:g id="NUMBER_0">%1$d</xliff:g> пъти. \n\nОпитайте отново след <xliff:g id="NUMBER_1">%2$d</xliff:g> секунди."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Въведохте неправилно паролата си <xliff:g id="NUMBER_0">%1$d</xliff:g> пъти. \n\nОпитайте отново след <xliff:g id="NUMBER_1">%2$d</xliff:g> секунди."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Въведохте неправилно ПИН кода си <xliff:g id="NUMBER_0">%1$d</xliff:g> пъти. \n\nОпитайте отново след <xliff:g id="NUMBER_1">%2$d</xliff:g> секунди."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Начертахте неправилно фигурата си за отключване <xliff:g id="NUMBER_0">%1$d</xliff:g> пъти. След още <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешни опита ще бъдете помолени да отключите таблета посредством данните си за вход в Google.\n\nОпитайте отново след <xliff:g id="NUMBER_2">%3$d</xliff:g> секунди."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Неправилно начертахте фигурата си за отключване <xliff:g id="NUMBER_0">%1$d</xliff:g> пъти. След още <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешни опита ще бъдете помолени да отключите телевизора си посредством данните си за вход в Google.\n\n Опитайте отново след <xliff:g id="NUMBER_2">%3$d</xliff:g> секунди."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Начертахте неправилно фигурата си за отключване <xliff:g id="NUMBER_0">%1$d</xliff:g> пъти. След още <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешни опита ще бъдете помолени да отключите телефона посредством данните си за вход в Google.\n\n Опитайте отново след <xliff:g id="NUMBER_2">%3$d</xliff:g> секунди."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Направихте опит да отключите таблета неправилно <xliff:g id="NUMBER_0">%1$d</xliff:g> пъти. След още <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешни опита ще бъдат възстановени стандартните му фабрични настройки и всички потребителски данни ще бъдат заличени."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Опитахте да отключите телевизора <xliff:g id="NUMBER_0">%1$d</xliff:g> пъти. След още <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешни опита ще бъдат възстановени фабричните му настройки и всички потребителски данни ще бъдат заличени."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Направихте опит да отключите телефона неправилно <xliff:g id="NUMBER_0">%1$d</xliff:g> пъти. След още <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешни опита ще бъдат възстановени стандартните му фабрични настройки и всички потребителски данни ще бъдат заличени."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Направихте опит да отключите таблета неправилно <xliff:g id="NUMBER">%d</xliff:g> пъти. Сега ще бъдат възстановени стандартните му фабрични настройки."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Направихте <xliff:g id="NUMBER">%d</xliff:g> неуспешни опита за отключване на телевизора. Сега ще бъдат възстановени фабричните му настройки."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Направихте опит да отключите телефона неправилно <xliff:g id="NUMBER">%d</xliff:g> пъти. Сега ще бъдат възстановени стандартните му фабрични настройки."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"дума"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"връзка"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"ред"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Фабричният тест не бе успешен"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Действието FACTORY_TEST се поддържа само за пакети, инсталирани в /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Не бе намерен пакет, предоставящ действието FACTORY_TEST."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Форматира се…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Не е поставено"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Не бяха намерени съответстващи дейности."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Насочване на изходящата мултимедия"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"маршрутизиране на извеждането на мултимедия"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Разрешава на приложението да насочва изходящата мултимедия към други външни устройства."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Четене на сесии за инсталиране"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"четене на сесии за инсталиране"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Разрешава на приложението да чете сесии за инсталиране. Това му позволява да вижда подробности за активните инсталирания на пакети."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Заявка за инсталиране на пакети"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"заявка на пакети за инсталиране"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Разрешава на приложението да заявява инсталиране на пакети."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Докоснете двукратно за управление на промяната на мащаба"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Приспособлението не можа да бъде добавено."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Невалидно потребителско име или парола."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Забравили сте потребителското име или паролата си?\nПосетете "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Профилът се проверява…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Въведохте неправилно ПИН кода си <xliff:g id="NUMBER_0">%d</xliff:g> пъти. \n\nОпитайте отново след <xliff:g id="NUMBER_1">%d</xliff:g> секунди."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Въведохте неправилно паролата си <xliff:g id="NUMBER_0">%d</xliff:g> пъти. \n\nОпитайте отново след <xliff:g id="NUMBER_1">%d</xliff:g> секунди."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Начертахте неправилно фигурата си за отключване <xliff:g id="NUMBER_0">%d</xliff:g> пъти. \n\nОпитайте отново след <xliff:g id="NUMBER_1">%d</xliff:g> секунди."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Направихте опит да отключите неправилно таблета <xliff:g id="NUMBER_0">%d</xliff:g> пъти. След още <xliff:g id="NUMBER_1">%d</xliff:g> неуспешни опита ще бъдат възстановени стандартните му фабрични настройки и всички потребителски данни ще бъдат заличени."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Опитахте да отключите телевизора <xliff:g id="NUMBER_0">%d</xliff:g> пъти. След още <xliff:g id="NUMBER_1">%d</xliff:g> неуспешни опита ще бъдат възстановени фабричните му настройки и всички потребителски данни ще бъдат заличени."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Направихте опит да отключите неправилно телефона <xliff:g id="NUMBER_0">%d</xliff:g> пъти. След още <xliff:g id="NUMBER_1">%d</xliff:g> неуспешни опита ще бъдат възстановени стандартните му фабрични настройки и всички потребителски данни ще бъдат заличени."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Въведохте неправилно ПИН кода си <xliff:g id="NUMBER_0">%1$d</xliff:g> пъти. \n\nОпитайте отново след <xliff:g id="NUMBER_1">%2$d</xliff:g> секунди."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Въведохте неправилно паролата си <xliff:g id="NUMBER_0">%1$d</xliff:g> пъти. \n\nОпитайте отново след <xliff:g id="NUMBER_1">%2$d</xliff:g> секунди."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Начертахте неправилно фигурата си за отключване <xliff:g id="NUMBER_0">%1$d</xliff:g> пъти. \n\nОпитайте отново след <xliff:g id="NUMBER_1">%2$d</xliff:g> секунди."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Направихте опит да отключите неправилно таблета <xliff:g id="NUMBER_0">%1$d</xliff:g> пъти. След още <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешни опита ще бъдат възстановени стандартните му фабрични настройки и всички потребителски данни ще бъдат заличени."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Опитахте да отключите телевизора <xliff:g id="NUMBER_0">%1$d</xliff:g> пъти. След още <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешни опита ще бъдат възстановени фабричните му настройки и всички потребителски данни ще бъдат заличени."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Направихте опит да отключите неправилно телефона <xliff:g id="NUMBER_0">%1$d</xliff:g> пъти. След още <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешни опита ще бъдат възстановени стандартните му фабрични настройки и всички потребителски данни ще бъдат заличени."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Направихте опит да отключите неправилно таблета <xliff:g id="NUMBER">%d</xliff:g> пъти. Сега ще бъдат възстановени стандартните му фабрични настройки."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Направихте <xliff:g id="NUMBER">%d</xliff:g> неуспешни опита за отключване на телевизора. Сега ще бъдат възстановени фабричните му настройки."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Направихте опит да отключите неправилно телефона <xliff:g id="NUMBER">%d</xliff:g> пъти. Сега ще бъдат възстановени стандартните му фабрични настройки."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Начертахте неправилно фигурата си за отключване <xliff:g id="NUMBER_0">%d</xliff:g> пъти. След още <xliff:g id="NUMBER_1">%d</xliff:g> неуспешни опита ще бъдете помолени да отключите таблета посредством имейл адрес.\n\n Опитайте отново след <xliff:g id="NUMBER_2">%d</xliff:g> секунди."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Неправилно начертахте фигурата си за отключване <xliff:g id="NUMBER_0">%d</xliff:g> пъти. След още <xliff:g id="NUMBER_1">%d</xliff:g> неуспешни опита ще бъдете помолени да отключите телевизора си посредством имейл адрес.\n\n Опитайте отново след <xliff:g id="NUMBER_2">%d</xliff:g> секунди."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Начертахте неправилно фигурата си за отключване <xliff:g id="NUMBER_0">%d</xliff:g> пъти. След още <xliff:g id="NUMBER_1">%d</xliff:g> неуспешни опита ще бъдете помолени да отключите телефона посредством имейл адрес.\n\n Опитайте отново след <xliff:g id="NUMBER_2">%d</xliff:g> секунди."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Начертахте неправилно фигурата си за отключване <xliff:g id="NUMBER_0">%1$d</xliff:g> пъти. След още <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешни опита ще бъдете помолени да отключите таблета посредством имейл адрес.\n\n Опитайте отново след <xliff:g id="NUMBER_2">%3$d</xliff:g> секунди."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Неправилно начертахте фигурата си за отключване <xliff:g id="NUMBER_0">%1$d</xliff:g> пъти. След още <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешни опита ще бъдете помолени да отключите телевизора си посредством имейл адрес.\n\n Опитайте отново след <xliff:g id="NUMBER_2">%3$d</xliff:g> секунди."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Начертахте неправилно фигурата си за отключване <xliff:g id="NUMBER_0">%1$d</xliff:g> пъти. След още <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешни опита ще бъдете помолени да отключите телефона посредством имейл адрес.\n\n Опитайте отново след <xliff:g id="NUMBER_2">%3$d</xliff:g> секунди."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Премахване"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Да се увеличи ли силата на звука над препоръчителното ниво?\n\nПродължителното слушане при висока сила на звука може да увреди слуха ви."</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">За 1 ч</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"До следващия будилник (<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Докато не изключите това"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Докато не изключите „Не безпокойте“"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-bn-rBD-watch/strings.xml b/core/res/res/values-bn-rBD-watch/strings.xml
index 4ef2d6ea..f68e824 100644
--- a/core/res/res/values-bn-rBD-watch/strings.xml
+++ b/core/res/res/values-bn-rBD-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_1">%2$d</xliff:g>টির মধ্যে <xliff:g id="NUMBER_0">%1$d</xliff:g>টি অ্যাপ্লিকেশান"</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"সেন্সরগুলি"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"আপনার পরিচিতিগুলিতে অ্যাক্সেস করুন"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"এই ঘড়ির অবস্থানে অ্যাক্সেস করুন"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"আপনার ক্যালেন্ডারে অ্যাক্সেস করুন"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"SMS বার্তাগুলি পাঠান এবং দেখুন"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"আপনার দেখা ফটো, মিডিয়া এবং ফাইলগুলিতে অ্যাক্সেস করুন"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"অডিও রেকর্ড করুন"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"ছবি তুলুন এবং ভিডিও রেকর্ড করুন"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"ফোন কলগুলি করুন এবং পরিচালনা করুন"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"আপনার গুরুত্বপূর্ণ লক্ষণ এবং শারীরিক ক্রিয়াকলাপের সম্পর্কে তথ্য অ্যাক্সেস করুন"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"স্থিতি দন্ডে থাকুন"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"শরীরের সেন্সর (হার্ট রেট মনিটারের মত) অ্যাক্সেস করুন"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"সুনির্দিষ্ট অবস্থান (GPS এবং নেটওয়ার্ক-ভিত্তিক) অ্যাক্সেস করুন"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"আনুমানিক অবস্থান (নেটওয়ার্ক-ভিত্তিক) অ্যাক্সেস করুন"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"SIM এ আদেশগুলি পাঠান"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"সম্পূর্ণ নেটওয়ার্ক অ্যাক্সেস রয়েছে"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"প্রোফাইল এবং ডিভাইস মালিকদের পরিচালনা করুন"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"WiMAX এর স্থিতি পরিবর্তন করুন"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"Android বীম স্থানান্তর স্থিতি গ্রহণ করুন"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"মিডিয়া আউটপুট রুট করুন"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"ইনস্টল সেশন পড়ুন"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"প্যাকেজগুলি ইনস্টল করার অনুরোধ"</string>
</resources>
diff --git a/core/res/res/values-bn-rBD/strings.xml b/core/res/res/values-bn-rBD/strings.xml
index 2d847ee..d66c249 100644
--- a/core/res/res/values-bn-rBD/strings.xml
+++ b/core/res/res/values-bn-rBD/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ক্রেডিট কার্ডের নম্বর ও পাসওয়ার্ডগুলির মতো ব্যক্তিগত তথ্য অন্তর্ভুক্ত করে৷"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"স্থিতি দন্ড নিষ্ক্রিয় অথবা সংশোধন করে"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"অ্যাপ্লিকেশানকে স্থিতি দন্ড অক্ষম করতে এবং সিস্টেম আইকনগুলি সরাতে দেয়৷"</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"স্থিতি দন্ড"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"স্থিতি দন্ডে থাকুন"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"অ্যাপ্লিকেশানটিকে স্থিতি দন্ডে থাকতে দেয়৷"</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"স্থিতি দন্ড সম্প্রসারিত/সঙ্কুচিত করে"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"অ্যাপ্লিকেশানটিকে স্থিতি দন্ড প্রসারিত বা সঙ্কুচিত করতে দেয়৷"</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"অ্যাপ্লিকেশানটিকে WAP বার্তা প্রাপ্ত করার এবং প্রক্রিয়া করার অনুমতি দেয়৷ এর মানে হল অ্যাপ্লিকেশানটি আপনার ডিভাইস থেকে পাঠানো বার্তাগুলিকে পর্যবেক্ষণ করতে পারে এবং মুছতে পারে সেগুলিকে আপনাকে না দেখিয়ে৷"</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"চলমান অ্যাপ্লিকেশান উদ্ধার করে"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"বর্তমানে ও সাম্প্রতিককালের সক্রিয় ক্রিয়াগুলি সম্বন্ধে তথ্য পুনরুদ্ধার করতে অ্যাপ্লিকেশানটিকে মঞ্জুর করে৷ এছাড়া এটি ডিভাইসটিতে কোন অ্যাপ্লিকেশানগুলি ব্যবহৃত হচ্ছে তার বিষয়ে তথ্য খুঁজে বের করতে অ্যাপ্লিকেশানটিকে মঞ্জুর করতে পারে৷"</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"প্রোফাইল এবং ডিভাইস মালিকদের পরিচালনা করুন"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"প্রোফাইল এবং ডিভাইস মালিকদের পরিচালনা করুন"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"অ্যাপ্লিকেশানগুলিকে প্রোফাইলের মালিকদের এবং ডিভাইসের মালিককে সেট করার অনুমতি দেয়৷"</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"চলমান অ্যাপ্লিকেশান পুনর্বিন্যাস করে"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"অ্যাপ্লিকেশানটিকে কার্যগুলিকে পুরোভাগে এবং পশ্চাদপটে সরানোর অনুমতি দেয়৷ অ্যাপ্লিকেশানটি আপনার ইনপুট ছাড়া এটি করতে পারে৷"</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"ইনকামিং ও আউটগোয়িং কলগুলি সম্পর্কিত ডেটা সহ আপনার ট্যাবলেটের কল লগ পরিবর্তন করতে দেয়৷ ক্ষতিকারক অ্যাপ্লিকেশানগুলি এটিকে আপনার কল লগ মুছে দিতে বা পরিবর্তন করতে ব্যবহার করতে পারে৷"</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"ইনকামিং ও আউটগোয়িং কলগুলি সম্পর্কিত ডেটা সহ আপনার টিভির কল লগ পরিবর্তন করতে দেয়৷ ক্ষতিকারক অ্যাপ্লিকেশানগুলি এটিকে আপনার কল লগ মুছে দিতে বা পরিবর্তন করতে ব্যবহার করতে পারে৷"</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"ইনকামিং ও আউটগোয়িং কলগুলি সম্পর্কিত ডেটা সহ আপনার ফোনের কল লগ পরিবর্তন করতে দেয়৷ ক্ষতিকারক অ্যাপ্লিকেশানগুলি এটিকে আপনার কল লগ মুছে দিতে বা পরিবর্তন করতে ব্যবহার করতে পারে৷"</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"শরীরের সেন্সর (হার্ট রেট মনিটারের মত)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"শরীরের সেন্সর (হার্ট রেট মনিটারের মত) অ্যাক্সেস করুন"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"অ্যাপ্লিকেশানটিকে আপনার শারীরিক অবস্থা যেমন, আপনার হৃৎস্পন্দন পর্যবেক্ষণ করে এমন সেন্সরগুলি অ্যাক্সেস করতে মঞ্জুরি দেয়।"</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"ক্যালেন্ডার ইভেন্ট, তার সাথে গোপন তথ্যও পড়ে"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"আপনার ট্যাবলেটে সঞ্চিত সমস্ত ক্যালেন্ডার ইভেন্ট পড়তে অ্যাপ্লিকেশানটিকে মঞ্জুর করে, এর মধ্যে বন্ধু ও সহকর্মীদেরগুলিও অন্তর্ভুক্ত৷ এটি গোপনীয়তা বা সংবেদনশীলতা নির্বিশেষে আপনার ক্যালেন্ডার ডেটা ভাগ ও সংরক্ষণ করতে অ্যাপ্লিকেশানটিকে মঞ্জুর করতে পারে৷"</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"সেইসকল বন্ধু বা সহকর্মী সহ আপনি আপনার ফোনে যে ইভেন্টগুলি সংশোধন করতে পারেন তা যুক্ত করাতে, সরাতে, পরিবর্তন করতে এই অ্যাপ্লিকেশানটিকে অনুমতি দেয়৷ এটি অ্যাপ্লিকেশানটিকে বার্তা পাঠাতে দেয় যা দেখে মনে হবে যে এটি ক্যালেন্ডার মালিকদের থেকে এসেছে অথবা মালিককে না জানিয়ে ইভেন্টগুলি পরিবর্তন করতে দিতে পারে৷"</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"অতিরিক্ত অবস্থান প্রদানকারী কমান্ডগুলি অ্যাক্সেস করে"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"অবস্থানের সাথে সম্পর্কিত তথ্য প্রদানকারীর অতিরিক্ত কম্যান্ডগুলিকে অ্যাপ্লিকেশানটিকে মঞ্জুর করে৷ এটি অ্যাপ্লিকেশানটিকে GPS অথবা অন্যান্য অবস্থান নির্ণয়ের সাথে সম্পর্কিত উৎসগুলির ক্রিয়াপ্রণালীর নিয়ন্ত্রণকে মঞ্জুর করতে পারে৷"</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"সুনির্দিষ্ট অবস্থান (GPS এবং নেটওয়ার্ক ভিত্তিক)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"সুনির্দিষ্ট অবস্থান (GPS এবং নেটওয়ার্ক-ভিত্তিক) অ্যাক্সেস করুন"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"গ্লোবাল পজিশনিং সিস্টেম (GPS) অথবা সেল টাওয়ার ও Wi-Fi এর মতো নেটওয়ার্কের অবস্থান উৎসগুলি ব্যবহার করে আপনার যথাযথ অবস্থান নির্ণয় করতে অ্যাপ্লিকেশানটিকে মঞ্জুর করে৷ এই অবস্থান নির্ণয়ের সাথে সম্পর্কিত পরিষেবাগুলিকে চালু করে রাখতে হবে এবং অ্যাপ্লিকেশানটি যাতে সেগুলি ব্যবহার করতে পারে সেজন্য সেগুলিকে আপনার ডিভাইসে উপলব্ধ করে রাখতে হবে৷ অ্যাপ্লিকেশানগুলি আপনার অবস্থান নির্ণয়ের কাজে এগুলির ব্যবহার করতে পারে, এবং এর জন্য অতিরিক্ত ব্যাটারি পাওয়ার লাগতে পারে৷"</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"আনুমানিক অবস্থান (নেটওয়ার্ক ভিত্তিক)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"আনুমানিক অবস্থান (নেটওয়ার্ক-ভিত্তিক) অ্যাক্সেস করুন"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"আপনার আনুমানিক অবস্থান নির্ণয় করতে অ্যাপ্লিকেশানটিকে অনুমোদিত করে৷ এই অবস্থান নির্ণয় সেল টাওয়ার ও Wi-Fi এর মতো নেটওয়ার্কের অবস্থান উৎসগুলি ব্যবহার করে অবস্থান নির্ধারণের সাথে সম্পর্কিত পরিষেবাগুলি থেকে নেওয়া হয়ে থাকে৷ এই অবস্থান নির্ণয়ের সাথে সম্পর্কিত পরিষেবাগুলিকে চালু করে রাখতে হবে এবং অ্যাপ্লিকেশানটি যাতে সেগুলি ব্যবহার করতে পারে সেজন্য সেগুলিকে আপনার ডিভাইসে উপলব্ধ করে রাখতে হবে৷ অ্যাপ্লিকেশানগুলি আপনার আনুমানিক অবস্থান নির্ণয়ের কাজে এগুলির ব্যবহার করতে পারে৷"</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"আপনার অডিও সেটিংস পরিবর্তন করে"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"ভলিউম এবং যেখানে স্পিকার আউটপুট সামগ্রী হিসাবে ব্যবহৃত হয় সেই সব ক্ষেত্রে গ্লোবাল অডিও সেটিংসের সংশোধন করতে অ্যাপ্লিকেশানটিকে মঞ্জুর করে৷"</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"অডিও রেকর্ড করে"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"অ্যাপ্লিকেশানটিকে মাইক্রোফোনের দ্বারা অডিও রেকর্ড করার অনুমতি দেয়৷ এই অনুমতিটি অ্যাপ্লিকেশানটিকে আপনার অনুমোদন ছাড়া যেকোনো সময় অডিও রেকর্ড করার অনুমতি দেয়৷"</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"sim যোগাযোগ"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"SIM এ আদেশগুলি পাঠান"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"অ্যাপ্লিকেশানটিকে সিম কার্ডে কমান্ডগুলি পাঠানোর অনুমতি দেয়৷ এটি খুবই বিপজ্জনক৷"</string>
<string name="permlab_camera" msgid="3616391919559751192">"ছবি এবং ভিডিও তোলে"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"ক্যামেরার সাহায্যে ছবি তুলতে ও ভিডিও তৈরি করতে অ্যাপ্লিকেশানটিকে মঞ্জুর করে৷ এই অনুমতিটি অ্যাপ্লিকেশানটিকে আপনার নিশ্চয়তা ছাড়াই যেকোনো সময় ক্যামেরা ব্যবহার করতে মঞ্জুর করে৷"</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"ফোনটির মেমরিতে থাকা অ্যাকাউন্টের তালিকাটি পেতে অ্যাপ্লিকেশানটিকে মঞ্জুর করে৷ এতে আপনার দ্বারা ইনস্টল করা অ্যাপ্লিকেশানগুলির মাধ্যমে তৈরি যেকোনো অ্যাকাউন্ট অন্তর্ভুক্ত হতে পারে৷"</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"নেটওয়ার্ক সংযোগগুলি দেখুন"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"কোন নেটওয়ার্কগুলি বিদ্যমান এবং সংযুক্ত রয়েছে তার তথ্য দেখার জন্য অ্যাপ্লিকেশানটিকে অনুমতি প্রদান করে৷"</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"সম্পূর্ণ নেটওয়ার্ক অ্যাক্সেস"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"সম্পূর্ণ নেটওয়ার্ক অ্যাক্সেস রয়েছে"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"নেটওয়ার্ক সকেটগুলি তৈরি করতে এবং কাস্টম নেটওয়ার্ক প্রোটোকলগুলি ব্যবহার করতে অ্যাপ্লিকেশানটিকে মঞ্জুর করে৷ ব্রাউজার ও অন্যান্য অ্যাপ্লিকেশানগুলি ইন্টারনেটে ডেটা প্রেরণ করার উপায় সরবরাহ করে, তাই ইন্টারনেটে ডেটা প্রেরণ করতে এই অনুমতিটির প্রয়োজন হয় না৷"</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"নেটওয়ার্ক সংযোগ পরিবর্তন করে"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"অ্যাপ্লিকেশানকে নেটওয়ার্ক সংযোগ অবস্থা পরিবর্তন করার অনুমতি দেয়৷"</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"অ্যাপ্লিকেশানটিকে স্থানীয় Bluetooth ফোনটিকে কনফিগার এবং দূরবর্তী ডিভাইসগুলি আবিষ্কার এবং এর সাথে যুক্ত করতে দেয়৷"</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"WiMAX এর সাথে সংযুক্ত হন বা সংযোগ বিচ্ছিন্ন করুন"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"অ্যাপ্লিকেশানটিকে WiMAX সক্ষম করা আছে কিনা সে বিষয়ে নিশ্চিত হতে এবং সংযুক্ত যেকোনো WiMAX নেটওয়ার্ক সম্পর্কিত তথ্য সম্বন্ধে নিশ্চিত হওয়ার অনুমতি প্রদান করে৷"</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX এর স্থিতি পরিবর্তন করুন"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"WiMAX এর স্থিতি পরিবর্তন করুন"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"WiMAX নেটওয়ার্কগুলির সাথে ট্যাবলেটটির সংযোগ স্থাপন করতে এবং সংযোগ বিচ্ছিন্ন করতে অ্যাপ্লিকেশানটিকে মঞ্জুর করে৷"</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"টিভিকে WiMAX এ সংযোগ করতে এবং সংযোগ বিচ্ছিন্ন করার কাজটি করতে অ্যাপ্লিকেশানটিকে অনুমতি দেয়৷"</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"WiMAX নেটওয়ার্কগুলির সাথে ফোনটির সংযোগ স্থাপন করতে এবং সংযোগ বিচ্ছিন্ন করতে অ্যাপ্লিকেশানটিকে মঞ্জুর করে৷"</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"অ্যাপ্লিকেশানকে টাচ স্ক্রীণের ক্রমাঙ্গন প্যারামিটার সংশোধন করতে দেয়৷ সাধারণ অ্যাপ্লিকেশানগুলির জন্য দরকারী নয়৷"</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"DRM শংসাপত্রগুলি অ্যাক্সেস করে"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"DRM শংসাপত্রগুলির বিধান এবং ব্যবহারা করার অনুমতি দিন৷ সাধারণ অ্যাপ্লিকেশানগুলির জন্য কোনোদিন প্রয়োজন হয় না৷"</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Android বীম স্থানান্তর স্থিতি গ্রহণ করুন"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"Android বীম স্থানান্তর স্থিতি গ্রহণ করুন"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"এই অ্যাপ্লিকেশানকে বর্তমান Android বীম স্থানান্তর সম্বন্ধে তথ্য গ্রহণ করার অনুমিত দেয়"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"DRM শংসাপত্রগুলি সরান"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"কোনো অ্যাপ্লিকেশানকে DRM শংসাপত্রগুলি সরানোর অনুমতি দেয়। সাধারণ অ্যাপ্লিকেশানগুলির জন্য কখনো প্রয়োজন হয় না।"</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"ভুল পিন কোড৷"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"আনলক করতে, মেনু টিপুন তারপর ০ টিপুন৷"</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"জরুরী নম্বর"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"কোনো পরিষেবা নেই৷"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"কোনো পরিষেবা নেই"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"স্ক্রীণ লক করা আছে৷"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"আনলক করতে বা জরুরী কল করতে মেনু টিপুন৷"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"আনলক করতে মেনু টিপুন৷"</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"ব্যবহারকারীর নির্দেশিকা দেখুন বা গ্রাহক পরিষেবা কেন্দ্রে যোগাযোগ করুন৷"</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"সিম কার্ড লক করা আছে৷"</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"সিম কার্ড আনলক করা হচ্ছে…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"আপনি আপনার আনলকের প্যাটার্ন আঁকার ক্ষেত্রে <xliff:g id="NUMBER_0">%d</xliff:g> বার ভুল করেছেন৷ \n\n<xliff:g id="NUMBER_1">%d</xliff:g> সেকেন্ডের মধ্যে আবার চেষ্টা করুন৷"</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"আপনি আপনার পাসওয়ার্ড <xliff:g id="NUMBER_0">%d</xliff:g> বার ভুল টাইপ করেছেন৷ \n\n<xliff:g id="NUMBER_1">%d</xliff:g> সেকেন্ডের মধ্যে আবার চেষ্টা করুন৷"</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"আপনি আপনার পাসওয়ার্ড <xliff:g id="NUMBER_0">%d</xliff:g> বার ভুল টাইপ করেছেন৷ \n\n<xliff:g id="NUMBER_1">%d</xliff:g> সেকেন্ডের মধ্যে আবার চেষ্টা করুন৷"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"আপনি <xliff:g id="NUMBER_0">%d</xliff:g> বার ভুল করে আপনার আনলক প্যাটার্ন অঙ্কিত করেছেন৷ আপনি <xliff:g id="NUMBER_1">%d</xliff:g>টি অসফল প্রচেষ্টার পরে, আপনাকে Google সাইন ইন দিয়ে আপনার ট্যাবলেট আনলক করার কথা বলা হবে৷\n\n <xliff:g id="NUMBER_2">%d</xliff:g> সেকেন্ড পরে আবার চেষ্টা করুন৷"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"আপনি <xliff:g id="NUMBER_0">%d</xliff:g> বার ভুল করে আপনার আনলক প্যাটার্ন অঙ্কিত করেছেন৷ আপনি <xliff:g id="NUMBER_1">%d</xliff:g>টি অসফল প্রচেষ্টার পরে, আপনাকে Google সাইন ইন দিয়ে আপনার টিভি আনলক করার কথা বলা হবে৷\n\n <xliff:g id="NUMBER_2">%d</xliff:g> সেকেন্ড পরে আবার চেষ্টা করুন৷"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"আপনি <xliff:g id="NUMBER_0">%d</xliff:g> বার ভুল করে আপনার আনলক প্যাটার্ন অঙ্কিত করেছেন৷ আপনি <xliff:g id="NUMBER_1">%d</xliff:g>টি অসফল প্রচেষ্টার পরে, আপনাকে Google সাইন ইন দিয়ে আপনার ফোন আনলক করার কথা বলা হবে৷\n\n <xliff:g id="NUMBER_2">%d</xliff:g> সেকেন্ড পরে আবার চেষ্টা করুন৷"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"আপনি <xliff:g id="NUMBER_0">%d</xliff:g> বার ভুল করে ট্যাবলেটটি আনলক করার চেষ্টা করেছেন৷ আরো <xliff:g id="NUMBER_1">%d</xliff:g>টি অসফল চেষ্টার পরে, ট্যাবলেটটি ফ্যাক্টরী ডিফল্টে রিসেট হবে এবং ব্যবহারকারীর সমস্ত ডেটা মুছে যাবে৷"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"আপনি <xliff:g id="NUMBER_0">%d</xliff:g> বার ভুল করে টিভি আনলক করার চেষ্টা করেছেন৷ <xliff:g id="NUMBER_1">%d</xliff:g>টি অসফল প্রচেষ্টার পরে, আপনার টিভি ফ্যাক্টরি ডিফল্টে পুনঃসেট হবে এবং সমস্ত ব্যবহারকারীর ডেটা মুছে যাবে৷"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"আপনি <xliff:g id="NUMBER_0">%d</xliff:g> বার ভুল করে ফোনটি আনলক করার চেষ্টা করেছেন৷ আরো <xliff:g id="NUMBER_1">%d</xliff:g>টি অসফল চেষ্টার পরে, ফোনটি ফ্যাক্টরী ডিফল্টে রিসেট হবে এবং ব্যবহারকারীর সমস্ত ডেটা মুছে যাবে৷"</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"আপনি আপনার আনলকের প্যাটার্ন আঁকার ক্ষেত্রে <xliff:g id="NUMBER_0">%1$d</xliff:g> বার ভুল করেছেন৷ \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> সেকেন্ডের মধ্যে আবার চেষ্টা করুন৷"</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"আপনি আপনার পাসওয়ার্ড <xliff:g id="NUMBER_0">%1$d</xliff:g> বার ভুল টাইপ করেছেন৷ \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> সেকেন্ডের মধ্যে আবার চেষ্টা করুন৷"</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"আপনি আপনার পাসওয়ার্ড <xliff:g id="NUMBER_0">%1$d</xliff:g> বার ভুল টাইপ করেছেন৷ \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> সেকেন্ডের মধ্যে আবার চেষ্টা করুন৷"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"আপনি <xliff:g id="NUMBER_0">%1$d</xliff:g> বার ভুল করে আপনার আনলক প্যাটার্ন অঙ্কিত করেছেন৷ আপনি <xliff:g id="NUMBER_1">%2$d</xliff:g>টি অসফল প্রচেষ্টার পরে, আপনাকে Google সাইন ইন দিয়ে আপনার ট্যাবলেট আনলক করার কথা বলা হবে৷\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> সেকেন্ড পরে আবার চেষ্টা করুন৷"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"আপনি <xliff:g id="NUMBER_0">%1$d</xliff:g> বার ভুল করে আপনার আনলক প্যাটার্ন অঙ্কিত করেছেন৷ আপনি <xliff:g id="NUMBER_1">%2$d</xliff:g>টি অসফল প্রচেষ্টার পরে, আপনাকে Google সাইন ইন দিয়ে আপনার টিভি আনলক করার কথা বলা হবে৷\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> সেকেন্ড পরে আবার চেষ্টা করুন৷"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"আপনি <xliff:g id="NUMBER_0">%1$d</xliff:g> বার ভুল করে আপনার আনলক প্যাটার্ন অঙ্কিত করেছেন৷ আপনি <xliff:g id="NUMBER_1">%2$d</xliff:g>টি অসফল প্রচেষ্টার পরে, আপনাকে Google সাইন ইন দিয়ে আপনার ফোন আনলক করার কথা বলা হবে৷\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> সেকেন্ড পরে আবার চেষ্টা করুন৷"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"আপনি <xliff:g id="NUMBER_0">%1$d</xliff:g> বার ভুল করে ট্যাবলেটটি আনলক করার চেষ্টা করেছেন৷ আরো <xliff:g id="NUMBER_1">%2$d</xliff:g>টি অসফল চেষ্টার পরে, ট্যাবলেটটি ফ্যাক্টরী ডিফল্টে রিসেট হবে এবং ব্যবহারকারীর সমস্ত ডেটা মুছে যাবে৷"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"আপনি <xliff:g id="NUMBER_0">%1$d</xliff:g> বার ভুল করে টিভি আনলক করার চেষ্টা করেছেন৷ <xliff:g id="NUMBER_1">%2$d</xliff:g>টি অসফল প্রচেষ্টার পরে, আপনার টিভি ফ্যাক্টরি ডিফল্টে পুনঃসেট হবে এবং সমস্ত ব্যবহারকারীর ডেটা মুছে যাবে৷"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"আপনি <xliff:g id="NUMBER_0">%1$d</xliff:g> বার ভুল করে ফোনটি আনলক করার চেষ্টা করেছেন৷ আরো <xliff:g id="NUMBER_1">%2$d</xliff:g>টি অসফল চেষ্টার পরে, ফোনটি ফ্যাক্টরী ডিফল্টে রিসেট হবে এবং ব্যবহারকারীর সমস্ত ডেটা মুছে যাবে৷"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"আপনি <xliff:g id="NUMBER">%d</xliff:g> বার ভুল করে ট্যাবলেটটি আনলক করার চেষ্টা করেছেন৷ ট্যাবলেটটি এখন ফ্যাক্টরী ডিফল্টে রিসেট হবে৷"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"আপনি <xliff:g id="NUMBER">%d</xliff:g> বার ভুল করে টিভি আনলক করার চেষ্টা করেছেন৷ টিভি এখন ফ্যাক্টরি ডিফল্টে পুনঃসেট হবে৷"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"আপনি <xliff:g id="NUMBER">%d</xliff:g> বার ভুল করে ফোনটি আনলক করার চেষ্টা করেছেন৷ ফোনটি এখন ফ্যাক্টরী ডিফল্টে রিসেট হবে৷"</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"শব্দ"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"লিঙ্ক"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"লাইন"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"ফ্যাক্টরী পরীক্ষা ব্যর্থ হয়েছে"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"FACTORY_TEST ক্রিয়াটি শুধুমাত্র /system/app এ ইনস্টল থাকা প্যাকেজগুলি সমর্থন করে৷"</string>
<string name="factorytest_no_action" msgid="872991874799998561">"এমন কোনো প্যাকেজ খুঁজে পাওয়া যায়নি যা FACTORY_TEST ক্রিয়া প্রদান করে৷"</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"ফরম্যাট করা হচ্ছে..."</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"ঢোকানো হয় নি"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"কোনো সমরূপ কার্যকলাপ খুঁজে পাওয়া যায়নি৷"</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"মিডিয়া আউটপুট রুট করুন"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"মিডিয়া আউটপুট রুট করুন"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"অ্যাপ্লিকেশানটিকে অন্যান্য বহিরাগত ডিভাইসে মিডিয়া আউটপুট রুট করার অনুমতি দেয়৷"</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"ইনস্টল সেশন পড়ুন"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"ইনস্টল সেশন পড়ুন"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"কোনো অ্যাপ্লিকেশানকে সেশনগুলি পড়ার অনুমতি দেয়। এটি সক্রিয় প্যাকেজ ইনস্টলেশনের বিশদ বিবরণ দেখতে দেয়।"</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"প্যাকেজগুলি ইনস্টল করার অনুরোধ"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"প্যাকেজগুলি ইনস্টল করার অনুরোধ"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"একটি অ্যাপ্লিকেশানকে প্যাকেজগুলির ইনস্টল করার অনুরোধ জানাতে অনুমতি দেয়৷"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"জুম নিয়ন্ত্রণের জন্য দুবার স্পর্শ করুন"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"উইজেট যোগ করা যায়নি৷"</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"অবৈধ ব্যবহারকারী নাম অথবা পাসওয়ার্ড৷"</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"আপনার ব্যবহারকারী নাম অথবা পাসওয়ার্ড ভুলে গেছেন?\n"<b>"google.com/accounts/recovery"</b>" এ যান৷"</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"অ্যাকাউন্ট পরীক্ষা করা হচ্ছে..."</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"আপনি আপনার পাসওয়ার্ড <xliff:g id="NUMBER_0">%d</xliff:g> বার ভুল টাইপ করেছেন৷ \n\n<xliff:g id="NUMBER_1">%d</xliff:g> সেকেন্ডের মধ্যে আবার চেষ্টা করুন৷"</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"আপনি আপনার পাসওয়ার্ড <xliff:g id="NUMBER_0">%d</xliff:g> বার ভুল টাইপ করেছেন৷ \n\n<xliff:g id="NUMBER_1">%d</xliff:g> সেকেন্ডের মধ্যে আবার চেষ্টা করুন৷"</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"আপনি আপনার আনলকের প্যাটার্ন আঁকার ক্ষেত্রে <xliff:g id="NUMBER_0">%d</xliff:g> বার ভুল করেছেন৷ \n\n<xliff:g id="NUMBER_1">%d</xliff:g> সেকেন্ডের মধ্যে আবার চেষ্টা করুন৷"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"আপনি আপনার ট্যাবলেট আনলকের প্রচেষ্টায় <xliff:g id="NUMBER_0">%d</xliff:g> বার ভুল করেছেন৷ আর <xliff:g id="NUMBER_1">%d</xliff:g> বার অসফল প্রচেষ্টা করা হলে, ট্যাবলেটের সেটিংস ফ্যাক্টরি ডিফল্ট অনুযায়ী হয়ে যাবে এবং সমস্ত ব্যবহারকারী ডেটা হারিয়ে যাবে৷"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"আপনি <xliff:g id="NUMBER_0">%d</xliff:g> বার ভুল করে টিভি আনলক করার চেষ্টা করেছেন৷ <xliff:g id="NUMBER_1">%d</xliff:g>টি অসফল প্রচেষ্টার পরে, আপনার টিভি ফ্যাক্টরি ডিফল্টে পুনঃসেট হবে এবং সমস্ত ব্যবহারকারীর ডেটা মুছে যাবে৷"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"আপনি আপনার ফোন আনলকের প্রচেষ্টায় <xliff:g id="NUMBER_0">%d</xliff:g> বার ভুল করেছেন৷ আর <xliff:g id="NUMBER_1">%d</xliff:g> বার অসফল প্রচেষ্টা করা হলে, ফোনটির সেটিংস ফ্যাক্টরি ডিফল্ট অনুযায়ী হয়ে যাবে এবং সমস্ত ব্যবহারকারী ডেটা হারিয়ে যাবে৷"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"আপনি আপনার পাসওয়ার্ড <xliff:g id="NUMBER_0">%1$d</xliff:g> বার ভুল টাইপ করেছেন৷ \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> সেকেন্ডের মধ্যে আবার চেষ্টা করুন৷"</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"আপনি আপনার পাসওয়ার্ড <xliff:g id="NUMBER_0">%1$d</xliff:g> বার ভুল টাইপ করেছেন৷ \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> সেকেন্ডের মধ্যে আবার চেষ্টা করুন৷"</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"আপনি আপনার আনলকের প্যাটার্ন আঁকার ক্ষেত্রে <xliff:g id="NUMBER_0">%1$d</xliff:g> বার ভুল করেছেন৷ \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> সেকেন্ডের মধ্যে আবার চেষ্টা করুন৷"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"আপনি আপনার ট্যাবলেট আনলকের প্রচেষ্টায় <xliff:g id="NUMBER_0">%1$d</xliff:g> বার ভুল করেছেন৷ আর <xliff:g id="NUMBER_1">%2$d</xliff:g> বার অসফল প্রচেষ্টা করা হলে, ট্যাবলেটের সেটিংস ফ্যাক্টরি ডিফল্ট অনুযায়ী হয়ে যাবে এবং সমস্ত ব্যবহারকারী ডেটা হারিয়ে যাবে৷"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"আপনি <xliff:g id="NUMBER_0">%1$d</xliff:g> বার ভুল করে টিভি আনলক করার চেষ্টা করেছেন৷ <xliff:g id="NUMBER_1">%2$d</xliff:g>টি অসফল প্রচেষ্টার পরে, আপনার টিভি ফ্যাক্টরি ডিফল্টে পুনঃসেট হবে এবং সমস্ত ব্যবহারকারীর ডেটা মুছে যাবে৷"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"আপনি আপনার ফোন আনলকের প্রচেষ্টায় <xliff:g id="NUMBER_0">%1$d</xliff:g> বার ভুল করেছেন৷ আর <xliff:g id="NUMBER_1">%2$d</xliff:g> বার অসফল প্রচেষ্টা করা হলে, ফোনটির সেটিংস ফ্যাক্টরি ডিফল্ট অনুযায়ী হয়ে যাবে এবং সমস্ত ব্যবহারকারী ডেটা হারিয়ে যাবে৷"</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"আপনি আপনার ট্যাবলেট আনলকের প্রচেষ্টায় <xliff:g id="NUMBER">%d</xliff:g> বার ভুল করেছেন৷ ট্যাবলেটটি এখন ফ্যাক্টরি ডিফল্ট অনুযায়ী পুনরায় সেট করা হবে৷"</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"আপনি <xliff:g id="NUMBER">%d</xliff:g> বার ভুল করে টিভি আনলক করার চেষ্টা করেছেন৷ টিভি এখন ফ্যাক্টরি ডিফল্টে পুনঃসেট হবে৷"</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"আপনি আপনার ফোন আনলকের প্রচেষ্টায় <xliff:g id="NUMBER">%d</xliff:g> বার ভুল করেছেন৷ ফোনটি এখন ফ্যাক্টরি ডিফল্ট অনুযায়ী পুনরায় সেট করা হবে৷"</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"আপনি আপনার আনলকের প্যাটার্ন আঁকার ক্ষেত্রে <xliff:g id="NUMBER_0">%d</xliff:g> বার ভুল করেছেন৷ আর <xliff:g id="NUMBER_1">%d</xliff:g> বার অসফল প্রচেষ্টা করা হলে আপনাকে একটি ইমেল অ্যাকাউন্ট মারফত আপনার ট্যাবলেট আনলক করতে বলা হবে৷\n\n <xliff:g id="NUMBER_2">%d</xliff:g> সেকেন্ডের মধ্যে আবার চেষ্টা করুন৷"</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"আপনি <xliff:g id="NUMBER_0">%d</xliff:g> বার ভুল করে আপনার আনলক প্যাটার্ন অঙ্কিত করেছেন৷ আপনি <xliff:g id="NUMBER_1">%d</xliff:g>টি অসফল প্রচেষ্টার পরে, আপনাকে কোনো ইমেল অ্যাকাউন্ট দিয়ে আপনার টিভি আনলক করার কথা বলা হবে৷\n\n <xliff:g id="NUMBER_2">%d</xliff:g> সেকেন্ড পরে আবার চেষ্টা করুন৷"</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"আপনি আপনার আনলকের প্যাটার্ন আঁকার ক্ষেত্রে <xliff:g id="NUMBER_0">%d</xliff:g> বার ভুল করেছেন৷ আর <xliff:g id="NUMBER_1">%d</xliff:g> বার অসফল প্রচেষ্টা করা হলে আপনাকে একটি ইমেল অ্যাকাউন্ট মারফত আপনার ফোন আনলক করতে বলা হবে৷\n\n <xliff:g id="NUMBER_2">%d</xliff:g> সেকেন্ডের মধ্যে আবার চেষ্টা করুন৷"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"আপনি আপনার আনলকের প্যাটার্ন আঁকার ক্ষেত্রে <xliff:g id="NUMBER_0">%1$d</xliff:g> বার ভুল করেছেন৷ আর <xliff:g id="NUMBER_1">%2$d</xliff:g> বার অসফল প্রচেষ্টা করা হলে আপনাকে একটি ইমেল অ্যাকাউন্ট মারফত আপনার ট্যাবলেট আনলক করতে বলা হবে৷\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> সেকেন্ডের মধ্যে আবার চেষ্টা করুন৷"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"আপনি <xliff:g id="NUMBER_0">%1$d</xliff:g> বার ভুল করে আপনার আনলক প্যাটার্ন অঙ্কিত করেছেন৷ আপনি <xliff:g id="NUMBER_1">%2$d</xliff:g>টি অসফল প্রচেষ্টার পরে, আপনাকে কোনো ইমেল অ্যাকাউন্ট দিয়ে আপনার টিভি আনলক করার কথা বলা হবে৷\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> সেকেন্ড পরে আবার চেষ্টা করুন৷"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"আপনি আপনার আনলকের প্যাটার্ন আঁকার ক্ষেত্রে <xliff:g id="NUMBER_0">%1$d</xliff:g> বার ভুল করেছেন৷ আর <xliff:g id="NUMBER_1">%2$d</xliff:g> বার অসফল প্রচেষ্টা করা হলে আপনাকে একটি ইমেল অ্যাকাউন্ট মারফত আপনার ফোন আনলক করতে বলা হবে৷\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> সেকেন্ডের মধ্যে আবার চেষ্টা করুন৷"</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"সরান"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"প্রস্তাবিত স্তরের চেয়ে বেশি উঁচুতে ভলিউম বাড়াবেন?\n\nউঁচু ভলিউমে বেশি সময় ধরে কিছু শুনলে আপনার শ্রবনশক্তির ক্ষতি হতে পারে।"</string>
@@ -1470,6 +1468,7 @@
<item quantity="other">%d ঘন্টার জন্য</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> পর্যন্ত"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> পর্যন্ত (পরবর্তী অ্যালার্ম)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"আপনার দ্বারা এটি বন্ধ করা পর্যন্ত"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"যতক্ষণ না পর্যন্ত আপনি বিরক্ত করবেন না বন্ধ করছেন"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-ca-watch/strings.xml b/core/res/res/values-ca-watch/strings.xml
index 231450f..1d2d96e 100644
--- a/core/res/res/values-ca-watch/strings.xml
+++ b/core/res/res/values-ca-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Aplicació <xliff:g id="NUMBER_0">%1$d</xliff:g> de <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Sensors"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"accedir als contactes"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"accedir a la ubicació del rellotge"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"accedir al calendari"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"enviar i llegir missatges SMS"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"accedir a les fotos, el contingut multimèdia i els fitxers del rellotge"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"enregistrar àudio"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"fer fotos i enregistrar vídeos"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"fer i gestionar trucades telefòniques"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"accedir a les dades del sensor sobre els signes vitals"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"aparèixer a la barra d\'estat"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"accedir als sensors corporals (com ara monitors de freqüència cardíaca)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"accedir a la ubicació precisa (basada en el GPS i la xarxa)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"accedir a la ubicació aproximada (basada en la xarxa)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"enviar ordres a la SIM"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"tenir accés complet a la xarxa"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"gestionar els propietaris del perfil i del dispositiu"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"canviar l\'estat de WiMAX"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"rebre l\'estat de la transferència d\'Android Beam"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"indicar la sortida del fitxer multimèdia"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"llegir les sessions d\'instal·lació"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"sol·licitar la instal·lació de paquets"</string>
</resources>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index e32c841..fae3b41 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Inclou dades personals com ara números de targetes de crèdit i contrasenyes."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"desactivar o modificar la barra d\'estat"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Permet que l\'aplicació desactivi la barra d\'estat o afegeixi i elimini icones del sistema."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"barra d\'estat"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"aparèixer a la barra d\'estat"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Permet que l\'aplicació sigui la barra d\'estat."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"desplega/contrau la barra d\'estat"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Permet que l\'aplicació desplegui o replegui la barra d\'estat."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Permet que l\'aplicació rebi i processi missatges WAP. Aquest permís inclou la capacitat de controlar o de suprimir missatges que s\'han enviat al teu dispositiu sense mostrar-te\'ls."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"recupera les aplicacions en execució"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Permet que l\'aplicació recuperi informació sobre les tasques que s\'executen actualment i les que s\'han executat recentment. Aquesta acció pot permetre que l\'aplicació descobreixi informació sobre les aplicacions que s\'utilitzen al dispositiu."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Gestionar els propietaris del perfil i del dispositiu"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"gestionar els propietaris del perfil i del dispositiu"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Permet que les aplicacions defineixin els propietaris del perfil i del dispositiu."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"canvia l\'ordre de les aplicacions en execució"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Permet que l\'aplicació desplaci tasques en primer o segon pla. L\'aplicació pot fer-ho sense que tu ho indiquis."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Permet que l\'aplicació modifiqui el registre de trucades de la teva tauleta, incloses les dades de les trucades entrants i sortints. És possible que les aplicacions malicioses ho utilitzin per eliminar o per modificar el teu registre de trucades."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Permet que l\'aplicació modifiqui el registre de trucades del televisor, com ara les dades de les trucades entrants i sortints. És possible que les aplicacions malicioses l\'utilitzin per esborrar o modificar les dades del registre de trucades."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Permet que l\'aplicació modifiqui el registre de trucades del teu telèfon, incloses les dades de les trucades entrants i sortints. És possible que les aplicacions malicioses ho utilitzin per eliminar o per modificar el teu registre de trucades."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"sensors corp. (monitors freq. cardíaca)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"accedir a sensors corporals (p. ex., monitors de freqüència cardíaca)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Permet que l\'aplicació accedeixi a les dades dels sensors que supervisen el teu estat físic, com ara la freqüència cardíaca."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"llegeix els esdeveniments del calendari més informació confidencial"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Permet que l\'aplicació llegeixi tots els esdeveniments del calendari emmagatzemats a la tauleta, inclosos els dels amics o dels companys de feina. Aquesta acció pot permetre que l\'aplicació comparteixi o desi les dades del teu calendari, sense tenir en compte la confidencialitat."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Permet que l\'aplicació afegeixi, elimini o canviï esdeveniments que pots modificar al telèfon, inclosos els d\'amics o de companys de feina. Aquesta acció pot permetre que l\'aplicació enviï missatges que sembli que provinguin dels propietaris del calendari o que modifiqui esdeveniments sense el coneixement dels propietaris."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"accedir a ordres del proveïdor d\'ubicació addicionals"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Permet que l\'aplicació accedeixi a ordres addicionals del proveïdor d\'ubicacions; per tant, és possible que l\'aplicació pugui interferir en el funcionament del GPS o d\'altres fonts d\'ubicacions."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"ubicació precisa (basada en GPS i xarxa)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"accedir a la ubicació precisa (basada en el GPS i en la xarxa)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Permet que l\'aplicació obtingui la teva ubicació precisa mitjançant un sistema de posicionament global (GPS) o fonts d\'ubicació de xarxa, com ara torres de telefonia mòbil i Wi-Fi. Aquests serveis d\'ubicació s\'han d\'activar i han d\'estar disponibles al dispositiu perquè l\'aplicació els pugui fer servir. Les aplicacions poden utilitzar aquestes dades per determinar aproximadament on et trobes i pot ser que consumeixin més bateria."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"ubicació aproximada (basada en xarxa)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"accedir a la ubicació aproximada (basada en la xarxa)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Permet que l\'aplicació obtingui la teva ubicació aproximada. Aquesta ubicació prové dels serveis d\'ubicació que utilitzen fonts d\'ubicació de la xarxa, com ara torres de telefonia mòbil i Wi-Fi. Aquests serveis d\'ubicació s\'han d\'activar i han d\'estar disponibles al dispositiu perquè l\'aplicació els pugui fer servir. Les aplicacions poden utilitzar aquestes dades per determinar aproximadament on et trobes."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"canviar la configuració d\'àudio"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Permet que l\'aplicació modifiqui la configuració d\'àudio general, com ara el volum i l\'altaveu de sortida que es fa servir."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"enregistrar àudio"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Permet que l\'aplicació enregistri àudio amb el micròfon. Aquest permís permet que l\'aplicació enregistri àudio en qualsevol moment sense la teva confirmació."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"comunicació SIM"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"enviar ordres a la SIM"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Permet que l\'aplicació enviï ordres a la SIM. Això és molt perillós."</string>
<string name="permlab_camera" msgid="3616391919559751192">"fer fotos i vídeos"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Permet que l\'aplicació faci fotos i vídeos amb la càmera. Aquest permís permet que l\'aplicació utilitzi la càmera en qualsevol moment sense la teva confirmació."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Permet que l\'aplicació obtingui la llista de comptes coneguts pel telèfon. Això pot incloure tots els comptes que hagin creat les aplicacions que tens instal·lades."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"veure connexions de xarxa"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Permet que l\'aplicació visualitzi informació sobre connexions de xarxa, com ara quines xarxes hi ha i quines estan connectades."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"accés total a la xarxa"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"tenir accés complet a la xarxa"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Permet que l\'aplicació creï sòcols de xarxa i que utilitzi protocols de xarxa personalitzats. El navegador i altres aplicacions proporcionen mitjans per enviar dades a Internet, de manera que aquest permís no és obligatori per enviar-n\'hi."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"canviar la connectivitat de xarxa"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Permet que l\'aplicació pugui canviar l\'estat de connectivitat de la xarxa."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Permet que l\'aplicació configuri el telèfon Bluetooth local i que detecti dispositius remots i s\'hi vinculi."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"connecta i desconnecta de WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Permet que l\'aplicació determini si WiMAX està activat i que vegi la informació sobre totes les xarxes WiMAX que estan connectades."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Canvia l\'estat de WiMAX"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"canvia l\'estat de WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Permet que l\'aplicació connecti i desconnecti la tauleta de les xarxes WiMAX."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Permet que l\'aplicació connecti el televisor a xarxes WiMAX, o bé que el desconnecti."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Permet que l\'aplicació connecti i desconnecti el telèfon de les xarxes WiMAX."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Permet que l\'aplicació modifiqui els paràmetres de calibratge de la pantalla tàctil. No ha de ser mai necessari per a aplicacions normals."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"accedir als certificats de DRM"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Permet que una aplicació proporcioni i utilitzi certificats de gestió de drets digitals (DRM, Digital Rights Management). No ha de ser mai necessari per a aplicacions normals."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Rep l\'estat de la transferència d\'Android Beam"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"rebre l\'estat de la transferència d\'Android Beam"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Permet que aquesta aplicació rebi informació sobre les transferències d\'Android Beam actuals."</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"suprimir els certificats DRM"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Permet que una aplicació suprimeixi els certificats DRM. No ha de ser mai necessari per a aplicacions normals."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Codi PIN incorrecte."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Per desbloquejar-lo, premeu Menú i després 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Número d\'emergència"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Sense servei."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Sense servei"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Pantalla bloquejada."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Premeu Menú per desbloquejar-lo o per fer una trucada d\'emergència."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Premeu Menú per desbloquejar."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Consulta la guia de l\'usuari o posa\'t en contacte amb el servei d\'atenció al client."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"La targeta SIM està bloquejada."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"S\'està desbloquejant la targeta SIM..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Has dibuixat el patró de desbloqueig de manera incorrecta <xliff:g id="NUMBER_0">%d</xliff:g> vegades. \n\nTorna-ho a provar d\'aquí a <xliff:g id="NUMBER_1">%d</xliff:g> segons."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Has escrit malament la contrasenya <xliff:g id="NUMBER_0">%d</xliff:g> vegades. \n\nTorna-ho a provar d\'aquí a <xliff:g id="NUMBER_1">%d</xliff:g> segons."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Has escrit malament la contrasenya <xliff:g id="NUMBER_0">%d</xliff:g> vegades. \n\nTorna-ho a provar d\'aquí a <xliff:g id="NUMBER_1">%d</xliff:g> segons."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Has dibuixat el patró de desbloqueig <xliff:g id="NUMBER_0">%d</xliff:g> vegades de manera incorrecta. Si falles <xliff:g id="NUMBER_1">%d</xliff:g> vegades més, se\'t demanarà que desbloquegis la tauleta amb l\'inici de sessió de Google.\n\n Torna-ho a provar d\'aquí a <xliff:g id="NUMBER_2">%d</xliff:g> segons."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Has dibuixat el patró de desbloqueig incorrectament <xliff:g id="NUMBER_0">%d</xliff:g> vegades. Tens <xliff:g id="NUMBER_1">%d</xliff:g> intents més abans no hagis de desbloquejar el televisor amb les dades d\'inici de sessió de Google.\n\n Torna a provar-ho d\'aquí a <xliff:g id="NUMBER_2">%d</xliff:g> segons."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Has dibuixat el patró de desbloqueig <xliff:g id="NUMBER_0">%d</xliff:g> vegades de manera incorrecta. Si falles <xliff:g id="NUMBER_1">%d</xliff:g> vegades més, se\'t demanarà que desbloquegis el telèfon amb l\'inici de sessió de Google.\n\n Torna-ho a provar d\'aquí a <xliff:g id="NUMBER_2">%d</xliff:g> segons."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Has provat de desbloquejar la tauleta <xliff:g id="NUMBER_0">%d</xliff:g> vegades incorrectament. D\'aquí a <xliff:g id="NUMBER_1">%d</xliff:g> intents incorrectes més, la tauleta es restablirà a la configuració predeterminada de fàbrica i es perdran totes les dades dels usuaris."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Has provat de desbloquejar el televisor incorrectament <xliff:g id="NUMBER_0">%d</xliff:g> vegades. Tens <xliff:g id="NUMBER_1">%d</xliff:g> intents més abans no es restableixin els valors de fàbrica de l\'aparell i es perdin totes les dades d\'usuari."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Has provat de desbloquejar la tauleta <xliff:g id="NUMBER_0">%d</xliff:g> vegades incorrectament. D\'aquí a <xliff:g id="NUMBER_1">%d</xliff:g> intents incorrectes més, la tauleta es restablirà a la configuració predeterminada de fàbrica i es perdran totes les dades dels usuaris."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Has dibuixat el patró de desbloqueig de manera incorrecta <xliff:g id="NUMBER_0">%1$d</xliff:g> vegades. \n\nTorna-ho a provar d\'aquí a <xliff:g id="NUMBER_1">%2$d</xliff:g> segons."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Has escrit malament la contrasenya <xliff:g id="NUMBER_0">%1$d</xliff:g> vegades. \n\nTorna-ho a provar d\'aquí a <xliff:g id="NUMBER_1">%2$d</xliff:g> segons."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Has escrit malament la contrasenya <xliff:g id="NUMBER_0">%1$d</xliff:g> vegades. \n\nTorna-ho a provar d\'aquí a <xliff:g id="NUMBER_1">%2$d</xliff:g> segons."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Has dibuixat el patró de desbloqueig <xliff:g id="NUMBER_0">%1$d</xliff:g> vegades de manera incorrecta. Si falles <xliff:g id="NUMBER_1">%2$d</xliff:g> vegades més, se\'t demanarà que desbloquegis la tauleta amb l\'inici de sessió de Google.\n\n Torna-ho a provar d\'aquí a <xliff:g id="NUMBER_2">%3$d</xliff:g> segons."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Has dibuixat el patró de desbloqueig incorrectament <xliff:g id="NUMBER_0">%1$d</xliff:g> vegades. Tens <xliff:g id="NUMBER_1">%2$d</xliff:g> intents més abans no hagis de desbloquejar el televisor amb les dades d\'inici de sessió de Google.\n\n Torna a provar-ho d\'aquí a <xliff:g id="NUMBER_2">%3$d</xliff:g> segons."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Has dibuixat el patró de desbloqueig <xliff:g id="NUMBER_0">%1$d</xliff:g> vegades de manera incorrecta. Si falles <xliff:g id="NUMBER_1">%2$d</xliff:g> vegades més, se\'t demanarà que desbloquegis el telèfon amb l\'inici de sessió de Google.\n\n Torna-ho a provar d\'aquí a <xliff:g id="NUMBER_2">%3$d</xliff:g> segons."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Has provat de desbloquejar la tauleta <xliff:g id="NUMBER_0">%1$d</xliff:g> vegades incorrectament. D\'aquí a <xliff:g id="NUMBER_1">%2$d</xliff:g> intents incorrectes més, la tauleta es restablirà a la configuració predeterminada de fàbrica i es perdran totes les dades dels usuaris."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Has provat de desbloquejar el televisor incorrectament <xliff:g id="NUMBER_0">%1$d</xliff:g> vegades. Tens <xliff:g id="NUMBER_1">%2$d</xliff:g> intents més abans no es restableixin els valors de fàbrica de l\'aparell i es perdin totes les dades d\'usuari."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Has provat de desbloquejar la tauleta <xliff:g id="NUMBER_0">%1$d</xliff:g> vegades incorrectament. D\'aquí a <xliff:g id="NUMBER_1">%2$d</xliff:g> intents incorrectes més, la tauleta es restablirà a la configuració predeterminada de fàbrica i es perdran totes les dades dels usuaris."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Has provat de desbloquejar la tauleta <xliff:g id="NUMBER">%d</xliff:g> vegades incorrectament. Ara la tauleta es restablirà a la configuració predeterminada de fàbrica."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Has provat de desbloquejar el televisor incorrectament <xliff:g id="NUMBER">%d</xliff:g> vegades. Ara es restabliran els valors de fàbrica de l\'aparell."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Has provat de desbloquejar el telèfon <xliff:g id="NUMBER">%d</xliff:g> vegades incorrectament. Ara el telèfon es restablirà a la configuració predeterminada de fàbrica."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"paraula"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"enllaç"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"línia"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Error a la prova de fàbrica"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"L\'acció FACTORY_TEST només és compatible amb els paquets instal·lats a /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"No s\'ha trobat cap paquet que proporcioni l\'acció FACTORY_TEST."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"S\'està formatant..."</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"No s\'ha inserit"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"No s\'ha trobat cap activitat coincident."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Indicació de ruta de sortida de contingut multimèdia"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"indicar la sortida del fitxer multimèdia"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Permet que una aplicació indiqui la ruta de sortida de contingut multimèdia a altres dispositius externs."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Lectura de les instal·lacions de sessió"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"llegir les sessions d\'instal·lació"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Permet que una aplicació llegeixi les sessions d\'instal·lació i això permet veure detalls sobre les instal·lacions de paquet actives."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Sol·licitar la instal·lació de paquets"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"sol·licitar la instal·lació de paquets"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Permet que una aplicació sol·liciti la instal·lació de paquets."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Toca dos cops per controlar el zoom"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"No s\'ha pogut afegir el widget."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Nom d\'usuari o contrasenya no vàlids."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Has oblidat el teu nom d\'usuari o la contrasenya?\nVisita "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"S\'està comprovant el compte…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Has escrit malament el PIN <xliff:g id="NUMBER_0">%d</xliff:g> vegades. \n\nTorna-ho a provar d\'aquí a <xliff:g id="NUMBER_1">%d</xliff:g> segons."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Has escrit malament la contrasenya <xliff:g id="NUMBER_0">%d</xliff:g> vegades. \n\nTorna-ho a provar d\'aquí a <xliff:g id="NUMBER_1">%d</xliff:g> segons."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Has dibuixat el patró de desbloqueig de manera incorrecta <xliff:g id="NUMBER_0">%d</xliff:g> vegades. \n\nTorna-ho a provar d\'aquí a <xliff:g id="NUMBER_1">%d</xliff:g> segons."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Has provat de desbloquejar la tauleta <xliff:g id="NUMBER_0">%d</xliff:g> vegades de manera incorrecta. D\'aquí a <xliff:g id="NUMBER_1">%d</xliff:g> intents incorrectes més, la tauleta es restablirà a la configuració predeterminada de fàbrica i es perdran totes les dades dels usuaris."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Has provat de desbloquejar el televisor incorrectament <xliff:g id="NUMBER_0">%d</xliff:g> vegades. Tens <xliff:g id="NUMBER_1">%d</xliff:g> intents més abans no es restableixin els valors de fàbrica de l\'aparell i es perdin totes les dades d\'usuari."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Has provat de desbloquejar el telèfon <xliff:g id="NUMBER_0">%d</xliff:g> vegades de manera incorrecta. D\'aquí a <xliff:g id="NUMBER_1">%d</xliff:g> intents incorrectes més, el telèfon es restablirà a la configuració predeterminada de fàbrica i es perdran totes les dades dels usuaris."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Has escrit malament el PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> vegades. \n\nTorna-ho a provar d\'aquí a <xliff:g id="NUMBER_1">%2$d</xliff:g> segons."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Has escrit malament la contrasenya <xliff:g id="NUMBER_0">%1$d</xliff:g> vegades. \n\nTorna-ho a provar d\'aquí a <xliff:g id="NUMBER_1">%2$d</xliff:g> segons."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Has dibuixat el patró de desbloqueig de manera incorrecta <xliff:g id="NUMBER_0">%1$d</xliff:g> vegades. \n\nTorna-ho a provar d\'aquí a <xliff:g id="NUMBER_1">%2$d</xliff:g> segons."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Has provat de desbloquejar la tauleta <xliff:g id="NUMBER_0">%1$d</xliff:g> vegades de manera incorrecta. D\'aquí a <xliff:g id="NUMBER_1">%2$d</xliff:g> intents incorrectes més, la tauleta es restablirà a la configuració predeterminada de fàbrica i es perdran totes les dades dels usuaris."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Has provat de desbloquejar el televisor incorrectament <xliff:g id="NUMBER_0">%1$d</xliff:g> vegades. Tens <xliff:g id="NUMBER_1">%2$d</xliff:g> intents més abans no es restableixin els valors de fàbrica de l\'aparell i es perdin totes les dades d\'usuari."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Has provat de desbloquejar el telèfon <xliff:g id="NUMBER_0">%1$d</xliff:g> vegades de manera incorrecta. D\'aquí a <xliff:g id="NUMBER_1">%2$d</xliff:g> intents incorrectes més, el telèfon es restablirà a la configuració predeterminada de fàbrica i es perdran totes les dades dels usuaris."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Has provat de desbloquejar la tauleta <xliff:g id="NUMBER">%d</xliff:g> vegades de manera incorrecta. Ara la tauleta es restablirà a la configuració predeterminada de fàbrica."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Has provat de desbloquejar el televisor incorrectament <xliff:g id="NUMBER">%d</xliff:g> vegades. Ara es restabliran els valors de fàbrica de l\'aparell."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Has provat de desbloquejar el telèfon <xliff:g id="NUMBER">%d</xliff:g> vegades de manera incorrecta. Ara el telèfon es restablirà a la configuració predeterminada de fàbrica."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Has dibuixat el patró de desbloqueig <xliff:g id="NUMBER_0">%d</xliff:g> vegades de manera incorrecta. Si falles <xliff:g id="NUMBER_1">%d</xliff:g> vegades més, se\'t demanarà que desbloquegis la tauleta amb un compte de correu electrònic.\n\n Torna-ho a provar d\'aquí a <xliff:g id="NUMBER_2">%d</xliff:g> segons."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Has dibuixat el patró de desbloqueig incorrectament <xliff:g id="NUMBER_0">%d</xliff:g> vegades. Tens <xliff:g id="NUMBER_1">%d</xliff:g> intents més abans no hagis de desbloquejar el televisor amb un compte de correu electrònic.\n\n Torna a provar-ho d\'aquí a <xliff:g id="NUMBER_2">%d</xliff:g> segons."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Has dibuixat el patró de desbloqueig <xliff:g id="NUMBER_0">%d</xliff:g> vegades de manera incorrecta. Si falles <xliff:g id="NUMBER_1">%d</xliff:g> vegades més, se\'t demanarà que desbloquegis el telèfon amb un compte de correu electrònic.\n\n Torna-ho a provar d\'aquí a <xliff:g id="NUMBER_2">%d</xliff:g> segons."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Has dibuixat el patró de desbloqueig <xliff:g id="NUMBER_0">%1$d</xliff:g> vegades de manera incorrecta. Si falles <xliff:g id="NUMBER_1">%2$d</xliff:g> vegades més, se\'t demanarà que desbloquegis la tauleta amb un compte de correu electrònic.\n\n Torna-ho a provar d\'aquí a <xliff:g id="NUMBER_2">%3$d</xliff:g> segons."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Has dibuixat el patró de desbloqueig incorrectament <xliff:g id="NUMBER_0">%1$d</xliff:g> vegades. Tens <xliff:g id="NUMBER_1">%2$d</xliff:g> intents més abans no hagis de desbloquejar el televisor amb un compte de correu electrònic.\n\n Torna a provar-ho d\'aquí a <xliff:g id="NUMBER_2">%3$d</xliff:g> segons."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Has dibuixat el patró de desbloqueig <xliff:g id="NUMBER_0">%1$d</xliff:g> vegades de manera incorrecta. Si falles <xliff:g id="NUMBER_1">%2$d</xliff:g> vegades més, se\'t demanarà que desbloquegis el telèfon amb un compte de correu electrònic.\n\n Torna-ho a provar d\'aquí a <xliff:g id="NUMBER_2">%3$d</xliff:g> segons."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Elimina"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Vols apujar el volum per sobre del nivell recomanat?\n\nSi escoltes música a un volum alt durant períodes llargs, pots danyar-te l\'oïda."</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">Durant 1 h</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Fins a les <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Fins a les <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (propera alarma)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Fins que no ho desactivis"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Fins que desactivis el mode No molesteu"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-cs-watch/strings.xml b/core/res/res/values-cs-watch/strings.xml
index 229e1eb..c087173 100644
--- a/core/res/res/values-cs-watch/strings.xml
+++ b/core/res/res/values-cs-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Aplikace <xliff:g id="NUMBER_0">%1$d</xliff:g> z <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Senzory"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"přístup ke kontaktům"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"přístup k poloze těchto hodinek"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"přístup ke kalendáři"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"odesílání a zobrazení zpráv SMS"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"přístup k fotkám, médiím a souborům v hodinkách"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"nahrávání zvuku"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"pořizování fotografií a nahrávání videa"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"uskutečňování a správa telefonních hovorů"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"přístup k údajům senzorů vašich životních funkcí"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"vydávání se za stavový řádek"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"přístup k tělesným senzorům (jako jsou snímače tepu)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"přístup k přesné poloze (pomocí GPS a sítě)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"přístup k přibližné poloze (pomocí sítě)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"odesílání příkazů do SIM karty"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"úplný přístup k síti"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"správa vlastníků profilu a zařízení"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"změna stavu připojení WiMAX"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"příjem stavu přenosů Android Beam"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"směrování výstupu médií"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"čtení instalačních relací"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"žádost o instalaci balíčků"</string>
</resources>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 7b05612..b68d1ef 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -256,7 +256,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Sledování zahrnuje osobní údaje, jako jsou například čísla kreditních karet a hesla."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"zakázání či změny stavového řádku"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Umožňuje aplikaci zakázat stavový řádek nebo přidat či odebrat systémové ikony."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"stavový řádek"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"vydávání se za stavový řádek"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Umožňuje aplikaci být stavovým řádkem."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"rozbalení a sbalení stavového řádku"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Umožňuje aplikaci rozbalit či sbalit stavový řádek."</string>
@@ -284,7 +284,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Umožňuje aplikaci přijmout a zpracovat zprávy WAP. Toto oprávnění umožňuje sledovat přijaté zprávy nebo je smazat, aniž by se vám zobrazily."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"načtení spuštěných aplikací"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Umožňuje aplikaci získat informace o aktuálně a naposledy spuštěných úlohách. Aplikace s tímto oprávněním může odhalit informace o aplikacích, které se v zařízení používají."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Správa vlastníků profilu a zařízení"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"správa vlastníků profilu a zařízení"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Umožňuje aplikacím nastavit vlastníky profilu a vlastníka zařízení."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"změna uspořádání spuštěných aplikací"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Umožňuje aplikaci přesunout úlohy na popředí nebo pozadí. Aplikace tak může činit bez vašeho zásahu."</string>
@@ -326,7 +326,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Umožňuje aplikaci upravovat seznam hovorů vašeho tabletu, včetně dat o příchozích a odchozích hovorech. Škodlivé aplikace to mohou zneužít k vymazání nebo změnám seznamu hovorů."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Umožňuje aplikaci upravovat seznam hovorů vaší televize včetně dat o příchozích a odchozích hovorech. Škodlivé aplikace to mohou zneužít k vymazání nebo změnám seznamu hovorů."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Umožňuje aplikaci upravovat seznam hovorů vašeho telefonu, včetně dat o příchozích a odchozích hovorech. Škodlivé aplikace to mohou zneužít k vymazání nebo změnám seznamu hovorů."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"tělesné senzory (například snímače tepu)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"přístup k tělesným senzorům (např. snímače tepu)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Umožňuje aplikaci používat data ze senzorů, které sledují vaši fyzickou kondici, například tepovou frekvenci."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"čtení událostí kalendáře a důvěrné informace"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Umožňuje aplikaci číst všechny události kalendáře uložené v tabletu, včetně událostí přátel nebo spolupracovníků. Aplikace s tímto oprávněním může sdílet nebo ukládat údaje v kalendáři bez ohledu na důvěrnost nebo citlivost těchto údajů."</string>
@@ -338,15 +338,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Umožňuje aplikaci přidat, odebrat nebo změnit události, které můžete v telefonu upravovat, a to včetně událostí přátel a spolupracovníků. Toto oprávnění umožňuje aplikaci odesílat zprávy, které budou zdánlivě přicházet od vlastníků kalendářů, nebo upravovat události bez vědomí vlastníků."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"přístup k dalším příkazům poskytovatele polohy"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Umožňuje aplikaci přístup k dalším příkazům poskytovatele polohy. To aplikaci umožní zasahovat do fungování systému GPS a dalších zdrojů polohy."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"přesná poloha (pomocí GPS a sítě)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"přístup k přesné poloze (pomocí GPS a sítě)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Umožňuje aplikaci zjistit vaši přesnou polohu pomocí systému GPS nebo síťových lokalizačních zdrojů, jako jsou vysílače mobilní sítě a sítě Wi-Fi. Aby aplikace mohla služby určování polohy použít, musejí být zapnuté a musejí být v zařízení k dispozici. Aplikace pomocí těchto informací mohou určit vaši přesnou polohu a mohou spotřebovávat více energie z baterie."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"přibližná poloha (pomocí sítě)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"přístup k přibližné poloze (pomocí sítě)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Umožňuje aplikaci získat vaši přibližnou polohu. Polohu stanovují služby určování polohy pomocí síťových zdrojů, jako jsou vysílače mobilních sítí a sítě Wi-Fi. Aby aplikace mohla služby určování polohy použít, musejí být zapnuté a musejí být ve vašem zařízení k dispozici. Aplikace na základě těchto informací mohou zjistit vaši přibližnou polohu."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"změna nastavení zvuku"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Umožňuje aplikaci změnit globální nastavení zvuku, například hlasitost či reproduktor pro výstup zvuku."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"nahrávání zvuku"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Umožňuje aplikaci zaznamenat zvuk pomocí mikrofonu. Toto oprávnění umožňuje aplikaci kdykoliv zaznamenat zvuk bez vašeho svolení."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"komunikace s kartou SIM"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"odesílání příkazů do SIM karty"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Umožňuje aplikaci odesílat příkazy na kartu SIM. Toto oprávnění je velmi nebezpečné."</string>
<string name="permlab_camera" msgid="3616391919559751192">"pořizování fotografií a videí"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Umožňuje aplikaci pořizovat fotografie a videa pomocí fotoaparátu. Toto oprávnění umožňuje aplikaci používat fotoaparát kdykoliv i bez vašeho svolení."</string>
@@ -384,7 +384,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Umožňuje aplikaci získat seznam účtů v telefonu. Mohou sem patřit i účty vytvořené aplikacemi, které jste nainstalovali."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"zobrazování síťových připojení"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Umožňuje aplikaci zobrazit informace o síťových připojeních, například o tom, které sítě jsou k dispozici a které jsou připojené."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"úplný přístup k síti"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"úplný přístup k síti"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Umožňuje aplikaci vytvářet síťové sokety a používat vlastní síťové protokoly. K odesílání údajů na internet toto oprávnění není nutné, protože údaje lze na internet odesílat prostřednictvím prohlížečů a dalších aplikací."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"změna připojení k síti"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Umožňuje aplikaci změnit stav připojení k síti."</string>
@@ -404,7 +404,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Umožňuje aplikaci konfigurovat místní telefon s rozhraním Bluetooth a vyhledávat a párovat vzdálená zařízení."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"připojení a odpojení od sítě WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Umožňuje aplikaci zjistit, zda je povoleno připojení WiMAX, a také získat informace o všech připojených sítích WiMAX."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Změnit stav připojení WiMAX"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"měnit stav připojení WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Umožňuje aplikaci připojovat tablet k sítím WiMAX a odpojovat jej od nich."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Umožňuje aplikaci připojovat televizi k sítím WiMAX a také ji od nich odpojovat."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Umožňuje aplikaci připojovat telefon k sítím WiMAX a odpojovat jej od nich."</string>
@@ -487,7 +487,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Umožňuje aplikaci měnit parametry kalibrace dotykové obrazovky. Běžné aplikace by toto oprávnění neměly nikdy potřebovat."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"přístup k certifikátům DRM"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Umožňuje aplikaci vydávat a používat certifikáty DRM. Běžné aplikace by toto oprávnění neměly nikdy potřebovat."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Příjem stavu přenosů Android Beam"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"příjem stavu přenosů Android Beam"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Umožňuje této aplikaci přijímat informace o aktuálních přenosech pomocí technologie Android Beam"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"odstranění certifikátů DRM"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Povoluje aplikaci odstranit certifikáty DRM. Běžné aplikace by toto oprávnění neměly nikdy potřebovat."</string>
@@ -652,7 +652,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Nesprávný kód PIN."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Chcete-li telefon odemknout, stiskněte Menu a poté 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Číslo tísňové linky"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Žádný signál"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Žádný signál"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Obrazovka uzamčena."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Chcete-li odemknout telefon nebo provést tísňové volání, stiskněte Menu."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Telefon odemknete stisknutím tlačítka Menu."</string>
@@ -684,15 +684,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Další informace naleznete v uživatelské příručce; nebo kontaktujte zákaznickou podporu."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM karta je zablokována."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Odblokování SIM karty..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Již <xliff:g id="NUMBER_0">%d</xliff:g>krát jste použili nesprávné bezpečnostní gesto. \n\nZkuste to znovu za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Již <xliff:g id="NUMBER_0">%d</xliff:g>krát jste nesprávně zadali heslo. \n\nZkuste to znovu za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Již <xliff:g id="NUMBER_0">%d</xliff:g>krát jste nesprávně zadali kód PIN. \n\nZkuste to znovu za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Již <xliff:g id="NUMBER_0">%d</xliff:g>krát jste nesprávně nakreslili své heslo odemknutí. Po dalších neúspěšných pokusech (<xliff:g id="NUMBER_1">%d</xliff:g>) budete požádáni o odemčení tabletu pomocí přihlášení Google.\n\n Zkuste to znovu za <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"<xliff:g id="NUMBER_0">%d</xliff:g>× jste nesprávně načrtli bezpečnostní gesto. Po dalších <xliff:g id="NUMBER_1">%d</xliff:g> neúspěšných pokusech budete požádáni o odemčení televize pomocí přihlášení přes Google.\n\nOpakujte akci za <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Již <xliff:g id="NUMBER_0">%d</xliff:g>krát jste nesprávně nakreslili své heslo odemknutí. Po dalších neúspěšných pokusech (<xliff:g id="NUMBER_1">%d</xliff:g>) budete požádáni o odemčení telefonu pomocí přihlášení Google.\n\n Zkuste to znovu za <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Tablet jste se pokusili nesprávným způsobem odemknout <xliff:g id="NUMBER_0">%d</xliff:g>krát. Po <xliff:g id="NUMBER_1">%d</xliff:g> dalších neúspěšných pokusech se v tabletu obnoví tovární nastavení a veškerá uživatelská data budou ztracena."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"<xliff:g id="NUMBER_0">%d</xliff:g>× jste se neúspěšně pokusili odemknout televizi. Po dalších <xliff:g id="NUMBER_1">%d</xliff:g> neúspěšných pokusech bude v televizi obnoveno tovární nastavení a veškerá uživatelská data budou ztracena."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Telefon jste se pokusili nesprávným způsobem odemknout <xliff:g id="NUMBER_0">%d</xliff:g>krát. Po <xliff:g id="NUMBER_1">%d</xliff:g> dalších neúspěšných pokusech se v telefonu obnoví tovární nastavení a veškerá uživatelská data budou ztracena."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Již <xliff:g id="NUMBER_0">%1$d</xliff:g>krát jste použili nesprávné bezpečnostní gesto. \n\nZkuste to znovu za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Již <xliff:g id="NUMBER_0">%1$d</xliff:g>krát jste nesprávně zadali heslo. \n\nZkuste to znovu za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Již <xliff:g id="NUMBER_0">%1$d</xliff:g>krát jste nesprávně zadali kód PIN. \n\nZkuste to znovu za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Již <xliff:g id="NUMBER_0">%1$d</xliff:g>krát jste nesprávně nakreslili své heslo odemknutí. Po dalších neúspěšných pokusech (<xliff:g id="NUMBER_1">%2$d</xliff:g>) budete požádáni o odemčení tabletu pomocí přihlášení Google.\n\n Zkuste to znovu za <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"<xliff:g id="NUMBER_0">%1$d</xliff:g>× jste nesprávně načrtli bezpečnostní gesto. Po dalších <xliff:g id="NUMBER_1">%2$d</xliff:g> neúspěšných pokusech budete požádáni o odemčení televize pomocí přihlášení přes Google.\n\nOpakujte akci za <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Již <xliff:g id="NUMBER_0">%1$d</xliff:g>krát jste nesprávně nakreslili své heslo odemknutí. Po dalších neúspěšných pokusech (<xliff:g id="NUMBER_1">%2$d</xliff:g>) budete požádáni o odemčení telefonu pomocí přihlášení Google.\n\n Zkuste to znovu za <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Tablet jste se pokusili nesprávným způsobem odemknout <xliff:g id="NUMBER_0">%1$d</xliff:g>krát. Po <xliff:g id="NUMBER_1">%2$d</xliff:g> dalších neúspěšných pokusech se v tabletu obnoví tovární nastavení a veškerá uživatelská data budou ztracena."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"<xliff:g id="NUMBER_0">%1$d</xliff:g>× jste se neúspěšně pokusili odemknout televizi. Po dalších <xliff:g id="NUMBER_1">%2$d</xliff:g> neúspěšných pokusech bude v televizi obnoveno tovární nastavení a veškerá uživatelská data budou ztracena."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Telefon jste se pokusili nesprávným způsobem odemknout <xliff:g id="NUMBER_0">%1$d</xliff:g>krát. Po <xliff:g id="NUMBER_1">%2$d</xliff:g> dalších neúspěšných pokusech se v telefonu obnoví tovární nastavení a veškerá uživatelská data budou ztracena."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Tablet jste se pokusili nesprávným způsobem odemknout <xliff:g id="NUMBER">%d</xliff:g>krát. V tabletu se nyní obnoví výchozí tovární nastavení."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"<xliff:g id="NUMBER">%d</xliff:g>× jste se neúspěšně pokusili odemknout televizi. V televizi nyní bude obnoveno tovární nastavení."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Telefon jste se pokusili nesprávným způsobem odemknout <xliff:g id="NUMBER">%d</xliff:g>krát. V telefonu se nyní obnoví výchozí tovární nastavení."</string>
@@ -744,8 +744,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"slovo"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"odkaz"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"řádek"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Test továrního nastavení se nezdařil"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Test FACTORY_TEST lze provést pouze u balíčků nainstalovaných ve složce /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Nebyl nalezen žádný balíček umožňující test FACTORY_TEST."</string>
@@ -1087,11 +1085,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Formátování..."</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Není vloženo"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Nebyly nalezeny žádné odpovídající aktivity."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Směrování výstupu médií"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"směrování výstupu médií"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Umožňuje aplikaci směrovat výstup médií do dalších externích zařízení."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Čtení instalačních relací"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"čtení instalačních relací"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Povoluje aplikaci číst instalační relace. Díky tomu můžete zobrazit podrobnosti o aktivních instalacích balíčku."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Žádat o instalaci balíčků"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"odesílání žádostí o přístup k instalačním balíčkům"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Umožňuje aplikaci požádat o instalaci balíčků."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Dvojitým dotykem můžete ovládat přiblížení"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Widget nelze přidat."</string>
@@ -1301,18 +1299,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Neplatné uživatelské jméno nebo heslo."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Zapomněli jste uživatelské jméno nebo heslo?\nPřejděte na stránku "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Kontrola účtu…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Již <xliff:g id="NUMBER_0">%d</xliff:g>krát jste zadali nesprávný kód PIN. \n\nZkuste to znovu za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Již <xliff:g id="NUMBER_0">%d</xliff:g>krát jste nesprávně zadali heslo. \n\nZkuste to znovu za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Již <xliff:g id="NUMBER_0">%d</xliff:g>krát jste zadali nesprávné bezpečnostní gesto. \n\nZkuste to znovu za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Již jste se <xliff:g id="NUMBER_0">%d</xliff:g>krát pokusili odemknout tablet nesprávným způsobem. Po <xliff:g id="NUMBER_1">%d</xliff:g> dalších neúspěšných pokusech se v tabletu obnoví tovární nastavení a veškerá uživatelská data budou ztracena."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"<xliff:g id="NUMBER_0">%d</xliff:g>× jste se neúspěšně pokusili odemknout televizi. Po dalších <xliff:g id="NUMBER_1">%d</xliff:g> neúspěšných pokusech bude v televizi obnoveno tovární nastavení a veškerá uživatelská data budou ztracena."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Již jste se <xliff:g id="NUMBER_0">%d</xliff:g>krát pokusili odemknout telefon nesprávným způsobem. Po <xliff:g id="NUMBER_1">%d</xliff:g> dalších neúspěšných pokusech se v telefonu obnoví tovární nastavení a veškerá uživatelská data budou ztracena."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Již <xliff:g id="NUMBER_0">%1$d</xliff:g>krát jste zadali nesprávný kód PIN. \n\nZkuste to znovu za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Již <xliff:g id="NUMBER_0">%1$d</xliff:g>krát jste nesprávně zadali heslo. \n\nZkuste to znovu za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Již <xliff:g id="NUMBER_0">%1$d</xliff:g>krát jste zadali nesprávné bezpečnostní gesto. \n\nZkuste to znovu za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Již jste se <xliff:g id="NUMBER_0">%1$d</xliff:g>krát pokusili odemknout tablet nesprávným způsobem. Po <xliff:g id="NUMBER_1">%2$d</xliff:g> dalších neúspěšných pokusech se v tabletu obnoví tovární nastavení a veškerá uživatelská data budou ztracena."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"<xliff:g id="NUMBER_0">%1$d</xliff:g>× jste se neúspěšně pokusili odemknout televizi. Po dalších <xliff:g id="NUMBER_1">%2$d</xliff:g> neúspěšných pokusech bude v televizi obnoveno tovární nastavení a veškerá uživatelská data budou ztracena."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Již jste se <xliff:g id="NUMBER_0">%1$d</xliff:g>krát pokusili odemknout telefon nesprávným způsobem. Po <xliff:g id="NUMBER_1">%2$d</xliff:g> dalších neúspěšných pokusech se v telefonu obnoví tovární nastavení a veškerá uživatelská data budou ztracena."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Již jste se <xliff:g id="NUMBER">%d</xliff:g>krát pokusili odemknout tablet nesprávným způsobem. V tabletu se nyní obnoví výchozí tovární nastavení."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"<xliff:g id="NUMBER">%d</xliff:g>× jste se neúspěšně pokusili odemknout televizi. V televizi nyní bude obnoveno tovární nastavení."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Již jste se <xliff:g id="NUMBER">%d</xliff:g>krát pokusili odemknout telefon nesprávným způsobem. V telefonu se nyní obnoví výchozí tovární nastavení."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Již <xliff:g id="NUMBER_0">%d</xliff:g>krát jste nesprávně nakreslili své heslo odemknutí. Po <xliff:g id="NUMBER_1">%d</xliff:g>dalších neúspěšných pokusech budete požádáni o odemčení tabletu pomocí e-mailového účtu.\n\n Zkuste to znovu za <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"<xliff:g id="NUMBER_0">%d</xliff:g>× jste nesprávně načrtli bezpečnostní gesto. Po dalších <xliff:g id="NUMBER_1">%d</xliff:g> neúspěšných pokusech budete požádáni o odemčení televize pomocí e-mailového účtu.\n\nOpakujte akci za <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Již <xliff:g id="NUMBER_0">%d</xliff:g>krát jste nesprávně nakreslili své heslo odemknutí. Po <xliff:g id="NUMBER_1">%d</xliff:g> dalších neúspěšných pokusech budete požádáni o odemčení telefonu pomocí e-mailového účtu.\n\n Zkuste to znovu za <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Již <xliff:g id="NUMBER_0">%1$d</xliff:g>krát jste nesprávně nakreslili své heslo odemknutí. Po <xliff:g id="NUMBER_1">%2$d</xliff:g>dalších neúspěšných pokusech budete požádáni o odemčení tabletu pomocí e-mailového účtu.\n\n Zkuste to znovu za <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"<xliff:g id="NUMBER_0">%1$d</xliff:g>× jste nesprávně načrtli bezpečnostní gesto. Po dalších <xliff:g id="NUMBER_1">%2$d</xliff:g> neúspěšných pokusech budete požádáni o odemčení televize pomocí e-mailového účtu.\n\nOpakujte akci za <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Již <xliff:g id="NUMBER_0">%1$d</xliff:g>krát jste nesprávně nakreslili své heslo odemknutí. Po <xliff:g id="NUMBER_1">%2$d</xliff:g> dalších neúspěšných pokusech budete požádáni o odemčení telefonu pomocí e-mailového účtu.\n\n Zkuste to znovu za <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Odebrat"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Zvýšit hlasitost nad doporučenou úroveň?\n\nDlouhodobý poslech hlasitého zvuku může poškodit sluch."</string>
@@ -1504,6 +1502,7 @@
<item quantity="one">1 h</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (příští budík)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Dokud tuto funkci nevypnete"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Dokud nevypnete režim Nerušit"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-da-watch/strings.xml b/core/res/res/values-da-watch/strings.xml
index 094a50f..01014ae 100644
--- a/core/res/res/values-da-watch/strings.xml
+++ b/core/res/res/values-da-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"App <xliff:g id="NUMBER_0">%1$d</xliff:g> af <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Sensorer"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"få adgang til dine kontaktpersoner"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"få adgang til dette urs placering"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"få adgang til din kalender"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"sende og se sms-beskeder"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"få adgang til billeder, medier og filer på dit ur"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"optage lyd"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"tage billeder og optage video"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"foretage og administrere telefonopkald"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"få adgang til sensordata om dine livstegn"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"være statusbjælken"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"få adgang til kropssensorer (f.eks. pulsmålere)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"få adgang til nøjagtig placering (baseret på GPS og netværk)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"få adgang til omtrentlig placering (netværksbaseret)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"sende kommandoer til SIM"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"få fuld netværksadgang"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"administrere profil- og enhedsejere"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"skifte WiMAX-tilstand"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"modtage status for Android Beam-overførsler"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"videreføre medieoutput"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"læse installationssessioner"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"anmode om installation af pakker"</string>
</resources>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 60f0502..f83828f 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -143,7 +143,7 @@
<string name="fcError" msgid="3327560126588500777">"Forbindelsesproblemer eller ugyldig funktionskode."</string>
<string name="httpErrorOk" msgid="1191919378083472204">"OK"</string>
<string name="httpError" msgid="7956392511146698522">"Der opstod en netværksfejl."</string>
- <string name="httpErrorLookup" msgid="4711687456111963163">"Webadressen kunne ikke findes."</string>
+ <string name="httpErrorLookup" msgid="4711687456111963163">"Webadressen blev ikke fundet."</string>
<string name="httpErrorUnsupportedAuthScheme" msgid="6299980280442076799">"Ordningen for webstedsgodkendelse understøttes ikke."</string>
<string name="httpErrorAuth" msgid="1435065629438044534">"Der kunne ikke godkendes."</string>
<string name="httpErrorProxyAuth" msgid="1788207010559081331">"Godkendelse via proxyserveren mislykkedes."</string>
@@ -155,7 +155,7 @@
<string name="httpErrorFailedSslHandshake" msgid="96549606000658641">"Der kunne ikke oprettes en sikker forbindelse."</string>
<string name="httpErrorBadUrl" msgid="3636929722728881972">"Siden kunne ikke åbnes, fordi webadressen er ugyldig."</string>
<string name="httpErrorFile" msgid="2170788515052558676">"Der kunne ikke fås adgang til filen."</string>
- <string name="httpErrorFileNotFound" msgid="6203856612042655084">"Den ønskede fil kunne ikke findes."</string>
+ <string name="httpErrorFileNotFound" msgid="6203856612042655084">"Den ønskede fil blev ikke fundet."</string>
<string name="httpErrorTooManyRequests" msgid="1235396927087188253">"Der behandles for mange anmodninger. Prøv igen senere."</string>
<string name="notification_title" msgid="8967710025036163822">"Loginfejl for <xliff:g id="ACCOUNT">%1$s</xliff:g>"</string>
<string name="contentServiceSync" msgid="8353523060269335667">"Synkroniser"</string>
@@ -219,7 +219,7 @@
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Flytilstand er slået FRA"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Indstillinger"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Assistance"</string>
- <string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
+ <string name="global_action_voice_assist" msgid="7751191495200504480">"Taleassistent"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Lås nu"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Sikker tilstand"</string>
@@ -248,13 +248,13 @@
<string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"undersøge indholdet i et vindue, du interagerer med."</string>
<string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"aktivere Udforsk ved berøring"</string>
<string name="capability_desc_canRequestTouchExploration" msgid="5800552516779249356">"De emner, der trykkes på, læses højt, og skærmen kan udforskes ved hjælp af bevægelser."</string>
- <string name="capability_title_canRequestEnhancedWebAccessibility" msgid="1739881766522594073">"aktivere forbedrede webhjælpefunktioner"</string>
+ <string name="capability_title_canRequestEnhancedWebAccessibility" msgid="1739881766522594073">"Aktivér udvidede webhjælpefunktioner"</string>
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Der installeres muligvis scripts for at gøre appindhold mere tilgængeligt."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"observere tekst, du skriver"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Dette omfatter personlige data såsom kreditkortnumre og adgangskoder."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"deaktiver eller rediger statuslinje"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Tillader, at appen kan deaktivere statusbjælken eller tilføje og fjerne systemikoner."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"statusbjælke"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"vær statusbjælken"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Tillader, at appen er statusbjælken."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"udvid/skjul statuslinje"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Tillader, at appen kan udvide og skjule statusbjælken."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Tillader, at appen kan modtage og behandle WAP-beskeder. Denne tilladelse omfatter muligheden for at overvåge eller slette de beskeder, der sendes til dig, uden at vise dem til dig."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"hente kørende apps"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Tillader, at appen kan hente oplysninger om nuværende og seneste opgaver. Med denne tilladelse kan appen finde oplysninger om, hvilke applikationer der bruges på enheden."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Administrer profil- og enhedsejere"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"administrer profil- og enhedsejere"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Tillader, at apps konfigurerer profilejerne og enhedens ejer."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"omorganisere kørende apps"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Tillader, at appen kan flytte opgaver til forgrunden og baggrunden. Appen kan gøre dette uden din bekræftelse."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Tillader, at appen ændrer din tablets opkaldsliste, f.eks. data om indgående og udgående opkald. Ondsindede apps kan bruge dette til at slette eller ændre din opkaldsliste."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Giver appen lov til at ændre opkaldslisten på dit tv, herunder data om indgående og udgående opkald. Ondsindede apps kan bruge dette til at slette eller ændre din opkaldsliste."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Tillader, at appen ændrer telefonens opkaldsliste, f.eks. data om indgående og udgående opkald. Ondsindede apps kan bruge dette til at slette eller ændre din opkaldsliste."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"kropssensorer (f.eks. pulsmålere)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"få adgang til kropssensorer (f.eks. pulsmålere)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Giver appen adgang til data fra sensorer, der overvåger din fysiske tilstand, f.eks. din puls."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"læse kalenderbegivenheder og fortrolige oplysninger"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Tillader, at appen kan læse alle de kalenderbegivenheder, der er gemt på din tablet, f.eks. venners eller kollegers. Med denne tilladelse kan appen dele eller gemme dine kalenderdata, uanset fortrolighed eller følsomhed."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Tillader, at appen kan tilføje, fjerne og ændre begivenheder, som du kan redigere på din telefon, f.eks. venners eller kollegers. Med denne tilladelse kan appen sende meddelelser, der synes at komme fra ejere af kalendere, eller ændre begivenheder uden ejernes viden."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"få adgang til yderligere kommandoer for placeringsudbyder"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Tillader, at appen kan få adgang til yderligere kommandoer for placeringsudbydere. Dette kan gøre det muligt for appen at forstyrre GPS-funktionen eller andre placeringskilder."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"præcis placering (GPS- og netværksbaseret)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"få adgang til nøjagtig placering (baseret på GPS og netværk)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Tillader, at appen henter din præcise placering ved hjælp af GPS (Global Positioning System) eller netværksplaceringskilder, såsom mobilmaster og Wi-Fi. Disse placeringstjenester skal være aktiverede og tilgængelige på din enhed, for at appen kan bruge dem. Apps kan bruge dette til at fastslå, hvor du er, og kan eventuelt bruge ekstra batterikapacitet."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"omtrentlig position (netværksbaseret)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"få adgang til omtrentlig placering (netværksbaseret)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Tillader, at appen henter din omtrentlige placering. Denne placering er udledt via placeringstjenester, der bruger netværksplaceringskilder, som f.eks. mobilmaster og Wi-Fi. Disse placeringstjenester skal være aktiverede og tilgængelige på din enhed, for at appen kan bruge dem. Apps kan bruge dette til at fastslå, hvor du omtrent er."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"skifte dine lydindstillinger"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Tillader, at appen kan ændre globale lydindstillinger, som f.eks. lydstyrke og hvilken højttaler der bruges til output."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"optage lyd"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Tillader, at appen kan optage lyd med mikrofonen. Med denne tilladelse kan appen til enhver tid optage lyd uden din bekræftelse."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"SIM-kommunikation"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"send kommandoer til SIM"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Tillader, at appen sender kommandoer til SIM-kortet. Dette er meget farligt."</string>
<string name="permlab_camera" msgid="3616391919559751192">"tage billeder og optage video"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Tillader, at appen kan tage billeder og videoer med kameraet. Med denne tilladelse kan appen til enhver tid bruge kameraet uden din bekræftelse."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Tillader, at appen kan hente listen over konti, der er kendt af telefonen. Dette kan omfatte alle konti, der er oprettet af de applikationer, som du har installeret."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"se netværksforbindelser"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Tillader, at appen kan læse oplysninger om netværksforbindelser, f.eks. eksisterende og forbundne netværk."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"fuld netværksadgang"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"få fuld netværksadgang"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Tillader, at appen kan oprette netværkssockets og bruge tilpassede netværksprotokoller. Browseren og andre applikationer indeholder midler til at sende data til internettet, så med denne tilladelse er der ingen forpligtelse til at sende data til internettet."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"skifte netværksforbindelse"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Tillader, at appen kan ændre netværksforbindelsens tilstand."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Tillader, at appen kan konfigurere den lokale Bluetooth-telefon samt finde og parre med eksterne enheder."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"tilslut og afbryd fra WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Tillader, at appen kan fastslå, hvorvidt WiMAX er aktiveret, og oplysninger om eventuelle WiMAX-netværk, der er forbundet."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Skift WiMAX-tilstand"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"skifte WiMAX-tilstand"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Tillader, at appen kan oprette forbindelse fra tabletten og afbryde forbindelsen til tabletten på WiMAX-netværk."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Giver appen lov til at oprette og afbryde fjernsynets forbindelse til WiMAX-netværk."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Tillader, at appen kan oprette forbindelse fra telefonen og afbryde forbindelsen til telefonen på WiMAX-netværk."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Tillader, at appen ændrer kalibreringsparametrene for berøringsskærmen. Dette bør aldrig være nødvendigt for almindelige apps."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"få adgang til DRM-certifikater"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Tillader, at en applikation leverer og anvender DRM-certfikater. Dette bør aldrig være nødvendigt for almindelige apps."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Modtag staus for Android Beam-overførsler"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"modtag status for Android Beam-overførsler"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Tillader, at applikationen modtager oplysninger om aktuelle Android Beam-overførsler"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"fjerne DRM-certifikater"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Tillader, at en app fjerner DRM-certifikater. Dette bør aldrig være nødvendigt for almindelige apps."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Forkert pinkode."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Tryk på Menu og dernæst på 0 for at låse op."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Nødnummer"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Ingen dækning."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Ingen dækning"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Skærmen er låst."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Tryk på Menu for at låse op eller foretage et nødopkald."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Tryk på Menu for at låse op."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Se brugervejledningen, eller kontakt kundeservice."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM-kortet er låst."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Låser SIM-kortet op ..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Du har tegnet dit oplåsningsmønster forkert <xliff:g id="NUMBER_0">%d</xliff:g> gange. \n\nPrøv igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Du har indtastet din adgangskode forkert <xliff:g id="NUMBER_0">%d</xliff:g> gange. \n\nPrøv igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Du har indtastet en forkert pinkode <xliff:g id="NUMBER_0">%d</xliff:g> gange. \n\nPrøv igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Du har tegnet dit oplåsningsmønster forkert <xliff:g id="NUMBER_0">%d</xliff:g> gange. Efter <xliff:g id="NUMBER_1">%d</xliff:g> yderligere forsøg vil du blive bedt om at låse din tablet op ved hjælp af dit Google-login\n\n Prøv igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Du har tegnet dit oplåsningsmønster forkert <xliff:g id="NUMBER_0">%d</xliff:g> gange. Efter endnu <xliff:g id="NUMBER_1">%d</xliff:g> mislykket forsøg bliver du bedt om at låse op for dit tv ved hjælp af din Google-login.\n\n Prøv igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Du har tegnet dit oplåsningsmønster forkert <xliff:g id="NUMBER_0">%d</xliff:g> gange. Efter <xliff:g id="NUMBER_1">%d</xliff:g> forsøg til vil du blive bedt om at låse din telefon op ved hjælp af dit Google-login.\n\n Prøv igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Du har forsøgt at låse tabletten op forkert <xliff:g id="NUMBER_0">%d</xliff:g> gange. Efter yderligere <xliff:g id="NUMBER_1">%d</xliff:g> mislykkede forsøg nulstilles tabletten til fabriksindstillingerne, og alle brugerdata mistes."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Du har forsøgt at låse tv\'et forkert op <xliff:g id="NUMBER_0">%d</xliff:g> gange. Efter endnu <xliff:g id="NUMBER_1">%d</xliff:g> mislykkede forsøg nulstilles fjernsynet til fabriksindstillingerne, og alle brugerdata går tabt."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Du har forsøgt at låse telefonen op forkert <xliff:g id="NUMBER_0">%d</xliff:g> gange. Efter yderligere <xliff:g id="NUMBER_1">%d</xliff:g> mislykkede forsøg, nulstilles telefonen til fabriksindstillingerne, og alle brugerdata mistes."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Du har tegnet dit oplåsningsmønster forkert <xliff:g id="NUMBER_0">%1$d</xliff:g> gange. \n\nPrøv igen om <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunder."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Du har indtastet din adgangskode forkert <xliff:g id="NUMBER_0">%1$d</xliff:g> gange. \n\nPrøv igen om <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunder."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Du har indtastet en forkert pinkode <xliff:g id="NUMBER_0">%1$d</xliff:g> gange. \n\nPrøv igen om <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunder."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Du har tegnet dit oplåsningsmønster forkert <xliff:g id="NUMBER_0">%1$d</xliff:g> gange. Efter <xliff:g id="NUMBER_1">%2$d</xliff:g> yderligere forsøg vil du blive bedt om at låse din tablet op ved hjælp af dit Google-login\n\n Prøv igen om <xliff:g id="NUMBER_2">%3$d</xliff:g> sekunder."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Du har tegnet dit oplåsningsmønster forkert <xliff:g id="NUMBER_0">%1$d</xliff:g> gange. Efter endnu <xliff:g id="NUMBER_1">%2$d</xliff:g> mislykket forsøg bliver du bedt om at låse op for dit tv ved hjælp af din Google-login.\n\n Prøv igen om <xliff:g id="NUMBER_2">%3$d</xliff:g> sekunder."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Du har tegnet dit oplåsningsmønster forkert <xliff:g id="NUMBER_0">%1$d</xliff:g> gange. Efter <xliff:g id="NUMBER_1">%2$d</xliff:g> forsøg til vil du blive bedt om at låse din telefon op ved hjælp af dit Google-login.\n\n Prøv igen om <xliff:g id="NUMBER_2">%3$d</xliff:g> sekunder."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Du har forsøgt at låse tabletten op forkert <xliff:g id="NUMBER_0">%1$d</xliff:g> gange. Efter yderligere <xliff:g id="NUMBER_1">%2$d</xliff:g> mislykkede forsøg nulstilles tabletten til fabriksindstillingerne, og alle brugerdata mistes."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Du har forsøgt at låse tv\'et forkert op <xliff:g id="NUMBER_0">%1$d</xliff:g> gange. Efter endnu <xliff:g id="NUMBER_1">%2$d</xliff:g> mislykkede forsøg nulstilles fjernsynet til fabriksindstillingerne, og alle brugerdata går tabt."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Du har forsøgt at låse telefonen op forkert <xliff:g id="NUMBER_0">%1$d</xliff:g> gange. Efter yderligere <xliff:g id="NUMBER_1">%2$d</xliff:g> mislykkede forsøg, nulstilles telefonen til fabriksindstillingerne, og alle brugerdata mistes."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Du har forsøgt at låse tabletten op forkert <xliff:g id="NUMBER">%d</xliff:g> gange. Tabletten nulstilles til fabriksindstillingerne."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Du har forsøgt at låse tv\'et forkert op <xliff:g id="NUMBER">%d</xliff:g> gange. Fjernsynet nulstilles nu til fabriksindstillingerne."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Du har forsøgt at låse telefonen op forkert <xliff:g id="NUMBER">%d</xliff:g> gange. Telefonen nulstilles til fabriksindstillingerne."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"ord"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"link"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"linje"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Fabrikstest mislykkedes"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Handlingen FACTORY_TEST understøttes kun af pakker installeret i /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Der blev ikke fundet nogen pakke, som leverer handlingen FACTORY_TEST."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Formaterer…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Ikke isat"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Der blev ikke fundet nogen matchende aktiviteter."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Viderefør medieoutput"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"viderefør medieoutput"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Tillader, at en applikation viderefører medieoutput til andre eksterne enheder."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Læs installationssessioner"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"læs installationssessioner"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Tillader, at en applikation læser installationssessioner. Dermed kan applikationen se oplysninger om aktive pakkeinstallationer."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Anmod om installation af pakker"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"anmod om installation af pakker"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Tillader, at en app anmoder om installation af pakker."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Tryk to gange for zoomstyring"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Widget kunne ikke tilføjes."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Ugyldigt brugernavn eller ugyldig adgangskode."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Har du glemt dit brugernavn eller din adgangskode?\nGå til "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Kontoen kontrolleres…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Du har indtastet en forkert pinkode <xliff:g id="NUMBER_0">%d</xliff:g> gange. \n\nPrøv igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Du har indtastet din adgangskode forkert <xliff:g id="NUMBER_0">%d</xliff:g> gange. \n\nPrøv igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Du har tegnet dit oplåsningsmønster forkert <xliff:g id="NUMBER_0">%d</xliff:g> gange. \n\nPrøv igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Du har forsøgt at låse tabletten op forkert <xliff:g id="NUMBER_0">%d</xliff:g> gange. Efter <xliff:g id="NUMBER_1">%d</xliff:g> yderligere mislykkede forsøg nulstilles tabletten til fabriksindstillingerne, og alle brugerdata mistes."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Du har forsøgt at låse tv\'et forkert op <xliff:g id="NUMBER_0">%d</xliff:g> gange. Efter endnu <xliff:g id="NUMBER_1">%d</xliff:g> mislykkede forsøg nulstilles fjernsynet til fabriksindstillingerne, og alle brugerdata går tabt."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Du har forsøgt at låse telefonen op forkert <xliff:g id="NUMBER_0">%d</xliff:g> gange. Efter <xliff:g id="NUMBER_1">%d</xliff:g> yderligere mislykkede forsøg, nulstilles telefonen til fabriksindstillingerne, og alle brugerdata mistes."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Du har indtastet en forkert pinkode <xliff:g id="NUMBER_0">%1$d</xliff:g> gange. \n\nPrøv igen om <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunder."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Du har indtastet din adgangskode forkert <xliff:g id="NUMBER_0">%1$d</xliff:g> gange. \n\nPrøv igen om <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunder."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Du har tegnet dit oplåsningsmønster forkert <xliff:g id="NUMBER_0">%1$d</xliff:g> gange. \n\nPrøv igen om <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunder."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Du har forsøgt at låse tabletten op forkert <xliff:g id="NUMBER_0">%1$d</xliff:g> gange. Efter <xliff:g id="NUMBER_1">%2$d</xliff:g> yderligere mislykkede forsøg nulstilles tabletten til fabriksindstillingerne, og alle brugerdata mistes."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Du har forsøgt at låse tv\'et forkert op <xliff:g id="NUMBER_0">%1$d</xliff:g> gange. Efter endnu <xliff:g id="NUMBER_1">%2$d</xliff:g> mislykkede forsøg nulstilles fjernsynet til fabriksindstillingerne, og alle brugerdata går tabt."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Du har forsøgt at låse telefonen op forkert <xliff:g id="NUMBER_0">%1$d</xliff:g> gange. Efter <xliff:g id="NUMBER_1">%2$d</xliff:g> yderligere mislykkede forsøg, nulstilles telefonen til fabriksindstillingerne, og alle brugerdata mistes."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Du har forsøgt at låse tabletten op forkert <xliff:g id="NUMBER">%d</xliff:g> gange. Tabletten nulstilles til fabriksindstillingerne."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Du har forsøgt at låse tv\'et forkert op <xliff:g id="NUMBER">%d</xliff:g> gange. Fjernsynet nulstilles nu til fabriksindstillingerne."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Du har forsøgt at låse telefonen op forkert <xliff:g id="NUMBER">%d</xliff:g> gange. Telefonen nulstilles til fabriksindstillingerne."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Du har tegnet dit oplåsningsmønster forkert <xliff:g id="NUMBER_0">%d</xliff:g> gange. Efter <xliff:g id="NUMBER_1">%d</xliff:g> yderligere mislykkede forsøg vil du blive bedt om at låse din tablet op ved hjælp af en e-mailkonto\n\n Prøv igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Du har tegnet dit oplåsningsmønster forkert <xliff:g id="NUMBER_0">%d</xliff:g> gange. Efter endnu <xliff:g id="NUMBER_1">%d</xliff:g> mislykket forsøg bliver du bedt om at låse op for dit tv ved hjælp af en e-mailkonto.\n\n Prøv igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Du har tegnet dit oplåsningsmønster forkert <xliff:g id="NUMBER_0">%d</xliff:g> gange. Efter <xliff:g id="NUMBER_1">%d</xliff:g> yderligere mislykkede forsøg til vil du blive bedt om at låse din telefon op ved hjælp af en e-mailkonto.\n\n Prøv igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Du har tegnet dit oplåsningsmønster forkert <xliff:g id="NUMBER_0">%1$d</xliff:g> gange. Efter <xliff:g id="NUMBER_1">%2$d</xliff:g> yderligere mislykkede forsøg vil du blive bedt om at låse din tablet op ved hjælp af en e-mailkonto\n\n Prøv igen om <xliff:g id="NUMBER_2">%3$d</xliff:g> sekunder."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Du har tegnet dit oplåsningsmønster forkert <xliff:g id="NUMBER_0">%1$d</xliff:g> gange. Efter endnu <xliff:g id="NUMBER_1">%2$d</xliff:g> mislykket forsøg bliver du bedt om at låse op for dit tv ved hjælp af en e-mailkonto.\n\n Prøv igen om <xliff:g id="NUMBER_2">%3$d</xliff:g> sekunder."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Du har tegnet dit oplåsningsmønster forkert <xliff:g id="NUMBER_0">%1$d</xliff:g> gange. Efter <xliff:g id="NUMBER_1">%2$d</xliff:g> yderligere mislykkede forsøg til vil du blive bedt om at låse din telefon op ved hjælp af en e-mailkonto.\n\n Prøv igen om <xliff:g id="NUMBER_2">%3$d</xliff:g> sekunder."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Fjern"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Vil du skrue højere op end det anbefalede lydstyrkeniveau?\n\nDu kan skade hørelsen ved at lytte til meget højt musik over længere tid."</string>
@@ -1470,6 +1468,7 @@
<item quantity="other">I %d t.</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Indtil <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Indtil <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (næste alarm)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Indtil du slår denne indstilling fra"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Indtil du slår \"Forstyr ikke\" fra"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-de-watch/strings.xml b/core/res/res/values-de-watch/strings.xml
index 6f5941b..db87a0b 100644
--- a/core/res/res/values-de-watch/strings.xml
+++ b/core/res/res/values-de-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"App <xliff:g id="NUMBER_0">%1$d</xliff:g> von <xliff:g id="NUMBER_1">%2$d</xliff:g>"</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Sensoren"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"auf Kontakte zugreifen"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"auf den Standort dieser Smartwatch zugreifen"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"auf den Kalender zugreifen"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"SMS senden und abrufen"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"auf Fotos, Medien und Dateien auf Ihrer Smartwatch zugreifen"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"Audio aufnehmen"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"Bilder und Videos aufnehmen"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"Telefonanrufe tätigen und verwalten"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"auf Sensordaten zu Ihren Vitaldaten zugreifen"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"Statusleiste darstellen"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"auf Körpersensoren wie beispielsweise Herzfrequenzmesser zugreifen"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"auf genauen Standort zugreifen (GPS- und netzwerkbasiert)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"auf den ungefähren Standort zugreifen (netzwerkbasiert)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"Befehle an die SIM senden"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"auf alle Netzwerke zugreifen"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"Profilinhaber und Geräteeigentümer verwalten"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"WiMAX-Status ändern"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"Status von Android Beam-Übertragungen erhalten"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"Medienausgabe umleiten"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"Installationssitzungen lesen"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"Installation von Paketen anfordern"</string>
</resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index fcbd762..ea6239c 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Einschließlich personenbezogener Daten wie Kreditkartennummern und Passwörter."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"Statusleiste deaktivieren oder ändern"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Ermöglicht der App, die Statusleiste zu deaktivieren oder Systemsymbole hinzuzufügen oder zu entfernen"</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"Statusleiste"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"Statusleiste darstellen"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Ermöglicht der App, zur Statusleiste zu werden"</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"Statusleiste ein-/ausblenden"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Ermöglicht der App, die Statusleiste ein- oder auszublenden"</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Ermöglicht der App, WAP-Nachrichten zu empfangen und zu verarbeiten. Mit der Berechtigung können Nachrichten, die an Sie gesendet wurden, überwacht und gelöscht werden, bevor sie Ihnen angezeigt werden."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"Aktive Apps abrufen"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Ermöglicht der App, Informationen zu aktuellen und kürzlich ausgeführten Aufgaben abzurufen. Damit kann die App möglicherweise ermitteln, welche Apps auf Ihrem Gerät zum Einsatz kommen."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Profilinhaber und Geräteeigentümer verwalten"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"Profilinhaber und Geräteeigentümer verwalten"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Ermöglicht Apps das Einrichten der Profilinhaber und des Geräteeigentümers"</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"Aktive Apps neu ordnen"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Ermöglicht der App, Aufgaben in den Vorder- und Hintergrund zu verschieben, ohne dass dazu ein Eingreifen Ihrerseits notwendig ist."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Ermöglicht der App, die Anrufliste Ihres Tablets zu ändern, einschließlich der Daten über ein- und ausgehende Anrufe. Schädliche Apps können so Ihre Anrufliste löschen oder ändern."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Ermöglicht der App, die Anrufliste Ihres Fernsehers zu ändern, einschließlich der Daten über ein- und ausgehende Anrufe. Schädliche Apps können so Ihre Anrufliste löschen oder ändern."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Ermöglicht der App, die Anrufliste Ihres Telefons zu ändern, einschließlich der Daten über ein- und ausgehende Anrufe. Schädliche Apps können so Ihre Anrufliste löschen oder ändern."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"Körpersensoren (wie Pulsmonitore)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"Auf Körpersensoren wie z. B. Herzfrequenzmesser zugreifen"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Ermöglicht der App, auf Daten von Sensoren zuzugreifen, die Ihre körperliche Verfassung überwachen, beispielsweise Ihren Puls"</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"Kalendertermine sowie vertrauliche Informationen lesen"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Ermöglicht der App, alle auf Ihrem Tablet gespeicherten Kalendertermine zu lesen, einschließlich der von Freunden und Kollegen. Damit kann die App möglicherweise Ihre Kalenderdaten unabhängig von der Vertraulichkeit weiterleiten oder speichern."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Ermöglicht der App, Termine, die Sie auf Ihrem Telefon ändern können, hinzuzufügen, zu entfernen und zu ändern, einschließlich der von Freunden und Kollegen. Damit kann die App Nachrichten senden, die so erscheinen, als stammten sie vom jeweiligen Kalenderinhaber, oder Termine ohne Wissen des Inhabers ändern."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"Auf zusätzliche Dienstanbieterbefehle für Standort zugreifen"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Ermöglicht der App, auf zusätzliche Standortanbieterbefehle zuzugreifen. Damit könnte die App die Funktionsweise von GPS oder anderen Standortquellen beeinträchtigen."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"Genauer Standort (GPS- und netzwerkbasiert)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"Auf genauen Standort zugreifen (GPS- und netzwerkbasiert)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Ermöglicht der App, Ihre genaue Position anhand von GPS-Daten (Global Positioning System) oder über Netzwerkstandortquellen wie Sendemasten oder WLAN zu ermitteln. Diese Standortdienste müssen auf Ihrem Gerät verfügbar und aktiviert sein, damit die App sie verwenden kann. Apps können Ihren Standort anhand dieser Daten ermitteln und verbrauchen eventuell zusätzliche Akkuleistung."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"Ungefährer Standort (netzwerkbasiert)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"Auf den ungefähren Standort zugreifen (netzwerkbasiert)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Ermöglicht der App, Ihren ungefähren Standort zu ermitteln. Diese Standortangabe stammt von Standortdiensten, die Netzwerkstandortquellen wie etwa Sendemasten oder WLAN verwenden. Diese Standortdienste müssen auf Ihrem Gerät verfügbar und aktiviert sein, damit die App sie verwenden kann. Apps können Ihren ungefähren Standort anhand dieser Daten ermitteln."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"Audio-Einstellungen ändern"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Ermöglicht der App, globale Audio-Einstellungen zu ändern, etwa die Lautstärke und den Lautsprecher für die Ausgabe."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"Audio aufnehmen"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Ermöglicht der App, Ton mithilfe des Mikrofons aufzunehmen. Die Berechtigung erlaubt der App, Tonaufnahmen jederzeit und ohne Ihre Bestätigung durchzuführen."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"SIM-Kommunikation"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"Befehle an die SIM senden"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Ermöglicht der App das Senden von Befehlen an die SIM-Karte. Dies ist äußerst risikoreich."</string>
<string name="permlab_camera" msgid="3616391919559751192">"Bilder und Videos aufnehmen"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Ermöglicht der App, Bilder und Videos mit der Kamera aufzunehmen. Die Berechtigung erlaubt der App, die Kamera jederzeit und ohne Ihre Bestätigung zu nutzen."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Ermöglicht der App, eine Liste der dem Telefon bekannten Konten abzurufen. Dabei kann es sich um Konten handeln, die von installierten Apps erstellt wurden."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"Netzwerkverbindungen abrufen"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Ermöglicht der App, Informationen zu Netzwerkverbindungen abzurufen, etwa welche Netzwerke existieren und verbunden sind."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"Voller Netzwerkzugriff"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"Auf alle Netzwerke zugreifen"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Ermöglicht der App die Erstellung von Netzwerk-Sockets und die Verwendung benutzerdefinierter Netzwerkprotokolle. Der Browser und andere Apps bieten die Möglichkeit, Daten über das Internet zu versenden. Daher ist diese Berechtigung nicht erforderlich, um Daten über das Internet versenden zu können."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"Netzwerkkonnektivität ändern"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Ermöglicht der App, den Status der Netzwerkkonnektivität zu ändern"</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Ermöglicht der App, das lokale Bluetooth-Telefon zu konfigurieren, Remote-Geräte zu erkennen und eine Verbindung zu diesen herzustellen"</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"WiMAX-Verbindungen herstellen und trennen"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Ermöglicht der App festzustellen, ob WiMAX aktiviert ist. Zudem kann sie Informationen zu verbundenen WiMAX-Netzwerken abrufen."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX-Status ändern"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"WiMAX-Status ändern"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Ermöglicht der App, eine Verbindung zwischen dem Tablet und WiMAX-Netzwerken herzustellen und solche zu trennen."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Ermöglicht der App, eine Verbindung zwischen dem Fernseher und WiMAX-Netzwerken herzustellen und diese zu trennen"</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Ermöglicht der App, eine Verbindung zwischen dem Telefon und WiMAX-Netzwerken herzustellen und solche zu trennen."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Ermöglicht der App, die Kalibrierungsparameter des Touchscreens zu ändern. Für normale Apps sollte dies nie erforderlich sein."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"Auf DRM-Zertifikate zugreifen"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Ermöglicht einer App die Bereitstellung und Nutzung von DRM-Zertifikaten. Sollte für normale Apps nie benötigt werden."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Status von Android Beam-Übertragungen erhalten"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"Status von Android Beam-Übertragungen erhalten"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Ermöglicht dieser App, Informationen zu aktuellen Android Beam-Übertragungen zu erhalten"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"DRM-Zertifikate entfernen"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Ermöglicht einer App das Entfernen von DRM-Zertifikaten. Sollte für normale Apps nie benötigt werden."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Falscher PIN-Code"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Drücken Sie zum Entsperren die Menütaste und dann auf \"0\"."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Notrufnummer"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Kein Dienst"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Kein Dienst"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Display gesperrt"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Drücken Sie die Menütaste, um das Telefon zu entsperren oder einen Notruf zu tätigen."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Zum Entsperren die Menütaste drücken"</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Weitere Informationen erhalten Sie im Nutzerhandbuch oder beim Kundendienst."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"PIN eingeben"</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"SIM-Karte wird entsperrt..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Sie haben Ihr Entsperrungsmuster <xliff:g id="NUMBER_0">%d</xliff:g>-mal falsch gezeichnet. \n\nBitte versuchen Sie es in <xliff:g id="NUMBER_1">%d</xliff:g> Sekunden noch einmal."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Sie haben Ihr Passwort <xliff:g id="NUMBER_0">%d</xliff:g>-mal falsch eingegeben.\n\nBitte versuchen Sie es in <xliff:g id="NUMBER_1">%d</xliff:g> Sekunden noch einmal."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Sie haben Ihr Passwort <xliff:g id="NUMBER_0">%d</xliff:g>-mal falsch eingegeben.\n\nBitte versuchen Sie es in <xliff:g id="NUMBER_1">%d</xliff:g> Sekunden noch einmal."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Sie haben Ihr Entsperrungsmuster <xliff:g id="NUMBER_0">%d</xliff:g>-mal falsch gezeichnet. Nach <xliff:g id="NUMBER_1">%d</xliff:g> weiteren erfolglosen Versuchen werden Sie aufgefordert, Ihr Tablet mithilfe Ihrer Google-Anmeldeinformationen zu entsperren.\n\n Bitte versuchen Sie es in <xliff:g id="NUMBER_2">%d</xliff:g> Sekunden noch einmal."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Sie haben Ihr Entsperrungsmuster <xliff:g id="NUMBER_0">%d</xliff:g>-mal falsch gezeichnet. Wenn Sie es noch <xliff:g id="NUMBER_1">%d</xliff:g>-mal falsch eingeben, werden Sie aufgefordert, Ihren Fernseher mithilfe Ihrer Google-Anmeldeinformationen zu entsperren.\n\n Versuchen Sie es in <xliff:g id="NUMBER_2">%d</xliff:g> Sekunden erneut."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Sie haben Ihr Entsperrungsmuster <xliff:g id="NUMBER_0">%d</xliff:g>-mal falsch gezeichnet. Nach <xliff:g id="NUMBER_1">%d</xliff:g> weiteren erfolglosen Versuchen werden Sie aufgefordert, Ihr Telefon mithilfe Ihrer Google-Anmeldeinformationen zu entsperren.\n\nBitte versuchen Sie es in <xliff:g id="NUMBER_2">%d</xliff:g> Sekunden noch einmal."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Sie haben <xliff:g id="NUMBER_0">%d</xliff:g> Mal erfolglos versucht, das Tablet zu entsperren. Nach <xliff:g id="NUMBER_1">%d</xliff:g> weiteren erfolglosen Versuchen wird das Tablet auf die Werkseinstellungen zurückgesetzt und alle Nutzerdaten gehen verloren."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Sie haben <xliff:g id="NUMBER_0">%d</xliff:g>-mal erfolglos versucht, den Fernseher zu entsperren. Nach <xliff:g id="NUMBER_1">%d</xliff:g> weiteren erfolglosen Versuchen wird der Fernseher auf die Werkseinstellungen zurückgesetzt und alle Nutzerdaten gehen verloren."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Sie haben <xliff:g id="NUMBER_0">%d</xliff:g> Mal erfolglos versucht, das Telefon zu entsperren. Nach <xliff:g id="NUMBER_1">%d</xliff:g> weiteren erfolglosen Versuchen wird das Telefon auf die Werkseinstellungen zurückgesetzt und alle Nutzerdaten gehen verloren."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Sie haben Ihr Entsperrungsmuster <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal falsch gezeichnet. \n\nBitte versuchen Sie es in <xliff:g id="NUMBER_1">%2$d</xliff:g> Sekunden noch einmal."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Sie haben Ihr Passwort <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal falsch eingegeben.\n\nBitte versuchen Sie es in <xliff:g id="NUMBER_1">%2$d</xliff:g> Sekunden noch einmal."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Sie haben Ihr Passwort <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal falsch eingegeben.\n\nBitte versuchen Sie es in <xliff:g id="NUMBER_1">%2$d</xliff:g> Sekunden noch einmal."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Sie haben Ihr Entsperrungsmuster <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal falsch gezeichnet. Nach <xliff:g id="NUMBER_1">%2$d</xliff:g> weiteren erfolglosen Versuchen werden Sie aufgefordert, Ihr Tablet mithilfe Ihrer Google-Anmeldeinformationen zu entsperren.\n\n Bitte versuchen Sie es in <xliff:g id="NUMBER_2">%3$d</xliff:g> Sekunden noch einmal."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Sie haben Ihr Entsperrungsmuster <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal falsch gezeichnet. Wenn Sie es noch <xliff:g id="NUMBER_1">%2$d</xliff:g>-mal falsch eingeben, werden Sie aufgefordert, Ihren Fernseher mithilfe Ihrer Google-Anmeldeinformationen zu entsperren.\n\n Versuchen Sie es in <xliff:g id="NUMBER_2">%3$d</xliff:g> Sekunden erneut."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Sie haben Ihr Entsperrungsmuster <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal falsch gezeichnet. Nach <xliff:g id="NUMBER_1">%2$d</xliff:g> weiteren erfolglosen Versuchen werden Sie aufgefordert, Ihr Telefon mithilfe Ihrer Google-Anmeldeinformationen zu entsperren.\n\nBitte versuchen Sie es in <xliff:g id="NUMBER_2">%3$d</xliff:g> Sekunden noch einmal."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Sie haben <xliff:g id="NUMBER_0">%1$d</xliff:g> Mal erfolglos versucht, das Tablet zu entsperren. Nach <xliff:g id="NUMBER_1">%2$d</xliff:g> weiteren erfolglosen Versuchen wird das Tablet auf die Werkseinstellungen zurückgesetzt und alle Nutzerdaten gehen verloren."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Sie haben <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal erfolglos versucht, den Fernseher zu entsperren. Nach <xliff:g id="NUMBER_1">%2$d</xliff:g> weiteren erfolglosen Versuchen wird der Fernseher auf die Werkseinstellungen zurückgesetzt und alle Nutzerdaten gehen verloren."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Sie haben <xliff:g id="NUMBER_0">%1$d</xliff:g> Mal erfolglos versucht, das Telefon zu entsperren. Nach <xliff:g id="NUMBER_1">%2$d</xliff:g> weiteren erfolglosen Versuchen wird das Telefon auf die Werkseinstellungen zurückgesetzt und alle Nutzerdaten gehen verloren."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Sie haben <xliff:g id="NUMBER">%d</xliff:g>-mal erfolglos versucht, das Tablet zu entsperren. Das Tablet wird nun auf die Werkseinstellungen zurückgesetzt."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Sie haben <xliff:g id="NUMBER">%d</xliff:g>-mal erfolglos versucht, den Fernseher zu entsperren. Der Fernseher wird nun auf die Werkseinstellungen zurückgesetzt."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Sie haben <xliff:g id="NUMBER">%d</xliff:g>-mal erfolglos versucht, das Telefon zu entsperren. Das Telefon wird nun auf die Werkseinstellungen zurückgesetzt."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"Wort"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"Link"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"Zeile"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Werkstest fehlgeschlagen"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Die Aktion FACTORY_TEST wird nur für unter \"/system/app\" gespeicherte Pakete unterstützt."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Es wurden kein Paket mit der Aktion FACTORY_TEST gefunden."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Wird formatiert..."</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Nicht eingelegt"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Keine passenden Aktivitäten gefunden"</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Medienausgabe umleiten"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"Medienausgabe umleiten"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Ermöglicht der App, die Medienausgabe auf andere externe Geräte umzuleiten."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Installationssitzungen lesen"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"Installationssitzungen lesen"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Ermöglicht der App, Installationssitzungen zu lesen. Dadurch kann sie Details aktiver Paketinstallationen abrufen."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Installation von Paketen anfordern"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"Installation von Paketen anfordern"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Ermöglicht der App, die Installation von Paketen anzufordern"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Für Zoomeinstellung zweimal berühren"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Widget konnte nicht hinzugefügt werden."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Ungültiger Nutzername oder ungültiges Passwort"</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Nutzernamen oder Passwort vergessen?\nBesuchen Sie "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Konto wird geprüft…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Sie haben Ihre PIN <xliff:g id="NUMBER_0">%d</xliff:g>-mal falsch eingegeben.\n\nVersuchen Sie es in <xliff:g id="NUMBER_1">%d</xliff:g> Sekunden erneut."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Sie haben Ihr Passwort <xliff:g id="NUMBER_0">%d</xliff:g>-mal falsch eingegeben.\n\nVersuchen Sie es in <xliff:g id="NUMBER_1">%d</xliff:g> Sekunden erneut."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Sie haben Ihr Entsperrungsmuster <xliff:g id="NUMBER_0">%d</xliff:g>-mal falsch gezeichnet. \n\nVersuchen Sie es in <xliff:g id="NUMBER_1">%d</xliff:g> Sekunden erneut."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Sie haben <xliff:g id="NUMBER_0">%d</xliff:g>-mal erfolglos versucht, das Tablet zu entsperren. Nach <xliff:g id="NUMBER_1">%d</xliff:g> weiteren erfolglosen Versuchen wird das Tablet auf die Werkseinstellungen zurückgesetzt und alle Nutzerdaten gehen verloren."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Sie haben <xliff:g id="NUMBER_0">%d</xliff:g>-mal erfolglos versucht, den Fernseher zu entsperren. Nach <xliff:g id="NUMBER_1">%d</xliff:g> weiteren erfolglosen Versuchen wird der Fernseher auf die Werkseinstellungen zurückgesetzt und alle Nutzerdaten gehen verloren."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Sie haben <xliff:g id="NUMBER_0">%d</xliff:g>-mal erfolglos versucht, das Telefon zu entsperren. Nach <xliff:g id="NUMBER_1">%d</xliff:g> weiteren erfolglosen Versuchen wird das Telefon auf die Werkseinstellungen zurückgesetzt und alle Nutzerdaten gehen verloren."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Sie haben Ihre PIN <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal falsch eingegeben.\n\nVersuchen Sie es in <xliff:g id="NUMBER_1">%2$d</xliff:g> Sekunden erneut."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Sie haben Ihr Passwort <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal falsch eingegeben.\n\nVersuchen Sie es in <xliff:g id="NUMBER_1">%2$d</xliff:g> Sekunden erneut."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Sie haben Ihr Entsperrungsmuster <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal falsch gezeichnet. \n\nVersuchen Sie es in <xliff:g id="NUMBER_1">%2$d</xliff:g> Sekunden erneut."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Sie haben <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal erfolglos versucht, das Tablet zu entsperren. Nach <xliff:g id="NUMBER_1">%2$d</xliff:g> weiteren erfolglosen Versuchen wird das Tablet auf die Werkseinstellungen zurückgesetzt und alle Nutzerdaten gehen verloren."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Sie haben <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal erfolglos versucht, den Fernseher zu entsperren. Nach <xliff:g id="NUMBER_1">%2$d</xliff:g> weiteren erfolglosen Versuchen wird der Fernseher auf die Werkseinstellungen zurückgesetzt und alle Nutzerdaten gehen verloren."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Sie haben <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal erfolglos versucht, das Telefon zu entsperren. Nach <xliff:g id="NUMBER_1">%2$d</xliff:g> weiteren erfolglosen Versuchen wird das Telefon auf die Werkseinstellungen zurückgesetzt und alle Nutzerdaten gehen verloren."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Sie haben <xliff:g id="NUMBER">%d</xliff:g>-mal erfolglos versucht, das Tablet zu entsperren. Das Tablet wird nun auf die Werkseinstellungen zurückgesetzt."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Sie haben <xliff:g id="NUMBER">%d</xliff:g>-mal erfolglos versucht, den Fernseher zu entsperren. Der Fernseher wird nun auf die Werkseinstellungen zurückgesetzt."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Sie haben <xliff:g id="NUMBER">%d</xliff:g>-mal erfolglos versucht, das Telefon zu entsperren. Das Telefon wird nun auf die Werkseinstellungen zurückgesetzt."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Sie haben Ihr Entsperrungsmuster <xliff:g id="NUMBER_0">%d</xliff:g>-mal falsch gezeichnet. Nach <xliff:g id="NUMBER_1">%d</xliff:g> weiteren erfolglosen Versuchen werden Sie aufgefordert, Ihr Tablet mithilfe eines E-Mail-Kontos zu entsperren.\n\n Versuchen Sie es in <xliff:g id="NUMBER_2">%d</xliff:g> Sekunden erneut."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Sie haben Ihr Entsperrungsmuster <xliff:g id="NUMBER_0">%d</xliff:g>-mal falsch gezeichnet. Wenn Sie es noch <xliff:g id="NUMBER_1">%d</xliff:g>-mal falsch eingeben, werden Sie aufgefordert, Ihren Fernseher mithilfe eines E-Mail-Kontos zu entsperren.\n\n Versuchen Sie es in <xliff:g id="NUMBER_2">%d</xliff:g> Sekunden erneut."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Sie haben Ihr Entsperrungsmuster <xliff:g id="NUMBER_0">%d</xliff:g>-mal falsch gezeichnet. Nach <xliff:g id="NUMBER_1">%d</xliff:g> weiteren erfolglosen Versuchen werden Sie aufgefordert, Ihr Telefon mithilfe eines E-Mail-Kontos zu entsperren.\n\n Versuchen Sie es in <xliff:g id="NUMBER_2">%d</xliff:g> Sekunden erneut."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Sie haben Ihr Entsperrungsmuster <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal falsch gezeichnet. Nach <xliff:g id="NUMBER_1">%2$d</xliff:g> weiteren erfolglosen Versuchen werden Sie aufgefordert, Ihr Tablet mithilfe eines E-Mail-Kontos zu entsperren.\n\n Versuchen Sie es in <xliff:g id="NUMBER_2">%3$d</xliff:g> Sekunden erneut."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Sie haben Ihr Entsperrungsmuster <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal falsch gezeichnet. Wenn Sie es noch <xliff:g id="NUMBER_1">%2$d</xliff:g>-mal falsch eingeben, werden Sie aufgefordert, Ihren Fernseher mithilfe eines E-Mail-Kontos zu entsperren.\n\n Versuchen Sie es in <xliff:g id="NUMBER_2">%3$d</xliff:g> Sekunden erneut."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Sie haben Ihr Entsperrungsmuster <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal falsch gezeichnet. Nach <xliff:g id="NUMBER_1">%2$d</xliff:g> weiteren erfolglosen Versuchen werden Sie aufgefordert, Ihr Telefon mithilfe eines E-Mail-Kontos zu entsperren.\n\n Versuchen Sie es in <xliff:g id="NUMBER_2">%3$d</xliff:g> Sekunden erneut."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Entfernen"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Lautstärke über den Schwellenwert anheben?\n\nWenn Sie über längere Zeiträume hinweg Musik in hoher Lautstärke hören, kann dies Ihr Gehör schädigen."</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">Für 1 h</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Bis <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Bis <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (nächste Weckzeit)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Bis zur Deaktivierung"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Bis zur Deaktivierung von \"Nicht stören\""</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-el-watch/strings.xml b/core/res/res/values-el-watch/strings.xml
index 83d9c3d..b142adb 100644
--- a/core/res/res/values-el-watch/strings.xml
+++ b/core/res/res/values-el-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Εφαρμογή <xliff:g id="NUMBER_0">%1$d</xliff:g> από <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Αισθητήρες"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"πρόσβαση στις επαφές σας"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"πρόσβαση στην τοποθεσία αυτού του ρολογιού"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"πρόσβαση στο ημερολόγιό σας"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"αποστολή και προβολή μηνυμάτων SMS"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"πρόσβαση στις φωτογραφίες, τα πολυμέσα και τα αρχεία στο ρολόι σας"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"εγγραφή ήχου"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"λήψη φωτογραφιών και εγγραφή βίντεο"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"πραγματοποίηση και διαχείριση τηλεφωνικών κλήσεων"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"πρόσβαση στα δεδομένα αισθητήρα σχετικά με τις ζωτικές ενδείξεις σας"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"ορισμός ως γραμμής κατάστασης"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"πρόσβαση στους αισθητήρες λειτουργιών (π.χ. παρακολούθηση καρδιακού παλμού)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"πρόσβαση στην ακριβή τοποθεσία (με βάση το GPS και το δίκτυο)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"πρόσβαση στην τοποθεσία κατά προσέγγιση (με βάση το δίκτυο)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"στέλνει εντολές στην κάρτα SIM"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"πλήρης πρόσβαση στο δίκτυο"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"διαχείριση προφίλ και κατόχων συσκευής"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"αλλαγή κατάστασης WiMAX"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"λήψη κατάστασης μεταφοράς Android Beam"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"δρομολόγηση εξόδου μέσων"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"ανάγνωση περιόδων σύνδεσης εγκατάστασης"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"αίτημα εγκατάστασης πακέτων"</string>
</resources>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index f2bb1d5..83ad7de 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Περιλαμβάνει προσωπικά δεδομένα, όπως είναι οι αριθμοί πιστωτικών καρτών και οι κωδικοί πρόσβασης."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"απενεργοποίηση ή τροποποίηση γραμμής κατάστασης"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Επιτρέπει στην εφαρμογή να απενεργοποιεί τη γραμμή κατάστασης ή να προσθέτει και να αφαιρεί εικονίδια συστήματος."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"γραμμή κατάστασης"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"ορισμός ως γραμμής κατάστασης"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Επιτρέπει στην εφαρμογή να αποτελεί τη γραμμή κατάστασης."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"ανάπτυξη/σύμπτυξη γραμμής κατάστασης"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Επιτρέπει στην εφαρμογή να αναπτύξει ή να συμπτύξει τη γραμμή κατάστασης."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Επιτρέπει στην εφαρμογή τη λήψη και την επεξεργασία μηνυμάτων WAP. Αυτό σημαίνει ότι η εφαρμογή θα μπορούσε να παρακολουθήσει ή να διαγράψει τα μηνύματα που αποστέλλονται στη συσκευή σας χωρίς αυτά να εμφανιστούν σε εσάς."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"ανάκτηση εκτελούμενων εφαρμογών"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Επιτρέπει στην εφαρμογή την ανάκτηση πληροφοριών σχετικά με τρέχουσες και πρόσφατα εκτελούμενες εργασίες. Αυτό μπορεί να δίνει τη δυνατότητα στην εφαρμογή να ανακαλύπτει πληροφορίες σχετικά με το ποιες εφαρμογές χρησιμοποιούνται στη συσκευή."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Διαχείριση προφίλ και κατόχων συσκευής"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"διαχείριση προφίλ και κατόχων συσκευής"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Επιτρέπει σε εφαρμογές να ορίζουν τους κατόχους προφίλ και τον κάτοχο της συσκευής."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"αναδιάταξη εκτελούμενων εφαρμογών"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Επιτρέπει στην εφαρμογή τη μετακίνηση εργασιών στο προσκήνιο και το παρασκήνιο. Η εφαρμογή μπορεί να το κάνει αυτό χωρίς να καταχωρίσετε δεδομένα εισόδου."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Επιτρέπει στην εφαρμογή να τροποποιεί το αρχείο καταγραφής κλήσεων του tablet σας, συμπεριλαμβανομένων των δεδομένων σχετικά με τις εισερχόμενες και τις εξερχόμενες κλήσεις. Οι κακόβουλες εφαρμογές μπορεί να το χρησιμοποιήσουν για να διαγράψουν ή να τροποποιήσουν το αρχείο καταγραφής κλήσεων."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Επιτρέπει στην εφαρμογή να τροποποιεί το αρχείο καταγραφής κλήσεων της τηλεόρασής σας, συμπεριλαμβανομένων των δεδομένων σχετικά με τις εισερχόμενες και τις εξερχόμενες κλήσεις. Κακόβουλες εφαρμογές μπορεί να χρησιμοποιήσουν αυτήν τη δυνατότητα, για να διαγράψουν ή να τροποποιήσουν το αρχείο καταγραφής κλήσεων."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Επιτρέπει στην εφαρμογή να τροποποιεί το αρχείο καταγραφής κλήσεων του τηλεφώνου σας, συμπεριλαμβανομένων των δεδομένων σχετικά με τις εισερχόμενες και τις εξερχόμενες κλήσεις. Οι κακόβουλες εφαρμογές μπορεί να το χρησιμοποιήσουν για να διαγράψουν ή να τροποποιήσουν το αρχείο καταγραφής κλήσεων."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"αισθητήρες λειτουργιών (π.χ. καρδιακό ρυθμό)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"πρόσβαση στους αισθητήρες λειτουργιών (π.χ. παρακολούθηση καρδιακού παλμού)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Επιτρέπει στην εφαρμογή να αποκτήσει πρόσβαση στα δεδομένα των αισθητήρων που παρακολουθούν τη φυσική σας κατάσταση, όπως τον καρδιακό ρυθμό σας."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"ανάγνωση συμβάντων ημερολογίου και εμπιστευτικών πληροφοριών"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Επιτρέπει στην εφαρμογή την ανάγνωση όλων των συμβάντων ημερολογίου που είναι αποθηκευμένα στο tablet σας, συμπεριλαμβανομένων εκείνων των φίλων ή των συναδέλφων σας. Αυτό μπορεί να δίνει τη δυνατότητα στην εφαρμογή να μοιράζεται ή να αποθηκεύει τα δεδομένα του ημερολογίου σας, ανεξάρτητα από το βαθμό εμπιστευτικότητας ή ευαισθησίας τους."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Επιτρέπει στην εφαρμογή την προσθήκη, την κατάργηση και την αλλαγή συμβάντων που μπορείτε να τροποποιήσετε στο τηλέφωνό σας, συμπεριλαμβανομένων εκείνων των φίλων ή των συναδέλφων σας. Αυτό μπορεί να επιτρέπει στην εφαρμογή να αποστέλλει μηνύματα που φαίνεται ότι προέρχονται από κατόχους ημερολογίων ή να τροποποιεί συμβάντα χωρίς να το γνωρίζουν οι κάτοχοι."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"πρόσβαση σε επιπλέον εντολές παρόχου τοποθεσίας"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Επιτρέπει στην εφαρμογή την πρόσβαση σε επιπλέον εντολές παρόχου τοποθεσίας. Αυτό μπορεί να δώσει τη δυνατότητα στην εφαρμογή να παρέμβει στη λειτουργία του GPS ή άλλων πηγών τοποθεσίας."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"ακριβής θέση (GPS και βάσει δικτύου)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"πρόσβαση στην ακριβή τοποθεσία (με βάση το GPS και το δίκτυο)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Επιτρέπει στην εφαρμογή να λαμβάνει την ακριβή θέση σας με τη χρήση του Παγκόσμιου Συστήματος Εντοπισμού (GPS) ή πηγών τοποθεσίας δικτύου, όπως κεραίες κινητής τηλεφωνίας και Wi-Fi. Αυτές οι υπηρεσίες τοποθεσίας πρέπει να είναι ενεργοποιημένες και διαθέσιμες στην συσκευή σας, ώστε να μπορούν να χρησιμοποιηθούν από την εφαρμογή. Οι εφαρμογές ενδέχεται να τις χρησιμοποιήσουν για να προσδιορίσουν τη θέση σας και ενδέχεται να καταναλώσουν επιπλέον ισχύ μπαταρίας."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"κατά προσέγγιση θέση (βάσει δικτύου)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"πρόσβαση στην τοποθεσία κατά προσέγγιση (με βάση το δίκτυο)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Επιτρέπει στην εφαρμογή τη λήψη της κατά προσέγγιση τοποθεσίας σας. Αυτή η τοποθεσία προκύπτει από τις υπηρεσίες τοποθεσίας με τη χρήση πηγών τοποθεσίας δικτύου, όπως κεραίες κινητής τηλεφωνίας και Wi-Fi. Αυτές οι υπηρεσίες τοποθεσίας πρέπει να είναι ενεργοποιημένες και διαθέσιμες στην συσκευή σας, ώστε να μπορούν να χρησιμοποιηθούν από την εφαρμογή. Οι εφαρμογές ενδέχεται να τις χρησιμοποιήσουν για να προσδιορίσουν κατά προσέγγιση τη θέση σας."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"αλλαγή των ρυθμίσεων ήχου"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Επιτρέπει στην εφαρμογή την τροποποίηση καθολικών ρυθμίσεων ήχου, όπως η ένταση και ποιο ηχείο χρησιμοποιείται για έξοδο."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"εγγραφή ήχου"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Επιτρέπει στην εφαρμογή την εγγραφή ήχου με το μικρόφωνο. Αυτή η άδεια δίνει τη δυνατότητα στην εφαρμογή να εγγράφει ήχο ανά πάσα στιγμή χωρίς την έγκρισή σας."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"επικοινωνία με κάρτα sim"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"στέλνει εντολές στην κάρτα SIM"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Επιτρέπει στην εφαρμογή την αποστολή εντολών στην κάρτα SIM. Αυτό είναι εξαιρετικά επικίνδυνο."</string>
<string name="permlab_camera" msgid="3616391919559751192">"λήψη φωτογραφιών και βίντεο"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Επιτρέπει στην εφαρμογή τη λήψη φωτογραφιών και βίντεο με τη φωτογραφική μηχανή. Αυτή η άδεια δίνει τη δυνατότητα στην εφαρμογή να χρησιμοποιεί τη φωτογραφική μηχανή ανά πάσα στιγμή χωρίς την έγκρισή σας."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Επιτρέπει στην εφαρμογή τη λήψη της λίστας λογαριασμών που υπάρχουν στο τηλέφωνο. Μπορεί να περιλαμβάνονται τυχόν λογαριασμοί που δημιουργήθηκαν από εφαρμογές που έχετε εγκαταστήσει."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"προβολή συνδέσεων δικτύου"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Επιτρέπει στην εφαρμογή την προβολή πληροφοριών σχετικά με συνδέσεις δικτύου, όπως ποια δίκτυα υπάρχουν και είναι συνδεδεμένα."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"πλήρης πρόσβαση στο δίκτυο"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"πλήρης πρόσβαση στο δίκτυο"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Επιτρέπει στην εφαρμογή τη δημιουργία θέσεων δικτύου και τη χρήση προσαρμοσμένων πρωτοκόλλων δικτύου. Το πρόγραμμα περιήγησης και άλλες εφαρμογές παρέχουν μέσα για την αποστολή δεδομένων στο διαδίκτυο, επομένως η συγκεκριμένη άδεια δεν είναι απαραίτητη για την αποστολή δεδομένων στο διαδίκτυο."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"αλλαγή συνδεσιμότητας δικτύου"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Επιτρέπει στην εφαρμογή την αλλαγή της κατάστασης συνδεσιμότητας δικτύου."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Επιτρέπει στην εφαρμογή τη διαμόρφωση του τοπικού tablet Bluetooth, τον εντοπισμό και τη σύζευξη με απομακρυσμένες συσκευές."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"σύνδεση και αποσύνδεση από το WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Επιτρέπει στην εφαρμογή να προσδιορίζει εάν το WiMAX είναι ενεργοποιημένο και πληροφορίες σχετικά με τυχόν δίκτυα WiMAX που είναι συνδεδεμένα."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Αλλαγή κατάστασης WiMAX"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"αλλαγή κατάστασης WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Επιτρέπει στην εφαρμογή τη σύνδεση στο tablet και την αποσύνδεση από αυτό, από δίκτυα WiMAX."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Επιτρέπει στην εφαρμογή να συνδέει και να αποσυνδέει την τηλεόραση από δίκτυα WiMAX."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Επιτρέπει στην εφαρμογή τη σύνδεση στο τηλέφωνο και την αποσύνδεση από αυτό, από δίκτυα WiMAX."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Επιτρέπει στην εφαρμογή να τροποποιεί τις παραμέτρους βαθμονόμησης της οθόνης αφής. Δεν απαιτείται για τις κανονικές εφαρμογές."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"πρόσβαση σε πιστοποιητικά DRM"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Επιτρέπει σε μια εφαρμογή να παρέχει και να χρησιμοποιεί πιστοποιητικά DRM. Δεν θα χρειαστεί ποτέ για κανονικές εφαρμογές."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Λήψη κατάστασης μεταφοράς Android Beam"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"λήψη κατάστασης μεταφοράς Android Beam"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Επιτρέπει σε αυτήν την εφαρμογή να λαμβάνει πληροφορίες σχετικά με τις τρέχουσες μεταφορές Android Beam"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"κατάργηση πιστοποιητικών DRM"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Επιτρέπει σε μια εφαρμογή την κατάργηση πιστοποιητικών DRM. Δεν χρειάζεται ποτέ για κανονικές εφαρμογές."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Λανθασμένος κωδικός PIN."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Για ξεκλείδωμα, πατήστε το πλήκτρο Menu και, στη συνέχεια, το πλήκτρο 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Αριθμός έκτακτης ανάγκης"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Καμία υπηρεσία."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Δίκτυο μη διαθέσιμο"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Η οθόνη κλειδώθηκε."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Πατήστε \"Menu\" για ξεκλείδωμα ή για κλήση έκτακτης ανάγκης."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Πατήστε \"Μενού\" για ξεκλείδωμα."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Ανατρέξτε στον Οδηγό χρήσης ή επικοινωνήστε με την Εξυπηρέτηση πελατών."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"Η κάρτα SIM είναι κλειδωμένη."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Ξεκλείδωμα κάρτας SIM..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Σχεδιάσατε εσφαλμένα το μοτίβο ξεκλειδώματος<xliff:g id="NUMBER_0">%d</xliff:g> φορές. \n\nΠροσπαθήστε ξανά σε <xliff:g id="NUMBER_1">%d</xliff:g> δευτερόλεπτα."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Έχετε πληκτρολογήσει τον κωδικό πρόσβασης εσφαλμένα <xliff:g id="NUMBER_0">%d</xliff:g> φορές. \n\nΠροσπαθήστε ξανά σε <xliff:g id="NUMBER_1">%d</xliff:g> δευτερόλεπτα."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Έχετε πληκτρολογήσει τον αριθμό σας PIN εσφαλμένα <xliff:g id="NUMBER_0">%d</xliff:g> φορές. \n\nΠροσπαθήστε ξανά σε <xliff:g id="NUMBER_1">%d</xliff:g> δευτερόλεπτα."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Σχεδιάσατε το μοτίβο ξεκλειδώματος εσφαλμένα <xliff:g id="NUMBER_0">%d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%d</xliff:g> ανεπιτυχείς προσπάθειες ακόμη, θα σας ζητηθεί να ξεκλειδώσετε το tablet σας με τη χρήση της σύνδεσής σας Google.\n\n Προσπαθήστε ξανά σε <xliff:g id="NUMBER_2">%d</xliff:g> δευτερόλεπτα."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Έχετε σχεδιάσει λανθασμένα το μοτίβο ξεκλειδώματος <xliff:g id="NUMBER_0">%d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%d</xliff:g> ακόμα μη επιτυχημένες προσπάθειες, θα σας ζητηθεί να ξεκλειδώσετε την τηλεόρασή σας χρησιμοποιώντας τη σύνδεση Google.\n\n Δοκιμάστε ξανά σε <xliff:g id="NUMBER_2">%d</xliff:g> δευτερόλεπτα."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Σχεδιάσατε το μοτίβο ξεκλειδώματος εσφαλμένα <xliff:g id="NUMBER_0">%d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%d</xliff:g> ανεπιτυχείς προσπάθειες ακόμη, θα σας ζητηθεί να ξεκλειδώσετε το τηλέφωνό σας με τη χρήση της σύνδεσής σας Google.\n\n Προσπαθήστε ξανά σε <xliff:g id="NUMBER_2">%d</xliff:g> δευτερόλεπτα."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Προσπαθήσατε να ξεκλειδώσετε εσφαλμένα το tablet <xliff:g id="NUMBER_0">%d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%d</xliff:g> προσπάθειες, το tablet θα επαναφερθεί στις εργοστασιακές ρυθμίσεις και όλα τα δεδομένα χρήστη θα χαθούν."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Έχετε προσπαθήσει να ξεκλειδώσετε λανθασμένα την τηλεόραση <xliff:g id="NUMBER_0">%d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%d</xliff:g> ακόμα μη επιτυχημένες προσπάθειες, η τηλεόραση θα επανέλθει στις προεπιλεγμένες, εργοστασιακές ρυθμίσεις και όλα τα δεδομένα χρήστη θα χαθούν."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Προσπαθήσατε να ξεκλειδώσετε εσφαλμένα το τηλέφωνο <xliff:g id="NUMBER_0">%d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%d</xliff:g> προσπάθειες, το τηλέφωνο θα επαναφερθεί στις εργοστασιακές ρυθμίσεις και όλα τα δεδομένα χρήστη θα χαθούν."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Σχεδιάσατε εσφαλμένα το μοτίβο ξεκλειδώματος<xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. \n\nΠροσπαθήστε ξανά σε <xliff:g id="NUMBER_1">%2$d</xliff:g> δευτερόλεπτα."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Έχετε πληκτρολογήσει τον κωδικό πρόσβασης εσφαλμένα <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. \n\nΠροσπαθήστε ξανά σε <xliff:g id="NUMBER_1">%2$d</xliff:g> δευτερόλεπτα."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Έχετε πληκτρολογήσει τον αριθμό σας PIN εσφαλμένα <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. \n\nΠροσπαθήστε ξανά σε <xliff:g id="NUMBER_1">%2$d</xliff:g> δευτερόλεπτα."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Σχεδιάσατε το μοτίβο ξεκλειδώματος εσφαλμένα <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%2$d</xliff:g> ανεπιτυχείς προσπάθειες ακόμη, θα σας ζητηθεί να ξεκλειδώσετε το tablet σας με τη χρήση της σύνδεσής σας Google.\n\n Προσπαθήστε ξανά σε <xliff:g id="NUMBER_2">%3$d</xliff:g> δευτερόλεπτα."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Έχετε σχεδιάσει λανθασμένα το μοτίβο ξεκλειδώματος <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%2$d</xliff:g> ακόμα μη επιτυχημένες προσπάθειες, θα σας ζητηθεί να ξεκλειδώσετε την τηλεόρασή σας χρησιμοποιώντας τη σύνδεση Google.\n\n Δοκιμάστε ξανά σε <xliff:g id="NUMBER_2">%3$d</xliff:g> δευτερόλεπτα."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Σχεδιάσατε το μοτίβο ξεκλειδώματος εσφαλμένα <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%2$d</xliff:g> ανεπιτυχείς προσπάθειες ακόμη, θα σας ζητηθεί να ξεκλειδώσετε το τηλέφωνό σας με τη χρήση της σύνδεσής σας Google.\n\n Προσπαθήστε ξανά σε <xliff:g id="NUMBER_2">%3$d</xliff:g> δευτερόλεπτα."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Προσπαθήσατε να ξεκλειδώσετε εσφαλμένα το tablet <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%2$d</xliff:g> προσπάθειες, το tablet θα επαναφερθεί στις εργοστασιακές ρυθμίσεις και όλα τα δεδομένα χρήστη θα χαθούν."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Έχετε προσπαθήσει να ξεκλειδώσετε λανθασμένα την τηλεόραση <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%2$d</xliff:g> ακόμα μη επιτυχημένες προσπάθειες, η τηλεόραση θα επανέλθει στις προεπιλεγμένες, εργοστασιακές ρυθμίσεις και όλα τα δεδομένα χρήστη θα χαθούν."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Προσπαθήσατε να ξεκλειδώσετε εσφαλμένα το τηλέφωνο <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%2$d</xliff:g> προσπάθειες, το τηλέφωνο θα επαναφερθεί στις εργοστασιακές ρυθμίσεις και όλα τα δεδομένα χρήστη θα χαθούν."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Προσπαθήσατε να ξεκλειδώσετε εσφαλμένα το tablet <xliff:g id="NUMBER">%d</xliff:g> φορές. Το tablet θα επαναφερθεί στην εργοστασιακή προεπιλογή."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Έχετε προσπαθήσει να ξεκλειδώσετε λανθασμένα την τηλεόραση <xliff:g id="NUMBER">%d</xliff:g> φορές. Η τηλεόραση θα επανέλθει τώρα στις προεπιλεγμένες, εργοστασιακές ρυθμίσεις."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Προσπαθήσατε να ξεκλειδώσετε εσφαλμένα το τηλέφωνο <xliff:g id="NUMBER">%d</xliff:g> φορές. Το τηλέφωνο θα επαναφερθεί στην εργοστασιακή προεπιλογή."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"λέξη"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"σύνδεσμος"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"γραμμή"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Η εργοστασιακή δοκιμή απέτυχε"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Η ενέργεια FACTORY_TEST υποστηρίζεται μόνο για πακέτα που είναι εγκατεστημένα στον κατάλογο /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Δεν βρέθηκε πακέτο που να παρέχει την ενέργεια FACTORY_TEST."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Διαμόρφωση…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Δεν έχει εισαχθεί"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Δεν βρέθηκαν δραστηριότητες που να συμφωνούν με τα κριτήρια."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Διαγραφή διαδρομής δεδομένων εξόδου μέσων"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"δρομολόγηση εξόδου μέσων"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Επιτρέπει σε μια εφαρμογή τη διαγραφή διαδρομής δεδομένων εξόδου μέσων σε άλλες εξωτερικές συσκευές."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Ανάγνωση περιόδων σύνδεσης εγκατάστασης"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"ανάγνωση περιόδων σύνδεσης εγκατάστασης"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Επιτρέπει σε μια εφαρμογή την ανάγνωση των περιόδων σύνδεσης εγκατάστασης. Αυτό της επιτρέπει να βλέπει λεπτομέρειες σχετικά με τις εγκαταστάσεις του ενεργού πακέτου."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Αίτημα εγκατάστασης πακέτων."</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"αίτημα εγκατάστασης πακέτων"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Επιτρέπει σε μια εφαρμογή να ζητά εγκατάσταση πακέτων."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Αγγίξτε δύο φορές για έλεγχο εστίασης"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Δεν ήταν δυνατή η προσθήκη του γραφικού στοιχείου."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Μη έγκυρο όνομα χρήστη ή κωδικός πρόσβασης."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Ξεχάσατε το όνομα χρήστη ή τον κωδικό πρόσβασής σας;\nΕπισκεφτείτε τη διεύθυνση "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Έλεγχος λογαριασμού…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Έχετε πληκτρολογήσει εσφαλμένα τον κωδικό σας PIN <xliff:g id="NUMBER_0">%d</xliff:g> φορές. \n\nΔοκιμάστε ξανά σε <xliff:g id="NUMBER_1">%d</xliff:g> δευτερόλεπτα."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Έχετε πληκτρολογήσει τον κωδικό πρόσβασης εσφαλμένα <xliff:g id="NUMBER_0">%d</xliff:g> φορές. \n\nΔοκιμάστε ξανά σε <xliff:g id="NUMBER_1">%d</xliff:g> δευτερόλεπτα."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Σχεδιάσατε εσφαλμένα το μοτίβο ξεκλειδώματος <xliff:g id="NUMBER_0">%d</xliff:g> φορές. \n\nΔοκιμάστε ξανά σε <xliff:g id="NUMBER_1">%d</xliff:g> δευτερόλετπα."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Προσπαθήσατε να ξεκλειδώσετε εσφαλμένα το tablet <xliff:g id="NUMBER_0">%d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%d</xliff:g> προσπάθειες, το tablet θα επαναφερθεί στις εργοστασιακές ρυθμίσεις και όλα τα δεδομένα χρήστη θα χαθούν."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Έχετε προσπαθήσει να ξεκλειδώσετε λανθασμένα την τηλεόραση <xliff:g id="NUMBER_0">%d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%d</xliff:g> ακόμα μη επιτυχημένες προσπάθειες, η τηλεόραση θα επανέλθει στις προεπιλεγμένες, εργοστασιακές ρυθμίσεις και όλα τα δεδομένα χρήστη θα χαθούν."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Προσπαθήσατε να ξεκλειδώσετε εσφαλμένα το τηλέφωνο <xliff:g id="NUMBER_0">%d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%d</xliff:g> προσπάθειες, το τηλέφωνο θα επαναφερθεί στις εργοστασιακές ρυθμίσεις και όλα τα δεδομένα χρήστη θα χαθούν."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Έχετε πληκτρολογήσει εσφαλμένα τον κωδικό σας PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. \n\nΔοκιμάστε ξανά σε <xliff:g id="NUMBER_1">%2$d</xliff:g> δευτερόλεπτα."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Έχετε πληκτρολογήσει τον κωδικό πρόσβασης εσφαλμένα <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. \n\nΔοκιμάστε ξανά σε <xliff:g id="NUMBER_1">%2$d</xliff:g> δευτερόλεπτα."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Σχεδιάσατε εσφαλμένα το μοτίβο ξεκλειδώματος <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. \n\nΔοκιμάστε ξανά σε <xliff:g id="NUMBER_1">%2$d</xliff:g> δευτερόλετπα."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Προσπαθήσατε να ξεκλειδώσετε εσφαλμένα το tablet <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%2$d</xliff:g> προσπάθειες, το tablet θα επαναφερθεί στις εργοστασιακές ρυθμίσεις και όλα τα δεδομένα χρήστη θα χαθούν."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Έχετε προσπαθήσει να ξεκλειδώσετε λανθασμένα την τηλεόραση <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%2$d</xliff:g> ακόμα μη επιτυχημένες προσπάθειες, η τηλεόραση θα επανέλθει στις προεπιλεγμένες, εργοστασιακές ρυθμίσεις και όλα τα δεδομένα χρήστη θα χαθούν."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Προσπαθήσατε να ξεκλειδώσετε εσφαλμένα το τηλέφωνο <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%2$d</xliff:g> προσπάθειες, το τηλέφωνο θα επαναφερθεί στις εργοστασιακές ρυθμίσεις και όλα τα δεδομένα χρήστη θα χαθούν."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Προσπαθήσατε να ξεκλειδώσετε εσφαλμένα το tablet <xliff:g id="NUMBER">%d</xliff:g> φορές. Το tablet θα επαναφερθεί στις εργοστασιακές ρυθμίσεις."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Έχετε προσπαθήσει να ξεκλειδώσετε λανθασμένα την τηλεόραση <xliff:g id="NUMBER">%d</xliff:g> φορές. Η τηλεόραση θα επανέλθει τώρα στις προεπιλεγμένες, εργοστασιακές ρυθμίσεις."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Προσπαθήσατε να ξεκλειδώσετε εσφαλμένα το τηλέφωνο <xliff:g id="NUMBER">%d</xliff:g> φορές. Το τηλέφωνο θα επαναφερθεί στις εργοστασιακές ρυθμίσεις."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Σχεδιάσατε το μοτίβο ξεκλειδώματος εσφαλμένα <xliff:g id="NUMBER_0">%d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%d</xliff:g> ανεπιτυχείς προσπάθειες ακόμη, θα σας ζητηθεί να ξεκλειδώσετε το tablet σας με τη χρήση ενός λογαριασμού ηλεκτρονικού ταχυδρομείου.\n\n Δοκιμάστε να συνδεθείτε ξανά σε <xliff:g id="NUMBER_2">%d</xliff:g> δευτερόλεπτα."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Έχετε σχεδιάσει λανθασμένα το μοτίβο ξεκλειδώματος <xliff:g id="NUMBER_0">%d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%d</xliff:g> ακόμα μη επιτυχημένες προσπάθειες, θα σας ζητηθεί να ξεκλειδώσετε την τηλεόρασή σας χρησιμοποιώντας έναν λογαριασμό ηλεκτρονικού ταχυδρομείου.\n\n Δοκιμάστε ξανά σε <xliff:g id="NUMBER_2">%d</xliff:g> δευτερόλεπτα."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Σχεδιάσατε το μοτίβο ξεκλειδώματος εσφαλμένα <xliff:g id="NUMBER_0">%d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%d</xliff:g> ανεπιτυχείς προσπάθειες ακόμη, θα σας ζητηθεί να ξεκλειδώσετε το τηλέφωνό σας με τη χρήση ενός λογαριασμού ηλεκτρονικού ταχυδρομείου.\n\n Δοκιμάστε ξανά σε <xliff:g id="NUMBER_2">%d</xliff:g> δευτερόλεπτα."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Σχεδιάσατε το μοτίβο ξεκλειδώματος εσφαλμένα <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%2$d</xliff:g> ανεπιτυχείς προσπάθειες ακόμη, θα σας ζητηθεί να ξεκλειδώσετε το tablet σας με τη χρήση ενός λογαριασμού ηλεκτρονικού ταχυδρομείου.\n\n Δοκιμάστε να συνδεθείτε ξανά σε <xliff:g id="NUMBER_2">%3$d</xliff:g> δευτερόλεπτα."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Έχετε σχεδιάσει λανθασμένα το μοτίβο ξεκλειδώματος <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%2$d</xliff:g> ακόμα μη επιτυχημένες προσπάθειες, θα σας ζητηθεί να ξεκλειδώσετε την τηλεόρασή σας χρησιμοποιώντας έναν λογαριασμό ηλεκτρονικού ταχυδρομείου.\n\n Δοκιμάστε ξανά σε <xliff:g id="NUMBER_2">%3$d</xliff:g> δευτερόλεπτα."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Σχεδιάσατε το μοτίβο ξεκλειδώματος εσφαλμένα <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%2$d</xliff:g> ανεπιτυχείς προσπάθειες ακόμη, θα σας ζητηθεί να ξεκλειδώσετε το τηλέφωνό σας με τη χρήση ενός λογαριασμού ηλεκτρονικού ταχυδρομείου.\n\n Δοκιμάστε ξανά σε <xliff:g id="NUMBER_2">%3$d</xliff:g> δευτερόλεπτα."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Κατάργηση"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Αυξάνετε την ένταση ήχου πάνω από το επίπεδο ασφαλείας;\n\nΑν ακούτε μουσική σε υψηλή ένταση για μεγάλο χρονικό διάστημα ενδέχεται να προκληθεί βλάβη στην ακοή σας."</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">Για 1 ώρα</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Έως τις <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Μέχρι τις <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (επόμενο ξυπνητήρι)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Μέχρι να το απενεργοποιήσετε"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Μέχρι να απενεργοποιήσετε τη ρύθμιση \"Μην ενοχλείτε\""</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-en-rAU-watch/strings.xml b/core/res/res/values-en-rAU-watch/strings.xml
index 30357ce..ac7b671 100644
--- a/core/res/res/values-en-rAU-watch/strings.xml
+++ b/core/res/res/values-en-rAU-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"App <xliff:g id="NUMBER_0">%1$d</xliff:g> of <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Sensors"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"access your contacts"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"access this watch\'s location"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"access your calendar"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"send and view SMS messages"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"access photos, media and files on your watch"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"record audio"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"take pictures and record videos"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"make and manage phone calls"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"access sensor data about your vital signs"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"be the status bar"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"access body sensors (like heart rate monitors)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"access precise location (GPS and network-based)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"access approximate location (network-based)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"send commands to the SIM"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"have full network access"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"manage profile and device owners"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"change WiMAX state"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"receive Android Beam transfer status"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"route media output"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"read install sessions"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"request install packages"</string>
</resources>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index ea33448..7ff1ef3 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Includes personal data such as credit card numbers and passwords."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"disable or modify status bar"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Allows the app to disable the status bar or add and remove system icons."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"status bar"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"be the status bar"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Allows the app to be the status bar."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"expand/collapse status bar"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Allows the app to expand or collapse the status bar."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Allows the app to receive and process WAP messages. This permission includes the ability to monitor or delete messages sent to you without showing them to you."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"retrieve running apps"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Allows the app to retrieve information about currently and recently running tasks. This may allow the app to discover information about which applications are used on the device."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Manage profile and device owners"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"manage profile and device owners"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Allows apps to set the profile owners and the device owner."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"re-order running apps"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Allows the app to move tasks to the foreground and background. The app may do this without your input."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Allows the app to modify your tablet\'s call log, including data about incoming and outgoing calls. Malicious apps may use this to erase or modify your call log."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Allows the app to modify your TV\'s call log, including data about incoming and outgoing calls. Malicious apps may use this to erase or modify your call log."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Allows the app to modify your phone\'s call log, including data about incoming and outgoing calls. Malicious apps may use this to erase or modify your call log."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"body sensors (like heart rate monitors)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"access body sensors (like heart rate monitors)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Allows the app to access data from sensors that monitor your physical condition, such as your heart rate."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"read calendar events plus confidential information"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Allows the app to read all calendar events stored on your tablet, including those of friends or co-workers. This may allow the app to share or save your calendar data, regardless of confidentiality or sensitivity."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Allows the app to add, remove and change events that you can modify on your phone, including those of friends or co-workers. This may allow the app to send messages that appear to come from calendar owners, or modify events without the owners\' knowledge."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"access extra location provider commands"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Allows the app to access extra location provider commands. This may allow the app to interfere with the operation of the GPS or other location sources."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"precise location (GPS and network-based)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"access precise location (GPS and network-based)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Allows the app to get your precise location using the Global Positioning System (GPS) or network location sources such as mobile towers and Wi-Fi. These location services must be turned on and available to your device for the app to use them. Apps may use this to determine where you are, and may consume additional battery power."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"approximate location (network-based)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"access approximate location (network-based)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Allows the app to get your approximate location. This location is derived by location services using network location sources such as mobile towers and Wi-Fi. These location services must be turned on and available to your device for the app to use them. Apps may use this to determine approximately where you are."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"change your audio settings"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Allows the app to modify global audio settings such as volume and which speaker is used for output."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"record audio"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Allows the app to record audio with the microphone. This permission allows the app to record audio at any time without your confirmation."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"SIM communication"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"send commands to the SIM"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Allows the app to send commands to the SIM. This is very dangerous."</string>
<string name="permlab_camera" msgid="3616391919559751192">"take pictures and videos"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Allows the app to take pictures and videos with the camera. This permission allows the app to use the camera at any time without your confirmation."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Allows the app to get the list of accounts known by the phone. This may include any accounts created by applications that you have installed."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"view network connections"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Allows the app to view information about network connections such as which networks exist and are connected."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"full network access"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"have full network access"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Allows the app to create network sockets and use customised network protocols. The browser and other applications provide means to send data to the Internet, so this permission is not required to send data to the Internet."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"change network connectivity"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Allows the app to change the state of network connectivity."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Allows the app to configure the local Bluetooth phone and to discover and pair with remote devices."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"connect and disconnect from WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Allows the app to determine whether WiMAX is enabled and information about any WiMAX networks that are connected."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Change WiMAX state"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"change WiMAX state"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Allows the app to connect the tablet to and disconnect the tablet from WiMAX networks."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Allows the app to connect the TV to and disconnect the TV from WiMAX networks."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Allows the app to connect the phone to and disconnect the phone from WiMAX networks."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Allows the app to modify the calibration parameters of the touch screen. Should never be needed for normal apps."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"access DRM certificates"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Allows an application to provision and use DRM certficates. Should never be needed for normal apps."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Receive Android Beam transfer status"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"receive Android Beam transfer status"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Allows this application to receive information about current Android Beam transfers"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"remove DRM certificates"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Allows an application to remove DRM certficates. Should never be needed for normal apps."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Incorrect PIN code."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"To unlock, press Menu, then 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Emergency number"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"No service"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"No service"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Screen locked."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Press Menu to unlock or place emergency call."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Press Menu to unlock."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"See the User Guide or contact Customer Care."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM card is locked."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Unlocking SIM card…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%d</xliff:g> seconds."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"You have incorrectly typed your password <xliff:g id="NUMBER_0">%d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%d</xliff:g> seconds."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"You have incorrectly typed your PIN <xliff:g id="NUMBER_0">%d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%d</xliff:g> seconds."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, you will be asked to unlock your tablet using your Google sign-in.\n\n Try again in <xliff:g id="NUMBER_2">%d</xliff:g> seconds."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, you will be asked to unlock your TV using your Google signin.\n\n Try again in <xliff:g id="NUMBER_2">%d</xliff:g> seconds."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"You have drawn your unlock pattern incorrectly <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, you will be asked to unlock your phone using your Google sign-in.\n\n Please try again in <xliff:g id="NUMBER_2">%d</xliff:g> seconds."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"You have incorrectly attempted to unlock the tablet <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, the tablet will be reset to factory default and all user data will be lost."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"You have incorrectly attempted to unlock the TV <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, the TV will be reset to factory default and all user data will be lost."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"You have incorrectly attempted to unlock the phone <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, the phone will be reset to factory default and all user data will be lost."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"You have incorrectly typed your password <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"You have incorrectly typed your PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, you will be asked to unlock your tablet using your Google sign-in.\n\n Try again in <xliff:g id="NUMBER_2">%3$d</xliff:g> seconds."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, you will be asked to unlock your TV using your Google signin.\n\n Try again in <xliff:g id="NUMBER_2">%3$d</xliff:g> seconds."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"You have drawn your unlock pattern incorrectly <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, you will be asked to unlock your phone using your Google sign-in.\n\n Please try again in <xliff:g id="NUMBER_2">%3$d</xliff:g> seconds."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"You have incorrectly attempted to unlock the tablet <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, the tablet will be reset to factory default and all user data will be lost."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"You have incorrectly attempted to unlock the TV <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, the TV will be reset to factory default and all user data will be lost."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"You have incorrectly attempted to unlock the phone <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, the phone will be reset to factory default and all user data will be lost."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"You have incorrectly attempted to unlock the tablet <xliff:g id="NUMBER">%d</xliff:g> times. The tablet will now be reset to factory default."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"You have incorrectly attempted to unlock the TV <xliff:g id="NUMBER">%d</xliff:g> times. The TV will now be reset to factory default."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"You have incorrectly attempted to unlock the phone <xliff:g id="NUMBER">%d</xliff:g> times. The phone will now be reset to factory default."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"word"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"link"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"line"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Factory test failed"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"The FACTORY_TEST action is only supported for packages installed in /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"No package was found that provides the FACTORY_TEST action."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Formatting…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Not inserted"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"No matching activities found."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Route media output"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"route media output"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Allows an application to route media output to other external devices."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Read install sessions"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"read install sessions"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Allows an application to read install sessions. This allows it to see details about active package installations."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Request install packages"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"request install packages"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Allows an application to request installation of packages."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Touch twice for zoom control"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Couldn\'t add widget."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Invalid username or password."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Forgot your username or password?\nVisit "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Checking account…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"You have incorrectly typed your PIN <xliff:g id="NUMBER_0">%d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%d</xliff:g> seconds."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"You have incorrectly typed your password <xliff:g id="NUMBER_0">%d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%d</xliff:g> seconds."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%d</xliff:g> seconds."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"You have incorrectly attempted to unlock the tablet <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, the tablet will be reset to factory default and all user data will be lost."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"You have incorrectly attempted to unlock the TV <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, the TV will be reset to factory default and all user data will be lost."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"You have incorrectly attempted to unlock the phone <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, the phone will be reset to factory default and all user data will be lost."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"You have incorrectly typed your PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"You have incorrectly typed your password <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"You have incorrectly attempted to unlock the tablet <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, the tablet will be reset to factory default and all user data will be lost."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"You have incorrectly attempted to unlock the TV <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, the TV will be reset to factory default and all user data will be lost."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"You have incorrectly attempted to unlock the phone <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, the phone will be reset to factory default and all user data will be lost."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"You have incorrectly attempted to unlock the tablet <xliff:g id="NUMBER">%d</xliff:g> times. The tablet will now be reset to factory default."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"You have incorrectly attempted to unlock the TV <xliff:g id="NUMBER">%d</xliff:g> times. The TV will now be reset to factory default."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"You have incorrectly attempted to unlock the phone <xliff:g id="NUMBER">%d</xliff:g> times. The phone will now be reset to factory default."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, you will be asked to unlock your tablet using an email account.\n\n Try again in <xliff:g id="NUMBER_2">%d</xliff:g> seconds."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, you will be asked to unlock your TV using an email account.\n\n Try again in <xliff:g id="NUMBER_2">%d</xliff:g> seconds."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, you will be asked to unlock your phone using an email account.\n\n Try again in <xliff:g id="NUMBER_2">%d</xliff:g> seconds."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, you will be asked to unlock your tablet using an email account.\n\n Try again in <xliff:g id="NUMBER_2">%3$d</xliff:g> seconds."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, you will be asked to unlock your TV using an email account.\n\n Try again in <xliff:g id="NUMBER_2">%3$d</xliff:g> seconds."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, you will be asked to unlock your phone using an email account.\n\n Try again in <xliff:g id="NUMBER_2">%3$d</xliff:g> seconds."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Remove"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Raise volume above recommended level?\n\nListening at high volume for long periods may damage your hearing."</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">For 1 hr</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (next alarm)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Until you turn this off"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Until you turn off Do Not Disturb"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-en-rGB-watch/strings.xml b/core/res/res/values-en-rGB-watch/strings.xml
index 30357ce..ac7b671 100644
--- a/core/res/res/values-en-rGB-watch/strings.xml
+++ b/core/res/res/values-en-rGB-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"App <xliff:g id="NUMBER_0">%1$d</xliff:g> of <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Sensors"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"access your contacts"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"access this watch\'s location"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"access your calendar"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"send and view SMS messages"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"access photos, media and files on your watch"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"record audio"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"take pictures and record videos"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"make and manage phone calls"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"access sensor data about your vital signs"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"be the status bar"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"access body sensors (like heart rate monitors)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"access precise location (GPS and network-based)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"access approximate location (network-based)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"send commands to the SIM"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"have full network access"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"manage profile and device owners"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"change WiMAX state"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"receive Android Beam transfer status"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"route media output"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"read install sessions"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"request install packages"</string>
</resources>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index ea33448..7ff1ef3 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Includes personal data such as credit card numbers and passwords."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"disable or modify status bar"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Allows the app to disable the status bar or add and remove system icons."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"status bar"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"be the status bar"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Allows the app to be the status bar."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"expand/collapse status bar"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Allows the app to expand or collapse the status bar."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Allows the app to receive and process WAP messages. This permission includes the ability to monitor or delete messages sent to you without showing them to you."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"retrieve running apps"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Allows the app to retrieve information about currently and recently running tasks. This may allow the app to discover information about which applications are used on the device."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Manage profile and device owners"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"manage profile and device owners"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Allows apps to set the profile owners and the device owner."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"re-order running apps"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Allows the app to move tasks to the foreground and background. The app may do this without your input."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Allows the app to modify your tablet\'s call log, including data about incoming and outgoing calls. Malicious apps may use this to erase or modify your call log."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Allows the app to modify your TV\'s call log, including data about incoming and outgoing calls. Malicious apps may use this to erase or modify your call log."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Allows the app to modify your phone\'s call log, including data about incoming and outgoing calls. Malicious apps may use this to erase or modify your call log."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"body sensors (like heart rate monitors)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"access body sensors (like heart rate monitors)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Allows the app to access data from sensors that monitor your physical condition, such as your heart rate."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"read calendar events plus confidential information"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Allows the app to read all calendar events stored on your tablet, including those of friends or co-workers. This may allow the app to share or save your calendar data, regardless of confidentiality or sensitivity."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Allows the app to add, remove and change events that you can modify on your phone, including those of friends or co-workers. This may allow the app to send messages that appear to come from calendar owners, or modify events without the owners\' knowledge."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"access extra location provider commands"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Allows the app to access extra location provider commands. This may allow the app to interfere with the operation of the GPS or other location sources."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"precise location (GPS and network-based)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"access precise location (GPS and network-based)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Allows the app to get your precise location using the Global Positioning System (GPS) or network location sources such as mobile towers and Wi-Fi. These location services must be turned on and available to your device for the app to use them. Apps may use this to determine where you are, and may consume additional battery power."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"approximate location (network-based)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"access approximate location (network-based)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Allows the app to get your approximate location. This location is derived by location services using network location sources such as mobile towers and Wi-Fi. These location services must be turned on and available to your device for the app to use them. Apps may use this to determine approximately where you are."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"change your audio settings"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Allows the app to modify global audio settings such as volume and which speaker is used for output."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"record audio"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Allows the app to record audio with the microphone. This permission allows the app to record audio at any time without your confirmation."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"SIM communication"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"send commands to the SIM"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Allows the app to send commands to the SIM. This is very dangerous."</string>
<string name="permlab_camera" msgid="3616391919559751192">"take pictures and videos"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Allows the app to take pictures and videos with the camera. This permission allows the app to use the camera at any time without your confirmation."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Allows the app to get the list of accounts known by the phone. This may include any accounts created by applications that you have installed."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"view network connections"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Allows the app to view information about network connections such as which networks exist and are connected."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"full network access"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"have full network access"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Allows the app to create network sockets and use customised network protocols. The browser and other applications provide means to send data to the Internet, so this permission is not required to send data to the Internet."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"change network connectivity"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Allows the app to change the state of network connectivity."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Allows the app to configure the local Bluetooth phone and to discover and pair with remote devices."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"connect and disconnect from WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Allows the app to determine whether WiMAX is enabled and information about any WiMAX networks that are connected."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Change WiMAX state"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"change WiMAX state"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Allows the app to connect the tablet to and disconnect the tablet from WiMAX networks."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Allows the app to connect the TV to and disconnect the TV from WiMAX networks."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Allows the app to connect the phone to and disconnect the phone from WiMAX networks."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Allows the app to modify the calibration parameters of the touch screen. Should never be needed for normal apps."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"access DRM certificates"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Allows an application to provision and use DRM certficates. Should never be needed for normal apps."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Receive Android Beam transfer status"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"receive Android Beam transfer status"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Allows this application to receive information about current Android Beam transfers"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"remove DRM certificates"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Allows an application to remove DRM certficates. Should never be needed for normal apps."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Incorrect PIN code."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"To unlock, press Menu, then 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Emergency number"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"No service"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"No service"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Screen locked."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Press Menu to unlock or place emergency call."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Press Menu to unlock."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"See the User Guide or contact Customer Care."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM card is locked."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Unlocking SIM card…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%d</xliff:g> seconds."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"You have incorrectly typed your password <xliff:g id="NUMBER_0">%d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%d</xliff:g> seconds."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"You have incorrectly typed your PIN <xliff:g id="NUMBER_0">%d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%d</xliff:g> seconds."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, you will be asked to unlock your tablet using your Google sign-in.\n\n Try again in <xliff:g id="NUMBER_2">%d</xliff:g> seconds."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, you will be asked to unlock your TV using your Google signin.\n\n Try again in <xliff:g id="NUMBER_2">%d</xliff:g> seconds."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"You have drawn your unlock pattern incorrectly <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, you will be asked to unlock your phone using your Google sign-in.\n\n Please try again in <xliff:g id="NUMBER_2">%d</xliff:g> seconds."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"You have incorrectly attempted to unlock the tablet <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, the tablet will be reset to factory default and all user data will be lost."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"You have incorrectly attempted to unlock the TV <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, the TV will be reset to factory default and all user data will be lost."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"You have incorrectly attempted to unlock the phone <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, the phone will be reset to factory default and all user data will be lost."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"You have incorrectly typed your password <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"You have incorrectly typed your PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, you will be asked to unlock your tablet using your Google sign-in.\n\n Try again in <xliff:g id="NUMBER_2">%3$d</xliff:g> seconds."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, you will be asked to unlock your TV using your Google signin.\n\n Try again in <xliff:g id="NUMBER_2">%3$d</xliff:g> seconds."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"You have drawn your unlock pattern incorrectly <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, you will be asked to unlock your phone using your Google sign-in.\n\n Please try again in <xliff:g id="NUMBER_2">%3$d</xliff:g> seconds."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"You have incorrectly attempted to unlock the tablet <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, the tablet will be reset to factory default and all user data will be lost."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"You have incorrectly attempted to unlock the TV <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, the TV will be reset to factory default and all user data will be lost."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"You have incorrectly attempted to unlock the phone <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, the phone will be reset to factory default and all user data will be lost."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"You have incorrectly attempted to unlock the tablet <xliff:g id="NUMBER">%d</xliff:g> times. The tablet will now be reset to factory default."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"You have incorrectly attempted to unlock the TV <xliff:g id="NUMBER">%d</xliff:g> times. The TV will now be reset to factory default."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"You have incorrectly attempted to unlock the phone <xliff:g id="NUMBER">%d</xliff:g> times. The phone will now be reset to factory default."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"word"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"link"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"line"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Factory test failed"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"The FACTORY_TEST action is only supported for packages installed in /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"No package was found that provides the FACTORY_TEST action."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Formatting…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Not inserted"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"No matching activities found."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Route media output"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"route media output"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Allows an application to route media output to other external devices."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Read install sessions"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"read install sessions"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Allows an application to read install sessions. This allows it to see details about active package installations."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Request install packages"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"request install packages"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Allows an application to request installation of packages."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Touch twice for zoom control"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Couldn\'t add widget."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Invalid username or password."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Forgot your username or password?\nVisit "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Checking account…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"You have incorrectly typed your PIN <xliff:g id="NUMBER_0">%d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%d</xliff:g> seconds."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"You have incorrectly typed your password <xliff:g id="NUMBER_0">%d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%d</xliff:g> seconds."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%d</xliff:g> seconds."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"You have incorrectly attempted to unlock the tablet <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, the tablet will be reset to factory default and all user data will be lost."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"You have incorrectly attempted to unlock the TV <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, the TV will be reset to factory default and all user data will be lost."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"You have incorrectly attempted to unlock the phone <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, the phone will be reset to factory default and all user data will be lost."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"You have incorrectly typed your PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"You have incorrectly typed your password <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"You have incorrectly attempted to unlock the tablet <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, the tablet will be reset to factory default and all user data will be lost."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"You have incorrectly attempted to unlock the TV <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, the TV will be reset to factory default and all user data will be lost."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"You have incorrectly attempted to unlock the phone <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, the phone will be reset to factory default and all user data will be lost."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"You have incorrectly attempted to unlock the tablet <xliff:g id="NUMBER">%d</xliff:g> times. The tablet will now be reset to factory default."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"You have incorrectly attempted to unlock the TV <xliff:g id="NUMBER">%d</xliff:g> times. The TV will now be reset to factory default."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"You have incorrectly attempted to unlock the phone <xliff:g id="NUMBER">%d</xliff:g> times. The phone will now be reset to factory default."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, you will be asked to unlock your tablet using an email account.\n\n Try again in <xliff:g id="NUMBER_2">%d</xliff:g> seconds."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, you will be asked to unlock your TV using an email account.\n\n Try again in <xliff:g id="NUMBER_2">%d</xliff:g> seconds."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, you will be asked to unlock your phone using an email account.\n\n Try again in <xliff:g id="NUMBER_2">%d</xliff:g> seconds."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, you will be asked to unlock your tablet using an email account.\n\n Try again in <xliff:g id="NUMBER_2">%3$d</xliff:g> seconds."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, you will be asked to unlock your TV using an email account.\n\n Try again in <xliff:g id="NUMBER_2">%3$d</xliff:g> seconds."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, you will be asked to unlock your phone using an email account.\n\n Try again in <xliff:g id="NUMBER_2">%3$d</xliff:g> seconds."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Remove"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Raise volume above recommended level?\n\nListening at high volume for long periods may damage your hearing."</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">For 1 hr</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (next alarm)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Until you turn this off"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Until you turn off Do Not Disturb"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-en-rIN-watch/strings.xml b/core/res/res/values-en-rIN-watch/strings.xml
index 30357ce..ac7b671 100644
--- a/core/res/res/values-en-rIN-watch/strings.xml
+++ b/core/res/res/values-en-rIN-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"App <xliff:g id="NUMBER_0">%1$d</xliff:g> of <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Sensors"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"access your contacts"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"access this watch\'s location"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"access your calendar"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"send and view SMS messages"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"access photos, media and files on your watch"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"record audio"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"take pictures and record videos"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"make and manage phone calls"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"access sensor data about your vital signs"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"be the status bar"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"access body sensors (like heart rate monitors)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"access precise location (GPS and network-based)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"access approximate location (network-based)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"send commands to the SIM"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"have full network access"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"manage profile and device owners"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"change WiMAX state"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"receive Android Beam transfer status"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"route media output"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"read install sessions"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"request install packages"</string>
</resources>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index ea33448..7ff1ef3 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Includes personal data such as credit card numbers and passwords."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"disable or modify status bar"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Allows the app to disable the status bar or add and remove system icons."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"status bar"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"be the status bar"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Allows the app to be the status bar."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"expand/collapse status bar"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Allows the app to expand or collapse the status bar."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Allows the app to receive and process WAP messages. This permission includes the ability to monitor or delete messages sent to you without showing them to you."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"retrieve running apps"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Allows the app to retrieve information about currently and recently running tasks. This may allow the app to discover information about which applications are used on the device."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Manage profile and device owners"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"manage profile and device owners"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Allows apps to set the profile owners and the device owner."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"re-order running apps"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Allows the app to move tasks to the foreground and background. The app may do this without your input."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Allows the app to modify your tablet\'s call log, including data about incoming and outgoing calls. Malicious apps may use this to erase or modify your call log."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Allows the app to modify your TV\'s call log, including data about incoming and outgoing calls. Malicious apps may use this to erase or modify your call log."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Allows the app to modify your phone\'s call log, including data about incoming and outgoing calls. Malicious apps may use this to erase or modify your call log."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"body sensors (like heart rate monitors)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"access body sensors (like heart rate monitors)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Allows the app to access data from sensors that monitor your physical condition, such as your heart rate."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"read calendar events plus confidential information"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Allows the app to read all calendar events stored on your tablet, including those of friends or co-workers. This may allow the app to share or save your calendar data, regardless of confidentiality or sensitivity."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Allows the app to add, remove and change events that you can modify on your phone, including those of friends or co-workers. This may allow the app to send messages that appear to come from calendar owners, or modify events without the owners\' knowledge."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"access extra location provider commands"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Allows the app to access extra location provider commands. This may allow the app to interfere with the operation of the GPS or other location sources."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"precise location (GPS and network-based)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"access precise location (GPS and network-based)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Allows the app to get your precise location using the Global Positioning System (GPS) or network location sources such as mobile towers and Wi-Fi. These location services must be turned on and available to your device for the app to use them. Apps may use this to determine where you are, and may consume additional battery power."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"approximate location (network-based)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"access approximate location (network-based)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Allows the app to get your approximate location. This location is derived by location services using network location sources such as mobile towers and Wi-Fi. These location services must be turned on and available to your device for the app to use them. Apps may use this to determine approximately where you are."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"change your audio settings"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Allows the app to modify global audio settings such as volume and which speaker is used for output."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"record audio"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Allows the app to record audio with the microphone. This permission allows the app to record audio at any time without your confirmation."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"SIM communication"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"send commands to the SIM"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Allows the app to send commands to the SIM. This is very dangerous."</string>
<string name="permlab_camera" msgid="3616391919559751192">"take pictures and videos"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Allows the app to take pictures and videos with the camera. This permission allows the app to use the camera at any time without your confirmation."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Allows the app to get the list of accounts known by the phone. This may include any accounts created by applications that you have installed."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"view network connections"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Allows the app to view information about network connections such as which networks exist and are connected."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"full network access"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"have full network access"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Allows the app to create network sockets and use customised network protocols. The browser and other applications provide means to send data to the Internet, so this permission is not required to send data to the Internet."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"change network connectivity"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Allows the app to change the state of network connectivity."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Allows the app to configure the local Bluetooth phone and to discover and pair with remote devices."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"connect and disconnect from WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Allows the app to determine whether WiMAX is enabled and information about any WiMAX networks that are connected."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Change WiMAX state"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"change WiMAX state"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Allows the app to connect the tablet to and disconnect the tablet from WiMAX networks."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Allows the app to connect the TV to and disconnect the TV from WiMAX networks."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Allows the app to connect the phone to and disconnect the phone from WiMAX networks."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Allows the app to modify the calibration parameters of the touch screen. Should never be needed for normal apps."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"access DRM certificates"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Allows an application to provision and use DRM certficates. Should never be needed for normal apps."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Receive Android Beam transfer status"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"receive Android Beam transfer status"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Allows this application to receive information about current Android Beam transfers"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"remove DRM certificates"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Allows an application to remove DRM certficates. Should never be needed for normal apps."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Incorrect PIN code."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"To unlock, press Menu, then 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Emergency number"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"No service"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"No service"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Screen locked."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Press Menu to unlock or place emergency call."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Press Menu to unlock."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"See the User Guide or contact Customer Care."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM card is locked."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Unlocking SIM card…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%d</xliff:g> seconds."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"You have incorrectly typed your password <xliff:g id="NUMBER_0">%d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%d</xliff:g> seconds."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"You have incorrectly typed your PIN <xliff:g id="NUMBER_0">%d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%d</xliff:g> seconds."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, you will be asked to unlock your tablet using your Google sign-in.\n\n Try again in <xliff:g id="NUMBER_2">%d</xliff:g> seconds."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, you will be asked to unlock your TV using your Google signin.\n\n Try again in <xliff:g id="NUMBER_2">%d</xliff:g> seconds."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"You have drawn your unlock pattern incorrectly <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, you will be asked to unlock your phone using your Google sign-in.\n\n Please try again in <xliff:g id="NUMBER_2">%d</xliff:g> seconds."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"You have incorrectly attempted to unlock the tablet <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, the tablet will be reset to factory default and all user data will be lost."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"You have incorrectly attempted to unlock the TV <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, the TV will be reset to factory default and all user data will be lost."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"You have incorrectly attempted to unlock the phone <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, the phone will be reset to factory default and all user data will be lost."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"You have incorrectly typed your password <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"You have incorrectly typed your PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, you will be asked to unlock your tablet using your Google sign-in.\n\n Try again in <xliff:g id="NUMBER_2">%3$d</xliff:g> seconds."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, you will be asked to unlock your TV using your Google signin.\n\n Try again in <xliff:g id="NUMBER_2">%3$d</xliff:g> seconds."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"You have drawn your unlock pattern incorrectly <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, you will be asked to unlock your phone using your Google sign-in.\n\n Please try again in <xliff:g id="NUMBER_2">%3$d</xliff:g> seconds."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"You have incorrectly attempted to unlock the tablet <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, the tablet will be reset to factory default and all user data will be lost."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"You have incorrectly attempted to unlock the TV <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, the TV will be reset to factory default and all user data will be lost."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"You have incorrectly attempted to unlock the phone <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, the phone will be reset to factory default and all user data will be lost."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"You have incorrectly attempted to unlock the tablet <xliff:g id="NUMBER">%d</xliff:g> times. The tablet will now be reset to factory default."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"You have incorrectly attempted to unlock the TV <xliff:g id="NUMBER">%d</xliff:g> times. The TV will now be reset to factory default."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"You have incorrectly attempted to unlock the phone <xliff:g id="NUMBER">%d</xliff:g> times. The phone will now be reset to factory default."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"word"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"link"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"line"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Factory test failed"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"The FACTORY_TEST action is only supported for packages installed in /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"No package was found that provides the FACTORY_TEST action."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Formatting…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Not inserted"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"No matching activities found."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Route media output"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"route media output"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Allows an application to route media output to other external devices."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Read install sessions"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"read install sessions"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Allows an application to read install sessions. This allows it to see details about active package installations."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Request install packages"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"request install packages"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Allows an application to request installation of packages."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Touch twice for zoom control"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Couldn\'t add widget."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Invalid username or password."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Forgot your username or password?\nVisit "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Checking account…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"You have incorrectly typed your PIN <xliff:g id="NUMBER_0">%d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%d</xliff:g> seconds."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"You have incorrectly typed your password <xliff:g id="NUMBER_0">%d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%d</xliff:g> seconds."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%d</xliff:g> seconds."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"You have incorrectly attempted to unlock the tablet <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, the tablet will be reset to factory default and all user data will be lost."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"You have incorrectly attempted to unlock the TV <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, the TV will be reset to factory default and all user data will be lost."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"You have incorrectly attempted to unlock the phone <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, the phone will be reset to factory default and all user data will be lost."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"You have incorrectly typed your PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"You have incorrectly typed your password <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"You have incorrectly attempted to unlock the tablet <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, the tablet will be reset to factory default and all user data will be lost."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"You have incorrectly attempted to unlock the TV <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, the TV will be reset to factory default and all user data will be lost."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"You have incorrectly attempted to unlock the phone <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, the phone will be reset to factory default and all user data will be lost."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"You have incorrectly attempted to unlock the tablet <xliff:g id="NUMBER">%d</xliff:g> times. The tablet will now be reset to factory default."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"You have incorrectly attempted to unlock the TV <xliff:g id="NUMBER">%d</xliff:g> times. The TV will now be reset to factory default."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"You have incorrectly attempted to unlock the phone <xliff:g id="NUMBER">%d</xliff:g> times. The phone will now be reset to factory default."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, you will be asked to unlock your tablet using an email account.\n\n Try again in <xliff:g id="NUMBER_2">%d</xliff:g> seconds."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, you will be asked to unlock your TV using an email account.\n\n Try again in <xliff:g id="NUMBER_2">%d</xliff:g> seconds."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, you will be asked to unlock your phone using an email account.\n\n Try again in <xliff:g id="NUMBER_2">%d</xliff:g> seconds."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, you will be asked to unlock your tablet using an email account.\n\n Try again in <xliff:g id="NUMBER_2">%3$d</xliff:g> seconds."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, you will be asked to unlock your TV using an email account.\n\n Try again in <xliff:g id="NUMBER_2">%3$d</xliff:g> seconds."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, you will be asked to unlock your phone using an email account.\n\n Try again in <xliff:g id="NUMBER_2">%3$d</xliff:g> seconds."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Remove"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Raise volume above recommended level?\n\nListening at high volume for long periods may damage your hearing."</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">For 1 hr</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (next alarm)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Until you turn this off"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Until you turn off Do Not Disturb"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-es-rUS-watch/strings.xml b/core/res/res/values-es-rUS-watch/strings.xml
index 703cb32..35d123f 100644
--- a/core/res/res/values-es-rUS-watch/strings.xml
+++ b/core/res/res/values-es-rUS-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Aplicación <xliff:g id="NUMBER_0">%1$d</xliff:g> de <xliff:g id="NUMBER_1">%2$d</xliff:g>"</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Sensores"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"acceder a los contactos"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"acceder a la ubicación de este reloj"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"acceder al calendario"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"enviar y ver mensajes SMS"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"acceder a las fotos, el contenido multimedia y los archivos del reloj"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"grabar audio"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"tomar fotografías y grabar videos"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"realizar y administrar llamadas telefónicas"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"acceder a los datos del sensor acerca de tus signos vitales"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"aparecer en la barra de estado"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"acceder a los sensores corporales (como los monitores de frecuencia cardíaca)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"acceder a la ubicación precisa (según el GPS y la red)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"acceder a la ubicación aproximada (según la red)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"enviar comandos a la tarjeta SIM"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"permitir acceso completo a la red"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"administrar perfiles de propietarios y propietario del dispositivo"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"cambiar estado de WiMAX"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"recibir estado de transferencias de Android Beam"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"dirigir salida de medios"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"leer sesiones de instalación"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"solicitar la instalación de paquetes"</string>
</resources>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 5782eab..bc05551 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Incluye datos personales, como números de tarjeta de crédito y contraseñas."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"desactivar o modificar la barra de estado"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Permite que la aplicación inhabilite la barra de estado o que agregue y elimine íconos del sistema."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"barra de estado"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"aparecer en la barra de estado"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Permite que la aplicación sea la barra de estado."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"expandir o reducir la barra de estado"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Permite que la aplicación muestre y oculte la barra de estado."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Permite que la aplicación reciba y procese mensajes WAP, lo que significa que podría controlar o eliminar mensajes enviados al usuario sin mostrártelos."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"recuperar aplicaciones en ejecución"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Permite que la aplicación recupere información sobre las tareas que se estén ejecutando en ese momento o que se hayan ejecutado recientemente. La aplicación puede utilizar este permiso para descubrir cuáles son las aplicaciones que se utilizan en el dispositivo."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Administrar perfiles de propietarios y propietario del dispositivo"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"administrar perfiles de propietarios y propietario del dispositivo"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Permite que las aplicaciones configuren los perfiles de propietarios y el propietario del dispositivo."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"reorganizar aplicaciones en ejecución"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Permite que la aplicación mueva tareas a segundo o a primer plano. La aplicación puede utilizar este permiso para realizar estos movimientos sin indicártelo."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Permite que la aplicación modifique el registro de llamadas de la tablet, incluidos los datos sobre llamadas entrantes y salientes. Las aplicaciones malintencionadas pueden usar este permiso para borrar o modificar el registro de llamadas."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Permite que la aplicación modifique el registro de llamadas de la TV, incluidos los datos sobre llamadas entrantes y salientes. Las aplicaciones malintencionadas pueden usar este permiso para borrar o modificar el registro de llamadas."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Permite que la aplicación modifique el registro de llamadas del dispositivo, incluidos los datos sobre llamadas entrantes y salientes. Las aplicaciones malintencionadas pueden usar este permiso para borrar o modificar el registro de llamadas."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"sensores corporales (frec. card)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"acceder a los sensores corporales (como los monitores de frecuencia cardíaca)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Permite que la aplicación acceda a datos de sensores que controlan tu condición física, como el ritmo cardíaco."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"leer eventos de calendario e información confidencial"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Permite que la aplicación consulte todos los eventos de calendario almacenados en la tablet, incluidos los de amigos y compañeros de trabajo. La aplicación puede utilizar este permiso para compartir o guardar datos del calendario del usuario sin tener en cuenta si son privados o confidenciales."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Permite que la aplicación agregue, elimine y cambie eventos que se pueden modificar en el dispositivo, incluidos los de amigos o compañeros de trabajo. La aplicación puede utilizar este permiso para enviar mensajes que parezcan proceder de propietarios de un calendario o para modificar los eventos sin el consentimiento de los propietarios."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"acceder a comandos adicionales del proveedor del lugar"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Permite que la aplicación acceda a comandos adicionales del proveedor de ubicación. Esto puede permitirle a la aplicación interferir con el funcionamiento del GPS o de otras fuentes de ubicación."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"ubicación precisa (según el GPS y la red)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"acceder a la ubicación precisa (según el GPS y la red)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Permite a la aplicación obtener tu ubicación precisa mediante el Sistema de Posicionamiento Global (GPS) o las fuentes de ubicación de red, como las torres de celulares y Wi-Fi. Estos servicios de ubicación deben estar encendidos y disponibles en tu dispositivo para que los use la aplicación. Las aplicaciones pueden usarlo para determinar tu ubicación, lo cual puede consumir más batería."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"ubicación aproximada (según la red)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"acceder a la ubicación aproximada (según la red)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Permite a la aplicación obtener tu ubicación aproximada. Esta ubicación deriva de los servicios de ubicación que usan fuentes de ubicación de red, como torres de celulares y Wi-Fi. Estos servicios de ubicación deben estar encendidos y disponibles en tu dispositivo para que los use la aplicación. Las aplicaciones pueden usarlo para determinar tu ubicación aproximada."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"cambiar tu configuración de audio"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Permite que la aplicación modifique la configuración de audio global, por ejemplo, el volumen y el altavoz de salida."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"grabar audio"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Permite que la aplicación grabe audio con el micrófono. La aplicación puede utilizar este permiso para grabar audio en cualquier momento sin tener tu confirmación."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"Comunicación con tarjeta SIM"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"enviar comandos a la tarjeta SIM"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Permite que la aplicación envíe comandos a la tarjeta SIM. Usar este permiso es peligroso."</string>
<string name="permlab_camera" msgid="3616391919559751192">"tomar fotografías y grabar videos"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Permite que la aplicación saque fotos o grabe videos con la cámara. Este permiso autoriza a la aplicación a utilizar la cámara en cualquier momento sin tu confirmación."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Permite que la aplicación obtenga una lista de las cuentas reconocidas por el dispositivo, entre las que se pueden incluir las cuentas creadas por las aplicaciones que hayas instalado."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"ver conexiones de red"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Permite que la aplicación vea información sobre las conexiones de red, por ejemplo, qué redes existen y están conectadas."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"acceso completo a la red"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"permitir acceso completo a la red"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Permite que la aplicación cree sockets de red y utilice protocolos de red personalizados. El navegador y otras aplicaciones proporcionan los medios necesarios para el envío de datos a Internet, por lo que no hace falta utilizar este permiso para eso."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"cambiar la conectividad de la red"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Permite que la aplicación cambie el estado de la conectividad de red."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Permite que la aplicación configure el dispositivo Bluetooth local y descubra y se sincronice con dispositivos remotos."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"conectarse y desconectarse de WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Permite que la aplicación determine si está activada la conexión WiMAX y que obtenga información sobre las redes WiMAX que están conectadas."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Cambiar el estado de WiMAX"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"cambiar estado de WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Permite que la aplicación conecte la tablet a una red WiMAX y que la desconecte de ella."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Permite que la aplicación conecte la TV a las redes WiMAX y que la desconecte de ellas."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Permite que la aplicación conecte el dispositivo a una red WiMAX y que lo desconecte de ella."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Permite que la aplicación modifique los parámetros de calibración de la pantalla táctil. Las aplicaciones normales no deberían necesitar este permiso."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"Acceder a certificados DRM"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Permite que una aplicación proporcione y utilice certificados DRM. Las aplicaciones normales no deberían necesitar este permiso."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Recibir estado de transferencias de Android Beam"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"recibir estado de transferencias de Android Beam"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Permite que esta aplicación reciba información sobre las transferencias actuales de Android Beam"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"eliminar certificados DRM"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Permite que una aplicación elimine certificados DRM. Las aplicaciones normales no deberían necesitar este permiso."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Código PIN incorrecto"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Para desbloquear, presiona el menú y luego 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Número de emergencia"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Sin servicio"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Sin servicio"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Pantalla bloqueada."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Presiona el Menú para desbloquear o realizar una llamada de emergencia."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Presionar Menú para desbloquear."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Consulta la guía del usuario o comunícate con el servicio de atención al cliente."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"La tarjeta SIM está bloqueada."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Desbloqueando tarjeta SIM…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Estableciste incorrectamente tu patrón de desbloqueo <xliff:g id="NUMBER_0">%d</xliff:g> veces. \n\nVuelve a intentarlo en <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Escribiste incorrectamente tu contraseña <xliff:g id="NUMBER_0">%d</xliff:g> veces. \n\nVuelve a intentarlo en <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Escribiste incorrectamente tu PIN <xliff:g id="NUMBER_0">%d</xliff:g> veces. \n\nVuelve a intentarlo en <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Has establecido incorrectamente tu patrón de desbloqueo <xliff:g id="NUMBER_0">%d</xliff:g> veces. Luego de <xliff:g id="NUMBER_1">%d</xliff:g> intentos incorrectos, se te solicitará que desbloquees tu tablet mediante el uso de tu información de acceso de Google.\n\n Vuelve a intentarlo en <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Trazaste el patrón de desbloqueo <xliff:g id="NUMBER_0">%d</xliff:g> veces de manera incorrecta. Después de <xliff:g id="NUMBER_1">%d</xliff:g> intentos incorrectos más, tendrás que desbloquear la TV con tus datos de acceso a Google.\n\n Vuelve a intentarlo en <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Has establecido incorrectamente tu patrón de desbloqueo <xliff:g id="NUMBER_0">%d</xliff:g> veces. Luego de <xliff:g id="NUMBER_1">%d</xliff:g> intentos incorrectos, se te solicitará que desbloquees tu dispositivo mediante el uso de tu información de acceso de Google.\n\n Vuelve a intentarlo en <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Intentaste desbloquear la tablet <xliff:g id="NUMBER_0">%d</xliff:g> veces, pero no lo lograste. Puedes intentarlo <xliff:g id="NUMBER_1">%d</xliff:g> veces más antes de que se restablezcan los valores predeterminados de fábrica de la tablet y se pierdan todos los datos de usuario."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Intentaste desbloquear la TV <xliff:g id="NUMBER_0">%d</xliff:g> veces de manera incorrecta. Después de <xliff:g id="NUMBER_1">%d</xliff:g> intentos incorrectos más, se restablecerá la configuración de fábrica de la TV y se perderán todos los datos del usuario."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Intentaste desbloquear el dispositivo <xliff:g id="NUMBER_0">%d</xliff:g> veces, pero no lo lograste. Puedes intentarlo <xliff:g id="NUMBER_1">%d</xliff:g> veces más antes de que se restablezcan los valores predeterminados de fábrica del dispositivo y se pierdan todos los datos de usuario."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Estableciste incorrectamente tu patrón de desbloqueo <xliff:g id="NUMBER_0">%1$d</xliff:g> veces. \n\nVuelve a intentarlo en <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Escribiste incorrectamente tu contraseña <xliff:g id="NUMBER_0">%1$d</xliff:g> veces. \n\nVuelve a intentarlo en <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Escribiste incorrectamente tu PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> veces. \n\nVuelve a intentarlo en <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Has establecido incorrectamente tu patrón de desbloqueo <xliff:g id="NUMBER_0">%1$d</xliff:g> veces. Luego de <xliff:g id="NUMBER_1">%2$d</xliff:g> intentos incorrectos, se te solicitará que desbloquees tu tablet mediante el uso de tu información de acceso de Google.\n\n Vuelve a intentarlo en <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Trazaste el patrón de desbloqueo <xliff:g id="NUMBER_0">%1$d</xliff:g> veces de manera incorrecta. Después de <xliff:g id="NUMBER_1">%2$d</xliff:g> intentos incorrectos más, tendrás que desbloquear la TV con tus datos de acceso a Google.\n\n Vuelve a intentarlo en <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Has establecido incorrectamente tu patrón de desbloqueo <xliff:g id="NUMBER_0">%1$d</xliff:g> veces. Luego de <xliff:g id="NUMBER_1">%2$d</xliff:g> intentos incorrectos, se te solicitará que desbloquees tu dispositivo mediante el uso de tu información de acceso de Google.\n\n Vuelve a intentarlo en <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Intentaste desbloquear la tablet <xliff:g id="NUMBER_0">%1$d</xliff:g> veces, pero no lo lograste. Puedes intentarlo <xliff:g id="NUMBER_1">%2$d</xliff:g> veces más antes de que se restablezcan los valores predeterminados de fábrica de la tablet y se pierdan todos los datos de usuario."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Intentaste desbloquear la TV <xliff:g id="NUMBER_0">%1$d</xliff:g> veces de manera incorrecta. Después de <xliff:g id="NUMBER_1">%2$d</xliff:g> intentos incorrectos más, se restablecerá la configuración de fábrica de la TV y se perderán todos los datos del usuario."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Intentaste desbloquear el dispositivo <xliff:g id="NUMBER_0">%1$d</xliff:g> veces, pero no lo lograste. Puedes intentarlo <xliff:g id="NUMBER_1">%2$d</xliff:g> veces más antes de que se restablezcan los valores predeterminados de fábrica del dispositivo y se pierdan todos los datos de usuario."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Intentaste desbloquear la tablet <xliff:g id="NUMBER">%d</xliff:g> veces, pero no lo lograste. Se restablecerán los valores predeterminados de fábrica de la tablet."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Intentaste desbloquear la TV <xliff:g id="NUMBER">%d</xliff:g> veces de manera incorrecta. Se restablecerá la configuración de fábrica de la TV."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Intentaste desbloquear el dispositivo <xliff:g id="NUMBER">%d</xliff:g> veces, pero no lo lograste. Se restablecerán los valores predeterminados de fábrica del dispositivo."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"Palabra"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"Vínculo"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"Línea"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Error en la prueba de fábrica"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"La acción FACTORY_TEST se admite solamente en paquetes instalados en /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"No se ha encontrado ningún paquete que proporcione la acción FACTORY_TEST ."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Formateando…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"No se insertó"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"No se encontraron actividades coincidentes."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Dirigir salida de medios"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"dirigir salida de medios"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Permite que la aplicación dirija salidas de medios a otros dispositivos externos."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Leer sesiones de instalación"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"leer sesiones de instalación"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Permite que una aplicación lea sesiones de instalación. Esto le permite ver detalles acerca de instalaciones de paquetes activas."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Solicitar la instalación de paquetes"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"solicitar la instalación de paquetes"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Permite que una aplicación solicite la instalación de paquetes."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Toca dos veces para acceder al control de zoom."</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"No se pudo agregar el widget."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Nombre de usuario o contraseña incorrectos"</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"¿Olvidaste tu nombre de usuario o contraseña?\nAccede a "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Comprobando la cuenta…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Escribiste incorrectamente tu PIN <xliff:g id="NUMBER_0">%d</xliff:g> veces. \n\nVuelve a intentarlo en <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Escribiste incorrectamente tu contraseña <xliff:g id="NUMBER_0">%d</xliff:g> veces. \n\nVuelve a intentarlo en <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Dibujaste incorrectamente tu patrón de desbloqueo <xliff:g id="NUMBER_0">%d</xliff:g> veces. \n\nVuelve a intentarlo en <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Intentaste desbloquear la tablet <xliff:g id="NUMBER_0">%d</xliff:g> veces, pero no lo lograste. Puedes intentarlo <xliff:g id="NUMBER_1">%d</xliff:g> veces más antes de que se restablezcan los valores predeterminados de fábrica de la tablet y se pierdan todos los datos del usuario."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Intentaste desbloquear la TV <xliff:g id="NUMBER_0">%d</xliff:g> veces de manera incorrecta. Después de <xliff:g id="NUMBER_1">%d</xliff:g> intentos incorrectos más, se restablecerá la configuración de fábrica de la TV y se perderán todos los datos del usuario."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Intentaste desbloquear el dispositivo <xliff:g id="NUMBER_0">%d</xliff:g> veces, pero no lo lograste. Puedes intentarlo <xliff:g id="NUMBER_1">%d</xliff:g> veces más antes de que se restablezcan los valores predeterminados de fábrica del dispositivo y se pierdan todos los datos del usuario."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Escribiste incorrectamente tu PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> veces. \n\nVuelve a intentarlo en <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Escribiste incorrectamente tu contraseña <xliff:g id="NUMBER_0">%1$d</xliff:g> veces. \n\nVuelve a intentarlo en <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Dibujaste incorrectamente tu patrón de desbloqueo <xliff:g id="NUMBER_0">%1$d</xliff:g> veces. \n\nVuelve a intentarlo en <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Intentaste desbloquear la tablet <xliff:g id="NUMBER_0">%1$d</xliff:g> veces, pero no lo lograste. Puedes intentarlo <xliff:g id="NUMBER_1">%2$d</xliff:g> veces más antes de que se restablezcan los valores predeterminados de fábrica de la tablet y se pierdan todos los datos del usuario."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Intentaste desbloquear la TV <xliff:g id="NUMBER_0">%1$d</xliff:g> veces de manera incorrecta. Después de <xliff:g id="NUMBER_1">%2$d</xliff:g> intentos incorrectos más, se restablecerá la configuración de fábrica de la TV y se perderán todos los datos del usuario."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Intentaste desbloquear el dispositivo <xliff:g id="NUMBER_0">%1$d</xliff:g> veces, pero no lo lograste. Puedes intentarlo <xliff:g id="NUMBER_1">%2$d</xliff:g> veces más antes de que se restablezcan los valores predeterminados de fábrica del dispositivo y se pierdan todos los datos del usuario."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Intentaste desbloquear la tablet <xliff:g id="NUMBER">%d</xliff:g> veces, pero no lo lograste. Se restablecerán los valores predeterminados de fábrica de la tablet."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Intentaste desbloquear la TV <xliff:g id="NUMBER">%d</xliff:g> veces de manera incorrecta. Se restablecerá la configuración de fábrica de la TV."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Intentaste desbloquear el dispositivo <xliff:g id="NUMBER">%d</xliff:g> veces, pero no lo lograste. Se restablecerán los valores predeterminados de fábrica del dispositivo."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Dibujaste incorrectamente tu patrón de desbloqueo <xliff:g id="NUMBER_0">%d</xliff:g> veces. Luego de <xliff:g id="NUMBER_1">%d</xliff:g> intentos incorrectos más, se te solicitará que desbloquees tu tablet mediante el uso de una cuenta de correo.\n\n Vuelve a intentarlo en <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Trazaste el patrón de desbloqueo <xliff:g id="NUMBER_0">%d</xliff:g> veces de manera incorrecta. Después de <xliff:g id="NUMBER_1">%d</xliff:g> intentos incorrectos más, tendrás que desbloquear la TV con una cuenta de correo electrónico.\n\n Vuelve a intentarlo en <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Dibujaste incorrectamente tu patrón de desbloqueo <xliff:g id="NUMBER_0">%d</xliff:g> veces. Luego de <xliff:g id="NUMBER_1">%d</xliff:g> intentos incorrectos más, se te solicitará que desbloquees tu dispositivo mediante el uso de una cuenta de correo.\n\n Vuelve a intentarlo en <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Dibujaste incorrectamente tu patrón de desbloqueo <xliff:g id="NUMBER_0">%1$d</xliff:g> veces. Luego de <xliff:g id="NUMBER_1">%2$d</xliff:g> intentos incorrectos más, se te solicitará que desbloquees tu tablet mediante el uso de una cuenta de correo.\n\n Vuelve a intentarlo en <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Trazaste el patrón de desbloqueo <xliff:g id="NUMBER_0">%1$d</xliff:g> veces de manera incorrecta. Después de <xliff:g id="NUMBER_1">%2$d</xliff:g> intentos incorrectos más, tendrás que desbloquear la TV con una cuenta de correo electrónico.\n\n Vuelve a intentarlo en <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Dibujaste incorrectamente tu patrón de desbloqueo <xliff:g id="NUMBER_0">%1$d</xliff:g> veces. Luego de <xliff:g id="NUMBER_1">%2$d</xliff:g> intentos incorrectos más, se te solicitará que desbloquees tu dispositivo mediante el uso de una cuenta de correo.\n\n Vuelve a intentarlo en <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Eliminar"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"¿Quieres subir el volumen por encima del nivel recomendado?\n\nEscuchar a un alto volumen durante largos períodos puede dañar tu audición."</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">Durante 1 hora</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Hasta la(s) <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Hasta la hora <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (próxima alarma)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Hasta que lo desactives"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Hasta que desactives No molestar"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-es-watch/strings.xml b/core/res/res/values-es-watch/strings.xml
index 7eedf0c..5e52823 100644
--- a/core/res/res/values-es-watch/strings.xml
+++ b/core/res/res/values-es-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Aplicación <xliff:g id="NUMBER_0">%1$d</xliff:g> de <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Sensores"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"acceder a tus contactos"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"acceder a la ubicación de este reloj"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"acceder a tu calendario"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"enviar y ver mensajes SMS"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"acceder a fotos, contenido multimedia y archivos en el reloj"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"grabar audio"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"hacer fotos y grabar vídeos"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"hacer y administrar llamadas de teléfono"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"acceder a datos del sensor sobre tus constantes vitales"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"aparecer en la barra de estado"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"acceder a sensores corporales (como monitores de frecuencia cardíaca)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"acceder a tu ubicación precisa (basada en red y GPS)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"acceder a tu ubicación aproximada (basada en red)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"enviar comandos a la tarjeta SIM"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"tener acceso completo a la red"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"administrar propietarios del perfil y del dispositivo"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"cambiar estado de WiMAX"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"recibir estado de transferencias de Android Beam"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"dirigir salida de medio"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"consultar sesiones de instalación"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"solicitar instalación de paquetes"</string>
</resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index bb44ed3..aadf387 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Incluye datos personales como números de tarjetas de crédito y contraseñas."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"inhabilitar o modificar la barra de estado"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Permite que la aplicación inhabilite la barra de estado o añada y elimine iconos del sistema."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"barra de estado"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"aparecer en la barra de estado"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Permite que la aplicación aparezca en la barra de estado."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"expandir/contraer la barra de estado"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Permite que la aplicación expanda o contraiga la barra de estado."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Permite que la aplicación reciba y procese mensajes WAP, lo que significa que podría utilizar este permiso para controlar o eliminar mensajes enviados al usuario sin mostrárselos."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"recuperar aplicaciones en ejecución"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Permite que aplicación recupere información sobre tareas que se están ejecutando en ese momento o que se han ejecutado recientemente. La aplicación puede utilizar este permiso para descubrir cuáles son las aplicaciones que se utilizan en el dispositivo."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Administrar propietarios del perfil y del dispositivo"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"administrar propietarios del perfil y del dispositivo"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Permite que las aplicaciones establezcan los propietarios del perfil y del dispositivo."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"reorganizar aplicaciones en ejecución"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Permite que la aplicación mueva tareas a segundo o a primer plano. La aplicación puede utilizar este permiso para realizar estos movimientos sin que se lo indique el usuario."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Permite que la aplicación modifique el registro de llamadas del tablet, incluidos datos sobre llamadas entrantes y salientes. Las aplicaciones malintencionadas pueden usar este permiso para borrar o modificar el registro de llamadas."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Permite que la aplicación modifique el registro de llamadas de la TV, incluidos datos sobre llamadas entrantes y salientes. Las aplicaciones malintencionadas pueden utilizar este permiso para borrar o modificar el registro de llamadas."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Permite que la aplicación modifique el registro de llamadas del teléfono, incluidos datos sobre llamadas entrantes y salientes. Las aplicaciones malintencionadas pueden usar este permiso para borrar o modificar el registro de llamadas."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"sensores corporales (como monitores de frecuencia cardíaca)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"sensores corp. (como monitores frec. cardíaca)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Permite que la aplicación acceda a datos de sensores que controlan tu condición física, como la frecuencia cardíaca."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"leer eventos de calendario e información confidencial"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Permite que la aplicación consulte todos los eventos de calendario almacenados en el tablet, incluidos los de amigos y compañeros de trabajo. La aplicación puede utilizar este permiso para compartir o guardar datos del calendario del usuario sin tener en cuenta si son privados o confidenciales."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Permite que la aplicación añada, elimine y cambie eventos que se pueden modificar en el teléfono, incluidos los de amigos o compañeros de trabajo. La aplicación puede utilizar este permiso para enviar mensajes que parezcan proceder de propietarios de un calendario o para modificar eventos sin conocimiento de los propietarios."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"acceder a comandos de proveedor de ubicación adicional"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Permite que la aplicación acceda a otros comandos del proveedor de ubicación. De esta forma, la aplicación podrá interferir en el funcionamiento del GPS o de otras fuentes de ubicación."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"ubicación precisa (basada en redes y GPS)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"acceder a tu ubicación precisa (basada en red y GPS)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Permite que la aplicación obtenga tu ubicación precisa mediante el Sistema de posicionamiento global (GPS) o fuentes de ubicación de red, como torres de telefonía y redes Wi-Fi. Estos servicios de ubicación deben estar activados y disponibles para que la aplicación pueda utilizarlos. Las aplicaciones pueden utilizar este permiso para determinar tu ubicación y es posible que el dispositivo consuma más batería."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"ubicación aproximada (basada en redes)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"acceder a tu ubicación aproximada (basada en red)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Permite que la aplicación obtenga tu ubicación aproximada. Esta ubicación se deriva de los servicios de ubicación que utilizan fuentes de ubicación de red, como torres de telefonía y redes Wi-Fi. Estos servicios de ubicación deben estar activados y disponibles para que la aplicación pueda utilizarlos. Las aplicaciones pueden utilizar este permiso para determinar tu ubicación de forma aproximada."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"cambiar la configuración de audio"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Permite que la aplicación modifique la configuración de audio global (por ejemplo, el volumen y el altavoz de salida)."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"grabar sonido"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Permite que la aplicación grabe audio con el micrófono. La aplicación puede utilizar este permiso para grabar audio en cualquier momento sin tener la confirmación del usuario."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"comunicación con la tarjeta SIM"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"enviar comandos a la tarjeta SIM"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Permite que la aplicación envíe comandos a la tarjeta SIM. Este permiso es muy peligroso."</string>
<string name="permlab_camera" msgid="3616391919559751192">"realizar fotografías y vídeos"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Permite que la aplicación haga fotos o grabe vídeos con la cámara. Este permiso autoriza a la aplicación a utilizar la cámara en cualquier momento sin tu confirmación."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Permite que la aplicación obtenga una lista de cuentas reconocidas por el teléfono, entre las que se pueden incluir las cuentas creadas por las aplicaciones que hayas instalado."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"ver conexiones de red"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Permite que la aplicación vea información sobre conexiones de red (por ejemplo, qué redes existen y están conectadas)."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"acceso completo a la red"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"tener acceso completo a la red"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Permite que la aplicación cree sockets de red y utilice protocolos de red personalizados. El navegador y otras aplicaciones proporcionan los medios necesarios para el envío de datos a Internet, por lo que no hace falta utilizar este permiso para eso."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"cambiar la conectividad de red"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Permite que la aplicación modifique el estado de la conectividad de red."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Permite que la aplicación configure el teléfono Bluetooth local y que detecte dispositivos remotos y se vincule con ellos."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"conectarse a WiMAX y desconectarse de esta red"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Permite que la aplicación determine si está habilitada la conexión WiMAX y obtenga información sobre las redes WiMAX que están conectadas."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Cambiar estado de WiMAX"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"cambiar estado de WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Permite que la aplicación conecte el tablet a redes WiMAX y lo desconecte de ellas."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Permite que la aplicación conecte la TV a una red WiMAX y la desconecte."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Permite que la aplicación conecte el teléfono a redes WiMAX y lo desconecte de ellas."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Permite que la aplicación modifique los parámetros de calibración de la pantalla táctil. No debe ser necesario para las aplicaciones normales."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"acceder a certificados DRM"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Permite que una aplicación proporcione y utilice certificados DRM. Las aplicaciones normales no deberían necesitar este permiso."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Recibir estado de transferencias de Android Beam"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"recibir estado de transferencias de Android Beam"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Permite que esta aplicación reciba información sobre las transferencias actuales de Android Beam"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"eliminar certificados DRM"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Permite a una aplicación eliminar los certificados DRM. Las aplicaciones normales no deberían necesitar este permiso."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Código PIN incorrecto"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Para desbloquear el teléfono, pulsa la tecla de menú y, a continuación, pulsa 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Número de emergencia"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Sin servicio"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Sin servicio"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Pantalla bloqueada"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Pulsa la tecla de menú para desbloquear el teléfono o realizar una llamada de emergencia."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Pulsa la tecla de menú para desbloquear la pantalla."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Consulta la guía del usuario o ponte en contacto con el servicio de atención al cliente."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"Introduce el código PIN."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Desbloqueando tarjeta SIM..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Has realizado <xliff:g id="NUMBER_0">%d</xliff:g> intentos fallidos de creación de un patrón de desbloqueo. \n\nInténtalo de nuevo dentro de <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Has introducido una contraseña incorrecta <xliff:g id="NUMBER_0">%d</xliff:g> veces. \n\nInténtalo de nuevo dentro de <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Has introducido un código PIN incorrecto <xliff:g id="NUMBER_0">%d</xliff:g> veces. \n\nInténtalo de nuevo dentro de <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Has fallado <xliff:g id="NUMBER_0">%d</xliff:g> veces al dibujar el patrón de desbloqueo. Si fallas otras <xliff:g id="NUMBER_1">%d</xliff:g> veces, deberás usar tus credenciales de acceso de Google para desbloquear el tablet.\n\n Inténtalo de nuevo dentro de <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Has dibujado el patrón de desbloqueo incorrectamente <xliff:g id="NUMBER_0">%d</xliff:g> veces. Si se producen <xliff:g id="NUMBER_1">%d</xliff:g> intentos incorrectos más, deberás desbloquear la TV iniciando sesión en Google.\n\n Vuelve a intentarlo dentro de <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Has fallado <xliff:g id="NUMBER_0">%d</xliff:g> veces al dibujar el patrón de desbloqueo. Si fallas otras <xliff:g id="NUMBER_1">%d</xliff:g> veces, deberás usar tus credenciales de acceso de Google para desbloquear el teléfono.\n\n Inténtalo de nuevo dentro de <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Has intentado desbloquear el tablet <xliff:g id="NUMBER_0">%d</xliff:g> veces, pero no lo has conseguido. Si fallas <xliff:g id="NUMBER_1">%d</xliff:g> veces más, se restablecerán los datos de fábrica y se perderán todos los datos del usuario."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Has intentado desbloquear la TV incorrectamente <xliff:g id="NUMBER_0">%d</xliff:g> veces. Si se producen <xliff:g id="NUMBER_1">%d</xliff:g> intentos incorrectos más, la TV se restablecerá a los valores predeterminados de fábrica y se perderán todos los datos del usuario."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Has intentado desbloquear el teléfono <xliff:g id="NUMBER_0">%d</xliff:g> veces, pero no lo has conseguido. Si fallas <xliff:g id="NUMBER_1">%d</xliff:g> veces más, se restablecerán los datos de fábrica y se perderán todos los datos del usuario."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Has realizado <xliff:g id="NUMBER_0">%1$d</xliff:g> intentos fallidos de creación de un patrón de desbloqueo. \n\nInténtalo de nuevo dentro de <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Has introducido una contraseña incorrecta <xliff:g id="NUMBER_0">%1$d</xliff:g> veces. \n\nInténtalo de nuevo dentro de <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Has introducido un código PIN incorrecto <xliff:g id="NUMBER_0">%1$d</xliff:g> veces. \n\nInténtalo de nuevo dentro de <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Has fallado <xliff:g id="NUMBER_0">%1$d</xliff:g> veces al dibujar el patrón de desbloqueo. Si fallas otras <xliff:g id="NUMBER_1">%2$d</xliff:g> veces, deberás usar tus credenciales de acceso de Google para desbloquear el tablet.\n\n Inténtalo de nuevo dentro de <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Has dibujado el patrón de desbloqueo incorrectamente <xliff:g id="NUMBER_0">%1$d</xliff:g> veces. Si se producen <xliff:g id="NUMBER_1">%2$d</xliff:g> intentos incorrectos más, deberás desbloquear la TV iniciando sesión en Google.\n\n Vuelve a intentarlo dentro de <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Has fallado <xliff:g id="NUMBER_0">%1$d</xliff:g> veces al dibujar el patrón de desbloqueo. Si fallas otras <xliff:g id="NUMBER_1">%2$d</xliff:g> veces, deberás usar tus credenciales de acceso de Google para desbloquear el teléfono.\n\n Inténtalo de nuevo dentro de <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Has intentado desbloquear el tablet <xliff:g id="NUMBER_0">%1$d</xliff:g> veces, pero no lo has conseguido. Si fallas <xliff:g id="NUMBER_1">%2$d</xliff:g> veces más, se restablecerán los datos de fábrica y se perderán todos los datos del usuario."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Has intentado desbloquear la TV incorrectamente <xliff:g id="NUMBER_0">%1$d</xliff:g> veces. Si se producen <xliff:g id="NUMBER_1">%2$d</xliff:g> intentos incorrectos más, la TV se restablecerá a los valores predeterminados de fábrica y se perderán todos los datos del usuario."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Has intentado desbloquear el teléfono <xliff:g id="NUMBER_0">%1$d</xliff:g> veces, pero no lo has conseguido. Si fallas <xliff:g id="NUMBER_1">%2$d</xliff:g> veces más, se restablecerán los datos de fábrica y se perderán todos los datos del usuario."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Has intentado desbloquear el tablet <xliff:g id="NUMBER">%d</xliff:g> veces, pero no lo has conseguido. Se restablecerán los datos de fábrica del dispositivo."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Has intentando desbloquear la TV incorrectamente <xliff:g id="NUMBER">%d</xliff:g> veces. La TV se restablecerá a los valores predeterminados de fábrica."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Has intentado desbloquear el teléfono <xliff:g id="NUMBER">%d</xliff:g> veces, pero no lo has conseguido. Se restablecerán los datos de fábrica del dispositivo."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"palabra"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"enlace"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"línea"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Fallo en la prueba de fábrica"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"La acción FACTORY_TEST solo es compatible con los paquetes instalados en /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"No se ha encontrado ningún paquete que proporcione la acción FACTORY_TEST."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Formateando…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"No insertado"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"No se ha encontrado ninguna actividad coincidente."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Dirigir salida de medio"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"dirigir salida de medio"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Permite que la aplicación dirija salidas de medios a otros dispositivos externos."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Consultar sesiones de instalación"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"consultar sesiones de instalación"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Permite que una aplicación consulte sesiones de instalación para ver detalles sobre instalaciones de paquetes activos."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Solicitar instalación de paquetes"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"solicitar instalación de paquetes"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Permite a una aplicación solicitar la instalación de paquetes."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Toca dos veces para acceder al control de zoom."</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"No se ha podido añadir el widget."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"El nombre de usuario o la contraseña no son válidos."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Si has olvidado tu nombre de usuario o tu contraseña,\naccede a la página "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Comprobando cuenta…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Has introducido un código PIN incorrecto <xliff:g id="NUMBER_0">%d</xliff:g> veces. \n\nInténtalo de nuevo en <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Has introducido una contraseña incorrecta <xliff:g id="NUMBER_0">%d</xliff:g> veces. \n\nInténtalo de nuevo en <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Has fallado <xliff:g id="NUMBER_0">%d</xliff:g> veces al dibujar tu patrón de desbloqueo. \n\nInténtalo de nuevo en <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Has intentado desbloquear el tablet <xliff:g id="NUMBER_0">%d</xliff:g> veces, pero no lo has conseguido. Si fallas otras <xliff:g id="NUMBER_1">%d</xliff:g> veces, se restablecerán los datos de fábrica y se perderán todos los datos del usuario."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Has intentado desbloquear la TV incorrectamente <xliff:g id="NUMBER_0">%d</xliff:g> veces. Si se producen <xliff:g id="NUMBER_1">%d</xliff:g> intentos incorrectos más, la TV se restablecerá a los valores predeterminados de fábrica y se perderán todos los datos del usuario."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Has intentado desbloquear el teléfono <xliff:g id="NUMBER_0">%d</xliff:g> veces, pero no lo has conseguido. Si fallas otras <xliff:g id="NUMBER_1">%d</xliff:g> veces, se restablecerán los datos de fábrica y se perderán todos los datos del usuario."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Has introducido un código PIN incorrecto <xliff:g id="NUMBER_0">%1$d</xliff:g> veces. \n\nInténtalo de nuevo en <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Has introducido una contraseña incorrecta <xliff:g id="NUMBER_0">%1$d</xliff:g> veces. \n\nInténtalo de nuevo en <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Has fallado <xliff:g id="NUMBER_0">%1$d</xliff:g> veces al dibujar tu patrón de desbloqueo. \n\nInténtalo de nuevo en <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Has intentado desbloquear el tablet <xliff:g id="NUMBER_0">%1$d</xliff:g> veces, pero no lo has conseguido. Si fallas otras <xliff:g id="NUMBER_1">%2$d</xliff:g> veces, se restablecerán los datos de fábrica y se perderán todos los datos del usuario."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Has intentado desbloquear la TV incorrectamente <xliff:g id="NUMBER_0">%1$d</xliff:g> veces. Si se producen <xliff:g id="NUMBER_1">%2$d</xliff:g> intentos incorrectos más, la TV se restablecerá a los valores predeterminados de fábrica y se perderán todos los datos del usuario."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Has intentado desbloquear el teléfono <xliff:g id="NUMBER_0">%1$d</xliff:g> veces, pero no lo has conseguido. Si fallas otras <xliff:g id="NUMBER_1">%2$d</xliff:g> veces, se restablecerán los datos de fábrica y se perderán todos los datos del usuario."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Has intentado desbloquear el tablet <xliff:g id="NUMBER">%d</xliff:g> veces, pero no lo has conseguido. Se restablecerán los datos de fábrica del dispositivo."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Has intentando desbloquear la TV incorrectamente <xliff:g id="NUMBER">%d</xliff:g> veces. La TV se restablecerá a los valores predeterminados de fábrica."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Has intentado desbloquear el teléfono <xliff:g id="NUMBER">%d</xliff:g> veces, pero no lo has conseguido. Se restablecerán los datos de fábrica del dispositivo."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Has fallado <xliff:g id="NUMBER_0">%d</xliff:g> veces al dibujar el patrón de desbloqueo. Si fallas otras <xliff:g id="NUMBER_1">%d</xliff:g> veces, deberás usar una cuenta de correo electrónico para desbloquear el tablet.\n\n Inténtalo de nuevo en <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Has dibujado el patrón de desbloqueo incorrectamente <xliff:g id="NUMBER_0">%d</xliff:g> veces. Si se producen <xliff:g id="NUMBER_1">%d</xliff:g> intentos incorrectos más, deberás desbloquear la TV mediante una cuenta de correo electrónico.\n\n Vuelve a intentarlo dentro de <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Has fallado <xliff:g id="NUMBER_0">%d</xliff:g> veces al dibujar el patrón de desbloqueo. Si fallas otras <xliff:g id="NUMBER_1">%d</xliff:g> veces, deberás usar una cuenta de correo electrónico para desbloquear el teléfono.\n\n Inténtalo de nuevo en <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Has fallado <xliff:g id="NUMBER_0">%1$d</xliff:g> veces al dibujar el patrón de desbloqueo. Si fallas otras <xliff:g id="NUMBER_1">%2$d</xliff:g> veces, deberás usar una cuenta de correo electrónico para desbloquear el tablet.\n\n Inténtalo de nuevo en <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Has dibujado el patrón de desbloqueo incorrectamente <xliff:g id="NUMBER_0">%1$d</xliff:g> veces. Si se producen <xliff:g id="NUMBER_1">%2$d</xliff:g> intentos incorrectos más, deberás desbloquear la TV mediante una cuenta de correo electrónico.\n\n Vuelve a intentarlo dentro de <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Has fallado <xliff:g id="NUMBER_0">%1$d</xliff:g> veces al dibujar el patrón de desbloqueo. Si fallas otras <xliff:g id="NUMBER_1">%2$d</xliff:g> veces, deberás usar una cuenta de correo electrónico para desbloquear el teléfono.\n\n Inténtalo de nuevo en <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Eliminar"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"¿Quieres subir el volumen por encima del nivel recomendado?\n\nEscuchar sonidos a alto volumen durante largos períodos de tiempo puede dañar tus oídos."</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">Durante 1 hora</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Hasta las <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Hasta las <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (próxima alarma)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Hasta apagar el dispositivo"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Hasta que desactives la opción No molestar"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-et-rEE-watch/strings.xml b/core/res/res/values-et-rEE-watch/strings.xml
index c554e38..fc9b98a 100644
--- a/core/res/res/values-et-rEE-watch/strings.xml
+++ b/core/res/res/values-et-rEE-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Rakendus <xliff:g id="NUMBER_0">%1$d</xliff:g>/<xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Andurid"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"juurdepääs kontaktidele"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"juurdepääs selle kella asukohale"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"juurdepääs kalendrile"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"SMS-sõnumite saatmine ja vaatamine"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"juurdepääs teie kellas olevatele fotodele, meediale ja failidele"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"heli salvestamine"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"pildistamine ja video salvestamine"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"helistamine ja telefonikõnede haldamine"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"juurdepääs anduri andmetele teie eluliste näitajate kohta"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"olekuribana kuvamine"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"juurdepääs kehaanduritele (nt pulsilugeja)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"juurdepääs täpsele asukohale (GPS-i ja võrgupõhine)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"juurdepääs ligikaudsele asukohale (võrgupõhine)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"SIM-kaardile käskluste saatmine"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"täielik juurdepääs võrgule"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"profiilide ja seadmeomanike haldamine"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"WiMAX-i oleku muutmine"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"Android Beami ülekande oleku vastuvõtmine"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"meediaväljundi marsruutimine"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"installiseansside lugemine"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"installipakettide taotlemine"</string>
</resources>
diff --git a/core/res/res/values-et-rEE/strings.xml b/core/res/res/values-et-rEE/strings.xml
index 98818ad..4aed72d 100644
--- a/core/res/res/values-et-rEE/strings.xml
+++ b/core/res/res/values-et-rEE/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Sisaldab isiklikke andmeid, nt krediitkaardi numbreid ja paroole."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"keela või muuda olekuriba"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Võimaldab rakendusel keelata olekuriba või lisada ja eemaldada süsteemiikoone."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"olekuriba"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"olekuribana kuvamine"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Võimaldab rakendusel olla olekuriba."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"laienda/ahenda olekuriba"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Võimaldab rakendusel laiendada või ahendada olekuriba."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Võimaldab rakendusel vastu võtta ja töödelda WAP-sõnumeid. See luba hõlmab võimet jälgida või kustutada teile saadetud sõnumeid neid teile näitamata."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"Käitatud rakenduste toomine"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Võimaldab rakendusel tuua teavet praegu ja hiljuti käitatud ülesannete kohta. See võib lubada rakendusel avastada teavet selle kohta, milliseid rakendusi seadmes kasutatakse."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Profiili- ja seadmeomanike haldamine"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"profiilide ja seadme omanike haldamine"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Võimaldab rakendustel määrata profiiliomanikud ja seadmeomaniku."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"käitatud rakenduste ümberjärjestamine"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Võimaldab rakendusel teisaldada ülesanded esiplaanile ja taustale. Rakendus võib seda teha teie sisendita."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Lubab rakendusel muuta tahvelarvuti kõnelogi, sh sissetulevate ja väljaminevate kõnede andmeid. Pahatahtlikud rakendused võivad kasutada seda kõnelogi kustutamiseks või muutmiseks."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Lubab rakendusel muuta teleri kõnelogi, sh sissetulevate ja väljaminevate kõnede andmeid. Pahatahtlikud rakendused võivad kasutada seda kõnelogi kustutamiseks või muutmiseks."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Lubab rakendusel muuta telefoni kõnelogi, sh sissetulevate ja väljaminevate kõnede andmeid. Pahatahtlikud rakendused võivad kasutada seda kõnelogi kustutamiseks või muutmiseks."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"kehaandurid (nt pulsilugeja)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"juurdepääs kehaanduritele (nt pulsilugeja)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Lubab rakendusel hankida juurdepääsu andmetele anduritest, mis jälgivad teie füüsilist seisundit, nt südame löögisagedust."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"kalendrisündmuste lugemine ja konfidentsiaalne teave"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Võimaldab rakendusel lugeda kõiki tahvelarvutisse salvestatud kalendrisündmusi, sh sõprade või töökaaslaste omi. See võib lubada rakendusel jagada või salvestada teie kalendriandmeid, hoolimata konfidentsiaalsusest või tundlikkusest."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Võimaldab rakendusel lisada, eemaldada ja muuta sündmusi, mida saate muuta oma telefonis, sh sõprade ja töökaaslaste omi. See võib võimaldada rakendusel saata sõnumeid, mis näivad tulevat kalendri omanikelt, või muuta sündmusi omaniku teadmata."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"juurdepääs asukohapakkuja lisakäskudele"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Võimaldab rakendusel juurde pääseda asukohapakkuja erikäskudele. See võib lubada rakendusel mõjutada GPS-i või muude asukohaallikate tööd."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"täpne asukoht (GPS- ja võrgupõhine)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"juurdepääs täpsele asukohale (GPS-i ja võrgupõhine)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Lubab rakendusel hankida teie täpse asukoha globaalse positsioneerimissüsteemi (GPS) või võrgu asukohaallikate (nt mobiilimastide ja WiFi) järgi. Need asukohateenused peavad olema sisse lülitatud ja teie seadme jaoks saadaval, et rakendus saaks neid kasutada. Rakendused võivad kasutada seda teie asukoha tuvastamiseks ja tarbida akut."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"ligikaudne asukoht (võrgupõhine)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"juurdepääs ligikaudsele asukohale (võrgupõhine)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Lubab rakendusel hankida juurdepääsu ligikaudsele asukohale. Asukoht tuletatakse asukohateenuste järgi, kasutades võrgu asukohaallikaid, nt mobiilimastid ja WiFi. Need asukohateenused peavad olema sisse lülitatud ja teie seadme jaoks saadaval, et rakendus saaks neid kasutada. Rakendused võivad kasutada seda teie ligikaudse asukoha tuvastamiseks."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"muuda heliseadeid"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Võimaldab rakendusel muuta üldiseid heliseadeid, näiteks helitugevust ja seda, millist kõlarit kasutatakse väljundiks."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"salvesta heli"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Võimaldab rakendusel salvestada mikrofoniga heli. See luba võimaldab rakendusel salvestada heli igal ajal ilma teie kinnituseta."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"side SIM-kaardiga"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"SIM-kaardile käskluste saatmine"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Lubab rakendusel saata käske SIM-kaardile. See on väga ohtlik."</string>
<string name="permlab_camera" msgid="3616391919559751192">"piltide ja videote tegemine"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Võimaldab rakendusel teha kaameraga pilte ja videoid. See luba võimaldab rakendusel kasutada kaamerat mis tahes ajal teie kinnituseta."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Võimaldab rakendusel hankida telefonile teadaolevaid kontoloendeid. See võib hõlmata mis tahes kontosid, mille on loonud teie installitud rakendused."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"vaadake võrguühendusi"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Lubab rakendusel vaadata teavet võrguühenduste kohta, näiteks seda, millised võrgud on olemas ja ühendatud."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"täielik juurdepääs võrgule"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"täielik juurdepääs võrgule"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Võimaldab rakendusel luua võrgupesasid ja kasutada kohandatud võrguprotokolle. Brauser ja teised rakendused annavad vahendid andmete saatmiseks Internetti, nii et seda luba ei ole vaja andmete saatmiseks Internetti."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"muuda võrguühenduvust"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Võimaldab rakendusel muuta võrguühenduvuse olekut."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Võimaldab rakendusel seadistada kohalikku Bluetooth-telefoni ning leida ja siduda seda kaugseadmetega."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"WiMAX-iga ühenduse loomine ja ühenduse katkestamine"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Võimaldab rakendusel määrata, kas WiMAX on lubatud, ja vaadata teavet kõikide ühendatud WiMAX-võrkude kohta."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX-i oleku muutmine"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"WiMAX-i oleku muutmine"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Võimaldab rakendusel luua ja katkestada tahvelarvuti ühenduse WiMAX-i võrkudega."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Lubab rakendusel ühendada teleri WiMAX-i võrku ja ühenduse katkestada."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Võimaldab rakendusel luua ja katkestada telefoni ühenduse WiMAX-i võrkudega."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Lubab rakendusel muuta puuteekraani kalibreerimisparameetreid. Ei tohiks kunagi olla vajalik tavaliste rakenduste puhul."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"juurdepääs DRM-i sertifikaatidele"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Lubab rakendusel ette valmistada ja kasutada DRM-i sertifikaate. Tavarakenduste puhul ei tohiks see vajalik olla."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Android Beami ülekande oleku vastuvõtmine"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"Android Beami ülekande oleku vastuvõtmine"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Lubab rakendusel saada teavet praeguste Android Beami ülekannete kohta"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"DRM-sertifikaatide eemaldamine"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Lubab rakendusel eemaldada DRM-sertifikaate. Pole kunagi vajalik tavaliste rakenduste puhul."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Vale PIN-kood."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Avamiseks vajutage menüüklahvi, seejärel klahvi 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Hädaabinumber"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Teenus puudub."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Teenus puudub"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Ekraan lukus."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Vajutage avamiseks või hädaabikõne tegemiseks menüünuppu"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Vajutage avamiseks menüüklahvi."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Vaadake kasutusjuhendit või võtke ühendust klienditeenindusega."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM-kaart on lukus."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"SIM-kaardi avamine ..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Olete oma avamismustrit <xliff:g id="NUMBER_0">%d</xliff:g> korda valesti koostanud. \n\nProovige <xliff:g id="NUMBER_1">%d</xliff:g> sekundi pärast uuesti."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Olete parooli <xliff:g id="NUMBER_0">%d</xliff:g> korda valesti sisestanud. \n\nProovige <xliff:g id="NUMBER_1">%d</xliff:g> sekundi pärast uuesti."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Olete PIN-koodi <xliff:g id="NUMBER_0">%d</xliff:g> korda valesti sisestanud. \n\nProovige <xliff:g id="NUMBER_1">%d</xliff:g> sekundi pärast uuesti."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Joonistasite oma avamismustri <xliff:g id="NUMBER_0">%d</xliff:g> korda valesti. Pärast veel <xliff:g id="NUMBER_1">%d</xliff:g> ebaõnnestunud katset palutakse teil tahvelarvuti avada Google\'i sisselogimisega.\n\n Proovige <xliff:g id="NUMBER_2">%d</xliff:g> sekundi pärast uuesti."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Olete avamismustri <xliff:g id="NUMBER_0">%d</xliff:g> korda valesti joonistanud. Kui joonistate mustri veel <xliff:g id="NUMBER_1">%d</xliff:g> korda valesti, palutakse teil avada teler Google\'i sisselogimisandmete abil.\n\n Proovige uuesti <xliff:g id="NUMBER_2">%d</xliff:g> sekundi pärast."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Joonistasite oma avamismustri <xliff:g id="NUMBER_0">%d</xliff:g> korda valesti. Pärast veel <xliff:g id="NUMBER_1">%d</xliff:g> ebaõnnestunud katset palutakse teil telefon avada Google\'i sisselogimisega.\n\n Proovige <xliff:g id="NUMBER_2">%d</xliff:g> sekundi pärast uuesti."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Olete üritanud <xliff:g id="NUMBER_0">%d</xliff:g> korda tahvelarvutit valesti avada. Pärast veel <xliff:g id="NUMBER_1">%d</xliff:g> edutut katset lähtestatakse tahvelarvuti tehase vaikeseadetele ja kõik kasutajaandmed lähevad kaotsi."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Teleri avamine on <xliff:g id="NUMBER_0">%d</xliff:g> korda ebaõnnestunud. Kui veel <xliff:g id="NUMBER_1">%d</xliff:g> katset ebaõnnestub, lähtestatakse teler tehaseseadetele ja kasutajaandmed lähevad kaotsi."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Olete üritanud <xliff:g id="NUMBER_0">%d</xliff:g> korda telefoni valesti avada. Pärast veel <xliff:g id="NUMBER_1">%d</xliff:g> ebaõnnestunud katset lähtestatakse telefon tehase vaikeseadetele ja kõik kasutajaandmed lähevad kaotsi."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Olete oma avamismustrit <xliff:g id="NUMBER_0">%1$d</xliff:g> korda valesti koostanud. \n\nProovige <xliff:g id="NUMBER_1">%2$d</xliff:g> sekundi pärast uuesti."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Olete parooli <xliff:g id="NUMBER_0">%1$d</xliff:g> korda valesti sisestanud. \n\nProovige <xliff:g id="NUMBER_1">%2$d</xliff:g> sekundi pärast uuesti."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Olete PIN-koodi <xliff:g id="NUMBER_0">%1$d</xliff:g> korda valesti sisestanud. \n\nProovige <xliff:g id="NUMBER_1">%2$d</xliff:g> sekundi pärast uuesti."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Joonistasite oma avamismustri <xliff:g id="NUMBER_0">%1$d</xliff:g> korda valesti. Pärast veel <xliff:g id="NUMBER_1">%2$d</xliff:g> ebaõnnestunud katset palutakse teil tahvelarvuti avada Google\'i sisselogimisega.\n\n Proovige <xliff:g id="NUMBER_2">%3$d</xliff:g> sekundi pärast uuesti."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Olete avamismustri <xliff:g id="NUMBER_0">%1$d</xliff:g> korda valesti joonistanud. Kui joonistate mustri veel <xliff:g id="NUMBER_1">%2$d</xliff:g> korda valesti, palutakse teil avada teler Google\'i sisselogimisandmete abil.\n\n Proovige uuesti <xliff:g id="NUMBER_2">%3$d</xliff:g> sekundi pärast."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Joonistasite oma avamismustri <xliff:g id="NUMBER_0">%1$d</xliff:g> korda valesti. Pärast veel <xliff:g id="NUMBER_1">%2$d</xliff:g> ebaõnnestunud katset palutakse teil telefon avada Google\'i sisselogimisega.\n\n Proovige <xliff:g id="NUMBER_2">%3$d</xliff:g> sekundi pärast uuesti."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Olete üritanud <xliff:g id="NUMBER_0">%1$d</xliff:g> korda tahvelarvutit valesti avada. Pärast veel <xliff:g id="NUMBER_1">%2$d</xliff:g> edutut katset lähtestatakse tahvelarvuti tehase vaikeseadetele ja kõik kasutajaandmed lähevad kaotsi."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Teleri avamine on <xliff:g id="NUMBER_0">%1$d</xliff:g> korda ebaõnnestunud. Kui veel <xliff:g id="NUMBER_1">%2$d</xliff:g> katset ebaõnnestub, lähtestatakse teler tehaseseadetele ja kasutajaandmed lähevad kaotsi."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Olete üritanud <xliff:g id="NUMBER_0">%1$d</xliff:g> korda telefoni valesti avada. Pärast veel <xliff:g id="NUMBER_1">%2$d</xliff:g> ebaõnnestunud katset lähtestatakse telefon tehase vaikeseadetele ja kõik kasutajaandmed lähevad kaotsi."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Olete püüdnud tahvelarvutit <xliff:g id="NUMBER">%d</xliff:g> korda valesti avada. Tahvelarvuti lähtestatakse nüüd tehase vaikeseadetele."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Teleri avamine on <xliff:g id="NUMBER">%d</xliff:g> korda ebaõnnestunud. Teler lähtestatakse tehaseseadetele."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Olete püüdnud telefoni <xliff:g id="NUMBER">%d</xliff:g> korda valesti avada. Telefon lähtestatakse nüüd tehase vaikeseadetele."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"sõna"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"link"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"rida"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Tehasetest ebaõnnestus"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Toimingut FACTORY_TEST toetatakse ainult kausta \\system\\app installitud pakettide puhul."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Ei leitud ühtegi paketti, mis võimaldaks toimingut FACTORY_TEST."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Vormindamine ..."</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Pole sisestatud"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Sobivat tegevust ei leitud"</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Meediaväljundi teekonna koostamine"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"meediaväljundi marsruutimine"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Võimaldab rakendusel koostada teekonna meediaväljundist teistesse välistesse seadmetesse."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Installiseansside lugemine"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"installiseansside lugemine"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Lubab rakendusel lugeda installiseansse. See võimaldab näha aktiivse paketi installimise üksikasju."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Installipakettide taotlemine"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"installipakettide taotlemine"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Võimaldab rakendusel pakettide installimist taotleda."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Suumi juhtimiseks puudutage kaks korda"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Vidinat ei saanud lisada."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Vale kasutajanimi või parool."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Kas unustasite kasutajanime või parooli?\nKülastage aadressi "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Konto kontrollimine ..."</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Olete PIN-koodi <xliff:g id="NUMBER_0">%d</xliff:g> korda valesti sisestanud.\n\nProovige <xliff:g id="NUMBER_1">%d</xliff:g> sekundi pärast uuesti."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Olete parooli <xliff:g id="NUMBER_0">%d</xliff:g> korda valesti sisestanud. \n\nProovige uuesti <xliff:g id="NUMBER_1">%d</xliff:g> sekundi pärast."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Joonistasite oma avamismustri <xliff:g id="NUMBER_0">%d</xliff:g> korda valesti.\n\nProovige <xliff:g id="NUMBER_1">%d</xliff:g> sekundi pärast uuesti."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Olete üritanud <xliff:g id="NUMBER_0">%d</xliff:g> korda tahvelarvutit valesti avada. Pärast veel <xliff:g id="NUMBER_1">%d</xliff:g> ebaõnnestunud katset lähtestatakse tahvelarvuti tehase vaikeseadetele ja kõik kasutajaandmed lähevad kaotsi."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Teleri avamine on <xliff:g id="NUMBER_0">%d</xliff:g> korda ebaõnnestunud. Kui veel <xliff:g id="NUMBER_1">%d</xliff:g> katset ebaõnnestub, lähtestatakse teler tehaseseadetele ja kasutajaandmed lähevad kaotsi."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Olete üritanud <xliff:g id="NUMBER_0">%d</xliff:g> korda telefoni valesti avada. Pärast veel <xliff:g id="NUMBER_1">%d</xliff:g> ebaõnnestunud katset lähtestatakse telefon tehase vaikeseadetele ja kõik kasutajaandmed lähevad kaotsi."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Olete PIN-koodi <xliff:g id="NUMBER_0">%1$d</xliff:g> korda valesti sisestanud.\n\nProovige <xliff:g id="NUMBER_1">%2$d</xliff:g> sekundi pärast uuesti."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Olete parooli <xliff:g id="NUMBER_0">%1$d</xliff:g> korda valesti sisestanud. \n\nProovige uuesti <xliff:g id="NUMBER_1">%2$d</xliff:g> sekundi pärast."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Joonistasite oma avamismustri <xliff:g id="NUMBER_0">%1$d</xliff:g> korda valesti.\n\nProovige <xliff:g id="NUMBER_1">%2$d</xliff:g> sekundi pärast uuesti."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Olete üritanud <xliff:g id="NUMBER_0">%1$d</xliff:g> korda tahvelarvutit valesti avada. Pärast veel <xliff:g id="NUMBER_1">%2$d</xliff:g> ebaõnnestunud katset lähtestatakse tahvelarvuti tehase vaikeseadetele ja kõik kasutajaandmed lähevad kaotsi."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Teleri avamine on <xliff:g id="NUMBER_0">%1$d</xliff:g> korda ebaõnnestunud. Kui veel <xliff:g id="NUMBER_1">%2$d</xliff:g> katset ebaõnnestub, lähtestatakse teler tehaseseadetele ja kasutajaandmed lähevad kaotsi."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Olete üritanud <xliff:g id="NUMBER_0">%1$d</xliff:g> korda telefoni valesti avada. Pärast veel <xliff:g id="NUMBER_1">%2$d</xliff:g> ebaõnnestunud katset lähtestatakse telefon tehase vaikeseadetele ja kõik kasutajaandmed lähevad kaotsi."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Olete püüdnud tahvelarvutit <xliff:g id="NUMBER">%d</xliff:g> korda valesti avada. Tahvelarvuti lähtestatakse nüüd tehase vaikeseadetele."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Teleri avamine on <xliff:g id="NUMBER">%d</xliff:g> korda ebaõnnestunud. Teler lähtestatakse tehaseseadetele."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Olete püüdnud telefoni <xliff:g id="NUMBER">%d</xliff:g> korda valesti avada. Telefon lähtestatakse nüüd tehase vaikeseadetele."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Joonistasite oma avamismustri <xliff:g id="NUMBER_0">%d</xliff:g> korda valesti. Pärast veel <xliff:g id="NUMBER_1">%d</xliff:g> ebaõnnestunud katset palutakse teil tahvelarvuti avada meilikontoga.\n\n Proovige uuesti <xliff:g id="NUMBER_2">%d</xliff:g> sekundi pärast."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Olete avamismustri <xliff:g id="NUMBER_0">%d</xliff:g> korda valesti joonistanud. Kui joonistate mustri veel <xliff:g id="NUMBER_1">%d</xliff:g> korda valesti, siis palutakse teil avada teler meilikonto abil.\n\n Proovige uuesti <xliff:g id="NUMBER_2">%d</xliff:g> sekundi pärast."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Joonistasite oma avamismustri <xliff:g id="NUMBER_0">%d</xliff:g> korda valesti. Pärast veel <xliff:g id="NUMBER_1">%d</xliff:g> ebaõnnestunud katset palutakse teil telefon avada meilikontoga.\n\n Proovige uuesti <xliff:g id="NUMBER_2">%d</xliff:g> sekundi pärast."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Joonistasite oma avamismustri <xliff:g id="NUMBER_0">%1$d</xliff:g> korda valesti. Pärast veel <xliff:g id="NUMBER_1">%2$d</xliff:g> ebaõnnestunud katset palutakse teil tahvelarvuti avada meilikontoga.\n\n Proovige uuesti <xliff:g id="NUMBER_2">%3$d</xliff:g> sekundi pärast."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Olete avamismustri <xliff:g id="NUMBER_0">%1$d</xliff:g> korda valesti joonistanud. Kui joonistate mustri veel <xliff:g id="NUMBER_1">%2$d</xliff:g> korda valesti, siis palutakse teil avada teler meilikonto abil.\n\n Proovige uuesti <xliff:g id="NUMBER_2">%3$d</xliff:g> sekundi pärast."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Joonistasite oma avamismustri <xliff:g id="NUMBER_0">%1$d</xliff:g> korda valesti. Pärast veel <xliff:g id="NUMBER_1">%2$d</xliff:g> ebaõnnestunud katset palutakse teil telefon avada meilikontoga.\n\n Proovige uuesti <xliff:g id="NUMBER_2">%3$d</xliff:g> sekundi pärast."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Eemalda"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Kas suurendada helitugevuse taset üle soovitatud taseme?\n\nPikaajaline valju helitugevusega kuulamine võib kuulmist kahjustada."</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">1 h</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Kuni <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Kuni <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (järgmine äratus)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Kuni lülitate selle välja"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Kuni lülitate välja valiku Mitte segada"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-eu-rES-watch/strings.xml b/core/res/res/values-eu-rES-watch/strings.xml
index 0fca702..670bc04 100644
--- a/core/res/res/values-eu-rES-watch/strings.xml
+++ b/core/res/res/values-eu-rES-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_0">%1$d</xliff:g>/<xliff:g id="NUMBER_1">%2$d</xliff:g> aplikaz."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Sentsoreak"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"Atzitu kontaktuak"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"Atzitu erlojuaren kokapena"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"Atzitu egutegia"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"Bidali eta ikusi SMS mezuak"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"Atzitu erlojuko argazkiak, multimedia-edukia eta fitxategiak"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"Grabatu audioa"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"Atera argazkiak eta grabatu bideoak"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"Egin eta kudeatu telefono-deiak"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"Atzitu bizi-konstanteei buruzko sentsore-datuak"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"Bihurtu egoera-barra"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"Atzitu gorputzaren sentsoreak (adibidez, bihotz-erritmoaren monitoreak)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"Atzitu kokapen zehatza (GPS sisteman eta sarean oinarrituta)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"Atzitu gutxi gorabeherako kokapena (sarean oinarrituta)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"Bidali aginduak SIM txartelera"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"Izan sarerako sarbide osoa"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"Kudeatu profilen eta gailuen jabeak"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"Aldatu WiMAX egoera"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"Jaso Android Beam transferentzien egoera"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"Bideratu multimedia-irteera"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"Irakurri instalazio-saioak"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"Eskatu instalazio-paketeak"</string>
</resources>
diff --git a/core/res/res/values-eu-rES/strings.xml b/core/res/res/values-eu-rES/strings.xml
index 8e7193a..9f10506 100644
--- a/core/res/res/values-eu-rES/strings.xml
+++ b/core/res/res/values-eu-rES/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Ez da salbuespenik egiten datu pertsonalekin, hala nola, kreditu-txartelen zenbakiekin eta pasahitzekin."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"Desgaitu edo aldatu egoera-barra"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Egoera-barra desgaitzea edo sistema-ikonoak gehitzea edo kentzea baimentzen die aplikazioei."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"egoera-barra"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"Bihurtu egoera-barra"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Egoera-barra izatea baimentzen die aplikazioei."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"zabaldu/tolestu egoera-barra"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Egoera-barra zabaltzea edo tolestea baimentzen die aplikazioei."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"WAP mezuak jasotzeko eta prozesatzeko baimena ematen die aplikazioei. Horrela, aplikazioak, besteak beste, gailura bidalitako mezuak kontrola eta ezaba ditzake zuri erakutsi gabe."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"Eskuratu abian diren aplikazioak"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Unean edo duela gutxi exekutatutako zereginei buruzko informazioa lortzeko baimena ematen die aplikazioei. Horrela, aplikazioak gailuan erabiltzen ari diren aplikazioei buruzko informazioa ezagut dezake."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Kudeatu profilen eta gailuen jabeak"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"Kudeatu profilen eta gailuen jabeak"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Profilaren eta gailuaren jabeak zehazteko baimena ematen die aplikazioei."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"Ordenatu abian diren aplikazioak"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Zereginak aurreko eta atzeko planora eramateko baimena ematen die aplikazioei. Aplikazioak zuk ezer egin gabe egin dezake hori."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Tabletaren deien erregistroa aldatzeko baimena ematen die aplikazioei, sarrerako eta irteerako deiei buruzko datuak barne. Aplikazio gaiztoek deien erregistroa ezabatzeko edo aldatzeko erabil dezakete."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Telebistako deien erregistroa aldatzea baimentzen die aplikazioei, sarrerako eta irteerako deiei buruzko datuak barne. Aplikazio gaiztoek deien erregistroa ezabatzeko edo aldatzeko erabil dezakete."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Telefonoaren deien erregistroa aldatzeko baimena ematen die aplikazioei, sarrerako eta irteerako deiei buruzko datuak barne. Aplikazio gaiztoek deien erregistroa ezabatzeko edo aldatzeko erabil dezakete."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"Gorputzaren sentsoreak (adibidez, bihotz-erritmoaren monitoreak)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"Atzitu gorputzaren sentsoreak (adibidez, bihotz-maiztasunarenak)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Zure egoera fisikoa kontrolatzen duten sentsoreetako datuak (adibidez, bihotz-maiztasuna) atzitzea baimentzen die aplikazioei."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"irakurri egutegiko gertaerak eta isilpeko informazioa"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Tabletan gordetako egunkari-gertaera guztiak irakurtzeko baimena ematen die aplikazioei, lagunenak eta lankideenak barne. Horrela, aplikazioak egutegiko datuak parteka edo gorde ditzake, isilpekotasuna edo konfidentzialtasuna kontuan izan gabe."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Telefonoan alda ditzakezun gertaerak gehitzeko, kentzeko eta aldatzeko baimena ematen die aplikazioei, lagunenak eta lankideenak barne. Horrela, aplikazioak egutegi-jabeenak diruditen mezuak bidal ditzake, edo gertaerak alda ditzake jabeak jakin gabe."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"atzitu kokapen-hornitzaileen komando gehigarriak"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Kokapen-hornitzailearen agindu gehigarriak atzitzea baimentzen die aplikazioei. Horrela, agian aplikazioek GPSaren edo bestelako kokapenaren iturburuen funtzionamenduan eragina izan dezakete."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"kokapena zehatza (GPSan eta sarean oinarrituta)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"Atzitu kokapen zehatza (GPS sisteman eta sarean oinarrituta)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"GPSaren edo sarearen kokapenaren iturburuak (adibidez telefonia mugikorreko dorreak eta Wi-Fi sarea) erabilita kokapen zehatza lortzeko baimena ematen die aplikazioei. Kokapen-zerbitzu horiek gailuan aktibatuta eta erabilgarri egon behar dute, aplikazioak erabil ditzan. Aplikazioek baimen hori erabil dezakete gutxi gorabehera non zauden jakiteko, eta bateria gehiago behar izan daiteke."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"Gutxi gorabeherako kokapena (sarean oinarrituta)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"Atzitu gutxi gorabeherako kokapena (sarean oinarrituta)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Zure gutxi gorabeherako kokapena lortzeko baimena ematen dio aplikazioari. Kokapena kokapen-zerbitzuetatik lortzen da sarearen kokapenaren iturburuak (adibidez, telefonia mugikorreko dorreak eta Wi-Fi sarea) erabilita. Kokapen-zerbitzu horiek gailuan aktibatuta eta erabilgarri egon behar dute, aplikazioak erabil ditzan. Aplikazioek baimen hori erabil dezakete gutxi gorabehera non zauden jakiteko."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"aldatu audio-ezarpenak"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Audio-ezarpen orokorrak aldatzeko baimena ematen dio; besteak beste, bolumena eta irteerarako zer bozgorailu erabiltzen den."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"grabatu audioa"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Mikrofonoarekin audioa grabatzeko baimena ematen die aplikazioei. Baimen horrekin, aplikazioak audioa edonoiz graba dezake zure baimenik gabe."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"Komunikatu SIMarekin"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"Bidali aginduak SIM txartelera"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"SIM txartelera aginduak bidaltzeko aukera ematen die aplikazioei. Oso arriskutsua da."</string>
<string name="permlab_camera" msgid="3616391919559751192">"atera argazkiak eta grabatu bideoak"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Kamerarekin argazkiak ateratzeko eta bideoak grabatzeko baimena ematen die aplikazioei. Baimen horrekin, aplikazioak kamera edonoiz erabil dezake zure baimenik gabe."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Telefonoak ezagutzen dituen kontuen zerrenda lortzeko baimena ematen die aplikazioei. Instalatuta dituzun aplikazioek sortutako kontuak har daitezke barne."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"sare-konexioak ikustea"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Sare-konexioei buruzko informazioa ikusteko baimena ematen die aplikazioei; adibidez, zer sare dauden eta zeintzuk dauden konektatuta."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"sare osorako sarbidea"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"Izan sarerako sarbide osoa"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Sare-socketak sortzeko eta sare-protokolo pertsonalizatuak erabiltzeko baimena ematen die aplikazioei. Arakatzaileak eta beste aplikazio batzuek Internetera konektatzeko moduak eskaintzen dituzte, beraz, baimen hori ez da beharrezkoa datuak Internetera bidaltzeko."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"aldatu sarearen konektagarritasuna"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Sarearen konexioaren egoera aldatzea baimentzen die aplikazioei."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Tokiko Bluetooth telefonoa konfiguratzea eta urruneko gailuak detektatzea eta haiekin parekatzea baimentzen die aplikazioei."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"WiMAX sarera konektatzea eta deskonektatzea"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"WiMAX gaituta dagoen zehazteko eta konektatutako WiMAX sareei buruzko informazioa ikusteko baimena ematen die aplikazioei."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX egoera aldatzea"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"Aldatu WiMAX egoera"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Tableta WiMAX sareetara konektatzeko edo haietatik deskonektatzeko baimena ematen die aplikazioei."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Telebista WiMAX sareetara konektatzea edo haietatik deskonektatzea baimentzen die aplikazioei."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Telefonoa WiMAX sareetara konektatzeko edo haietatik deskonektatzeko baimena ematen die aplikazioei."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Ukipen-pantailaren kalibrazio-parametroak aldatzea baimentzen die aplikazioei. Aplikazio normalek ez lukete beharko."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"Atzitu DRM ziurtagiriak"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"DRM ziurtagiriak hornitzea eta erabiltzea baimentzen die aplikazioei. Aplikazio normalek ez lukete beharko."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Jaso Android Beam transferentzien egoera"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"Jaso Android Beam transferentzien egoera"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Uneko Android Beam transferentziei buruzko informazioa jasotzea baimentzen die aplikazioei"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"kendu DRM ziurtagiriak"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"DRM ziurtagiriak kentzea baimentzen die aplikazioei. Aplikazio normalek ez lukete beharko."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PIN kode okerra."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Desblokeatzeko, sakatu Menua eta, ondoren, 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Larrialdietarako zenbakia"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Ez dago zerbitzurik."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Ez dago zerbitzurik"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Pantaila blokeatuta dago."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Desblokeatzeko edo larrialdi-deia egiteko, sakatu Menua."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Desblokeatzeko, sakatu Menua."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Ikusi erabiltzailearen gida edo jarri bezeroarentzako arreta-zerbitzuarekin harremanetan."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM txartela blokeatuta dago."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"SIM txartela desblokeatzen…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Desblokeatzeko eredua oker marraztu duzu <xliff:g id="NUMBER_0">%d</xliff:g> aldiz. \n\nSaiatu berriro <xliff:g id="NUMBER_1">%d</xliff:g> segundo barru."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Pasahitza oker idatzi duzu <xliff:g id="NUMBER_0">%d</xliff:g> aldiz. \n\nSaiatu berriro <xliff:g id="NUMBER_1">%d</xliff:g> segundo barru."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"PINa oker idatzi duzu <xliff:g id="NUMBER_0">%d</xliff:g> aldiz. \n\nSaiatu berriro <xliff:g id="NUMBER_1">%d</xliff:g> segundo barru."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Desblokeatzeko eredua oker marraztu duzu <xliff:g id="NUMBER_0">%d</xliff:g> aldiz. Beste <xliff:g id="NUMBER_1">%d</xliff:g> aldiz idazten baduzu oker, tableta desblokeatzeko, Google-n saioa hasteko kredentzialak erabiltzea eskatuko dizugu.\n\n Saiatu berriro <xliff:g id="NUMBER_2">%d</xliff:g> segundo barru."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Desblokeatzeko eredua oker marraztu duzu <xliff:g id="NUMBER_0">%d</xliff:g> aldiz. Beste <xliff:g id="NUMBER_1">%d</xliff:g> aldiz oker marrazten baduzu, Google-n saioa hasteko kredentzialak erabiltzea eskatuko dizugu.\n\n Saiatu berriro <xliff:g id="NUMBER_2">%d</xliff:g> segundo barru."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Desblokeatzeko eredua oker marraztu duzu <xliff:g id="NUMBER_0">%d</xliff:g> aldiz. Beste <xliff:g id="NUMBER_1">%d</xliff:g> aldiz idazten baduzu oker, telefonoa desblokeatzeko, Google-n saioa hasteko kredentzialak erabiltzea eskatuko dizugu.\n\n Saiatu berriro <xliff:g id="NUMBER_2">%d</xliff:g> segundo barru."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Tableta <xliff:g id="NUMBER_0">%d</xliff:g> aldiz desblokeatzen saiatu zara. Beste <xliff:g id="NUMBER_1">%d</xliff:g> aldiz huts egiten baduzu, fabrikako ezarpen lehenetsiak berrezarriko dira eta datu guztiak galduko dituzu."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Telebista desblokeatzen saiatu zara, baina huts egin duzu <xliff:g id="NUMBER_0">%d</xliff:g> aldiz. Datozen <xliff:g id="NUMBER_1">%d</xliff:g> saiakeretan ere huts egiten baduzu, jatorrizko ezarpen lehenetsiak berrezarriko dira telebistan eta erabiltzaile-datu guztiak galduko dira."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Telefonoa <xliff:g id="NUMBER_0">%d</xliff:g> aldiz desblokeatzen saiatu zara. Beste <xliff:g id="NUMBER_1">%d</xliff:g> aldiz huts egiten baduzu, fabrikako ezarpen lehenetsiak berrezarriko dira eta datu guztiak galduko dituzu."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Desblokeatzeko eredua oker marraztu duzu <xliff:g id="NUMBER_0">%1$d</xliff:g> aldiz. \n\nSaiatu berriro <xliff:g id="NUMBER_1">%2$d</xliff:g> segundo barru."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Pasahitza oker idatzi duzu <xliff:g id="NUMBER_0">%1$d</xliff:g> aldiz. \n\nSaiatu berriro <xliff:g id="NUMBER_1">%2$d</xliff:g> segundo barru."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"PINa oker idatzi duzu <xliff:g id="NUMBER_0">%1$d</xliff:g> aldiz. \n\nSaiatu berriro <xliff:g id="NUMBER_1">%2$d</xliff:g> segundo barru."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Desblokeatzeko eredua oker marraztu duzu <xliff:g id="NUMBER_0">%1$d</xliff:g> aldiz. Beste <xliff:g id="NUMBER_1">%2$d</xliff:g> aldiz idazten baduzu oker, tableta desblokeatzeko, Google-n saioa hasteko kredentzialak erabiltzea eskatuko dizugu.\n\n Saiatu berriro <xliff:g id="NUMBER_2">%3$d</xliff:g> segundo barru."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Desblokeatzeko eredua oker marraztu duzu <xliff:g id="NUMBER_0">%1$d</xliff:g> aldiz. Beste <xliff:g id="NUMBER_1">%2$d</xliff:g> aldiz oker marrazten baduzu, Google-n saioa hasteko kredentzialak erabiltzea eskatuko dizugu.\n\n Saiatu berriro <xliff:g id="NUMBER_2">%3$d</xliff:g> segundo barru."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Desblokeatzeko eredua oker marraztu duzu <xliff:g id="NUMBER_0">%1$d</xliff:g> aldiz. Beste <xliff:g id="NUMBER_1">%2$d</xliff:g> aldiz idazten baduzu oker, telefonoa desblokeatzeko, Google-n saioa hasteko kredentzialak erabiltzea eskatuko dizugu.\n\n Saiatu berriro <xliff:g id="NUMBER_2">%3$d</xliff:g> segundo barru."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Tableta <xliff:g id="NUMBER_0">%1$d</xliff:g> aldiz desblokeatzen saiatu zara. Beste <xliff:g id="NUMBER_1">%2$d</xliff:g> aldiz huts egiten baduzu, fabrikako ezarpen lehenetsiak berrezarriko dira eta datu guztiak galduko dituzu."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Telebista desblokeatzen saiatu zara, baina huts egin duzu <xliff:g id="NUMBER_0">%1$d</xliff:g> aldiz. Datozen <xliff:g id="NUMBER_1">%2$d</xliff:g> saiakeretan ere huts egiten baduzu, jatorrizko ezarpen lehenetsiak berrezarriko dira telebistan eta erabiltzaile-datu guztiak galduko dira."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Telefonoa <xliff:g id="NUMBER_0">%1$d</xliff:g> aldiz desblokeatzen saiatu zara. Beste <xliff:g id="NUMBER_1">%2$d</xliff:g> aldiz huts egiten baduzu, fabrikako ezarpen lehenetsiak berrezarriko dira eta datu guztiak galduko dituzu."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Tableta <xliff:g id="NUMBER">%d</xliff:g> aldiz desblokeatzen saiatu zara. Orain, fabrikako ezarpen lehenetsiak berrezarriko dira."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Telebista desblokeatzen saiatu zara, baina huts egin duzu <xliff:g id="NUMBER">%d</xliff:g> aldiz. Jatorrizko ezarpen lehenetsiak berrezarriko dira telebistan."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Telefonoa <xliff:g id="NUMBER">%d</xliff:g> aldiz desblokeatzen saiatu zara. Orain, fabrikako ezarpen lehenetsiak berrezarriko dira."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"hitza"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"esteka"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"lerroa"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Fabrikako probak huts egin du"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"FACTORY_TEST ekintza /system/app karpetan instalatuta dauden paketeetarako onartzen da soilik."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Ez da FACTORY_TEST ekintza hornitzen duen paketerik aurkitu."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Formateatzen…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Ez dago sartuta"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Ez da bat datorren jarduerarik aurkitu."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Multimedia-irteera bideratzea"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"Bideratu multimedia-irteera"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Multimedia elementuak kanpoko gailuetara bideratzeko baimena ematen die aplikazioei."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Irakurri instalazio-saioak"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"Irakurri instalazio-saioak"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Instalazio-saioak irakurtzea baimentzen die aplikazioei. Horrela, pakete-instalazio aktiboei buruzko xehetasunak ikus ditzakete."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Eskatu paketeak instalatzeko"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"Eskatu instalazio-paketeak"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Paketeak instalatzeko eskatzea baimentzen die aplikazioei."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Ukitu birritan zooma kontrolatzeko"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Ezin izan da widgeta gehitu."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Erabiltzaile-izen edo pasahitz baliogabea."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Erabiltzaile-izena edo pasahitza ahaztu zaizu?\nZoaz "<b>"google.com/accounts/recovery"</b>" helbidera."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Kontua egiaztatzen…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"PINa oker idatzi duzu <xliff:g id="NUMBER_0">%d</xliff:g> aldiz. \n\nSaiatu berriro <xliff:g id="NUMBER_1">%d</xliff:g> segundo barru."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Pasahitza oker idatzi duzu <xliff:g id="NUMBER_0">%d</xliff:g> aldiz. \n\nSaiatu berriro <xliff:g id="NUMBER_1">%d</xliff:g> segundo barru."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Desblokeatzeko eredua oker marraztu duzu <xliff:g id="NUMBER_0">%d</xliff:g> aldiz. \n\nSaiatu berriro <xliff:g id="NUMBER_1">%d</xliff:g> segundo barru."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Tableta desblokeatzen saiatu zara <xliff:g id="NUMBER_0">%d</xliff:g> aldiz. Beste <xliff:g id="NUMBER_1">%d</xliff:g> saiakera okerren ondoren, jatorrizko ezarpen lehenetsiak berrezarriko dira tabletan eta erabiltzaile-datu guztiak galduko dira."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Telebista desblokeatzen saiatu zara, baina huts egin duzu <xliff:g id="NUMBER_0">%d</xliff:g> aldiz. Datozen <xliff:g id="NUMBER_1">%d</xliff:g> saiakeretan ere huts egiten baduzu, jatorrizko ezarpen lehenetsiak berrezarriko dira telebistan eta erabiltzaile-datu guztiak galduko dira."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Telefonoa desblokeatzen saiatu zara <xliff:g id="NUMBER_0">%d</xliff:g> aldiz. Beste <xliff:g id="NUMBER_1">%d</xliff:g> saiakera okerren ondoren, jatorrizko ezarpen lehenetsiak berrezarriko dira telefonoan eta erabiltzaile-datu guztiak galduko dira."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"PINa oker idatzi duzu <xliff:g id="NUMBER_0">%1$d</xliff:g> aldiz. \n\nSaiatu berriro <xliff:g id="NUMBER_1">%2$d</xliff:g> segundo barru."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Pasahitza oker idatzi duzu <xliff:g id="NUMBER_0">%1$d</xliff:g> aldiz. \n\nSaiatu berriro <xliff:g id="NUMBER_1">%2$d</xliff:g> segundo barru."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Desblokeatzeko eredua oker marraztu duzu <xliff:g id="NUMBER_0">%1$d</xliff:g> aldiz. \n\nSaiatu berriro <xliff:g id="NUMBER_1">%2$d</xliff:g> segundo barru."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Tableta desblokeatzen saiatu zara <xliff:g id="NUMBER_0">%1$d</xliff:g> aldiz. Beste <xliff:g id="NUMBER_1">%2$d</xliff:g> saiakera okerren ondoren, jatorrizko ezarpen lehenetsiak berrezarriko dira tabletan eta erabiltzaile-datu guztiak galduko dira."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Telebista desblokeatzen saiatu zara, baina huts egin duzu <xliff:g id="NUMBER_0">%1$d</xliff:g> aldiz. Datozen <xliff:g id="NUMBER_1">%2$d</xliff:g> saiakeretan ere huts egiten baduzu, jatorrizko ezarpen lehenetsiak berrezarriko dira telebistan eta erabiltzaile-datu guztiak galduko dira."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Telefonoa desblokeatzen saiatu zara <xliff:g id="NUMBER_0">%1$d</xliff:g> aldiz. Beste <xliff:g id="NUMBER_1">%2$d</xliff:g> saiakera okerren ondoren, jatorrizko ezarpen lehenetsiak berrezarriko dira telefonoan eta erabiltzaile-datu guztiak galduko dira."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Tableta desblokeatzen saiatu zara <xliff:g id="NUMBER">%d</xliff:g> aldiz. Jatorrizko ezarpen lehenetsiak berrezarriko dira."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Telebista desblokeatzen saiatu zara, baina huts egin duzu <xliff:g id="NUMBER">%d</xliff:g> aldiz. Jatorrizko ezarpen lehenetsiak berrezarriko dira telebistan."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Telefonoa desblokeatzen saiatu zara <xliff:g id="NUMBER">%d</xliff:g> aldiz. Jatorrizko ezarpen lehenetsiak berrezarriko dira."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Desblokeatzeko eredua oker marraztu duzu <xliff:g id="NUMBER_0">%d</xliff:g> aldiz. Beste <xliff:g id="NUMBER_1">%d</xliff:g> aldiz oker marrazten baduzu, tableta posta-kontu baten bidez desblokeatzeko eskatuko dizugu.\n\n Saiatu berriro <xliff:g id="NUMBER_2">%d</xliff:g> segundo barru."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Desblokeatzeko eredua oker marraztu duzu <xliff:g id="NUMBER_0">%d</xliff:g> aldiz. Beste <xliff:g id="NUMBER_1">%d</xliff:g> aldiz oker marrazten baduzu, telebista posta-kontu baten bidez desblokeatzeko eskatuko dizugu.\n\n Saiatu berriro <xliff:g id="NUMBER_2">%d</xliff:g> segundo barru."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Desblokeatzeko eredua oker marraztu duzu <xliff:g id="NUMBER_0">%d</xliff:g> aldiz. Beste <xliff:g id="NUMBER_1">%d</xliff:g> aldiz oker marrazten baduzu, telefonoa posta-kontu baten bidez desblokeatzeko eskatuko dizugu.\n\n Saiatu berriro <xliff:g id="NUMBER_2">%d</xliff:g> segundo barru."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Desblokeatzeko eredua oker marraztu duzu <xliff:g id="NUMBER_0">%1$d</xliff:g> aldiz. Beste <xliff:g id="NUMBER_1">%2$d</xliff:g> aldiz oker marrazten baduzu, tableta posta-kontu baten bidez desblokeatzeko eskatuko dizugu.\n\n Saiatu berriro <xliff:g id="NUMBER_2">%3$d</xliff:g> segundo barru."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Desblokeatzeko eredua oker marraztu duzu <xliff:g id="NUMBER_0">%1$d</xliff:g> aldiz. Beste <xliff:g id="NUMBER_1">%2$d</xliff:g> aldiz oker marrazten baduzu, telebista posta-kontu baten bidez desblokeatzeko eskatuko dizugu.\n\n Saiatu berriro <xliff:g id="NUMBER_2">%3$d</xliff:g> segundo barru."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Desblokeatzeko eredua oker marraztu duzu <xliff:g id="NUMBER_0">%1$d</xliff:g> aldiz. Beste <xliff:g id="NUMBER_1">%2$d</xliff:g> aldiz oker marrazten baduzu, telefonoa posta-kontu baten bidez desblokeatzeko eskatuko dizugu.\n\n Saiatu berriro <xliff:g id="NUMBER_2">%3$d</xliff:g> segundo barru."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Kendu"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Bolumena gomendatutako mailatik gora igo nahi duzu?\n\nMusika bolumen handian eta denbora luzez entzuteak entzumena kalte diezazuke."</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">Ordubetez</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> arte"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> arte (hurrengo alarma)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Zuk desaktibatu arte"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"\"Ez molestatu\" desaktibatzen duzun arte"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-fa-watch/strings.xml b/core/res/res/values-fa-watch/strings.xml
index 5fe8359..1ddb386 100644
--- a/core/res/res/values-fa-watch/strings.xml
+++ b/core/res/res/values-fa-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"برنامه <xliff:g id="NUMBER_0">%1$d</xliff:g> از <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"حسگرها"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"دسترسی به مخاطبین شما"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"دسترسی به مکان این ساعت"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"دسترسی به تقویم شما"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"ارسال و مشاهده پیامکها"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"دسترسی به عکس، رسانه و فایلهای روی ساعتتان"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"ضبط صدا"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"عکس گرفتن و فیلمبرداری"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"برقراری و مدیریت تماسهای تلفنی"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"دسترسی به دادههای حسگر در رابطه با علائم حیاتی شما"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"نوار وضعیت باشد"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"دسترسی به حسگرهای بدن (مانند مانیتورهای ضربان قلب)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"دسترسی به مکان دقیق (مبتنی بر GPS و شبکه)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"دسترسی به مکان تقریبی (مبتنی بر شبکه)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"ارسال فرمانها به سیم کارت"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"داشتن دسترسی کامل به شبکه"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"مدیریت نمایه و مالکان دستگاه"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"تغییر وضعیت WiMAX"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"دریافت وضعیت انتقال پرتوی Android"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"تعیین مسیر خروجی رسانه"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"خواندن جلسات نصب"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"درخواست نصب بستهبندی"</string>
</resources>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 5032c25..2a41913 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"اطلاعات شخصی مانند شماره کارت اعتباری و گذرواژهها را لحاظ میکند."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"غیرفعال کردن یا تغییر نوار وضعیت"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"به برنامه اجازه میدهد تا نوار وضعیت را غیرفعال کند یا نمادهای سیستم را اضافه یا حذف کند."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"نوار وضعیت"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"نوار وضعیت باشد"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"به برنامه اجازه میدهد که تبدیل به نوار وضعیت شود."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"گسترش دادن/جمع کردن نوار وضعیت"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"به برنامه اجازه میدهد تا نوار ابزار را جمع کند یا باز کند."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"به برنامه اجازه میدهد پیامهای WAP را دریافت و پردازش کند. این مجوز میتواند پیامهای ارسالی به شما را بدون نمایش آنها به شما حذف یا کنترل کند."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"بازیابی برنامههای در حال اجرا"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"به برنامه امکان میدهد اطلاعات مربوط به کارهای در حال اجرای اخیر و کنونی را بازیابی کند. این ممکن است به برنامه امکان دهد به اطلاعات مربوط به برنامههایی که در دستگاه استفاده میشوند دست یابد."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"مدیریت مالکان نمایه و دستگاه"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"مدیریت نمایه و مالکان دستگاه"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"به برنامهها امکان میدهد، مالکان نمایه و مالک دستگاه را تنظیم کنند."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"تنظیم مجدد ترتیب برنامههای در حال اجرا"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"به برنامه اجازه میدهد تا کارها را به پیشزمینه و پسزمینه منتقل کند. برنامه ممکن است بدون دخالت شما این کار را انجام دهد."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"به برنامه اجازه میدهد گزارشات تماس رایانهٔ لوحی شما، از جمله دادههایی درمورد تماسهای ورودی و خروجی را تغییر دهد. برنامههای مخرب ممکن است از این ویژگی برای پاک کردن یا تغییر گزارش تماس شما استفاده کنند."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"به برنامه اجازه میدهد گزارشات تماس تلویزیون شما، از جمله دادههایی درمورد تماسهای ورودی و خروجی را تغییر دهد. برنامههای مخرب شاید از این ویژگی برای پاک کردن یا تغییر گزارش تماس شما استفاده کنند."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"به برنامه اجازه میدهد گزارشات تماس تلفنی شما، از جمله دادههایی درمورد تماسهای ورودی و خروجی را تغییر دهد. برنامههای مخرب ممکن است از این ویژگی برای پاک کردن یا تغییر گزارش تماس شما استفاده کنند."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"حسگرهای بدن (مانند پایشگرهای ضربان قلب)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"دسترسی به حسگرهای بدن (مانند پایشگرهای ضربان قلب)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"به برنامه امکان میدهد به اطلاعات حسگرهایی که بر شرایط فیزیکی شما مانند ضربان قلبتان، نظارت دارند، دسترسی داشته باشد."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"خواندن رویدادهای تقویم به همراه اطلاعات محرمانه"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"به برنامه امکان میدهد همه رویدادهای تقویم ذخیره شده در رایانهٔ لوحی شما را بخواند، از جمله رویدادهای دوستان یا همکاران. این ممکن است به برنامه امکان دهد دادههای تقویم شما را صرفنظر از محرمانه یا حساس بودن آنها به اشتراک گذاشته یا ذخیره کند."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"به برنامه اجازه میدهد رویدادهایی را که میتوانید در تلفن خود اصلاح نمایید، از جمله رویدادهای دوستان یا همکاران خود را، اضافه یا حذف کرده یا تغییر دهد. این ویژگی ممکن است به برنامه اجازه دهد پیامهایی را که به نظر میرسد از مالکین تقویم رسیده است ارسال نموده یا رویدادها را بدون اطلاع مالک اصلاح کنند."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"دسترسی به فرمانهای بیشتر ارائه دهنده مکان"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"به برنامه اجازه میدهد به دستورات ارائهدهنده مکان تکمیلی دسترسی داشته باشد. این کار ممکن است به برنامه امکان دهد با کارکرد GPS یا منابع دیگر مکان تداخل داشته باشد."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"موقعیت مکانی دقیق (مبتنی بر GPS و شبکه)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"دسترسی به مکان دقیق (مبتنی بر GPS و شبکه)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"به برنامه اجازه میدهد که موقعیت مکانی دقیق شما را با استفاده از سیستم موقعیتیاب جهانی (GPS) یا منابع موقعیت مکانی شبکهای مانند برجهای سلولی یا Wi-Fi دریافت کند. این سرویسهای موقعیت مکانی باید در دستگاه شما برای برنامهای که از آنها استفاده میکند، فعال و در دسترس باشد. برنامهها ممکن است از آن برای تعیین جایی که هستید، استفاده کنند و ممکن است نیروی باتری بیشتری مصرف کنند."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"موقعیت مکانی تقریبی (مبتنی بر شبکه)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"دسترسی به مکان تقریبی (مبتنی بر شبکه)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"به برنامه اجازه میدهد که موقعیت مکانی تقریبی شما را بدست آورد. این موقعیت مکانی از سرویسهای موقعیت مکانی که از منابع موقعیت مکانی شبکهای مانند برجهای سلولی و Wi-Fi استفاده میکنند، بدست میآید. این سرویسهای موقعیت مکانی باید در دستگاه شما برای برنامهای که از آنها استفاده میکند، فعال و در دسترس باشد. برنامهها ممکن است از آن برای تعیین تقریبی جایی که هستید، استفاده کنند."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"تغییر تنظیمات صوتی"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"به برنامه امکان میدهد تنظیمات صوتی کلی مانند میزان صدا و بلندگوی مورد استفاده برای پخش صدا را اصلاح کند."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"ضبط صدا"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"به برنامه اجازه میدهد صدا را با میکروفن ضبط کند. این مجوز به برنامه اجازه میدهد صدا را در هر زمان که بخواهید بدون تأیید شما ضبط کند."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"ارتباطات سیم کارت"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"ارسال فرمان به سیم کارت"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"به برنامه اجازه ارسال دستورات به سیم کارت را میدهد. این بسیار خطرناک است."</string>
<string name="permlab_camera" msgid="3616391919559751192">"عکسبرداری و فیلمبرداری"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"به برنامه اجازه میدهد با دوربین به عکسبرداری و فیلمبرداری بپردازد. این مجوز به برنامه اجازه میدهد از دوربین در هر زمانی بدون تأیید شما استفاده کند."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"به برنامه اجازه میدهد به لیست حسابهای شناخته شده توسط تلفن دسترسی پیدا کند. این ممکن است حسابهای ایجاد شده توسط برنامههایی را که نصب کردهاید، شامل شود."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"مشاهدهٔ اتصالات شبکه"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"به برنامه امکان میدهد اطلاعات مربوط به اتصالات شبکه مانند شبکههای موجود و متصل را مشاهده کند."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"دسترسی کامل به شبکه"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"دسترسی کامل به شبکه"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"به برنامه امکان میدهد سوکتهای شبکه را ایجاد کند و از پروتکلهای شبکه سفارشی استفاده نماید. مرورگر و سایر برنامهها روشی را برای ارسال دادهها به اینترنت ارائه میکنند بنابراین این مجوز برای ارسال داده به اینترنت ضروری نیست."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"تغییر قابلیت اتصال شبکه"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"به برنامه اجازه میدهد تا وضعیت اتصال شبکه را تغییر دهد."</string>
@@ -397,16 +397,16 @@
<string name="permdesc_changeWifiMulticastState" product="tv" msgid="9031975661145014160">"به برنامه اجازه میدهد تا بستههایی را دریافت کند که در شبکه Wi-Fi با استفاده از آدرسهای چندبخشی نه تنها به تلویزیون شما، بلکه به همه دستگاهها ارسال میشود. این حالت نسبت به حالت غیر چندبخشی از انرژی بیشتری استفاده میکند."</string>
<string name="permdesc_changeWifiMulticastState" product="default" msgid="6851949706025349926">"به برنامه اجازه میدهد به دریافت بستههای ارسالی به همه دستگاههای موجود در شبکه Wi-Fi با استفاده از آدرسهای پخش چندگانه و نه فقط به تلفن شما بپردازند. این از توان مصرف بیشتری نسبت به حالت پخش غیرچندگانه استفاده میکند."</string>
<string name="permlab_bluetoothAdmin" msgid="6006967373935926659">"دسترسی به تنظیمات بلوتوث"</string>
- <string name="permdesc_bluetoothAdmin" product="tablet" msgid="6921177471748882137">"به برنامه اجازه میدهد تا رایانهٔ لوحی بلوتوث محلی را پیکربندی کرده، دستگاههای راه دور را شناسایی کرده و با آنها جفت شود."</string>
+ <string name="permdesc_bluetoothAdmin" product="tablet" msgid="6921177471748882137">"به برنامه اجازه میدهد تا رایانهٔ لوحی بلوتوث محلی را پیکربندی کرده، دستگاههای راه دور را شناسایی کرده و با آنها مرتبطسازی شود."</string>
<string name="permdesc_bluetoothAdmin" product="tv" msgid="3373125682645601429">"به برنامه اجازه میدهد تا تلویزیون بلوتوث محلی را پیکربندی کند و دستگاههای از راه دور را شناسایی کند و با آنها مرتبط شود."</string>
- <string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"به برنامه اجازه میدهد تا تلفن بلوتوث محلی را پیکربندی کند و دستگاههای راه دور را پیدا کند و با آنها جفت شود."</string>
+ <string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"به برنامه اجازه میدهد تا تلفن بلوتوث محلی را پیکربندی کند و دستگاههای راه دور را پیدا کند و با آنها مرتبطسازی شود."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"اتصال و قطع اتصال از WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"به برنامه امکان میدهد فعال بودن وایمکس و اطلاعات مربوط به هر یک از شبکههای وایمکس متصل را مشخص کند."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"تغییر وضعیت WiMAX"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"تغییر وضعیت WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"به برنامه امکان میدهد رایانهٔ لوحی را به شبکههای وایمکس متصل کرده یا اتصال آن را از این شبکهها قطع کند."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"به برنامه اجازه میدهد تا تلویزیون را به شبکههای WiMAX وصل یا ارتباط آن را با این شبکهها قطع کند."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"به برنامه امکان میدهد تا تلفن را به شبکههای وایمکس متصل کرده یا اتصال آنرا از این شبکهها قطع کند."</string>
- <string name="permlab_bluetooth" msgid="6127769336339276828">"جفت کردن با دستگاههای بلوتوث"</string>
+ <string name="permlab_bluetooth" msgid="6127769336339276828">"مرتبطسازی با دستگاههای بلوتوث"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"به برنامه اجازه میدهد تا پیکربندی بلوتوث در رایانهٔ لوحی را مشاهده کند و اتصال با دستگاههای مرتبط را برقرار کرده و بپذیرد."</string>
<string name="permdesc_bluetooth" product="tv" msgid="3974124940101104206">"به برنامه اجازه میدهد تا پیکربندی بلوتوث را در تلویزیون مشاهده کند و اتصالات را با دستگاههای مرتبطشده ایجاد کند و بپذیرد."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"به برنامه اجازه میدهد تا پیکربندی بلوتوث در تلفن را مشاهده کند، و اتصالات دستگاههای مرتبط را برقرار کرده و بپذیرد."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"به برنامه امکان میدهد پارامترهای کالیبراسیون صفحه لمسی را تغییر دهد. هرگز نباید برای برنامههای عادی مورد نیاز باشد."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"دسترسی به گواهیهای DRM"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"به یک برنامه کاربردی اجازه ارائه مجوز و استفاده از گواهیهای DRM را میدهد. هرگز برای برنامههای عادی مورد نیاز نیست."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"دریافت وضعیت انتقال پرتوی Android"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"دریافت وضعیت انتقال پرتوی Android"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"به برنامه امکان میدهد تا اطلاعاتی درباره انتقالهای کنونی پرتوی Android به دست آورد"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"حذف گواهیهای DRM"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"به برنامه امکان میدهد گواهیهای DRM را حذف کند. نباید برای برنامههای عادی هیچوقت لازم باشد."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"پین کد اشتباه است."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"برای بازگشایی قفل، منو را فشار دهید و سپس 0 را فشار دهید."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"شماره اضطراری"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"سرویسی وجود ندارد."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"بدون سرویس"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"صفحه قفل شد."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"برای بازگشایی قفل یا انجام تماس اضطراری روی منو فشار دهید."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"برای بازگشایی قفل روی منو فشار دهید."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"لطفاً به راهنمای کاربر مراجعه کرده یا با مرکز پشتیبانی از مشتریان تماس بگیرید."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"سیم کارت قفل شد."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"بازگشایی قفل سیم کارت..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"الگوی بازگشایی قفل خود را <xliff:g id="NUMBER_0">%d</xliff:g> بار اشتباه کشیدهاید. \n\nپس از <xliff:g id="NUMBER_1">%d</xliff:g> ثانیه دوباره امتحان کنید."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"گذرواژهٔ خود را <xliff:g id="NUMBER_0">%d</xliff:g> بار اشتباه تایپ کردهاید. \n\nپس از <xliff:g id="NUMBER_1">%d</xliff:g> ثانیه دوباره امتحان کنید."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"پین را<xliff:g id="NUMBER_0">%d</xliff:g> بار اشتباه تایپ کردهاید. \n\nپس از <xliff:g id="NUMBER_1">%d</xliff:g> ثانیه دوباره امتحان کنید."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"شما الگوی بازگشایی قفل خود را <xliff:g id="NUMBER_0">%d</xliff:g> بار اشتباه کشیدهاید. بعد از <xliff:g id="NUMBER_1">%d</xliff:g> تلاش ناموفق، از شما خواسته میشود که برای بازگشایی قفل رایانهٔ لوحی خود به Google وارد شوید.\n\n لطفاً پس از <xliff:g id="NUMBER_2">%d</xliff:g> ثانیه دوباره امتحان کنید."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"الگوی بازگشایی را <xliff:g id="NUMBER_0">%d</xliff:g> دفعه به صورت نادرست رسم کردهاید. پس از <xliff:g id="NUMBER_1">%d</xliff:g> تلاش ناموفق دیگر، از شما خواسته خواهد شد تا با استفاده از ورود به سیستم Google، قفل تلویزیونتان را باز کنید.\n\n پس از <xliff:g id="NUMBER_2">%d</xliff:g> ثانیه، دوباره امتحان کنید."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"الگوی قفلگشایی را <xliff:g id="NUMBER_0">%d</xliff:g> بار اشتباه کشیدهاید. پس از <xliff:g id="NUMBER_1">%d</xliff:g> تلاش ناموفق دیگر از شما خواسته میشود که برای بازگشایی قفل گوشی خود به برنامه Google وارد شوید.\n\n پس از <xliff:g id="NUMBER_2">%d</xliff:g> ثانیه دوباره امتحان کنید."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"شما به اشتباه <xliff:g id="NUMBER_0">%d</xliff:g> بار اقدام به باز کردن قفل رایانهٔ لوحی کردهاید. پس از <xliff:g id="NUMBER_1">%d</xliff:g> تلاش ناموفق دیگر، رایانهٔ لوحی به پیشفرض کارخانه بازنشانی میشود و تمام دادههای کاربر از دست خواهد رفت."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"<xliff:g id="NUMBER_0">%d</xliff:g> دفعه به صورت نادرست سعی کردهاید قفل تلویزیون را باز کنید. پس از <xliff:g id="NUMBER_1">%d</xliff:g> تلاش ناموفق دیگر، تلویزیون به تنظیمات پیشفرض کارخانه بازنشانی خواهد شد و اطلاعات کاربر از دست خواهد رفت."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"شما به اشتباه <xliff:g id="NUMBER_0">%d</xliff:g> بار اقدام به باز کردن قفل تلفن کردهاید. پس از<xliff:g id="NUMBER_1">%d</xliff:g> تلاش ناموفق دیگر، تلفن به پیشفرض کارخانه بازنشانی میشود و تمام دادههای کاربر از دست خواهد رفت."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"الگوی بازگشایی قفل خود را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیدهاید. \n\nپس از <xliff:g id="NUMBER_1">%2$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"گذرواژهٔ خود را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه تایپ کردهاید. \n\nپس از <xliff:g id="NUMBER_1">%2$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"پین را<xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه تایپ کردهاید. \n\nپس از <xliff:g id="NUMBER_1">%2$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"شما الگوی بازگشایی قفل خود را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیدهاید. بعد از <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق، از شما خواسته میشود که برای بازگشایی قفل رایانهٔ لوحی خود به Google وارد شوید.\n\n لطفاً پس از <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"الگوی بازگشایی را <xliff:g id="NUMBER_0">%1$d</xliff:g> دفعه به صورت نادرست رسم کردهاید. پس از <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق دیگر، از شما خواسته خواهد شد تا با استفاده از ورود به سیستم Google، قفل تلویزیونتان را باز کنید.\n\n پس از <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانیه، دوباره امتحان کنید."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"الگوی قفلگشایی را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیدهاید. پس از <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق دیگر از شما خواسته میشود که برای بازگشایی قفل گوشی خود به برنامه Google وارد شوید.\n\n پس از <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"شما به اشتباه <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اقدام به باز کردن قفل رایانهٔ لوحی کردهاید. پس از <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق دیگر، رایانهٔ لوحی به پیشفرض کارخانه بازنشانی میشود و تمام دادههای کاربر از دست خواهد رفت."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"<xliff:g id="NUMBER_0">%1$d</xliff:g> دفعه به صورت نادرست سعی کردهاید قفل تلویزیون را باز کنید. پس از <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق دیگر، تلویزیون به تنظیمات پیشفرض کارخانه بازنشانی خواهد شد و اطلاعات کاربر از دست خواهد رفت."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"شما به اشتباه <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اقدام به باز کردن قفل تلفن کردهاید. پس از<xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق دیگر، تلفن به پیشفرض کارخانه بازنشانی میشود و تمام دادههای کاربر از دست خواهد رفت."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"شما به اشتباه اقدام به باز کردن قفل <xliff:g id="NUMBER">%d</xliff:g> رایانهٔ لوحی کردهاید. رایانهٔ لوحی در حال حاضر به پیشفرض کارخانه بازنشانی میشود."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"<xliff:g id="NUMBER">%d</xliff:g> دفعه به صورت نادرست سعی کردهاید قفل تلویزیون را باز کنید. اکنون تلویزیون به تنظیمات پیشفرض کارخانه بازنشانی خواهد شد."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"شما به اشتباه <xliff:g id="NUMBER">%d</xliff:g> بار اقدام به باز کردن قفل تلفن کردهاید. این تلفن در حال حاضر به پیشفرض کارخانه بازنشانی میشود."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"کلمه"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"پیوند"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"خط"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"تست کارخانه انجام نشد"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"عملکرد FACTORY_TEST تنها برای بستههای نصب شده در /system/app پشتیبانی میشود."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"بستهای یافت نشد که عملکرد FACTORY_TEST را ارائه کند."</string>
@@ -783,7 +781,7 @@
<string name="permlab_setAlarm" msgid="1379294556362091814">"تنظیم یک هشدار"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"به برنامه اجازه میدهد تا هشداری را در برنامه ساعت زنگدار نصب شده تنظیم کند. برخی از برنامههای ساعت زنگدار نمیتوانند این ویژگی را اعمال کنند."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"افزودن پست صوتی"</string>
- <string name="permdesc_addVoicemail" msgid="6604508651428252437">"به برنامه اجازه میدهد تا پیامها را به صندوق دریافت پست صوتی شما اضافه کند."</string>
+ <string name="permdesc_addVoicemail" msgid="6604508651428252437">"به برنامه اجازه میدهد تا پیامها را به صندوق ورودی پست صوتی شما اضافه کند."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"تغییر مجوزهای مکان جغرافیایی مرورگر"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"به برنامه اجازه میدهد تا مجوزهای جغرافیایی مرورگر را تغییر دهد. برنامههای مخرب میتوانند از آن استفاده کنند تا اطلاعات موقعیت مکانی را به سایتهای وب کتابخانه بفرستند."</string>
<string name="save_password_message" msgid="767344687139195790">"میخواهید مرورگر این گذرواژه را به خاطر داشته باشد؟"</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"درحال قالببندی…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"جاگذاری نشده"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"فعالیتی مطابق با این مورد یافت نشد."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"تعیین مسیر خروجی رسانه"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"تعیین مسیر خروجی رسانه"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"به یک برنامه اجازه میدهد خروجی رسانه را به دستگاههای خارجی دیگر تعیین مسیر کند."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"خواندن جلسات نصب"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"خواندن جلسات نصب"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"به برنامه اجازه میدهد جلسات نصب را بخواند. این کار به برنامه اجازه میدهد جزئیات نصبهای بسته فعال را ببیند."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"درخواست نصب بستهبندی"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"درخواست نصب بسته"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"به برنامه اجازه میدهد درخواست نصب بستهبندی کند."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"دوبار لمس کنید تا بزرگنمایی کنترل شود"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"افزودن ابزارک انجام نشد."</string>
@@ -1143,7 +1141,7 @@
<string name="gpsNotifTitle" msgid="5446858717157416839">"درخواست موقعیت مکانی"</string>
<string name="gpsNotifMessage" msgid="1374718023224000702">"درخواست شده توسط <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SERVICE">%2$s</xliff:g>)"</string>
<string name="gpsVerifYes" msgid="2346566072867213563">"بله"</string>
- <string name="gpsVerifNo" msgid="1146564937346454865">"خیر"</string>
+ <string name="gpsVerifNo" msgid="1146564937346454865">"نه"</string>
<string name="sync_too_many_deletes" msgid="5296321850662746890">"از حد مجاز حذف فراتر رفت"</string>
<string name="sync_too_many_deletes_desc" msgid="496551671008694245">"<xliff:g id="NUMBER_OF_DELETED_ITEMS">%1$d</xliff:g> مورد حذف شده برای <xliff:g id="TYPE_OF_SYNC">%2$s</xliff:g>، حساب <xliff:g id="ACCOUNT_NAME">%3$s</xliff:g> وجود دارد. میخواهید چه کاری انجام دهید؟"</string>
<string name="sync_really_delete" msgid="2572600103122596243">"حذف موارد"</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"نام کاربری یا گذرواژه نامعتبر."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"نام کاربری یا گذرواژه خود را فراموش کردید؟\nاز "<b>"google.com/accounts/recovery"</b>" بازدید کنید."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"درحال بررسی حساب..."</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"پین خود را <xliff:g id="NUMBER_0">%d</xliff:g> بار اشتباه تایپ کردید. \n\nپس از <xliff:g id="NUMBER_1">%d</xliff:g> ثانیه دوباره امتحان کنید."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"گذرواژه خود را <xliff:g id="NUMBER_0">%d</xliff:g> بار اشتباه تایپ کردید. \n\nپس از <xliff:g id="NUMBER_1">%d</xliff:g> ثانیه دوباره امتحان کنید."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"الگوی بازگشایی قفل خود را <xliff:g id="NUMBER_0">%d</xliff:g> بار اشتباه کشیدید. \n\nلطفاً پس از <xliff:g id="NUMBER_1">%d</xliff:g> ثانیه دوباره امتحان کنید."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"شما به اشتباه <xliff:g id="NUMBER_0">%d</xliff:g> بار اقدام به باز کردن قفل رایانه لوحی کردهاید. پس از <xliff:g id="NUMBER_1">%d</xliff:g> تلاش ناموفق دیگر، رایانهٔ لوحی به پیشفرض کارخانه بازنشانی میشود و تمام دادههای کاربر از دست خواهد رفت."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"<xliff:g id="NUMBER_0">%d</xliff:g> دفعه به صورت نادرست سعی کردهاید قفل تلویزیون را باز کنید. پس از <xliff:g id="NUMBER_1">%d</xliff:g> تلاش ناموفق دیگر، تلویزیون به تنظیمات پیشفرض کارخانه بازنشانی خواهد شد و اطلاعات کاربر از دست خواهد رفت."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"شما به اشتباه <xliff:g id="NUMBER_0">%d</xliff:g> بار اقدام به باز کردن قفل تلفن کردهاید. پس از <xliff:g id="NUMBER_1">%d</xliff:g> تلاش ناموفق دیگر، تلفن به پیشفرض کارخانه بازنشانی میشود و تمام دادههای کاربر از دست خواهد رفت."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"پین خود را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه تایپ کردید. \n\nپس از <xliff:g id="NUMBER_1">%2$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"گذرواژه خود را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه تایپ کردید. \n\nپس از <xliff:g id="NUMBER_1">%2$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"الگوی بازگشایی قفل خود را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیدید. \n\nلطفاً پس از <xliff:g id="NUMBER_1">%2$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"شما به اشتباه <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اقدام به باز کردن قفل رایانه لوحی کردهاید. پس از <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق دیگر، رایانهٔ لوحی به پیشفرض کارخانه بازنشانی میشود و تمام دادههای کاربر از دست خواهد رفت."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"<xliff:g id="NUMBER_0">%1$d</xliff:g> دفعه به صورت نادرست سعی کردهاید قفل تلویزیون را باز کنید. پس از <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق دیگر، تلویزیون به تنظیمات پیشفرض کارخانه بازنشانی خواهد شد و اطلاعات کاربر از دست خواهد رفت."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"شما به اشتباه <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اقدام به باز کردن قفل تلفن کردهاید. پس از <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق دیگر، تلفن به پیشفرض کارخانه بازنشانی میشود و تمام دادههای کاربر از دست خواهد رفت."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"شما به اشتباه <xliff:g id="NUMBER">%d</xliff:g> بار اقدام به باز کردن قفل رایانه لوحی کردهاید. رایانه لوحی اکنون به پیشفرض کارخانه بازنشانی میشود."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"<xliff:g id="NUMBER">%d</xliff:g> دفعه به صورت نادرست سعی کردهاید قفل تلویزیون را باز کنید. اکنون تلویزیون به تنظیمات پیشفرض کارخانه بازنشانی خواهد شد."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"شما به اشتباه <xliff:g id="NUMBER">%d</xliff:g> بار اقدام به باز کردن قفل تلفن کردهاید. این تلفن اکنون به پیشفرض کارخانه بازنشانی میشود."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"شما الگوی بازگشایی قفل خود را <xliff:g id="NUMBER_0">%d</xliff:g> بار اشتباه کشیدهاید. بعد از <xliff:g id="NUMBER_1">%d</xliff:g> تلاش ناموفق، از شما خواسته میشود که با استفاده از یک حساب ایمیل قفل رایانه لوحی خود را باز کنید.\n\n لطفاً پس از <xliff:g id="NUMBER_2">%d</xliff:g> ثانیه دوباره امتحان کنید."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"الگوی بازگشاییتان را <xliff:g id="NUMBER_0">%d</xliff:g> دفعه به صورت نادرست رسم کردهاید. <xliff:g id="NUMBER_1">%d</xliff:g> پس از \n تلاش ناموفق دیگر، از شما خواسته میشود تا با استفاده از یک حساب ایمیل، قفل تلویزیونتان را باز کنید.\n پس از <xliff:g id="NUMBER_2">%d</xliff:g> ثانیه دوباره امتحان کنید."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"شما الگوی بازگشایی قفل خود را <xliff:g id="NUMBER_0">%d</xliff:g> بار اشتباه کشیدهاید. پس از <xliff:g id="NUMBER_1">%d</xliff:g> تلاش ناموفق، از شما خواسته میشود که با استفاده از یک حساب ایمیل قفل تلفن خود را باز کنید.\n\n لطفاً پس از <xliff:g id="NUMBER_2">%d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"شما الگوی بازگشایی قفل خود را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیدهاید. بعد از <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق، از شما خواسته میشود که با استفاده از یک حساب ایمیل قفل رایانه لوحی خود را باز کنید.\n\n لطفاً پس از <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"الگوی بازگشاییتان را <xliff:g id="NUMBER_0">%1$d</xliff:g> دفعه به صورت نادرست رسم کردهاید. <xliff:g id="NUMBER_1">%2$d</xliff:g> پس از \n تلاش ناموفق دیگر، از شما خواسته میشود تا با استفاده از یک حساب ایمیل، قفل تلویزیونتان را باز کنید.\n پس از <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"شما الگوی بازگشایی قفل خود را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیدهاید. پس از <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق، از شما خواسته میشود که با استفاده از یک حساب ایمیل قفل تلفن خود را باز کنید.\n\n لطفاً پس از <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"حذف"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"میزان صدا را به بالاتر از حد توصیه شده افزایش میدهید؟\n\nگوش دادن به صداهای بلند برای مدت طولانی میتواند به شنواییتان آسیب وارد کند."</string>
@@ -1470,6 +1468,7 @@
<item quantity="other">برای %d ساعت</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"تا <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"تا <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (هشدار بعدی)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"تا وقتی آن را خاموش کنید"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"تا زمانی که «مزاحم نشوید» را خاموش کنید"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-fi-watch/strings.xml b/core/res/res/values-fi-watch/strings.xml
index 662e0ab..3c7b925 100644
--- a/core/res/res/values-fi-watch/strings.xml
+++ b/core/res/res/values-fi-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Sovellus <xliff:g id="NUMBER_0">%1$d</xliff:g>/<xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Anturit"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"käyttää yhteystietoja"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"käyttää tämän kellon sijaintia"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"käyttää kalenteria"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"lähettää ja tarkastella tekstiviestejä"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"käyttää kellosi kuvia, mediaa ja tiedostoja"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"tallentaa ääntä"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"ottaa kuvia ja videoita"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"soittaa ja hallinnoida puheluita"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"käyttää anturitietoja elintoiminnoistasi"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"sijaita tilapalkissa"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"käyttää kehon antureita (kuten sykemittareita)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"käyttää tarkkaa sijaintia (GPS- ja verkkopohjainen)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"käyttää likimääräistä sijaintia (verkkopohjainen)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"lähettää komentoja SIM-kortille"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"saada täydet verkon käyttöoikeudet"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"hallinnoida laitteen ja profiilien omistajia"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"vaihtaa WiMAX-verkon tilaa"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"vastaanottaa Android Beam -siirron tilatietoja"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"reitittää mediaa"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"lukea asennusistuntoja"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"pyytää pakettien asennusta"</string>
</resources>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 7298cba..b83307e 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Sisältää henkilökohtaisia tietoja, kuten luottokortin numeroita ja salasanoja."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"poista tilapalkki käytöstä tai muokkaa tilapalkkia"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Antaa sovelluksen poistaa tilapalkin käytöstä ja lisätä tai poistaa järjestelmäkuvakkeita."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"tilapalkki"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"sijaita tilapalkissa"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Antaa sovelluksen sijaita tilapalkissa."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"laajentaa/tiivistää tilarivin"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Antaa sovelluksen laajentaa tai tiivistää tilarivin."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Antaa sovelluksen vastaanottaa ja käsitellä WAP-viestejä. Sovellus voi valvoa tai poistaa laitteeseesi lähetettyjä viestejä näyttämättä niitä sinulle."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"käynnissä olevien sovellusten noutaminen"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Antaa sovelluksen noutaa tietoja käynnissä olevista ja äskettäin suoritetuista tehtävistä. Sovellus voi saada tietoja laitteella käytetyistä sovelluksista."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Hallinnoi laitteen ja profiilien omistajia"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"hallinnoida laitteen ja profiilien omistajia"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Sallii sovelluksien määrittää laitteen ja profiilien omistajat."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"käynnissä olevien sovellusten järjesteleminen"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Antaa sovelluksen siirtää tehtäviä etualalle ja taustalle. Sovellus ei tarvitse toimiin käyttäjän lupaa."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Antaa sovelluksen muokata tablet-laitteesi puhelulokia, kuten tietoja vastatuista ja soitetuista puheluista. Haitalliset sovellukset voivat poistaa puhelulokisi tai muokata sitä."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Antaa sovelluksen muokata television puhelulokia, kuten tietoja vastatuista ja soitetuista puheluista. Haitalliset sovellukset voivat poistaa puhelulokisi tai muokata sitä."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Antaa sovelluksen muokata puhelimesi puhelulokia, kuten tietoja vastatuista ja soitetuista puheluista. Haitalliset sovellukset voivat poistaa puhelulokisi tai muokata sitä."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"kehon anturit (kuten sykemittarit)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"käyttää kehon antureita (kuten sykemittareita)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Antaa sovelluksen käyttää kehosi tilaa seuraavien anturien tietoja, esimerkiksi sykettä."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"lue kalenteritapahtumia ja luottamuksellisia tietoja"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Antaa sovelluksen lukea tablet-laitteelle tallennettuja kalenteritapahtumia, myös kavereiden tai työkavereiden tapahtumia. Sovellus voi jakaa tai tallentaa kalenteritietojasi niiden arkaluonteisuudesta huolimatta."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Antaa sovelluksen lisätä, poistaa ja muuttaa tapahtumia, joita voit muokata puhelimellasi. Näihin kuuluvat myös kavereidesi tai työkavereidesi tapahtumat. Sovellus voi lähettää viestejä, jotka vaikuttavat kalenterin omistajien lähettämiltä, tai muokata tapahtumia ilman niiden omistajien lupaa."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"käytä lisää sijainnintarjoajakomentoja"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Antaa sovelluksen käyttää ylimääräisiä sijaintipalvelukomentoja. Sovellus saattaa tällöin häiritä GPS:n tai muiden sijaintilähteiden toimintaa."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"tarkka sijainti (GPS- ja verkkopohjainen)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"käyttää tarkkaa sijaintia (GPS- ja verkkopohjainen)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Antaa sovelluksen käyttää tarkkaa sijaintiasi, joka määritetään GPS:n tai verkon sijaintilähteiden kuten radiomastojen ja Wi-Fi-verkkojen avulla. Sijaintipalveluiden täytyy olla käytössä ja laitteesi saatavilla, jotta sovellus voi käyttää niitä. Sovellus voi määrittää tämän luvan avulla sijaintisi ja lisätä akun käyttöä."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"likimääräinen sijainti (verkkopohjainen)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"käyttää likimääräistä sijaintia (verkkopohjainen)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Antaa sovelluksen käyttää likimääräistä sijaintiasi. Sijainnin määrittävät sijaintipalvelut verkon sijaintilähteiden kuten radiomastojen ja Wi-Fi-verkkojen avulla. Sijaintipalveluiden täytyy olla käytössä ja laitteesi saatavilla, jotta sovellus voi käyttää niitä. Sovellus voi määrittää tämän luvan avulla likimääräisen sijaintisi."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"muuta ääniasetuksia"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Antaa sovelluksen muokata yleisiä ääniasetuksia, kuten äänenvoimakkuutta ja käytettävää kaiutinta."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"tallentaa ääntä"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Antaa sovelluksen tallentaa ääntä mikrofonin avulla. Sovellus voi tallentaa ääntä milloin tahansa pyytämättä sinulta lupaa."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"SIM-viestintä"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"lähettää komentoja SIM-kortille"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Antaa sovelluksen lähettää komentoja SIM-kortille. Tämä ei ole turvallista."</string>
<string name="permlab_camera" msgid="3616391919559751192">"ota kuvia ja videoita"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Antaa sovelluksen ottaa kuvia ja kuvata videoita kameralla. Sovellus voi käyttää kameraa milloin tahansa ilman lupaasi."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Antaa sovelluksen tarkastella puhelimeen tallennettuja tilejä. Näihin voivat kuulua myös asentamiesi sovelluksien luomat tilit."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"tarkastele verkkoyhteyksiä"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Antaa sovelluksen tarkastella verkkoyhteyksiä koskevia tietoja, kuten mitä verkkoja on olemassa ja mihin on muodostettu yhteys."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"internetin käyttäminen"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"saada täydet verkon käyttöoikeudet"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Antaa sovelluksen luoda verkkovastakkeita ja käyttää muokattuja verkkoprotokollia. Tietoja voidaan lähettää verkkoon selaimen ja muiden sovellusten avulla, joten tätä lupaa ei tarvita tietojen lähettämiseksi."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"muuta verkkoyhteyksiä"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Antaa sovelluksen muuttaa verkkoyhteyden tilaa."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Antaa sovelluksen määrittää paikallisen Bluetooth-puhelimen asetukset sekä tunnistaa muita laitteita ja muodostaa niiden kanssa laitepareja."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"muodosta yhteys WiMAXiin ja katkaise yhteys"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Antaa sovelluksen määrittää, onko WiMAX käytössä, sekä saada selville tietoja WiMAX-verkoista, joihin on muodostettu yhteys."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Vaihda WiMAX-verkon tilaa"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"vaihda WiMAX-verkon tilaa"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Antaa sovelluksen muodostaa tablet-laitteella yhteyden WiMAX-verkkoon ja katkaista yhteyden."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Antaa sovelluksen muodostaa ja katkaista yhteyden television ja WiMAX-verkkojen välillä."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Antaa sovelluksen muodostaa puhelimella yhteyden WiMAX-verkkoon ja katkaista yhteyden."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Antaa sovelluksen muokata kosketusnäytön kalibrointiparametreja. Ei tavallisten sovellusten käyttöön."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"DRM-varmenteiden käyttö"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Antaa sovelluksen käyttää DRM-varmenteita ja hallita niiden käyttäjiä. Ei tavallisten sovellusten käyttöön."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Vastaanota Android Beam -siirron tilatietoja"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"vastaanottaa Android Beam -siirron tilatietoja"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Antaa sovelluksen vastaanottaa tietoja nykyisistä Android Beam -siirroista"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"DRM-varmenteiden poistaminen"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Antaa sovelluksen poistaa DRM-varmenteita. Ei tavallisten sovellusten käyttöön."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PIN-koodi väärin."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Poista lukitus painamalla Valikko-painiketta ja 0-näppäintä."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Hätänumero"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Ei yhteyttä."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Ei yhteyttä"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Näyttö lukittu."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Poista lukitus tai soita hätäpuhelu painamalla Valikko-painiketta."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Poista lukitus painamalla Valikko-painiketta."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Katso ohjeita käyttöoppaasta tai ota yhteyttä asiakaspalveluun."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM-kortti on lukittu."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"SIM-kortin lukitusta poistetaan…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Olet piirtänyt lukituksenpoistokuvion väärin <xliff:g id="NUMBER_0">%d</xliff:g> kertaa. \n\nYritä uudelleen <xliff:g id="NUMBER_1">%d</xliff:g> sekunnin kuluttua."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Olet kirjoittanut salasanan väärin <xliff:g id="NUMBER_0">%d</xliff:g> kertaa. \n\nYritä uudelleen <xliff:g id="NUMBER_1">%d</xliff:g> sekunnin kuluttua."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Olet kirjoittanut PIN-koodin väärin <xliff:g id="NUMBER_0">%d</xliff:g> kertaa. \n\nYritä uudelleen <xliff:g id="NUMBER_1">%d</xliff:g> sekunnin kuluttua."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Piirsit lukituksenpoistokuvion väärin <xliff:g id="NUMBER_0">%d</xliff:g> kertaa. Jos piirrät kuvion väärin vielä <xliff:g id="NUMBER_1">%d</xliff:g> kertaa, sinua pyydetään poistamaan tablet-laitteesi lukitus Google-sisäänkirjautumisen avulla.\n\n Yritä uudelleen <xliff:g id="NUMBER_2">%d</xliff:g> sekunnin kuluttua."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Olet piirtänyt lukituksenpoistokuvion väärin <xliff:g id="NUMBER_0">%d</xliff:g> kertaa. Jos piirrät kuvion väärin vielä <xliff:g id="NUMBER_1">%d</xliff:g> kertaa, sinua pyydetään poistamaan television lukitus Google-käyttäjätunnuksen avulla.\n\n Yritä uudelleen <xliff:g id="NUMBER_2">%d</xliff:g> sekunnin kuluttua."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Piirsit lukituksenpoistokuvion väärin <xliff:g id="NUMBER_0">%d</xliff:g> kertaa. Jos piirrät kuvion väärin vielä <xliff:g id="NUMBER_1">%d</xliff:g> kertaa, sinua pyydetään poistamaan puhelimesi lukitus Google-sisäänkirjautumisen avulla.\n\n Yritä uudelleen <xliff:g id="NUMBER_2">%d</xliff:g> sekunnin kuluttua."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Tablet-laitteen lukituksen poisto epäonnistui <xliff:g id="NUMBER_0">%d</xliff:g> kertaa. Jos teet vielä <xliff:g id="NUMBER_1">%d</xliff:g> epäonnistunutta yritystä, tablet-laitteeseen palautetaan tehdasasetukset ja kaikki käyttäjätiedot häviävät."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Television lukituksen poistaminen on epäonnistunut <xliff:g id="NUMBER_0">%d</xliff:g> kertaa. Jos se epäonnistuu vielä <xliff:g id="NUMBER_1">%d</xliff:g> kertaa, television tehdasasetukset palautetaan ja käyttäjätiedot poistetaan."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Puhelimen lukituksen poisto epäonnistui <xliff:g id="NUMBER_0">%d</xliff:g> kertaa. Jos teet vielä <xliff:g id="NUMBER_1">%d</xliff:g> epäonnistunutta yritystä, puhelimeen palautetaan tehdasasetukset ja kaikki käyttäjätiedot häviävät."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Olet piirtänyt lukituksenpoistokuvion väärin <xliff:g id="NUMBER_0">%1$d</xliff:g> kertaa. \n\nYritä uudelleen <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunnin kuluttua."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Olet kirjoittanut salasanan väärin <xliff:g id="NUMBER_0">%1$d</xliff:g> kertaa. \n\nYritä uudelleen <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunnin kuluttua."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Olet kirjoittanut PIN-koodin väärin <xliff:g id="NUMBER_0">%1$d</xliff:g> kertaa. \n\nYritä uudelleen <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunnin kuluttua."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Piirsit lukituksenpoistokuvion väärin <xliff:g id="NUMBER_0">%1$d</xliff:g> kertaa. Jos piirrät kuvion väärin vielä <xliff:g id="NUMBER_1">%2$d</xliff:g> kertaa, sinua pyydetään poistamaan tablet-laitteesi lukitus Google-sisäänkirjautumisen avulla.\n\n Yritä uudelleen <xliff:g id="NUMBER_2">%3$d</xliff:g> sekunnin kuluttua."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Olet piirtänyt lukituksenpoistokuvion väärin <xliff:g id="NUMBER_0">%1$d</xliff:g> kertaa. Jos piirrät kuvion väärin vielä <xliff:g id="NUMBER_1">%2$d</xliff:g> kertaa, sinua pyydetään poistamaan television lukitus Google-käyttäjätunnuksen avulla.\n\n Yritä uudelleen <xliff:g id="NUMBER_2">%3$d</xliff:g> sekunnin kuluttua."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Piirsit lukituksenpoistokuvion väärin <xliff:g id="NUMBER_0">%1$d</xliff:g> kertaa. Jos piirrät kuvion väärin vielä <xliff:g id="NUMBER_1">%2$d</xliff:g> kertaa, sinua pyydetään poistamaan puhelimesi lukitus Google-sisäänkirjautumisen avulla.\n\n Yritä uudelleen <xliff:g id="NUMBER_2">%3$d</xliff:g> sekunnin kuluttua."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Tablet-laitteen lukituksen poisto epäonnistui <xliff:g id="NUMBER_0">%1$d</xliff:g> kertaa. Jos teet vielä <xliff:g id="NUMBER_1">%2$d</xliff:g> epäonnistunutta yritystä, tablet-laitteeseen palautetaan tehdasasetukset ja kaikki käyttäjätiedot häviävät."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Television lukituksen poistaminen on epäonnistunut <xliff:g id="NUMBER_0">%1$d</xliff:g> kertaa. Jos se epäonnistuu vielä <xliff:g id="NUMBER_1">%2$d</xliff:g> kertaa, television tehdasasetukset palautetaan ja käyttäjätiedot poistetaan."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Puhelimen lukituksen poisto epäonnistui <xliff:g id="NUMBER_0">%1$d</xliff:g> kertaa. Jos teet vielä <xliff:g id="NUMBER_1">%2$d</xliff:g> epäonnistunutta yritystä, puhelimeen palautetaan tehdasasetukset ja kaikki käyttäjätiedot häviävät."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Tablet-laitteen lukituksen poisto epäonnistui <xliff:g id="NUMBER">%d</xliff:g> kertaa. Laitteeseen palautetaan nyt tehdasasetukset."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Television lukituksen poistaminen on epäonnistunut <xliff:g id="NUMBER">%d</xliff:g> kertaa. Television tehdasasetukset palautetaan."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Puhelimen lukituksen poisto epäonnistui <xliff:g id="NUMBER">%d</xliff:g> kertaa. Puhelimeen palautetaan nyt tehdasasetukset."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"sana"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"linkki"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"rivi"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Tehdastesti epäonnistui"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"FACTORY_TEST-toimintoa tuetaan vain paketeille, jotka on tallennettu kansioon /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"FACTORY_TEST-toiminnon tarjoavaa pakettia ei löytynyt."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Alustetaan…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Ei liitetty"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Osuvia toimintoja ei löytynyt."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Median reititys"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"reitittää mediaa"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Antaa sovelluksen reitittää mediaa muihin ulkoisiin laitteisiin."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Lue asennusistuntoja"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"lukea asennusistuntoja"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Sallii sovelluksen lukea asennusistuntoja. Toiminto sallii sovelluksen lukea aktiivisten asennuspakettien tietoja."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Pyydä pakettien asennusta"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"pyytää asennuspaketteja"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Antaa sovelluksen pyytää pakettien asennusta."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Ohjaa zoomausta napauttamalla kahdesti"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Widgetin lisääminen epäonnistui."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Virheellinen käyttäjänimi tai salasana."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Unohditko käyttäjänimesi tai salasanasi?\nKäy osoitteessa "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Tarkistetaan tiliä..."</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Olet kirjoittanut PIN-koodin väärin <xliff:g id="NUMBER_0">%d</xliff:g> kertaa. \n\nYritä uudelleen <xliff:g id="NUMBER_1">%d</xliff:g> sekunnin kuluttua."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Olet kirjoittanut salasanan väärin <xliff:g id="NUMBER_0">%d</xliff:g> kertaa. \n\nYritä uudelleen <xliff:g id="NUMBER_1">%d</xliff:g> sekunnin kuluttua."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Olet piirtänyt lukituksenpoistokuvion väärin <xliff:g id="NUMBER_0">%d</xliff:g> kertaa. \n\nYritä uudelleen <xliff:g id="NUMBER_1">%d</xliff:g> sekunnin kuluttua."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Tablet-laitteen lukituksen poisto epäonnistui <xliff:g id="NUMBER_0">%d</xliff:g> kertaa. Jos teet vielä <xliff:g id="NUMBER_1">%d</xliff:g> epäonnistunutta yritystä, tablet-laitteeseen palautetaan tehdasasetukset ja kaikki käyttäjätiedot häviävät."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Television lukituksen poistaminen on epäonnistunut <xliff:g id="NUMBER_0">%d</xliff:g> kertaa. Jos se epäonnistuu vielä <xliff:g id="NUMBER_1">%d</xliff:g> kertaa, television tehdasasetukset palautetaan ja käyttäjätiedot poistetaan."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Puhelimen lukituksen poisto epäonnistui <xliff:g id="NUMBER_0">%d</xliff:g> kertaa. Jos teet vielä <xliff:g id="NUMBER_1">%d</xliff:g> epäonnistunutta yritystä, puhelimeen palautetaan tehdasasetukset ja kaikki käyttäjätiedot häviävät."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Olet kirjoittanut PIN-koodin väärin <xliff:g id="NUMBER_0">%1$d</xliff:g> kertaa. \n\nYritä uudelleen <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunnin kuluttua."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Olet kirjoittanut salasanan väärin <xliff:g id="NUMBER_0">%1$d</xliff:g> kertaa. \n\nYritä uudelleen <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunnin kuluttua."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Olet piirtänyt lukituksenpoistokuvion väärin <xliff:g id="NUMBER_0">%1$d</xliff:g> kertaa. \n\nYritä uudelleen <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunnin kuluttua."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Tablet-laitteen lukituksen poisto epäonnistui <xliff:g id="NUMBER_0">%1$d</xliff:g> kertaa. Jos teet vielä <xliff:g id="NUMBER_1">%2$d</xliff:g> epäonnistunutta yritystä, tablet-laitteeseen palautetaan tehdasasetukset ja kaikki käyttäjätiedot häviävät."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Television lukituksen poistaminen on epäonnistunut <xliff:g id="NUMBER_0">%1$d</xliff:g> kertaa. Jos se epäonnistuu vielä <xliff:g id="NUMBER_1">%2$d</xliff:g> kertaa, television tehdasasetukset palautetaan ja käyttäjätiedot poistetaan."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Puhelimen lukituksen poisto epäonnistui <xliff:g id="NUMBER_0">%1$d</xliff:g> kertaa. Jos teet vielä <xliff:g id="NUMBER_1">%2$d</xliff:g> epäonnistunutta yritystä, puhelimeen palautetaan tehdasasetukset ja kaikki käyttäjätiedot häviävät."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Tablet-laitteen lukituksen poisto epäonnistui <xliff:g id="NUMBER">%d</xliff:g> kertaa. Laitteeseen palautetaan nyt tehdasasetukset."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Television lukituksen poistaminen on epäonnistunut <xliff:g id="NUMBER">%d</xliff:g> kertaa. Television tehdasasetukset palautetaan."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Puhelimen lukituksen poisto epäonnistui <xliff:g id="NUMBER">%d</xliff:g> kertaa. Puhelimeen palautetaan nyt tehdasasetukset."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Piirsit lukituksenpoistokuvion väärin <xliff:g id="NUMBER_0">%d</xliff:g> kertaa. Jos piirrät kuvion väärin vielä <xliff:g id="NUMBER_1">%d</xliff:g> kertaa, sinua pyydetään poistamaan tablet-laitteesi lukitus sähköpostitilin avulla.\n\n Yritä uudelleen <xliff:g id="NUMBER_2">%d</xliff:g> sekunnin kuluttua."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Olet piirtänyt lukituksenpoistokuvion väärin <xliff:g id="NUMBER_0">%d</xliff:g> kertaa. Jos piirrät kuvion väärin vielä <xliff:g id="NUMBER_1">%d</xliff:g> kertaa, sinua pyydetään avaamaan television lukitus sähköpostitilin avulla.\n\n Yritä uudelleen <xliff:g id="NUMBER_2">%d</xliff:g> sekunnin kuluttua."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Piirsit lukituksenpoistokuvion väärin <xliff:g id="NUMBER_0">%d</xliff:g> kertaa. Jos piirrät kuvion väärin vielä <xliff:g id="NUMBER_1">%d</xliff:g> kertaa, sinua pyydetään poistamaan puhelimesi lukitus sähköpostitilin avulla.\n\n Yritä uudelleen <xliff:g id="NUMBER_2">%d</xliff:g> sekunnin kuluttua."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Piirsit lukituksenpoistokuvion väärin <xliff:g id="NUMBER_0">%1$d</xliff:g> kertaa. Jos piirrät kuvion väärin vielä <xliff:g id="NUMBER_1">%2$d</xliff:g> kertaa, sinua pyydetään poistamaan tablet-laitteesi lukitus sähköpostitilin avulla.\n\n Yritä uudelleen <xliff:g id="NUMBER_2">%3$d</xliff:g> sekunnin kuluttua."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Olet piirtänyt lukituksenpoistokuvion väärin <xliff:g id="NUMBER_0">%1$d</xliff:g> kertaa. Jos piirrät kuvion väärin vielä <xliff:g id="NUMBER_1">%2$d</xliff:g> kertaa, sinua pyydetään avaamaan television lukitus sähköpostitilin avulla.\n\n Yritä uudelleen <xliff:g id="NUMBER_2">%3$d</xliff:g> sekunnin kuluttua."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Piirsit lukituksenpoistokuvion väärin <xliff:g id="NUMBER_0">%1$d</xliff:g> kertaa. Jos piirrät kuvion väärin vielä <xliff:g id="NUMBER_1">%2$d</xliff:g> kertaa, sinua pyydetään poistamaan puhelimesi lukitus sähköpostitilin avulla.\n\n Yritä uudelleen <xliff:g id="NUMBER_2">%3$d</xliff:g> sekunnin kuluttua."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Poista"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Nostetaanko äänenvoimakkuus suositellun tason yläpuolelle?\n\nPitkäkestoinen kova äänenvoimakkuus saattaa heikentää kuuloa."</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">1 tunnin ajan</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Kunnes kello on <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> asti (seuraava hälytys)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Kunnes poistat tämän käytöstä"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Kunnes poistat Varattu-tilan käytöstä."</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-fr-rCA-watch/strings.xml b/core/res/res/values-fr-rCA-watch/strings.xml
index 8f2e683..6115a59 100644
--- a/core/res/res/values-fr-rCA-watch/strings.xml
+++ b/core/res/res/values-fr-rCA-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Appli <xliff:g id="NUMBER_0">%1$d</xliff:g> de <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Capteurs"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"accéder à vos contacts"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"accéder à la position de cette montre"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"accéder à votre agenda"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"envoyer et afficher des messages texte"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"accéder à des photos, à des contenus multimédias et à des fichiers sur votre montre"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"enregistrer des fichiers audio"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"prendre des photos et filmer des vidéos"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"faire et gérer des appels téléphoniques"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"accéder aux données des capteurs sur vos signes vitaux"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"faire office de barre d\'état"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"accéder aux capteurs corporels (comme les moniteurs de fréquence cardiaque)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"accéder à votre position précise (GPS et réseau)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"accéder à votre position approximative (réseau)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"envoyer des commandes à la carte SIM"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"bénéficier d\'un accès complet au réseau"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"gérer les propriétaires des profils et de l\'appareil"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"modifier l\'état du WiMAX"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"recevoir des données sur l\'état du transfert Android Beam"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"diriger la sortie multimédia"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"accéder aux sessions d\'installation"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"demander l\'installation de paquets"</string>
</resources>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index ba17051..6f54ece 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Inclut des données personnelles telles que les numéros de cartes de paiement et les mots de passe."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"désactiver ou modifier la barre d\'état"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Permet à l\'application de désactiver la barre d\'état, ou d\'ajouter et de supprimer des icônes système."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"barre d\'état"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"servir de barre d\'état"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Permet à l\'application de faire office de barre d\'état."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"agrandir ou réduire la barre d\'état"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Permet à l\'application de réduire ou de développer la barre d\'état."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Permet à l\'application de recevoir et de traiter les messages WAP. Cette autorisation lui donne la possibilité de surveiller ou de supprimer les messages envoyés à votre appareil sans vous les montrer."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"récupérer les données des applications en cours d\'exécution"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Permet à l\'application de récupérer des données sur des tâches en cours d\'exécution et récemment exécutées. L\'application est ainsi susceptible d\'obtenir des données concernant les applications utilisées sur l\'appareil."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Gérer les propriétaires des profils et de l\'appareil"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"gérer les propriétaires des profils et de l\'appareil"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Autoriser les applications à définir les propriétaires des profils et celui de l\'appareil"</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"réorganiser les applications en cours d\'exécution"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Permet à l\'application de déplacer les tâches au premier plan et en arrière-plan. L\'application peut procéder à ces opérations sans votre intervention."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Permet à l\'application de lire le journal d\'appels de votre tablette, y compris les données relatives aux appels entrants et sortants. Des applications malveillantes peuvent utiliser cette fonctionnalité pour effacer ou modifier votre journal d\'appels."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Permet à l\'application de modifier le journal d\'appels de votre téléviseur, y compris les données sur les appels entrants et sortants. Des applications malveillantes pourraient utiliser cette fonctionnalité pour effacer ou modifier votre journal d\'appels."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Permet à l\'application de lire le journal d\'appels de votre téléphone, y compris les données relatives aux appels entrants et sortants. Des applications malveillantes peuvent utiliser cette fonctionnalité pour effacer ou modifier votre journal d\'appels."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"capteurs corporels (tels que les moniteurs de fréquence cardiaque)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"accéder aux capteurs corporels (comme les moniteurs de fréquence cardiaque)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Permet à l\'application d\'accéder aux données des capteurs qui surveillent votre condition physique, comme votre rythme cardiaque."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"consulter les événements d\'agenda ainsi que les données confidentielles qu\'ils contiennent"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Permet à l\'application de lire tous les événements d\'agenda stockés sur votre tablette, y compris ceux de vos amis ou de vos collègues. Cette autorisation peut lui permettre de partager ou d\'enregistrer vos données d\'agenda, indépendamment de leur caractère confidentiel ou sensible."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Permet à l\'application d\'ajouter, de supprimer et d\'apporter des modifications aux événements modifiables sur votre téléphone, y compris ceux de vos amis ou de vos collègues. Cette autorisation peut lui permettre d\'envoyer des messages qui semblent provenir de propriétaires de l\'agenda ou de modifier les événements à l\'insu des propriétaires."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"accéder aux commandes de fournisseur de position géographique supplémentaires"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Permet à l\'application d\'accéder à des commandes de localisation supplémentaires offertes par le fournisseur. Elle est ainsi susceptible d\'interférer avec le bon fonctionnement du GPS ou de toute autre source de localisation."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"position précise (GPS et réseau)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"accéder à votre position précise (GPS et réseau)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Permet à l\'application d\'obtenir votre position exacte à l\'aide du récepteur satellite GPS ou des sources de localisation de réseau tels que les points d\'accès Wi-Fi et les antennes-relais. Ces services de localisation doivent être activés et disponibles sur votre appareil pour que l\'application puissent déterminer où vous vous trouvez, le cas échéant. Cette autorisation peut entraîner une utilisation accrue de la batterie."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"connaître votre position approximative (réseau)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"accéder à votre position approximative (réseau)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Permet à l\'application d\'obtenir votre position approximative. Celle-ci est fournie par des services de localisation sur la base des sources de localisation de réseau tels que les points d\'accès Wi-Fi et les antennes-relais. Ces services de localisation doivent être activés et disponibles sur votre appareil pour que l\'application puisse déterminer où vous vous trouvez de façon approximative, le cas échéant."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"modifier vos paramètres audio"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Permet à l\'application de modifier les paramètres audio généraux, tels que le volume et la sortie audio utilisée."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"enregistrer des fichiers audio"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Permet à l\'application d\'enregistrer des contenus audio à l\'aide du microphone. Cette autorisation lui donne la possibilité d\'enregistrer du contenu audio à tout moment sans votre consentement."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"Communication avec la carte SIM"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"envoyer des commandes à la carte SIM"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Permet à l\'application d\'envoyer des commandes à la carte SIM. Cette fonctionnalité est très dangereuse."</string>
<string name="permlab_camera" msgid="3616391919559751192">"prendre des photos et filmer des vidéos"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Permet à l\'application de prendre des photos et de filmer des vidéos avec l\'appareil photo. Cette autorisation lui permet d\'utiliser l\'appareil photo à tout moment sans votre consentement."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Permet à l\'application d\'obtenir la liste des comptes connus par le téléphone. Il peut s\'agir de n\'importe quel compte créé par les applications que vous avez installées."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"afficher les connexions réseau"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Permet à l\'application d\'accéder à des détails concernant les connexions réseau, comme les réseaux existants et connectés."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"bénéficier d\'un accès complet au réseau"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"bénéficier d\'un accès complet au réseau"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Permet à l\'application de créer des interfaces de connexion réseau et d\'utiliser des protocoles réseau personnalisés. Le navigateur et d\'autres applications permettent d\'envoyer des données sur Internet. Cette autorisation n\'est donc pas nécessaire pour envoyer des données sur Internet."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"modifier la connectivité réseau"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Permet à l\'application de modifier l\'état de la connectivité réseau."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Permet à l\'application de configurer le téléphone Bluetooth local, d\'identifier des appareils distants et de les associer au téléphone."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"se connecter au réseau WiMAX et s\'en déconnecter"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Permet à l\'application de déterminer si le WiMAX est activé et d\'obtenir des détails sur tous les réseaux WiMAX connectés."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Modifier l\'état du WiMAX"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"modifier l\'état du WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Permet à l\'application de connecter la tablette aux réseaux WiMAX et de l\'en déconnecter."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Permet à l\'application de se connecter au téléviseur et de le déconnecter de réseaux WiMAX."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Permet à l\'application de connecter le téléphone aux réseaux WiMAX et de l\'en déconnecter."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Permet à l\'application de modifier les paramètres de calibrage de l\'écran tactile. Ne devrait jamais être nécessaire pour les applications standards."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"accéder aux certificats GDN"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Permet à une application de fournir et d\'utiliser les certificats de GDN. Cela ne devrait jamais être nécessaire pour les applications normales."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Recevoir des données sur l\'état du transfert Android Beam"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"recevoir des données sur l\'état du transfert Android Beam"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Autoriser cette application à recevoir des données sur les transferts Android Beam en cours"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"supprimer des certificats GDN"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Permet à une application de supprimer les certificats GDN. Cela ne devrait jamais être nécessaire pour des applications normales."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"NIP erroné."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Pour déverrouiller le téléphone, appuyez sur \"Menu\", puis sur 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Numéro d\'urgence"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Aucun service"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Aucun service"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Écran verrouillé"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Appuyez sur \"Menu\" pour débloquer le téléphone ou appeler un numéro d\'urgence."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Appuyez sur \"Menu\" pour déverrouiller l\'appareil."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Veuillez consulter le guide utilisateur ou contacter le service à la clientèle."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"La carte SIM est verrouillée."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Déverrouillage de la carte SIM en cours…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%d</xliff:g> reprises.\n\nVeuillez réessayer dans <xliff:g id="NUMBER_1">%d</xliff:g> secondes."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Vous avez saisi un mot de passe incorrect à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. \n\nVeuillez réessayer dans <xliff:g id="NUMBER_1">%d</xliff:g> secondes."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Vous avez saisi un NIP incorrect à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. \n\nVeuillez réessayer dans <xliff:g id="NUMBER_1">%d</xliff:g> secondes."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. Au bout de <xliff:g id="NUMBER_1">%d</xliff:g> échecs supplémentaires, vous devrez déverrouiller votre tablette à l\'aide de votre identifiant Google.\n\nVeuillez réessayer dans <xliff:g id="NUMBER_2">%d</xliff:g> secondes."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Vous avez tracé incorrectement votre schéma de déverrouillage <xliff:g id="NUMBER_0">%d</xliff:g> fois. Après <xliff:g id="NUMBER_1">%d</xliff:g> autres tentatives infructueuses, vous devrez déverrouiller votre téléviseur en vous connectant à votre compte Google.\n\nEssayez à nouveau dans <xliff:g id="NUMBER_2">%d</xliff:g> secondes."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. Au bout de <xliff:g id="NUMBER_1">%d</xliff:g> échecs supplémentaires, vous devrez déverrouiller votre téléphone à l\'aide de votre identifiant Google.\n\nVeuillez réessayer dans <xliff:g id="NUMBER_2">%d</xliff:g> secondes."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Vous avez tenté de déverrouiller la tablette de façon incorrecte à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. Si vous échouez encore <xliff:g id="NUMBER_1">%d</xliff:g> fois, sa configuration d\'usine sera rétablie, et toutes les données utilisateur seront perdues."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Vous avez effectué <xliff:g id="NUMBER_0">%d</xliff:g> tentative(s) pour déverrouiller le téléviseur. Après <xliff:g id="NUMBER_1">%d</xliff:g> tentatives infructueuses, le téléviseur sera réinitialisé aux paramètres d\'usine et toutes les données d\'utilisateur seront perdues."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Vous avez tenté de déverrouiller le téléphone de façon incorrecte à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. Si vous échouez encore <xliff:g id="NUMBER_1">%d</xliff:g> fois, sa configuration d\'usine sera rétablie, et toutes les données utilisateur seront perdues."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises.\n\nVeuillez réessayer dans <xliff:g id="NUMBER_1">%2$d</xliff:g> secondes."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Vous avez saisi un mot de passe incorrect à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises. \n\nVeuillez réessayer dans <xliff:g id="NUMBER_1">%2$d</xliff:g> secondes."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Vous avez saisi un NIP incorrect à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises. \n\nVeuillez réessayer dans <xliff:g id="NUMBER_1">%2$d</xliff:g> secondes."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises. Au bout de <xliff:g id="NUMBER_1">%2$d</xliff:g> échecs supplémentaires, vous devrez déverrouiller votre tablette à l\'aide de votre identifiant Google.\n\nVeuillez réessayer dans <xliff:g id="NUMBER_2">%3$d</xliff:g> secondes."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Vous avez tracé incorrectement votre schéma de déverrouillage <xliff:g id="NUMBER_0">%1$d</xliff:g> fois. Après <xliff:g id="NUMBER_1">%2$d</xliff:g> autres tentatives infructueuses, vous devrez déverrouiller votre téléviseur en vous connectant à votre compte Google.\n\nEssayez à nouveau dans <xliff:g id="NUMBER_2">%3$d</xliff:g> secondes."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises. Au bout de <xliff:g id="NUMBER_1">%2$d</xliff:g> échecs supplémentaires, vous devrez déverrouiller votre téléphone à l\'aide de votre identifiant Google.\n\nVeuillez réessayer dans <xliff:g id="NUMBER_2">%3$d</xliff:g> secondes."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Vous avez tenté de déverrouiller la tablette de façon incorrecte à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises. Si vous échouez encore <xliff:g id="NUMBER_1">%2$d</xliff:g> fois, sa configuration d\'usine sera rétablie, et toutes les données utilisateur seront perdues."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Vous avez effectué <xliff:g id="NUMBER_0">%1$d</xliff:g> tentative(s) pour déverrouiller le téléviseur. Après <xliff:g id="NUMBER_1">%2$d</xliff:g> tentatives infructueuses, le téléviseur sera réinitialisé aux paramètres d\'usine et toutes les données d\'utilisateur seront perdues."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Vous avez tenté de déverrouiller le téléphone de façon incorrecte à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises. Si vous échouez encore <xliff:g id="NUMBER_1">%2$d</xliff:g> fois, sa configuration d\'usine sera rétablie, et toutes les données utilisateur seront perdues."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Vous avez tenté de déverrouiller la tablette de façon incorrecte à <xliff:g id="NUMBER">%d</xliff:g> reprises. Sa configuration d\'usine va être rétablie."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Vous avez effectué <xliff:g id="NUMBER">%d</xliff:g> tentatives infructueuses pour déverrouiller le téléviseur. Le téléviseur va maintenant être réinitialisé aux paramètres d\'usine."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Vous avez tenté de déverrouiller le téléphone de façon incorrecte à <xliff:g id="NUMBER">%d</xliff:g> reprises. Sa configuration d\'usine va être rétablie."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"mot"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"Lié"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"ligne"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Échec du test usine"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"L\'action FACTORY_TEST est uniquement prise en charge pour les paquets de données installés dans in/system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Impossible de trouver un paquet proposant l\'action FACTORY_TEST."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Formatage en cours..."</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Non insérée"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Aucune activité correspondante trouvée."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Diriger la sortie multimédia"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"diriger la sortie multimédia"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Permet à une application de diriger la sortie multimédia vers d\'autres appareils externes."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Accéder aux sessions d\'installation"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"accéder aux sessions d\'installation"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Permet à une application d\'accéder aux sessions d\'installation. Cela lui permet de consulter les détails relatifs à l\'installation des paquets actifs."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Demander l\'installation de paquets"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"demander l\'installation de paquets"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Permet à une application de demander l\'installation de paquets."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Appuyer deux fois pour régler le zoom"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Impossible d\'ajouter le widget."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Nom d\'utilisateur ou mot de passe non valide."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Vous avez oublié votre nom d\'utilisateur ou votre mot de passe?\nRendez-vous sur la page "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Vérification du compte en cours…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Vous avez saisi un NIP incorrect à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. \n\nVeuillez réessayer dans <xliff:g id="NUMBER_1">%d</xliff:g> secondes."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Vous avez saisi un mot de passe incorrect à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. \n\nVeuillez réessayer dans <xliff:g id="NUMBER_1">%d</xliff:g> secondes."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%d</xliff:g> reprises.\n\nVeuillez réessayer dans <xliff:g id="NUMBER_1">%d</xliff:g> secondes."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Vous avez tenté de déverrouiller la tablette de façon incorrecte à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. Si vous échouez encore <xliff:g id="NUMBER_1">%d</xliff:g> fois, sa configuration d\'usine sera rétablie, et toutes les données utilisateur seront perdues."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Vous avez effectué <xliff:g id="NUMBER_0">%d</xliff:g> tentative(s) pour déverrouiller le téléviseur. Après <xliff:g id="NUMBER_1">%d</xliff:g> tentatives infructueuses, le téléviseur sera réinitialisé aux paramètres d\'usine et toutes les données d\'utilisateur seront perdues."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Vous avez tenté de déverrouiller le téléphone de façon incorrecte à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. Si vous échouez encore <xliff:g id="NUMBER_1">%d</xliff:g> fois, sa configuration d\'usine sera rétablie, et toutes les données utilisateur seront perdues."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Vous avez saisi un NIP incorrect à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises. \n\nVeuillez réessayer dans <xliff:g id="NUMBER_1">%2$d</xliff:g> secondes."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Vous avez saisi un mot de passe incorrect à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises. \n\nVeuillez réessayer dans <xliff:g id="NUMBER_1">%2$d</xliff:g> secondes."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises.\n\nVeuillez réessayer dans <xliff:g id="NUMBER_1">%2$d</xliff:g> secondes."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Vous avez tenté de déverrouiller la tablette de façon incorrecte à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises. Si vous échouez encore <xliff:g id="NUMBER_1">%2$d</xliff:g> fois, sa configuration d\'usine sera rétablie, et toutes les données utilisateur seront perdues."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Vous avez effectué <xliff:g id="NUMBER_0">%1$d</xliff:g> tentative(s) pour déverrouiller le téléviseur. Après <xliff:g id="NUMBER_1">%2$d</xliff:g> tentatives infructueuses, le téléviseur sera réinitialisé aux paramètres d\'usine et toutes les données d\'utilisateur seront perdues."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Vous avez tenté de déverrouiller le téléphone de façon incorrecte à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises. Si vous échouez encore <xliff:g id="NUMBER_1">%2$d</xliff:g> fois, sa configuration d\'usine sera rétablie, et toutes les données utilisateur seront perdues."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Vous avez tenté de déverrouiller la tablette de façon incorrecte à <xliff:g id="NUMBER">%d</xliff:g> reprises. Sa configuration d\'usine va être rétablie."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Vous avez effectué <xliff:g id="NUMBER">%d</xliff:g> tentatives infructueuses pour déverrouiller le téléviseur. Le téléviseur va maintenant être réinitialisé aux paramètres d\'usine."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Vous avez tenté de déverrouiller le téléphone de façon incorrecte à <xliff:g id="NUMBER">%d</xliff:g> reprises. Sa configuration d\'usine va être rétablie."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. Si vous échouez encore <xliff:g id="NUMBER_1">%d</xliff:g> fois, vous devrez déverrouiller votre tablette à l\'aide d\'un compte de messagerie électronique.\n\n Veuillez réessayer dans <xliff:g id="NUMBER_2">%d</xliff:g> secondes."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Vous avez tracé incorrectement votre schéma de déverrouillage <xliff:g id="NUMBER_0">%d</xliff:g> fois. Après <xliff:g id="NUMBER_1">%d</xliff:g> autres tentatives infructueuses, vous devrez déverrouiller votre téléviseur à l\'aide d\'un compte de courriel.\n\nEssayez à nouveau dans <xliff:g id="NUMBER_2">%d</xliff:g> secondes."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. Si vous échouez encore <xliff:g id="NUMBER_1">%d</xliff:g> fois, vous devrez déverrouiller votre téléphone à l\'aide d\'un compte de messagerie électronique.\n\n Veuillez réessayer dans <xliff:g id="NUMBER_2">%d</xliff:g> secondes."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises. Si vous échouez encore <xliff:g id="NUMBER_1">%2$d</xliff:g> fois, vous devrez déverrouiller votre tablette à l\'aide d\'un compte de messagerie électronique.\n\n Veuillez réessayer dans <xliff:g id="NUMBER_2">%3$d</xliff:g> secondes."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Vous avez tracé incorrectement votre schéma de déverrouillage <xliff:g id="NUMBER_0">%1$d</xliff:g> fois. Après <xliff:g id="NUMBER_1">%2$d</xliff:g> autres tentatives infructueuses, vous devrez déverrouiller votre téléviseur à l\'aide d\'un compte de courriel.\n\nEssayez à nouveau dans <xliff:g id="NUMBER_2">%3$d</xliff:g> secondes."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises. Si vous échouez encore <xliff:g id="NUMBER_1">%2$d</xliff:g> fois, vous devrez déverrouiller votre téléphone à l\'aide d\'un compte de messagerie électronique.\n\n Veuillez réessayer dans <xliff:g id="NUMBER_2">%3$d</xliff:g> secondes."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Supprimer"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Augmenter le volume au-dessus du niveau recommandé?\n\nL\'écoute prolongée à un volume élevé peut endommager vos facultés auditives."</string>
@@ -1470,6 +1468,7 @@
<item quantity="other">Pendant %d h</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Jusqu\'à <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Jusqu\'à <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (alarme suivante)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Jusqu\'à la désactivation"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Jusqu\'à ce que vous désactiviez le mode « Ne pas déranger »"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-fr-watch/strings.xml b/core/res/res/values-fr-watch/strings.xml
index 21bf8a6..aab65c0 100644
--- a/core/res/res/values-fr-watch/strings.xml
+++ b/core/res/res/values-fr-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Appli <xliff:g id="NUMBER_0">%1$d</xliff:g> sur <xliff:g id="NUMBER_1">%2$d</xliff:g>"</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Capteurs"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"accéder à vos contacts"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"accéder à la position de cette montre"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"accéder à votre agenda"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"envoyer et consulter des SMS"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"accéder à des photos, à des contenus multimédias et à des fichiers sur votre montres"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"enregistrer des fichiers audio"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"prendre des photos et enregistrer des vidéos"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"effectuer et gérer des appels téléphoniques"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"accéder aux données des capteurs relatives à vos signes vitaux"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"remplacer la barre d\'état"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"accéder aux capteurs corporels (tels que les cardiofréquencemètres)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"accéder à votre position précise (GPS et réseau)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"accéder à votre position approximative (selon le réseau)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"envoyer des commandes à la carte SIM"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"bénéficier d\'un accès complet au réseau"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"gérer les propriétaires des profils et de l\'appareil"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"modifier l\'état du WiMAX"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"recevoir des informations sur l\'état du transfert Android Beam"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"diriger la sortie multimédia"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"accéder aux sessions d\'installation"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"demander l\'installation de packages"</string>
</resources>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 15536cf..7a7a814 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Inclut des données personnelles telles que les numéros de cartes de paiement et les mots de passe."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"Désactivation ou modification de la barre d\'état"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Permet à l\'application de désactiver la barre d\'état, ou d\'ajouter et de supprimer des icônes système."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"barre d\'état"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"remplacer la barre d\'état"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Permet à l\'application de faire office de barre d\'état."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"Agrandir/réduire la barre d\'état"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Permet à l\'application de réduire ou de développer la barre d\'état."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Permet à l\'application de recevoir et de traiter les messages WAP. Cette autorisation lui donne la possibilité de surveiller ou supprimer les messages envoyés à votre appareil sans vous les montrer."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"récupérer les applications en cours d\'exécution"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Permet à l\'application de récupérer des informations sur des tâches en cours d\'exécution et récemment exécutées. L\'application est ainsi susceptible d\'obtenir des informations sur les applications utilisées sur l\'appareil."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Gérer les propriétaires des profils et de l\'appareil"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"gérer les propriétaires des profils et de l\'appareil"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Autoriser les applications à définir les propriétaires des profils et celui de l\'appareil"</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"réorganiser les applications en cours d\'exécution"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Permet à l\'application de déplacer les tâches au premier plan et en arrière-plan. L\'application peut procéder à ces opérations sans votre intervention."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Permet à l\'application de lire le journal d\'appels de votre tablette, y compris les données relatives aux appels entrants et sortants. Des applications malveillantes peuvent utiliser cette fonctionnalité pour effacer ou modifier votre journal d\'appels."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Permet à l\'application de lire le journal d\'appels du téléviseur, y compris les données relatives aux appels entrants et sortants. Des applications malveillantes peuvent utiliser cette fonctionnalité pour effacer ou modifier votre journal d\'appels."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Permet à l\'application de lire le journal d\'appels de votre téléphone, y compris les données relatives aux appels entrants et sortants. Des applications malveillantes peuvent utiliser cette fonctionnalité pour effacer ou modifier votre journal d\'appels."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"capteurs corporels (tels que les cardiofréquencemètres)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"accéder capteurs corp. (ex : cardiofréquencemètres)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Permet à l\'application d\'accéder aux données des capteurs qui contrôlent votre condition physique, comme votre rythme cardiaque."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"consulter les événements d\'agenda ainsi que les informations confidentielles"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Permet à l\'application de lire tous les événements d\'agenda stockés sur votre tablette, y compris ceux de vos amis ou de vos collègues. Cette autorisation peut lui permettre de partager ou d\'enregistrer vos données d\'agenda, indépendamment de leur caractère confidentiel ou sensible."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Permet à l\'application d\'ajouter, de supprimer et d\'apporter des modifications aux événements modifiables sur votre téléphone, y compris ceux de vos amis ou de vos collègues. Cette autorisation peut lui permettre d\'envoyer des messages qui semblent provenir de propriétaires de l\'agenda ou de modifier les événements à l\'insu des propriétaires."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"Accès aux commandes de fournisseur de position géographique supplémentaires"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Permet à l\'application d\'accéder à des commandes de localisation supplémentaires offertes par le fournisseur. Elle est ainsi susceptible d\'interférer avec le bon fonctionnement du GPS ou de toute autre source de localisation."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"connaître votre position précise (GPS et réseau)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"accéder à votre position précise (GPS et réseau)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Permet à l\'application d\'obtenir votre position exacte à l\'aide du récepteur satellite GPS ou des sources de localisation de réseau tels que les points d\'accès Wi-Fi et les antennes-relais. Ces services de localisation doivent être activés et disponibles sur votre appareil pour que l\'application puisse déterminer où vous vous trouvez, le cas échéant. Cette autorisation peut entraîner une utilisation accrue de la batterie."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"connaître votre position approximative (réseau)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"accéder à votre position approximative (selon le réseau)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Permet à l\'application d\'obtenir votre position approximative. Celle-ci est fournie par des services de localisation sur la base des sources de localisation de réseau tels que les points d\'accès Wi-Fi et les antennes-relais. Ces services de localisation doivent être activés et disponibles sur votre appareil pour que l\'application puisse déterminer où vous vous trouvez de façon approximative, le cas échéant."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"modifier vos paramètres audio"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Permet à l\'application de modifier les paramètres audio généraux, tels que le volume et la sortie audio utilisée."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"enregistrer des fichiers audio"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Permet à l\'application d\'enregistrer des contenus audio à l\'aide du microphone. Cette autorisation lui donne la possibilité d\'enregistrer du contenu audio à tout moment sans votre consentement."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"Communication avec la carte SIM"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"envoyer des commandes à la carte SIM"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Autoriser l\'envoi de commandes à la carte SIM via l\'application. Cette fonctionnalité est très risquée."</string>
<string name="permlab_camera" msgid="3616391919559751192">"prendre des photos et enregistrer des vidéos"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Permet à l\'application de prendre des photos et de filmer des vidéos avec l\'appareil photo. Cette autorisation lui permet d\'utiliser l\'appareil photo à tout moment sans votre consentement."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Permet à l\'application d\'obtenir la liste des comptes connus par le téléphone. Il peut s\'agir de n\'importe quel compte créé par les applications que vous avez installées."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"afficher les connexions réseau"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Permet à l\'application d\'accéder à des informations sur les connexions réseau, comme les réseaux existants et connectés."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"bénéficier d\'un accès complet au réseau"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"bénéficier d\'un accès complet au réseau"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Permet à l\'application de créer des sockets réseau et d\'utiliser des protocoles réseau personnalisés. Le navigateur et d\'autres applications permettent d\'envoyer des données sur Internet. Cette autorisation n\'est donc pas nécessaire pour envoyer des données sur Internet."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"modifier la connectivité réseau"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Permet à l\'application de modifier l\'état de la connectivité du réseau."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Permet à l\'application de configurer le téléphone Bluetooth local, d\'identifier des appareils distants et de les associer au téléphone."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"se connecter au réseau WiMAX et s\'en déconnecter"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Permet à l\'application de déterminer si le WiMAX est activé et d\'obtenir des informations sur tous les réseaux WiMAX connectés."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Modifier l\'état du WiMAX"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"modifier l\'état du WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Permet à l\'application de connecter la tablette aux réseaux WiMAX et de l\'en déconnecter."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Permet à l\'application de connecter le téléviseur et de le déconnecter des réseaux WiMAX."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Permet à l\'application de connecter le téléphone aux réseaux WiMAX et de l\'en déconnecter."</string>
@@ -420,7 +420,7 @@
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Autoriser l\'application à utiliser le matériel d\'empreintes digitales pour l\'authentification"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Empreinte numérique partiellement détectée. Veuillez réessayer."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Impossible de reconnaître l\'empreinte numérique. Veuillez réessayer."</string>
- <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Le capteur d\'empreintes numériques est sale. Veuillez le nettoyer, puis réessayer."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Le lecteur d\'empreintes numériques est sale. Veuillez le nettoyer, puis réessayer."</string>
<string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"Vous avez déplacé votre doigt trop rapidement. Veuillez réessayer."</string>
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Vous avez déplacé votre doigt trop lentement. Veuillez réessayer."</string>
<string-array name="fingerprint_acquired_vendor">
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Permettre à l\'application de modifier les paramètres de calibrage de l\'écran tactile. Ne devrait jamais être nécessaire pour les applications standards."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"accéder aux certificats de GDN"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Permettre à une application de fournir et d\'utiliser des certificats de GDN. Ne devrait jamais être nécessaire pour les applications standards."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Recevoir des informations sur l\'état du transfert Android Beam"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"recevoir des informations sur l\'état du transfert Android Beam"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Autoriser cette application à recevoir des informations sur les transferts Android Beam en cours"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"supprimer les certificats de GDN"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Permet à une application de supprimer les certificats de GDN. Ne devrait jamais être nécessaire pour les applications standards."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Le code PIN est erroné."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Pour déverrouiller le clavier, appuyez sur \"Menu\" puis sur 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Numéro d\'urgence"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Aucun service"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Aucun service"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Écran verrouillé"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Appuyez sur \"Menu\" pour déverrouiller le téléphone ou appeler un numéro d\'urgence"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Appuyez sur \"Menu\" pour déverrouiller le téléphone."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Veuillez consulter le guide utilisateur ou contacter le service client."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"La carte SIM est verrouillée."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Déblocage de la carte SIM..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%d</xliff:g> reprises.\n\nVeuillez réessayer dans <xliff:g id="NUMBER_1">%d</xliff:g> secondes."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Vous avez saisi un mot de passe incorrect <xliff:g id="NUMBER_0">%d</xliff:g> fois. \n\nVeuillez réessayer dans <xliff:g id="NUMBER_1">%d</xliff:g> secondes."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Vous avez saisi un code PIN incorrect <xliff:g id="NUMBER_0">%d</xliff:g> fois. \n\nVeuillez réessayer dans <xliff:g id="NUMBER_1">%d</xliff:g> secondes."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. Au bout de <xliff:g id="NUMBER_1">%d</xliff:g> échecs supplémentaires, vous devrez déverrouiller votre tablette à l\'aide de votre identifiant Google.\n\nVeuillez réessayer dans <xliff:g id="NUMBER_2">%d</xliff:g> secondes."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Cela fait <xliff:g id="NUMBER_0">%d</xliff:g> fois que votre tentative de déverrouillage du téléviseur échoue. Si vous échouez encore <xliff:g id="NUMBER_1">%d</xliff:g> fois, vous devrez déverrouiller le téléviseur en vous connectant à votre compte Google.\n\n Veuillez réessayer dans <xliff:g id="NUMBER_2">%d</xliff:g> secondes."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. Au bout de <xliff:g id="NUMBER_1">%d</xliff:g> échecs supplémentaires, vous devrez déverrouiller votre téléphone à l\'aide de votre identifiant Google.\n\nVeuillez réessayer dans <xliff:g id="NUMBER_2">%d</xliff:g> secondes."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Vous avez tenté de déverrouiller la tablette de façon incorrecte à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. Si vous échouez encore <xliff:g id="NUMBER_1">%d</xliff:g> fois, sa configuration d\'usine sera rétablie, et toutes les données utilisateur seront perdues."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Cela fait <xliff:g id="NUMBER_0">%d</xliff:g> fois que votre tentative de déverrouillage du téléviseur échoue. Si vous échouez encore <xliff:g id="NUMBER_1">%d</xliff:g> fois, la configuration d\'usine du téléviseur sera rétablie, et vous perdrez toutes les données utilisateur."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Vous avez tenté de déverrouiller le téléphone de façon incorrecte à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. Si vous échouez encore <xliff:g id="NUMBER_1">%d</xliff:g> fois, sa configuration d\'usine sera rétablie, et toutes les données utilisateur seront perdues."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises.\n\nVeuillez réessayer dans <xliff:g id="NUMBER_1">%2$d</xliff:g> secondes."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Vous avez saisi un mot de passe incorrect <xliff:g id="NUMBER_0">%1$d</xliff:g> fois. \n\nVeuillez réessayer dans <xliff:g id="NUMBER_1">%2$d</xliff:g> secondes."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Vous avez saisi un code PIN incorrect <xliff:g id="NUMBER_0">%1$d</xliff:g> fois. \n\nVeuillez réessayer dans <xliff:g id="NUMBER_1">%2$d</xliff:g> secondes."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises. Au bout de <xliff:g id="NUMBER_1">%2$d</xliff:g> échecs supplémentaires, vous devrez déverrouiller votre tablette à l\'aide de votre identifiant Google.\n\nVeuillez réessayer dans <xliff:g id="NUMBER_2">%3$d</xliff:g> secondes."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Cela fait <xliff:g id="NUMBER_0">%1$d</xliff:g> fois que votre tentative de déverrouillage du téléviseur échoue. Si vous échouez encore <xliff:g id="NUMBER_1">%2$d</xliff:g> fois, vous devrez déverrouiller le téléviseur en vous connectant à votre compte Google.\n\n Veuillez réessayer dans <xliff:g id="NUMBER_2">%3$d</xliff:g> secondes."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises. Au bout de <xliff:g id="NUMBER_1">%2$d</xliff:g> échecs supplémentaires, vous devrez déverrouiller votre téléphone à l\'aide de votre identifiant Google.\n\nVeuillez réessayer dans <xliff:g id="NUMBER_2">%3$d</xliff:g> secondes."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Vous avez tenté de déverrouiller la tablette de façon incorrecte à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises. Si vous échouez encore <xliff:g id="NUMBER_1">%2$d</xliff:g> fois, sa configuration d\'usine sera rétablie, et toutes les données utilisateur seront perdues."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Cela fait <xliff:g id="NUMBER_0">%1$d</xliff:g> fois que votre tentative de déverrouillage du téléviseur échoue. Si vous échouez encore <xliff:g id="NUMBER_1">%2$d</xliff:g> fois, la configuration d\'usine du téléviseur sera rétablie, et vous perdrez toutes les données utilisateur."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Vous avez tenté de déverrouiller le téléphone de façon incorrecte à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises. Si vous échouez encore <xliff:g id="NUMBER_1">%2$d</xliff:g> fois, sa configuration d\'usine sera rétablie, et toutes les données utilisateur seront perdues."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Vous avez tenté de déverrouiller la tablette de façon incorrecte à <xliff:g id="NUMBER">%d</xliff:g> reprises. Sa configuration d\'usine va être rétablie."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Cela fait <xliff:g id="NUMBER">%d</xliff:g> fois que votre tentative de déverrouillage du téléviseur échoue. Sa configuration d\'usine va être rétablie."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Vous avez tenté de déverrouiller le téléphone de façon incorrecte à <xliff:g id="NUMBER">%d</xliff:g> reprises. Sa configuration d\'usine va être rétablie."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"mot"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"lien"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"ligne"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Échec du test usine"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"L\'action FACTORY_TEST est uniquement prise en charge pour les paquets de données installés dans in/system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Impossible de trouver un paquet proposant l\'action FACTORY_TEST."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Formatage en cours…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Non inséré"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Aucune activité correspondante trouvée."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Diriger la sortie multimédia"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"diriger la sortie multimédia"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Permet à une application de diriger la sortie multimédia vers d\'autres appareils externes."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Accéder aux sessions d\'installation"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"accéder aux sessions d\'installation"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Permet à une application d\'accéder aux sessions d\'installation. Cela lui permet de consulter les détails relatifs à l\'installation des packages actifs."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Demander l\'installation de packages"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"demander l\'installation de packages"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Permet à une application de demander l\'installation de packages."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Appuyez deux fois pour régler le zoom."</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Impossible d\'ajouter le widget."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Nom d\'utilisateur ou mot de passe non valide."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Vous avez oublié votre nom d\'utilisateur ou votre mot de passe ?\nRendez-vous sur la page "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Vérification du compte en cours…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Vous avez saisi un code PIN incorrect à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. \n\nVeuillez réessayer dans <xliff:g id="NUMBER_1">%d</xliff:g> secondes."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Vous avez saisi un mot de passe incorrect à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. \n\nVeuillez réessayer dans <xliff:g id="NUMBER_1">%d</xliff:g> secondes."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%d</xliff:g> reprises.\n\nVeuillez réessayer dans <xliff:g id="NUMBER_1">%d</xliff:g> secondes."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Vous avez tenté de déverrouiller la tablette de façon incorrecte à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. Si vous échouez encore <xliff:g id="NUMBER_1">%d</xliff:g> fois, sa configuration d\'usine sera rétablie, et toutes les données utilisateur seront perdues."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Cela fait <xliff:g id="NUMBER_0">%d</xliff:g> fois que votre tentative de déverrouillage du téléviseur échoue. Si vous échouez encore <xliff:g id="NUMBER_1">%d</xliff:g> fois, la configuration d\'usine du téléviseur sera rétablie, et vous perdrez toutes les données utilisateur."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Vous avez tenté de déverrouiller le téléphone de façon incorrecte à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. Si vous échouez encore <xliff:g id="NUMBER_1">%d</xliff:g> fois, sa configuration d\'usine sera rétablie, et toutes les données utilisateur seront perdues."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Vous avez saisi un code PIN incorrect à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises. \n\nVeuillez réessayer dans <xliff:g id="NUMBER_1">%2$d</xliff:g> secondes."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Vous avez saisi un mot de passe incorrect à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises. \n\nVeuillez réessayer dans <xliff:g id="NUMBER_1">%2$d</xliff:g> secondes."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises.\n\nVeuillez réessayer dans <xliff:g id="NUMBER_1">%2$d</xliff:g> secondes."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Vous avez tenté de déverrouiller la tablette de façon incorrecte à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises. Si vous échouez encore <xliff:g id="NUMBER_1">%2$d</xliff:g> fois, sa configuration d\'usine sera rétablie, et toutes les données utilisateur seront perdues."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Cela fait <xliff:g id="NUMBER_0">%1$d</xliff:g> fois que votre tentative de déverrouillage du téléviseur échoue. Si vous échouez encore <xliff:g id="NUMBER_1">%2$d</xliff:g> fois, la configuration d\'usine du téléviseur sera rétablie, et vous perdrez toutes les données utilisateur."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Vous avez tenté de déverrouiller le téléphone de façon incorrecte à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises. Si vous échouez encore <xliff:g id="NUMBER_1">%2$d</xliff:g> fois, sa configuration d\'usine sera rétablie, et toutes les données utilisateur seront perdues."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Vous avez tenté de déverrouiller la tablette de façon incorrecte à <xliff:g id="NUMBER">%d</xliff:g> reprises. Sa configuration d\'usine va être rétablie."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Cela fait <xliff:g id="NUMBER">%d</xliff:g> fois que votre tentative de déverrouillage du téléviseur échoue. Sa configuration d\'usine va être rétablie."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Vous avez tenté de déverrouiller le téléphone de façon incorrecte à <xliff:g id="NUMBER">%d</xliff:g> reprises. Sa configuration d\'usine va être rétablie."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. Si vous échouez encore <xliff:g id="NUMBER_1">%d</xliff:g> fois, vous devrez déverrouiller votre tablette à l\'aide d\'un compte de messagerie électronique.\n\n Veuillez réessayer dans <xliff:g id="NUMBER_2">%d</xliff:g> secondes."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Cela fait <xliff:g id="NUMBER_0">%d</xliff:g> fois que votre tentative de déverrouillage du téléviseur échoue. Si vous échouez encore <xliff:g id="NUMBER_1">%d</xliff:g> fois, vous devrez déverrouiller le téléviseur à l\'aide d\'un compte de messagerie.\n\n Veuillez réessayer dans <xliff:g id="NUMBER_2">%d</xliff:g> secondes."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. Si vous échouez encore <xliff:g id="NUMBER_1">%d</xliff:g> fois, vous devrez déverrouiller votre téléphone à l\'aide d\'un compte de messagerie électronique.\n\n Veuillez réessayer dans <xliff:g id="NUMBER_2">%d</xliff:g> secondes."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises. Si vous échouez encore <xliff:g id="NUMBER_1">%2$d</xliff:g> fois, vous devrez déverrouiller votre tablette à l\'aide d\'un compte de messagerie électronique.\n\n Veuillez réessayer dans <xliff:g id="NUMBER_2">%3$d</xliff:g> secondes."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Cela fait <xliff:g id="NUMBER_0">%1$d</xliff:g> fois que votre tentative de déverrouillage du téléviseur échoue. Si vous échouez encore <xliff:g id="NUMBER_1">%2$d</xliff:g> fois, vous devrez déverrouiller le téléviseur à l\'aide d\'un compte de messagerie.\n\n Veuillez réessayer dans <xliff:g id="NUMBER_2">%3$d</xliff:g> secondes."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises. Si vous échouez encore <xliff:g id="NUMBER_1">%2$d</xliff:g> fois, vous devrez déverrouiller votre téléphone à l\'aide d\'un compte de messagerie électronique.\n\n Veuillez réessayer dans <xliff:g id="NUMBER_2">%3$d</xliff:g> secondes."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Supprimer"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Augmenter le volume au dessus du niveau recommandé ?\n\nL\'écoute prolongée à un volume élevé peut endommager vos facultés auditives."</string>
@@ -1470,6 +1468,7 @@
<item quantity="other">Pendant %d h</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Jusqu\'à <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Jusqu\'à <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (alarme suivante)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Jusqu\'à la désactivation"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Jusqu\'à ce que vous désactiviez la fonctionnalité \"Ne pas déranger\""</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-gl-rES-watch/strings.xml b/core/res/res/values-gl-rES-watch/strings.xml
index 95d6400..5e52823 100644
--- a/core/res/res/values-gl-rES-watch/strings.xml
+++ b/core/res/res/values-gl-rES-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Aplicación <xliff:g id="NUMBER_0">%1$d</xliff:g> de <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Sensores"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"acceder aos contactos"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"acceder á localización deste reloxo"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"acceder ao calendario"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"envíar e consultar mensaxes de SMS"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"acceder ás fotos, ao contido multimedia e aos ficheiros do teu reloxo"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"gravar audio"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"tirar fotos e gravar vídeos"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"facer e xestionar chamadas telefónicas"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"acceder aos datos do sensor sobre as túas constantes vitais"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"actuar como a barra de estado"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"acceder a sensores do corpo (como monitores de ritmo cardíaco)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"acceder á localización precisa (baseada no GPS e na rede)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"acceder á localización aproximada (baseada na rede)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"enviar comandos á SIM"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"ter acceso completo á rede"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"xestionar propietarios do perfil e do dispositivo"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"cambiar estado de WiMAX"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"recibir o estado das transferencias de Android Beam"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"dirixir saída multimedia"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"ler sesións de instalación"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"solicitar instalación de paquetes"</string>
</resources>
diff --git a/core/res/res/values-gl-rES/strings.xml b/core/res/res/values-gl-rES/strings.xml
index c32add0..ab24047 100644
--- a/core/res/res/values-gl-rES/strings.xml
+++ b/core/res/res/values-gl-rES/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Inclúe datos persoais como números e contrasinais de tarxetas de crédito."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"desactivar ou modificar a barra de estado"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Permite á aplicación desactivar a barra de estado ou engadir e eliminar as iconas do sistema."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"barra de estado"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"actuar como a barra de estado"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Permite á aplicación ser a barra de estado."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"ampliar/contraer a barra de estado"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Permite á aplicación ampliar ou contraer a barra de estado."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Permite á aplicación recibir e procesar mensaxes WAP. Este permiso inclúe a capacidade de supervisar ou eliminar mensaxes enviadas a ti sen mostrarchas."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"recuperar aplicacións en execución"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Permite á aplicación recuperar información acerca de tarefas que se están executando actualmente ou que se executaron recentemente. É posible que esta acción permita á aplicación descubrir información acerca de que aplicacións se utilizan no dispositivo."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Xestionar os propietarios do perfil e do dispositivo"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"xestionar propietarios do perfil e do dispositivo"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Permite ás aplicacións establecer os propietarios do perfil e o propietario do dispositivo."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"reordenar as aplicacións en execución"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Permite á aplicación mover tarefas ao primeiro e segundo plano. É posible que a aplicación leve a cabo esta acción sen a túa intervención."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Permite á aplicación modificar o rexistro de chamadas do tablet, incluídos os datos acerca de chamadas entrantes e saíntes. É posible que aplicacións maliciosas utilicen esta acción para borrar ou modificar o teu rexistro de chamadas."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Permite que a aplicación modifique o rexistro de chamadas da televisión, incluídos os datos sobre chamadas entrantes e saíntes. As aplicacións maliciosas poden utilizar este permiso para borrar ou modificar o rexistro de chamadas."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Permite á aplicación modificar o rexistro de chamadas do teléfono, incluídos os datos acerca de chamadas entrantes e saíntes. É posible que aplicacións maliciosas utilicen esta acción para borrar ou modificar o teu rexistro de chamadas."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"sensores de corpo (como monitores de ritmo cardíaco)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"acceder a sensores do corpo (como monitores de ritmo cardíaco)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Permite que a aplicación acceda aos datos dos sensores que controlan o teu estado físico, como o ritmo cardíaco."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"ler os eventos do calendario e a información confidencial"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Permite á aplicación ler todos os eventos do calendario que están almacenados no tablet, incluídos os pertencentes aos teus amigos ou compañeiros de traballo. É posible que esta acción permita á aplicación compartir ou gardar os datos do teu calendario, independentemente da confidencialidade."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Permite á aplicación engadir, eliminar e cambiar eventos que podes modificar no teu teléfono, incluídos os de amigos ou compañeiros de traballo. É posible que esta acción permita á aplicación enviar mensaxes que parecen proceder de propietarios de calendarios ou modificar eventos sen o coñecemento dos propietarios."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"acceder a comandos adicionais do provedor de situación"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Permite á aplicación acceder a comandos adicionais de fornecedor de localizacións. É posible que isto provoque que a aplicación interfira co funcionamento do GPS ou doutras fontes da localización."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"localización precisa (baseada en GPS e na rede)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"acceder á localización precisa (baseada no GPS e na rede)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Permite á aplicación obter a túa localización precisa co sistema de posicionamento global (GPS ou Global Positioning System) ou con fontes da localización de rede como as torres de telecomunicacións e a wifi. Estes servizos de localización deben estar activados e dispoñibles para o teu dispositivo para que a aplicación poida utilizalos. As aplicacións poden usar esta opción para determinar aproximadamente o lugar en que te atopas e é posible que consuman batería adicional."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"situación aproximada (baseada na rede)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"acceder á localización aproximada (baseada na rede)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Permite á aplicación obter a túa localización aproximada. Esta localización provén dos servizos de localización que utilizan fontes da localización de rede como as torres de telecomunicacións e a wifi. Estes servizos de localización deben estar activados e dispoñibles para o teu dispositivo para que a aplicación poida utilizalos. As aplicacións poden esta opción para determinar aproximadamente o lugar en que te atopas."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"cambiar a configuración de son"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Permite á aplicación modificar a configuración de audio global, como o volume e que altofalante se utiliza para a saída."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"gravar audio"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Permite á aplicación gravar audio co micrófono. Este permiso permite á aplicación gravar audio en calquera momento sen a túa confirmación."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"comunicación SIM"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"enviar comandos á SIM"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Permite á aplicación enviar comandos á SIM. Isto é moi perigoso."</string>
<string name="permlab_camera" msgid="3616391919559751192">"facer fotos e vídeos"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Permite á aplicación tomar imaxes e vídeos coa cámara. Con este permiso a aplicación pode utilizar a cámara en calquera momento sen a túa confirmación."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Permite á aplicación obter a lista de contas coñecidas polo teléfono. É posible que aquí se inclúan as contas creadas por aplicacións que tes instaladas."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"ver conexións de rede"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Permite á aplicación ver información acerca das conexións da rede, como que redes existen e cales están conectadas."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"acceso total á rede"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"ter acceso completo á rede"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Permite á aplicación crear sockets de rede e utilizar protocolos de rede personalizados. O navegador e outras aplicacións ofrecen medios para enviar datos a Internet, polo que non se require este permiso para enviar datos a Internet."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"cambiar a conectividade de rede"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Permite á aplicación cambiar o estado da conectividade de rede."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Permite á aplicación configurar o teléfono Bluetooth local e descubrir e sincronizar con dispositivos remotos."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"conectar e desconectar de WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Permite á aplicación determinar se WiMAX está activado e obter información acerca das redes WiMAX que están conectadas."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Cambiar estado de WiMAX"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"cambiar estado de WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Permite á aplicación conectar e desconectar o tablet de redes WiMAX."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Permite que a aplicación conecte ou desconecte a televisión de redes WiMAX."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Permite á aplicación conectar e desconectar o teléfono de redes WiMAX."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Permite á aplicación modificar os parámetros de calibración da pantalla táctil. As aplicacións normais non deberían necesitar este permiso."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"acceso a certificados DRM"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Permite a unha aplicación fornecer e utilizar certificados DRM. Non se deberían precisar nunca para as aplicacións normais."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Recibir estado das transferencias de Android Beam"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"recibir o estado das transferencias de Android Beam"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Permite a esta aplicación recibir información acerca das transferencias actuais de Android Beam"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"eliminar certificados DRM"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Permite a unha aplicación eliminar os certificados DRM. As aplicacións normais non o deberían precisar nunca."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Código PIN incorrecto"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Para desbloquear, preme Menú e, a continuación, 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Número de emerxencia"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Non hai servizo."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Sen servizo"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Pantalla bloqueada"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Preme Menú para desbloquear ou realizar unha chamada de emerxencia."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Preme Menú para desbloquear."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Consulta a guía do usuario ou ponte en contacto co servizo de asistencia ao cliente."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"A tarxeta SIM está bloqueada."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Desbloqueando tarxeta SIM…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Debuxaches incorrectamente o padrón de desbloqueo <xliff:g id="NUMBER_0">%d</xliff:g> veces. \n\nTéntao de novo en <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Introduciches o contrasinal incorrectamente <xliff:g id="NUMBER_0">%d</xliff:g> veces. \n\nTéntao de novo en <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Introduciches o PIN incorrectamente <xliff:g id="NUMBER_0">%d</xliff:g> veces. \n\nTéntao de novo en <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Debuxaches o padrón de desbloqueo <xliff:g id="NUMBER_0">%d</xliff:g> veces de forma incorrecta. Se realizas <xliff:g id="NUMBER_1">%d</xliff:g> intentos incorrectos máis, terás que desbloquear o tablet cos datos de inicio de sesión de Google.\n\n Téntao de novo en <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Debuxaches o padrón de desbloqueo <xliff:g id="NUMBER_0">%d</xliff:g> veces de forma incorrecta. Se realizas <xliff:g id="NUMBER_1">%d</xliff:g> intentos incorrectos máis, terás que desbloquear a televisión cos datos de inicio de sesión de Google.\n\n Téntao de novo en <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Debuxaches o padrón de desbloqueo <xliff:g id="NUMBER_0">%d</xliff:g> veces de forma incorrecta. Se realizas <xliff:g id="NUMBER_1">%d</xliff:g> intentos incorrectos máis, terás que desbloquear o teléfono cos datos de inicio de sesión de Google.\n\n Téntao de novo en <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Tentaches desbloquear o tablet <xliff:g id="NUMBER_0">%d</xliff:g> veces sen conseguilo. Se se realizan <xliff:g id="NUMBER_1">%d</xliff:g> intentos máis sen logralo, restablecerase a configuración de fábrica predeterminada e perderanse todos os datos de usuario."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Tentaches desbloquear a televisión <xliff:g id="NUMBER_0">%d</xliff:g> veces de forma incorrecta. Se realizas <xliff:g id="NUMBER_1">%d</xliff:g> intentos incorrectos máis, restableceranse os valores de fábrica do aparello e perderanse todos os datos de usuario."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Tentaches desbloquear o teléfono <xliff:g id="NUMBER_0">%d</xliff:g> veces sen conseguilo. Se se realizan <xliff:g id="NUMBER_1">%d</xliff:g> intentos máis sen logralo, restablecerase a configuración de fábrica predeterminada e perderanse todos os datos do usuario."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Debuxaches incorrectamente o padrón de desbloqueo <xliff:g id="NUMBER_0">%1$d</xliff:g> veces. \n\nTéntao de novo en <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Introduciches o contrasinal incorrectamente <xliff:g id="NUMBER_0">%1$d</xliff:g> veces. \n\nTéntao de novo en <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Introduciches o PIN incorrectamente <xliff:g id="NUMBER_0">%1$d</xliff:g> veces. \n\nTéntao de novo en <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Debuxaches o padrón de desbloqueo <xliff:g id="NUMBER_0">%1$d</xliff:g> veces de forma incorrecta. Se realizas <xliff:g id="NUMBER_1">%2$d</xliff:g> intentos incorrectos máis, terás que desbloquear o tablet cos datos de inicio de sesión de Google.\n\n Téntao de novo en <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Debuxaches o padrón de desbloqueo <xliff:g id="NUMBER_0">%1$d</xliff:g> veces de forma incorrecta. Se realizas <xliff:g id="NUMBER_1">%2$d</xliff:g> intentos incorrectos máis, terás que desbloquear a televisión cos datos de inicio de sesión de Google.\n\n Téntao de novo en <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Debuxaches o padrón de desbloqueo <xliff:g id="NUMBER_0">%1$d</xliff:g> veces de forma incorrecta. Se realizas <xliff:g id="NUMBER_1">%2$d</xliff:g> intentos incorrectos máis, terás que desbloquear o teléfono cos datos de inicio de sesión de Google.\n\n Téntao de novo en <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Tentaches desbloquear o tablet <xliff:g id="NUMBER_0">%1$d</xliff:g> veces sen conseguilo. Se se realizan <xliff:g id="NUMBER_1">%2$d</xliff:g> intentos máis sen logralo, restablecerase a configuración de fábrica predeterminada e perderanse todos os datos de usuario."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Tentaches desbloquear a televisión <xliff:g id="NUMBER_0">%1$d</xliff:g> veces de forma incorrecta. Se realizas <xliff:g id="NUMBER_1">%2$d</xliff:g> intentos incorrectos máis, restableceranse os valores de fábrica do aparello e perderanse todos os datos de usuario."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Tentaches desbloquear o teléfono <xliff:g id="NUMBER_0">%1$d</xliff:g> veces sen conseguilo. Se se realizan <xliff:g id="NUMBER_1">%2$d</xliff:g> intentos máis sen logralo, restablecerase a configuración de fábrica predeterminada e perderanse todos os datos do usuario."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Tentaches desbloquear o tablet <xliff:g id="NUMBER">%d</xliff:g> veces sen conseguilo. Restablecerase a configuración de fábrica predeterminada."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Tentaches desbloquear a televisión <xliff:g id="NUMBER">%d</xliff:g> veces de forma incorrecta. Agora restableceranse os valores de fábrica do aparello."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Tentaches desbloquear o teléfono <xliff:g id="NUMBER">%d</xliff:g> veces sen conseguilo. Restablecerase a configuración de fábrica predeterminada."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"palabra"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"ligazón"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"liña"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Produciuse un erro durante a proba de fábrica"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"A acción FACTORY_TEST só é compatible cos paquetes instalados en /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Non se atopou ningún paquete que forneza a acción FACTORY_TEST."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Formatando…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Non se inseriu"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Non se atoparon actividades que coincidan."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Dirixir saída multimedia"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"dirixir saída multimedia"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Permite a unha aplicación dirixir a saída multimedia a outros dispositivos externos."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Consultar sesións de instalación"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"ler sesións de instalación"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Permite que unha aplicación consulte as sesións de instalación. Desta forma, pode ver os detalles acerca das instalacións de paquetes activas."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Solicitar instalación dos paquetes"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"solicitar instalación de paquetes"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Permite a unha aplicación solicitar a instalación dos paquetes."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Toca dúas veces para controlar o zoom"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Non se puido engadir o widget."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"O nome de usuario ou o contrasinal non son válidos."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Esqueciches o teu nome de usuario ou contrasinal?\nVisita a páxina "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Comprobando a conta..."</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Introduciches o PIN incorrectamente <xliff:g id="NUMBER_0">%d</xliff:g> veces. \n\nTéntao de novo en <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Introduciches o contrasinal incorrectamente <xliff:g id="NUMBER_0">%d</xliff:g> veces. \n\nTéntao de novo en <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Debuxaches incorrectamente o padrón de desbloqueo <xliff:g id="NUMBER_0">%d</xliff:g> veces. \n\nTéntao de novo en <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Tentaches desbloquear o tablet <xliff:g id="NUMBER_0">%d</xliff:g> veces sen conseguilo. Se se realizan <xliff:g id="NUMBER_1">%d</xliff:g> intentos máis sen logralo, restablecerase á configuración de fábrica predeterminada e perderanse todos os datos do usuario."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Tentaches desbloquear a televisión <xliff:g id="NUMBER_0">%d</xliff:g> veces de forma incorrecta. Se realizas <xliff:g id="NUMBER_1">%d</xliff:g> intentos incorrectos máis, restableceranse os valores de fábrica do aparello e perderanse todos os datos de usuario."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Tentaches desbloquear o teléfono <xliff:g id="NUMBER_0">%d</xliff:g> veces sen conseguilo. Se se realizan <xliff:g id="NUMBER_1">%d</xliff:g> intentos máis sen logralo, restablecerase a configuración de fábrica predeterminada e perderanse todos os datos do usuario."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Introduciches o PIN incorrectamente <xliff:g id="NUMBER_0">%1$d</xliff:g> veces. \n\nTéntao de novo en <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Introduciches o contrasinal incorrectamente <xliff:g id="NUMBER_0">%1$d</xliff:g> veces. \n\nTéntao de novo en <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Debuxaches incorrectamente o padrón de desbloqueo <xliff:g id="NUMBER_0">%1$d</xliff:g> veces. \n\nTéntao de novo en <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Tentaches desbloquear o tablet <xliff:g id="NUMBER_0">%1$d</xliff:g> veces sen conseguilo. Se se realizan <xliff:g id="NUMBER_1">%2$d</xliff:g> intentos máis sen logralo, restablecerase á configuración de fábrica predeterminada e perderanse todos os datos do usuario."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Tentaches desbloquear a televisión <xliff:g id="NUMBER_0">%1$d</xliff:g> veces de forma incorrecta. Se realizas <xliff:g id="NUMBER_1">%2$d</xliff:g> intentos incorrectos máis, restableceranse os valores de fábrica do aparello e perderanse todos os datos de usuario."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Tentaches desbloquear o teléfono <xliff:g id="NUMBER_0">%1$d</xliff:g> veces sen conseguilo. Se se realizan <xliff:g id="NUMBER_1">%2$d</xliff:g> intentos máis sen logralo, restablecerase a configuración de fábrica predeterminada e perderanse todos os datos do usuario."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Tentouse desbloquear o tablet <xliff:g id="NUMBER">%d</xliff:g> veces sen conseguilo. Agora, restablecerase á configuración de fábrica predeterminada."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Tentaches desbloquear a televisión <xliff:g id="NUMBER">%d</xliff:g> veces de forma incorrecta. Agora restableceranse os valores de fábrica do aparello."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Tentaches desbloquear o teléfono <xliff:g id="NUMBER">%d</xliff:g> veces sen conseguilo. Agora, restablecerase á configuración de fábrica predeterminada."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Debuxaches o padrón de desbloqueo incorrectamente <xliff:g id="NUMBER_0">%d</xliff:g> veces. Se realizas <xliff:g id="NUMBER_1">%d</xliff:g> intentos incorrectos máis, terás que desbloquear o tablet a través dunha unha conta de correo electrónico.\n\n Téntao de novo dentro de <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Debuxaches o padrón de desbloqueo <xliff:g id="NUMBER_0">%d</xliff:g> veces de forma incorrecta. Se realizas <xliff:g id="NUMBER_1">%d</xliff:g> intentos incorrectos máis, terás que desbloquear a televisión cunha conta de correo electrónico.\n\n Téntao de novo en <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Debuxaches o padrón de desbloqueo incorrectamente <xliff:g id="NUMBER_0">%d</xliff:g> veces. Se realizas <xliff:g id="NUMBER_1">%d</xliff:g> intentos incorrectos máis, terás que desbloquear o teléfono a través dunha conta de correo electrónico.\n\n Téntao de novo dentro de <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Debuxaches o padrón de desbloqueo incorrectamente <xliff:g id="NUMBER_0">%1$d</xliff:g> veces. Se realizas <xliff:g id="NUMBER_1">%2$d</xliff:g> intentos incorrectos máis, terás que desbloquear o tablet a través dunha unha conta de correo electrónico.\n\n Téntao de novo dentro de <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Debuxaches o padrón de desbloqueo <xliff:g id="NUMBER_0">%1$d</xliff:g> veces de forma incorrecta. Se realizas <xliff:g id="NUMBER_1">%2$d</xliff:g> intentos incorrectos máis, terás que desbloquear a televisión cunha conta de correo electrónico.\n\n Téntao de novo en <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Debuxaches o padrón de desbloqueo incorrectamente <xliff:g id="NUMBER_0">%1$d</xliff:g> veces. Se realizas <xliff:g id="NUMBER_1">%2$d</xliff:g> intentos incorrectos máis, terás que desbloquear o teléfono a través dunha conta de correo electrónico.\n\n Téntao de novo dentro de <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Eliminar"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Queres subir o volume máis do nivel recomendado?\n\nA reprodución de son a un volume elevado durante moito tempo pode provocar danos nos oídos."</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">Durante unha h</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Ata as <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Ata as <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (próxima alarma)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Ata que desactives isto"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Ata que desactives o modo Non molestar"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-gu-rIN-watch/strings.xml b/core/res/res/values-gu-rIN-watch/strings.xml
index af1b068..6320fcc 100644
--- a/core/res/res/values-gu-rIN-watch/strings.xml
+++ b/core/res/res/values-gu-rIN-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_1">%2$d</xliff:g> માંથી <xliff:g id="NUMBER_0">%1$d</xliff:g> એપ્લિકેશન."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"સેન્સર્સ"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"તમારા સંપર્કોને ઍક્સેસ કરો"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"આ ઘડિયાળના સ્થાનને ઍક્સેસ કરો"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"તમારા કેલેન્ડરને ઍક્સેસ કરો"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"SMS સંદેશા મોકલો અને જુઓ"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"તમારી ઘડિયાળ પર ફોટા, મીડિયા અને ફાઇલો ઍક્સેસ કરો"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"ઑડિઓ રેકોર્ડ કરો"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"ચિત્રો લો અને વિડિઓ રેકોર્ડ કરો"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"ફોન કૉલ્સ કરો તથા સંચાલિત કરો"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"તમારા મહત્વપૂર્ણ ચિહ્નો વિશે સેન્સર ડેટા ઍક્સેસ કરો"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"સ્થિતિ બાર થાઓ"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"બૉડી સેન્સર્સ ઍક્સેસ કરો (જેમ કે હાર્ટ રેટ મૉનિટર્સ)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"નિશ્ચિત સ્થાન ઍક્સેસ કરો (GPS અને નેટવર્ક-આધારિત)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"અંદાજિત સ્થાન (નેટવર્ક-આધારિત)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"SIM ને આદેશો મોકલો"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"પૂર્ણ નેટવર્ક ઍક્સેસ મેળવો"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"પ્રોફાઇલ અને ઉપકરણ માલિકોને સંચાલિત કરો"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"WiMAX સ્થિતિ બદલો"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"Android બીમ ટ્રાન્સફર સ્થિતિ પ્રાપ્ત કરો"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"મીડિયા આઉટપુટ રૂટ કરો"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"ઇન્સ્ટોલ સત્રો વાંચો"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"પૅકેજેસ ઇન્સ્ટોલ કરવાની વિનંતી કરો"</string>
</resources>
diff --git a/core/res/res/values-gu-rIN/strings.xml b/core/res/res/values-gu-rIN/strings.xml
index 6281abf..93faa70 100644
--- a/core/res/res/values-gu-rIN/strings.xml
+++ b/core/res/res/values-gu-rIN/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ક્રેડિટ કાર્ડ નંબર્સ અને પાસવર્ડ્સ જેવો વ્યક્તિગત ડેટા શામેલ છે."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"સ્થિતિ બાર અક્ષમ કરો અથવા સંશોધિત કરો"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"એપ્લિકેશનને સ્થિતિ બાર અક્ષમ કરવાની અથવા સિસ્ટમ આયકન્સ ઉમેરવા અને દૂર કરવાની મંજૂરી આપે છે."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"સ્થિતિ બાર"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"સ્થિતિ બાર થાઓ"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"એપ્લિકેશનને સ્થિતિ બાર થવાની મંજૂરી આપે છે."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"સ્થિતિ બાર વિસ્તૃત કરો/સંકુકિત કરો"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"એપ્લિકેશનને સ્થિતિ બાર વિસ્તૃત કરવાની અને સંકુચિત કરવાની મંજૂરી આપે છે."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"એપ્લિકેશનને WAP સંદેશા પ્રાપ્ત કરવાની અને તેના પર પ્રક્રિયા કરવાની મંજૂરી આપે છે. આ પરવાનગીમાં તમને દર્શાવ્યા વિના તમને મોકલેલ સંદેશાઓનું નિરીક્ષણ કરવાની અને કાઢી નાખવાની ક્ષમતાનો સમાવેશ થાય છે."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"ચાલુ એપ્લિકેશનો પુનઃપ્રાપ્ત કરો"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"એપ્લિકેશનને વર્તમાનમાં અને તાજેતરમાં ચાલી રહેલ કાર્યો વિશેની વિગતવાર માહિતી પુનઃપ્રાપ્ત કરવાની મંજૂરી આપે છે. આ એપ્લિકેશનને ઉપકરણ પર કઈ એપ્લિકેશન્સનો ઉપયોગ થાય છે તેના વિશેની માહિતી શોધવાની મંજૂરી આપી શકે છે."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"પ્રોફાઇલ અને ઉપકરણ માલિકોને સંચાલિત કરો"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"પ્રોફાઇલ અને ઉપકરણ માલિકોને સંચાલિત કરો"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"એપ્લિકેશન્સને પ્રોફાઇલ માલિકો અને ઉપકરણ માલિકો સેટ કરવાની મંજૂરી આપે છે."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"ચાલુ એપ્લિકેશન્સને ફરી ગોઠવો"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"એપ્લિકેશનને અગ્રભૂમિ અને પૃષ્ટભૂમિમાં કાર્યો ખસેડવાની મંજૂરી આપે છે. તમારા ઇનપુટ વિના એપ્લિકેશન આ કરી શકે છે."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"એપ્લિકેશનને ઇનકમિંગ અને આઉટગોઇંગ કૉલ્સ વિશેનાં ડેટા સહિત, તમારા ટેબ્લેટના કૉલ લૉગને સંશોધિત કરવાની મંજૂરી આપે છે. દુર્ભાવનાપૂર્ણ એપ્લિકેશનો આનો ઉપયોગ તમારા કૉલ લૉગને કાઢી નાખવા અથવા સંશોધિત માટે કરી શકે છે."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"એપ્લિકેશનને ઇનકમિંગ અને આઉટગોઇંગ કૉલ્સ વિશેનાં ડેટા સહિત, તમારા TV ના કૉલ લૉગને સંશોધિત કરવાની મંજૂરી આપે છે. દુર્ભાવનાપૂર્ણ એપ્લિકેશનો આનો ઉપયોગ તમારા કૉલ લૉગને કાઢી નાખવા અથવા સંશોધિત માટે કરી શકે છે."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"એપ્લિકેશનને ઇનકમિંગ અને આઉટગોઇંગ કૉલ્સ વિશેનાં ડેટા સહિત, તમારા ફોનના કૉલ લૉગને સંશોધિત કરવાની મંજૂરી આપે છે. દુર્ભાવનાપૂર્ણ એપ્લિકેશનો આનો ઉપયોગ તમારા કૉલ લૉગને કાઢી નાખવા અથવા સંશોધિત માટે કરી શકે છે."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"બૉડી સેન્સર્સ (જેમ કે હાર્ટ રેટ મૉનિટર્સ)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"બૉડીસેન્સર્સ ઍક્સેસ(જેમકે હ્રદય ગતી મૉનિટર)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"એપ્લિકેશનને તમારી હૃદય ગતિ જેવી તમારી શારીરિક સ્થિતિને મૉનિટર કરતાં સેન્સર્સથી ડેટા ઍક્સેસ કરવાની મંજૂરી આપે છે."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"કેલેન્ડર ઇવેન્ટ્સ વત્તા ગોપનીયતા માહિતી વાંચો"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ટેબ્લેટ પર સંગ્રહિત તમામ કેલેન્ડર ઇવેન્ટ્સ વાંચવાની મંજૂરી આપે છે. આ એપ્લિકેશનને તમારા કેલેન્ડર ડેટાને શેર કરવા કે સાચવવાની મંજૂરી આપી શકે છે, પછી ભલે ગોપનીયતા અથવા સંવેદિતા કોઈપણ હોય."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ફોન પર તમે સંશોધિત કરી શકો તે ઇવેન્ટ્સ ઉમેરવા, દૂર કરવા, બદલવાની મંજૂરી આપે છે. આ એપ્લિકેશનને કેલેન્ડર માલિક તરફથી આવતાં હોય તેવા સંદેશા મોકલવાની અથવા માલિકની જાણ વિના ઇવેન્ટ્સ સંશોધિત કરવાની મંજૂરી આપી શકે છે."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"વધારાના સ્થાન પ્રદાતા આદેશોને ઍક્સેસ કરો"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"એપ્લિકેશનને વધારાના સ્થાન પ્રદાતા આદેશોને ઍક્સેસ કરવાની મંજૂરી આપે છે. આ એપ્લિકેશનને GPS અથવા અન્ય સ્થાન સ્રોતોના ઓપરેશનમાં દખલ કરવાની મંજૂરી આપી શકે છે."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"નિશ્ચિત સ્થાન (GPS અને નેટવર્ક-આધારિત)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"નિશ્ચિત સ્થાન ઍક્સેસ કરો (GPS અને નેટવર્ક-આધારિત)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"એપ્લિકેશનને ગ્લોબલ પોઝિશનિંગ સિસ્ટમ (GPS) અથવા સ્થાન સેલ ટાવર્સ અને Wi-Fi જેવા નેટવર્ક સ્થાન સ્રોતોનો ઉપયોગ કરીને તમારું ચોક્કસ સ્થાન મેળવવાની મંજૂરી આપે છે. એપ્લિકેશન દ્વારા તેમનો ઉપયોગ કરવા માટે તમારા ઉપકરણ પર આ સ્થાન સેવાઓ ચાલુ અને ઉપલબ્ધ હોવી આવશ્યક છે. એપ્લિકેશનો તમે ક્યાં છો તે નિર્ધારિત કરવા માટે આનો ઉપયોગ કરી શકે છે અને અતિરિક્ત બૅટરી પાવરનો ઉપયોગ કરી શકે છે."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"અંદાજિત સ્થાન (નેટવર્ક-આધારિત)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"અંદાજિત સ્થાન ઍક્સેસ કરો (નેટવર્ક-આધારિત)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"એપ્લિકેશનને તમારું અંદાજિત સ્થાન મેળવવાની મંજૂરી આપે છે. આ સ્થાન સેલ ટાવર્સ અને Wi-Fi જેવા નેટવર્ક સ્થાન સ્રોતોનો ઉપયોગ કરીને સ્થાન સેવાઓ દ્વારા મેળવવામાં આવે છે. એપ્લિકેશન દ્વારા તેમનો ઉપયોગ કરવા માટે તમારા ઉપકરણ પર આ સ્થાન સેવાઓ ચાલુ અને ઉપલબ્ધ હોવી આવશ્યક છે. એપ્લિકેશનો તમે અંદાજે ક્યાં છો તે નિર્ધારિત કરવા માટે આનો ઉપયોગ કરી શકે છે."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"તમારી ઑડિઓ સેટિંગ્સ બદલો"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"એપ્લિકેશનને વૈશ્વિક ઑડિઓ સેટિંગ્સને સંશોધિત કરવાની મંજૂરી આપે છે, જેમ કે વોલ્યુમ અને આઉટપુટ માટે કયા સ્પીકરનો ઉપયોગ કરવો."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"ઑડિઓ રેકોર્ડ કરો"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"એપ્લિકેશનને માઇક્રોફોન વડે ઑડિઓ રેકોર્ડ કરવાની મંજૂરી આપે છે. આ પરવાનગી એપ્લિકેશનને તમારી પુષ્ટિ વિના કોઈપણ સમયે ઑડિઓ રેકોર્ડ કરવાની મંજૂરી આપે છે."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"sim સંચાર"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"SIM ને આદેશો મોકલો"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"એપ્લિકેશનને SIM પરા આદેશો મોકલવાની મંજૂરી આપે છે. આ ખૂબ જ ખતરનાક છે."</string>
<string name="permlab_camera" msgid="3616391919559751192">"ચિત્રો અને વિડિઓઝ લો"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"એપ્લિકેશનને કૅમેરા વડે ચિત્રો અને વિડિઓઝ લેવાની મંજૂરી આપે છે. આ પરવાનગી એપ્લિકેશનને તમારી પુષ્ટિ વિના કોઈપણ સમયે કૅમેરાના ઉપયોગની મંજૂરી આપે છે."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"એપ્લિકેશનને ફોન દ્વારા પરિચિત એકાઉન્ટ્સની સૂચિ મેળવવાની મંજૂરી આપે છે. આમાં તમે ઇન્સ્ટોલ કરેલ એપ્લિકેશનો દ્વારા બનાવેલ કોઈપણ એકાઉન્ટ્સ શામેલ હોઈ શકે છે."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"નેટવર્ક કનેક્શન્સ જુઓ"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"એપ્લિકેશનને નેટવર્ક કનેક્શન્સ વિશેની માહિતી જોવાની મંજૂરી આપે છે જેમ કે કયા નેટવર્ક્સ અસ્તિત્વમાં છે અને કનેક્ટ થયેલ છે."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"પૂર્ણ નેટવર્ક ઍક્સેસ"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"પૂર્ણ નેટવર્ક ઍક્સેસ મેળવો"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"એપ્લિકેશનને નેટવર્ક સૉકેટ્સ બનાવવાની અને કસ્ટમ નેટવર્ક પ્રોટોકોલ્સના ઉપયોગની મંજૂરી આપે છે. બ્રાઉઝર અને એપ્લિકેશનો ઇન્ટરનેટ પર ડેટા મોકલવાના સાધનો પૂરા પાડે છે, તેથી ઇન્ટરનેટ પર ડેટા મોકલવા માટે આ પરવાનગી જરૂરી નથી."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"નેટવર્ક કનેક્ટિવિટી બદલો"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"એપ્લિકેશનને નેટવર્ક કનેક્ટિવિટીની સ્થિતિ બદલવાની મંજૂરી આપે છે."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"એપ્લિકેશનને સ્થાનિક Bluetooth ફોન ગોઠવવાની અને રિમોટ ઉપકરણો શોધવા અને તેમની સાથે જોડી કરવાની મંજૂરી આપે છે."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"WiMAX થી કનેક્ટ અને ડિસ્કનેક્ટ કરો"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"એપ્લિકેશનને WiMAX સક્ષમ છે કે કેમ અને કનેક્ટ થયેલ છે તે કોઈપણ WiMAX નેટવર્ક્સ વિશેની માહિતી નિર્ધારિત કરવાની મંજૂરી આપે છે."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX સ્થિતિ બદલો"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"WiMAX સ્થિતિ બદલો"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"ટેબ્લેટને WiMAX નેટવર્ક્સ પર કનેક્ટ કરવાની અને ટેબ્લેટને તેનાથી ડિસ્કનેક્ટ કરવાની મંજૂરી એપ્લિકેશનને આપે છે."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"ટીવીને WiMAX નેટવર્ક્સ પર કનેક્ટ કરવાની અને ટીવીને તેનાથી ડિસ્કનેક્ટ કરવાની મંજૂરી એપ્લિકેશનને આપે છે."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"ફોનને WiMAX નેટવર્ક્સ પર કનેક્ટ કરવાની અને ફોનને તેનાથી ડિસ્કનેક્ટ કરવાની મંજૂરી એપ્લિકેશનને આપે છે."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"એપ્લિકેશનને ટચ સ્ક્રીનના કેલિબ્રેશન પેરામીટર્સને સંશોધિત કરવાની મંજૂરી આપે છે. સામાન્ય એપ્લિકેશનો માટે ક્યારેય જરૂરી હોતું નથી."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"DRM પ્રમાણપત્રોને ઍક્સેસ કરો"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"એપ્લિકેશનને DRM પ્રમાણપત્રોની જોગવાઈ કરવાની અને તેનો ઉપયોગ કરવાની મંજૂરી આપે છે. સામાન્ય એપ્લિકેશનો માટે ક્યારેય જરૂરી હોતું નથી."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Android બીમ ટ્રાન્સફર સ્થિતિ પ્રાપ્ત કરો"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"Android બીમ ટ્રાન્સફર સ્થિતિ પ્રાપ્ત કરો"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"એપ્લિકેશનને Android બીમ ટ્રાંસ્ફર્સ વિશે માહિતી પ્રાપ્ત કરવાની મંજૂરી આપે છે"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"DRM પ્રમાણપત્રો દૂર કરો"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"એપ્લિકેશનને DRM પ્રમાણપત્રો દૂર કરવાની મંજૂરી આપે છે. સામાન્ય એપ્લિકેશનો માટે ક્યારેય જરૂરી હોતું નથી."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"ખોટો PIN કોડ."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"અનલૉક કરવા માટે, મેનૂ દબાવો તે પછી 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"ઇમરજન્સિ નંબર"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"કોઈ સેવા નથી."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"કોઈ સેવા નથી"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"સ્ક્રીન લૉક કરી."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"અનલૉક કરવા માટે અથવા કટોકટીનો કૉલ કરવા માટે મેનૂ દબાવો."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"અનલૉક કરવા માટે મેનૂ દબાવો."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"વપરાશકર્તા માર્ગદર્શિકા જુઓ અથવા ગ્રાહક સંભાળનો સંપર્ક કરો."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM કાર્ડ લૉક કરેલ છે."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"SIM કાર્ડ અનલૉક કરી રહ્યાં છે…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"તમે <xliff:g id="NUMBER_0">%d</xliff:g> વખત ખોટી રીતે તમારી અનલૉક પેટર્ન દોરી. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> સેકંડમાં ફરીથી પ્રયાસ કરો."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"તમે <xliff:g id="NUMBER_0">%d</xliff:g> વખત ખોટી રીતે તમારો પાસવર્ડ લખ્યો છે. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> સેકંડમાં ફરીથી પ્રયાસ કરો."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"તમે <xliff:g id="NUMBER_0">%d</xliff:g> વખત ખોટી રીતે તમારો PIN લખ્યો છે. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> સેકંડમાં ફરીથી પ્રયાસ કરો."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"તમે <xliff:g id="NUMBER_0">%d</xliff:g> વખત ખોટી રીતે અનલૉક પેટર્ન દોરી છે. વધુ <xliff:g id="NUMBER_1">%d</xliff:g> વખત અસફળ પ્રયાસ પછી, તમને તમારા Google સાઇન ઇનનો ઉપયોગ કરીને તમારા ટેબ્લેટને અનલૉક કરવા માટે કહેવામાં આવશે.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> સેકંડમાં ફરી પ્રયાસ કરો."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"તમે <xliff:g id="NUMBER_0">%d</xliff:g> વખત ખોટી રીતે અનલૉક પેટર્ન દોરી છે. વધુ <xliff:g id="NUMBER_1">%d</xliff:g> વખત અસફળ પ્રયાસ પછી, તમને તમારા Google સાઇન ઇનનો ઉપયોગ કરીને તમારા ટીવીને અનલૉક કરવા માટે કહેવામાં આવશે.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> સેકંડમાં ફરી પ્રયાસ કરો."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"તમે <xliff:g id="NUMBER_0">%d</xliff:g> વખત ખોટી રીતે અનલૉક પેટર્ન દોરી છે. વધુ <xliff:g id="NUMBER_1">%d</xliff:g> વખત અસફળ પ્રયાસ પછી, તમને તમારા Google સાઇન ઇનનો ઉપયોગ કરીને તમારા ફોનને અનલૉક કરવા માટે કહેવામાં આવશે.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> સેકંડમાં ફરી પ્રયાસ કરો."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"તમે ટેબ્લેટને અનલૉક કરવા માટે <xliff:g id="NUMBER_0">%d</xliff:g> વખત ખોટી રીતે પ્રયાસ કર્યો. <xliff:g id="NUMBER_1">%d</xliff:g> વધુ અસફળ પ્રયાસ પછી, ટેબ્લેટને ફેક્ટરી ડિફોલ્ટ પર ફરીથી સેટ કરવામાં આવશે અને તેનો તમામ ડેટા કાઢી નાખવામાં આવશે."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"તમે ટીવીને અનલૉક કરવા માટે <xliff:g id="NUMBER_0">%d</xliff:g> વખત ખોટી રીતે પ્રયાસ કર્યો. <xliff:g id="NUMBER_1">%d</xliff:g> વધુ અસફળ પ્રયાસ પછી, ટીવીને ફેક્ટરી ડિફોલ્ટ પર ફરીથી સેટ કરવામાં આવશે અને તેનો તમામ ડેટા કાઢી નાખવામાં આવશે."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"તમે ફોનને અનલૉક કરવા માટે <xliff:g id="NUMBER_0">%d</xliff:g> વખત ખોટી રીતે પ્રયાસ કર્યો. <xliff:g id="NUMBER_1">%d</xliff:g> વધુ અસફળ પ્રયાસો પછી, ફોનને ફેક્ટરી ડિફોલ્ટ પર ફરીથી સેટ કરવામાં આવશે અને તેનો તમામ ડેટા કાઢી નાખવામાં આવશે."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"તમે <xliff:g id="NUMBER_0">%1$d</xliff:g> વખત ખોટી રીતે તમારી અનલૉક પેટર્ન દોરી. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> સેકંડમાં ફરીથી પ્રયાસ કરો."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"તમે <xliff:g id="NUMBER_0">%1$d</xliff:g> વખત ખોટી રીતે તમારો પાસવર્ડ લખ્યો છે. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> સેકંડમાં ફરીથી પ્રયાસ કરો."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"તમે <xliff:g id="NUMBER_0">%1$d</xliff:g> વખત ખોટી રીતે તમારો PIN લખ્યો છે. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> સેકંડમાં ફરીથી પ્રયાસ કરો."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"તમે <xliff:g id="NUMBER_0">%1$d</xliff:g> વખત ખોટી રીતે અનલૉક પેટર્ન દોરી છે. વધુ <xliff:g id="NUMBER_1">%2$d</xliff:g> વખત અસફળ પ્રયાસ પછી, તમને તમારા Google સાઇન ઇનનો ઉપયોગ કરીને તમારા ટેબ્લેટને અનલૉક કરવા માટે કહેવામાં આવશે.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> સેકંડમાં ફરી પ્રયાસ કરો."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"તમે <xliff:g id="NUMBER_0">%1$d</xliff:g> વખત ખોટી રીતે અનલૉક પેટર્ન દોરી છે. વધુ <xliff:g id="NUMBER_1">%2$d</xliff:g> વખત અસફળ પ્રયાસ પછી, તમને તમારા Google સાઇન ઇનનો ઉપયોગ કરીને તમારા ટીવીને અનલૉક કરવા માટે કહેવામાં આવશે.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> સેકંડમાં ફરી પ્રયાસ કરો."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"તમે <xliff:g id="NUMBER_0">%1$d</xliff:g> વખત ખોટી રીતે અનલૉક પેટર્ન દોરી છે. વધુ <xliff:g id="NUMBER_1">%2$d</xliff:g> વખત અસફળ પ્રયાસ પછી, તમને તમારા Google સાઇન ઇનનો ઉપયોગ કરીને તમારા ફોનને અનલૉક કરવા માટે કહેવામાં આવશે.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> સેકંડમાં ફરી પ્રયાસ કરો."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"તમે ટેબ્લેટને અનલૉક કરવા માટે <xliff:g id="NUMBER_0">%1$d</xliff:g> વખત ખોટી રીતે પ્રયાસ કર્યો. <xliff:g id="NUMBER_1">%2$d</xliff:g> વધુ અસફળ પ્રયાસ પછી, ટેબ્લેટને ફેક્ટરી ડિફોલ્ટ પર ફરીથી સેટ કરવામાં આવશે અને તેનો તમામ ડેટા કાઢી નાખવામાં આવશે."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"તમે ટીવીને અનલૉક કરવા માટે <xliff:g id="NUMBER_0">%1$d</xliff:g> વખત ખોટી રીતે પ્રયાસ કર્યો. <xliff:g id="NUMBER_1">%2$d</xliff:g> વધુ અસફળ પ્રયાસ પછી, ટીવીને ફેક્ટરી ડિફોલ્ટ પર ફરીથી સેટ કરવામાં આવશે અને તેનો તમામ ડેટા કાઢી નાખવામાં આવશે."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"તમે ફોનને અનલૉક કરવા માટે <xliff:g id="NUMBER_0">%1$d</xliff:g> વખત ખોટી રીતે પ્રયાસ કર્યો. <xliff:g id="NUMBER_1">%2$d</xliff:g> વધુ અસફળ પ્રયાસો પછી, ફોનને ફેક્ટરી ડિફોલ્ટ પર ફરીથી સેટ કરવામાં આવશે અને તેનો તમામ ડેટા કાઢી નાખવામાં આવશે."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"તમે <xliff:g id="NUMBER">%d</xliff:g> વખત ખોટી રીતે ટેબ્લેટને અનલૉક કરવાનો પ્રયાસ કર્યો. ટેબ્લેટ હવે ફેક્ટરી ડિફોલ્ટ પર ફરીથી સેટ કરવામાં આવશે."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"તમે <xliff:g id="NUMBER">%d</xliff:g> વખત ખોટી રીતે ટીવીને અનલૉક કરવાનો પ્રયાસ કર્યો. ટીવી હવે ફેક્ટરી ડિફોલ્ટ પર ફરીથી સેટ કરવામાં આવશે."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"તમે <xliff:g id="NUMBER">%d</xliff:g> વખત ખોટી રીતે ફોનને અનલૉક કરવાનો પ્રયાસ કર્યો. ફોન હવે ફેક્ટરી ડિફોલ્ટ પર ફરીથી સેટ કરવામાં આવશે."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"શબ્દ"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"લિંક કરો"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"લાઇન"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"ફેક્ટરી પરીક્ષણ નિષ્ફળ થયું"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"FACTORY_TEST ક્રિયા ફક્ત /system/app માં ઇન્સ્ટોલ કરેલા પૅકેજીસ માટે જ સમર્થિત છે."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"FACTORY_TEST ક્રિયા પ્રદાન કરનાર કોઈ પૅકેજ મળ્યું નહોતું."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"ફોર્મેટ કરી રહ્યાં છે..."</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"શામેલ નથી"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"કોઈ મેળ ખાતી પ્રવૃત્તિઓ મળી નથી."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"મીડિયા આઉટપુટ રૂટ કરો"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"મીડિયા આઉટપુટ રૂટ કરો"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"એપ્લિકેશનને અન્ય બાહ્ય ઉપકરણો પર મીડિયા આઉટપુટને રૂટ કરવની મંજૂરી આપે છે."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"ઇન્સ્ટોલ સત્રો વાંચો"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"ઇન્સ્ટૉલ સત્રો વાંચો"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"એપ્લિકેશનને ઇન્સ્ટોલ સત્રોને વાંચવાની મંજૂરી આપે છે. આ તેને સક્રિય પૅકેજ ઇન્સ્ટોલેશન્સ વિશે વિગતો જોવાની મંજૂરી આપે છે."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"પૅકેજેસ ઇન્સ્ટોલ કરવાની વિનંતી કરો"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"પૅકેજેસ ઇન્સ્ટૉલ કરવાની વિનંતી કરો"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"એપ્લિકેશનને પૅકેજેસના ઇન્સ્ટોલેશનની વિનંતી કરવાની મંજૂરી આપો."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"ઝૂમ નિયંત્રણ માટે બેવાર ટચ કરો"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"વિજેટ ઉમેરી શકાયું નથી."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"અમાન્ય વપરાશકર્તાનામ અથવા પાસવર્ડ"</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"તમારું વપરાશકર્તાનામ અથવા પાસવર્ડ ભૂલાઈ ગયો?\n "<b>"google.com/accounts/recovery"</b>" ની મુલાકાત લો."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"એકાઉન્ટ તપાસી રહ્યું છે…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"તમે <xliff:g id="NUMBER_0">%d</xliff:g> વખત ખોટી રીતે તમારો PIN લખ્યો છે. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> સેકંડમાં ફરીથી પ્રયાસ કરો."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"તમે <xliff:g id="NUMBER_0">%d</xliff:g> વખત ખોટી રીતે તમારો પાસવર્ડ લખ્યો છે. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> સેકંડમાં ફરીથી પ્રયાસ કરો."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"તમે <xliff:g id="NUMBER_0">%d</xliff:g> વખત ખોટી રીતે તમારી અનલૉક પેટર્ન દોરી. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> સેકંડમાં ફરીથી પ્રયાસ કરો."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"તમે ટેબ્લેટને અનલૉક કરવા માટે <xliff:g id="NUMBER_0">%d</xliff:g> વખત ખોટી રીતે પ્રયાસ કર્યો. <xliff:g id="NUMBER_1">%d</xliff:g> વધુ અસફળ પ્રયાસ પછી, ટેબ્લેટને ફેક્ટરી ડિફોલ્ટ પર ફરીથી સેટ કરવામાં આવશે અને તેનો તમામ ડેટા કાઢી નાખવામાં આવશે."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"તમે ટીવીને અનલૉક કરવા માટે <xliff:g id="NUMBER_0">%d</xliff:g> વખત ખોટી રીતે પ્રયાસ કર્યો. <xliff:g id="NUMBER_1">%d</xliff:g> વધુ અસફળ પ્રયાસ પછી, ટીવીને ફેક્ટરી ડિફોલ્ટ પર ફરીથી સેટ કરવામાં આવશે અને તેનો તમામ ડેટા કાઢી નાખવામાં આવશે."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"તમે ફોનને અનલૉક કરવા માટે <xliff:g id="NUMBER_0">%d</xliff:g> વખત ખોટી રીતે પ્રયાસ કર્યો. <xliff:g id="NUMBER_1">%d</xliff:g> વધુ અસફળ પ્રયાસો પછી, ફોનને ફેક્ટરી ડિફોલ્ટ પર ફરીથી સેટ કરવામાં આવશે અને તેનો તમામ ડેટા કાઢી નાખવામાં આવશે."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"તમે <xliff:g id="NUMBER_0">%1$d</xliff:g> વખત ખોટી રીતે તમારો PIN લખ્યો છે. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> સેકંડમાં ફરીથી પ્રયાસ કરો."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"તમે <xliff:g id="NUMBER_0">%1$d</xliff:g> વખત ખોટી રીતે તમારો પાસવર્ડ લખ્યો છે. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> સેકંડમાં ફરીથી પ્રયાસ કરો."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"તમે <xliff:g id="NUMBER_0">%1$d</xliff:g> વખત ખોટી રીતે તમારી અનલૉક પેટર્ન દોરી. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> સેકંડમાં ફરીથી પ્રયાસ કરો."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"તમે ટેબ્લેટને અનલૉક કરવા માટે <xliff:g id="NUMBER_0">%1$d</xliff:g> વખત ખોટી રીતે પ્રયાસ કર્યો. <xliff:g id="NUMBER_1">%2$d</xliff:g> વધુ અસફળ પ્રયાસ પછી, ટેબ્લેટને ફેક્ટરી ડિફોલ્ટ પર ફરીથી સેટ કરવામાં આવશે અને તેનો તમામ ડેટા કાઢી નાખવામાં આવશે."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"તમે ટીવીને અનલૉક કરવા માટે <xliff:g id="NUMBER_0">%1$d</xliff:g> વખત ખોટી રીતે પ્રયાસ કર્યો. <xliff:g id="NUMBER_1">%2$d</xliff:g> વધુ અસફળ પ્રયાસ પછી, ટીવીને ફેક્ટરી ડિફોલ્ટ પર ફરીથી સેટ કરવામાં આવશે અને તેનો તમામ ડેટા કાઢી નાખવામાં આવશે."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"તમે ફોનને અનલૉક કરવા માટે <xliff:g id="NUMBER_0">%1$d</xliff:g> વખત ખોટી રીતે પ્રયાસ કર્યો. <xliff:g id="NUMBER_1">%2$d</xliff:g> વધુ અસફળ પ્રયાસો પછી, ફોનને ફેક્ટરી ડિફોલ્ટ પર ફરીથી સેટ કરવામાં આવશે અને તેનો તમામ ડેટા કાઢી નાખવામાં આવશે."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"તમે <xliff:g id="NUMBER">%d</xliff:g> વખત ખોટી રીતે ટેબ્લેટને અનલૉક કરવાનો પ્રયાસ કર્યો. ટેબ્લેટ હવે ફેક્ટરી ડિફોલ્ટ પર ફરીથી સેટ કરવામાં આવશે."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"તમે <xliff:g id="NUMBER">%d</xliff:g> વખત ખોટી રીતે ટીવીને અનલૉક કરવાનો પ્રયાસ કર્યો. ટીવી હવે ફેક્ટરી ડિફોલ્ટ પર ફરીથી સેટ કરવામાં આવશે."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"તમે <xliff:g id="NUMBER">%d</xliff:g> વખત ખોટી રીતે ફોનને અનલૉક કરવાનો પ્રયાસ કર્યો. ફોન હવે ફેક્ટરી ડિફોલ્ટ પર ફરીથી સેટ કરવામાં આવશે."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"તમે <xliff:g id="NUMBER_0">%d</xliff:g> વખત ખોટી રીતે અનલૉક પેટર્ન દોરી છે. વધુ <xliff:g id="NUMBER_1">%d</xliff:g> વખત અસફળ પ્રયાસો પછી, તમને એક ઇમેઇલ એકાઉન્ટનો ઉપયોગ કરીને તમારા ટેબ્લેટને અનલૉક કરવા માટે પૂછવામાં આવશે.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> સેકંડમાં ફરી પ્રયાસ કરો."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"તમે <xliff:g id="NUMBER_0">%d</xliff:g> વખત ખોટી રીતે અનલૉક પેટર્ન દોરી છે. વધુ <xliff:g id="NUMBER_1">%d</xliff:g> વખત અસફળ પ્રયાસો પછી, તમને એક ઇમેઇલ એકાઉન્ટનો ઉપયોગ કરીને તમારા ટીવીને અનલૉક કરવા માટે પૂછવામાં આવશે.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> સેકંડમાં ફરી પ્રયાસ કરો."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"તમે તમારી અનલૉક પેટર્ન <xliff:g id="NUMBER_0">%d</xliff:g> વખત ખોટી રીતે દોરી. હજી <xliff:g id="NUMBER_1">%d</xliff:g> અસફળ પ્રયાસ પછી, તમને ઇમેઇલ એકાઉન્ટનો ઉપયોગ કરીને ફોનને અનલૉક કરવાનું કહેવામાં આવશે.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> સેકંડમાં ફરીથી પ્રયાસ કરો."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"તમે <xliff:g id="NUMBER_0">%1$d</xliff:g> વખત ખોટી રીતે અનલૉક પેટર્ન દોરી છે. વધુ <xliff:g id="NUMBER_1">%2$d</xliff:g> વખત અસફળ પ્રયાસો પછી, તમને એક ઇમેઇલ એકાઉન્ટનો ઉપયોગ કરીને તમારા ટેબ્લેટને અનલૉક કરવા માટે પૂછવામાં આવશે.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> સેકંડમાં ફરી પ્રયાસ કરો."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"તમે <xliff:g id="NUMBER_0">%1$d</xliff:g> વખત ખોટી રીતે અનલૉક પેટર્ન દોરી છે. વધુ <xliff:g id="NUMBER_1">%2$d</xliff:g> વખત અસફળ પ્રયાસો પછી, તમને એક ઇમેઇલ એકાઉન્ટનો ઉપયોગ કરીને તમારા ટીવીને અનલૉક કરવા માટે પૂછવામાં આવશે.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> સેકંડમાં ફરી પ્રયાસ કરો."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"તમે તમારી અનલૉક પેટર્ન <xliff:g id="NUMBER_0">%1$d</xliff:g> વખત ખોટી રીતે દોરી. હજી <xliff:g id="NUMBER_1">%2$d</xliff:g> અસફળ પ્રયાસ પછી, તમને ઇમેઇલ એકાઉન્ટનો ઉપયોગ કરીને ફોનને અનલૉક કરવાનું કહેવામાં આવશે.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> સેકંડમાં ફરીથી પ્રયાસ કરો."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"દૂર કરો"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"ભલામણ કરેલ સ્તરની ઉપર વૉલ્યૂમ વધાર્યો?\n\nલાંબા સમય સુધી ઊંચા અવાજે સાંભળવું તમારી શ્રવણક્ષમતાને નુકસાન પહોંચાડી શકે છે."</string>
@@ -1470,6 +1468,7 @@
<item quantity="other">%d કલાક માટે</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> સુધી"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (આગલા એલાર્મ) સુધી"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"તમે આ બંધ ન કરો ત્યાં સુધી"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"તમે ખલેલ પાડશો નહીં બંધ ન કરો ત્યાં સુધી"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-hi-watch/strings.xml b/core/res/res/values-hi-watch/strings.xml
index afed795..5d8fd27 100644
--- a/core/res/res/values-hi-watch/strings.xml
+++ b/core/res/res/values-hi-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_1">%2$d</xliff:g> में से <xliff:g id="NUMBER_0">%1$d</xliff:g> ऐप."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"संवेदक"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"अपने संपर्कों को ऐक्सेस करें"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"इस घड़ी के स्थान को ऐक्सेस करें"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"अपने कैलेंडर को ऐक्सेस करें"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"SMS संदेश भेजें और देखें"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"अपनी घड़ी पर फ़ोटो, मीडिया और फ़ाइलें ऐक्सेस करें"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"ऑडियो रिकॉर्ड करें"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"चित्र लें और वीडियो रिकॉर्ड करें"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"फ़ोन कॉल करें और प्रबंधित करें"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"अपने महत्वपूर्ण संकेतों के बारे में सेंसर डेटा को ऐक्सेस करें"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"स्थिति बार होने दें"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"शरीर संवेदक ऐक्सेस करें (जैसे हृदय गति मॉनीटर)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"सटीक स्थान ऐक्सेस करें (GPS और नेटवर्क-आधारित)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"अनुमानित स्थान ऐक्सेस करें (नेटवर्क-आधारित)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"SIM पर आदेश भेजें"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"पूर्ण नेटवर्क ऐक्सेस पाएं"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"प्रोफ़ाइल और डिवाइस स्वामियों को प्रबंधित करें"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"WiMAX स्थिति बदलें"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"Android Beam ट्रांसफर स्थिति प्राप्त करें"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"मीडिया आउटपुट को रूट करें"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"इंस्टॉल सत्रों को पढ़ें"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"पैकेज इंस्टॉल करने का अनुरोध करें"</string>
</resources>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 96eb95c..6dba1fb 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"क्रेडिट कार्ड नंबर और पासवर्ड जैसा व्यक्तिगत डेटा शामिल होता है."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"स्थिति बार अक्षम या बदलें"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"ऐप्स को स्थिति बार अक्षम करने या सिस्टम आइकन को जोड़ने या निकालने देता है."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"स्थिति बार"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"स्थिति बार होने दें"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"ऐप्स को स्थिति बार होने देता है."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"स्थिति बार विस्तृत/संक्षिप्त करें"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"ऐप्स को स्थिति बार को विस्तृत या संक्षिप्त करने देता है."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"ऐप्स को WAP संदेशों को प्राप्त और संसाधित करने देता है. इस अनुमति में आपको भेजे गए संदेशों की निगरानी आपको दिखाए बिना करने और हटाने की क्षमता शामिल है."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"चल रहे ऐप्स पुनर्प्राप्त करें"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"ऐप्स को वर्तमान में और हाल ही में चल रहे कार्यों के बारे में जानकारी को पुन: प्राप्त करने देता है. इससे ऐप्स डिवाइस पर उपयोग किए गए ऐप्स के बारे में जानकारी खोज सकता है."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"प्रोफ़ाइल और डिवाइस स्वामियों को प्रबंधित करें"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"प्रोफ़ाइल और डिवाइस स्वामियों को प्रबंधित करें"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"ऐप्स को प्रोफ़ाइल स्वामी और डिवाइस स्वामी सेट करने दें."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"चल रहे ऐप्स पुन: क्रमित करें"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"ऐप्स को कार्यों को अग्रभूमि और पृष्ठभूमि पर ले जाने देता है. ऐप्स आपके इनपुट के बिना यह कर सकता है."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"ऐप्स को इनकमिंग और आउटगोइंग कॉल के डेटा सहित, आपके टेबलेट का कॉल लॉग संशोधित करने देता है. दुर्भावनापूर्ण ऐप्स आपके कॉल लॉग को मिटाने या संशोधित करने के लिए इसका उपयोग कर सकते हैं."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"ऐप को इनकमिंग और आउटगोइंग कॉल के डेटा सहित, आपके टैबलेट के कॉल लॉग में बदलाव करने देती है. दुर्भावनापूर्ण ऐप्स आपके कॉल लॉग को मिटाने या संशोधित करने के लिए उसका उपयोग कर सकते हैं."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"ऐप्स को इनकमिंग और आउटगोइंग कॉल के डेटा सहित, आपके फ़ोन का कॉल लॉग संशोधित करने देता है. दुर्भावनापूर्ण ऐप्स आपके कॉल लॉग को मिटाने या संशोधित करने के लिए इसका उपयोग कर सकते हैं."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"बॉडी सेंसर (जैसे हृदय गति मॉनीटर)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"शरीर संवेदक एक्सेस करें (जैसे हृदय गति मॉनीटर)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"ऐप को आपकी शारीरिक स्थिति, जैसे आपकी हृदय गति पर नज़र रखने वाले संवेदकों का डेटा एक्सेस करने देती है."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"केलैंडर ईवेंट के साथ-साथ गोपनीय जानकारी पढ़ें"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"ऐप्स को मित्रों या सहकर्मियों के कैलेंडर इवेंट सहित, आपके टेबलेट पर संग्रहीत कैलेंडर इवेंट पढ़ने देता है. इससे गोपनीयता या संवेदनशीलता पर ध्यान दिए बिना, ऐप्स आपके कैलेंडर डेटा को साझा कर सकता है या सहेज सकता है."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"ऐप्स को मित्रों या सहकर्मियों के ईवेंट के साथ ही वे ईवेंट जोड़ने, निकालने, बदलने देता है जिन्हें आप अपने फ़ोन पर संशोधित कर सकते हैं. इससे ऐप्स , अपनी जानकारी के बिना उन संदेशों को भेज सकता है जो कैलेंडर स्वामियों की ओर से आते दिखाई देते हैं, या ईवेंट संशोधित कर सकता है."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"अतिरिक्त स्थान प्रदाता आदेशों में पहुंचे"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"ऐप्स को अतिरिक्त स्थान प्रदाता आदेशों पर पहुंचने देती है. इससे ऐप्स GPS या अन्य स्थान स्रोतों के संचालन में अवरोध पहुंचा सकता है."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"सटीक स्थान (GPS और नेटवर्क-आधारित)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"सटीक स्थान एक्सेस करें (GPS और नेटवर्क-आधारित)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"ऐप्स को ग्लोबल पोज़िशनिंग सिस्टम (GPS) या सेल टॉवर और वाई-फ़ाई जैसे नेटवर्क स्थान स्रोतों का उपयोग करके आपका सटीक स्थान प्राप्त करने देती है. ऐप्स द्वारा इन स्थान सेवाओं का उपयोग किए जाने के लिए इन्हें चालू होना चाहिए और आपके डिवाइस पर उपलब्ध होना चाहिए. ऐप्स इसका उपयोग यह पता करने में कर सकते हैं कि आप कहां पर हैं, और अतिरिक्त बैटरी की खपत कर सकते हैं."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"अनुमानित स्थान (नेटवर्क-आधारित)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"अनुमानित स्थान एक्सेस करें (नेटवर्क-आधारित)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"ऐप्स को आपका अनुमानित स्थान प्राप्त करने देती है. इस स्थान को सेल टॉवर और वाई-फ़ाई जैसे नेटवर्क स्थान स्रोतों का उपयोग करके स्थान सेवाओं द्वारा प्राप्त किया गया है. ऐप्स द्वारा इन स्थान सेवाओं का उपयोग करने के लिए इन्हें चालू होना चाहिए और आपके डिवाइस में उपलब्ध होना चाहिए. ऐप्स इसका उपयोग यह पता लगाने में कर सकते हैं कि आप लगभग कहां पर हैं."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"अपनी ऑडियो सेटिंग बदलें"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"ऐप्स को वैश्विक ऑडियो सेटिंग, जैसे वॉल्यूम और कौन-सा स्पीकर आउटपुट के लिए उपयोग किया गया, संशोधित करने देता है."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"ऑडियो रिकॉर्ड करें"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"ऐप्स को माइक्रोफ़ोन द्वारा ऑडियो रिकार्ड करने देता है. यह अनुमति ऐप्स को आपकी पुष्टि के बिना किसी भी समय ऑडियो रिकार्ड करने देती है."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"सिम संचार"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"SIM पर आदेश भेजें"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"ऐप्स को सिम में आदेश भेजने देती है. यह बहुत ही खतरनाक है."</string>
<string name="permlab_camera" msgid="3616391919559751192">"चित्र और वीडियो लें"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"ऐप्स को कैमरे से चित्र और वीडियो लेने देता है. यह अनुमति ऐप्स को किसी भी समय आपकी पुष्टि के बिना कैमरे का उपयोग करने देती है."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"ऐप्स को फ़ोन द्वारा ज्ञात खातों की सूची प्राप्त करने देता है. इसमें वे खाते शामिल हो सकते हैं जिन्हें आपके द्वारा इंस्टॉल किए गए ऐप्स ने बनाया है."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"नेटवर्क कनेक्शन देखें"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"ऐप्स को नेटवर्क कनेक्शन के बारे में जानकारी देखने देता है जैसे कौन से नेटवर्क मौजूद हैं और कनेक्ट हैं."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"पूर्ण नेटवर्क एक्सेस"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"पूर्ण नेटवर्क एक्सेस पाएं"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"ऐप्स को नेटवर्क सॉकेट बनाने और कस्टम नेटवर्क प्रोटोकॉल का उपयोग करने देता है. ब्राउज़र और अन्य ऐप्स इंटरनेट को डेटा भेजने के साधन उपलब्ध कराते हैं, ताकि इंटरनेट को डेटा भेजने के लिए इस अनुमति की आवश्यकता नहीं हो."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"नेटवर्क कनेक्टिविटी बदलें"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"ऐप्स को नेटवर्क कनेक्टिविटी की स्थिति बदलने देता है."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"ऐप्स को स्थानीय ब्लूटूथ फ़ोन कॉन्फ़िगर करने देता है, और रिमोट डिवाइस के साथ खोजने और युग्मित करने देता है."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"WiMAX से कनेक्ट और डिस्कनेक्ट करें"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"ऐप्स को WiMAX सक्षम है या नहीं और कनेक्ट किए गए किसी WiMAX नेटवर्क के बारे में जानकारी निर्धारित करने देता है."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX स्थिति बदलें"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"WiMAX स्थिति बदलें"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"ऐप्स को WiMAX नेटवर्क से टेबलेट को कनेक्ट और डिस्कनेक्ट करने देता है."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"ऐप को, टीवी को WiMAX नेटवर्कों से कनेक्ट करने और उनसे डिस्कनेक्ट करने देती है."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"ऐप्स को WiMAX नेटवर्क से फ़ोन को कनेक्ट और डिस्कनेक्ट करने देता है."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"ऐप्स को टच स्क्रीन के कैलिब्रेशन पैरामीटर को बदलने देती है. सामान्य ऐप्स के लिए कभी भी आवश्यक नहीं होना चाहिए."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"DRM प्रमाणपत्र एक्सेस करें"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"ऐप्लिकेशन को DRM प्रमाणपत्रों का प्रावधान और उपयोग करने देती है. सामान्य ऐप्स के लिए कभी भी आवश्यकता नहीं होना चाहिए."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Android Beam स्थानान्तरण स्थिति प्राप्त करें"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"Android Beam ट्रांसफर स्थिति प्राप्त करें"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"इस ऐप्लिकेशन को वर्तमान Android Beam स्थानान्तरणों के बारे में जानकारी प्राप्त करने देती है."</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"DRM प्रमाणपत्रों को निकाल सकता है"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"एप्लिकेशन को DRM प्रमाणपत्रों को निकालने देता है. सामान्य ऐप्स के लिए कभी भी आवश्यकता नहीं होनी चाहिए."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"गलत पिन कोड."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"अनलॉक करने के लिए, मेनू दबाएं और फिर 0 दबाएं."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"आपातकालीन नंबर"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"कोई सेवा नहीं."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"कोई सेवा नहीं"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"स्क्रीन लॉक की गई है."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"अनलॉक करने के लिए मेनू दबाएं या आपातलकालीन कॉल करें."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"अनलॉक करने के लिए मेनू दबाएं."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"कृपया उपयोगकर्ता मार्गदर्शिका देखें या ग्राहक सहायता से संपर्क करें."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"सिम कार्ड लॉक किया गया है."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"सिम कार्ड अनलॉक कर रहा है…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"आपने अपना अनलॉक आकार <xliff:g id="NUMBER_0">%d</xliff:g> बार गलत बनाया है. \n\n <xliff:g id="NUMBER_1">%d</xliff:g> सेकंड में पुन: प्रयास करें."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"आपने अपना पासवर्ड <xliff:g id="NUMBER_0">%d</xliff:g> बार गलत तरीके से लिखा है. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> सेकंड में पुन: प्रयास करें."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"आपने अपना पिन <xliff:g id="NUMBER_0">%d</xliff:g> बार गलत तरीके से लिखा है. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> सेकंड में फिर से प्रयास करें."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"आपने अपना अनलॉक आकार <xliff:g id="NUMBER_0">%d</xliff:g> बार गलत बनाया है. <xliff:g id="NUMBER_1">%d</xliff:g> और असफल प्रयासों के बाद, आपसे अपने Google साइन-इन का उपयोग करके आपके टेबलेट को अनलॉक करने को कहा जाएगा.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> सेकंड में पुन: प्रयास करें."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"आपने अपना अनलॉक पैटन <xliff:g id="NUMBER_0">%d</xliff:g> बार गलत तरीके से बनाया है. <xliff:g id="NUMBER_1">%d</xliff:g> और असफल प्रयासों के बाद, आपसे अपने टीवी को अपने Google साइन-इन का उपयोग करके अनलॉक करने के लिए कहा जाएगा.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> सेकंड में पुन: प्रयास करें."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"आपने अपना अनलॉक आकार <xliff:g id="NUMBER_0">%d</xliff:g> बार गलत बनाया है. <xliff:g id="NUMBER_1">%d</xliff:g> और असफल प्रयासों के बाद, आपसे अपने Google साइन-इन का उपयोग करके आपके फ़ोन को अनलॉक करने को कहा जाएगा.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> सेकंड में पुन: प्रयास करें."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"आप गलत तरीके से टेबलेट को अनलॉक करने का प्रयास <xliff:g id="NUMBER_0">%d</xliff:g> बार कर चुके हैं. <xliff:g id="NUMBER_1">%d</xliff:g> और असफल प्रयास के बाद, टेबलेट फ़ैक्टरी डिफ़ॉल्ट पर रीसेट हो जाएगा और सभी उपयोगकर्ता डेटा खो जाएगा."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"आपने टीवी को अनलॉक करने के लिए <xliff:g id="NUMBER_0">%d</xliff:g> बार गलत तरीके से प्रयास किया है. <xliff:g id="NUMBER_1">%d</xliff:g> और असफल प्रयासों के बाद, टीवी को फ़ैक्टरी डिफ़ॉल्ट पर रीसेट कर दिया जाएगा और सभी उपयोगकर्ता डेटा खो जाएगा."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"आप गलत तरीके से फ़ोन को अनलॉक करने का प्रयास <xliff:g id="NUMBER_0">%d</xliff:g> बार कर चुके हैं. <xliff:g id="NUMBER_1">%d</xliff:g> और असफल प्रयास के बाद, फ़ोन फ़ैक्टरी डिफ़ॉल्ट पर रीसेट हो जाएगा और सभी उपयोगकर्ता डेटा खो जाएगा."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"आपने अपना अनलॉक आकार <xliff:g id="NUMBER_0">%1$d</xliff:g> बार गलत बनाया है. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंड में पुन: प्रयास करें."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"आपने अपना पासवर्ड <xliff:g id="NUMBER_0">%1$d</xliff:g> बार गलत तरीके से लिखा है. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंड में पुन: प्रयास करें."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"आपने अपना पिन <xliff:g id="NUMBER_0">%1$d</xliff:g> बार गलत तरीके से लिखा है. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंड में फिर से प्रयास करें."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"आपने अपना अनलॉक आकार <xliff:g id="NUMBER_0">%1$d</xliff:g> बार गलत बनाया है. <xliff:g id="NUMBER_1">%2$d</xliff:g> और असफल प्रयासों के बाद, आपसे अपने Google साइन-इन का उपयोग करके आपके टेबलेट को अनलॉक करने को कहा जाएगा.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकंड में पुन: प्रयास करें."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"आपने अपना अनलॉक पैटन <xliff:g id="NUMBER_0">%1$d</xliff:g> बार गलत तरीके से बनाया है. <xliff:g id="NUMBER_1">%2$d</xliff:g> और असफल प्रयासों के बाद, आपसे अपने टीवी को अपने Google साइन-इन का उपयोग करके अनलॉक करने के लिए कहा जाएगा.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकंड में पुन: प्रयास करें."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"आपने अपना अनलॉक आकार <xliff:g id="NUMBER_0">%1$d</xliff:g> बार गलत बनाया है. <xliff:g id="NUMBER_1">%2$d</xliff:g> और असफल प्रयासों के बाद, आपसे अपने Google साइन-इन का उपयोग करके आपके फ़ोन को अनलॉक करने को कहा जाएगा.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकंड में पुन: प्रयास करें."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"आप गलत तरीके से टेबलेट को अनलॉक करने का प्रयास <xliff:g id="NUMBER_0">%1$d</xliff:g> बार कर चुके हैं. <xliff:g id="NUMBER_1">%2$d</xliff:g> और असफल प्रयास के बाद, टेबलेट फ़ैक्टरी डिफ़ॉल्ट पर रीसेट हो जाएगा और सभी उपयोगकर्ता डेटा खो जाएगा."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"आपने टीवी को अनलॉक करने के लिए <xliff:g id="NUMBER_0">%1$d</xliff:g> बार गलत तरीके से प्रयास किया है. <xliff:g id="NUMBER_1">%2$d</xliff:g> और असफल प्रयासों के बाद, टीवी को फ़ैक्टरी डिफ़ॉल्ट पर रीसेट कर दिया जाएगा और सभी उपयोगकर्ता डेटा खो जाएगा."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"आप गलत तरीके से फ़ोन को अनलॉक करने का प्रयास <xliff:g id="NUMBER_0">%1$d</xliff:g> बार कर चुके हैं. <xliff:g id="NUMBER_1">%2$d</xliff:g> और असफल प्रयास के बाद, फ़ोन फ़ैक्टरी डिफ़ॉल्ट पर रीसेट हो जाएगा और सभी उपयोगकर्ता डेटा खो जाएगा."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"आप टेबलेट को गलत तरीके से <xliff:g id="NUMBER">%d</xliff:g> बार अनलॉक करने का प्रयास कर चुके हैं. टेबलेट अब फ़ैक्टरी डिफ़ॉल्ट पर रीसेट हो जाएगा."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"आपने टीवी को अनलॉक करने के लिए <xliff:g id="NUMBER">%d</xliff:g> बार गलत तरीके से प्रयास किया है. अब टीवी को फ़ैक्टरी डिफ़ॉल्ट पर रीसेट कर दिया जाएगा."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"आप फ़ोन को गलत तरीके से <xliff:g id="NUMBER">%d</xliff:g> बार अनलॉक करने का प्रयास कर चुके हैं. फ़ोन अब फ़ैक्टरी डिफ़ॉल्ट पर रीसेट हो जाएगा."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"शब्द"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"लिंक"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"पंक्ति"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"फ़ैक्ट्री परीक्षण विफल"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"FACTORY_TEST क्रिया केवल /system/app में इंस्टॉल किए गए पैकेज के लिए समर्थित है."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"ऐसा कोई पैकेज नहीं मिला था जो FACTORY_TEST कार्रवाई प्रदान करता हो."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"प्रारूपित किया जा रहा है..."</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"नहीं लगाया गया"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"कोई मिलती-जुलती गतिविधि नहीं मिली."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"मीडिया आउटपुट को रूट करें"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"मीडिया आउटपुट को रूट करें"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"ऐप्स को मीडिया आउटपुट को अन्य बाहरी डिवाइस पर रूट करने देता है."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"इंस्टॉल सत्रों को पढ़ें"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"इंस्टॉल सत्रों को पढ़ें"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"ऐप्लिकेशन को इंस्टॉल सत्रों को पढ़ने देती है. इससे उसे सक्रिय पैकेज इंस्टॉलेशन के बारे में विवरण देखने की अनुमति मिल जाती है."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"पैकेज इंस्टॉल करने का अनुरोध"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"पैकेज इंस्टॉल करने का अनुरोध करें"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"किसी ऐप्लिकेशन को पैकेज इंस्टॉल करने के अनुरोध की अनुमति देता है."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"ज़ूम नियंत्रण के लिए दो बार स्पर्श करें"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"विजेट नहीं जोड़ा जा सका."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"अमान्य उपयोगकर्ता नाम या पासवर्ड."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"अपना उपयोगकर्ता नाम या पासवर्ड भूल गए?\n "<b>"google.com/accounts/recovery"</b>" पर जाएं."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"खाते की जांच की जा रही है…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"आप अपना PIN <xliff:g id="NUMBER_0">%d</xliff:g> बार गलत तरीके से लिख चुके हैं. \n\n <xliff:g id="NUMBER_1">%d</xliff:g> सेकंड में पुन: प्रयास करें."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"आप अपना पासवर्ड <xliff:g id="NUMBER_0">%d</xliff:g> बार गलत तरीके से लिख चुके हैं. \n\n <xliff:g id="NUMBER_1">%d</xliff:g> सेकंड में पुन: प्रयास करें."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"आपने अपना अनलॉक आकार <xliff:g id="NUMBER_0">%d</xliff:g> बार गलत तरीके से आरेखित किया है. \n\n <xliff:g id="NUMBER_1">%d</xliff:g> सेकंड में पुन: प्रयास करें."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"आप टेबलेट को अनलॉक करने के लिए <xliff:g id="NUMBER_0">%d</xliff:g> बार गलत तरीके से प्रयास कर चुके हैं. <xliff:g id="NUMBER_1">%d</xliff:g> और असफल प्रयासों के बाद, टेबलेट फ़ैक्टरी डिफ़ॉल्ट पर रीसेट हो जाएगा और सभी उपयोगकर्ता डेटा खो जाएगा."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"आपने टीवी को अनलॉक करने के लिए <xliff:g id="NUMBER_0">%d</xliff:g> बार गलत तरीके से प्रयास किया है. <xliff:g id="NUMBER_1">%d</xliff:g> और असफल प्रयासों के बाद, टीवी को फ़ैक्टरी डिफ़ॉल्ट पर रीसेट कर दिया जाएगा और सभी उपयोगकर्ता डेटा खो जाएगा."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"आप फ़ोन को अनलॉक करने के लिए <xliff:g id="NUMBER_0">%d</xliff:g> बार गलत तरीके से प्रयास कर चुके हैं. <xliff:g id="NUMBER_1">%d</xliff:g> और असफल प्रयासों के बाद, फ़ोन फ़ैक्टरी डिफ़ॉल्ट पर रीसेट हो जाएगा और सभी उपयोगकर्ता डेटा खो जाएगा."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"आप अपना PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> बार गलत तरीके से लिख चुके हैं. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंड में पुन: प्रयास करें."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"आप अपना पासवर्ड <xliff:g id="NUMBER_0">%1$d</xliff:g> बार गलत तरीके से लिख चुके हैं. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंड में पुन: प्रयास करें."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"आपने अपना अनलॉक आकार <xliff:g id="NUMBER_0">%1$d</xliff:g> बार गलत तरीके से आरेखित किया है. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंड में पुन: प्रयास करें."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"आप टेबलेट को अनलॉक करने के लिए <xliff:g id="NUMBER_0">%1$d</xliff:g> बार गलत तरीके से प्रयास कर चुके हैं. <xliff:g id="NUMBER_1">%2$d</xliff:g> और असफल प्रयासों के बाद, टेबलेट फ़ैक्टरी डिफ़ॉल्ट पर रीसेट हो जाएगा और सभी उपयोगकर्ता डेटा खो जाएगा."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"आपने टीवी को अनलॉक करने के लिए <xliff:g id="NUMBER_0">%1$d</xliff:g> बार गलत तरीके से प्रयास किया है. <xliff:g id="NUMBER_1">%2$d</xliff:g> और असफल प्रयासों के बाद, टीवी को फ़ैक्टरी डिफ़ॉल्ट पर रीसेट कर दिया जाएगा और सभी उपयोगकर्ता डेटा खो जाएगा."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"आप फ़ोन को अनलॉक करने के लिए <xliff:g id="NUMBER_0">%1$d</xliff:g> बार गलत तरीके से प्रयास कर चुके हैं. <xliff:g id="NUMBER_1">%2$d</xliff:g> और असफल प्रयासों के बाद, फ़ोन फ़ैक्टरी डिफ़ॉल्ट पर रीसेट हो जाएगा और सभी उपयोगकर्ता डेटा खो जाएगा."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"आप टेबलेट को अनलॉक करने के लिए <xliff:g id="NUMBER">%d</xliff:g> बार गलत तरीके से प्रयास कर चुके हैं. टेबलेट अब फ़ैक्टरी डिफ़ॉल्ट पर रीसेट हो जाएगा."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"आपने टीवी को अनलॉक करने के लिए <xliff:g id="NUMBER">%d</xliff:g> बार गलत तरीके से प्रयास किया है. अब टीवी को फ़ैक्टरी डिफ़ॉल्ट पर रीसेट कर दिया जाएगा."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"आप फ़ोन को अनलॉक करने के लिए <xliff:g id="NUMBER">%d</xliff:g> बार गलत तरीके से प्रयास कर चुके हैं. फ़ोन अब फ़ैक्टरी डिफ़ॉल्ट पर रीसेट हो जाएगा."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"आपने अपने अनलॉक आकार को <xliff:g id="NUMBER_0">%d</xliff:g> बार गलत तरीके से आरेखित किया है. <xliff:g id="NUMBER_1">%d</xliff:g> और असफल प्रयासों के बाद, आपसे अपने टेबलेट को किसी ईमेल खाते के उपयोग से अनलॉक करने के लिए कहा जाएगा.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> सेकंड में पुन: प्रयास करें."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"आपने अपना अनलॉक पैटर्न <xliff:g id="NUMBER_0">%d</xliff:g> बार गलत तरीके से बनाया है. और <xliff:g id="NUMBER_1">%d</xliff:g> असफल प्रयासों के बाद, आपसे अपने टीवी को किसी ईमेल पते का उपयोग करके अनलॉक करने के लिए कहा जाएगा.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> सेकंड में पुन: प्रयास करें."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"आपने अपने अनलॉक आकार को <xliff:g id="NUMBER_0">%d</xliff:g> बार गलत तरीके से आरेखित किया है. <xliff:g id="NUMBER_1">%d</xliff:g> और असफल प्रयासों के बाद, आपसे अपने फ़ोन को किसी ईमेल खाते का उपयोग करके अनलॉक करने के लिए कहा जाएगा.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> सेकंड में पुन: प्रयास करें."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"आपने अपने अनलॉक आकार को <xliff:g id="NUMBER_0">%1$d</xliff:g> बार गलत तरीके से आरेखित किया है. <xliff:g id="NUMBER_1">%2$d</xliff:g> और असफल प्रयासों के बाद, आपसे अपने टेबलेट को किसी ईमेल खाते के उपयोग से अनलॉक करने के लिए कहा जाएगा.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकंड में पुन: प्रयास करें."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"आपने अपना अनलॉक पैटर्न <xliff:g id="NUMBER_0">%1$d</xliff:g> बार गलत तरीके से बनाया है. और <xliff:g id="NUMBER_1">%2$d</xliff:g> असफल प्रयासों के बाद, आपसे अपने टीवी को किसी ईमेल पते का उपयोग करके अनलॉक करने के लिए कहा जाएगा.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकंड में पुन: प्रयास करें."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"आपने अपने अनलॉक आकार को <xliff:g id="NUMBER_0">%1$d</xliff:g> बार गलत तरीके से आरेखित किया है. <xliff:g id="NUMBER_1">%2$d</xliff:g> और असफल प्रयासों के बाद, आपसे अपने फ़ोन को किसी ईमेल खाते का उपयोग करके अनलॉक करने के लिए कहा जाएगा.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकंड में पुन: प्रयास करें."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"निकालें"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"वॉल्यूम को सुझाए गए स्तर से ऊपर बढ़ाएं?\n\nअत्यधिक वॉल्यूम पर अधिक समय तक सुनने से आपकी सुनने की क्षमता को नुकसान हो सकता है."</string>
@@ -1470,6 +1468,7 @@
<item quantity="other">%d घंटे के लिए</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> तक"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (अगले अलार्म) तक"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"जब तक आप इसे बंद नहीं कर देते"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"जब तक कि आप परेशान ना करें को बंद नहीं कर देते"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-hr-watch/strings.xml b/core/res/res/values-hr-watch/strings.xml
index dda3cb2..7b372ed 100644
--- a/core/res/res/values-hr-watch/strings.xml
+++ b/core/res/res/values-hr-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Aplikacija <xliff:g id="NUMBER_0">%1$d</xliff:g> od <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Senzori"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"pristupati vašim kontaktima"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"pristupati lokaciji ovog sata"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"pristupati kalendaru"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"slati i pregledavati SMS poruke"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"pristupati fotografijama, medijima i datotekama na vašem satu"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"snimati zvuk"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"snimati fotografije i videozapise"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"uspostavljati telefonske pozive i upravljati njima"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"pristupati podacima senzora o vašim vitalnim znakovima"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"biti traka statusa"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"pristupati biometrijskim senzorima (kao što su monitori otkucaja srca)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"pristupati preciznoj lokaciji (na temelju GPS-a i mreža)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"pristupati približnoj lokaciji (na temelju mreža)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"slati naredbe SIM-u"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"imati puni mrežni pristup"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"upravljati vlasnicima profila i uređaja"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"promijeniti stanje WiMAX-a"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"primati status prijenosa Android Beama"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"usmjeravati medijski izlaz"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"čitati sesije instaliranja"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"zahtijevati instaliranje paketa"</string>
</resources>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 9b741bc..643cf0d 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -255,7 +255,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Uključuje osobne podatke kao što su brojevi kreditnih kartica i zaporke."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"onemogućavanje ili izmjena trake statusa"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Aplikaciji omogućuje onemogućavanje trake statusa ili dodavanje i uklanjanje sistemskih ikona."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"traka statusa"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"biti traka statusa"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Aplikaciji omogućuje da bude traka statusa."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"proširivanje/sažimanje trake statusa"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Omogućuje aplikaciji proširivanje ili sažimanje trake statusa."</string>
@@ -283,7 +283,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Aplikaciji omogućuje primanje i obradu WAP poruka. Ta dozvola uključuje mogućnost nadziranja ili brisanja vama poslanih poruka, a da vam ih ne prikaže."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"dohvaćanje pokrenutih aplikacija"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Aplikaciji omogućuje dohvaćanje informacija o trenutačnim i nedavnim tekućim zadacima. To aplikaciji može omogućiti otkrivanje informacija o tome koje se aplikacije upotrebljavaju na uređaju."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Upravljanje vlasnicima profila i uređaja"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"upravljati vlasnicima profila i uređaja"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Omogućuje aplikaciji postavljanje vlasnika profila i vlasnika uređaja."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"promjena redoslijeda pokrenutih aplikacija"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Aplikaciji omogućuje premještanje zadataka u prednji plan ili pozadinu. Aplikacija to može napraviti bez vašeg naloga."</string>
@@ -325,7 +325,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Aplikaciji omogućuje izmjenu dnevnika poziva vašeg tabletnog računala zajedno s podacima o dolaznim i odlaznim pozivima. Zlonamjerne aplikacije to mogu upotrebljavati za brisanje ili izmjenu vašeg dnevnika poziva."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Aplikaciji omogućuje izmjenu zapisnika poziva vašeg televizora zajedno s podacima o dolaznim i odlaznim pozivima. Zlonamjerne aplikacije to mogu upotrebljavati za brisanje ili izmjenu vašeg zapisnika poziva."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Aplikaciji omogućuje izmjenu dnevnika poziva vašeg telefona zajedno s podacima o dolaznim i odlaznim pozivima. Zlonamjerne aplikacije to mogu upotrebljavati za brisanje ili izmjenu vašeg dnevnika poziva."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"senzori tjelesnih funkcija (npr. monitori otkucaja srca)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"pristupati biometrijskim senzorima (kao što su monitori otkucaja srca)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Omogućuje aplikaciji pristup podacima sa senzora koji nadziru vaše fizičko stanje, na primjer, broj otkucaja srca."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"čitajte kalendarske događaje i povjerljive informacije"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Aplikaciji omogućuje čitanje svih događaja u kalendaru pohranjenih na vašem tabletnom računalu, uključujući one od vaših prijatelja ili suradnika. To aplikaciji može omogućiti dijeljenje ili spremanje vaših podataka kalendara, neovisno o povjerljivosti ili osjetljivosti."</string>
@@ -337,15 +337,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Aplikaciji omogućuje dodavanje, uklanjanje i promjenu događaja koje možete izmijeniti na telefonu, uključujući one od vaših prijatelja ili suradnika. To aplikaciji može omogućiti slanje poruka koje izgledaju kao da dolaze od vlasnika kalendara ili izmjenu događaja bez znanja vlasnika."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"pristup dodatnim naredbama davatelja lokacije"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Omogućuje aplikaciji pristup dodatnim naredbama davatelja usluga lokacije. To može omogućiti aplikaciji ometanje rada GPS-a ili drugih izvora lokacije."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"precizna lokacija (GPS i mreža)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"pristupati preciznoj lokaciji (na temelju GPS-a i mreža)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Aplikacija može dobiti vašu preciznu lokaciju pomoću globalnog pozicijskog sustava (GPS-a) ili mrežnih izvora lokacije kao što su bazne stanice i Wi-Fi. Te lokacijske usluge moraju biti uključene i dostupne vašem uređaju da bi ih aplikacija mogla upotrebljavati. Aplikacije mogu upotrebljavati tu mogućnost kako bi utvrdile vašu lokaciju i mogu dodatno trošiti bateriju."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"približna lokacija (mreža)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"pristupati približnoj lokaciji (na temelju mreža)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Aplikacija može dobiti vašu približnu lokaciju. Tu lokaciju izvode lokacijske usluge pomoću mrežnih izvora lokacije kao što su bazne stanice i Wi-Fi. Te lokacijske usluge moraju biti uključene i dostupne vašem uređaju da bi ih aplikacija mogla upotrebljavati. Aplikacije mogu upotrebljavati tu mogućnost kako bi utvrdile vašu približnu lokaciju."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"promjena postavki zvuka"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Aplikaciji omogućuje izmjenu globalnih postavki zvuka, primjerice glasnoće i zvučnika koji se upotrebljava za izlaz."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"snimanje zvuka"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Aplikaciji omogućuje snimanje zvuka mikrofonom. Ta dozvola aplikaciji omogućuje snimanje zvuka u bilo kojem trenutku bez vašeg odobrenja."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"komunikacija sa SIM-om"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"slati naredbe SIM-u"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Omogućuje aplikaciji slanje naredbi SIM-u. To je vrlo opasno."</string>
<string name="permlab_camera" msgid="3616391919559751192">"snimi fotografije i videozapise"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Aplikaciji omogućuje snimanje slika i videozapisa fotoaparatom. Ta dozvola aplikaciji omogućuje upotrebu fotoaparata u bilo kojem trenutku bez vašeg odobrenja."</string>
@@ -383,7 +383,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Aplikaciji omogućuje dobivanje popisa računa koje telefon poznaje. Uključeni mogu biti svi računi koje izrade aplikacije koje ste instalirali."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"prikaz mrežnih veza"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Aplikaciji omogućuje pregled informacija o mrežnim vezama, primjerice koje mreže postoje i koje su spojene."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"puni mrežni pristup"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"imati puni mrežni pristup"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Aplikaciji omogućuje stvaranje mrežnih utičnica i upotrebu prilagođenih mrežnih protokola. Preglednik i druge aplikacije pružaju sredstva za slanje podataka na internet, tako da ta dozvola nije potrebna za slanje podataka na internet."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"promjena mrežne povezivosti"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Aplikaciji omogućuje promjenu stanja mrežnog povezivanja."</string>
@@ -403,7 +403,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Aplikaciji omogućuje konfiguraciju lokalnog Bluetooth telefona i otkrivanje i uparivanje s udaljenim uređajima."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"uspostavljanje i prekidanje veze s WiMAX-om"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Aplikaciji omogućuje utvrđivanje omogućenosti WiMAX mreže te daje informaciju o tome je li spojena neka WiMAX mreža."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Promjena stanja WiMAX mreže"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"promjena stanja WiMAX mreže"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Aplikaciji omogućuje povezivanje tabletnog računala s WiMAX mrežama i prekidanje veze tabletnog računala s njima."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Aplikaciji omogućuje povezivanje i prekidanje veze televizora s WiMAX mrežama."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Aplikaciji omogućuje povezivanje telefona s WiMAX mrežama i prekidanje veze telefona s njima."</string>
@@ -486,7 +486,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Omogućuje aplikaciji izmjenu parametara kalibracije dodirnog zaslona. Ne bi trebalo biti potrebno za uobičajene aplikacije."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"pristup DRM certifikatima"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Aplikaciji omogućuje pružanje i korištenje DRM certifikata. Ne bi trebalo biti potrebno za uobičajene aplikacije."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Primanje statusa prijenosa Android Beama"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"primati status prijenosa Android Beama"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Omogućuje aplikaciji primanje podataka o trenutačnim prijenosima Android Beama"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"uklanjanje DRM certifikata"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Omogućuje aplikaciji uklanjanje DRM certifikata. Ne bi trebalo biti potrebno za uobičajene aplikacije."</string>
@@ -651,7 +651,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Netočan PIN kôd."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Za otključavanje pritisnite Izbornik pa 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Broj hitne službe"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Nema usluge."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Nema usluge"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Zaslon zaključan."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Pritisnite Izbornik za otključavanje ili pozivanje hitnih službi."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Pritisnite Izbornik za otključavanje."</string>
@@ -683,15 +683,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Pogledajte korisnički priručnik ili kontaktirajte korisničku službu."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM kartica je zaključana."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Otključavanje SIM kartice…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Netočno ste iscrtali uzorak za otključavanje <xliff:g id="NUMBER_0">%d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Netočno ste napisali zaporku <xliff:g id="NUMBER_0">%d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Netočno ste napisali PIN <xliff:g id="NUMBER_0">%d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Netočno ste iscrtali uzorak za otključavanje <xliff:g id="NUMBER_0">%d</xliff:g> puta. Nakon još ovoliko neuspješnih pokušaja: <xliff:g id="NUMBER_1">%d</xliff:g> zamolit ćemo vas da otključate tabletno računalo putem prijave na Google.\n\n Pokušajte ponovo za <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Iscrtali ste netočan uzorak za otključavanje <xliff:g id="NUMBER_0">%d</xliff:g> puta. Ako ne uspijete ni nakon još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja, morat ćete otključati televizor putem Google prijave.\n\n Pokušajte ponovo za <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Netočno ste iscrtali uzorak za otključavanje <xliff:g id="NUMBER_0">%d</xliff:g> puta. Nakon još ovoliko neuspješnih pokušaja: <xliff:g id="NUMBER_1">%d</xliff:g> morat ćete otključati telefon putem prijave na Google.\n\n Pokušajte ponovo za <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Neispravno ste pokušali otključati tabletno računalo ovoliko puta: <xliff:g id="NUMBER_0">%d</xliff:g>. Ono će se vratiti na tvorničke postavke i svi korisnički podaci bit će izgubljeni nakon još ovoliko neuspjelih pokušaja: <xliff:g id="NUMBER_1">%d</xliff:g>."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Pokušali ste otključati TV <xliff:g id="NUMBER_0">%d</xliff:g> puta. Ako ne uspijete ni nakon još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja, TV će se vratiti na tvorničke postavke i izgubiti sve podatke korisnika."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Neispravno ste pokušali otključati telefon ovoliko puta: <xliff:g id="NUMBER_0">%d</xliff:g>. On će se vratiti na tvorničke postavke i svi korisnički podaci bit će izgubljeni nakon još ovoliko neuspjelih pokušaja: <xliff:g id="NUMBER_1">%d</xliff:g>."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Netočno ste iscrtali uzorak za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Netočno ste napisali zaporku <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Netočno ste napisali PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Netočno ste iscrtali uzorak za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Nakon još ovoliko neuspješnih pokušaja: <xliff:g id="NUMBER_1">%2$d</xliff:g> zamolit ćemo vas da otključate tabletno računalo putem prijave na Google.\n\n Pokušajte ponovo za <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Iscrtali ste netočan uzorak za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Ako ne uspijete ni nakon još <xliff:g id="NUMBER_1">%2$d</xliff:g> pokušaja, morat ćete otključati televizor putem Google prijave.\n\n Pokušajte ponovo za <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Netočno ste iscrtali uzorak za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Nakon još ovoliko neuspješnih pokušaja: <xliff:g id="NUMBER_1">%2$d</xliff:g> morat ćete otključati telefon putem prijave na Google.\n\n Pokušajte ponovo za <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Neispravno ste pokušali otključati tabletno računalo ovoliko puta: <xliff:g id="NUMBER_0">%1$d</xliff:g>. Ono će se vratiti na tvorničke postavke i svi korisnički podaci bit će izgubljeni nakon još ovoliko neuspjelih pokušaja: <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Pokušali ste otključati TV <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Ako ne uspijete ni nakon još <xliff:g id="NUMBER_1">%2$d</xliff:g> pokušaja, TV će se vratiti na tvorničke postavke i izgubiti sve podatke korisnika."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Neispravno ste pokušali otključati telefon ovoliko puta: <xliff:g id="NUMBER_0">%1$d</xliff:g>. On će se vratiti na tvorničke postavke i svi korisnički podaci bit će izgubljeni nakon još ovoliko neuspjelih pokušaja: <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Neispravno ste pokušali otključati tabletno računalo ovoliko puta: <xliff:g id="NUMBER">%d</xliff:g>. Sada će biti vraćeno na tvorničke postavke."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Pokušali ste otključati televizor <xliff:g id="NUMBER">%d</xliff:g> puta. Televizor će se sada vratiti na tvorničke postavke."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Neispravno ste pokušali otključati telefon ovoliko puta: <xliff:g id="NUMBER">%d</xliff:g>. Sada će biti vraćen na tvorničke postavke."</string>
@@ -743,8 +743,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"riječ"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"veza"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"redak"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Tvorničko testiranje nije uspjelo"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Radnja FACTORY_TEST podržana je samo za pakete instalirane na /sustavu/aplikaciji."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Nije pronađen paket koji sadrži radnju FACTORY_TEST."</string>
@@ -1080,11 +1078,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Formatiranje…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Nije umetnut"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Nisu pronađene podudarne radnje."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Usmjeravanje medijskog izlaza"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"usmjeravati medijski izlaz"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Aplikaciji omogućuje usmjeravanje medijskog izlaza na druge vanjske uređaje."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Čitanje sesija instaliranja"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"čitati sesije instaliranja"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Omogućuje aplikaciji čitanje sesija instaliranja. Aplikacija može vidjeti pojedinosti o aktivnim instaliranjima paketa."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Zahtijevaj instaliranje paketa"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"zahtijevati instaliranje paketa"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Aplikaciji omogućuje zahtijevanje instaliranja paketa."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Dodirnite dvaput za upravljanje zumiranjem"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Widget nije moguće dodati."</string>
@@ -1293,18 +1291,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Nevažeće korisničko ime ili zaporka."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Zaboravili ste korisničko ime ili zaporku?\nPosjetite "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Provjera računa..."</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Netočno ste napisali PIN <xliff:g id="NUMBER_0">%d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Netočno ste napisali zaporku <xliff:g id="NUMBER_0">%d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Netočno ste iscrtali obrazac za otključavanje <xliff:g id="NUMBER_0">%d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Netočno ste pokušali otključati tabletno računalo <xliff:g id="NUMBER_0">%d</xliff:g> puta. Ono će se vratiti na tvorničke postavke i svi korisnički podaci bit će izgubljeni nakon još ovoliko neuspjelih pokušaja: <xliff:g id="NUMBER_1">%d</xliff:g>."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Pokušali ste otključati televizor <xliff:g id="NUMBER_0">%d</xliff:g> puta. Ako ne uspijete ni nakon još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja, televizor će se vratiti na tvorničke postavke i izgubiti sve podatke korisnika."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Netočno ste pokušali otključati telefon <xliff:g id="NUMBER_0">%d</xliff:g> puta. On će se vratiti na tvorničke postavke i svi korisnički podaci bit će izgubljeni nakon još ovoliko neuspjelih pokušaja: <xliff:g id="NUMBER_1">%d</xliff:g>."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Netočno ste napisali PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Netočno ste napisali zaporku <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Netočno ste iscrtali obrazac za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Netočno ste pokušali otključati tabletno računalo <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Ono će se vratiti na tvorničke postavke i svi korisnički podaci bit će izgubljeni nakon još ovoliko neuspjelih pokušaja: <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Pokušali ste otključati televizor <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Ako ne uspijete ni nakon još <xliff:g id="NUMBER_1">%2$d</xliff:g> pokušaja, televizor će se vratiti na tvorničke postavke i izgubiti sve podatke korisnika."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Netočno ste pokušali otključati telefon <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. On će se vratiti na tvorničke postavke i svi korisnički podaci bit će izgubljeni nakon još ovoliko neuspjelih pokušaja: <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Netočno ste pokušali otključati tabletno računalo <xliff:g id="NUMBER">%d</xliff:g> puta. Sada će se vratiti na tvorničke postavke."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Pokušali ste otključati televizor <xliff:g id="NUMBER">%d</xliff:g> puta. Televizor će se sada vratiti na tvorničke postavke."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Netočno ste pokušali otključati telefon <xliff:g id="NUMBER">%d</xliff:g> puta. Sada će se vratiti na tvorničke postavke."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Netočno ste iscrtali obrazac za otključavanje <xliff:g id="NUMBER_0">%d</xliff:g> puta. Nakon još ovoliko neuspješnih pokušaja: <xliff:g id="NUMBER_1">%d</xliff:g> morat ćete otključati tabletno računalo pomoću računa e-pošte.\n\n Pokušajte ponovo za <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Iscrtali ste netočan uzorak za otključavanje <xliff:g id="NUMBER_0">%d</xliff:g> puta. Ako ne uspijete ni nakon još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja, morat ćete otključati televizor putem računa e-pošte.\n\n Pokušajte ponovo za <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Netočno ste iscrtali obrazac za otključavanje <xliff:g id="NUMBER_0">%d</xliff:g> puta. Nakon još ovoliko neuspješnih pokušaja: <xliff:g id="NUMBER_1">%d</xliff:g> morat ćete otključati telefon pomoću računa e-pošte.\n\n Pokušajte ponovo za <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Netočno ste iscrtali obrazac za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Nakon još ovoliko neuspješnih pokušaja: <xliff:g id="NUMBER_1">%2$d</xliff:g> morat ćete otključati tabletno računalo pomoću računa e-pošte.\n\n Pokušajte ponovo za <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Iscrtali ste netočan uzorak za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Ako ne uspijete ni nakon još <xliff:g id="NUMBER_1">%2$d</xliff:g> pokušaja, morat ćete otključati televizor putem računa e-pošte.\n\n Pokušajte ponovo za <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Netočno ste iscrtali obrazac za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Nakon još ovoliko neuspješnih pokušaja: <xliff:g id="NUMBER_1">%2$d</xliff:g> morat ćete otključati telefon pomoću računa e-pošte.\n\n Pokušajte ponovo za <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Ukloni"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Želite li pojačati zvuk iznad preporučene razine?\n\nDugotrajno slušanje glasne glazbe može vam oštetiti sluh."</string>
@@ -1487,6 +1485,7 @@
<item quantity="other">%d h</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (sljedeći alarm)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Dok ne isključite"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Dok ne isključite \"Ne uznemiravaj\""</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-hu-watch/strings.xml b/core/res/res/values-hu-watch/strings.xml
index 7c45326..060ab23 100644
--- a/core/res/res/values-hu-watch/strings.xml
+++ b/core/res/res/values-hu-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_0">%1$d</xliff:g>/<xliff:g id="NUMBER_1">%2$d</xliff:g>. alkalmazás"</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Érzékelők"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"hozzáférés a névjegyekhez"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"hozzáférés ennek az órának a helyadataihoz"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"hozzáférés a naptárhoz"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"SMS-ek küldése és megtekintése"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"hozzáférés az órán lévő fotókhoz, médiatartalmakhoz és fájlokhoz"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"hanganyag rögzítése"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"fotók és videók készítése"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"telefonhívások kezdeményezése és kezelése"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"hozzáférés az érzékelők által mért, életjelekkel kapcsolatos adatokhoz"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"az állapotsor szerepének átvétele"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"hozzáférés a testérzékelőkhöz (például pulzusmérők)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"hozzáférés a pontos (GPS- és hálózatalapú) helyadatokhoz"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"hozzáférés a hozzávetőleges (hálózatalapú) helyadatokhoz"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"parancsok küldése a SIM kártyára"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"teljes hálózati hozzáférés"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"profil és eszköztulajdonosok kezelése"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"WiMAX-állapot módosítása"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"android Beam-átviteli állapot fogadása"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"médiafájlok kimenetének irányítása"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"telepítési munkamenetek olvasása"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"telepítőcsomagok kérése"</string>
</resources>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index b063f0c..a6fdad3 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -53,7 +53,7 @@
<string name="serviceErased" msgid="1288584695297200972">"A törlés sikerült."</string>
<string name="passwordIncorrect" msgid="7612208839450128715">"Helytelen jelszó."</string>
<string name="mmiComplete" msgid="8232527495411698359">"MMI kész."</string>
- <string name="badPin" msgid="9015277645546710014">"A megadott régi PIN kód helytelen."</string>
+ <string name="badPin" msgid="9015277645546710014">"A megadott régi PIN-kód helytelen."</string>
<string name="badPuk" msgid="5487257647081132201">"A megadott PUK kód helytelen."</string>
<string name="mismatchPin" msgid="609379054496863419">"A beírt PIN kódok nem egyeznek."</string>
<string name="invalidPin" msgid="3850018445187475377">"Írjon be egy 4-8 számjegyű PIN kódot."</string>
@@ -75,7 +75,7 @@
<string name="CwMmi" msgid="9129678056795016867">"Hívásvárakoztatás"</string>
<string name="BaMmi" msgid="455193067926770581">"Hívásletiltás"</string>
<string name="PwdMmi" msgid="7043715687905254199">"Jelszómódosítás"</string>
- <string name="PinMmi" msgid="3113117780361190304">"PIN kód módosítása"</string>
+ <string name="PinMmi" msgid="3113117780361190304">"PIN-kód módosítása"</string>
<string name="CnipMmi" msgid="3110534680557857162">"Szám hívása"</string>
<string name="CnirMmi" msgid="3062102121430548731">"Hívószám korlátozva"</string>
<string name="ThreeWCMmi" msgid="9051047170321190368">"Háromutas hívás"</string>
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Beleértve a személyes adatokat, például a hitelkártyaszámokat és jelszavakat."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"állapotsor kikapcsolása vagy módosítása"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Lehetővé teszi az alkalmazás számára az állapotsor kikapcsolását, illetve rendszerikonok hozzáadását és eltávolítását."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"állapotsor"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"az állapotsor szerepének átvétele"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Lehetővé teszi az alkalmazás számára, hogy az állapotsoron legyen."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"állapotsáv részletes- és listanézete"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Lehetővé teszi az alkalmazás számára, hogy váltson az állapotsor részletes és listanézete között."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Lehetővé teszi az alkalmazás számára, hogy WAP-üzeneteket fogadjon és dolgozzon fel. Ez azt is jelenti, hogy az alkalmazás megfigyelheti vagy törölheti a beérkező üzeneteket anélkül, hogy Ön látná azokat."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"futó alkalmazások lekérése"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Lehetővé teszi az alkalmazás számára a jelenleg futó és nemrég befejezett feladatokkal kapcsolatos információk lekérését. Ezáltal az alkalmazás engedélyt kap az eszközön használt alkalmazásokkal kapcsolatos információk felderítésére."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Profil- és eszköztulajdonosok kezelése"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"profil és eszköztulajdonosok kezelése"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Lehetővé teszi, hogy az alkalmazások beállítsák a profil- és az eszköztulajdonosokat."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"futó alkalmazások átrendezése"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Lehetővé teszi az alkalmazás számára, hogy feladatokat helyezzen át az előtérből a háttérbe és fordítva. Az alkalmazás ezt az Ön jóváhagyása nélkül is megteheti."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Lehetővé teszi, hogy az alkalmazás módosítsa a táblagép híváslistáját, beleértve a bejövő és kimenő hívások adatait is. A rosszindulatú alkalmazások ezt arra használhatják, hogy híváslistáját töröljék vagy módosítsák."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Lehetővé teszi, hogy az alkalmazás módosítsa a tévé hívásnaplóját, így például a bejövő és kimenő hívások adatait is. A rosszindulatú alkalmazások ezt hívásnaplója törlésére vagy módosítására használhatják."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Lehetővé teszi, hogy az alkalmazás módosítsa a telefon híváslistáját, beleértve a bejövő és kimenő hívások adatait is. A rosszindulatú alkalmazások ezt arra használhatják, hogy híváslistáját töröljék vagy módosítsák."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"testérzékelők (pl. pulzusmérő)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"hozzáférés a testérzékelőkhöz (például pulzusmérők)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Engedélyezi az alkalmazásnak, hogy hozzáférjen az Ön fizikai állapotát – például a pulzusszámát – figyelő érzékelők adataihoz."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"naptári események és bizalmas információk beolvasása"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Lehetővé teszi az alkalmazás számára a táblagépén tárolt összes naptári esemény beolvasását, beleértve az ismerősök vagy munkatársak eseményeit is. Az alkalmazás így megoszthatja vagy elmentheti az Ön naptáradatait azok titkos vagy bizalmas jellegétől függetlenül."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Lehetővé teszi az alkalmazás számára a telefonon módosítható események hozzáadását, törlését vagy módosítását, beleértve az ismerősök vagy munkatársak eseményeit is. Az engedéllyel rendelkező alkalmazás üzeneteket küldhet, amelyek úgy tűnhetnek, hogy a naptár tulajdonosától származnak, illetve módosíthatják az eseményeket a tulajdonosok tudta nélkül."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"további helyszolgáltatói parancsok elérése"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Lehetővé teszi az alkalmazás számára további helyszolgáltatói parancsok elérését. Ezáltal az alkalmazás beavatkozhat a GPS vagy más helyforrások működésébe."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"pontos (GPS- és hálózatalapú) tartózkodási hely"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"hozzáférés a pontos (GPS- és hálózatalapú) helyadatokhoz"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Lehetővé teszi az alkalmazás számára a pontos tartózkodási helyének lekérését a GPS és a hálózati helyforrások, így például az adótornyok és a Wi-Fi hálózatok adatainak felhasználásával. A helyszolgáltatásokat be kell kapcsolni, és az adatoknak elérhetőknek kell lenniük az eszközén ahhoz, hogy az alkalmazás használhassa őket. Használatukkal az alkalmazások meghatározhatják az Ön tartózkodási helyét, és az akkumulátort is fokozottan fogyaszthatják."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"hozzávetőleges (hálózatalapú) tartózkodási hely"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"hozzáférés a hozzávetőleges (hálózatalapú) helyadatokhoz"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Lehetővé teszi az alkalmazás számára a körülbelüli tartózkodási helyének lekérését. A helymeghatározás a hálózati helyforrások, így például az adótornyok és a Wi-Fi hálózatok adatainak felhasználásával történik. A helyszolgáltatásokat be kell kapcsolni, és az adatoknak elérhetőknek kell lenniük az eszközén ahhoz, hogy az alkalmazás használhassa őket. Használatukkal az alkalmazások meghatározhatják az Ön hozzávetőleges tartózkodási helyét."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"hangbeállítások módosítása"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Lehetővé teszi az alkalmazás számára az általános hangbeállítások, például a hangerő és a használni kívánt kimeneti hangszóró módosítását."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"hanganyag rögzítése"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Lehetővé teszi az alkalmazás számára a mikrofonnal való hangfelvételt.Az engedéllyel rendelkező alkalmazás az Ön jóváhagyása nélkül, bármikor rögzíthet hanganyagot."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"SIM-kommunikáció"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"parancsok küldése a SIM-kártyára"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Engedélyezi, hogy az alkalmazás parancsokat küldjön a SIM kártyára. Ez rendkívül veszélyes lehet."</string>
<string name="permlab_camera" msgid="3616391919559751192">"fotók és videók készítése"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Lehetővé teszi az alkalmazás számára, hogy a fényképezőgéppel fotókat és videókat készítsen. Az engedéllyel rendelkező alkalmazás bármikor, az Ön jóváhagyása nélkül használhatja a fényképezőgépet."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Lehetővé teszi az alkalmazás számára a telefon által ismert hálózatok listájának lekérését; beleértve a telepített alkalmazások által létrehozott bármely fiókot."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"hálózati kapcsolatok megtekintése"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Lehetővé teszi az alkalmazás számára, hogy hozzáférjen hálózati kapcsolatokra vonatkozó információkhoz, például melyek létező hálózatok, és melyek vannak csatlakoztatva."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"teljes hálózati hozzáférés"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"teljes hálózati hozzáférés"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Lehetővé teszi az alkalmazás számára hálózati szoftvercsatornák létrehozását, valamint egyéni hálózati protokollok használatát. A böngésző és egyéb alkalmazások lehetővé teszik adatok küldését az internetre, így ez az engedély nem szükséges az internetre való adatküldéshez."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"hálózati csatlakoztathatóság módosítása"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Lehetővé teszi az alkalmazás számára a hálózati csatlakoztathatóság állapotának módosítását."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Lehetővé teszi az alkalmazás számára, hogy konfigurálja a helyi Bluetooth telefont, valamint hogy távoli eszközöket fedezzen fel és párosítson."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"WiMAX-kapcsolódás és a kapcsolat bontása"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Lehetővé teszi az alkalmazás számára, hogy ellenőrizze, a WiMax engedélyezve van-e, valamint hogy információt gyűjtsön a csatlakoztatott WiMax-hálózatokról."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX-állapot módosítása"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"WiMAX-állapot módosítása"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Lehetővé teszi az alkalmazás számára, hogy a táblagépet csatlakoztassa WiMAX-hálózathoz vagy leválassza azt róla."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Lehetővé teszi az alkalmazás számára a tévé WiMAX-hálózatokhoz való csatlakoztatását, illetve az ilyen hálózatokról való leválasztását."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Lehetővé teszi az alkalmazás számára, hogy a telefont csatlakoztassa WiMAX-hálózathoz vagy leválassza azt róla."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Lehetővé teszi, hogy az alkalmazás módosítsa az érintőképernyő kalibrációs paramétereit. A normál alkalmazásoknál erre elvileg soha nincs szükség."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"DRM-tanúsítványokhoz való hozzáférés"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Engedélyezi egy alkalmazás számára a DRM-tanúsítványokhoz való hozzáférést és azok használatát. Átlagos alkalmazásoknak erre nem lehet szükségük."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Android Beam-átviteli állapot fogadása"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"android Beam-átviteli állapot fogadása"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Lehetővé teszi az alkalmazás számára a folyamatban lévő Android Beam-átvitelekről szóló információk fogadását"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"DRM-tanúsítványok eltávolítása"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Lehetővé teszi, hogy az alkalmazás eltávolítsa a DRM-tanúsítványokat. A normál alkalmazásoknak erre soha nincs szükségük."</string>
@@ -643,14 +643,14 @@
<string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Írja be a PIN kódot"</string>
<string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Írja be a PUK kódot, majd az új PIN kódot"</string>
<string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"PUK kód"</string>
- <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Új PIN kód"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"Új PIN-kód"</string>
<string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Érintsen jelszó megadásához"</font></string>
<string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"A feloldáshoz írja be a jelszót"</string>
<string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Feloldáshoz írja be a PIN kódot"</string>
- <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Helytelen PIN kód."</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Helytelen PIN-kód."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"A feloldáshoz nyomja meg a Menü, majd a 0 gombot."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Segélyhívó szám"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Nincs szolgáltatás."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Nincs szolgáltatás"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"A képernyő le van zárva."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"A feloldáshoz vagy segélyhívás kezdeményezéséhez nyomja meg a Menü gombot."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"A feloldáshoz nyomja meg a Menü gombot."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Nézze meg a felhasználói útmutatót, vagy vegye fel a kapcsolatot az ügyfélszolgálattal."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"A SIM kártya le van zárva."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"SIM kártya feloldása..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"<xliff:g id="NUMBER_0">%d</xliff:g> alkalommal rosszul rajzolta le feloldási mintát. \n\nKérjük, <xliff:g id="NUMBER_1">%d</xliff:g> másodperc múlva próbálja újra."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Ön helytelenül adta meg a jelszót <xliff:g id="NUMBER_0">%d</xliff:g> alkalommal. \n \n Próbálja újra <xliff:g id="NUMBER_1">%d</xliff:g> másodperc múlva."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Ön <xliff:g id="NUMBER_0">%d</xliff:g> alkalommal helytelenül adta meg PIN kódját. \n \n Próbálja újra <xliff:g id="NUMBER_1">%d</xliff:g> másodperc múlva."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Helytelenül rajzolta le a feloldási mintát <xliff:g id="NUMBER_0">%d</xliff:g> alkalommal. További <xliff:g id="NUMBER_1">%d</xliff:g> sikertelen kísérlet után a Google rendszerében használt bejelentkezési adataival kell feloldania a táblagépét.\n\n Kérjük, próbálja újra <xliff:g id="NUMBER_2">%d</xliff:g> másodperc múlva."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"<xliff:g id="NUMBER_0">%d</xliff:g> alkalommal helytelenül adta meg a feloldási mintát. <xliff:g id="NUMBER_1">%d</xliff:g> további sikertelen kísérlet esetén a rendszer arra kéri majd, hogy Google-hoz használt bejelentkezési adataival oldja fel a tévét.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> másodpercen belül ismét próbálkozhat."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Helytelenül rajzolta le a feloldási mintát <xliff:g id="NUMBER_0">%d</xliff:g> alkalommal. További <xliff:g id="NUMBER_1">%d</xliff:g> sikertelen kísérlet után a Google rendszerében használt bejelentkezési adataival kell feloldania a telefonját.\n\n Kérjük, próbálja újra <xliff:g id="NUMBER_2">%d</xliff:g> másodperc múlva."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"A táblagépet <xliff:g id="NUMBER_0">%d</xliff:g> alkalommal próbálta meg sikertelenül feloldani. <xliff:g id="NUMBER_1">%d</xliff:g> további sikertelen próbálkozás után a rendszer visszaállítja a táblagép gyári alapértelmezett beállításait, és minden felhasználói adat elvész."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"<xliff:g id="NUMBER_0">%d</xliff:g> alkalommal sikertelenül próbálta feloldani a tévét. A tévé <xliff:g id="NUMBER_1">%d</xliff:g> további sikertelen kísérlet esetén visszaáll a gyári alapbeállításokra, és minden felhasználói adat elvész."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"A telefont <xliff:g id="NUMBER_0">%d</xliff:g> alkalommal próbálta meg sikertelenül feloldani. <xliff:g id="NUMBER_1">%d</xliff:g> további sikertelen próbálkozás után a rendszer visszaállítja a telefon gyári alapértelmezett beállításait, és minden felhasználói adat elvész."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"<xliff:g id="NUMBER_0">%1$d</xliff:g> alkalommal rosszul rajzolta le feloldási mintát. \n\nKérjük, <xliff:g id="NUMBER_1">%2$d</xliff:g> másodperc múlva próbálja újra."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Ön helytelenül adta meg a jelszót <xliff:g id="NUMBER_0">%1$d</xliff:g> alkalommal. \n \n Próbálja újra <xliff:g id="NUMBER_1">%2$d</xliff:g> másodperc múlva."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Ön <xliff:g id="NUMBER_0">%1$d</xliff:g> alkalommal helytelenül adta meg PIN kódját. \n \n Próbálja újra <xliff:g id="NUMBER_1">%2$d</xliff:g> másodperc múlva."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Helytelenül rajzolta le a feloldási mintát <xliff:g id="NUMBER_0">%1$d</xliff:g> alkalommal. További <xliff:g id="NUMBER_1">%2$d</xliff:g> sikertelen kísérlet után a Google rendszerében használt bejelentkezési adataival kell feloldania a táblagépét.\n\n Kérjük, próbálja újra <xliff:g id="NUMBER_2">%3$d</xliff:g> másodperc múlva."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"<xliff:g id="NUMBER_0">%1$d</xliff:g> alkalommal helytelenül adta meg a feloldási mintát. <xliff:g id="NUMBER_1">%2$d</xliff:g> további sikertelen kísérlet esetén a rendszer arra kéri majd, hogy Google-hoz használt bejelentkezési adataival oldja fel a tévét.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> másodpercen belül ismét próbálkozhat."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Helytelenül rajzolta le a feloldási mintát <xliff:g id="NUMBER_0">%1$d</xliff:g> alkalommal. További <xliff:g id="NUMBER_1">%2$d</xliff:g> sikertelen kísérlet után a Google rendszerében használt bejelentkezési adataival kell feloldania a telefonját.\n\n Kérjük, próbálja újra <xliff:g id="NUMBER_2">%3$d</xliff:g> másodperc múlva."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"A táblagépet <xliff:g id="NUMBER_0">%1$d</xliff:g> alkalommal próbálta meg sikertelenül feloldani. <xliff:g id="NUMBER_1">%2$d</xliff:g> további sikertelen próbálkozás után a rendszer visszaállítja a táblagép gyári alapértelmezett beállításait, és minden felhasználói adat elvész."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"<xliff:g id="NUMBER_0">%1$d</xliff:g> alkalommal sikertelenül próbálta feloldani a tévét. A tévé <xliff:g id="NUMBER_1">%2$d</xliff:g> további sikertelen kísérlet esetén visszaáll a gyári alapbeállításokra, és minden felhasználói adat elvész."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"A telefont <xliff:g id="NUMBER_0">%1$d</xliff:g> alkalommal próbálta meg sikertelenül feloldani. <xliff:g id="NUMBER_1">%2$d</xliff:g> további sikertelen próbálkozás után a rendszer visszaállítja a telefon gyári alapértelmezett beállításait, és minden felhasználói adat elvész."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"A táblagépet <xliff:g id="NUMBER">%d</xliff:g> alkalommal próbálta meg sikertelenül feloldani. A rendszer visszaállítja a táblagép gyári alapértelmezett beállításait."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"<xliff:g id="NUMBER">%d</xliff:g> alkalommal sikertelenül próbálta feloldani a tévét. A tévé most visszaáll a gyári alapbeállításokra."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"A telefont <xliff:g id="NUMBER">%d</xliff:g> alkalommal próbálta meg sikertelenül feloldani. A rendszer visszaállítja a telefon gyári alapértelmezett beállításait."</string>
@@ -699,7 +699,7 @@
<string name="lockscreen_glogin_forgot_pattern" msgid="2588521501166032747">"Fiók feloldása"</string>
<string name="lockscreen_glogin_too_many_attempts" msgid="2751368605287288808">"Túl sok mintarajzolási próbálkozás"</string>
<string name="lockscreen_glogin_instructions" msgid="3931816256100707784">"A feloldáshoz jelentkezzen be Google-fiókjával."</string>
- <string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"Felhasználónév (e-mail cím)"</string>
+ <string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"Felhasználónév (e-mail-cím)"</string>
<string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"Jelszó"</string>
<string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"Bejelentkezés"</string>
<string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"Érvénytelen felhasználónév vagy jelszó."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"szó"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"link"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"sor"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"A gyári teszt sikertelen"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"A FACTORY_TEST művelet csak a /system/app könyvtárba telepített csomagok esetében használható."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Nem található olyan csomag, amely tartalmazná a FACTORY_TEST műveletet."</string>
@@ -986,7 +984,7 @@
<string name="wifi_p2p_from_message" msgid="570389174731951769">"Feladó:"</string>
<string name="wifi_p2p_to_message" msgid="248968974522044099">"Címzett:"</string>
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Adja meg a szükséges PIN kódot:"</string>
- <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN kód:"</string>
+ <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN-kód:"</string>
<string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"A táblagép ideiglenesen lecsatlakozik a Wi-Fi hálózatról, míg a(z) <xliff:g id="DEVICE_NAME">%1$s</xliff:g> eszközhöz csatlakozik"</string>
<string name="wifi_p2p_frequency_conflict_message" product="tv" msgid="3087858235069421128">"A tévé ideiglenesen lekapcsolódik a Wi-Fi-hálózatról addig, amíg a(z) <xliff:g id="DEVICE_NAME">%1$s</xliff:g> eszközhöz csatlakozik."</string>
<string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"A telefon ideiglenesen kilép a Wi-Fi hálózatról, míg a(z) <xliff:g id="DEVICE_NAME">%1$s</xliff:g> eszközhöz csatlakozik."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Formázás…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Nincs behelyezve"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Nincs megfelelő tevékenység."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Médiafájlok kimenetének irányítása"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"médiafájlok kimenetének irányítása"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Lehetővé teszi az alkalmazás számára, hogy más külső eszközökre irányítsa a médiafájlok lejátszását."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Telepítési munkamenetek olvasása"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"telepítési munkamenetek olvasása"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Engedélyezi az alkalmazásnak a telepítési munkamenetek olvasását. Ezáltal részleteket kaphat az egyes csomagok éppen folyamatban lévő telepítéséről."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Telepítőcsomagok kérése"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"telepítőcsomagok kérése"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Lehetővé teszi az alkalmazás számára csomagok telepítésének kérését."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Érintse meg kétszer a nagyítás beállításához"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Nem sikerült hozzáadni a modult."</string>
@@ -1262,41 +1260,41 @@
<string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Elfelejtett minta"</string>
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Helytelen minta"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Helytelen jelszó"</string>
- <string name="kg_wrong_pin" msgid="1131306510833563801">"Helytelen PIN kód"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Helytelen PIN-kód"</string>
<string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Próbálkozzon újra <xliff:g id="NUMBER">%1$d</xliff:g> másodperc múlva."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Rajzolja le a mintát"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Adja meg a SIM kártya PIN kódját"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Adja meg a PIN kódot"</string>
<string name="kg_password_instructions" msgid="5753646556186936819">"Írja be a jelszót"</string>
<string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"A SIM kártya le van tiltva. A folytatáshoz adja meg a PUK kódot. A részletekért vegye fel a kapcsolatot szolgáltatójával."</string>
- <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Kívánt PIN kód megadása"</string>
- <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Kívánt PIN kód megerősítése"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Kívánt PIN-kód megadása"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Kívánt PIN-kód megerősítése"</string>
<string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"SIM kártya feloldása..."</string>
- <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Helytelen PIN kód."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Helytelen PIN-kód."</string>
<string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"4–8 számjegyű PIN kódot írjon be."</string>
<string name="kg_invalid_sim_puk_hint" msgid="6025069204539532000">"A PUK kód 8 karakter hosszú kell, hogy legyen."</string>
<string name="kg_invalid_puk" msgid="3638289409676051243">"Adja meg újra a helyes PUK kódot. Az ismételt próbálkozással véglegesen letiltja a SIM kártyát."</string>
<string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"A PIN kódok nem egyeznek."</string>
<string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Túl sok mintarajzolási próbálkozás"</string>
<string name="kg_login_instructions" msgid="1100551261265506448">"A feloldáshoz jelentkezzen be Google-fiókjával."</string>
- <string name="kg_login_username_hint" msgid="5718534272070920364">"Felhasználónév (e-mail cím)"</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Felhasználónév (e-mail-cím)"</string>
<string name="kg_login_password_hint" msgid="9057289103827298549">"Jelszó"</string>
<string name="kg_login_submit_button" msgid="5355904582674054702">"Bejelentkezés"</string>
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Érvénytelen felhasználónév vagy jelszó."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Elfelejtette a felhasználónevét vagy jelszavát?\nKeresse fel a "<b>"google.com/accounts/recovery"</b>" webhelyet."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Fiók ellenőrzése..."</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"<xliff:g id="NUMBER_0">%d</xliff:g> alkalommal helytelenül adta meg PIN kódját. \n\nPróbálja újra <xliff:g id="NUMBER_1">%d</xliff:g> másodperc múlva."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"<xliff:g id="NUMBER_0">%d</xliff:g> alkalommal helytelenül adta meg a jelszót. \n\nPróbálja újra <xliff:g id="NUMBER_1">%d</xliff:g> másodperc múlva."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"<xliff:g id="NUMBER_0">%d</xliff:g> alkalommal rosszul rajzolta le feloldási mintát. \n\nPróbálja újra <xliff:g id="NUMBER_1">%d</xliff:g> másodperc múlva."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"A táblagépet <xliff:g id="NUMBER_0">%d</xliff:g> alkalommal próbálta meg sikertelenül feloldani. <xliff:g id="NUMBER_1">%d</xliff:g> további sikertelen próbálkozás után a rendszer visszaállítja a táblagép gyári alapértelmezett beállításait, és minden felhasználói adat elvész."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"<xliff:g id="NUMBER_0">%d</xliff:g> alkalommal sikertelenül próbálta feloldani a tévét. A tévé <xliff:g id="NUMBER_1">%d</xliff:g> további sikertelen kísérlet esetén visszaáll a gyári alapbeállításokra, és minden felhasználói adat elvész."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"A telefont <xliff:g id="NUMBER_0">%d</xliff:g> alkalommal próbálta meg sikertelenül feloldani. <xliff:g id="NUMBER_1">%d</xliff:g> további sikertelen próbálkozás után a rendszer visszaállítja a telefon gyári alapértelmezett beállításait, és minden felhasználói adat elvész."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"<xliff:g id="NUMBER_0">%1$d</xliff:g> alkalommal helytelenül adta meg PIN kódját. \n\nPróbálja újra <xliff:g id="NUMBER_1">%2$d</xliff:g> másodperc múlva."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"<xliff:g id="NUMBER_0">%1$d</xliff:g> alkalommal helytelenül adta meg a jelszót. \n\nPróbálja újra <xliff:g id="NUMBER_1">%2$d</xliff:g> másodperc múlva."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"<xliff:g id="NUMBER_0">%1$d</xliff:g> alkalommal rosszul rajzolta le feloldási mintát. \n\nPróbálja újra <xliff:g id="NUMBER_1">%2$d</xliff:g> másodperc múlva."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"A táblagépet <xliff:g id="NUMBER_0">%1$d</xliff:g> alkalommal próbálta meg sikertelenül feloldani. <xliff:g id="NUMBER_1">%2$d</xliff:g> további sikertelen próbálkozás után a rendszer visszaállítja a táblagép gyári alapértelmezett beállításait, és minden felhasználói adat elvész."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"<xliff:g id="NUMBER_0">%1$d</xliff:g> alkalommal sikertelenül próbálta feloldani a tévét. A tévé <xliff:g id="NUMBER_1">%2$d</xliff:g> további sikertelen kísérlet esetén visszaáll a gyári alapbeállításokra, és minden felhasználói adat elvész."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"A telefont <xliff:g id="NUMBER_0">%1$d</xliff:g> alkalommal próbálta meg sikertelenül feloldani. <xliff:g id="NUMBER_1">%2$d</xliff:g> további sikertelen próbálkozás után a rendszer visszaállítja a telefon gyári alapértelmezett beállításait, és minden felhasználói adat elvész."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"A táblagépet <xliff:g id="NUMBER">%d</xliff:g> alkalommal próbálta meg sikertelenül feloldani. A rendszer visszaállítja a táblagép gyári alapértelmezett beállításait."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"<xliff:g id="NUMBER">%d</xliff:g> alkalommal sikertelenül próbálta feloldani a tévét. A tévé most visszaáll a gyári alapbeállításokra."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"A telefont <xliff:g id="NUMBER">%d</xliff:g> alkalommal próbálta meg sikertelenül feloldani. A rendszer visszaállítja a telefon gyári alapértelmezett beállításait."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"<xliff:g id="NUMBER_0">%d</xliff:g> alkalommal helytelenül rajzolta le a feloldási mintát. További <xliff:g id="NUMBER_1">%d</xliff:g> sikertelen kísérlet után egy e-mail fiók használatával kell feloldania a táblagépét.\n\n Kérjük, próbálja újra <xliff:g id="NUMBER_2">%d</xliff:g> másodperc múlva."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"<xliff:g id="NUMBER_0">%d</xliff:g> alkalommal helytelenül adta meg a feloldási mintát. <xliff:g id="NUMBER_1">%d</xliff:g> további sikertelen kísérlet esetén a rendszer arra kéri majd, hogy e-mail fiók használatával oldja fel a tévét.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> másodpercen belül ismét próbálkozhat."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"<xliff:g id="NUMBER_0">%d</xliff:g> alkalommal helytelenül rajzolta le a feloldási mintát. További <xliff:g id="NUMBER_1">%d</xliff:g> sikertelen kísérlet után egy e-mail fiók használatával kell feloldania a telefonját.\n\n Kérjük, próbálja újra <xliff:g id="NUMBER_2">%d</xliff:g> másodperc múlva."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"<xliff:g id="NUMBER_0">%1$d</xliff:g> alkalommal helytelenül rajzolta le a feloldási mintát. További <xliff:g id="NUMBER_1">%2$d</xliff:g> sikertelen kísérlet után egy e-mail fiók használatával kell feloldania a táblagépét.\n\n Kérjük, próbálja újra <xliff:g id="NUMBER_2">%3$d</xliff:g> másodperc múlva."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"<xliff:g id="NUMBER_0">%1$d</xliff:g> alkalommal helytelenül adta meg a feloldási mintát. <xliff:g id="NUMBER_1">%2$d</xliff:g> további sikertelen kísérlet esetén a rendszer arra kéri majd, hogy e-mail fiók használatával oldja fel a tévét.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> másodpercen belül ismét próbálkozhat."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"<xliff:g id="NUMBER_0">%1$d</xliff:g> alkalommal helytelenül rajzolta le a feloldási mintát. További <xliff:g id="NUMBER_1">%2$d</xliff:g> sikertelen kísérlet után egy e-mail fiók használatával kell feloldania a telefonját.\n\n Kérjük, próbálja újra <xliff:g id="NUMBER_2">%3$d</xliff:g> másodperc múlva."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Eltávolítás"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Az ajánlott szint fölé szeretné emelni a hangerőt?\n\nHa hosszú időn át teszi ki magát nagy hangerőnek, azzal károsíthatja a hallását."</string>
@@ -1399,15 +1397,15 @@
<string name="reason_service_unavailable" msgid="7824008732243903268">"A nyomtatási szolgáltatás nincs bekapcsolva"</string>
<string name="print_service_installed_title" msgid="2246317169444081628">"A(z) <xliff:g id="NAME">%s</xliff:g> szolgáltatás telepítve"</string>
<string name="print_service_installed_message" msgid="5897362931070459152">"Koppintson az engedélyezéshez"</string>
- <string name="restr_pin_enter_admin_pin" msgid="783643731895143970">"Rendszergazdai PIN kód megadása"</string>
- <string name="restr_pin_enter_pin" msgid="3395953421368476103">"PIN kód megadása"</string>
+ <string name="restr_pin_enter_admin_pin" msgid="783643731895143970">"Rendszergazdai PIN-kód megadása"</string>
+ <string name="restr_pin_enter_pin" msgid="3395953421368476103">"PIN-kód megadása"</string>
<string name="restr_pin_incorrect" msgid="8571512003955077924">"Helytelen"</string>
- <string name="restr_pin_enter_old_pin" msgid="1462206225512910757">"Jelenlegi PIN kód"</string>
- <string name="restr_pin_enter_new_pin" msgid="5959606691619959184">"Új PIN kód"</string>
- <string name="restr_pin_confirm_pin" msgid="8501523829633146239">"Új PIN kód megerősítése"</string>
- <string name="restr_pin_create_pin" msgid="8017600000263450337">"PIN kód létrehozása a korlátozások módosításához"</string>
+ <string name="restr_pin_enter_old_pin" msgid="1462206225512910757">"Jelenlegi PIN-kód"</string>
+ <string name="restr_pin_enter_new_pin" msgid="5959606691619959184">"Új PIN-kód"</string>
+ <string name="restr_pin_confirm_pin" msgid="8501523829633146239">"Új PIN-kód megerősítése"</string>
+ <string name="restr_pin_create_pin" msgid="8017600000263450337">"PIN-kód létrehozása a korlátozások módosításához"</string>
<string name="restr_pin_error_doesnt_match" msgid="2224214190906994548">"A PIN kódok nem egyeznek. Próbálja újra."</string>
- <string name="restr_pin_error_too_short" msgid="8173982756265777792">"A PIN kód túl rövid. Legalább 4 számjegyből kell állnia."</string>
+ <string name="restr_pin_error_too_short" msgid="8173982756265777792">"A PIN-kód túl rövid. Legalább 4 számjegyből kell állnia."</string>
<plurals name="restr_pin_countdown" formatted="false" msgid="9061246974881224688">
<item quantity="other">Próbálja újra <xliff:g id="COUNT">%d</xliff:g> másodperc múlva</item>
<item quantity="one">Próbálja újra 1 másodperc múlva</item>
@@ -1430,7 +1428,7 @@
<string name="lock_to_app_toast_locked" msgid="9125176335701699164">"Az alkalmazás rögzítve van: a rögzítés feloldása nem engedélyezett ezen az eszközön."</string>
<string name="lock_to_app_start" msgid="6643342070839862795">"Képernyő rögzítve"</string>
<string name="lock_to_app_exit" msgid="8598219838213787430">"Képernyő rögzítése feloldva"</string>
- <string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"PIN kód kérése a rögzítés feloldásához"</string>
+ <string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"PIN-kód kérése a rögzítés feloldásához"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"Feloldási minta kérése a rögzítés feloldásához"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"Jelszó kérése a rögzítés feloldásához"</string>
<string name="package_installed_device_owner" msgid="8420696545959087545">"A rendszergazda telepítette"</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">1 órán keresztül</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Eddig: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Eddig: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (ez a következő riasztás)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Amíg ki nem kapcsolja ezt"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Amíg ki nem kapcsolja a „Ne zavarjanak” lehetőséget"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-hy-rAM-watch/strings.xml b/core/res/res/values-hy-rAM-watch/strings.xml
index 9bff6ad..5aeab8c 100644
--- a/core/res/res/values-hy-rAM-watch/strings.xml
+++ b/core/res/res/values-hy-rAM-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Հավելված <xliff:g id="NUMBER_0">%1$d</xliff:g>՝ <xliff:g id="NUMBER_1">%2$d</xliff:g>-ից:"</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Սենսորներ"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"օգտագործել ձեր կոնտակտները"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"օգտագործել այս ժամացույցի տեղադրությունը"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"օգտագործել ձեր օրացույցը"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"ուղարկել և դիտել SMS հաղորդագրությունները"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"օգտագործել լուսանկարները, մեդիա ֆայլերը և ձեր ժամացույցում պահվող այլ ֆայլերը"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"ձայնագրել"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"լուսանկարել և տեսագրել"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"կատարել հեռախոսազանգեր և կառավարել դրանք"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"օգտագործել ձեր հիմնական ֆիզիոլոգիական ցուցանիշների վերաբերյալ սենսորի տվյալները"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"լինել կարգավիճակի գոտի"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"օգտագործել մարմնի սենսորները (օրինակ` սրտի կծկումների հաճախականության չափիչ)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"օգտագործել ճշգրիտ տեղադրությունը (GPS և ցանցային)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"օգտագործել մոտավոր տեղադրությունը (ցանցային)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"ուղարկել հրամաններ SIM քարտին"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"ունենալ ամբողջական ցանցային մուտք"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"կառավարել պրոֆիլները և սարքի սեփականատերերին"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"փոխել WiMAX-ի կարգավիճակը"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"ստանալ Android Beam-ով փոխանցման կարգավիճակը"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"երթուղել մեդիա արտածումը"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"կարդալ տեղադրման աշխատաշրջանները"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"պահանջել տեղադրման փաթեթներ"</string>
</resources>
diff --git a/core/res/res/values-hy-rAM/strings.xml b/core/res/res/values-hy-rAM/strings.xml
index a295306..e829a76 100644
--- a/core/res/res/values-hy-rAM/strings.xml
+++ b/core/res/res/values-hy-rAM/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Ներառում է անձնական տվյալներ, ինչպիսիք են վարկային քարտերի համարները և գաղտնաբառերը:"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"անջատել կամ փոփոխել կարգավիճակի գոտին"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Թույլ է տալիս հավելվածին անջատել կարգավիճակի գոտին կամ ավելացնել ու հեռացնել համակարգի պատկերակները:"</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"կարգավիճակի գոտի"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"լինել կարգավիճակի գոտի"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Թույլ է տալիս հավելվածին կարգավիճակի գոտին լինել:"</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"ընդլայնել կամ ետ ծալել կարգավիճակի գոտին"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Թույլ է տալիս ծրագրին ընդլայնել կամ ետ ծալել կարգավիճակի գոտին:"</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Թույլ է տալիս հավելվածին ստանալ և գործարկել WAP հաղորդագրությունները: Այս թույլտվությունը ներառում է ձեզ ուղարկված հաղորդագրությունները հետևելու կամ ջնջելու կարողությունը` առանց ձեր տեսնելու:"</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"առբերել աշխատող հավելվածները"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Թույլ է տալիս հավելվածին առբերել մանրամասն տեղեկություններ առկա և վերջերս աշխատող առաջադրանքների մասին: Սա կարող է թույլ տալ հավելվածին հայտնաբերել անձնական տեղեկություններ այլ հավելվածների վերաբերյալ:"</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Պրոֆիլների և սարքի սեփականատերերի կառավարում"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"կառավարել պրոֆիլները և սարքի սեփականատերերին"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Թույլ է տալիս հավելվածներին սահմանել պրոֆիլների սեփականատերերին և սարքի սեփականատիրոջը:"</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"վերադասավորել աշխատող հավելվածները"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Թույլ է տալիս հավելվածին փոխանցել առաջադրանքները առջևք և հետնաշերտ: Հավելվածը կարող է սա անել առանց ձեր ներածման:"</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Թույլ է տալիս հավելվածին փոփոխել ձեր գրասալիկի զանգերի մատյանը, այդ թվում` մուտքային և ելքային զանգերի մասին տվյալները: Վնասարար հավելվածները կարող են սա օգտագործել` ձեր զանգերի մատյանը ջնջելու կամ փոփոխելու համար:"</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Թույլ է տալիս հավելվածին փոփոխել հեռուստացույցի զանգերի մատյանը, այդ թվում` մուտքային և ելքային զանգերի մասին տվյալները: Վնասարար հավելվածները կարող են սա օգտագործել` ձեր զանգերի մատյանը ջնջելու կամ փոփոխելու համար:"</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Թույլ է տալիս հավելվածին փոփոխել ձեր հեռախոսի զանգերի մատյանը, այդ թվում` մուտքային և ելքային զանգերի մասին տվյալները: Վնասարար հավելվածները կարող են սա օգտագործել` ձեր զանգերի մատյանը ջնջելու կամ փոփոխելու համար:"</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"մարմնի սենսորներ (օր.` սրտի)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"օգտագործել մարմնի սենսորները (օրինակ` սրտի կծկումների հաճախականության չափիչ)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Հավելվածին թույլ է տալիս մուտք ունենալ սենսորների տվյալներին, որոնք վերահսկում են ձեր ֆիզիկական վիճակը, օրինակ՝ ձեր սրտի զարկերը:"</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"կարդալ օրացուցային իրադարձությունները և գաղտնի տեղեկությունները"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Թույլ է տալիս հավելվածին կարդալ ձեր գրասալիկում պահված բոլոր օրացուցային իրադարձությունները, այդ թվում` ընկերների կամ գործընկերների: Սա կարող է թույլ տալ հավելվածին տարածել կամ պահել ձեր օրացուցային տվյալները` անկախ գաղտնիությունից կամ զգայունությունից:"</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Թույլ է տալիս հավելվածին ավելացնել, հեռացնել, փոխել այն իրադարձությունները, որոնք կարող եք փոփոխել ձեր հեռախոսից, այդ թվում` ընկերների կամ գործընկերների: Սա կարող է թույլ տալ հավելվածին ուղարկել հաղորդագրություններ, որոնք իբրև գալիս են օրացույցի սեփականատիրոջից, կամ փոփոխել իրադարձությունները` առանց սեփականատիրոջ իմացության:"</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"օգտագործել տեղադրություն տրամադրող հավելվյալ հրամաններ"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Ծրագրին թույլ է տալիս օգտագործել տեղադրության մասին տվյալների աղբյուրների կառավարման լրացուցիչ հրահանգներ: Սա կարող է ծրագրին թույլ տալ միջամտել GPS-ի կամ տեղադրության մասին տվյալների այլ աղբյուրների գործառույթներին:"</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"ճշգրիտ վայրը (ըստ GPS-ի և ցանցի)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"օգտագործել ճշգրիտ տեղադրությունը (GPS և ցանցային)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Թույլ է տալիս հավելվածին ստանալ ձեր ճշգրիտ տեղադրությունը` օգտագործելով Գլոբալ Դիրքավորման Համակարգը (GPS) կամ ցանցային տեղանքի աղբյուրները, ինչպես օրինակ` բջջային աշտարակները և Wi-Fi-ը: Այս տեղադրության ծառայությունները պետք է միացվեն և հասանելի լինեն ձեր սարքի համար, որպեսզի հավելվածն օգտագործի դրանք: Հավելվածները կարող են սա օգտագործել` որոշելու համար ձեր գտնվելու վայրը և կարող են սպառել մարտկոցի լրացուցիչ լիցք:"</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"մոտավոր տեղադրությունը (ցանցային)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"օգտագործել մոտավոր տեղադրությունը (ցանցային)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Թույլ է տալիս հավելվածին ստանալ ձեր մոտավոր տեղադրությունը: Այս տեղադրությունը ստացվում է տեղանքի ծառայությունների կողմից, ինչպես օրինակ` բջջային աշտարակներից և Wi-Fi-ից: Այս տեղանքի ծառայությունները պետք է միացված և հասանելի լինեն ձեր սարքին, որպեսզի հավելվածն օգտագործի դրանք: Հավելվածները կարող են սա օգտագործել` ձեր մոտավոր գտնվելու վայրը որոշելու համար:"</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"փոխել ձեր աուդիո կարգավորումները"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Թույլ է տալիս հավելվածին փոփոխել ձայնանյութի գլոբալ կարգավորումները, ինչպես օրինակ` ձայնը և թե որ խոսափողն է օգտագործված արտածման համար:"</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"ձայնագրել ձայնանյութ"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Թույլ է տալիս հավելվածին բարձրախոսով ձայնագրել ձայնանյութ: Այս թույլտվությունը հնարավորություն է տալիս հավելվածին ձայնանյութ ձայնագրել ցանկացած ժամանակ` առանց ձեր հաստատման:"</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"SIM հաղորդակցում"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"ուղարկել հրամաններ SIM քարտին"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Թույլ է տալիս հավելվածին հրամաններ ուղարկել SIM-ին: Սա շատ վտանգավոր է:"</string>
<string name="permlab_camera" msgid="3616391919559751192">"լուսանկարել և տեսանկարել"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Թույլ է տալիս հավելվածին ֆոտոխցիկով լուսանկարել և տեսանկարել: Այս թույլտվությունը հնարավորություն է տալիս հավելվածին օգտագործել ֆոտոխցիկը ցանկացած ժամանակ` առանց ձեր հաստատման:"</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Թույլ է տալիս հավելվածին ստանալ հեռախոսի կողմից ճանաչված հաշիվների ցանկը: Սա կարող է ներառել ցանկացած հաշիվ, որ ստեղծվել է ձեր տեղադրած հավելվածների կողմից:"</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"դիտել ցանցային միացումները"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Թույլ է տալիս հավելվածին տեսնել ցանցային կապերի մասին տեղեկություններ, ինչպես օրինակ, թե ինչ կապեր կան և որոնք են միացված:"</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"լրիվ ցանցային մուտք"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"ունենալ ամբողջական ցանցային մուտք"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Թույլ է տալիս հավելվածին ստեղծել ցանցային բնիկներ և օգտագործել հատուկ ցանցային պրոտոկոլներ: Զննարկիչը և այլ հավելվածները միջոցներ են տրամադրում ինտերնետին տվյալներ ուղարկելու համար, ուստի այս թույլտվությունը չի պահանջվում ինտերնետին տվյալներ ուղարկելու համար:"</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"փոխել ցանցի կապը"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Թույլ է տալիս հավելվածին փոխել ցանցի միացման կարգավիճակը:"</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Թույլ է տալիս հավելվածին կարգավորել տեղային Bluetooth հեռախոսը և հայտնաբերել ու զուգակցվել հեռակա սարքերի հետ:"</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"միանալ WiMAX-ին և անջատվել դրանից"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Թույլ է տալիս հավելվածին պարզել, արդյոք WiMAX-ը միացված է և ցանկացած միացված WiMAX ցանցի մասին տեղեկություններ:"</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Փոխել WiMAX-ի կարգավիճակը"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"փոխել WiMAX-ի կարգավիճակը"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Թույլ է տալիս հավելվածին գրասալիկը միացնել WiMAX ցանցին և անջատվել այդ ցանցից:"</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Թույլ է տալիս հավելվածին կապակցել հեռուստացույցը և ապակապակցել այն WiMAX ցանցերից:"</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Թույլ է տալիս հավելվածին հեռախոսը միացնել WiMAX ցանցին և անջատել այդ ցանցից:"</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Թույլ է տալիս ծրագրին փոփոխել հպէկրանի չափաբերման կարգավորումները: Սովորական ծրագրերի համար երբեք պետք չի գալու:"</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"DRM հավաստագրերի մատչում"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Ծրագրին թույլ է տալիս տրամադրել և օգտագործել DRM վկայագրեր: Սովորական ծրագրերի համար երբեք պետք չի գալիս:"</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Ստանալ Android Beam-ով փոխանցման կարգավիճակը"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"ստանալ Android Beam-ով փոխանցման կարգավիճակը"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Ծրագրին թույլ է տալիս ստանալ Android Beam-ով ընթացիկ փոխանցումների մասին տեղեկատվություն:"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"հեռացնել DRM վկայագրեր"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Ծրագրին թույլ է տալիս հեռացնել DRM վկայագրեր: Սովորական ծրագրերի համար երբեք պետք չի գալիս:"</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Սխալ PIN ծածկագիր:"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Ապակողպման համար սեղմեք Ցանկ, ապա 0:"</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Արտակարգ իրավիճակների հեռախոսահամար"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Ծառայություն չկա:"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Ծառայություն չկա"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Էկրանը կողպված է:"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Սեղմեք Ցանկ` ապակողպելու համար, կամ կատարեք արտակարգ իրավիճակների զանգ:"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Ապակողպելու համար սեղմեք Ցանկը:"</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Տեսեք Օգտվողի ուղեցույցը կամ դիմեք Բաժանորդների սպասարկման կենտրոն:"</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM քարտը կողպված է:"</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"SIM քարտը ապակողպվում է…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Դուք <xliff:g id="NUMBER_0">%d</xliff:g> անգամ սխալ եք հավաքել ձեր ապակողպման սխեման: \n\nՓորձեք կրկին <xliff:g id="NUMBER_1">%d</xliff:g> վայրկյանից:"</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Դուք սխալ եք մուտքագրել ձեր գաղտնաբառը <xliff:g id="NUMBER_0">%d</xliff:g> անգամ: \n\n Փորձեք կրկին <xliff:g id="NUMBER_1">%d</xliff:g> վայրկյանից:"</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Դուք <xliff:g id="NUMBER_0">%d</xliff:g> անգամ սխալ եք մուտքագրել ձեր PIN-ը: \n\nՓորձեք կրկին <xliff:g id="NUMBER_1">%d</xliff:g> վայրկյանից:"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Դուք <xliff:g id="NUMBER_0">%d</xliff:g> անգամ սխալ եք հավաքել ձեր ապակողպման սխեման: <xliff:g id="NUMBER_1">%d</xliff:g> անգամից ավել անհաջող փորձերից հետո ձեզ կառաջարկվի ապակողպել ձեր գրասալիկը` օգտագործելով ձեր Google-ի մուտքի օգտանունը:\n \n Փորձեք կրկին <xliff:g id="NUMBER_2">%d</xliff:g> վայրկյանից:"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Դուք <xliff:g id="NUMBER_0">%d</xliff:g> անգամ սխալ եք գծել ապակողպման նախշը: Եվս <xliff:g id="NUMBER_1">%d</xliff:g> անհաջող փորձից հետո հեռուստացույցը կկարողանաք ապակողպել միայն մուտք գործելով ձեր Google հաշիվ:\n\n Նորից փորձեք <xliff:g id="NUMBER_2">%d</xliff:g> վայրկյանից:"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Դուք <xliff:g id="NUMBER_0">%d</xliff:g> անգամ սխալ եք հավաքել ձեր ապակողպման սխեման: Եվս <xliff:g id="NUMBER_1">%d</xliff:g> անհաջող փորձից հետո ձեզ կառաջարկվի ապակողպել ձեր հեռախոսը` օգտագործելով Google-ի ձեր մուտքը:\n \n Փորձեք կրկին <xliff:g id="NUMBER_2">%d</xliff:g> վայրկյանից:"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Դուք <xliff:g id="NUMBER_0">%d</xliff:g> անգամ գրասալիկն ապակողպելու սխալ փորձ եք արել: Եվս <xliff:g id="NUMBER_1">%d</xliff:g> անհաջող փորձից հետո գրասալիկը կվերակարգավորվի գործարանային լռելյայնի, և օգտվողի բոլոր տվյալները կկորեն:"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Դուք հեռուստացույցն ապակողպելու <xliff:g id="NUMBER_0">%d</xliff:g> սխալ փորձ եք կատարել: Եվս <xliff:g id="NUMBER_1">%d</xliff:g> անհաջող փորձից հետո հեռուստացույցի գործարանային կարգավորումները կվերականգնվեն և օգտվողի բոլոր տվյալները կջնջվեն:"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Դուք <xliff:g id="NUMBER_0">%d</xliff:g> անգամ հեռախոսը ապակողպելու սխալ փորձ եք արել: Եվս <xliff:g id="NUMBER_1">%d</xliff:g> անհաջող փորձից հետո հեռախոսը կվերակարգավորվի գործարանային սկզբնադիր ռեժիմի, և օգտվողի բոլոր տվյալները կկորեն:"</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Դուք <xliff:g id="NUMBER_0">%1$d</xliff:g> անգամ սխալ եք հավաքել ձեր ապակողպման սխեման: \n\nՓորձեք կրկին <xliff:g id="NUMBER_1">%2$d</xliff:g> վայրկյանից:"</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Դուք սխալ եք մուտքագրել ձեր գաղտնաբառը <xliff:g id="NUMBER_0">%1$d</xliff:g> անգամ: \n\n Փորձեք կրկին <xliff:g id="NUMBER_1">%2$d</xliff:g> վայրկյանից:"</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Դուք <xliff:g id="NUMBER_0">%1$d</xliff:g> անգամ սխալ եք մուտքագրել ձեր PIN-ը: \n\nՓորձեք կրկին <xliff:g id="NUMBER_1">%2$d</xliff:g> վայրկյանից:"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Դուք <xliff:g id="NUMBER_0">%1$d</xliff:g> անգամ սխալ եք հավաքել ձեր ապակողպման սխեման: <xliff:g id="NUMBER_1">%2$d</xliff:g> անգամից ավել անհաջող փորձերից հետո ձեզ կառաջարկվի ապակողպել ձեր գրասալիկը` օգտագործելով ձեր Google-ի մուտքի օգտանունը:\n \n Փորձեք կրկին <xliff:g id="NUMBER_2">%3$d</xliff:g> վայրկյանից:"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Դուք <xliff:g id="NUMBER_0">%1$d</xliff:g> անգամ սխալ եք գծել ապակողպման նախշը: Եվս <xliff:g id="NUMBER_1">%2$d</xliff:g> անհաջող փորձից հետո հեռուստացույցը կկարողանաք ապակողպել միայն մուտք գործելով ձեր Google հաշիվ:\n\n Նորից փորձեք <xliff:g id="NUMBER_2">%3$d</xliff:g> վայրկյանից:"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Դուք <xliff:g id="NUMBER_0">%1$d</xliff:g> անգամ սխալ եք հավաքել ձեր ապակողպման սխեման: Եվս <xliff:g id="NUMBER_1">%2$d</xliff:g> անհաջող փորձից հետո ձեզ կառաջարկվի ապակողպել ձեր հեռախոսը` օգտագործելով Google-ի ձեր մուտքը:\n \n Փորձեք կրկին <xliff:g id="NUMBER_2">%3$d</xliff:g> վայրկյանից:"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Դուք <xliff:g id="NUMBER_0">%1$d</xliff:g> անգամ գրասալիկն ապակողպելու սխալ փորձ եք արել: Եվս <xliff:g id="NUMBER_1">%2$d</xliff:g> անհաջող փորձից հետո գրասալիկը կվերակարգավորվի գործարանային լռելյայնի, և օգտվողի բոլոր տվյալները կկորեն:"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Դուք հեռուստացույցն ապակողպելու <xliff:g id="NUMBER_0">%1$d</xliff:g> սխալ փորձ եք կատարել: Եվս <xliff:g id="NUMBER_1">%2$d</xliff:g> անհաջող փորձից հետո հեռուստացույցի գործարանային կարգավորումները կվերականգնվեն և օգտվողի բոլոր տվյալները կջնջվեն:"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Դուք <xliff:g id="NUMBER_0">%1$d</xliff:g> անգամ հեռախոսը ապակողպելու սխալ փորձ եք արել: Եվս <xliff:g id="NUMBER_1">%2$d</xliff:g> անհաջող փորձից հետո հեռախոսը կվերակարգավորվի գործարանային սկզբնադիր ռեժիմի, և օգտվողի բոլոր տվյալները կկորեն:"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Դուք <xliff:g id="NUMBER">%d</xliff:g> անգամ սխալ փորձ եք արել գրասալիկն ապակողպելու համար: Գրասալիկն այժմ կվերակարգավորվի գործարանային լռելյայնի:"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Դուք հեռուստացույցն ապակողպելու <xliff:g id="NUMBER">%d</xliff:g> սխալ փորձ եք կատարել: Այժմ կվերականգնվեն հեռուստացույցի գործարանային կարգավորումները:"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Դուք <xliff:g id="NUMBER">%d</xliff:g> անգամ հեռախոսը ապակողպելու սխալ փորձ եք արել: Հեռախոսն այժմ կվերակարգավորվի գործարանային սկզբնադիր ռեժիմի:"</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"բառ"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"հղում"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"գիծ"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Գործարանային թեստը ձախողվեց"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"FACTORY_TEST գործողությունը միայն աջակցվում է /համակարգում/ծրագրում տեղադրված փաթեթների համար:"</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Չի գտնվել ոչ մի փաթեթ, որը ապահովում է FACTORY_TEST գործողությունը:"</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Ձևաչափում…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Տեղադրված չէ"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Համընկնող գործունեություններ չգտնվեցին:"</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Երթուղել մեդիա արտածումը"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"երթուղել մեդիա արտածումը"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Թույլ է տալիս հավելվածին մեդիա արտածումը երթուղել այլ արտաքին սարքեր:"</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Կարդալ տեղադրման աշխատաշրջանները"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"կարդալ տեղադրման աշխատաշրջանները"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Ծրագրին թույլ է տալիս կարդալ տեղադրման աշխատաշրջանները: Սա թույլ է տալիս տեղեկանալ փաթեթների ակտիվ տեղադրումների մանրամասներին:"</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Պահանջել փաթեթների տեղադրում"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"պահանջել տեղադրման փաթեթներ"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Թույլ է տալիս հավելվածին պահանջել փաթեթների տեղադրումը:"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Հպեք երկու անգամ` դիտափոխման կարգավորման համար"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Չհաջողվեց վիջեթ ավելացնել:"</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Սխալ օգտանուն կամ գաղտնաբառ:"</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Մոռացե՞լ եք ձեր օգտանունը կամ գաղտնաբառը:\nԱյցելեք "<b>"google.com /accounts/recovery"</b>":"</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Հաշիվը ստուգվում է..."</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Դուք <xliff:g id="NUMBER_0">%d</xliff:g> անգամ սխալ եք մուտքագրել ձեր PIN-ը: \n\nՓորձեք կրկին <xliff:g id="NUMBER_1">%d</xliff:g> վայրկյանից:"</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Դուք սխալ եք մուտքագրել ձեր գաղտնաբառը <xliff:g id="NUMBER_0">%d</xliff:g> անգամ: \n\nՓորձեք կրկին <xliff:g id="NUMBER_1">%d</xliff:g> վայրկյանից:"</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Դուք <xliff:g id="NUMBER_0">%d</xliff:g> անգամ սխալ եք հավաքել ձեր ապակողպման սխեման: \n\nՓորձեք կրկին <xliff:g id="NUMBER_1">%d</xliff:g> վայրկյանից:"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Դուք <xliff:g id="NUMBER_0">%d</xliff:g> անգամ սխալ փորձ եք արել գրասալիկն ապակողպելու համար: <xliff:g id="NUMBER_1">%d</xliff:g> անգամից ավել անհաջող փորձերից հետո գրասալիկը կվերակարգավորվի գործարանային լռելյայնի, և օգտվողի բոլոր տվյալները կկորեն:"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Դուք հեռուստացույցն ապակողպելու <xliff:g id="NUMBER_0">%d</xliff:g> սխալ փորձ եք կատարել: Եվս <xliff:g id="NUMBER_1">%d</xliff:g> անհաջող փորձից հետո հեռուստացույցի գործարանային կարգավորումները կվերականգնվեն և օգտվողի բոլոր տվյալները կջնջվեն:"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Դուք <xliff:g id="NUMBER_0">%d</xliff:g> անգամ սխալ փորձ եք արել հեռախոսն ապակողպելու համար: <xliff:g id="NUMBER_1">%d</xliff:g> անգամից ավել անհաջող փորձերից հետո հեռախոսը կվերակարգավորվի գործարանային լռելյայնի, և օգտվողի բոլոր տվյալները կկորեն:"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Դուք <xliff:g id="NUMBER_0">%1$d</xliff:g> անգամ սխալ եք մուտքագրել ձեր PIN-ը: \n\nՓորձեք կրկին <xliff:g id="NUMBER_1">%2$d</xliff:g> վայրկյանից:"</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Դուք սխալ եք մուտքագրել ձեր գաղտնաբառը <xliff:g id="NUMBER_0">%1$d</xliff:g> անգամ: \n\nՓորձեք կրկին <xliff:g id="NUMBER_1">%2$d</xliff:g> վայրկյանից:"</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Դուք <xliff:g id="NUMBER_0">%1$d</xliff:g> անգամ սխալ եք հավաքել ձեր ապակողպման սխեման: \n\nՓորձեք կրկին <xliff:g id="NUMBER_1">%2$d</xliff:g> վայրկյանից:"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Դուք <xliff:g id="NUMBER_0">%1$d</xliff:g> անգամ սխալ փորձ եք արել գրասալիկն ապակողպելու համար: <xliff:g id="NUMBER_1">%2$d</xliff:g> անգամից ավել անհաջող փորձերից հետո գրասալիկը կվերակարգավորվի գործարանային լռելյայնի, և օգտվողի բոլոր տվյալները կկորեն:"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Դուք հեռուստացույցն ապակողպելու <xliff:g id="NUMBER_0">%1$d</xliff:g> սխալ փորձ եք կատարել: Եվս <xliff:g id="NUMBER_1">%2$d</xliff:g> անհաջող փորձից հետո հեռուստացույցի գործարանային կարգավորումները կվերականգնվեն և օգտվողի բոլոր տվյալները կջնջվեն:"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Դուք <xliff:g id="NUMBER_0">%1$d</xliff:g> անգամ սխալ փորձ եք արել հեռախոսն ապակողպելու համար: <xliff:g id="NUMBER_1">%2$d</xliff:g> անգամից ավել անհաջող փորձերից հետո հեռախոսը կվերակարգավորվի գործարանային լռելյայնի, և օգտվողի բոլոր տվյալները կկորեն:"</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Դուք <xliff:g id="NUMBER">%d</xliff:g> անգամ սխալ փորձ եք արել գրասալիկն ապակողպելու համար: Գրասալիկն այժմ կվերակարգավորվի գործարանային լռելյայնի:"</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Դուք հեռուստացույցն ապակողպելու <xliff:g id="NUMBER">%d</xliff:g> սխալ փորձ եք կատարել: Այժմ կվերականգնվեն հեռուստացույցի գործարանային կարգավորումները:"</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Դուք <xliff:g id="NUMBER">%d</xliff:g> անգամ սխալ փորձ եք արել հեռախոսն ապակողպելու համար: Հեռախոսն այժմ կվերակարգավորվի գործարանային լռելյայնի:"</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Դուք սխալ եք հավաքել ձեր ապակողպման սխեման <xliff:g id="NUMBER_0">%d</xliff:g> անգամ: Եվս <xliff:g id="NUMBER_1">%d</xliff:g> անհաջող փորձից հետո ձեզանից կպահանջվի ապակողպել ձեր գրասալիկը` օգտագործելով էլփոստի հաշիվ:\n\n Փորձեք կրկին <xliff:g id="NUMBER_2">%d</xliff:g> վայրկյանից:"</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Դուք <xliff:g id="NUMBER_0">%d</xliff:g> անգամ սխալ եք գծել ապակողպման նախշը: Եվս <xliff:g id="NUMBER_1">%d</xliff:g> անհաջող փորձից հետո հեռուստացույցը կկարողանաք ապակողպել միայն էլփոստի հաշվի միջոցով:\n\n Նորից փորձեք <xliff:g id="NUMBER_2">%d</xliff:g> վայրկյանից:"</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Դուք <xliff:g id="NUMBER_0">%d</xliff:g> անգամ սխալ եք հավաքել ձեր ապակողպման նմուշը: <xliff:g id="NUMBER_1">%d</xliff:g> անգամից ավել անհաջող փորձերից հետո ձեզ կառաջարկվի ապակողպել ձեր հեռախոսը` օգտագործելով էլփոստի հաշիվ:\n\n Փորձեք կրկին <xliff:g id="NUMBER_2">%d</xliff:g> վայրկյանից:"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Դուք սխալ եք հավաքել ձեր ապակողպման սխեման <xliff:g id="NUMBER_0">%1$d</xliff:g> անգամ: Եվս <xliff:g id="NUMBER_1">%2$d</xliff:g> անհաջող փորձից հետո ձեզանից կպահանջվի ապակողպել ձեր գրասալիկը` օգտագործելով էլփոստի հաշիվ:\n\n Փորձեք կրկին <xliff:g id="NUMBER_2">%3$d</xliff:g> վայրկյանից:"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Դուք <xliff:g id="NUMBER_0">%1$d</xliff:g> անգամ սխալ եք գծել ապակողպման նախշը: Եվս <xliff:g id="NUMBER_1">%2$d</xliff:g> անհաջող փորձից հետո հեռուստացույցը կկարողանաք ապակողպել միայն էլփոստի հաշվի միջոցով:\n\n Նորից փորձեք <xliff:g id="NUMBER_2">%3$d</xliff:g> վայրկյանից:"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Դուք <xliff:g id="NUMBER_0">%1$d</xliff:g> անգամ սխալ եք հավաքել ձեր ապակողպման նմուշը: <xliff:g id="NUMBER_1">%2$d</xliff:g> անգամից ավել անհաջող փորձերից հետո ձեզ կառաջարկվի ապակողպել ձեր հեռախոսը` օգտագործելով էլփոստի հաշիվ:\n\n Փորձեք կրկին <xliff:g id="NUMBER_2">%3$d</xliff:g> վայրկյանից:"</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Հեռացնել"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Ձայնը բարձրացնե՞լ խորհուրդ տրվող մակարդակից ավել:\n\nԵրկարատև բարձրաձայն լսելը կարող է վնասել ձեր լսողությունը:"</string>
@@ -1470,6 +1468,7 @@
<item quantity="other">%d ժամով</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Մինչև <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Մինչև ժ. <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>-ը (հաջորդ զարթուցիչը)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Քանի դեռ չեք անջատել"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Մինչև չանջատեք «Չանհանգստացնել» գործառույթը"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-in-watch/strings.xml b/core/res/res/values-in-watch/strings.xml
index ef88a11..762c9e6 100644
--- a/core/res/res/values-in-watch/strings.xml
+++ b/core/res/res/values-in-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Aplikasi <xliff:g id="NUMBER_0">%1$d</xliff:g> dari <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Sensor"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"akses kontak"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"akses lokasi jam tangan ini"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"akses kalender"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"kirim dan lihat pesan SMS"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"akses foto, media, dan file di jam tangan"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"rekam audio"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"ambil gambar dan rekam video"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"lakukan dan kelola panggilan telepon"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"akses data sensor tentang tanda-tanda vital"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"jadikan bilah status"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"akses sensor tubuh (misalnya, monitor detak jantung)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"akses lokasi akurat (berbasis jaringan dan GPS)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"akses perkiraan lokasi (berbasis jaringan)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"kirimkan perintah ke SIM"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"dapatkan akses jaringan penuh"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"kelola pemilik profil dan perangkat"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"ubah status WiMAX"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"terima status transfer Android Beam"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"tentukan rute keluaran media"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"baca sesi pemasangan"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"minta pasang paket"</string>
</resources>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 27f26a2..f626389 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Meliputi data pribadi seperti nomor kartu kredit dan sandi."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"nonaktifkan atau ubah bilah status"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Mengizinkan apl menonaktifkan bilah status atau menambah dan menghapus ikon sistem."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"bilah status"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"jadikan bilah status"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Mengizinkan apl menjadi bilah status."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"luaskan/ciutkan bilah status"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Mengizinkan apl memperluas atau menciutkan bilah status."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Memungkinkan aplikasi menerima dan memproses pesan WAP. Izin ini mencakup kemampuan untuk memantau atau menghapus pesan yang dikirim kepada Anda tanpa menunjukkannya kepada Anda."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"mengambil apl yang berjalan"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Memungkinkan aplikasi mengambil informasi tentang tugas yang dijalankan saat ini dan baru-baru ini. Izin ini memungkinkan aplikasi menemukan informasi tentang aplikasi mana yang digunakan pada perangkat."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Mengelola pemilik profil dan perangkat"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"kelola pemilik profil dan perangkat"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Mengizinkan aplikasi menyetel pemilik profil dan pemilik perangkat"</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"menyusun ulang apl yang berjalan"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Memungkinkan aplikasi memindah tugas ke latar depan dan latar belakang. Aplikasi dapat melakukannya tanpa masukan dari Anda."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Memungkinkan apl memodifikasi log panggilan tablet Anda, termasuk data tentang panggilan masuk dan keluar. Apl berbahaya dapat menggunakan ini untuk menghapus atau memodifikasi log panggilan Anda."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Mengizinkan aplikasi untuk memodifikasi log panggilan TV, termasuk data tentang panggilan masuk dan keluar. Aplikasi berbahaya mungkin menggunakan ini untuk menghapus atau memodifikasi log panggilan."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Memungkinkan apl memodifikasi log panggilan ponsel Anda, termasuk data tentang panggilan masuk dan keluar. Apl berbahaya dapat menggunakan ini untuk menghapus atau memodifikasi log panggilan Anda."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"sensor tubuh (misal: monitor detak jantung)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"akses sensor tubuh (misalnya, monitor detak jantung)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Mengizinkan aplikasi untuk mengakses data dari sensor yang memantau kondisi fisik Anda, seperti denyut jantung."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"baca acara kalender serta informasi rahasia"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Memungkinkan aplikasi membaca semua acara kalender yang tersimpan di tablet Anda, termasuk milik teman atau rekan kerja. Izin ini memungkinkan aplikasi berbagi atau menyimpan data kalender Anda, terlepas dari kerahasiaan atau sensitivitas."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Memungkinkan aplikasi menambahkan, menghapus, mengubah acara yang dapat Anda ubah pada ponsel, termasuk acara teman atau rekan kerja. Izin ini memungkinkan aplikasi mengirim pesan yang kelihatannya berasal dari pemilik kalender, atau mengubah acara tanpa sepengetahuan pemilik."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"akses perintah penyedia lokasi ekstra"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Memungkinkan aplikasi mengakses perintah penyedia lokasi ekstra. Tindakan ini memungkinkan aplikasi mengganggu pengoperasian GPS atau sumber lokasi lain."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"lokasi akurat (berbasis jaringan dan GPS)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"akses lokasi akurat (berbasis jaringan dan GPS)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Mengizinkan aplikasi memperoleh lokasi Anda yang akurat menggunakan Sistem Pemosisian Global (GPS) atau sumber lokasi jaringan, misalnya menara seluler dan Wi-Fi. Layanan lokasi ini harus diaktifkan dan tersedia untuk perangkat Anda agar aplikasi dapat menggunakannya. Aplikasi dapat menggunakan ini untuk menentukan perkiraan tempat Anda berada dan dapat menghabiskan daya baterai tambahan."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"perkiraan lokasi (berbasis jaringan)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"akses perkiraan lokasi (berbasis jaringan)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Mengizinkan aplikasi untuk mendapatkan perkiraan lokasi Anda. Lokasi ini diperoleh dengan layanan lokasi yang menggunakan sumber lokasi jaringan, misalnya menara seluler dan Wi-Fi. Layanan lokasi ini harus diaktifkan dan tersedia untuk perangkat Anda agar aplikasi dapat menggunakannya. Aplikasi dapat menggunakan ini untuk menentukan perkiraan tempat Anda berada."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"ubah setelan audio Anda"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Memungkinkan aplikasi mengubah setelan audio global, misalnya volume dan pengeras suara mana yang digunakan untuk keluaran."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"rekam audio"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Memungkinkan aplikasi merekam audio dengan mikrofon. Izin ini memungkinkan aplikasi merekam audio kapan saja tanpa konfirmasi Anda."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"komunikasi sim"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"kirimkan perintah ke SIM"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Mengizinkan aplikasi mengirim perintah ke SIM. Ini sangat berbahaya."</string>
<string name="permlab_camera" msgid="3616391919559751192">"ambil gambar dan video"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Memungkinkan aplikasi mengambil gambar dan video dengan kamera. Izin ini memungkinkan aplikasi menggunakan kamera kapan saja tanpa konfirmasi Anda."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Memungkinkan aplikasi mendapatkan daftar akun yang dikenal oleh ponsel. Ini mungkin termasuk akun yang dibuat oleh aplikasi yang telah Anda pasang."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"lihat sambungan jaringan"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Memungkinkan aplikasi melihat informasi tentang sambungan jaringan, misalnya jaringan yang ada dan tersambung."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"akses jaringan penuh"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"dapatkan akses jaringan penuh"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Memungkinkan aplikasi membuat soket jaringan dan menggunakan protokol jaringan khusus. Browser dan aplikasi lain menyediakan sarana untuk mengirim data ke internet sehingga izin ini tidak diperlukan untuk mengirim data ke internet."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"ubah konektivitas jaringan"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Mengizinkan apl mengubah keadaan konektivitas jaringan."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Mengizinkan apl mengonfigurasi ponsel Bluetooth lokal, dan menemukan serta menyandingkan dengan perangkat jarak jauh."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"sambungkan dan putuskan dari WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Memungkinkan aplikasi menentukan apakah WiMAX diaktifkan dan informasi tentang jaringan WiMAX apa saja yang tersambung."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Ubah status WiMAX"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"Ganti status WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Memungkinkan aplikasi menyambungkan tablet ke dan memutus tablet dari jaringan WiMAX."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Mengizinkan aplikasi untuk menghubungkan TV ke dan memutuskan hubungan TV dari jaringan WiMAX."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Memungkinkan aplikasi menyambungkan ponsel ke dan memutus ponsel dari jaringan WiMAX."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Memungkinkan aplikasi mengubah parameter kalibrasi layar sentuh. Tidak diperlukan oleh aplikasi normal."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"mengakses sertifikat DRM"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Memungkinkan aplikasi menyediakan dan menggunakan sertifikat DRM. Tidak pernah dibutuhkan untuk aplikasi normal."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Menerima status transfer Android Beam"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"terima status transfer Android Beam"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Memungkinkan aplikasi ini menerima informasi tentang transfer Android Beam saat ini"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"membuang serifikat DRM"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Memungkinkan aplikasi membuang sertifikat DRM. Tidak pernah dibutuhkan untuk aplikasi normal."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Kode PIN salah."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Untuk membuka, tekan Menu lalu 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Nomor darurat"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Tidak ada layanan."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Tidak ada layanan"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Layar terkunci."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Tekan Menu untuk membuka atau melakukan panggilan darurat."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Tekan Menu untuk membuka."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Lihatlah Panduan Pengguna atau hubungi Layanan Pelanggan."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"Kartu SIM terkunci."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Membuka kartu SIM…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Anda telah <xliff:g id="NUMBER_0">%d</xliff:g> kali salah menggambar pola pembuka kunci. \n\nCoba lagi dalam <xliff:g id="NUMBER_1">%d</xliff:g> detik."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Anda telah <xliff:g id="NUMBER_0">%d</xliff:g> kali salah mengetik sandi. \n\nCoba lagi dalam <xliff:g id="NUMBER_1">%d</xliff:g> detik."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Anda telah <xliff:g id="NUMBER_0">%d</xliff:g> kali salah mengetik PIN. \n\nCoba lagi dalam <xliff:g id="NUMBER_1">%d</xliff:g> detik."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Anda telah <xliff:g id="NUMBER_0">%d</xliff:g> kali salah menggambar pola pembuka kunci. Setelah <xliff:g id="NUMBER_1">%d</xliff:g> lagi upaya gagal, Anda akan diminta membuka kunci tablet menggunakan proses masuk Google.\n\nCoba lagi dalam <xliff:g id="NUMBER_2">%d</xliff:g> detik."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Sudah <xliff:g id="NUMBER_0">%d</xliff:g> kali Anda salah menggambar pola pembuka kunci. Setelah <xliff:g id="NUMBER_1">%d</xliff:g> lagi upaya yang tidak berhasil, Anda akan diminta untuk membuka kunci TV menggunakan info masuk Google.\n\n Coba lagi dalam <xliff:g id="NUMBER_2">%d</xliff:g> detik."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Anda telah <xliff:g id="NUMBER_0">%d</xliff:g> kali salah menggambar pola pembuka kunci. Setelah <xliff:g id="NUMBER_1">%d</xliff:g> lagi upaya gagal, Anda akan diminta membuka kunci ponsel menggunakan proses masuk Google.\n\nCoba lagi dalam <xliff:g id="NUMBER_2">%d</xliff:g> detik."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Anda telah gagal mencoba membuka gembok tablet sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. Setelah <xliff:g id="NUMBER_1">%d</xliff:g> upaya gagal lagi, tablet akan disetel ulang ke setelan default pabrik dan semua data pengguna hilang."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Sudah <xliff:g id="NUMBER_0">%d</xliff:g> kali Anda berupaya membuka kunci TV dengan tidak benar. Setelah <xliff:g id="NUMBER_1">%d</xliff:g> lagi upaya yang tidak berhasil, TV akan dikembalikan ke setelan default pabrik dan semua data pengguna akan hilang."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Anda telah gagal mencoba membuka gembok ponsel sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. Setelah <xliff:g id="NUMBER_1">%d</xliff:g> upaya gagal lagi, ponsel akan disetel ulang ke setelan default pabrik dan semua data pengguna hilang."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali salah menggambar pola pembuka kunci. \n\nCoba lagi dalam <xliff:g id="NUMBER_1">%2$d</xliff:g> detik."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali salah mengetik sandi. \n\nCoba lagi dalam <xliff:g id="NUMBER_1">%2$d</xliff:g> detik."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali salah mengetik PIN. \n\nCoba lagi dalam <xliff:g id="NUMBER_1">%2$d</xliff:g> detik."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali salah menggambar pola pembuka kunci. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya gagal, Anda akan diminta membuka kunci tablet menggunakan proses masuk Google.\n\nCoba lagi dalam <xliff:g id="NUMBER_2">%3$d</xliff:g> detik."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Sudah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali Anda salah menggambar pola pembuka kunci. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya yang tidak berhasil, Anda akan diminta untuk membuka kunci TV menggunakan info masuk Google.\n\n Coba lagi dalam <xliff:g id="NUMBER_2">%3$d</xliff:g> detik."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali salah menggambar pola pembuka kunci. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya gagal, Anda akan diminta membuka kunci ponsel menggunakan proses masuk Google.\n\nCoba lagi dalam <xliff:g id="NUMBER_2">%3$d</xliff:g> detik."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Anda telah gagal mencoba membuka gembok tablet sebanyak <xliff:g id="NUMBER_0">%1$d</xliff:g> kali. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> upaya gagal lagi, tablet akan disetel ulang ke setelan default pabrik dan semua data pengguna hilang."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Sudah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali Anda berupaya membuka kunci TV dengan tidak benar. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya yang tidak berhasil, TV akan dikembalikan ke setelan default pabrik dan semua data pengguna akan hilang."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Anda telah gagal mencoba membuka gembok ponsel sebanyak <xliff:g id="NUMBER_0">%1$d</xliff:g> kali. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> upaya gagal lagi, ponsel akan disetel ulang ke setelan default pabrik dan semua data pengguna hilang."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Anda telah gagal mencoba membuka gembok tablet sebanyak <xliff:g id="NUMBER">%d</xliff:g> kali. Kini tablet akan disetel ulang ke setelan default pabrik."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Sudah <xliff:g id="NUMBER">%d</xliff:g> kali Anda berupaya membuka kunci TV dengan tidak benar. TV sekarang akan dikembalikan ke setelan default pabrik."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Anda telah gagal mencoba membuka gembok ponsel sebanyak <xliff:g id="NUMBER">%d</xliff:g> kali. Kini ponsel akan disetel ulang ke setelan default pabrik."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"kata"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"tautan"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"baris"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Uji pabrik gagal"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Tindakan FACTORY_TEST hanya didukung untuk paket yang terpasang pada /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Tidak ada paket yang memberikan tindakan FACTORY_TEST."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Menformat..."</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Tidak dicolokkan"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Tidak ditemukan aktivitas yang sesuai."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Menentukan rute keluaran media"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"tentukan rute keluaran media"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Memungkinkan aplikasi menentukan rute keluaran media ke perangkat eksternal lainnya."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Membaca sesi pemasangan"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"baca sesi pemasangan"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Memungkinkan aplikasi membaca sesi pemasangan. Tindakan ini memungkinkannya melihat detail tentang pemasangan paket aktif."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Minta pasang paket"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"minta pasang paket"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Mengizinkan aplikasi meminta pemasangan paket."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Sentuh dua kali untuk mengontrol perbesar/perkecil"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Tidak dapat menambahkan widget."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Nama pengguna atau sandi tidak valid."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Lupa nama pengguna atau sandi Anda?\nKunjungi "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Memeriksa akun…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Anda telah <xliff:g id="NUMBER_0">%d</xliff:g> kali salah mengetik PIN. \n\nCoba lagi dalam <xliff:g id="NUMBER_1">%d</xliff:g> detik."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Anda telah <xliff:g id="NUMBER_0">%d</xliff:g> kali salah mengetik sandi. \n\nCoba lagi dalam <xliff:g id="NUMBER_1">%d</xliff:g> detik."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Anda telah <xliff:g id="NUMBER_0">%d</xliff:g> kali salah menggambar pola pembuka kunci. \n\nCoba lagi dalam <xliff:g id="NUMBER_1">%d</xliff:g> detik."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Anda telah <xliff:g id="NUMBER_0">%d</xliff:g> kali gagal saat berusaha membuka kunci tablet. Setelah <xliff:g id="NUMBER_1">%d</xliff:g> lagi upaya gagal, tablet akan disetel ulang ke setelan default pabrik dan semua data pengguna akan hilang."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Sudah <xliff:g id="NUMBER_0">%d</xliff:g> kali Anda berupaya membuka kunci TV dengan tidak benar. Setelah <xliff:g id="NUMBER_1">%d</xliff:g> lagi upaya yang tidak berhasil, TV akan dikembalikan ke setelan default pabrik dan semua data pengguna akan hilang."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Anda telah <xliff:g id="NUMBER_0">%d</xliff:g> kali gagal saat berusaha membuka kunci ponsel. Setelah <xliff:g id="NUMBER_1">%d</xliff:g> lagi upaya gagal, ponsel akan disetel ulang ke setelan default pabrik dan semua data pengguna akan hilang."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali salah mengetik PIN. \n\nCoba lagi dalam <xliff:g id="NUMBER_1">%2$d</xliff:g> detik."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali salah mengetik sandi. \n\nCoba lagi dalam <xliff:g id="NUMBER_1">%2$d</xliff:g> detik."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali salah menggambar pola pembuka kunci. \n\nCoba lagi dalam <xliff:g id="NUMBER_1">%2$d</xliff:g> detik."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali gagal saat berusaha membuka kunci tablet. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya gagal, tablet akan disetel ulang ke setelan default pabrik dan semua data pengguna akan hilang."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Sudah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali Anda berupaya membuka kunci TV dengan tidak benar. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya yang tidak berhasil, TV akan dikembalikan ke setelan default pabrik dan semua data pengguna akan hilang."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali gagal saat berusaha membuka kunci ponsel. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya gagal, ponsel akan disetel ulang ke setelan default pabrik dan semua data pengguna akan hilang."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Anda telah <xliff:g id="NUMBER">%d</xliff:g> kali gagal saat berusaha membuka kunci tablet. Kini tablet akan disetel ulang ke setelan default pabrik."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Sudah <xliff:g id="NUMBER">%d</xliff:g> kali Anda berupaya membuka kunci TV dengan tidak benar. TV sekarang akan dikembalikan ke setelan default pabrik."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Anda telah <xliff:g id="NUMBER">%d</xliff:g> kali gagal saat berusaha untuk membuka kunci ponsel. Kini ponsel akan disetel ulang ke setelan default pabrik."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Anda telah <xliff:g id="NUMBER_0">%d</xliff:g> kali salah menggambar pola pembuka kunci. Setelah <xliff:g id="NUMBER_1">%d</xliff:g> lagi upaya gagal, Anda akan diminta membuka kunci tablet menggunakan akun email.\n\nCoba lagi dalam <xliff:g id="NUMBER_2">%d</xliff:g> detik."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Sudah <xliff:g id="NUMBER_0">%d</xliff:g> kali Anda salah menggambar pola pembuka kunci. Setelah <xliff:g id="NUMBER_1">%d</xliff:g> lagi upaya yang tidak berhasil, Anda akan diminta untuk membuka kunci TV menggunakan akun email.\n\n Coba lagi dalam <xliff:g id="NUMBER_2">%d</xliff:g> detik."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Anda telah <xliff:g id="NUMBER_0">%d</xliff:g> kali salah menggambar pola pembuka kunci. Setelah <xliff:g id="NUMBER_1">%d</xliff:g> lagi upaya gagal, Anda akan diminta membuka kunci ponsel menggunakan akun email.\n\nCoba lagi dalam <xliff:g id="NUMBER_2">%d</xliff:g> detik."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali salah menggambar pola pembuka kunci. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya gagal, Anda akan diminta membuka kunci tablet menggunakan akun email.\n\nCoba lagi dalam <xliff:g id="NUMBER_2">%3$d</xliff:g> detik."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Sudah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali Anda salah menggambar pola pembuka kunci. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya yang tidak berhasil, Anda akan diminta untuk membuka kunci TV menggunakan akun email.\n\n Coba lagi dalam <xliff:g id="NUMBER_2">%3$d</xliff:g> detik."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali salah menggambar pola pembuka kunci. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya gagal, Anda akan diminta membuka kunci ponsel menggunakan akun email.\n\nCoba lagi dalam <xliff:g id="NUMBER_2">%3$d</xliff:g> detik."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Hapus"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Mengeraskan volume di atas tingkat yang disarankan?\n\nMendengarkan dengan volume keras dalam waktu yang lama dapat merusak pendengaran Anda."</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">Selama 1 jam</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Hingga <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Hingga <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (alarm berikutnya)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Hingga Anda menonaktifkan ini"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Hingga Anda menonaktifkan status Jangan Ganggu"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-is-rIS-watch/strings.xml b/core/res/res/values-is-rIS-watch/strings.xml
index d512b6e..74acce6 100644
--- a/core/res/res/values-is-rIS-watch/strings.xml
+++ b/core/res/res/values-is-rIS-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Forrit <xliff:g id="NUMBER_0">%1$d</xliff:g> af <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Skynjarar"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"fá aðgang að tengiliðunum þínum"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"fá aðgang að staðsetningu þessa úrs"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"fá aðgang að dagatalinu þínu"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"senda og skoða SMS-skilaboð"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"fá aðgang að myndum, efni og skrám í úrinu þínu"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"taka upp hljóð"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"taka myndir og myndskeið"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"hringja og stjórna símtölum"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"fá aðgang að skynjaragögnum yfir lífsmörk þín"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"vera stöðustikan"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"fá aðgang að líkamsskynjurum (s.s. hjartsláttarmælum)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"fá aðgang að nákvæmri staðsetningu (frá GPS og símkerfi)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"fá aðgang að áætlaðri staðsetningu (frá símkerfi)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"senda skipanir til SIM-kortsins"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"hafa fullan netaðgang"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"stjórna eigendum sniða og tækja"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"breyta stöðu WiMAX"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"fá flutningsstöðu Android Beam"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"beina margmiðlunarúttaki"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"lesa uppsetningarlotur"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"fara fram á uppsetningu pakka"</string>
</resources>
diff --git a/core/res/res/values-is-rIS/strings.xml b/core/res/res/values-is-rIS/strings.xml
index c09914b..3ebbad7 100644
--- a/core/res/res/values-is-rIS/strings.xml
+++ b/core/res/res/values-is-rIS/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Felur í sér persónuleg gögn á borð við kreditkortanúmer og aðgangsorð."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"slökkva á eða breyta stöðustiku"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Leyfir forriti að slökkva á stöðustikunni eða bæta við og fjarlægja kerfistákn."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"stöðustika"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"vera stöðustikan"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Leyfir forriti að vera stöðustikan."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"stækka/minnka stöðustiku"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Leyfir forriti að stækka og minnka stöðustikuna."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Leyfir forriti að taka á móti og vinna úr WAP-skilaboðum. Þessi heimild felur í sér möguleikann á að fylgjast með eða eyða skilaboðum sem þér eru send án þess að birta þér þau."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"sækja forrit í gangi"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Leyfir forriti að sækja upplýsingar um opin forrit og forrit sem nýlega hafa verið opin. Þetta getur gert forritinu kleift að nálgast upplýsingar um forritin sem notuð eru í tækinu."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Stjórna eigendum sniða og tækis"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"stjórna eigendum sniða og tækja"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Leyfir forritum að stilla eigendur sniða og eiganda tækisins."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"endurraða forritum í gangi"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Leyfir forriti að færa verk á milli forgrunns og bakgrunns. Forritið getur gert þetta án inngrips frá þér."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Leyfir forriti að breyta símtalaskrá spjaldtölvunnar, þ. á m. gögnum um hringd og móttekin símtöl. Spilliforrit geta notað þetta til að eyða eða breyta símtalaskránni."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Leyfir forriti að breyta símtalaskrá sjónvarpsins, þ. á m. gögnum um hringd og móttekin símtöl. Spilliforrit geta notað þetta til að eyða eða breyta símtalaskránni."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Leyfir forriti að breyta símtalaskrá símans, þ. á m. gögnum um hringd og móttekin símtöl. Spilliforrit geta notað þetta til að eyða eða breyta símtalaskránni."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"líkamsskynjarar (s.s. hjartsláttarmælar)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"fá aðgang að líkamsskynjurum (s.s. hjartsláttarmælum)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Veitir forritinu aðgang að gögnum frá skynjurum sem fylgjast með líkamsstarfsemi þinni, svo sem hjartslætti."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"lesa dagatalsviðburði og trúnaðarupplýsingar"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Leyfir forriti að lesa alla dagatalsviðburði sem vistaðir eru í spjaldtölvunni, þ. á m. þá sem vinir eða samstarfsmenn eiga. Þetta getur gert forritinu kleift að deila dagatalsgögnunum þínum burtséð frá því hvort þau innihalda trúnaðargögn eða viðkvæmar upplýsingar."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Leyfir forriti að bæta við, fjarlægja og breyta viðburðum sem hægt er að breyta í símanum, þ. á m. viðburðum sem vinir eða samstarfsmenn eiga. Þetta getur gert forritinu kleift að senda skilaboð sem virðast koma frá eigendum viðburðarins eða breyta viðburðum án vitundar eigenda þeirra."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"aðgangur að viðbótarskipunum staðsetningarveitu"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Leyfir forriti að fá aðgang að fleiri skipunum staðsetningarveitu. Þetta getur gert forritinu kleift að hafa áhrif á virkni GPS og annars staðsetningarbúnaðar."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"nákvæm staðsetning (frá GPS og símakerfi)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"fá aðgang að nákvæmri staðsetningu (frá GPS og símkerfi)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Leyfir forriti að sjá nákvæma staðsetningu þína með hjálp GPS-kerfis eða staðsetningarbúnaðar sem byggir á netkerfum á borð við farsímasenda og Wi-Fi. Það verður að vera kveikt á slíkri staðsetningarþjónustu og hún þarf að vera aðgengileg tækinu til að forritið geti notað hana. Forrit geta notað þjónustuna til að áætla staðsetningu þína og kunna að ganga hraðar á rafhlöðuna."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"gróflega áætluð staðsetning (frá símakerfi)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"fá aðgang að áætlaðri staðsetningu (frá símkerfi)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Leyfir forriti að sjá gróflega áætlaða staðsetningu þína. Sú staðsetning er sótt í staðsetningarbúnað sem byggir á netkerfum á borð við farsímasenda og Wi-Fi. Það verður að vera kveikt á slíkri staðsetningarþjónustu og hún þarf að vera aðgengileg tækinu til að forritið geti notað hana. Forrit geta notað þjónustuna til að áætla staðsetningu þína gróflega."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"breyta hljóðstillingum"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Leyfir forriti að breyta altækum hljóðstillingum, s.s. hljóðstyrk og hvaða hátalari er notaður sem úttak."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"taka upp hljóð"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Leyfir forriti að taka upp hljóð með hljóðnemanum. Þessi heimild leyfir forritinu að taka upp hljóð hvenær sem er án þinnar heimildar."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"sim-samskipti"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"senda skipanir til SIM-kortsins"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Leyfir forriti að senda SIM-kortinu skipanir. Þetta er mjög hættulegt."</string>
<string name="permlab_camera" msgid="3616391919559751192">"taka myndir og myndskeið"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Leyfir forriti að taka myndir og myndskeið með myndavélinni. Þessi heimild leyfir forritinu að nota myndavélina hvenær sem er án þinnar heimildar."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Leyfir forriti að fá lista yfir reikninga sem síminn þekkir. Þar á meðal kunna að vera reikningar stofnaðir af forritum sem þú hefur sett upp."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"skoða nettengingar"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Leyfir forriti að skoða upplýsingar um nettengingar, svo sem hvaða net eru til og eru tengd."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"fullur netaðgangur"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"hafa fullan netaðgang"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Leyfir forriti að búa til nettengla og nota sérstilltar netsamskiptareglur. Vafrinn og önnur forrit geta sjálf sent gögn inn á internetið svo þessi heimild er ekki þörf til að senda gögn á internetið."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"breyta nettengingu"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Leyfir forriti að breyta stöðu nettengingar."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Leyfir forriti að stilla Bluetooth-símann og finna og parast við fjartengd tæki."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"tengjast og aftengja frá WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Leyfir forriti að greina hvort WiMAX er virkt og upplýsingar um tengd WiMAX-net."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Breyta stöðu WiMAX"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"breyta stöðu WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Leyfir forriti að tengja og aftengja spjaldtölvuna við WiMAX-net."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Leyfir forriti að tengja sjónvarpið við WiMAX-net og aftengja frá þeim."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Leyfir forriti að tengja og aftengja símann við WiMAX-net."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Leyfir forriti að breyta kvörðunarbreytum snertiskjásins. Ætti aldrei að vera nauðsynlegt fyrir venjuleg forrit."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"aðgangur að DRM-vottorðum"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Leyfir forriti að úthluta og nota DRM-vottorð. Ætti aldrei að þurfa fyrir venjuleg forrit."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Fá flutningsstöðu Android Beam"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"fá flutningsstöðu Android Beam"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Leyfir þessu forriti að fá upplýsingar um flutning sem fram fer með Android Beam"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"fjarlægja DRM-vottorð"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Leyfir forriti að fjarlægja DRM-vottorð. Ætti aldrei að vera nauðsynlegt fyrir venjuleg forrit."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Rangt PIN-númer."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Til að taka úr lás ýtirðu á valmyndartakkann og síðan 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Neyðarnúmer"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Ekkert símasamband."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Ekkert símasamband"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Skjár læstur."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Ýttu á valmyndartakkann til að taka úr lás eða hringja neyðarsímtal."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Ýttu á valmyndartakkann til að taka úr lás."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Skoðaðu notendahandbókina eða hafðu samband við þjónustudeild."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM-kortið er læst."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Tekur SIM-kort úr lás…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Þú hefur teiknað rangt opnunarmynstur <xliff:g id="NUMBER_0">%d</xliff:g> sinnum. \n\nReyndu aftur eftir <xliff:g id="NUMBER_1">%d</xliff:g> sekúndur."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Þú hefur slegið inn rangt aðgangsorð <xliff:g id="NUMBER_0">%d</xliff:g> sinnum. \n\nReyndu aftur eftir <xliff:g id="NUMBER_1">%d</xliff:g> sekúndur."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Þú hefur slegið inn rangt PIN-númer <xliff:g id="NUMBER_0">%d</xliff:g> sinnum. \n\nReyndu aftur eftir <xliff:g id="NUMBER_1">%d</xliff:g> sekúndur."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Þú hefur teiknað opnunarmynstrið rangt <xliff:g id="NUMBER_0">%d</xliff:g> sinnum. Eftir <xliff:g id="NUMBER_1">%d</xliff:g> árangurslausar tilraunir í viðbót verður beðið um að þú notir Google innskráningu til að opna spjaldtölvuna.\n\n Prófaðu aftur eftir <xliff:g id="NUMBER_2">%d</xliff:g> sekúndur."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Þú hefur teiknað rangt opnunarmynstur <xliff:g id="NUMBER_0">%d</xliff:g> sinnum. Ef þú teiknar rangt mynstur í <xliff:g id="NUMBER_1">%d</xliff:g> skipti í viðbót verður beðið um að þú skráir þig inn á Google til að opna fyrir sjónvarpið.\n\n Reyndu aftur eftir <xliff:g id="NUMBER_2">%d</xliff:g> sekúndur."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Þú hefur teiknað opnunarmynstrið rangt <xliff:g id="NUMBER_0">%d</xliff:g> sinnum. Eftir <xliff:g id="NUMBER_1">%d</xliff:g> árangurslausar tilraunir í viðbót verður beðið um að þú notir Google innskráningu til að opna símann.\n\n Prófaðu aftur eftir <xliff:g id="NUMBER_2">%d</xliff:g> sekúndur."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Þú hefur gert <xliff:g id="NUMBER_0">%d</xliff:g> tilraunir til að taka spjaldtölvuna úr lás. Eftir <xliff:g id="NUMBER_1">%d</xliff:g> árangurslausar tilraunir í viðbót verður spjaldtölvan stillt á sjálfgefnar stillingar og öll notandagögn hennar glatast."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Þú hefur gert <xliff:g id="NUMBER_0">%d</xliff:g> árangurslausar tilraunir til að taka sjónvarpið úr lás. Eftir <xliff:g id="NUMBER_1">%d</xliff:g> skipti í viðbót án árangurs verður sjónvarpið stillt á verksmiðjustillingar og þú glatar öllum notandagögnum."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Þú hefur gert <xliff:g id="NUMBER_0">%d</xliff:g> tilraunir til að taka símann úr lás. Eftir <xliff:g id="NUMBER_1">%d</xliff:g> árangurslausar tilraunir í viðbót verður síminn stilltur á sjálfgefnar stillingar og öll notandagögn hans glatast."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Þú hefur teiknað rangt opnunarmynstur <xliff:g id="NUMBER_0">%1$d</xliff:g> sinnum. \n\nReyndu aftur eftir <xliff:g id="NUMBER_1">%2$d</xliff:g> sekúndur."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Þú hefur slegið inn rangt aðgangsorð <xliff:g id="NUMBER_0">%1$d</xliff:g> sinnum. \n\nReyndu aftur eftir <xliff:g id="NUMBER_1">%2$d</xliff:g> sekúndur."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Þú hefur slegið inn rangt PIN-númer <xliff:g id="NUMBER_0">%1$d</xliff:g> sinnum. \n\nReyndu aftur eftir <xliff:g id="NUMBER_1">%2$d</xliff:g> sekúndur."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Þú hefur teiknað opnunarmynstrið rangt <xliff:g id="NUMBER_0">%1$d</xliff:g> sinnum. Eftir <xliff:g id="NUMBER_1">%2$d</xliff:g> árangurslausar tilraunir í viðbót verður beðið um að þú notir Google innskráningu til að opna spjaldtölvuna.\n\n Prófaðu aftur eftir <xliff:g id="NUMBER_2">%3$d</xliff:g> sekúndur."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Þú hefur teiknað rangt opnunarmynstur <xliff:g id="NUMBER_0">%1$d</xliff:g> sinnum. Ef þú teiknar rangt mynstur í <xliff:g id="NUMBER_1">%2$d</xliff:g> skipti í viðbót verður beðið um að þú skráir þig inn á Google til að opna fyrir sjónvarpið.\n\n Reyndu aftur eftir <xliff:g id="NUMBER_2">%3$d</xliff:g> sekúndur."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Þú hefur teiknað opnunarmynstrið rangt <xliff:g id="NUMBER_0">%1$d</xliff:g> sinnum. Eftir <xliff:g id="NUMBER_1">%2$d</xliff:g> árangurslausar tilraunir í viðbót verður beðið um að þú notir Google innskráningu til að opna símann.\n\n Prófaðu aftur eftir <xliff:g id="NUMBER_2">%3$d</xliff:g> sekúndur."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Þú hefur gert <xliff:g id="NUMBER_0">%1$d</xliff:g> tilraunir til að taka spjaldtölvuna úr lás. Eftir <xliff:g id="NUMBER_1">%2$d</xliff:g> árangurslausar tilraunir í viðbót verður spjaldtölvan stillt á sjálfgefnar stillingar og öll notandagögn hennar glatast."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Þú hefur gert <xliff:g id="NUMBER_0">%1$d</xliff:g> árangurslausar tilraunir til að taka sjónvarpið úr lás. Eftir <xliff:g id="NUMBER_1">%2$d</xliff:g> skipti í viðbót án árangurs verður sjónvarpið stillt á verksmiðjustillingar og þú glatar öllum notandagögnum."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Þú hefur gert <xliff:g id="NUMBER_0">%1$d</xliff:g> tilraunir til að taka símann úr lás. Eftir <xliff:g id="NUMBER_1">%2$d</xliff:g> árangurslausar tilraunir í viðbót verður síminn stilltur á sjálfgefnar stillingar og öll notandagögn hans glatast."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Þú hefur gert <xliff:g id="NUMBER">%d</xliff:g> tilraunir til að taka spjaldtölvuna úr lás. Spjaldtölvan verður nú stillt á sjálfgefnar stillingar."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Þú hefur gert <xliff:g id="NUMBER">%d</xliff:g> árangurslausar tilraunir til að taka sjónvarpið úr lás. Sjónvarpið verður nú stillt aftur á verksmiðjustillingar."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Þú hefur gert <xliff:g id="NUMBER">%d</xliff:g> tilraunir til að taka símann úr lás. Síminn verður nú stilltur á sjálfgefnar stillingar."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"orð"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"tengill"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"lína"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Verksmiðjuprófun mistókst"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Aðgerðin FACTORY_TEST er aðeins studd fyrir pakka sem settir eru upp í /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Enginn pakki fannst sem gefur kost á aðgerðinni FACTORY_TEST."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Forsníður…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Ekki sett í"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Engar aðgerðir með samsvörun fundust."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Beina margmiðlunarúttaki"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"beina margmiðlunarúttaki"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Leyfir forriti að beina margmiðlunarúttaki til annarra ytri tækja."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Lesa uppsetningarlotur"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"lesa uppsetningarlotur"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Leyfir forriti að lesa uppsetningarlotur. Þetta gerir því kleift að sjá upplýsingar um virkar pakkauppsetningar."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Fara fram á uppsetningu pakka"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"fara fram á uppsetningu pakka"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Leyfir forriti að fara fram á uppsetningu pakka."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Ýttu tvisvar til að fá upp aðdráttarstýringar"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Ekki tókst að bæta græju við."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Ógilt notandanafn eða aðgangsorð."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Gleymdirðu notandanafninu eða aðgangsorðinu?\nFarðu á "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Athugar reikninginn…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Þú hefur slegið inn rangt PIN-númer <xliff:g id="NUMBER_0">%d</xliff:g> sinnum. \n\nReyndu aftur eftir <xliff:g id="NUMBER_1">%d</xliff:g> sekúndur."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Þú hefur slegið inn rangt aðgangsorð <xliff:g id="NUMBER_0">%d</xliff:g> sinnum. \n\nReyndu aftur eftir <xliff:g id="NUMBER_1">%d</xliff:g> sekúndur."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Þú hefur teiknað rangt opnunarmynstur <xliff:g id="NUMBER_0">%d</xliff:g> sinnum. \n\nReyndu aftur eftir <xliff:g id="NUMBER_1">%d</xliff:g> sekúndur."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Þú hefur gert <xliff:g id="NUMBER_0">%d</xliff:g> tilraunir til að opna spjaldtölvuna. Eftir <xliff:g id="NUMBER_1">%d</xliff:g> árangurslausar tilraunir í viðbót verður tölvan stillt á sjálfgefar stillingar og öll notandagögn hennar glatast."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Þú hefur gert <xliff:g id="NUMBER_0">%d</xliff:g> árangurslausar tilraunir til að taka sjónvarpið úr lás. Eftir <xliff:g id="NUMBER_1">%d</xliff:g> skipti í viðbót án árangurs verður sjónvarpið stillt á verksmiðjustillingar og þú glatar öllum notandagögnum."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Þú hefur gert <xliff:g id="NUMBER_0">%d</xliff:g> tilraunir til að opna símann. Eftir <xliff:g id="NUMBER_1">%d</xliff:g> árangurslausar tilraunir í viðbót verður síminn stilltur á sjálfgefnar stillingar og öll notandagögn hans glatast."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Þú hefur slegið inn rangt PIN-númer <xliff:g id="NUMBER_0">%1$d</xliff:g> sinnum. \n\nReyndu aftur eftir <xliff:g id="NUMBER_1">%2$d</xliff:g> sekúndur."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Þú hefur slegið inn rangt aðgangsorð <xliff:g id="NUMBER_0">%1$d</xliff:g> sinnum. \n\nReyndu aftur eftir <xliff:g id="NUMBER_1">%2$d</xliff:g> sekúndur."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Þú hefur teiknað rangt opnunarmynstur <xliff:g id="NUMBER_0">%1$d</xliff:g> sinnum. \n\nReyndu aftur eftir <xliff:g id="NUMBER_1">%2$d</xliff:g> sekúndur."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Þú hefur gert <xliff:g id="NUMBER_0">%1$d</xliff:g> tilraunir til að opna spjaldtölvuna. Eftir <xliff:g id="NUMBER_1">%2$d</xliff:g> árangurslausar tilraunir í viðbót verður tölvan stillt á sjálfgefar stillingar og öll notandagögn hennar glatast."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Þú hefur gert <xliff:g id="NUMBER_0">%1$d</xliff:g> árangurslausar tilraunir til að taka sjónvarpið úr lás. Eftir <xliff:g id="NUMBER_1">%2$d</xliff:g> skipti í viðbót án árangurs verður sjónvarpið stillt á verksmiðjustillingar og þú glatar öllum notandagögnum."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Þú hefur gert <xliff:g id="NUMBER_0">%1$d</xliff:g> tilraunir til að opna símann. Eftir <xliff:g id="NUMBER_1">%2$d</xliff:g> árangurslausar tilraunir í viðbót verður síminn stilltur á sjálfgefnar stillingar og öll notandagögn hans glatast."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Þú hefur gert <xliff:g id="NUMBER">%d</xliff:g> tilraunir til að opna spjaldtölvuna án árangurs. Hún verður nú stillt á sjálfgefnar stillingar."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Þú hefur gert <xliff:g id="NUMBER">%d</xliff:g> árangurslausar tilraunir til að taka sjónvarpið úr lás. Sjónvarpið verður nú stillt aftur á verksmiðjustillingar."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Þú hefur gert <xliff:g id="NUMBER">%d</xliff:g> tilraunir til að opna símann án árangurs. Hann verður nú stilltur á sjálfgefnar stillingar."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Þú hefur teiknað rangt opnunarmynstur <xliff:g id="NUMBER_0">%d</xliff:g> sinnum. Eftir <xliff:g id="NUMBER_1">%d</xliff:g> árangurslausar tilraunir í viðbót verður þú beðin(n) um að opna spjaldtölvuna með tölvupóstreikningi.\n\n Reyndu aftur eftir <xliff:g id="NUMBER_2">%d</xliff:g> sekúndur."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Þú hefur teiknað rangt opnunarmynstur <xliff:g id="NUMBER_0">%d</xliff:g> sinnum. Ef þú teiknar rangt mynstur í <xliff:g id="NUMBER_1">%d</xliff:g> skipti í viðbót verður beðið um að þú notir tölvupóstreikning til að opna fyrir sjónvarpið.\n\n Reyndu aftur eftir <xliff:g id="NUMBER_2">%d</xliff:g> sekúndur."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Þú hefur teiknað rangt opnunarmynstur <xliff:g id="NUMBER_0">%d</xliff:g> sinnum. Eftir <xliff:g id="NUMBER_1">%d</xliff:g> árangurslausar tilraunir í viðbót verður þú beðin(n) um að opna símann með tölvupóstreikningi.\n\n Reyndu aftur eftir <xliff:g id="NUMBER_2">%d</xliff:g> sekúndur."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Þú hefur teiknað rangt opnunarmynstur <xliff:g id="NUMBER_0">%1$d</xliff:g> sinnum. Eftir <xliff:g id="NUMBER_1">%2$d</xliff:g> árangurslausar tilraunir í viðbót verður þú beðin(n) um að opna spjaldtölvuna með tölvupóstreikningi.\n\n Reyndu aftur eftir <xliff:g id="NUMBER_2">%3$d</xliff:g> sekúndur."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Þú hefur teiknað rangt opnunarmynstur <xliff:g id="NUMBER_0">%1$d</xliff:g> sinnum. Ef þú teiknar rangt mynstur í <xliff:g id="NUMBER_1">%2$d</xliff:g> skipti í viðbót verður beðið um að þú notir tölvupóstreikning til að opna fyrir sjónvarpið.\n\n Reyndu aftur eftir <xliff:g id="NUMBER_2">%3$d</xliff:g> sekúndur."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Þú hefur teiknað rangt opnunarmynstur <xliff:g id="NUMBER_0">%1$d</xliff:g> sinnum. Eftir <xliff:g id="NUMBER_1">%2$d</xliff:g> árangurslausar tilraunir í viðbót verður þú beðin(n) um að opna símann með tölvupóstreikningi.\n\n Reyndu aftur eftir <xliff:g id="NUMBER_2">%3$d</xliff:g> sekúndur."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Fjarlægja"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Hækka hljóðstyrk umfram ráðlagðan styrk?\n\nEf hlustað er á háum hljóðstyrk í langan tíma kann það að skaða heyrnina."</string>
@@ -1470,6 +1468,7 @@
<item quantity="other">Í %d klst.</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Til <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Þangað til <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (næsta viðvörun)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Þar til þú slekkur á þessu"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Þar til þú slekkur á „Ónáðið ekki“"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-it-watch/strings.xml b/core/res/res/values-it-watch/strings.xml
index aa6a4be..80865f1 100644
--- a/core/res/res/values-it-watch/strings.xml
+++ b/core/res/res/values-it-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"App <xliff:g id="NUMBER_0">%1$d</xliff:g> di <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Sensori"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"accedere ai contatti"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"accedere alla posizione dell\'orologio"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"accedere al calendario"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"inviare e visualizzare SMS"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"accedere a foto, contenuti multimediali e file sull\'orologio"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"registrare audio"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"scattare foto e registrare video"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"eseguire e gestire le telefonate"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"accedere ai dati dei sensori relativi ai tuoi parametri vitali"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"avere la funzione di barra di stato"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"accedere ai sensori (come il cardiofrequenzimetro)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"accedere alla posizione esatta (basata su GPS e rete)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"accedere alla posizione approssimativa (basata sulla rete)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"inviare comandi alla SIM"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"avere accesso completo alla rete"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"gestire proprietari di dispositivi e profili"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"modificare lo stato WiMAX"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"ricevere lo stato dei trasferimenti Android Beam"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"indirizzare l\'output dei contenuti multimediali"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"leggere sessioni di installazione"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"richiedere pacchetti di installazione"</string>
</resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index a034a25..521eae2 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Sono inclusi dati personali come numeri di carte di credito e password."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"disattivare o modificare la barra di stato"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Consente all\'applicazione di disattivare la barra di stato o di aggiungere e rimuovere icone di sistema."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"barra di stato"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"ruolo di barra di stato"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Consente di visualizzare l\'applicazione nella barra di stato."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"espansione/compressione barra di stato"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Consente all\'applicazione di espandere o comprimere la barra di stato."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Consente all\'applicazione di ricevere ed elaborare messaggi WAP. Questa autorizzazione include la facoltà di monitorare o eliminare i messaggi che ti vengono inviati senza mostrarteli."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"recupero applicazioni in esecuzione"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Consente all\'applicazione di recuperare informazioni sulle attività attualmente e recentemente in esecuzione. Ciò potrebbe consentire all\'applicazione di scoprire informazioni sulle applicazioni in uso sul dispositivo."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Gestione dei proprietari di profili e dispositivi"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"gestione dei proprietari di dispositivi e profili"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Consente alle app di impostare i proprietari dei profili e dei dispositivi."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"riordinamento applicazioni in esecuzione"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Consente all\'applicazione di spostare attività in primo piano e in background. L\'applicazione potrebbe farlo senza un tuo comando."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Consente all\'applicazione di modificare il registro chiamate del tablet, inclusi i dati sulle chiamate in arrivo e in uscita. Le applicazioni dannose potrebbero farne uso per cancellare o modificare il registro chiamate."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Consente all\'app di modificare il registro chiamate della TV, inclusi i dati sulle chiamate in arrivo e in uscita. Le app dannose potrebbero farne uso per cancellare o modificare il registro chiamate."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Consente all\'applicazione di modificare il registro chiamate del telefono, inclusi i dati sulle chiamate in arrivo e in uscita. Le applicazioni dannose potrebbero farne uso per cancellare o modificare il registro chiamate."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"sensori per il corpo (come il cardiofrequenzimetro)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"accesso ai sensori (come il cardiofrequenzimetro)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Consente all\'app di accedere ai dati relativi ai sensori che monitorano le tue condizioni fisiche, ad esempio la frequenza cardiaca."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"lettura di eventi di calendario e di informazioni riservate"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Consente all\'applicazione di leggere tutti gli eventi di calendario memorizzati sul tablet, inclusi quelli di amici o colleghi. Ciò potrebbe consentire all\'applicazione di condividere o salvare i dati del tuo calendario, a prescindere dal livello di riservatezza o privacy."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Consente all\'applicazione di aggiungere, rimuovere, modificare gli eventi che puoi modificare sul telefono, inclusi quelli di amici o colleghi. Ciò potrebbe consentire all\'applicazione di inviare messaggi apparentemente provenienti dai proprietari del calendario o di modificare eventi all\'insaputa dei proprietari."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"accesso a comandi aggiuntivi del provider di localizz."</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Consente all\'app di accedere a ulteriori comandi del fornitore di posizione. Ciò potrebbe consentire all\'app di interferire con il funzionamento del GPS o di altre fonti di localizzazione."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"posizione precisa (GPS e basata sulla rete)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"accesso alla posizione esatta (basata su GPS e rete)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Consente all\'applicazione di ottenere la tua posizione esatta utilizzando il sistema GPS (Global Positioning System) o fonti di geolocalizzazione delle reti come ripetitori di telefonia mobile e Wi-Fi. Questi servizi di localizzazione devono essere attivi e disponibili sul dispositivo per poter essere utilizzati dall\'applicazione. Le applicazioni potrebbero utilizzare questa autorizzazione per stabilire la tua posizione e potrebbero consumare ulteriore batteria."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"posizione approssimativa (basata sulla rete)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"accesso alla posizione approssimativa (basata sulla rete)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Consente all\'applicazione di ottenere la tua posizione approssimativa. Questa posizione viene ottenuta da servizi di localizzazione utilizzando fonti di geolocalizzazione delle reti come ripetitori di telefonia mobile e Wi-Fi. Questi servizi di localizzazione devono essere attivi e disponibili sul dispositivo per poter essere utilizzati dall\'applicazione. Le applicazioni potrebbero utilizzare questa autorizzazione per stabilire la tua posizione approssimativa."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"modifica impostazioni audio"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Consente all\'applicazione di modificare le impostazioni audio globali, come il volume e quale altoparlante viene utilizzato per l\'uscita."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"registrare audio"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Consente all\'applicazione di registrare audio con il microfono. Questa autorizzazione consente all\'applicazione di registrare audio in qualsiasi momento senza la tua conferma."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"comunicazione SIM"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"invio di comandi alla SIM"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Consente all\'app di inviare comandi alla SIM. Questo è molto pericoloso."</string>
<string name="permlab_camera" msgid="3616391919559751192">"acquisizione di foto e video"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Consente all\'applicazione di scattare foto e riprendere video con la fotocamera. Questa autorizzazione consente all\'applicazione di utilizzare la fotocamera in qualsiasi momento senza la tua conferma."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Consente all\'applicazione di accedere all\'elenco degli account noti al telefono. Ciò può includere eventuali account creati da applicazioni installate."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"visualizzazione connessioni di rete"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Consente all\'applicazione di visualizzare informazioni sulle connessioni di rete, ad esempio le reti esistenti e connesse."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"accesso di rete completo"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"accesso completo alla rete"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Consente all\'app di creare socket di rete e di utilizzare protocolli di rete personalizzati. Il browser e altre applicazioni forniscono mezzi per inviare i dati a Internet, quindi non è richiesta questa autorizzazione per inviare dati a Internet."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"modifica connettività di rete"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Consente all\'applicazione di modificare lo stato di connettività della rete."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Consente all\'applicazione di configurare il telefono Bluetooth locale e di rilevare ed effettuare l\'accoppiamento con dispositivi remoti."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"connessione e disconnessione da WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Consente all\'applicazione di determinare se WiMAX è abilitato e informazioni su eventuali reti WiMAX che sono connesse."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Modifica stato WiMAX"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"modifica stato WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Consente all\'applicazione di connettere/disconnettere il tablet dalle reti WiMAX."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Consente all\'app di collegare la TV a e scollegarla da reti WiMAX."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Consente all\'applicazione di connettere/disconnettere il telefono dalle reti WiMAX."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Consente all\'app di modificare i parametri di calibrazione del touch screen. Questa opzione non deve essere utilizzata per le app normali."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"accesso a certificati DRM"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Consente a un\'app di fornire e utilizzare ceritificati DRM. Questa opzione non deve essere utilizzata per app normali."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Ricevi lo stato dei trasferimenti Android Beam"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"ricezione dello stato dei trasferimenti Android Beam"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Consente all\'applicazione di ricevere informazioni sugli attuali trasferimenti Android Beam"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"rimozione di certificati DRM"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Consente a un\'applicazione di rimuovere certificati DRM. Non dovrebbe mai essere necessaria per le normali applicazioni."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Codice PIN errato."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Per sbloccare, premi Menu, poi 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Numero di emergenza"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Nessun servizio."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Nessun servizio"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Schermo bloccato."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Premi Menu per sbloccare o effettuare chiamate di emergenza."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Premi Menu per sbloccare."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Consulta la Guida dell\'utente o contatta il servizio clienti."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"La SIM è bloccata."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Sblocco SIM..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"<xliff:g id="NUMBER_0">%d</xliff:g> tentativi errati di inserimento della sequenza di sblocco. \n\nRiprova tra <xliff:g id="NUMBER_1">%d</xliff:g> secondi."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Hai digitato la tua password <xliff:g id="NUMBER_0">%d</xliff:g> volte in modo errato. \n\nRiprova tra <xliff:g id="NUMBER_1">%d</xliff:g> secondi."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Hai digitato il tuo PIN <xliff:g id="NUMBER_0">%d</xliff:g> volte in modo errato. \n\nRiprova tra <xliff:g id="NUMBER_1">%d</xliff:g> secondi."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"<xliff:g id="NUMBER_0">%d</xliff:g> tentativi errati di inserimento della sequenza di sblocco. Dopo altri <xliff:g id="NUMBER_1">%d</xliff:g> tentativi falliti, ti verrà chiesto di sbloccare il tablet con i tuoi dati di accesso Google.\n\n Riprova tra <xliff:g id="NUMBER_2">%d</xliff:g> secondi."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Hai tracciato la sequenza di sblocco sbagliata per <xliff:g id="NUMBER_0">%d</xliff:g> volte. Dopo altri <xliff:g id="NUMBER_1">%d</xliff:g> tentativi non riusciti ti verrà chiesto di sbloccare la TV utilizzando i tuoi dati di accesso di Google.\n\n Riprova tra <xliff:g id="NUMBER_2">%d</xliff:g> secondi."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"<xliff:g id="NUMBER_0">%d</xliff:g> tentativi errati di inserimento della sequenza di sblocco. Dopo altri <xliff:g id="NUMBER_1">%d</xliff:g> tentativi falliti, ti verrà chiesto di sbloccare il telefono con i tuoi dati di accesso Google.\n\n Riprova tra <xliff:g id="NUMBER_2">%d</xliff:g> secondi."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Hai erroneamente tentato di sbloccare il tablet <xliff:g id="NUMBER_0">%d</xliff:g> volte. Dopo altri <xliff:g id="NUMBER_1">%d</xliff:g> tentativi non riusciti, il tablet verrà sottoposto a un ripristino dati di fabbrica e tutti i dati utente andranno persi."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Hai cercato di sbloccare la TV senza riuscirci per <xliff:g id="NUMBER_0">%d</xliff:g> volte. Dopo altri <xliff:g id="NUMBER_1">%d</xliff:g> tentativi non riusciti verranno ripristinate le impostazioni predefinite di fabbrica della TV e tutti i dati utente verranno eliminati."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Hai erroneamente tentato di sbloccare il telefono <xliff:g id="NUMBER_0">%d</xliff:g> volte. Dopo altri <xliff:g id="NUMBER_1">%d</xliff:g> tentativi non riusciti, il telefono verrà sottoposto a un ripristino dati di fabbrica e tutti i dati utente andranno persi."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"<xliff:g id="NUMBER_0">%1$d</xliff:g> tentativi errati di inserimento della sequenza di sblocco. \n\nRiprova tra <xliff:g id="NUMBER_1">%2$d</xliff:g> secondi."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Hai digitato la tua password <xliff:g id="NUMBER_0">%1$d</xliff:g> volte in modo errato. \n\nRiprova tra <xliff:g id="NUMBER_1">%2$d</xliff:g> secondi."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Hai digitato il tuo PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> volte in modo errato. \n\nRiprova tra <xliff:g id="NUMBER_1">%2$d</xliff:g> secondi."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"<xliff:g id="NUMBER_0">%1$d</xliff:g> tentativi errati di inserimento della sequenza di sblocco. Dopo altri <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativi falliti, ti verrà chiesto di sbloccare il tablet con i tuoi dati di accesso Google.\n\n Riprova tra <xliff:g id="NUMBER_2">%3$d</xliff:g> secondi."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Hai tracciato la sequenza di sblocco sbagliata per <xliff:g id="NUMBER_0">%1$d</xliff:g> volte. Dopo altri <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativi non riusciti ti verrà chiesto di sbloccare la TV utilizzando i tuoi dati di accesso di Google.\n\n Riprova tra <xliff:g id="NUMBER_2">%3$d</xliff:g> secondi."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"<xliff:g id="NUMBER_0">%1$d</xliff:g> tentativi errati di inserimento della sequenza di sblocco. Dopo altri <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativi falliti, ti verrà chiesto di sbloccare il telefono con i tuoi dati di accesso Google.\n\n Riprova tra <xliff:g id="NUMBER_2">%3$d</xliff:g> secondi."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Hai erroneamente tentato di sbloccare il tablet <xliff:g id="NUMBER_0">%1$d</xliff:g> volte. Dopo altri <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativi non riusciti, il tablet verrà sottoposto a un ripristino dati di fabbrica e tutti i dati utente andranno persi."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Hai cercato di sbloccare la TV senza riuscirci per <xliff:g id="NUMBER_0">%1$d</xliff:g> volte. Dopo altri <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativi non riusciti verranno ripristinate le impostazioni predefinite di fabbrica della TV e tutti i dati utente verranno eliminati."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Hai erroneamente tentato di sbloccare il telefono <xliff:g id="NUMBER_0">%1$d</xliff:g> volte. Dopo altri <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativi non riusciti, il telefono verrà sottoposto a un ripristino dati di fabbrica e tutti i dati utente andranno persi."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Hai erroneamente tentato di sbloccare il tablet <xliff:g id="NUMBER">%d</xliff:g> volte. Il tablet ora verrà sottoposto a un ripristino dati di fabbrica."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Hai cercato di sbloccare la TV senza riuscirci per <xliff:g id="NUMBER">%d</xliff:g> volte. Verranno ripristinate le impostazioni predefinite di fabbrica della TV."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Hai erroneamente tentato di sbloccare il telefono <xliff:g id="NUMBER">%d</xliff:g> volte. Il telefono ora verrà sottoposto a un ripristino dati di fabbrica."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"parola"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"link"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"linea"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Test di fabbrica non riuscito"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"L\'azione FACTORY_TEST è supportata soltanto per i pacchetti installati in /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Nessun pacchetto trovato che fornisca l\'azione FACTORY_TEST."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Formattazione…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Non inserito"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Nessuna attività corrispondente trovata."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Indirizzamento uscita media"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"indirizzamento dell\'uscita dei contenuti multimediali"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Consente a un\'applicazione di indirizzare l\'uscita di media verso altri dispositivi esterni."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Lettura di sessioni di installazione"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"lettura delle sessioni di installazione"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Consente a un\'applicazione di leggere le sessioni di installazione. L\'app può conoscere i dettagli sulle installazioni di pacchetti attive."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Richiesta di pacchetti di installazione"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"richiesta di pacchetti di installazione"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Consente a un\'applicazione di richiedere l\'installazione di pacchetti."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Tocca due volte per il comando dello zoom"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Aggiunta del widget non riuscita."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Nome utente o password non validi."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Hai dimenticato il nome utente o la password?\nVisita "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Controllo account…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Hai digitato il tuo PIN <xliff:g id="NUMBER_0">%d</xliff:g> volte in modo errato. \n\nRiprova tra <xliff:g id="NUMBER_1">%d</xliff:g> secondi."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Hai digitato la tua password <xliff:g id="NUMBER_0">%d</xliff:g> volte in modo errato. \n\nRiprova tra <xliff:g id="NUMBER_1">%d</xliff:g> secondi."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"<xliff:g id="NUMBER_0">%d</xliff:g> tentativi errati di inserimento della sequenza di sblocco. \n\nRiprova tra <xliff:g id="NUMBER_1">%d</xliff:g> secondi."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"<xliff:g id="NUMBER_0">%d</xliff:g> tentativi errati di sblocco del tablet. Dopo altri <xliff:g id="NUMBER_1">%d</xliff:g> tentativi falliti, il tablet verrà sottoposto a un ripristino dei dati di fabbrica e tutti i dati utente andranno persi."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Hai cercato di sbloccare la TV senza riuscirci per <xliff:g id="NUMBER_0">%d</xliff:g> volte. Dopo altri <xliff:g id="NUMBER_1">%d</xliff:g> tentativi non riusciti verranno ripristinate le impostazioni predefinite di fabbrica della TV e tutti i dati utente verranno eliminati."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"<xliff:g id="NUMBER_0">%d</xliff:g> tentativi errati di sblocco del telefono. Dopo altri <xliff:g id="NUMBER_1">%d</xliff:g> tentativi falliti, il telefono verrà sottoposto a un ripristino dei dati di fabbrica e tutti i dati utente andranno persi."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Hai digitato il tuo PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> volte in modo errato. \n\nRiprova tra <xliff:g id="NUMBER_1">%2$d</xliff:g> secondi."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Hai digitato la tua password <xliff:g id="NUMBER_0">%1$d</xliff:g> volte in modo errato. \n\nRiprova tra <xliff:g id="NUMBER_1">%2$d</xliff:g> secondi."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"<xliff:g id="NUMBER_0">%1$d</xliff:g> tentativi errati di inserimento della sequenza di sblocco. \n\nRiprova tra <xliff:g id="NUMBER_1">%2$d</xliff:g> secondi."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"<xliff:g id="NUMBER_0">%1$d</xliff:g> tentativi errati di sblocco del tablet. Dopo altri <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativi falliti, il tablet verrà sottoposto a un ripristino dei dati di fabbrica e tutti i dati utente andranno persi."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Hai cercato di sbloccare la TV senza riuscirci per <xliff:g id="NUMBER_0">%1$d</xliff:g> volte. Dopo altri <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativi non riusciti verranno ripristinate le impostazioni predefinite di fabbrica della TV e tutti i dati utente verranno eliminati."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"<xliff:g id="NUMBER_0">%1$d</xliff:g> tentativi errati di sblocco del telefono. Dopo altri <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativi falliti, il telefono verrà sottoposto a un ripristino dei dati di fabbrica e tutti i dati utente andranno persi."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"<xliff:g id="NUMBER">%d</xliff:g> tentativi errati di sblocco del tablet. Il tablet verrà sottoposto a un ripristino dei dati di fabbrica."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Hai cercato di sbloccare la TV senza riuscirci per <xliff:g id="NUMBER">%d</xliff:g> volte. Verranno ripristinate le impostazioni predefinite di fabbrica della TV."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"<xliff:g id="NUMBER">%d</xliff:g> tentativi errati di sblocco del telefono. Il telefono verrà sottoposto a un ripristino dei dati di fabbrica."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"<xliff:g id="NUMBER_0">%d</xliff:g> tentativi errati di inserimento della sequenza di sblocco. Dopo altri <xliff:g id="NUMBER_1">%d</xliff:g> tentativi falliti, ti verrà chiesto di sbloccare il tablet con un account email.\n\n Riprova tra <xliff:g id="NUMBER_2">%d</xliff:g> secondi."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Hai tracciato la sequenza di sblocco sbagliata per <xliff:g id="NUMBER_0">%d</xliff:g> volte. Dopo altri <xliff:g id="NUMBER_1">%d</xliff:g> tentativi non riusciti ti verrà chiesto di sbloccare la TV utilizzando un account email.\n\n Riprova tra <xliff:g id="NUMBER_2">%d</xliff:g> secondi."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"<xliff:g id="NUMBER_0">%d</xliff:g> tentativi errati di inserimento della sequenza di sblocco. Dopo altri <xliff:g id="NUMBER_1">%d</xliff:g> tentativi falliti, ti verrà chiesto di sbloccare il telefono con un account email.\n\n Riprova tra <xliff:g id="NUMBER_2">%d</xliff:g> secondi."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"<xliff:g id="NUMBER_0">%1$d</xliff:g> tentativi errati di inserimento della sequenza di sblocco. Dopo altri <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativi falliti, ti verrà chiesto di sbloccare il tablet con un account email.\n\n Riprova tra <xliff:g id="NUMBER_2">%3$d</xliff:g> secondi."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Hai tracciato la sequenza di sblocco sbagliata per <xliff:g id="NUMBER_0">%1$d</xliff:g> volte. Dopo altri <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativi non riusciti ti verrà chiesto di sbloccare la TV utilizzando un account email.\n\n Riprova tra <xliff:g id="NUMBER_2">%3$d</xliff:g> secondi."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"<xliff:g id="NUMBER_0">%1$d</xliff:g> tentativi errati di inserimento della sequenza di sblocco. Dopo altri <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativi falliti, ti verrà chiesto di sbloccare il telefono con un account email.\n\n Riprova tra <xliff:g id="NUMBER_2">%3$d</xliff:g> secondi."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Rimuovi"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Vuoi aumentare il volume oltre il livello consigliato?\n\nL\'ascolto ad alto volume per lunghi periodi di tempo potrebbe danneggiare l\'udito."</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">Per 1 ora</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Fino alle ore <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Fino alle ore <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (prossima sveglia)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Fino alla disattivazione"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Fino alla disattivazione di Non disturbare"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-iw-watch/strings.xml b/core/res/res/values-iw-watch/strings.xml
index 1e4bad4..3d0dde2 100644
--- a/core/res/res/values-iw-watch/strings.xml
+++ b/core/res/res/values-iw-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"אפליקציה <xliff:g id="NUMBER_0">%1$d</xliff:g> מתוך <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"חיישנים"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"גישה אל אנשי הקשר"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"גישה אל מיקום השעון הזה"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"גישה אל היומן"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"שליחה והצגה של הודעות SMS"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"גישה אל תמונות, מדיה וקבצים בשעון שלך"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"הקלטת אודיו"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"צילום תמונות והקלטת סרטונים"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"התקשרות וניהול של שיחות טלפון"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"גישה אל נתוני חיישנים של הסימנים החיוניים שלך"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"להיות שורת מצב"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"גישה אל חיישני גוף (כמו מוניטורים עבור קצב לב)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"גישה אל מיקום מדויק (מבוסס GPS ורשת)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"גישה אל מיקום משוער (מבוסס רשת)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"שלח פקודות אל ה-SIM"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"קבלת גישת רשת מלאה"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"ניהול בעלים של פרופיל ומכשיר"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"שנה את מצב WiMAX"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"קבלת סטטוס העברה של Android Beam"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"ניתוב פלט מדיה"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"קריאת פעילות התקנה"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"בקשה להתקנת חבילות"</string>
</resources>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index a56332c..4eb153a 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -256,7 +256,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"כולל נתונים אישיים כמו מספרי כרטיס אשראי וסיסמאות."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"השבת או שנה את שורת המצב"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"מאפשר לאפליקציה להשבית את שורת המצב או להוסיף ולהסיר סמלי מערכת."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"שורת מצב"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"להיות שורת הסטטוס"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"מאפשר לאפליקציה להופיע בשורת המצב."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"הרחב/כווץ את שורת המצב"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"מאפשר לאפליקציה להרחיב או לכווץ את שורת המצב."</string>
@@ -284,7 +284,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"מאפשר לאפליקציה לקבל ולעבד הודעות WAP. אישור זה כולל את היכולת לעקוב אחר הודעות שנשלחו אליך ולמחוק אותן מבלי להציג לך אותן."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"אחזור אפליקציות פעילות"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"מאפשר לאפליקציה לאחזר מידע לגבי משימות הפועלות כרגע ושפעלו לאחרונה. ייתכן שהדבר יתיר לאפליקציה לגלות מידע לגבי האפליקציות שבהן נעשה שימוש במכשיר."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"ניהול בעלים של פרופיל ומכשיר"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"ניהול בעלים של פרופיל ומכשיר"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"מאפשרת לאפליקציות להגדיר את הבעלים של הפרופיל ואת בעל המכשיר."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"סידור מחדש של אפליקציות פעילות"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"מאפשר לאפליקציה להעביר משימות לחזית ולרקע. האפליקציה עשוי לעשות זאת ללא התערבותך."</string>
@@ -326,7 +326,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"מאפשר לאפליקציה לשנות את יומן השיחות של הטאבלט, כולל נתונים על שיחות נכנסות ויוצאות. אפליקציות זדוניות עלולות לעשות בכך שימוש כדי למחוק או לשנות את יומן השיחות שלך."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"מאפשרת לאפליקציה לשנות את יומן השיחות של הטלוויזיה, כולל נתונים על שיחות נכנסות ויוצאות. אפליקציות זדוניות עלולות להשתמש בהרשאה זו כדי למחוק או לשנות את יומן השיחות שלך."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"מאפשר לאפליקציה לשנות את יומן השיחות של הטלפון, כולל נתונים על שיחות נכנסות ויוצאות. אפליקציות זדוניות עלולות לעשות בכך שימוש כדי למחוק או לשנות את יומן השיחות שלך."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"חיישני גוף (כמו מוניטורים עבור קצב לב)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"גישה אל חיישני גוף (כמו מוניטורים לקצב לב)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"מאפשר לאפליקציה לגשת אל נתוני חיישנים העוקבים אחר מצבך הגופני, כמו קצב הלב."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"קריאת אירועי יומן וגם מידע סודי"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"מאפשר לאפליקציה לקרוא את כל אירועי היומן המאוחסנים בטאבלט, כולל אלה של חברים ועמיתים לעבודה. הדבר עשוי להתיר לאפליקציה לשתף או לשמור את נתוני היומן שלך, ללא התחשבות בסודיות או ברגישות."</string>
@@ -338,15 +338,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"מאפשר לאפליקציה להוסיף, להסיר ולשנות אירועים שאתה יכול לשנות בטלפון, כולל אלה של חברים או עמיתים לעבודה. הדבר עשוי להתיר לאפליקציה לשלוח הודעות הנראות כאילו שנשלחו מבעלי יומן או לשנות אירועים ללא ידיעת הבעלים."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"גישה לפקודות ספק מיקום נוספות"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"מאפשרת לאפליקציה לגשת לפקודות נוספות של ספק המיקום. הרשאה זו עשויה לאפשר לאפליקציה לשבש את פעולת ה-GPS או מקורות מיקום אחרים."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"מיקום מדויק (מבוסס GPS ורשת)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"גישה אל מיקום מדויק (מבוסס GPS ורשת)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"מאפשר לאפליקציה לקבל את המיקום המדויק שלך באמצעות מערכת המיקום הגלובלית (GPS) או מקורות מיקום ברשת כגון אנטנות סלולריות ו-Wi-Fi. שירותי מיקום אלה חייבים להיות מופעלים ונגישים למכשיר שלך כדי שהאפליקציה תשתמש בהם. ייתכן שאפליקציות יעשו בכך שימוש כדי לקבוע היכן אתה נמצא ולגרום לצריכת סוללה מוגברת."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"מיקום משוער (מבוסס רשת)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"גישה אל מיקום משוער (מבוסס רשת)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"מאפשר לאפליקציה לקבל את מיקומך המשוער. מיקום זה נגזר על-פי שירותי מיקום העושים שימוש במקורות מיקום ברשת, כגון אנטנות סלולריות ו-Wi-Fi. שירותי מיקום אלה חייבים להיות מופעלים ונגישים למכשיר שלך כדי שהאפליקציה תשתמש בהם. ייתכן שאפליקציות יעשו בכך שימוש כדי לקבוע את מיקומך המשוער."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"שנה את הגדרות האודיו שלך"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"מאפשר לאפליקציה לשנות הגדרות אודיו גלובליות כמו עוצמת קול ובחירת הרמקול המשמש לפלט."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"הקלט אודיו"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"מאפשר לאפליקציה להקליט אודיו באמצעות המיקרופון. אישור זה מתיר לאפליקציה להקליט אודיו בכל עת ללא אישורך."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"תקשורת SIM"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"שליחת פקודות אל ה-SIM"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"מאפשרת ליישום לשלוח פקודות ל-SIM. זוהי הרשאה מסוכנת מאוד."</string>
<string name="permlab_camera" msgid="3616391919559751192">"צלם תמונות וסרטונים"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"מאפשר לאפליקציה לצלם תמונות וסרטונים באמצעות המצלמה. אישור זה מאפשר לאפליקציה להשתמש במצלמה בכל עת ללא אישורך."</string>
@@ -384,7 +384,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"מאפשר לאפליקציה לקבל רשימה של חשבונות המוכרים לטלפון. הדבר עשוי לכלול חשבונות שנוצרו על ידי אפליקציות שהתקנת."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"הצג חיבורי רשת"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"מאפשר לאפליקציה להציג מידע לגבי חיבורי רשת, למשל, אילו רשתות קיימות ומחוברות."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"גישת רשת מלאה"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"קבלת גישת רשת מלאה"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"מאפשר לאפליקציה ליצור Sockets ולהשתמש בפרוטוקולי רשת מותאמים אישית. הדפדפן, כמו אפליקציות אחרות, מספק אמצעים לשליחת נתונים לאינטרנט, כך שאישור זה אינו נחוץ לשליחת נתונים לאינטרנט."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"שנה את קישוריות הרשת"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"מאפשר לאפליקציה לשנות את מצב הקישוריות של הרשת."</string>
@@ -404,7 +404,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"מאפשר לאפליקציה להגדיר את תצורתו של הטלפון המקומי מסוג Bluetooth וכן לגלות מכשירים מרוחקים ולבצע התאמה איתם."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"התחברות והתנתקות מ-WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"מאפשר לאפליקציה לדעת האם WiNMAX מופעל, כמו גם לקבל מידע האם רשתות WiNMAX כלשהן מחוברות."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"שנה את מצב WiMAX"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"שנה את מצב WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"מאפשר לאפליקציה לחבר את הטאבלט לרשתות WiMAX ולהתנתק מהן."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"מאפשרת לאפליקציה לחבר את הטלוויזיה לרשתות WiMAX ולנתק את החיבור שלה מהן."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"מאפשר לאפליקציה לחבר את הטלפון לרשתות WiMAX ולהתנתק מהן."</string>
@@ -487,7 +487,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"מאפשרת לאפליקציה לשנות את פרמטרי הכיול של מסך המגע. לעולם לא אמורה להיות נחוצה לאפליקציות רגילות."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"גישה אל אישורי DRM"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"מאפשרת לאפליקציה לנהל תצורה של אישורי DRM ולהשתמש בהם. לעולם לא אמורה להיות נחוצה עבור אפליקציה רגילה."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"קבלת סטטוס העברה של Android Beam"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"קבלת סטטוס העברה של Android Beam"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"מאפשר לאפליקציה הזו לקבל מידע על העברות Android Beam נוכחיות"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"הסרת אישורי DRM"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"הרשאה זו מאפשרת לאפליקציה להסיר אישורי DRM. באפליקציות רגילות אף פעם לא אמור להיות בה צורך."</string>
@@ -652,7 +652,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"קוד PIN שגוי"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"כדי לבטל את הנעילה, לחץ על \'תפריט\' ולאחר מכן על 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"מספר חירום"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"אין שירות"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"אין שירות"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"המסך נעול."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"לחץ על \'תפריט\' כדי לבטל את הנעילה או כדי לבצע שיחת חירום."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"לחץ על \'תפריט\' כדי לבטל את הנעילה."</string>
@@ -684,15 +684,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"עיין במדריך למשתמש או פנה לשירות הלקוחות."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"כרטיס ה-SIM נעול."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"מבטל נעילה של כרטיס SIM…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"שרטטת את קו ביטול הנעילה באופן שגוי <xliff:g id="NUMBER_0">%d</xliff:g> פעמים. \n\nנסה שוב בעוד <xliff:g id="NUMBER_1">%d</xliff:g> שניות."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"הקלדת סיסמה שגויה <xliff:g id="NUMBER_0">%d</xliff:g> פעמים.\n\nנסה שוב בעוד <xliff:g id="NUMBER_1">%d</xliff:g> שניות."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"הקלדת קוד PIN שגוי <xliff:g id="NUMBER_0">%d</xliff:g> פעמים.\n\nנסה שוב בעוד <xliff:g id="NUMBER_1">%d</xliff:g> שניות."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"שרטטת באופן שגוי את קו ביטול הנעילה <xliff:g id="NUMBER_0">%d</xliff:g> פעמים. לאחר <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות כושלים נוספים, תתבקש לבטל את נעילת הטאבלט באמצעות פרטי הכניסה שלך ל-Google.\n\nנסה שוב בעוד <xliff:g id="NUMBER_2">%d</xliff:g> שניות."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"שרטטת את קו ביטול הנעילה <xliff:g id="NUMBER_0">%d</xliff:g> פעמים באופן שגוי. לאחר <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות כושלים נוספים, תתבקש לבטל את נעילת הטלוויזיה באמצעות כניסה לחשבון Google שלך.\n\n נסה שוב בעוד <xliff:g id="NUMBER_2">%d</xliff:g> שניות."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"שרטטת את קו ביטול הנעילה באופן שגוי <xliff:g id="NUMBER_0">%d</xliff:g> פעמים. בעוד <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות כושלים נוספים, תתבקש לבטל את נעילת הטלפון באמצעות פרטי הכניסה שלך ל-Google.\n\n נסה שוב בעוד <xliff:g id="NUMBER_2">%d</xliff:g> שניות."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"ביצעת <xliff:g id="NUMBER_0">%d</xliff:g> ניסיונות שגויים לביטול נעילת הטאבלט. לאחר <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות כושלים נוספים, הטאבלט יעבור איפוס לברירת המחדל של היצרן וכל נתוני המשתמש יאבדו."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"ניסית לבטל את נעילת הטלוויזיה <xliff:g id="NUMBER_0">%d</xliff:g> פעמים באופן שגוי. לאחר <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות כושלים נוספים, הטלוויזיה תאופס לנתוני היצרן וכתוצאה מכך כל נתוני המשתמש יאבדו."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"ביצעת <xliff:g id="NUMBER_0">%d</xliff:g> ניסיונות שגויים לביטול נעילת הטלפון. לאחר <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות כושלים נוספים, הטלפון יעבור איפוס לברירת המחדל של היצרן וכל נתוני המשתמש יאבדו."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"שרטטת את קו ביטול הנעילה באופן שגוי <xliff:g id="NUMBER_0">%1$d</xliff:g> פעמים. \n\nנסה שוב בעוד <xliff:g id="NUMBER_1">%2$d</xliff:g> שניות."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"הקלדת סיסמה שגויה <xliff:g id="NUMBER_0">%1$d</xliff:g> פעמים.\n\nנסה שוב בעוד <xliff:g id="NUMBER_1">%2$d</xliff:g> שניות."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"הקלדת קוד PIN שגוי <xliff:g id="NUMBER_0">%1$d</xliff:g> פעמים.\n\nנסה שוב בעוד <xliff:g id="NUMBER_1">%2$d</xliff:g> שניות."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"שרטטת באופן שגוי את קו ביטול הנעילה <xliff:g id="NUMBER_0">%1$d</xliff:g> פעמים. לאחר <xliff:g id="NUMBER_1">%2$d</xliff:g> ניסיונות כושלים נוספים, תתבקש לבטל את נעילת הטאבלט באמצעות פרטי הכניסה שלך ל-Google.\n\nנסה שוב בעוד <xliff:g id="NUMBER_2">%3$d</xliff:g> שניות."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"שרטטת את קו ביטול הנעילה <xliff:g id="NUMBER_0">%1$d</xliff:g> פעמים באופן שגוי. לאחר <xliff:g id="NUMBER_1">%2$d</xliff:g> ניסיונות כושלים נוספים, תתבקש לבטל את נעילת הטלוויזיה באמצעות כניסה לחשבון Google שלך.\n\n נסה שוב בעוד <xliff:g id="NUMBER_2">%3$d</xliff:g> שניות."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"שרטטת את קו ביטול הנעילה באופן שגוי <xliff:g id="NUMBER_0">%1$d</xliff:g> פעמים. בעוד <xliff:g id="NUMBER_1">%2$d</xliff:g> ניסיונות כושלים נוספים, תתבקש לבטל את נעילת הטלפון באמצעות פרטי הכניסה שלך ל-Google.\n\n נסה שוב בעוד <xliff:g id="NUMBER_2">%3$d</xliff:g> שניות."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"ביצעת <xliff:g id="NUMBER_0">%1$d</xliff:g> ניסיונות שגויים לביטול נעילת הטאבלט. לאחר <xliff:g id="NUMBER_1">%2$d</xliff:g> ניסיונות כושלים נוספים, הטאבלט יעבור איפוס לברירת המחדל של היצרן וכל נתוני המשתמש יאבדו."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"ניסית לבטל את נעילת הטלוויזיה <xliff:g id="NUMBER_0">%1$d</xliff:g> פעמים באופן שגוי. לאחר <xliff:g id="NUMBER_1">%2$d</xliff:g> ניסיונות כושלים נוספים, הטלוויזיה תאופס לנתוני היצרן וכתוצאה מכך כל נתוני המשתמש יאבדו."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"ביצעת <xliff:g id="NUMBER_0">%1$d</xliff:g> ניסיונות שגויים לביטול נעילת הטלפון. לאחר <xliff:g id="NUMBER_1">%2$d</xliff:g> ניסיונות כושלים נוספים, הטלפון יעבור איפוס לברירת המחדל של היצרן וכל נתוני המשתמש יאבדו."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"ביצעת <xliff:g id="NUMBER">%d</xliff:g> ניסיונות שגויים לביטול נעילת הטאבלט. הטאבלט יעבור כעת איפוס לברירת המחדל של היצרן."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"ניסית לבטל את נעילת הטלוויזיה <xliff:g id="NUMBER">%d</xliff:g> פעמים באופן שגוי. הטלוויזיה תאופס כעת לנתוני היצרן."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"ביצעת <xliff:g id="NUMBER">%d</xliff:g> ניסיונות שגויים לביטול נעילת הטלפון. הטלפון יעבור כעת איפוס לברירת המחדל של היצרן."</string>
@@ -744,8 +744,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"מילה"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"קישור"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"שורה"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"בדיקת היצרן נכשלה"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"הפעולה FACTORY_TEST נתמכת רק עבור חבילות שהותקנו ב-/system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"לא נמצאה חבילה המספקת את הפעולה FACTORY_TEST."</string>
@@ -1087,11 +1085,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"מפרמט…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"לא הוכנס"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"לא נמצאו פעילויות תואמות."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"ניתוב פלט מדיה"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"ניתוב פלט מדיה"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"מאפשר לאפליקציה לנתב פלט מדיה למכשירים חיצוניים אחרים."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"קריאת פעילות התקנה"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"קריאת פעילות התקנה"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"מאפשר לאפליקציה לקרוא הפעלות התקנה. הרשאה זו מאפשרת לה לראות פרטים על התקנות פעילות של חבילות."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"בקשה להתקנת חבילות"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"בקשה להתקנת חבילות"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"מתיר לאפליקציה לבקש התקנה של חבילות."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"גע פעמיים לבקרת מרחק מתצוגה"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"לא ניתן להוסיף widget."</string>
@@ -1301,18 +1299,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"שם משתמש או סיסמה לא חוקיים."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"שכחת את שם המשתמש או הסיסמה?\nבקר בכתובת "<b>"google.com/accounts/recovery"</b></string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"בודק חשבון…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"הקלדת מספר PIN שגוי <xliff:g id="NUMBER_0">%d</xliff:g> פעמים. \n\nנסה שוב בעוד <xliff:g id="NUMBER_1">%d</xliff:g> שניות."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"הקלדת סיסמה שגויה <xliff:g id="NUMBER_0">%d</xliff:g> פעמים.\n\nנסה שוב בעוד <xliff:g id="NUMBER_1">%d</xliff:g> שניות."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"שרטטת את קו ביטול הנעילה באופן שגוי <xliff:g id="NUMBER_0">%d</xliff:g> פעמים. \n\nנסה שוב בעוד <xliff:g id="NUMBER_1">%d</xliff:g> שניות."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"ביצעת <xliff:g id="NUMBER_0">%d</xliff:g> ניסיונות שגויים לביטול נעילת הטלפון. לאחר <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות כושלים נוספים, הטאבלט יעבור איפוס לברירת המחדל של היצרן וכל נתוני המשתמש יאבדו."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"ניסית לבטל את נעילת הטלוויזיה <xliff:g id="NUMBER_0">%d</xliff:g> פעמים באופן שגוי. לאחר <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות כושלים נוספים, הטלוויזיה תאופס לנתוני היצרן וכתוצאה מכך כל נתוני המשתמש יאבדו."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"ביצעת <xliff:g id="NUMBER_0">%d</xliff:g> ניסיונות שגויים לביטול נעילת הטלפון. לאחר <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות כושלים נוספים, הטלפון יעבור איפוס לברירת המחדל של היצרן וכל נתוני המשתמש יאבדו."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"הקלדת מספר PIN שגוי <xliff:g id="NUMBER_0">%1$d</xliff:g> פעמים. \n\nנסה שוב בעוד <xliff:g id="NUMBER_1">%2$d</xliff:g> שניות."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"הקלדת סיסמה שגויה <xliff:g id="NUMBER_0">%1$d</xliff:g> פעמים.\n\nנסה שוב בעוד <xliff:g id="NUMBER_1">%2$d</xliff:g> שניות."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"שרטטת את קו ביטול הנעילה באופן שגוי <xliff:g id="NUMBER_0">%1$d</xliff:g> פעמים. \n\nנסה שוב בעוד <xliff:g id="NUMBER_1">%2$d</xliff:g> שניות."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"ביצעת <xliff:g id="NUMBER_0">%1$d</xliff:g> ניסיונות שגויים לביטול נעילת הטלפון. לאחר <xliff:g id="NUMBER_1">%2$d</xliff:g> ניסיונות כושלים נוספים, הטאבלט יעבור איפוס לברירת המחדל של היצרן וכל נתוני המשתמש יאבדו."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"ניסית לבטל את נעילת הטלוויזיה <xliff:g id="NUMBER_0">%1$d</xliff:g> פעמים באופן שגוי. לאחר <xliff:g id="NUMBER_1">%2$d</xliff:g> ניסיונות כושלים נוספים, הטלוויזיה תאופס לנתוני היצרן וכתוצאה מכך כל נתוני המשתמש יאבדו."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"ביצעת <xliff:g id="NUMBER_0">%1$d</xliff:g> ניסיונות שגויים לביטול נעילת הטלפון. לאחר <xliff:g id="NUMBER_1">%2$d</xliff:g> ניסיונות כושלים נוספים, הטלפון יעבור איפוס לברירת המחדל של היצרן וכל נתוני המשתמש יאבדו."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"ביצעת <xliff:g id="NUMBER">%d</xliff:g> ניסיונות שגויים לביטול נעילת הטאבלט. הטאבלט יעבור כעת איפוס לברירת המחדל של היצרן."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"ניסית לבטל את נעילת הטלוויזיה <xliff:g id="NUMBER">%d</xliff:g> פעמים באופן שגוי. הטלוויזיה תאופס כעת לנתוני היצרן."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"ביצעת <xliff:g id="NUMBER">%d</xliff:g> ניסיונות שגויים לביטול נעילת הטלפון. הטלפון יעבור כעת איפוס לברירת המחדל של היצרן."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"שרטטת את קו ביטול הנעילה באופן שגוי <xliff:g id="NUMBER_0">%d</xliff:g> פעמים. לאחר <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות כושלים נוספים, תתבקש לבטל את נעילת הטאבלט באמצעות חשבון אימייל.\n\nנסה שוב בעוד <xliff:g id="NUMBER_2">%d</xliff:g> שניות."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"שרטטת את קו ביטול הנעילה <xliff:g id="NUMBER_0">%d</xliff:g> פעמים באופן שגוי. לאחר <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות כושלים נוספים, תתבקש לבטל את נעילת הטלוויזיה באמצעות חשבון אימייל.\n\n נסה שוב בעוד <xliff:g id="NUMBER_2">%d</xliff:g> שניות."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"שרטטת את קו ביטול הנעילה באופן שגוי <xliff:g id="NUMBER_0">%d</xliff:g> פעמים. לאחר <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות כושלים נוספים, תתבקש לבטל את נעילת הטלפון באמצעות חשבון אימייל.\n\nנסה שוב בעוד <xliff:g id="NUMBER_2">%d</xliff:g> שניות."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"שרטטת את קו ביטול הנעילה באופן שגוי <xliff:g id="NUMBER_0">%1$d</xliff:g> פעמים. לאחר <xliff:g id="NUMBER_1">%2$d</xliff:g> ניסיונות כושלים נוספים, תתבקש לבטל את נעילת הטאבלט באמצעות חשבון אימייל.\n\nנסה שוב בעוד <xliff:g id="NUMBER_2">%3$d</xliff:g> שניות."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"שרטטת את קו ביטול הנעילה <xliff:g id="NUMBER_0">%1$d</xliff:g> פעמים באופן שגוי. לאחר <xliff:g id="NUMBER_1">%2$d</xliff:g> ניסיונות כושלים נוספים, תתבקש לבטל את נעילת הטלוויזיה באמצעות חשבון אימייל.\n\n נסה שוב בעוד <xliff:g id="NUMBER_2">%3$d</xliff:g> שניות."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"שרטטת את קו ביטול הנעילה באופן שגוי <xliff:g id="NUMBER_0">%1$d</xliff:g> פעמים. לאחר <xliff:g id="NUMBER_1">%2$d</xliff:g> ניסיונות כושלים נוספים, תתבקש לבטל את נעילת הטלפון באמצעות חשבון אימייל.\n\nנסה שוב בעוד <xliff:g id="NUMBER_2">%3$d</xliff:g> שניות."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"הסר"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"האם להעלות את עוצמת הקול מעל לרמה המומלצת?\n\nהאזנה בעוצמת קול גבוהה למשכי זמן ממושכים עלולה לפגוע בשמיעה."</string>
@@ -1504,6 +1502,7 @@
<item quantity="one">למשך שעה אחת</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"עד <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"עד <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (ההתראה הבאה)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"עד שתכבה"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"עד שתכבה את \'נא לא להפריע\'"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-ja-watch/strings.xml b/core/res/res/values-ja-watch/strings.xml
index 82c3ffd..893063e 100644
--- a/core/res/res/values-ja-watch/strings.xml
+++ b/core/res/res/values-ja-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"アプリ<xliff:g id="NUMBER_0">%1$d</xliff:g>/<xliff:g id="NUMBER_1">%2$d</xliff:g>"</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"センサー"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"連絡先へのアクセス"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"この時計の位置情報へのアクセス"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"カレンダーへのアクセス"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"SMSメッセージの送信と表示"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"時計上の写真、メディア、ファイルへのアクセス"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"録音"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"写真の撮影と動画の記録"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"通話の発信と管理"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"バイタルサインに関するセンサーデータへのアクセス"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"ステータスバーへの表示"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"ボディーセンサー(心拍数モニターなど)へのアクセス"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"正確な位置情報(GPSとネットワーク基地局)へのアクセス"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"おおよその位置情報(ネットワーク基地局)へのアクセス"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"SIMへのコマンド送信"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"ネットワークへのフルアクセス"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"プロファイルの所有者と端末の所有者の管理"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"WiMAX状態の変更"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"Androidビーム転送のステータスの受信"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"メディア出力のルーティング"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"インストールセッションの読み取り"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"インストールパッケージのリクエスト"</string>
</resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 135c7ea..f900301 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"クレジットカードの番号やパスワードなどの個人データが含まれます。"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"ステータスバーの無効化や変更"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"ステータスバーの無効化、システムアイコンの追加や削除をアプリに許可します。"</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"ステータスバーへの表示"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"ステータスバーへの表示"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"ステータスバーへの表示をアプリに許可します。"</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"ステータスバーの拡大/縮小"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"ステータスバーの展開/折りたたみをアプリに許可します。"</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"WAPメッセージの受信と処理をアプリに許可します。これにより、アプリが端末に届いたメッセージを表示することなく監視または削除できるようになります。"</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"実行中のアプリの取得"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"現在実行中または最近実行したタスクに関する情報の取得をアプリに許可します。これにより、その端末でどのアプリを使用しているかをアプリから識別できるようになる可能性があります。"</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"プロファイルの所有者と端末の所有者の管理"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"プロファイルの所有者と端末の所有者の管理"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"プロファイルの所有者と端末の所有者の設定をアプリに許可します。"</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"実行中のアプリの順序変更"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"タスクをフォアグラウンドやバックグラウンドに移動することをアプリに許可します。これにより、アプリがユーザーからの入力なしでこの処理を実行する可能性があります。"</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"タブレットの通話履歴(着信や発信のデータなど)の変更をアプリに許可します。この許可を悪意のあるアプリに利用されると、通話履歴が消去または変更される恐れがあります。"</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"テレビの通話履歴(着信や発信のデータなど)の変更をアプリに許可します。この許可を悪意のあるアプリに利用されると、通話履歴が消去または変更される恐れがあります。"</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"携帯端末の通話履歴(着信や発信のデータなど)の変更をアプリに許可します。この許可を悪意のあるアプリに利用されると、通話履歴が消去または変更される恐れがあります。"</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"ボディーセンサー(心拍数モニターなど)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"ボディーセンサー(心拍数モニターなど)へのアクセス"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"心拍数など、身体状態を監視するセンサーからのデータにアクセスすることをアプリに許可します。"</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"カレンダーの予定と機密情報の読み取り"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"タブレットに保存されているカレンダーの予定(友だちや同僚の予定も含めすべて)を読み取ることをアプリに許可します。これにより、アプリがカレンダーのデータを機密性に関係なく共有または保存できるようになる可能性があります。"</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"ユーザーが携帯端末から編集できる予定(友だちや同僚の予定も含む)を追加、削除、変更することをアプリに許可します。これによりアプリは、カレンダーの所有者から発信されたかのようなメッセージを送信したり、所有者の知らないうちに予定を変更したりできるようになる可能性があります。"</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"位置情報提供者の追加コマンドアクセス"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"位置情報提供元の追加のコマンドにアクセスすることをアプリに許可します。許可すると、アプリがGPSなどの位置情報源の動作を妨害する恐れがあります。"</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"正確な位置情報(GPSとネットワーク基地局)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"正確な位置情報(GPSとネットワーク基地局)へのアクセス"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"グローバルポジショニングシステム(GPS)またはネットワーク位置情報源(携帯基地局やWi-Fiなど)を利用して正確な位置情報を取得することをアプリに許可します。これらの位置情報サービスはONの状態にして、端末でアプリがサービスを利用できるようにする必要があります。アプリはこの位置情報を利用してユーザーの現在地を特定できます。また、これにより電池の消費量が増える可能性があります。"</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"おおよその位置情報(ネットワーク基地局)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"おおよその位置情報(ネットワーク基地局)へのアクセス"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"ユーザーのおおよその位置情報を取得することをアプリに許可します。この位置情報はネットワーク位置情報源(携帯基地局やWi-Fiなど)を利用した位置情報サービスから取得されます。これらの位置情報サービスはONの状態にして、端末でアプリがサービスを利用できるようにする必要があります。アプリはこの位置情報を利用してユーザーのおおよその現在地を特定できます。"</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"音声設定の変更"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"音声全般の設定(音量、出力に使用するスピーカーなど)の変更をアプリに許可します。"</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"録音"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"マイクを使った録音をアプリに許可します。これにより、アプリがいつでも確認なしで録音できるようになります。"</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"SIM通信"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"SIMへのコマンド送信"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"SIMにコマンドを送信することをアプリに許可します。この許可は非常に危険です。"</string>
<string name="permlab_camera" msgid="3616391919559751192">"写真と動画の撮影"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"カメラでの写真と動画の撮影をアプリに許可します。これにより、アプリが確認なしでいつでもカメラを使用できるようになります。"</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"携帯端末で認識されているアカウントのリストの取得をアプリに許可します。これには、インストールしたアプリによって作成されたアカウントも含まれます。"</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"ネットワーク接続の表示"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"存在するネットワークや接続しているネットワークなど、ネットワーク接続に関する情報を表示することをアプリに許可します。"</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"ネットワークへのフルアクセス"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"ネットワークへのフルアクセス"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"ネットワークソケットの作成とカスタムネットワークプロトコルの使用をアプリに許可します。インターネットにデータを送信する手段はブラウザや他のアプリが提供するため、インターネットへのデータ送信のためにこれを許可する必要はありません。"</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"ネットワーク接続の変更"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"ネットワーク接続状態の変更をアプリに許可します。"</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"ローカルのBluetooth携帯端末を設定することと、リモート端末を検出してペアに設定することをアプリに許可します。"</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"WiMAXへの接続と切断"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"WiMAXがONになっているかどうかを識別し、接続されているWiMAXネットワークの情報を表示することをアプリに許可します。"</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX状態の変更"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"WiMAX状態の変更"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"タブレットのWiMAXネットワークへの接続と切断をアプリに許可します。"</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"テレビのWiMAXネットワークへの接続と切断をアプリに許可します。"</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"携帯端末のWiMAXネットワークへの接続と切断をアプリに許可します。"</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"タッチスクリーンの調整パラメータの変更をアプリに許可します。通常のアプリでは必要ありません。"</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"DRM証明書へのアクセス権"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"DRM証明書のプロビジョニングと使用をアプリに許可します。通常のアプリでは不要です。"</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Androidビーム転送のステータスを受信"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"Androidビーム転送のステータスの受信"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"現在のAndroidビーム転送に関する情報を受信することをこのアプリに許可します。"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"DRM証明書の削除"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"DRM証明書の削除をアプリに許可します。通常のアプリでは不要です。"</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PINコードが正しくありません。"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"MENU、0キーでロック解除"</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"緊急通報番号"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"通信サービスはありません"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"通信サービスはありません"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"画面ロック中"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"MENUキーでロック解除(または緊急通報)"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"MENUキーでロック解除"</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"ユーザーガイドをご覧いただくか、お客様サポートにお問い合わせください。"</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIMカードはロックされています。"</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"SIMカードのロック解除中..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"ロック解除パターンの入力を<xliff:g id="NUMBER_0">%d</xliff:g>回間違えました。\n\n<xliff:g id="NUMBER_1">%d</xliff:g>秒後にもう一度お試しください。"</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"正しくないパスワードを<xliff:g id="NUMBER_0">%d</xliff:g>回入力しました。\n\n<xliff:g id="NUMBER_1">%d</xliff:g>秒後にもう一度お試しください。"</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"正しくないPINを<xliff:g id="NUMBER_0">%d</xliff:g>回入力しました。\n\n<xliff:g id="NUMBER_1">%d</xliff:g>秒後にもう一度お試しください。"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"ロック解除パターンの入力を<xliff:g id="NUMBER_0">%d</xliff:g>回間違えました。あと<xliff:g id="NUMBER_1">%d</xliff:g>回間違えると、タブレットのロック解除にGoogleへのログインが必要になります。\n\n<xliff:g id="NUMBER_2">%d</xliff:g>秒後にもう一度お試しください。"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"ロック解除パターンの入力を<xliff:g id="NUMBER_0">%d</xliff:g>回間違えました。あと<xliff:g id="NUMBER_1">%d</xliff:g>回間違えると、テレビのロック解除にGoogleへのログインが必要になります。\n\n<xliff:g id="NUMBER_2">%d</xliff:g>秒以内にもう一度お試しください。"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"ロック解除パターンの入力を<xliff:g id="NUMBER_0">%d</xliff:g>回間違えました。あと<xliff:g id="NUMBER_1">%d</xliff:g>回間違えると、携帯端末のロック解除にGoogleへのログインが必要になります。\n\n<xliff:g id="NUMBER_2">%d</xliff:g>秒後にもう一度お試しください。"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"タブレットのロック解除に<xliff:g id="NUMBER_0">%d</xliff:g>回失敗しました。あと<xliff:g id="NUMBER_1">%d</xliff:g>回失敗すると、タブレットは工場出荷状態にリセットされ、ユーザーのデータはすべて失われます。"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"テレビのロック解除に<xliff:g id="NUMBER_0">%d</xliff:g>回失敗しました。あと<xliff:g id="NUMBER_1">%d</xliff:g>回失敗すると、テレビは出荷時設定にリセットされ、ユーザーのデータはすべて失われます。"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"端末のロック解除に<xliff:g id="NUMBER_0">%d</xliff:g>回失敗しました。あと<xliff:g id="NUMBER_1">%d</xliff:g>回失敗すると、端末は工場出荷状態にリセットされ、ユーザーのデータはすべて失われます。"</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"ロック解除パターンの入力を<xliff:g id="NUMBER_0">%1$d</xliff:g>回間違えました。\n\n<xliff:g id="NUMBER_1">%2$d</xliff:g>秒後にもう一度お試しください。"</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"正しくないパスワードを<xliff:g id="NUMBER_0">%1$d</xliff:g>回入力しました。\n\n<xliff:g id="NUMBER_1">%2$d</xliff:g>秒後にもう一度お試しください。"</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"正しくないPINを<xliff:g id="NUMBER_0">%1$d</xliff:g>回入力しました。\n\n<xliff:g id="NUMBER_1">%2$d</xliff:g>秒後にもう一度お試しください。"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"ロック解除パターンの入力を<xliff:g id="NUMBER_0">%1$d</xliff:g>回間違えました。あと<xliff:g id="NUMBER_1">%2$d</xliff:g>回間違えると、タブレットのロック解除にGoogleへのログインが必要になります。\n\n<xliff:g id="NUMBER_2">%3$d</xliff:g>秒後にもう一度お試しください。"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"ロック解除パターンの入力を<xliff:g id="NUMBER_0">%1$d</xliff:g>回間違えました。あと<xliff:g id="NUMBER_1">%2$d</xliff:g>回間違えると、テレビのロック解除にGoogleへのログインが必要になります。\n\n<xliff:g id="NUMBER_2">%3$d</xliff:g>秒以内にもう一度お試しください。"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"ロック解除パターンの入力を<xliff:g id="NUMBER_0">%1$d</xliff:g>回間違えました。あと<xliff:g id="NUMBER_1">%2$d</xliff:g>回間違えると、携帯端末のロック解除にGoogleへのログインが必要になります。\n\n<xliff:g id="NUMBER_2">%3$d</xliff:g>秒後にもう一度お試しください。"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"タブレットのロック解除に<xliff:g id="NUMBER_0">%1$d</xliff:g>回失敗しました。あと<xliff:g id="NUMBER_1">%2$d</xliff:g>回失敗すると、タブレットは工場出荷状態にリセットされ、ユーザーのデータはすべて失われます。"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"テレビのロック解除に<xliff:g id="NUMBER_0">%1$d</xliff:g>回失敗しました。あと<xliff:g id="NUMBER_1">%2$d</xliff:g>回失敗すると、テレビは出荷時設定にリセットされ、ユーザーのデータはすべて失われます。"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"端末のロック解除に<xliff:g id="NUMBER_0">%1$d</xliff:g>回失敗しました。あと<xliff:g id="NUMBER_1">%2$d</xliff:g>回失敗すると、端末は工場出荷状態にリセットされ、ユーザーのデータはすべて失われます。"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"タブレットのロック解除を<xliff:g id="NUMBER">%d</xliff:g>回失敗しました。タブレットを工場出荷状態にリセットします。"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"テレビのロック解除に<xliff:g id="NUMBER">%d</xliff:g>回失敗しました。テレビは出荷時設定にリセットされます。"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"端末のロック解除を<xliff:g id="NUMBER">%d</xliff:g>回失敗しました。端末を工場出荷状態にリセットします。"</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"単語"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"リンク"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"行"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"出荷時試験が失敗"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"FACTORY_TEST操作は、/system/appにインストールされたパッケージのみが対象です。"</string>
<string name="factorytest_no_action" msgid="872991874799998561">"FACTORY_TEST操作を行うパッケージは見つかりませんでした。"</string>
@@ -1029,7 +1027,7 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"タップしてUSBデバッグを無効化"</string>
<string name="select_input_method" msgid="8547250819326693584">"キーボードの変更"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"キーボードの選択"</string>
- <string name="show_ime" msgid="9157568568695230830">"入力方法を表示する"</string>
+ <string name="show_ime" msgid="9157568568695230830">"スクリーンキーボードを表示する"</string>
<string name="hardware" msgid="7517821086888990278">"ハードウェア"</string>
<string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"キーボードレイアウトの選択"</string>
<string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"タップしてキーボードレイアウトを選択してください。"</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"フォーマットしています…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"挿入されていません"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"一致するアクティビティが見つかりません。"</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"メディア出力のルーティング"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"メディア出力のルーティング"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"メディア出力を他の外部デバイスにルーティングすることをアプリに許可します。"</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"インストールセッションの読み取り"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"インストールセッションの読み取り"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"インストールセッションの読み取りをアプリに許可します。これにより、アプリはアクティブパッケージのインストールに関する詳細情報を参照できるようになります。"</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"パッケージインストールのリクエスト"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"インストールパッケージのリクエスト"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"パッケージのインストールをリクエストすることをアプリケーションに許可します。"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"ダブルタップでズームコントロール"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"ウィジェットを追加できませんでした。"</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"ユーザー名またはパスワードが無効です。"</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"ユーザー名またはパスワードを忘れた場合は\n "<b>"google.com/accounts/recovery"</b>" にアクセスしてください。"</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"アカウントをチェックしています…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"PINの入力を<xliff:g id="NUMBER_0">%d</xliff:g>回間違えました。\n\n<xliff:g id="NUMBER_1">%d</xliff:g>秒後にもう一度お試しください。"</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"パスワードの入力を<xliff:g id="NUMBER_0">%d</xliff:g>回間違えました。\n\n<xliff:g id="NUMBER_1">%d</xliff:g>秒後にもう一度お試しください。"</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"ロック解除パターンの入力を<xliff:g id="NUMBER_0">%d</xliff:g>回間違えました。\n\n<xliff:g id="NUMBER_1">%d</xliff:g>秒後にもう一度お試しください。"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"タブレットのロック解除に<xliff:g id="NUMBER_0">%d</xliff:g>回失敗しました。あと<xliff:g id="NUMBER_1">%d</xliff:g>回失敗すると、タブレットは出荷時設定にリセットされ、ユーザーのデータはすべて失われます。"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"テレビのロック解除に<xliff:g id="NUMBER_0">%d</xliff:g>回失敗しました。あと<xliff:g id="NUMBER_1">%d</xliff:g>回失敗すると、テレビは出荷時設定にリセットされ、ユーザーのデータはすべて失われます。"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"携帯端末のロック解除に<xliff:g id="NUMBER_0">%d</xliff:g>回失敗しました。あと<xliff:g id="NUMBER_1">%d</xliff:g>回失敗すると、端末は出荷時設定にリセットされ、ユーザーのデータはすべて失われます。"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"PINの入力を<xliff:g id="NUMBER_0">%1$d</xliff:g>回間違えました。\n\n<xliff:g id="NUMBER_1">%2$d</xliff:g>秒後にもう一度お試しください。"</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"パスワードの入力を<xliff:g id="NUMBER_0">%1$d</xliff:g>回間違えました。\n\n<xliff:g id="NUMBER_1">%2$d</xliff:g>秒後にもう一度お試しください。"</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"ロック解除パターンの入力を<xliff:g id="NUMBER_0">%1$d</xliff:g>回間違えました。\n\n<xliff:g id="NUMBER_1">%2$d</xliff:g>秒後にもう一度お試しください。"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"タブレットのロック解除に<xliff:g id="NUMBER_0">%1$d</xliff:g>回失敗しました。あと<xliff:g id="NUMBER_1">%2$d</xliff:g>回失敗すると、タブレットは出荷時設定にリセットされ、ユーザーのデータはすべて失われます。"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"テレビのロック解除に<xliff:g id="NUMBER_0">%1$d</xliff:g>回失敗しました。あと<xliff:g id="NUMBER_1">%2$d</xliff:g>回失敗すると、テレビは出荷時設定にリセットされ、ユーザーのデータはすべて失われます。"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"携帯端末のロック解除に<xliff:g id="NUMBER_0">%1$d</xliff:g>回失敗しました。あと<xliff:g id="NUMBER_1">%2$d</xliff:g>回失敗すると、端末は出荷時設定にリセットされ、ユーザーのデータはすべて失われます。"</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"タブレットのロック解除を<xliff:g id="NUMBER">%d</xliff:g>回失敗しました。タブレットは出荷時設定にリセットされます。"</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"テレビのロック解除に<xliff:g id="NUMBER">%d</xliff:g>回失敗しました。テレビは出荷時設定にリセットされます。"</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"携帯端末のロック解除を<xliff:g id="NUMBER">%d</xliff:g>回失敗しました。端末は出荷時設定にリセットされます。"</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"ロック解除パターンの入力を<xliff:g id="NUMBER_0">%d</xliff:g>回間違えました。あと<xliff:g id="NUMBER_1">%d</xliff:g>回間違えると、タブレットのロック解除にメールアカウントが必要になります。\n\n<xliff:g id="NUMBER_2">%d</xliff:g>秒後にもう一度お試しください。"</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"ロック解除パターンの入力を<xliff:g id="NUMBER_0">%d</xliff:g>回間違えました。あと<xliff:g id="NUMBER_1">%d</xliff:g>回間違えると、テレビのロック解除にメールアカウントが必要になります。\n\n<xliff:g id="NUMBER_2">%d</xliff:g>秒以内にもう一度お試しください。"</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"ロック解除パターンの入力を<xliff:g id="NUMBER_0">%d</xliff:g>回間違えました。あと<xliff:g id="NUMBER_1">%d</xliff:g>回間違えると、携帯端末のロック解除にメールアカウントが必要になります。\n\n<xliff:g id="NUMBER_2">%d</xliff:g>秒後にもう一度お試しください。"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"ロック解除パターンの入力を<xliff:g id="NUMBER_0">%1$d</xliff:g>回間違えました。あと<xliff:g id="NUMBER_1">%2$d</xliff:g>回間違えると、タブレットのロック解除にメールアカウントが必要になります。\n\n<xliff:g id="NUMBER_2">%3$d</xliff:g>秒後にもう一度お試しください。"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"ロック解除パターンの入力を<xliff:g id="NUMBER_0">%1$d</xliff:g>回間違えました。あと<xliff:g id="NUMBER_1">%2$d</xliff:g>回間違えると、テレビのロック解除にメールアカウントが必要になります。\n\n<xliff:g id="NUMBER_2">%3$d</xliff:g>秒以内にもう一度お試しください。"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"ロック解除パターンの入力を<xliff:g id="NUMBER_0">%1$d</xliff:g>回間違えました。あと<xliff:g id="NUMBER_1">%2$d</xliff:g>回間違えると、携帯端末のロック解除にメールアカウントが必要になります。\n\n<xliff:g id="NUMBER_2">%3$d</xliff:g>秒後にもう一度お試しください。"</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" - "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"削除"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"推奨レベルを超えるまで音量を上げますか?\n\n大音量で長時間聞き続けると、聴力を損なう恐れがあります。"</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">1時間</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>まで"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>(次のアラーム)まで"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"ユーザーがOFFにするまで"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"[通知を非表示]をOFFにするまで"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-ka-rGE-watch/strings.xml b/core/res/res/values-ka-rGE-watch/strings.xml
index 9e2ca1f..0c2f4ad 100644
--- a/core/res/res/values-ka-rGE-watch/strings.xml
+++ b/core/res/res/values-ka-rGE-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"აპი <xliff:g id="NUMBER_0">%1$d</xliff:g>, სულ <xliff:g id="NUMBER_1">%2$d</xliff:g>-დან."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"სენსორები"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"თქვენს კონტაქტებზე წვდომა"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"ამ ნახვის მდებარეობაზე წვდომა"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"თქვენს კალენდარზე წვდომა"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"SMS შეტყობინებების გაგზავნა და ნახვა"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"თქვენს სანახავ ფოტოებზე, მედიაზე და ფაილებზე წვდომა"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"აუდიოს ჩაწერა"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"ფოტოებისა და ვიდეოების გადაღება"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"სატელეფონო ზარების გაშვება და მართვა"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"თქვენი სასიცოცხლო ნიშნების შესახებ სენეორის მონაცემებზე წვდომა"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"სტატუსის ზოლის ჩანაცვლება"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"სხეულის სენსორებზე (როგორიცაა გულისცემის სიხშირე) წვდომა"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"ზუსტ მდებარეობაზე წვდომა (GPS-ის და ქსელის მიხედვით)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"მიახლოებით მდებარეობაზე წვდომა (ქსელის მიხედვით)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"ბრძანებების SIM-ზე გაგზავნა"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"მთელ ქსელზე წვდომის მიღება"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"პროფილისა და მოწყობილობის მფლობელების მართვა"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"WiMAX-ის მდგომარეობის შეცვლა"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"Android სხივით გადაცემის სტატუსის მიღება"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"მედიის მონაცემების მარშრუტიზაცია"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"ინსტალაციის სესიების წაკითხვა"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"პაკეტების ინსტალაციის მოთხოვნა"</string>
</resources>
diff --git a/core/res/res/values-ka-rGE/strings.xml b/core/res/res/values-ka-rGE/strings.xml
index df302f5..94988ab 100644
--- a/core/res/res/values-ka-rGE/strings.xml
+++ b/core/res/res/values-ka-rGE/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"შეიცავს ისეთ პირად მონაცემებს, როგორიცაა საკრედიტო ბარათის ნომრები და პაროლები."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"სტატუსის ზოლის გათიშვა ან ცვლილება"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"აპს შეეძლება სტატუსების ზოლის გათიშვა და სისტემის ხატულების დამატება/წაშლა."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"სტატუსის ზოლი"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"სტატუსის ზოლის ჩანაცვლება"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"აპს შეეძლება სტატუსის ზოლის ჩანაცვლება."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"სტატუსების ზოლის გაფართოება/აკეცვა"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"აპს შეეძლება სტატუსის ზოლის გახსნა-დახურვა."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"აპს შეეძლება WAP შეტყობინებების მიღება და გენერირება. ამ უფლებით აპი ისე დააკვირდება და წაშლის თქვენთვის გამოგზავნილ შეტყობინებებს, რომ თქვენ ვერც ნახავთ."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"მოქმედი აპების მოძიება"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"აპს შეეძლება მოიძიოს ინფორმაცია ამჟამად და უახლოეს წარსულში მიმდინარე ამოცანების შესახებ. ამგვარად, აპს აქვს შესაძლებლობა აღმოაჩინოს ინფორმაცია იმის შესახებ, თუ რომელი აპლიკაციებია გამოყენებული მოწყობილობაზე."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"პროფილისა და მოწყობილობის მფლობელების მართვა"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"პროფილისა და მოწყობილობის მფლობელების მართვა"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"აპებს უფლებას აძლევს, დააყენოს პროფილის მფლობელები და მოწყობილობის მფლობელი."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"მოქმედი აპების წყობის შეცვლა"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"აპს შეეძლება ამოცანების გადატანა წინა და უკანა პლანზე. ამას თქვენი ჩარევის გარეშე გააკეთებს."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"აპს შეეძლება, შეცვალოს თქვენი ტაბლეტის ზარების ჟურნალი, მათ შორის შემომავალი და გამავალი ზარების მონაცემები. მავნე აპებმა შეიძლება გამოიყენონ ეს თქვენი ზარების ჟურნალის წასაშლელად ან შესაცვლელად."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"ნებას რთვს აპლიკაციას, შეცვალოს თქვენი ტელევიზორის ზარების ჟურნალი, შემომავალი და გამავალი ზარების მონაცემთა ჩათვლით. მავნე აპლიკაციებს შეუძლიათ ამოშალონ ან შეცვალონ თქვენი ზარების ჟურნალი."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"აპს შეეძლება, შეცვალოს თქვენი ტელეფონის ზარების ჟურნალი, მათ შორის შემომავალი და გამავალი ზარების მონაცემები. მავნე აპებმა შეიძლება გამოიყენონ ეს თქვენი ზარების ჟურნალის წასაშლელად ან შესაცვლელად."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"სხეულის სენსორები (მაგ. გულისცემის მონიტორები)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"სხეულის სენსორებზე წვდომა (მაგ., გულისცემის მონიტორები)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"აპისთვის ნების დართვა, რათა მას ჰქონდეს წვდომა თქვენი ფიზიკური მდგომარეობის მონიტორინგის სენსორების მონაცემებზე."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"კალენდრის ღონისძიებებისა და კონფიდენციალური ინფორმაციის წაკითხვა"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"აპს შეეძლება, წაიკითხოს თქვენ ტაბლეტზე შენახული კალენდრის ყველა მოვლენა, მათ შორის მეგობრებისა და თანამშრომლების მოვლენებიც. ამან შეიძლება უფლება მისცეს აპს, გააზიაროს ან შეინახოს თქვენი კალენდრის მონაცემები, მიუხედავად კონფიდენციალურობისა თუ მგრძობიარობისა."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"აპს შეეძლება იმ ღონისძიებების დამატება, წაშლა და შეცვლა, რომლებსაც თქვენს ტელეფონზე ქმნით, ასევე თქვენი მეგობრების და თანამშრომლების ღონისძიებებიც. ამგვარად, აპს ექნება შესაძლებლობა ისე დააგზავნოს შეტყობინებები კალენდრის მფლობელის სახელით ან შეცვალოს ღონისძიებები, რომ მფლობელმა ამის შესახებ არაფერი იცოდეს."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"მდებარეობის პროვაიდერის დამატებით ბრძანებებზე წვდომა"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"აპს შეეძლება წვდომა ჰქონდეს მდებარეობის სერვისის დამატებით ბრძანებებზე. შესაძლოა აპმა ეს გამოიყენოს GPS-ისა და მდებარეობის სხვა წყაროების მუშაობის პროცესში ჩარევისთვის."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"ზუსტი მდებარეობა (GPS და ქსელის კოორდინატების მიხედვით)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"ზუსტ მდებარეობაზე წვდომა (GPS-ისა და ქსელის მეშვეობით)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"აძლევს აპს უფლებას მოიპოვოს ზუსტი მდებარეობა გლობალური პოზიციონირების სისტემის (GPS) გამოყენებით ან ქსელის მდებარეობის წყაროს მიხედვით, როგორიცაა ქსელის ანძები და Wi-Fi. მდებარეობის ეს სერვისები ჩართული უნდა იყოს და თქვენს მოწყობილობაზე აპისთვის მისაწვდომი, რათა შეძლოს მათი გამოყენება. აპებში შესაძლებელია მათი გამოყენება თქვენი მდებარეობის განსასაზღვრად და ამან ელემენტის დამატებითი ხარჯვა შეიძლება გამოიწვიოს."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"სავარაუდო (ქსელის კოორდინატების მიხედვით) მდებარეობა"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"მიახლოებით მდებარეობაზე წვდომა (ქსელის მეშვეობით)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"აპს შეეძლება გაიგოს თქვენი სავარაუდო მდებარეობა. ის გამოითვლება მდებარეობის სერვისის მიერ ქსელის მონაცემების - მობილური კავშირგაბმულობის ანძებისა და Wi-Fi-ის მიხედვით. ეს სერვისები ჩართული უნდა იყოს თქვენს მოწყობილობაზე, ხოლო აპებს უნდა ჰქონდეთ მათი გამოყენების უფლება. აპები მათი მონაცემების მიხედვით სავარაუდო მდებარეობის გამოთვლას შეძლებენ."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"თქვენი აუდიო პარამეტრების შეცვლა"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"აპს შეეძლება აუდიოს გლობალური პარამეტრების შეცვლა. მაგ.: ხმის სიმაღლე და რომელი დინამიკი გამოიყენება სიგნალის გამოსტანად."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"აუდიოს ჩაწერა"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"აპს შეეძლება აუდიო ჩაწერა მიკროფონით. ნებართვა აპს აუდიო ჩაწერის უფლებას აძლევს ნებისმიერ დროს, თქვენი თანხმობის გარეშე."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"კომუნიკაცია SIM-თან"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"ბრძანებების SIM-ზე გაგზავნა"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"აპისთვის ნების დართვა გაუგზავნოს ბრძანებები SIM-ბარათს. ეს ძალიან საშიშია."</string>
<string name="permlab_camera" msgid="3616391919559751192">"სურათებისა და ვიდეოების გადაღება"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"აპს შეეძლება კამერით სურათისა და ვიდეოს გადაღება. ეს ნებართვა აპს უფლებას აძლევს, ნებისმიერ დროს გამოიყენოს კამერა თქვენი დადასტურების გარეშე."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"აპს შეეძლება, მიიღოს ტელეფონისთვის ცნობილი ანგარიშების სია. ეს შეიძლება მოიცავდეს ნებისმიერ ანგარიშს, რომელიც თქვენ მიერ დაყენებული აპლიკაციებით შეიქმნა."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"ქსელის კავშირების ნახვა"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"აპს შეეძლება ქსელის კავშირის შესახებ ინფორმაციის ნახვა, მაგ. რომელი ქსელები არსებობს და რომელია დაკავშირებული."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"ქსელზე სრული წვდომა"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"ქსელზე სრული წვდომის მიღება"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"აპს შეეძლება შექმნას ქსელური ბუდეები და გამოიყენოს მორგებული ქსელის პროტოკოლები. ბრაუზერი და სხვა აპლიკაციები უზრუნველყოფს ინტერნეტში მონაცემების გაგზავნის საშუალებას, ამგვარად ეს უფლება ინფორმაციის გასაგზავნად საჭირო არაა."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"კავშირის მდგომარეობის/პარამეტრების შეცვლა"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"აპს შეეძლება, შეცვალოს ქსელის კავშირის მდგომარეობა."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"აპს შეეძლება ტელეფონის ადგილობრივი Bluetooth პარამეტრების დაყენება და დისტანციური მოწყობილობების აღმოჩენა და დაწყვილება."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"WiMAX-თან დაკავშირება და კავშირის გაწყვეტა"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"აპს შეეძლება განსაზღვროს, WiMAX არის თუ არა ჩართული და ასევე ინფორმაცია ნებისმიერი დაკავშირებული WiMAX ქსელის შესახებ."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX მდგომარეობის შეცვლა"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"WiMAX-ის მდგომარეობის შეცვლა"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"აპს შეეძლება, დაუკავშიროს და გამოაერთოს ტაბლეტი WiMAX ქსელებიდან."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"ნებას რთავს აპლიკაციას, ჩართოს ან გამორთოს ტელევიზორი WiMAX-ის ქსელიდან."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"აპს შეეძლება, დაუკავშიროს და გამოაერთოს ტელეფონი WiMAX ქსელებიდან."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"საშუალებას აძლევს აპს შეცვალოს სენსორული ეკრანის კალიბრაციის პარამეტრები. ჩვეულებრივ აპებს წესით არ უნდა დაჭირდეს."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"DRM სერთიფიკატებზე წვდომა"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"საშუალებას აძლევს აპლიკაციას დანერგოს და გამოიყენოს DRM სერთიფიკატები. ეს უფლება ჩვეულებრივ აპებს არ ჭირდება."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Android Beam ტრანსფერის სტატუსის მიღება"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"Android სხივით გადაცემის სტატუსის მიღება"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"ნებას რთავს ამ აპლიკაციას, მიიღოს ინფორმაცია მიმდინარე Android Beam-ის ტრანსფერების შესახებ"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"DRM სერტიფიკატების ამოშლა"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"საშუალებას აძლევს აპლიკაციას ამოშალოს DRM სერtიფიკატები. ეს წესით ჩვეულებრივ აპებს არ უნდა დაჭირდეს."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"არასწორი PIN კოდი."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"განბლოკვისათვის დააჭირეთ მენიუს და შემდეგ 0-ს."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"გადაუდებელი დახმარების ნომრები"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"სერვისი არ არის."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"სერვისი არ არის"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"ეკრანი დაბლოკილია."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"განბლოკვისთვის ან გადაუდებელი ზარისთვის დააჭირეთ მენიუს."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"განბლოკვისთვის დააჭირეთ მენიუს."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"იხილეთ მომხმარებლის სახელმძღვანელო ან დაუკავშირდით კლიენტების მომსახურებას."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM ბარათი დაბლოკილია."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"SIM ბარათის განბლოკვა…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"თქვენ <xliff:g id="NUMBER_0">%d</xliff:g>-ჯერ დახატეთ განბლოკვის ნიმუში. \n\nსცადეთ ხელახლა <xliff:g id="NUMBER_1">%d</xliff:g> წამში."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"თქვენ არასწორად დაბეჭდეთ თქვენი პაროლი <xliff:g id="NUMBER_0">%d</xliff:g> ჯერ. \n\nხელახლა სცადეთ <xliff:g id="NUMBER_1">%d</xliff:g> წამში."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"თქვენ <xliff:g id="NUMBER_0">%d</xliff:g>-ჯერ არასწორად შეიყვანეთ PIN კოდი. \n\nსცადეთ ხელახლა <xliff:g id="NUMBER_1">%d</xliff:g> წამში."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"თქვენ არასწორად დახატეთ განბლოკვის ნიმუში <xliff:g id="NUMBER_0">%d</xliff:g>-ჯერ. კიდევ <xliff:g id="NUMBER_1">%d</xliff:g> წარუმატებელი ცდის შემდეგ დაგჭირდებათ თქვენი ტაბლეტის განბლოკვა Google-ში შესვლით.\n\n გთხოვთ, ხელახლა სცადოთ <xliff:g id="NUMBER_2">%d</xliff:g> წამში."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"თქვენ არასწორად შეიყვანეთ თქვენი განბლოკვის თარგი <xliff:g id="NUMBER_0">%d</xliff:g> ჯერ. კიდევ <xliff:g id="NUMBER_1">%d</xliff:g> წარუმატებელი მცდელობის შემდეგ, თქვენ მოგეთხოვებათ განბლოკოთ თქვენი Google შესვლის მონაცემების მეშვეობით. \n\n სცადეთ ხელახლა <xliff:g id="NUMBER_2">%d</xliff:g> წამი."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"თქვენ არასწორად დახატეთ განბლოკვის ნიმუში <xliff:g id="NUMBER_0">%d</xliff:g>-ჯერ. კიდევ <xliff:g id="NUMBER_1">%d</xliff:g> წარუმატებელი ცდის შემდეგ დაგჭირდებათ თქვენი ტელეფონის განბლოკვა Google-ში შესვლით.\n\n გთხოვთ, ხელახლა სცადოთ <xliff:g id="NUMBER_2">%d</xliff:g> წამში."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"თქვენ არასწორად სცადეთ ტაბლეტის განბლოკვა <xliff:g id="NUMBER_0">%d</xliff:g>-ჯერ. კიდევ <xliff:g id="NUMBER_1">%d</xliff:g> წარუმატებელი ცდის შემდეგ ტაბლეტზე დაყენდება საწყისი, ქარხნული პარამეტრები და მომხმარებლის ყველა მონაცემი დაიკარგება."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"თქვენ არასწორად სცადეთ ტელევიზორის განბლოკვა <xliff:g id="NUMBER_0">%d</xliff:g> ჯერ. კიდეც <xliff:g id="NUMBER_1">%d</xliff:g> წარუმატებელი მცდელობის შემდეგ, ტელევიზორი დაუბრუნდება ქარხნულ ნაგულისხმევ პარამეტრებს და მომხმარებლის ყველა მონაცემი დაიკარგება."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"თქვენ არასწორად სცადეთ ტელეფონის განბლოკვა <xliff:g id="NUMBER_0">%d</xliff:g> ჯერ. კიდევ <xliff:g id="NUMBER_1">%d</xliff:g> წარუმატებელი ცდის შემდეგ ტელეფონზე დაყენდება საწყისი, ქარხნული პარამეტრები და მომხმარებლის ყველა მონაცემი დაიკარგება."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"თქვენ <xliff:g id="NUMBER_0">%1$d</xliff:g>-ჯერ დახატეთ განბლოკვის ნიმუში. \n\nსცადეთ ხელახლა <xliff:g id="NUMBER_1">%2$d</xliff:g> წამში."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"თქვენ არასწორად დაბეჭდეთ თქვენი პაროლი <xliff:g id="NUMBER_0">%1$d</xliff:g> ჯერ. \n\nხელახლა სცადეთ <xliff:g id="NUMBER_1">%2$d</xliff:g> წამში."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"თქვენ <xliff:g id="NUMBER_0">%1$d</xliff:g>-ჯერ არასწორად შეიყვანეთ PIN კოდი. \n\nსცადეთ ხელახლა <xliff:g id="NUMBER_1">%2$d</xliff:g> წამში."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"თქვენ არასწორად დახატეთ განბლოკვის ნიმუში <xliff:g id="NUMBER_0">%1$d</xliff:g>-ჯერ. კიდევ <xliff:g id="NUMBER_1">%2$d</xliff:g> წარუმატებელი ცდის შემდეგ დაგჭირდებათ თქვენი ტაბლეტის განბლოკვა Google-ში შესვლით.\n\n გთხოვთ, ხელახლა სცადოთ <xliff:g id="NUMBER_2">%3$d</xliff:g> წამში."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"თქვენ არასწორად შეიყვანეთ თქვენი განბლოკვის თარგი <xliff:g id="NUMBER_0">%1$d</xliff:g> ჯერ. კიდევ <xliff:g id="NUMBER_1">%2$d</xliff:g> წარუმატებელი მცდელობის შემდეგ, თქვენ მოგეთხოვებათ განბლოკოთ თქვენი Google შესვლის მონაცემების მეშვეობით. \n\n სცადეთ ხელახლა <xliff:g id="NUMBER_2">%3$d</xliff:g> წამი."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"თქვენ არასწორად დახატეთ განბლოკვის ნიმუში <xliff:g id="NUMBER_0">%1$d</xliff:g>-ჯერ. კიდევ <xliff:g id="NUMBER_1">%2$d</xliff:g> წარუმატებელი ცდის შემდეგ დაგჭირდებათ თქვენი ტელეფონის განბლოკვა Google-ში შესვლით.\n\n გთხოვთ, ხელახლა სცადოთ <xliff:g id="NUMBER_2">%3$d</xliff:g> წამში."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"თქვენ არასწორად სცადეთ ტაბლეტის განბლოკვა <xliff:g id="NUMBER_0">%1$d</xliff:g>-ჯერ. კიდევ <xliff:g id="NUMBER_1">%2$d</xliff:g> წარუმატებელი ცდის შემდეგ ტაბლეტზე დაყენდება საწყისი, ქარხნული პარამეტრები და მომხმარებლის ყველა მონაცემი დაიკარგება."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"თქვენ არასწორად სცადეთ ტელევიზორის განბლოკვა <xliff:g id="NUMBER_0">%1$d</xliff:g> ჯერ. კიდეც <xliff:g id="NUMBER_1">%2$d</xliff:g> წარუმატებელი მცდელობის შემდეგ, ტელევიზორი დაუბრუნდება ქარხნულ ნაგულისხმევ პარამეტრებს და მომხმარებლის ყველა მონაცემი დაიკარგება."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"თქვენ არასწორად სცადეთ ტელეფონის განბლოკვა <xliff:g id="NUMBER_0">%1$d</xliff:g> ჯერ. კიდევ <xliff:g id="NUMBER_1">%2$d</xliff:g> წარუმატებელი ცდის შემდეგ ტელეფონზე დაყენდება საწყისი, ქარხნული პარამეტრები და მომხმარებლის ყველა მონაცემი დაიკარგება."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"თქვენ <xliff:g id="NUMBER">%d</xliff:g>-ჯერ არასწორად სცადეთ ტაბლეტის განბლოკვა. ამიტომ ტაბლეტზე დადგება საწყისი, ქარხნული პარამეტრები."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"თქვენ არასწორად სცადეთ ტელევიზორის განბლოკვა <xliff:g id="NUMBER">%d</xliff:g> ჯერ. ტელევიზორი დაუბრუნდება ქარხნულ ნაგულისხმევ პარამეტრებს."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"თქვენ არასწორად სცადეთ ტელეფონის განბლოკვა <xliff:g id="NUMBER">%d</xliff:g> ჯერ. ახლა ტელეფონზე დაყენდება საწყისი, ქარხნული პარამეტრები."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"სიტყვა"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"ბმული"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"სტრიქონი"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"წარმოების ტესტი ვერ განხორციელდა"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"აქტივობა FACTORY_TEST მხარდაჭერილია მხოლოდ იმ პაკეტებისთვის, რომლებიც მოთავსებულია /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"ვერ მოიძებნა პაკეტი, რომელიც უზრუნველყოფს ქარხნულ ტესტ ქმედებას."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"დაფორმატება…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"არ არის ჩასმული"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"შესატყვისი აქტივობები არ არის."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"მულტიმედია მონაცემების გადამისამართება"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"გამომავალი მედიის მარშრუტიზაცია"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"აპლიკაციას შეეძლება გადაამისამართოს მულტიმედია მონაცემები სხვა გარე მოწყობილობებისკენ."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"ინსტალაციის სესიების წაკითხვა"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"ინსტალაციის სესიების წაკითხვა"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"საშუალებას აძლევს აპლიკაციას წაიკითხოს ინსტალაციის სესიები. ამით მას საშუალება აქვს იხილოს პაკეტის აქტიური ინსტალაციები."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"პაკეტების ინსტალაციის მოთხოვნა"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"პაკეტების ინსტალაციის მოთხოვნა"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"აპლიკაციას შეეძლება მოითხოვოს პაკეტების ინსტალაცია."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"მასშტაბის მართვისთვის შეეხეთ ორჯერ."</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"ვერ დაემატა ვიჯეტი."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"არასწორი სახელი, ან პაროლი."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"დაგავიწყდათ მომხმარებლის სახელი და პაროლი?\nეწვიეთ "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"მიმდინარეობს ანგარიშის შემოწმება…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"თქვენ <xliff:g id="NUMBER_0">%d</xliff:g>-ჯერ არასწორად შეიყვანეთ PIN კოდი. \n\nსცადეთ ხელახლა <xliff:g id="NUMBER_1">%d</xliff:g> წამში."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"თქვენ <xliff:g id="NUMBER_0">%d</xliff:g>-ჯერ არასწორად დაბეჭდეთ თქვენი პაროლი. \n\nხელახლა სცადეთ <xliff:g id="NUMBER_1">%d</xliff:g> წამში."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"თქვენ <xliff:g id="NUMBER_0">%d</xliff:g>-ჯერ დახატეთ განბლოკვის ნიმუში. \n\nსცადეთ ხელახლა <xliff:g id="NUMBER_1">%d</xliff:g> წამში."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"თქვენ არასწორად სცადეთ ტაბლეტის განბლოკვა <xliff:g id="NUMBER_0">%d</xliff:g>-ჯერ. კიდევ <xliff:g id="NUMBER_1">%d</xliff:g> წარუმატებელი ცდის შემდეგ ტაბლეტზე დაყენდება საწყისი, ქარხნული პარამეტრები და მომხმარებლის ყველა მონაცემი დაიკარგება."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"თქვენ არასწორად სცადეთ ტელევიზორის განბლოკვა <xliff:g id="NUMBER_0">%d</xliff:g> ჯერ. კიდევ <xliff:g id="NUMBER_1">%d</xliff:g> წარუმატებელი მცდელობის შემდეგ, ტელევიზორი დაუბრუნდება ქარხნულ ნაგულისხმევ პარამეტრებს და მომხმარებლის ყველა მონაცემი დაკარგება."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"თქვენ არასწორად სცადეთ ტელეფონის განბლოკვა <xliff:g id="NUMBER_0">%d</xliff:g>-ჯერ. კიდევ <xliff:g id="NUMBER_1">%d</xliff:g> წარუმატებელი ცდის შემდეგ ტელეფონზე დაყენდება საწყისი, ქარხნული პარამეტრები და მომხმარებლის ყველა მონაცემი დაიკარგება."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"თქვენ <xliff:g id="NUMBER_0">%1$d</xliff:g>-ჯერ არასწორად შეიყვანეთ PIN კოდი. \n\nსცადეთ ხელახლა <xliff:g id="NUMBER_1">%2$d</xliff:g> წამში."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"თქვენ <xliff:g id="NUMBER_0">%1$d</xliff:g>-ჯერ არასწორად დაბეჭდეთ თქვენი პაროლი. \n\nხელახლა სცადეთ <xliff:g id="NUMBER_1">%2$d</xliff:g> წამში."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"თქვენ <xliff:g id="NUMBER_0">%1$d</xliff:g>-ჯერ დახატეთ განბლოკვის ნიმუში. \n\nსცადეთ ხელახლა <xliff:g id="NUMBER_1">%2$d</xliff:g> წამში."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"თქვენ არასწორად სცადეთ ტაბლეტის განბლოკვა <xliff:g id="NUMBER_0">%1$d</xliff:g>-ჯერ. კიდევ <xliff:g id="NUMBER_1">%2$d</xliff:g> წარუმატებელი ცდის შემდეგ ტაბლეტზე დაყენდება საწყისი, ქარხნული პარამეტრები და მომხმარებლის ყველა მონაცემი დაიკარგება."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"თქვენ არასწორად სცადეთ ტელევიზორის განბლოკვა <xliff:g id="NUMBER_0">%1$d</xliff:g> ჯერ. კიდევ <xliff:g id="NUMBER_1">%2$d</xliff:g> წარუმატებელი მცდელობის შემდეგ, ტელევიზორი დაუბრუნდება ქარხნულ ნაგულისხმევ პარამეტრებს და მომხმარებლის ყველა მონაცემი დაკარგება."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"თქვენ არასწორად სცადეთ ტელეფონის განბლოკვა <xliff:g id="NUMBER_0">%1$d</xliff:g>-ჯერ. კიდევ <xliff:g id="NUMBER_1">%2$d</xliff:g> წარუმატებელი ცდის შემდეგ ტელეფონზე დაყენდება საწყისი, ქარხნული პარამეტრები და მომხმარებლის ყველა მონაცემი დაიკარგება."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"თქვენ არასწორად სცადეთ ტაბლეტის განბლოკვა <xliff:g id="NUMBER">%d</xliff:g>-ჯერ. ტაბლეტზე დაყენდება საწყისი, ქარხნული პარამეტრები და მომხმარებლის ყველა მონაცემი დაიკარგება."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"თქვენ არასწორად სცადეთ ტელევიზორის განბლოკვა <xliff:g id="NUMBER">%d</xliff:g> ჯერ. ტელევიზორი დაუბრუნდება ქარხნულ ნაგულისხმევ პარამეტრებს."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"თქვენ <xliff:g id="NUMBER">%d</xliff:g>-ჯერ არასწორად სცადეთ ტელეფონის განბლოკვა. ამიტომ ტელეფონზე დადგება საწყისი, ქარხნული პარამეტრები."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"თქვენ არასწორად დახატეთ თქვენი განბლოკვის ნიმუში <xliff:g id="NUMBER_0">%d</xliff:g> ჯერ. კიდევ <xliff:g id="NUMBER_1">%d</xliff:g> წარუმატებელი ცდის შემდეგ მოგთხოვთ ტაბლეტის განბლოკვას ელფოსტის ანგარიშის გამოყენებით.\n\n ხელახლა სცადეთ <xliff:g id="NUMBER_2">%d</xliff:g> წამში."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"თქვენ არასწორად მოხაზეთ თქვენი განბლოკვის თარგი <xliff:g id="NUMBER_0">%d</xliff:g> ჯერ. კიდევ <xliff:g id="NUMBER_1">%d</xliff:g> წარუმატებელი მცდელობის შემდეგ, თქვენ მოგეთხოვებათ განბლოკოთ თქვენი ტელევიზორი ელ ანგარიშის მეშვეობით. \n\n სცადეთ ხელახლა <xliff:g id="NUMBER_2">%d</xliff:g> წამში."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"თქვენ არასწორად დახატეთ თქვენი განბლოკვის ნიმუში <xliff:g id="NUMBER_0">%d</xliff:g>-ჯერ. კიდევ <xliff:g id="NUMBER_1">%d</xliff:g> წარუმატებელი ცდის შემდეგ, დაგჭირდებათ თქვენი ტელეფონის განბლოკვა ელფოსტის ანგარიშის გამოყენებით.\n\n ხელახლა სცადეთ <xliff:g id="NUMBER_2">%d</xliff:g> წამში."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"თქვენ არასწორად დახატეთ თქვენი განბლოკვის ნიმუში <xliff:g id="NUMBER_0">%1$d</xliff:g> ჯერ. კიდევ <xliff:g id="NUMBER_1">%2$d</xliff:g> წარუმატებელი ცდის შემდეგ მოგთხოვთ ტაბლეტის განბლოკვას ელფოსტის ანგარიშის გამოყენებით.\n\n ხელახლა სცადეთ <xliff:g id="NUMBER_2">%3$d</xliff:g> წამში."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"თქვენ არასწორად მოხაზეთ თქვენი განბლოკვის თარგი <xliff:g id="NUMBER_0">%1$d</xliff:g> ჯერ. კიდევ <xliff:g id="NUMBER_1">%2$d</xliff:g> წარუმატებელი მცდელობის შემდეგ, თქვენ მოგეთხოვებათ განბლოკოთ თქვენი ტელევიზორი ელ ანგარიშის მეშვეობით. \n\n სცადეთ ხელახლა <xliff:g id="NUMBER_2">%3$d</xliff:g> წამში."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"თქვენ არასწორად დახატეთ თქვენი განბლოკვის ნიმუში <xliff:g id="NUMBER_0">%1$d</xliff:g>-ჯერ. კიდევ <xliff:g id="NUMBER_1">%2$d</xliff:g> წარუმატებელი ცდის შემდეგ, დაგჭირდებათ თქვენი ტელეფონის განბლოკვა ელფოსტის ანგარიშის გამოყენებით.\n\n ხელახლა სცადეთ <xliff:g id="NUMBER_2">%3$d</xliff:g> წამში."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"ამოშლა"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"გსურთ ხმის რეკომენდებულ დონეზე მაღლა აწევა?\n\nხანგრძლივად ხმამაღლა მოსმენით შესაძლოა სმენადობა დაიზიანოთ."</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">1 სთ.</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>-მდე"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>-მდე (შემდეგი მაღვიძარა)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"სანამ ამას გამორთავდეთ"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"სანამ გამორთავთ „არ შემაწუხოთ“ ფუნქციას"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-kk-rKZ-watch/strings.xml b/core/res/res/values-kk-rKZ-watch/strings.xml
index 8c41688..3ba56869 100644
--- a/core/res/res/values-kk-rKZ-watch/strings.xml
+++ b/core/res/res/values-kk-rKZ-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_0">%1$d</xliff:g>/<xliff:g id="NUMBER_1">%2$d</xliff:g> бағдарлама."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Сенсорлар"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"контактілерге қатынасу"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"осы сағаттың орнына қатынасу"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"күнтізбеге қатынасу"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"SMS хабарларын жіберу және көру"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"сағаттағы фотосуреттерге, медиафайлдарға және файлдарға қатынасу"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"аудио жазу"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"суреттер түсіру және бейне жазу"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"қоңырау шалу және телефон қоңырауларын басқару"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"ағза күйінің көрсеткіштері туралы сенсор деректеріне қатынасу"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"күй жолағы болу"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"дене датчиктеріне (мысалы, жүрек соғу жиілігінің мониторларына) қатынасу"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"дәл орынға қатынасу (GPS және желіге негізделген)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"шамамен алған орынға қатынасу (желі негізінде)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"SIM картасына пәрмендер жіберу"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"желіге толық қатынасы бар"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"профиль және құрылғы иелерін басқару"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"WiMAX күйін өзгерту"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"Android Beam тасымалдау күйін алу"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"медиа шығысын бағыттау"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"орнату сеанстарын оқу"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"орнату бумаларын сұрау"</string>
</resources>
diff --git a/core/res/res/values-kk-rKZ/strings.xml b/core/res/res/values-kk-rKZ/strings.xml
index 7af4df0..8e45087 100644
--- a/core/res/res/values-kk-rKZ/strings.xml
+++ b/core/res/res/values-kk-rKZ/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Кредит карта нөмірі және кілтсөздер сияқты жеке деректерді қоса."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"күйін көрсету тақтасын өшіру немесе өзгерту"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Қолданбаға күй жолағын өшіруге немесе жүйелік белгішелерді қосуға және жоюға рұқсат береді."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"күйін көрсету жолағы"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"күй жолағы болу"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Қолданбаға күй жолағы болуға рұқсат береді."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"күйі жолағын кеңейту/жиыру"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Қолданбаға статус жолағын жаюға емесе тасалауға рұқсат береді."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Қолданбаға WAP хабарларын алу және өңдеу мүмкіндігін береді. Бұл қолданба құрылғыңызға жіберілген хабарларды сізге көрсетпестен қабылдай және жоя алады дегенді білдіреді."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"жұмыс істеп жатқан қолданбаларды шығарып алу"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Қолданбаларға ағымдағы және соңғы тапсырмалар туралы ақпарат алу мүмкіндігін береді. Бұл қолданбаға құрылғы қолданатын басқа қолданбалар туралы деректері анықтау мүмкіндігін беруі ықтимал."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Профиль және құрылғы иелерін басқару"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"профиль және құрылғы иелерін басқару"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Қолданбаларға профиль иелері мен құрылғы иесін орнатуға мүмкіндік береді."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"жұмыс істеп жатқан қолданбалардың ретін өзгерту"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Қолданбаға тапсырмаларды алғы немесе артқы шепке жылжыту мүмкіндігін береді. Қолданба бұны сіздің қатысуыңызсыз жасауы мүмкін."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Қолданбаға сіздің планшетіңіздегі қоңырау тіркеуін, келетін немесе шығатын қоңыраулар туралы деректерді қоса, өзгерту мүмкіндігін береді. Залалды қолданбалар бұны сіздің қоңырау тіркеуіңізді өшіру үшін қолдануы мүмкін."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Қолданбаға ТД қоңыраулар журналын, соның ішінде, кіріс және шығыс қоңыраулар туралы деректерді өзгертуге рұқсат етеді. Зиянкес қолданбалар мұны қоңыраулар журналын өшіру немесе өзгерту үшін пайдалануы мүмкін."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Қолданбаға сіздің телефоныңыздың қоңырау тіркеуін, келетін немесе шығатын қоңыраулар туралы деректерді қоса, өзгерту мүмкіндігін береді. Залалды қолданбалар бұны сіздің қоңырау тіркеуіңізді өшіру үшін қолдануы мүмкін."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"дене сен-ры (жүрек соғу жиіл. мон-ры сияқты)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"дене датчиктеріне (мысалы, жүрек соғу жиілігінің мониторларына) қатынасу"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Қолданбаға жүрек соғу жиілігіңіз сияқты дене күйіңізді бақылайтын сенсорлардың деректеріне қатынасуға рұқсат етеді."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"күнтізбе шаралары мен құпия ақпаратты оқу"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Қолданбаға планшетте сақталған барлық күнтізбе шараларын, достар немесе әріптестердің шараларын қоса, оқу мүмкіндігін береді. Бұл қолданбаға күнтізбе деректерін, құпиялығы мен сезімталдығына қарамастан, бөлісу немесе сақтау мүмкіндігін беруі ықтимал."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Қолданбаға телефондағы сізге өзгертуге болатын шараларды, достарыңыз бен әріптестеріңіздің шараларын қоса, қосу, алу және өзгерту мүмкіндігін береді. Бұл қолданбаға күнтізбе иелерінен келген сияқты көрсетілетін хабарлар жіберу немесе иесінің хабарынсыз шараларды өзгерту мүмкіндігін береді."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"қосымша аймақ жабдықтаушы пәрмендеріне қол жетімділік"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Қолданбаға орын жеткізушісінің қосымша пәрмендеріне қатынасуға рұқсат береді. Бұл қолданбаға GPS немесе басқа орын көздерінің жұмысына кедергі келтіруге рұқсат беруі мүмкін."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"нақты аймақ (GPS және желі негізделген)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"дәл орынға қатынасу (GPS және желіге негізделген)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Қолданбаға Жаһандық Аймақ Анықтау Жүйесін (GPS) немесе ұялы мұнара және Wi-Fi сияқты желі арқылы аймақ анықтау қызметтерін қолданып, тұрған жеріңізді нақты анықтау мүмкіндігін береді. Бұл аймақ қызметтері қолданбаларға қол жетімді болу үшін қосылып тұруы қажет. Қолданбалар оны сіздің тұрған жеріңізді шамалап анықтау үшін қолдануы мүмкін және батарея тұтынуы мүмкін."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"шамаланған аймақ (желі негізінде)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"шамамен алған орынға қатынасу (желі негізінде)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Қолданбаға сіздің тұрған жеріңізді шамалап анықтау мүмкіндігін береді. Бұл аймақ ұялы мұнара және Wi-Fi сияқты желі арқылы аймақ анықтау қызметтерін қолданатын аймақ анықтау функциясы арқылы анықталған. Бұл аймақ қызметтері қолданбаларға қол жетімді болу үшін қосылып тұруы қажет. Қолданбалар оны сіздің тұрған жеріңізді шамалап анықтау үшін қолдануы мүмкін."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"аудио параметрлерін өзгерту"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Қолданбаға дыбыс қаттылығы және аудио шығыс үндеткішін таңдау сияқты жаһандық аудио параметрлерін өзгерту мүмкіндігін береді."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"аудио жазу"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Қолданбаға микрофон арқылы аудио жазу мүмкіндігін береді. Бұл рұқсат қолданбаға кез келген уақытта сіздің құптауыңызсыз аудио жазып алу мүмкіндігін береді."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"sim байланысы"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"SIM картасына пәрмендер жіберу"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Қолданбаға SIM картасына пәрмен жіберу мүмкіндігін береді. Бұл өте қауіпті."</string>
<string name="permlab_camera" msgid="3616391919559751192">"фотосурет жасау және бейне жазу"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Қолданбаға камераны қолданып, фотосурет немесе бейне жазу мүмкіндігін береді. Бұл рұқсат камераны кез келген уақытта сіздің құптауыңызды қажет етпей қолдану мүмкіндігін береді."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Қолданбаға телефон арқылы белгілі есептік жазбалар тізімін алу мүмкіндігін береді. Сіз орнатқан қолданбалар жасақтаған есептік жазбалар да қамтылуы мүмкін."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"желі байланыстарын көру"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Қолданбаға желі байланысы туралы ақпаратты, мысалы, қайсысы бар және қосылған деген сияқты, көру мүмкіндігін береді."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"желіге толық қол жетімділік"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"желіге толық қатынасы бар"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Қолданбаларға желі ұяларын жасақтау және қалыпты желі протоколдарын қолдану мүмкіндігін береді. Деректерді интернетке жіберу үшін бұл рұқсат талап етілмес үшін браузер және басқа қолданбалар деректерді интернетке жіберу жолдарын қамтамасыз етеді."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"желі байланысын өзгерту"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Қолданбаға желілік қосылым күйін өзгертуге рұқсат береді."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Қолданбаға жергілікті Bluetooth телефонын конфигурациялауға, әрі қашықтағы құрылғыларды табуға және олармен жұптауға рұқсат береді."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"WiMAX байланысына жалғану және ажырау"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Қолданбаға WiMAX қосылғаны және қосылған қандай да WiMAX желісі жайлы ақпаратты анықтау мүмкіндігін береді."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX күйін өзгерту"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"WiMAX күйін өзгерту"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Қолданбаларға планшетті WiMAX желілеріне қосу және ажырату мүмкіндіктерін береді."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Қолданбаға теледидарға қосылуға және теледидарды WiMAX желілерінен ажыратуға рұқсат етеді."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Қолданбаларға телефонды WiMAX желілеріне қосу және ажырату мүмкіндіктерін береді."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Қолданбаға сенсорлы экранның параметрлерін өзгертуге рұқсат береді. Қалыпты қолданбалар үшін ешқашан қажет болмайды."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"DRM сертификаттарына қатынасу"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Қолданбаға DRM сертификаттарын қамтамасыз етуге және пайдалануға рұқсат береді. Қалыпты қолданбалар үшін ешқашан қажет болмайды."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Android Beam тасымалдау күйін алу"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"Android Beam тасымалдау күйін алу"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Осы қолданбаға ағымдағы Android Beam тасымалдаулары туралы ақпарат алуға рұқсат ету"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"DRM сертификаттарын жою"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Қолданбаға DRM сертификаттарын жоюға рұқсат етеді. Қалыпты қолданбалар үшін ешқашан қажет болмайды."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Қате PIN код"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Бекітпесін ашу үшін Мәзір, одан кейін 0 пернесін түртіңіз."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Төтенше жағдай нөмірі"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Қызмет көрсетілмейді."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Қызмет жоқ"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Экран бекітілген."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Бекітпесін ашу үшін немесе төтенше қоңырауды табу үшін Мәзір тармағын басыңыз."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Ашу үшін Мәзір пернесін басыңыз."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Пайдаланушы нұсқаулығын қараңыз немесе тұтынушыларды қолдау орталығына хабарласыңыз."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM картасы бөгелген."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"SIM картаны ашуда…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Құлыпты ашу өрнегін <xliff:g id="NUMBER_0">%d</xliff:g> рет қате салдыңыз. \n\n <xliff:g id="NUMBER_1">%d</xliff:g> секундтан кейін әрекетті қайталаңыз."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Құпия сөзді <xliff:g id="NUMBER_0">%d</xliff:g> рет қате тердіңіз. \n\n <xliff:g id="NUMBER_1">%d</xliff:g> секундтан кейін қайталаңыз."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"PIN кодын <xliff:g id="NUMBER_0">%d</xliff:g> рет қате тердіңіз. \n\n <xliff:g id="NUMBER_1">%d</xliff:g> секундтан кейін қайталаңыз."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Бекітпесін ашу өрнегін <xliff:g id="NUMBER_0">%d</xliff:g> рет қате салдыңыз. Тағы <xliff:g id="NUMBER_1">%d</xliff:g> сәтсіз әрекеттен кейін сізден Google жүйесіне кіріп планшет бекітпесін ашу сұралады.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> секундтан кейін әрекетті қайталаңыз."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Бекітпесін ашу өрнегін <xliff:g id="NUMBER_0">%d</xliff:g> рет дұрыс емес салдыңыз. Тағы <xliff:g id="NUMBER_1">%d</xliff:g> сәтсіз әрекеттен кейін сізден Google кіруді пайдаланып ТД бекітпесін ашу сұралады.\n\n Әрекетті <xliff:g id="NUMBER_2">%d</xliff:g> секундтан кейін қайталаңыз."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Бекітпесін ашу өрнегін <xliff:g id="NUMBER_0">%d</xliff:g> рет қате салдыңыз. Тағы <xliff:g id="NUMBER_1">%d</xliff:g> сәтсіз әрекеттен кейін сізден Google жүйесіне кіріп телефон бекітпесін ашу сұралады.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> секундтан кейін әрекетті қайталаңыз."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Планшеттің бекітпесін ашуға <xliff:g id="NUMBER_0">%d</xliff:g> рет қате әрекеттендіңіз. <xliff:g id="NUMBER_1">%d</xliff:g> сәтсіз әрекеттен кейін, телефон зауыттың бастапқы параметрлеріне қайта реттеледі және пайдаланушы деректері жоғалады."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"ТД бекітпесін <xliff:g id="NUMBER_0">%d</xliff:g> рет дұрыс емес ашу әрекетін жасадыңыз. Тағы <xliff:g id="NUMBER_1">%d</xliff:g> сәтсіз әрекеттен кейін ТД зауыттық әдепкі параметрлері қалпына келтіріледі және бүкіл пайдаланушы деректері жоғалады."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Телефонның бекітпесін ашуға <xliff:g id="NUMBER_0">%d</xliff:g> рет қате әрекеттендіңіз. <xliff:g id="NUMBER_1">%d</xliff:g> сәтсіз әрекеттен кейін, телефон зауыттың бастапқы параметрлеріне қайта реттеледі және пайдаланушы деректері жоғалады."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Құлыпты ашу өрнегін <xliff:g id="NUMBER_0">%1$d</xliff:g> рет қате салдыңыз. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> секундтан кейін әрекетті қайталаңыз."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Құпия сөзді <xliff:g id="NUMBER_0">%1$d</xliff:g> рет қате тердіңіз. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> секундтан кейін қайталаңыз."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"PIN кодын <xliff:g id="NUMBER_0">%1$d</xliff:g> рет қате тердіңіз. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> секундтан кейін қайталаңыз."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Бекітпесін ашу өрнегін <xliff:g id="NUMBER_0">%1$d</xliff:g> рет қате салдыңыз. Тағы <xliff:g id="NUMBER_1">%2$d</xliff:g> сәтсіз әрекеттен кейін сізден Google жүйесіне кіріп планшет бекітпесін ашу сұралады.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундтан кейін әрекетті қайталаңыз."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Бекітпесін ашу өрнегін <xliff:g id="NUMBER_0">%1$d</xliff:g> рет дұрыс емес салдыңыз. Тағы <xliff:g id="NUMBER_1">%2$d</xliff:g> сәтсіз әрекеттен кейін сізден Google кіруді пайдаланып ТД бекітпесін ашу сұралады.\n\n Әрекетті <xliff:g id="NUMBER_2">%3$d</xliff:g> секундтан кейін қайталаңыз."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Бекітпесін ашу өрнегін <xliff:g id="NUMBER_0">%1$d</xliff:g> рет қате салдыңыз. Тағы <xliff:g id="NUMBER_1">%2$d</xliff:g> сәтсіз әрекеттен кейін сізден Google жүйесіне кіріп телефон бекітпесін ашу сұралады.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундтан кейін әрекетті қайталаңыз."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Планшеттің бекітпесін ашуға <xliff:g id="NUMBER_0">%1$d</xliff:g> рет қате әрекеттендіңіз. <xliff:g id="NUMBER_1">%2$d</xliff:g> сәтсіз әрекеттен кейін, телефон зауыттың бастапқы параметрлеріне қайта реттеледі және пайдаланушы деректері жоғалады."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"ТД бекітпесін <xliff:g id="NUMBER_0">%1$d</xliff:g> рет дұрыс емес ашу әрекетін жасадыңыз. Тағы <xliff:g id="NUMBER_1">%2$d</xliff:g> сәтсіз әрекеттен кейін ТД зауыттық әдепкі параметрлері қалпына келтіріледі және бүкіл пайдаланушы деректері жоғалады."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Телефонның бекітпесін ашуға <xliff:g id="NUMBER_0">%1$d</xliff:g> рет қате әрекеттендіңіз. <xliff:g id="NUMBER_1">%2$d</xliff:g> сәтсіз әрекеттен кейін, телефон зауыттың бастапқы параметрлеріне қайта реттеледі және пайдаланушы деректері жоғалады."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Планшетті ашуға <xliff:g id="NUMBER">%d</xliff:g> рет қате әрекеттендіңіз. Планшет зауыттың бастапқы параметрлеріне қайта реттеледі."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"ТД бекітпесін <xliff:g id="NUMBER">%d</xliff:g> рет дұрыс емес ашу әрекетін жасадыңыз. ТД қазір зауыттық әдепкі параметрлерге қалпына келтіріледі."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Телефонды ашуға <xliff:g id="NUMBER">%d</xliff:g> рет қате әрекеттендіңіз. Телефон зауыттың бастапқы параметрлеріне қайта реттеледі."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"сөз"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"сілтеме"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"сызық"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Зауыт тесті орындалмады."</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"ЗАУЫТ_TEСТІ әрекетінің қолдауы жүйеде/қолданбада орнатылған жинақтар үшін ғана ұсынылған."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"ЗАУЫТ_TEСТІ әрекетін жабдықтайтын жинақ табылмады."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Пішімделуде..."</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Салынбады"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Сәйкес әрекеттер табылмады."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Медиа шығысын бағыттау"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"медиа шығысын бағыттау"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Қолданбаға медиа шығысын басқа сыртқы құрылғыларға бағыттау мүмкіндігін береді."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Орнату сеанстарын оқу"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"орнату сеанстарын оқу"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Қолданбаға орнату сеанстарын оқуға рұқсат етеді. Бұл оған белсенді бума орнатулары туралы мәліметтерді көруге рұқсат етеді."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Бумаларды орнатуға рұқсат сұрау"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"орнату бумаларын сұрау"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Қолданбаның бумаларды орнатуға рұқсат сұрауына мүмкіндік береді."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Масштабтауды басқару үшін екі рет түртіңіз"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Виджетті қосу."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Пайдаланушы атауы немесе кілтсөз жарамсыз."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Пайдаланушы атауын немесе кілтсөзді ұмытып қалдыңыз ба?\n"<b>"google.com/accounts/recovery"</b>" веб-сайтына кіріңіз."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Есептік жазбаны тексеруде…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"PIN кодты <xliff:g id="NUMBER_0">%d</xliff:g> рет қате тердіңіз. \n\n <xliff:g id="NUMBER_1">%d</xliff:g> секундтан кейін қайталаңыз."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Құпия сөзді <xliff:g id="NUMBER_0">%d</xliff:g> рет қате тердіңіз. \n\n <xliff:g id="NUMBER_1">%d</xliff:g> секундтан кейін қайталаңыз."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Құлыпты ашу өрнегін <xliff:g id="NUMBER_0">%d</xliff:g> рет қате салдыңыз. \n\n <xliff:g id="NUMBER_1">%d</xliff:g> секундтан кейін әрекетті қайталаңыз."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Планшетті ашуға <xliff:g id="NUMBER_0">%d</xliff:g> рет қате әрекеттендіңіз. <xliff:g id="NUMBER_1">%d</xliff:g> сәтсіз әрекеттен кейін телефоныңыз бастапқы зауыттық параметрлеріне қайта реттеледі және барлық пайдаланушы деректері жойылады."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"ТД бекітпесін <xliff:g id="NUMBER_0">%d</xliff:g> рет дұрыс емес ашу әрекетін жасадыңыз. Тағы <xliff:g id="NUMBER_1">%d</xliff:g> сәтсіз әрекеттен кейін ТД зауыттық әдепкі параметрлері қалпына келтіріледі және бүкіл пайдаланушы деректері жоғалады."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Телефонды ашуға <xliff:g id="NUMBER_0">%d</xliff:g> рет қате әрекеттендіңіз. <xliff:g id="NUMBER_1">%d</xliff:g> сәтсіз әрекеттен кейін телефоныңыз бастапқы зауыттық параметрлеріне қайта реттеледі және барлық пайдаланушы деректері жойылады."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"PIN кодты <xliff:g id="NUMBER_0">%1$d</xliff:g> рет қате тердіңіз. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> секундтан кейін қайталаңыз."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Құпия сөзді <xliff:g id="NUMBER_0">%1$d</xliff:g> рет қате тердіңіз. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> секундтан кейін қайталаңыз."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Құлыпты ашу өрнегін <xliff:g id="NUMBER_0">%1$d</xliff:g> рет қате салдыңыз. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> секундтан кейін әрекетті қайталаңыз."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Планшетті ашуға <xliff:g id="NUMBER_0">%1$d</xliff:g> рет қате әрекеттендіңіз. <xliff:g id="NUMBER_1">%2$d</xliff:g> сәтсіз әрекеттен кейін телефоныңыз бастапқы зауыттық параметрлеріне қайта реттеледі және барлық пайдаланушы деректері жойылады."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"ТД бекітпесін <xliff:g id="NUMBER_0">%1$d</xliff:g> рет дұрыс емес ашу әрекетін жасадыңыз. Тағы <xliff:g id="NUMBER_1">%2$d</xliff:g> сәтсіз әрекеттен кейін ТД зауыттық әдепкі параметрлері қалпына келтіріледі және бүкіл пайдаланушы деректері жоғалады."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Телефонды ашуға <xliff:g id="NUMBER_0">%1$d</xliff:g> рет қате әрекеттендіңіз. <xliff:g id="NUMBER_1">%2$d</xliff:g> сәтсіз әрекеттен кейін телефоныңыз бастапқы зауыттық параметрлеріне қайта реттеледі және барлық пайдаланушы деректері жойылады."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Планшетті ашуға <xliff:g id="NUMBER">%d</xliff:g> рет қате әрекеттендіңіз. Планшет бастапқы зауыттық параметрлеріне қайта реттеледі."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"ТД бекітпесін <xliff:g id="NUMBER">%d</xliff:g> рет дұрыс емес ашу әрекетін жасадыңыз. ТД қазір зауыттық әдепкі параметрлерге қалпына келтіріледі."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Телефонды ашуға <xliff:g id="NUMBER">%d</xliff:g> рет қате әрекеттендіңіз. Телефон бастапқы зауыттық параметрлеріне қайта реттеледі."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Бекітпені ашу кескінін <xliff:g id="NUMBER_0">%d</xliff:g> рет қате сыздыңыз. After <xliff:g id="NUMBER_1">%d</xliff:g> сәтсіз әрекеттен кейін планшетіңізді есептік жазба арқылы ашу өтінішін аласыз.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> секундтан кейін қайта әрекеттеніңіз."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Бекітпесін ашу өрнегін <xliff:g id="NUMBER_0">%d</xliff:g> рет қате салдыңыз. Тағы <xliff:g id="NUMBER_1">%d</xliff:g> сәтсіз әрекеттен кейін сізден электрондық пошта есептік жазбасын пайдаланып ТД бекітпесін ашу сұралады.\n\n Әрекетті <xliff:g id="NUMBER_2">%d</xliff:g> секундтан кейін қайталаңыз."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Бекітпені ашу кескінін <xliff:g id="NUMBER_0">%d</xliff:g> рет қате сыздыңыз. <xliff:g id="NUMBER_1">%d</xliff:g> сәтсіз әрекеттен кейін телефоныңызды есептік жазба арқылы ашу өтінішін аласыз. \n\n <xliff:g id="NUMBER_2">%d</xliff:g> секундтан кейін қайта әрекеттеніңіз."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Бекітпені ашу кескінін <xliff:g id="NUMBER_0">%1$d</xliff:g> рет қате сыздыңыз. After <xliff:g id="NUMBER_1">%2$d</xliff:g> сәтсіз әрекеттен кейін планшетіңізді есептік жазба арқылы ашу өтінішін аласыз.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундтан кейін қайта әрекеттеніңіз."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Бекітпесін ашу өрнегін <xliff:g id="NUMBER_0">%1$d</xliff:g> рет қате салдыңыз. Тағы <xliff:g id="NUMBER_1">%2$d</xliff:g> сәтсіз әрекеттен кейін сізден электрондық пошта есептік жазбасын пайдаланып ТД бекітпесін ашу сұралады.\n\n Әрекетті <xliff:g id="NUMBER_2">%3$d</xliff:g> секундтан кейін қайталаңыз."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Бекітпені ашу кескінін <xliff:g id="NUMBER_0">%1$d</xliff:g> рет қате сыздыңыз. <xliff:g id="NUMBER_1">%2$d</xliff:g> сәтсіз әрекеттен кейін телефоныңызды есептік жазба арқылы ашу өтінішін аласыз. \n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундтан кейін қайта әрекеттеніңіз."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Алып тастау"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Дыбыс деңгейін ұсынылған деңгейден көтеру керек пе?\n\nЖоғары дыбыс деңгейінде ұзақ кезеңдер бойы тыңдау есту қабілетіңізге зиян тигізуі мүмкін."</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">1 сағат</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> дейін"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> дейін (келесі дабыл)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Сіз осыны өшіргенше"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Өшірмейінше мазаламау"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-km-rKH-watch/strings.xml b/core/res/res/values-km-rKH-watch/strings.xml
index 5c1bf8a..624aab7 100644
--- a/core/res/res/values-km-rKH-watch/strings.xml
+++ b/core/res/res/values-km-rKH-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"កម្មវិធី <xliff:g id="NUMBER_0">%1$d</xliff:g> នៃ <xliff:g id="NUMBER_1">%2$d</xliff:g>។"</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"ឧបករណ៍ចាប់សញ្ញា"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"ចូលដំណើរការទំនាក់ទំនងរបស់អ្នក"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"ចូលដំណើរការទីតាំងនាឡិកាដៃនេះ"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"ចូលដំណើរការប្រិតិទិនរបស់អ្នក"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"ផ្ញើ និងមើលសារ SMS"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"ចូលដំណើរការរូបថត មេឌៀ និងឯកសារនៅលើនាឡិកាដៃរបស់អ្នក។"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"ថតសំឡេង"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"ថតរូប និងថតវីដេអូ"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"ហៅទូរស័ព្ទ និងគ្រប់គ្រងការហៅទូរស័ព្ទ"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"ចូលដំណើរការទិន្នន័យឧបករណ៍ចាប់សញ្ញាអំពីស្ថានភាពសុខភាពរបស់អ្នក"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"ធ្វើជារបារស្ថានភាព"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"ចូលដំណើរការឧបករណ៍ចាប់សញ្ញារាងកាយ (ដូចជាម៉ាស៊ីនវាស់ចង្វាក់បេះដូង)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"ចូលដំណើរការទីតាំងច្បាស់លាស់ (ផ្អែកលើបណ្តាញ និង GPS)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"ចូលដំណើរការទីតាំងប្រហាក់ប្រហែល (ផ្អែកលើបណ្តាញ)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"ផ្ញើពាក្យបញ្ជាទៅស៊ីមកាត"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"មានការចូលដំណើរការបណ្ដាញពេញលេញ"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"គ្រប់គ្រងម្ចាស់ឧបករណ៍ និងប្រវត្តិរូប"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"ប្ដូរស្ថានភាព WiMAX"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"ទទួលបានស្ថានភាពផ្ទេរ Android Beam"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"នាំផ្លូវលទ្ធផលមេឌៀ"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"អានវេនដំឡើង"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"ស្នើសុំកញ្ចប់ដំឡើង"</string>
</resources>
diff --git a/core/res/res/values-km-rKH/strings.xml b/core/res/res/values-km-rKH/strings.xml
index 3a02e76..69fe257 100644
--- a/core/res/res/values-km-rKH/strings.xml
+++ b/core/res/res/values-km-rKH/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"រួមបញ្ចូលទិន្នន័យផ្ទាល់ខ្លួន ដូចជាលេខកាតឥណទាន និងពាក្យសម្ងាត់។"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"បិទ ឬកែរបារស្ថានភាព"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"ឲ្យកម្មវិធីបិទរបារស្ថានភាព ឬបន្ថែម និងលុបរូបតំណាងប្រព័ន្ធ។"</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"របារស្ថានភាព"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"ធ្វើជារបារស្ថានភាព"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"ឲ្យកម្មវិធីក្លាយជារបារស្ថានភាព។"</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"ពង្រីក/បង្រួមរបារស្ថានភាព"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"ឲ្យកម្មវិធីពង្រីក ឬបង្រួមរបារស្ថានភាព។"</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"ឲ្យកម្មវិធីទទួល និងដំណើរការសារ WAP ។ សិទ្ធិនេះមានលទ្ធភាពតាមដាន ឬលុបសារដែលបានផ្ញើឲ្យអ្នកដោយមិនបង្ហាញ។"</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"ទៅយកកម្មវិធីកំពុងដំណើរការ"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"ឲ្យកម្មវិធីទៅយកព័ត៌មានលម្អិតអំពីកិច្ចការដែលកំពុងដំណើរការបច្ចុប្បន្ន។ វាអាចឲ្យកម្មវិធីរកមើលព័ត៌មានថាតើកម្មវិធីណាមួយត្រូវបានប្រើលើឧបករណ៍។"</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"គ្រប់គ្រងម្ចាស់ឧបករណ៍ និងប្រវត្តិរូប"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"គ្រប់គ្រងម្ចាស់ឧបករណ៍ និងប្រវត្តិរូប"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"អនុញ្ញាតឲ្យកម្មវិធីកំណត់ម្ចាស់ប្រវត្តិរូប និងម្ចាស់ឧបករណ៍។"</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"តម្រៀបកម្មវិធីកំពុងដំណើរការឡើងវិញ"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"ឲ្យកម្មវិធីផ្លាស់ទីភារកិច្ចទៅផ្ទៃខាងមុខ។ កម្មវិធីអាចធ្វើវាដោយគ្មានការបញ្ចូលរបស់អ្នក។"</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"ឲ្យកម្មវិធីកែបញ្ជីហៅកុំព្យូទ័របន្ទះរបស់អ្នករួមមានទិន្នន័យអំពីការហៅចូល និងចេញ។កម្មវិធីព្យាបាទអាចប្រើវា ដើម្បីលុប ឬកែបញ្ជីហៅរបស់អ្នក។"</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"អនុញ្ញាតឲ្យកម្មវិធីកែសម្រួលកំណត់ហេតុហៅទូរស័ព្ទទូរទស្សន៍របស់អ្នក ដោយរាប់បញ្ចូលទាំងទិន្នន័យអំពីការហៅចូល និងការហៅចេញ។ កម្មវិធីព្យាបាទអាចប្រើវាដើម្បីលុប ឬកែសម្រួលកំណត់ហេតុការហៅទូរស័ព្ទរបស់អ្នក។"</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"ឲ្យកម្មវិធីកែបញ្ជីហៅនៃទូរស័ព្ទរបស់អ្នក រួមមានទិន្នន័យអំពីការហៅចូល និងចេញ។ កម្មវិធីព្យាបាទអាចប្រើវា ដើម្បីលុប ឬកែបញ្ជីការហៅរបស់អ្នក។"</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"ឧបករណ៍ចាប់សញ្ញារាងកាយ(ដូចជាម៉ាស៊ីនវាស់ចង្វាក់បេះដូង)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"ចូលដំណើរការឧបករណ៍ចាប់សញ្ញារាងកាយ (ដូចជាម៉ាស៊ីនវាស់ចង្វាក់បេះដូង)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"ឲ្យកម្មវិធីចូលដំណើរការទិន្នន័យពីឧបករណ៍ចាប់សញ្ញាដែលតាមដានលក្ខខណ្ឌសុខភាពរបស់អ្នក ដូចជាចង្វាក់បេះដូង។"</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"អានព្រឹត្តិការណ៍ប្រតិទិននិងព័ត៌មានសម្ងាត់"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"ឲ្យកម្មវិធីអាចព្រឹត្តិការណ៍ប្រតិទិនទាំងអស់ដែលបានរក្សាទុកក្នុងទូរស័ព្ទរបស់អ្នក រួមមានមិត្តភ័ក្ដិ និងមិត្តរួមការងារ។ វាអាចឲ្យកម្មវិធីចែករំលែក ឬរក្សាទុកទិន្នន័យប្រតិទិនរបស់អ្នកដោយមិនគិតពីការសម្ងាត់ ឬការយល់ដឹង។"</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"ឲ្យកម្មវិធីបន្ថែម លុប ឬប្ដូរព្រឹត្តិការណ៍ដែលអ្នកអាចកែប្រែលើទូរស័ព្ទរបស់អ្នក រួមមានមិត្តភ័ក្ដិ ឬមិត្តរួមការងារ។ វាអាចឲ្យកម្មវិធីផ្ញើសារដែលបង្ហាញថាមកពីម្ចាស់ប្រតិទិន ឬកែព្រឹត្តិការណ៍ដោយមិនឲ្យអ្នកដឹង។"</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"ចូលដំណើរការពាក្យបញ្ជាក្រុមហ៊ុនផ្ដល់ទីតាំង"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"ឲ្យកម្មវិធីចូលដំណើរការពាក្យបញ្ជាកម្មវិធីផ្ដល់ទីតាំងបន្ថែម។ វាអាចអនុញ្ញាតឲ្យកម្មវិធីទាក់ទងជាមួយប្រតិបត្តិការជីភីអេស ឬប្រភពទីតាំងផ្សេង។"</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"ទីតាំងពិតប្រាកដ (GPS និងមានមូលដ្ឋានលើបណ្ដាញ)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"ចូលដំណើរការទីតាំងច្បាស់លាស់ (ផ្អែកលើបណ្តាញ និង GPS)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"ឲ្យកម្មវិធីទទួលទីតាំងពិតប្រាកដរបស់អ្នក ដោយប្រើប្រព័ន្ធកំណត់ទីតាំងសកម្ម (GPS) ឬប្រភពទីតាំងបណ្ដាញដូចជា អង់តែនចល័ត និងវ៉ាយហ្វាយ។ សេវាកម្មទីតាំងទាំងនេះត្រូវតែបើក និងអាចប្រើចំពោះឧបករណ៍របស់អ្នកសម្រាប់កម្មវិធីដែលប្រើពួកវា។ កម្មវិធីអាចប្រើវា ដើម្បីកំណត់ទីកន្លែងរបស់អ្នក និងអាចប្រើថាមពលថ្មបន្ថែម។"</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"ទីតាំងប្រហាក់ប្រហែល (មានមូលដ្ឋានលើបណ្ដាញ)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"ចូលដំណើរការទីតាំងប្រហាក់ប្រហែល (ផ្អែកលើបណ្តាញ)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"ឲ្យកម្មវិធីទទួលទីតាំងប្រហាក់ប្រហែល។ ទីតាំងនេះត្រូវបានទទួលតាមសេវាកម្មទីតាំងដោយប្រើប្រភពទីតាំងបណ្ដាញដូចជា អង់តែន និងវ៉ាយហ្វាយ។ សេវាកម្មទីតាំងទាំងនេះត្រូវតែបើក និងអាចប្រើបានចំពោះឧបករណ៍របស់អ្នកសម្រាប់កម្មវិធីដែលប្រើពួកវា។ កម្មវិធីអាចប្រើវា ដើម្បីកំណត់កន្លែងដែលអ្នកនៅប្រហាក់ប្រហែល។"</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"ប្ដូរការកំណត់អូឌីយូរបស់អ្នក"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"ឲ្យកម្មវិធីកែការកំណត់សំឡេងសកល ដូចជាកម្រិតសំឡេង និងអូប៉ាល័រដែលបានប្រើសម្រាប់លទ្ធផល។"</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"ថតសំឡេង"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"ឱ្យកម្មវិធីថតសំឡេងជាមួយមីក្រូហ្វូន។ សិទ្ធិនេះអនុញ្ញាតឲ្យកម្មវិធីថតសំឡេងនៅពេលណាមួយដោយគ្មានការបញ្ជាក់របស់អ្នក។"</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"ការភ្ជាប់ស៊ីមកាត"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"ផ្ញើពាក្យបញ្ជាទៅស៊ីមកាត"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"ឲ្យកម្មវិធីផ្ញើពាក្យបញ្ជាទៅស៊ីមកាត។ វាគ្រោះថ្នាក់ណាស់។"</string>
<string name="permlab_camera" msgid="3616391919559751192">"ថតរូប និងវីដេអូ"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"ឲ្យកម្មវិធីថតរូប និងវីដេអូដោយប្រើម៉ាស៊ីនថត។ វាឲ្យកម្មវិធីប្រើម៉ាស៊ីនថតនៅពេលណាមួយដោយគ្មានការបញ្ជាក់របស់អ្នក។"</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"ឲ្យកម្មវិធីទទួលបញ្ជីគណនីដែលទូរស័ព្ទបានស្គាល់។ វាអាចមានគណនីដែលបានបង្កើតដោយកម្មវិធីដែលអ្នកបានដំឡើង។"</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"មើលការតភ្ជាប់បណ្ដាញ"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"ឲ្យកម្មវិធីមើលព័ត៌មានអំពីការតភ្ជាប់បណ្ដាញដូចជាមានបណ្ដាញណាមួយ និងបណ្ដាញត្រូវបានភ្ជាប់។"</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"ចូលដំណើរការបណ្ដាញពេញលេញ"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"មានការចូលដំណើរការបណ្ដាញពេញលេញ"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"ឲ្យកម្មវិធីបង្កើតរន្ធបណ្ដាញ និងប្រើពិធីការបណ្ដាញតាមបំណង។ កម្មវិធីអ៊ីនធឺណិត និងកម្មវិធីផ្សេងៗផ្ដល់វិធីផ្ញើទិន្នន័យទៅអ៊ីនធឺណិត ដូច្នេះសិទ្ធិនេះមិនទាមទារឲ្យផ្ញើទិន្នន័យទៅអ៊ីនធឺណិត។"</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"ប្ដូរការតភ្ជាប់បណ្ដាញ"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"ឲ្យកម្មវិធីប្ដូរស្ថានភាពតភ្ជាប់បណ្ដាញ។"</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"ឲ្យកម្មវិធីមើលការកំណត់រចនាសម្ព័ន្ធប៊្លូធូសក្នុងទូរស័ព្ទ ដើម្បីរកមើល និងផ្គូផ្គងជាមួយឧបករណ៍ពីចម្ងាយ។"</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"ភ្ជាប់ និងផ្ដាច់ពី WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"ឲ្យកម្មវិធីកំណត់ថាតើ WiMAX ត្រូវបានបើក និងព័ត៌មានអំពីបណ្ដាញ WiMAX ដែលត្រូវបានតភ្ជាប់។"</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"ប្ដូរស្ថានភាព WiMAX"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"ប្ដូរស្ថានភាព WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"ឲ្យកម្មវិធីតភ្ជាប់ និងផ្ដាច់កុំព្យូទ័របន្ទះពីបណ្ដាញ WiMAX ។"</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"អនុញ្ញាតឲ្យកម្មវិធីភ្ជាប់ទៅទូរទស្សន៍ ហើយផ្តាច់ទូរទស្សន៍ពីបណ្តាញ WiMAX។"</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"ឲ្យកម្មវិធីភ្ជាប់ទូរស័ព្ទ និងផ្ដាច់ពីបណ្ដាញ WiMAX ។"</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"ឲ្យកម្មវិធីកែប៉ារ៉ាម៉ែត្រកែចំណុចនៃការប៉ះអេក្រង់។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"ចូលមើលវិញ្ញាបនបត្រ DRM"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"ឲ្យកម្មវិធីផ្ដល់ និងប្រើវិញ្ញាបនបត្រ DRM ។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"ទទួលស្ថានភាពផ្ទេរ Android Beam"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"ទទួលបានស្ថានភាពផ្ទេរ Android Beam"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"ឲ្យកម្មវិធីទទួលព័ត៌មានអំពីការផ្ទេរ Android Beam បច្ចុប្បន្ន"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"លុបវិញ្ញាបនបត្រ DRM ចេញ"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"អនុញ្ញាតឲ្យកម្មវិធីលុបចេញវិញ្ញាបនបត្រ DRM ។ គួរតែមិនត្រូវការសម្រាប់កម្មវិធីធម្មតា។"</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"កូដ PIN មិនត្រឹមត្រូវ។"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"ដើម្បីដោះសោ ចុចម៉ឺនុយ បន្ទាប់មក 0 ។"</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"លេខពេលអាសន្ន"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"គ្មានសេវា"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"គ្មានសេវាទេ"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"ចាក់អេក្រង់។"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"ចុចម៉ឺនុយ ដើម្បីដោះសោ ឬហៅពេលអាសន្ន។"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"ចុចម៉ឺនុយ ដើម្បីដោះសោ។"</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"មើលមគ្គុទ្ទេសក៍អ្នកប្រើ ឬទាក់ទងសេវាអតិថិជន។"</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"ស៊ីមកាតជាប់សោ។"</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"កំពុងដោះសោស៊ីមកាត..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"អ្នកបានគូរលំនាំដោះសោមិនត្រឹមត្រូវចំនួន <xliff:g id="NUMBER_0">%d</xliff:g> ដង។ \n\nព្យាយាមម្ដងទៀតក្នុងរយៈពេល <xliff:g id="NUMBER_1">%d</xliff:g> វិនាទី។"</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"អ្នកបានបញ្ចូលពាក្យសម្ងាត់របស់អ្នកមិនត្រឹមត្រូវ <xliff:g id="NUMBER_0">%d</xliff:g> ដង។ \n\nព្យាយាមម្ដងទៀតក្នុងរយៈពេល <xliff:g id="NUMBER_1">%d</xliff:g> វិនាទី។"</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"អ្នកបានបញ្ចូលកូដ PIN មិនត្រឹមត្រូវចំនួន <xliff:g id="NUMBER_0">%d</xliff:g> ដង។ \n\n ព្យាយាមម្ដងទៀតក្នុងរយៈពេល <xliff:g id="NUMBER_1">%d</xliff:g> វិនាទី។"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"អ្នកបានគូរលំនាំដោះសោរបស់អ្នកមិនត្រឹមត្រូវចំនួន <xliff:g id="NUMBER_0">%d</xliff:g> ដង។ បន្ទាប់ពីការព្យាយាមមិនជោគជ័យច្រើនជាង <xliff:g id="NUMBER_1">%d</xliff:g> ដង អ្នកនឹងត្រូវបានស្នើឲ្យដោះសោកុំព្យូទ័របន្ទះរបស់អ្នក ដោយប្រើការចូលក្នុងគណនី Google របស់អ្នក។\n\n សូមព្យាយាមម្ដងទៀតក្នុងរយៈពេល <xliff:g id="NUMBER_2">%d</xliff:g> វិនាទី។"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"អ្នកបានគូសលំនាំដោះសោរបស់អ្នកមិនត្រឹមត្រូវ <xliff:g id="NUMBER_0">%d</xliff:g> ដង។ បន្ទាប់ពីការព្យាយាមបរាជ័យ <xliff:g id="NUMBER_1">%d</xliff:g> ដងទៀត អ្នកនឹងត្រូវបានសួរឲ្យដោះសោទូរទស្សន៍របស់អ្នកដោយប្រើការចូលគណនី Google។\n\n ព្យាយាមម្តងទៀតក្នុងរយៈពេល <xliff:g id="NUMBER_2">%d</xliff:g> វិនាទី។"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"អ្នកបានគូរលំនាំដោះសោរបស់អ្នកមិនត្រឹមត្រូវ <xliff:g id="NUMBER_0">%d</xliff:g> ដង។ បន្ទាប់ពីការព្យាយាមមិនជោគជ័យ <xliff:g id="NUMBER_1">%d</xliff:g> ដងទៀត អ្នកនឹងត្រូវបានស្នើឲ្យដោះសោទូរស័ព្ទរបស់អ្នកដោយប្រើការចូល Google ។\n\n ព្យាយាមម្ដងទៀតក្នុងរយៈពេល <xliff:g id="NUMBER_2">%d</xliff:g> វិនាទី។"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"អ្នកបានព្យាយាមដោះសោកុំព្យូទ័របន្ទះមិនត្រឹមត្រូវ <xliff:g id="NUMBER_0">%d</xliff:g> ដង។ បន្ទាប់ពីការព្យាយាមមិនជោគជ័យ <xliff:g id="NUMBER_1">%d</xliff:g> ដងទៀត កុំព្យូទ័របន្ទះនឹងត្រូវបានកំណត់ដូចចេញពីរោងចក្រឡើងវិញ ហើយទិន្នន័យអ្នកប្រើទាំងអស់នឹងបាត់បង់។"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"អ្នកបានព្យាយាមដោះសោទូរទស្សន៍របស់អ្នកមិនត្រឹមត្រូវ <xliff:g id="NUMBER_0">%d</xliff:g> ដង។ បន្ទាប់ពីការព្យាយាមបរាជ័យ <xliff:g id="NUMBER_1">%d</xliff:g> ដងទៀត ទូរទស្សន៍របស់អ្នកនឹងត្រូវបានកំណត់ឡើងវិញទាំងស្រុងតាមលំនាំដើម ហើយទិន្នន័យអ្នកប្រើទាំងអស់នឹងត្រូវបាត់បង់។"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"អ្នកបានព្យាយាមដោះសោទូរស័ព្ទមិនត្រឹមត្រូវ <xliff:g id="NUMBER_0">%d</xliff:g> ដង។ បន្ទាប់ពីការព្យាយាមមិនជោគជ័យ <xliff:g id="NUMBER_1">%d</xliff:g> ទូរស័ព្ទនឹងត្រូវបានកំណត់ដូចចេញពីរោងចក្រឡើងវិញ ហើយបាត់បង់ទិន្នន័យអ្នកប្រើទាំងអស់។"</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"អ្នកបានគូរលំនាំដោះសោមិនត្រឹមត្រូវចំនួន <xliff:g id="NUMBER_0">%1$d</xliff:g> ដង។ \n\nព្យាយាមម្ដងទៀតក្នុងរយៈពេល <xliff:g id="NUMBER_1">%2$d</xliff:g> វិនាទី។"</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"អ្នកបានបញ្ចូលពាក្យសម្ងាត់របស់អ្នកមិនត្រឹមត្រូវ <xliff:g id="NUMBER_0">%1$d</xliff:g> ដង។ \n\nព្យាយាមម្ដងទៀតក្នុងរយៈពេល <xliff:g id="NUMBER_1">%2$d</xliff:g> វិនាទី។"</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"អ្នកបានបញ្ចូលកូដ PIN មិនត្រឹមត្រូវចំនួន <xliff:g id="NUMBER_0">%1$d</xliff:g> ដង។ \n\n ព្យាយាមម្ដងទៀតក្នុងរយៈពេល <xliff:g id="NUMBER_1">%2$d</xliff:g> វិនាទី។"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"អ្នកបានគូរលំនាំដោះសោរបស់អ្នកមិនត្រឹមត្រូវចំនួន <xliff:g id="NUMBER_0">%1$d</xliff:g> ដង។ បន្ទាប់ពីការព្យាយាមមិនជោគជ័យច្រើនជាង <xliff:g id="NUMBER_1">%2$d</xliff:g> ដង អ្នកនឹងត្រូវបានស្នើឲ្យដោះសោកុំព្យូទ័របន្ទះរបស់អ្នក ដោយប្រើការចូលក្នុងគណនី Google របស់អ្នក។\n\n សូមព្យាយាមម្ដងទៀតក្នុងរយៈពេល <xliff:g id="NUMBER_2">%3$d</xliff:g> វិនាទី។"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"អ្នកបានគូសលំនាំដោះសោរបស់អ្នកមិនត្រឹមត្រូវ <xliff:g id="NUMBER_0">%1$d</xliff:g> ដង។ បន្ទាប់ពីការព្យាយាមបរាជ័យ <xliff:g id="NUMBER_1">%2$d</xliff:g> ដងទៀត អ្នកនឹងត្រូវបានសួរឲ្យដោះសោទូរទស្សន៍របស់អ្នកដោយប្រើការចូលគណនី Google។\n\n ព្យាយាមម្តងទៀតក្នុងរយៈពេល <xliff:g id="NUMBER_2">%3$d</xliff:g> វិនាទី។"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"អ្នកបានគូរលំនាំដោះសោរបស់អ្នកមិនត្រឹមត្រូវ <xliff:g id="NUMBER_0">%1$d</xliff:g> ដង។ បន្ទាប់ពីការព្យាយាមមិនជោគជ័យ <xliff:g id="NUMBER_1">%2$d</xliff:g> ដងទៀត អ្នកនឹងត្រូវបានស្នើឲ្យដោះសោទូរស័ព្ទរបស់អ្នកដោយប្រើការចូល Google ។\n\n ព្យាយាមម្ដងទៀតក្នុងរយៈពេល <xliff:g id="NUMBER_2">%3$d</xliff:g> វិនាទី។"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"អ្នកបានព្យាយាមដោះសោកុំព្យូទ័របន្ទះមិនត្រឹមត្រូវ <xliff:g id="NUMBER_0">%1$d</xliff:g> ដង។ បន្ទាប់ពីការព្យាយាមមិនជោគជ័យ <xliff:g id="NUMBER_1">%2$d</xliff:g> ដងទៀត កុំព្យូទ័របន្ទះនឹងត្រូវបានកំណត់ដូចចេញពីរោងចក្រឡើងវិញ ហើយទិន្នន័យអ្នកប្រើទាំងអស់នឹងបាត់បង់។"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"អ្នកបានព្យាយាមដោះសោទូរទស្សន៍របស់អ្នកមិនត្រឹមត្រូវ <xliff:g id="NUMBER_0">%1$d</xliff:g> ដង។ បន្ទាប់ពីការព្យាយាមបរាជ័យ <xliff:g id="NUMBER_1">%2$d</xliff:g> ដងទៀត ទូរទស្សន៍របស់អ្នកនឹងត្រូវបានកំណត់ឡើងវិញទាំងស្រុងតាមលំនាំដើម ហើយទិន្នន័យអ្នកប្រើទាំងអស់នឹងត្រូវបាត់បង់។"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"អ្នកបានព្យាយាមដោះសោទូរស័ព្ទមិនត្រឹមត្រូវ <xliff:g id="NUMBER_0">%1$d</xliff:g> ដង។ បន្ទាប់ពីការព្យាយាមមិនជោគជ័យ <xliff:g id="NUMBER_1">%2$d</xliff:g> ទូរស័ព្ទនឹងត្រូវបានកំណត់ដូចចេញពីរោងចក្រឡើងវិញ ហើយបាត់បង់ទិន្នន័យអ្នកប្រើទាំងអស់។"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"អ្នកបានព្យាយាមដោះសោកុំព្យូទ័របន្ទះមិនត្រឹមត្រូវចំនួន <xliff:g id="NUMBER">%d</xliff:g> ដង។ ឥឡូវកុំព្យូទ័របន្ទះនឹងកំណត់ទៅលំនាំដើមដូចចេញពីរោងចក្រ។"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"អ្នកបានព្យាយាមដោះសោទូរទស្សន៍របស់អ្នកមិនត្រឹមត្រូវ <xliff:g id="NUMBER">%d</xliff:g> ដង។ ទូរទស្សន៍របស់អ្នកនឹងត្រូវបានកំណត់ឡើងវិញទាំងស្រុងតាមលំនាំដើម។"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"អ្នកបានព្យាយាមដោះសោទូរស័ព្ទមិនត្រឹមត្រូវ <xliff:g id="NUMBER">%d</xliff:g> ដង។ ឥឡូវទូរស័ព្ទត្រូវបានកំណត់ទៅលំនាំដើមដូចចេញពីរោងចក្រ។"</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"ពាក្យ"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"តំណ"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"បន្ទាត់"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"បានបរាជ័យក្នុងការសាកល្បងរោងចក្រ"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"សកម្មភាព FACTORY_TEST ត្រូវបានគាំទ្រសម្រាប់តែកញ្ចប់បានដំឡើងក្នុង /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"រកមិនឃើញកញ្ចប់ដែលផ្ដល់សកម្មភាព FACTORY_TEST ។"</string>
@@ -1075,11 +1073,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"កំពុងសម្អាត…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"មិនបានដាក់ចូលទេ"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"រកមិនឃើញសកម្មភាពផ្គូផ្គង។"</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"នាំផ្លូវលទ្ធផលមេឌៀ"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"នាំផ្លូវលទ្ធផលមេឌៀ"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"ឲ្យកម្មវិធីនាំផ្លូវលទ្ធផលមេឌៀទៅឧបករណ៍ខាងក្រៅផ្សេង។"</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"អានសម័យដំឡើង"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"អានវេនដំឡើង"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"ឲ្យកម្មវិធីអានសម័យដំឡើង។ វាអនុញ្ញាតឲ្យឃើញព័ត៌មានលម្អិតអំពីការដំឡើងកញ្ចប់សកម្ម។"</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"ស្នើសុំដំឡើងកញ្ចប់"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"ស្នើសុំកញ្ចប់ដំឡើង"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"អនុញ្ញាតឲ្យកម្មវិធីស្នើសុំដំឡើងកញ្ចប់ (ឯកសារ/មាតិកា)។"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"ប៉ះពីរដង ដើម្បីគ្រប់គ្រងការពង្រីក"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"មិនអាចបន្ថែមធាតុក្រាហ្វិក។"</string>
@@ -1287,18 +1285,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"ឈ្មោះអ្នកប្រើ ឬពាក្យសម្ងាត់មិនត្រឹមត្រូវ។"</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"ភ្លេចឈ្មោះអ្នកប្រើ ឬពាក្យសម្ងាត់របស់អ្នក?\nមើល "<b>"google.com/accounts/recovery"</b>" ។"</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"កំពុងពិនិត្យមើលគណនី..."</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"អ្នកបានបញ្ចូលកូដ PIN របស់អ្នកមិនត្រឹមត្រូវចំនួន <xliff:g id="NUMBER_0">%d</xliff:g> ដង។\n\n ព្យាយាមម្ដងទៀតក្នុងរយៈពេល <xliff:g id="NUMBER_1">%d</xliff:g> វិនាទី។"</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"អ្នកបានបញ្ចូលពាក្យសម្ងាត់មិនត្រឹមត្រូវ <xliff:g id="NUMBER_0">%d</xliff:g> ដង។\n\nព្យាយាមម្ដងទៀតក្នុងរយៈពេល <xliff:g id="NUMBER_1">%d</xliff:g> វិនាទី។"</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"អ្នកបានគូរលំនាំដោះសោរបស់អ្នកមិនត្រឹមត្រូវចំនួន <xliff:g id="NUMBER_0">%d</xliff:g> ដង។\n\nព្យាយាមម្ដងទៀតក្នុងរយៈពេល <xliff:g id="NUMBER_1">%d</xliff:g> វិនាទី។"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"អ្នកបានព្យាយាមដោះសោកុំព្យូទ័របន្ទះមិនត្រឹមត្រូវចំនួន <xliff:g id="NUMBER_0">%d</xliff:g> ដង។ បន្ទាប់ពីការព្យាយាមមិនជោគជ័យច្រើនជាង <xliff:g id="NUMBER_1">%d</xliff:g> ដង កុំព្យូទ័របន្ទះនឹងត្រូវបានកំណត់ទៅលំនាំដើមដូចចេញពីរោងចក្រ ហើយទិន្នន័យអ្នកប្រើនឹងបាត់បង់។"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"អ្នកបានព្យាយាមដោះសោទូរទស្សន៍របស់អ្នកមិនត្រឹមត្រូវ <xliff:g id="NUMBER_0">%d</xliff:g> ដង។ បន្ទាប់ពីការព្យាយាមបរាជ័យ <xliff:g id="NUMBER_1">%d</xliff:g> ដងទៀត ទូរទស្សន៍របស់អ្នកនឹងត្រូវបានកំណត់ឡើងវិញទាំងស្រុងតាមលំនាំដើម ហើយទិន្នន័យអ្នកប្រើទាំងអស់នឹងត្រូវបាត់បង់។"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"អ្នកបានព្យាយាមដោះសោទូរស័ព្ទមិនត្រឹមត្រូវចំនួន <xliff:g id="NUMBER_0">%d</xliff:g> ដង។ បន្ទាប់ពីការព្យាយាមមិនជោគជ័យច្រើនជាង <xliff:g id="NUMBER_1">%d</xliff:g> ដង ទូរស័ព្ទនឹងត្រូវបានកំណត់ទៅលំនាំដើមដូចចេញពីរោងចក្រ ហើយទិន្នន័យអ្នកប្រើនឹងបាត់បង់។"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"អ្នកបានបញ្ចូលកូដ PIN របស់អ្នកមិនត្រឹមត្រូវចំនួន <xliff:g id="NUMBER_0">%1$d</xliff:g> ដង។\n\n ព្យាយាមម្ដងទៀតក្នុងរយៈពេល <xliff:g id="NUMBER_1">%2$d</xliff:g> វិនាទី។"</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"អ្នកបានបញ្ចូលពាក្យសម្ងាត់មិនត្រឹមត្រូវ <xliff:g id="NUMBER_0">%1$d</xliff:g> ដង។\n\nព្យាយាមម្ដងទៀតក្នុងរយៈពេល <xliff:g id="NUMBER_1">%2$d</xliff:g> វិនាទី។"</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"អ្នកបានគូរលំនាំដោះសោរបស់អ្នកមិនត្រឹមត្រូវចំនួន <xliff:g id="NUMBER_0">%1$d</xliff:g> ដង។\n\nព្យាយាមម្ដងទៀតក្នុងរយៈពេល <xliff:g id="NUMBER_1">%2$d</xliff:g> វិនាទី។"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"អ្នកបានព្យាយាមដោះសោកុំព្យូទ័របន្ទះមិនត្រឹមត្រូវចំនួន <xliff:g id="NUMBER_0">%1$d</xliff:g> ដង។ បន្ទាប់ពីការព្យាយាមមិនជោគជ័យច្រើនជាង <xliff:g id="NUMBER_1">%2$d</xliff:g> ដង កុំព្យូទ័របន្ទះនឹងត្រូវបានកំណត់ទៅលំនាំដើមដូចចេញពីរោងចក្រ ហើយទិន្នន័យអ្នកប្រើនឹងបាត់បង់។"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"អ្នកបានព្យាយាមដោះសោទូរទស្សន៍របស់អ្នកមិនត្រឹមត្រូវ <xliff:g id="NUMBER_0">%1$d</xliff:g> ដង។ បន្ទាប់ពីការព្យាយាមបរាជ័យ <xliff:g id="NUMBER_1">%2$d</xliff:g> ដងទៀត ទូរទស្សន៍របស់អ្នកនឹងត្រូវបានកំណត់ឡើងវិញទាំងស្រុងតាមលំនាំដើម ហើយទិន្នន័យអ្នកប្រើទាំងអស់នឹងត្រូវបាត់បង់។"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"អ្នកបានព្យាយាមដោះសោទូរស័ព្ទមិនត្រឹមត្រូវចំនួន <xliff:g id="NUMBER_0">%1$d</xliff:g> ដង។ បន្ទាប់ពីការព្យាយាមមិនជោគជ័យច្រើនជាង <xliff:g id="NUMBER_1">%2$d</xliff:g> ដង ទូរស័ព្ទនឹងត្រូវបានកំណត់ទៅលំនាំដើមដូចចេញពីរោងចក្រ ហើយទិន្នន័យអ្នកប្រើនឹងបាត់បង់។"</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"អ្នកបានព្យាយាមដោះសោកុំព្យូទ័របន្ទះមិនត្រឹមត្រូវចំនួន <xliff:g id="NUMBER">%d</xliff:g> ដង។ កុំព្យូទ័របន្ទះនឹងត្រូវបានកំណត់ទៅលំនាំដើមដូចចេញពីរោងចក្រ"</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"អ្នកបានព្យាយាមដោះសោទូរទស្សន៍របស់អ្នកមិនត្រឹមត្រូវ <xliff:g id="NUMBER">%d</xliff:g> ដង។ ទូរទស្សន៍របស់អ្នកនឹងត្រូវបានកំណត់ឡើងវិញទាំងស្រុងតាមលំនាំដើម។"</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"អ្នកបានព្យាយាមដោះសោទូរស័ព្ទមិនត្រឹមត្រូវចំនួន <xliff:g id="NUMBER">%d</xliff:g> ដង។ ឥឡូវទូរស័ព្ទនឹងកំណត់ទៅលំនាំដើមដូចចេញពីរោងចក្រ។"</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"អ្នកបានគូរលំនាំដោះសោមិនត្រឹមត្រូវ <xliff:g id="NUMBER_0">%d</xliff:g> ដង។ បន្ទាប់ពីការព្យាយាម <xliff:g id="NUMBER_1">%d</xliff:g> ដងមិនជោគជ័យ អ្នកនឹងត្រូវបានស្នើឲ្យដោះសោកុំព្យូទ័របន្ទះរបស់អ្នក ដោយប្រើគណនីអ៊ីមែល។\n\n ព្យាយាមម្ដងទៀតក្នុងរយៈពេល <xliff:g id="NUMBER_2">%d</xliff:g> វិនាទី។"</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"អ្នកបានគូសលំនាំដោះសោរបស់អ្នកមិនត្រឹមត្រូវ <xliff:g id="NUMBER_0">%d</xliff:g> ដង។ បន្ទាប់ពីការព្យាយាមបរាជ័យ <xliff:g id="NUMBER_1">%d</xliff:g> ដងទៀត អ្នកនឹងត្រូវបានសួរឲ្យដោះសោទូរទស្សន៍របស់អ្នកដោយប្រើគណនីអ៊ីម៉ែល។\n\n ព្យាយាមម្តងទៀតក្នុងរយៈពេល <xliff:g id="NUMBER_2">%d</xliff:g> វិនាទី។"</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"អ្នកបានគូរលំនាំដោះសោរបស់អ្នកមិនត្រឹមត្រូវចំនួន <xliff:g id="NUMBER_0">%d</xliff:g> ដង។ បន្ទាប់ពីការព្យាយាមមិនជោគជ័យច្រើនជាង <xliff:g id="NUMBER_1">%d</xliff:g> ដង អ្នកនឹងត្រូវបានស្នើឲ្យដោះសោទូរស័ព្ទរបស់អ្នកដោយប្រើគណនីអ៊ីមែល។\n\n ព្យាយាមម្ដងទៀតក្នុងរយៈពេល <xliff:g id="NUMBER_2">%d</xliff:g> វិនាទី។"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"អ្នកបានគូរលំនាំដោះសោមិនត្រឹមត្រូវ <xliff:g id="NUMBER_0">%1$d</xliff:g> ដង។ បន្ទាប់ពីការព្យាយាម <xliff:g id="NUMBER_1">%2$d</xliff:g> ដងមិនជោគជ័យ អ្នកនឹងត្រូវបានស្នើឲ្យដោះសោកុំព្យូទ័របន្ទះរបស់អ្នក ដោយប្រើគណនីអ៊ីមែល។\n\n ព្យាយាមម្ដងទៀតក្នុងរយៈពេល <xliff:g id="NUMBER_2">%3$d</xliff:g> វិនាទី។"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"អ្នកបានគូសលំនាំដោះសោរបស់អ្នកមិនត្រឹមត្រូវ <xliff:g id="NUMBER_0">%1$d</xliff:g> ដង។ បន្ទាប់ពីការព្យាយាមបរាជ័យ <xliff:g id="NUMBER_1">%2$d</xliff:g> ដងទៀត អ្នកនឹងត្រូវបានសួរឲ្យដោះសោទូរទស្សន៍របស់អ្នកដោយប្រើគណនីអ៊ីម៉ែល។\n\n ព្យាយាមម្តងទៀតក្នុងរយៈពេល <xliff:g id="NUMBER_2">%3$d</xliff:g> វិនាទី។"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"អ្នកបានគូរលំនាំដោះសោរបស់អ្នកមិនត្រឹមត្រូវចំនួន <xliff:g id="NUMBER_0">%1$d</xliff:g> ដង។ បន្ទាប់ពីការព្យាយាមមិនជោគជ័យច្រើនជាង <xliff:g id="NUMBER_1">%2$d</xliff:g> ដង អ្នកនឹងត្រូវបានស្នើឲ្យដោះសោទូរស័ព្ទរបស់អ្នកដោយប្រើគណនីអ៊ីមែល។\n\n ព្យាយាមម្ដងទៀតក្នុងរយៈពេល <xliff:g id="NUMBER_2">%3$d</xliff:g> វិនាទី។"</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"លុបចេញ"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"បង្កើនកម្រិតសំឡេងលើសពីកម្រិតបានផ្ដល់យោបល់?\n\nការស្ដាប់នៅកម្រិតសំឡេងខ្លាំងយូរអាចធ្វើឲ្យខូចត្រចៀក។"</string>
@@ -1472,6 +1470,7 @@
<item quantity="one">អស់រយៈពេល 1 ម៉ោង</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"រហូតដល់ <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"រហូតដល់ <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (ម៉ោងរោទិ៍បន្ទាប់)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"រហូតដល់ពេលអ្នកបិទវា"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"រហូតទាល់តែអ្នកបិទ កុំរំខាន"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-kn-rIN-watch/strings.xml b/core/res/res/values-kn-rIN-watch/strings.xml
index def2130..4738ad5 100644
--- a/core/res/res/values-kn-rIN-watch/strings.xml
+++ b/core/res/res/values-kn-rIN-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_1">%2$d</xliff:g> ರಲ್ಲಿ <xliff:g id="NUMBER_0">%1$d</xliff:g> ಅಪ್ಲಿಕೇಶನ್."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"ಸೆನ್ಸರ್ಗಳು"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"ನಿಮ್ಮ ಸಂಪರ್ಕಗಳನ್ನು ಪ್ರವೇಶಿಸಿ"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"ಈ ಗಡಿಯಾರದ ಸ್ಥಳವನ್ನು ಪ್ರವೇಶಿಸಿ"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"ನಿಮ್ಮ ಕ್ಯಾಲೆಂಡರ್ ಪ್ರವೇಶಿಸಿ"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"SMS ಸಂದೇಶಗಳನ್ನು ಕಳುಹಿಸಿ ಮತ್ತು ವೀಕ್ಷಿಸಿ"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"ನಿಮ್ಮ ಗಡಿಯಾರದಲ್ಲಿನ ಫೋಟೋಗಳು, ಮಾಧ್ಯಮ ಮತ್ತು ಫೈಲ್ಗಳನ್ನು ಪ್ರವೇಶಿಸಿ"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"ಆಡಿಯೊ ರೆಕಾರ್ಡ್ ಮಾಡಿ"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"ಚಿತ್ರಗಳನ್ನು ತೆಗೆಯಿರಿ ಹಾಗೂ ವೀಡಿಯೊ ರೆಕಾರ್ಡ್ ಮಾಡಿ"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"ಫೋನ್ ಕರೆಗಳನ್ನು ಮಾಡಿ ಹಾಗೂ ನಿರ್ವಹಿಸಿ"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"ನಿಮ್ಮ ಮುಖ್ಯ ಲಕ್ಷಣಗಳ ಕುರಿತು ಸೆನ್ಸರ್ ಡೇಟಾವನ್ನು ಪ್ರವೇಶಿಸಿ"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"ಸ್ಥಿತಿ ಪಟ್ಟಿಯಾಗಿರಲು"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"ದೇಹ ಸೆನ್ಸರ್ಗಳನ್ನು ಪ್ರವೇಶಿಸಿ (ಹೃದಯದ ಬಡಿತ ಮಾನಿಟರ್ಗಳಂತಹ)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"ನಿಖರ ಸ್ಥಳವನ್ನು ಪ್ರವೇಶಿಸಿ (GPS ಮತ್ತು ನೆಟ್ವರ್ಕ್-ಆಧಾರಿತ)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"ಅಂದಾಜು ಸ್ಥಳವನ್ನು ಪ್ರವೇಶಿಸಿ (ನೆಟ್ವರ್ಕ್-ಆಧಾರಿತ)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"ಸಿಮ್ಗೆ ಆಜ್ಞೆಗಳನ್ನು ಕಳುಹಿಸಿ"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"ಪೂರ್ಣ ನೆಟ್ವರ್ಕ್ ಪ್ರವೇಶವನ್ನು ಹೊಂದಿರಿ"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"ಪ್ರೊಫೈಲ್ ಮತ್ತು ಸಾಧನ ಮಾಲೀಕರನ್ನು ನಿರ್ವಹಿಸಿ"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"WiMAX ಸ್ಥಿತಿಯನ್ನು ಬದಲಿಸಿ"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"Android ಬೀಮ್ ವರ್ಗಾವಣೆ ಸ್ಥಿತಿ ಸ್ವೀಕರಿಸಿ"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"ಮಾಧ್ಯಮ ಔಟ್ಪುಟ್ ಅನ್ನು ರೂಟ್ ಮಾಡಿ"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"ಸ್ಥಾಪನೆ ಸೆಷನ್ಗಳನ್ನು ಓದಿ"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"ಸ್ಥಾಪನೆ ಪ್ಯಾಕೇಜ್ಗಳನ್ನು ವಿನಂತಿಸಿ"</string>
</resources>
diff --git a/core/res/res/values-kn-rIN/strings.xml b/core/res/res/values-kn-rIN/strings.xml
index 0e98430..f3ff668 100644
--- a/core/res/res/values-kn-rIN/strings.xml
+++ b/core/res/res/values-kn-rIN/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ಕ್ರೆಡಿಟ್ ಕಾರ್ಡ್ ಸಂಖ್ಯೆಗಳು ಮತ್ತು ಪಾಸ್ವರ್ಡ್ಗಳಂತಹ ವೈಯಕ್ತಿಕ ಡೇಟಾವನ್ನು ಒಳಗೊಂಡಿರುತ್ತದೆ."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"ಸ್ಥಿತಿ ಪಟ್ಟಿಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ ಇಲ್ಲವೇ ಮಾರ್ಪಡಿಸಿ"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"ಸ್ಥಿತಿ ಪಟ್ಟಿಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲು ಅಥವಾ ಸೇರಿಸಲು ಮತ್ತು ಸಿಸ್ಟಂ ಐಕಾನ್ಗಳನ್ನು ತೆಗೆದುಹಾಕಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅವಕಾಶ ನೀಡುತ್ತದೆ."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"ಸ್ಥಿತಿ ಪಟ್ಟಿ"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"ಸ್ಥಿತಿ ಪಟ್ಟಿಯಾಗಿರಲು"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಸ್ಥಿತಿ ಪಟ್ಟಿಯಾಗಿ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"ಸ್ಥಿತಿ ಪಟ್ಟಿಯನ್ನು ವಿಸ್ತರಿಸಿ/ಸಂಕುಚಿಸಿ"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"ಸ್ಥಿತಿ ಪಟ್ಟಿಯನ್ನು ವಿಸ್ತರಿಸಲು ಅಥವಾ ಸಂಕುಚಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"WAP ಸಂದೇಶಗಳನ್ನು ಸ್ವೀಕರಿಸಲು ಮತ್ತು ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅವಕಾಶ ಮಾಡಿಕೊಡುತ್ತದೆ. ಈ ಅನುಮತಿಯು, ನಿಮಗೆ ಕಳುಹಿಸಲಾಗಿರುವ ಸಂದೇಶಗಳನ್ನು ನಿಮಗೆ ತೋರಿಸದೆಯೇ, ಅವುಗಳನ್ನು ಮಾನಿಟರ್ ಮಾಡುವ ಅಥವಾ ಅಳಿಸುವ ಸಾಮರ್ಥ್ಯವನ್ನು ಒಳಗೊಂಡಿರುತ್ತದೆ."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"ರನ್ ಆಗುತ್ತಿರುವ ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಹಿಂಪಡೆಯಿರಿ"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"ಪ್ರಸ್ತುತವಿರುವ ಮತ್ತು ಇತ್ತೀಚಿಗೆ ಚಾಲ್ತಿಯಾಗಿರುವ ಕಾರ್ಯಗಳ ಕುರಿತ ಮಾಹಿತಿಯನ್ನು ಮರುಪಡೆದುಕೊಳ್ಳಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅವಕಾಶ ಮಾಡಿಕೊಡುತ್ತದೆ. ಇದು ಸಾಧನದಲ್ಲಿ ಯಾವ ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಬಳಸಲಾಗಿದೆ ಎಂಬುದರ ಕುರಿತ ಮಾಹಿತಿಯನ್ನು ಅನ್ವೇಷಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸಬಹುದು."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"ಪ್ರೊಫೈಲ್ ಮತ್ತು ಸಾಧನ ಮಾಲೀಕರನ್ನು ನಿರ್ವಹಿಸಿ"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"ಪ್ರೊಫೈಲ್ ಮತ್ತು ಸಾಧನ ಮಾಲೀಕರನ್ನು ನಿರ್ವಹಿಸಿ"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"ಪ್ರೊಫೈಲ್ ಮಾಲೀಕರು ಮತ್ತು ಸಾಧನ ಮಾಲೀಕರನ್ನು ಹೊಂದಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"ರನ್ ಆಗುತ್ತಿರುವ ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಮರುಕ್ರಮಗೊಳಿಸಿ"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"ಮುನ್ನೆಲೆ ಮತ್ತು ಹಿನ್ನಲೆಗೆ ಕಾರ್ಯಗಳನ್ನು ಸರಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ. ನಿಮ್ಮ ಇನ್ಪುಟ್ ಇಲ್ಲದೆಯೇ, ಅಪ್ಲಿಕೇಶನ್ ಈ ಕಾರ್ಯವನ್ನು ಮಾಡಬಹುದು."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"ಒಳಬರುವ ಮತ್ತು ಹೊರಹೋಗುವ ಕರೆಗಳ ಕುರಿತ ಡೇಟಾ ಸೇರಿದಂತೆ, ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್ನ ಕರೆಯ ಲಾಗ್ ಅನ್ನು ಮಾರ್ಪಡಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅವಕಾಶ ನೀಡುತ್ತದೆ. ದುರುದ್ದೇಶಪೂರಿತ ಅಪ್ಲಿಕೇಶನ್ಗಳು ನಿಮ್ಮ ಕರೆಯ ಲಾಗ್ ಅನ್ನು ಅಳಿಸಲು ಅಥವಾ ಮಾರ್ಪಡಿಸಲು ಇದನ್ನು ಬಳಸಿಕೊಳ್ಳಬಹುದು."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"ಒಳಬರುವ ಮತ್ತು ಹೊರಹೋಗುವ ಕರೆಗಳ ಕುರಿತು ಡೇಟಾ ಸೇರಿದಂತೆ ನಿಮ್ಮ ಟಿವಿಯ ಕರೆಯ ಲಾಗ್ ಅನ್ನು ಮಾರ್ಪಡಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ. ದುರುದ್ದೇಶಪೂರಿತ ಅಪ್ಲಿಕೇಶನ್ಗಳು ನಿಮ್ಮ ಕರೆಯ ಲಾಗ್ ಅನ್ನು ಅಳಿಸಲು ಅಥವಾ ಮಾರ್ಪಡಿಸಲು ಇದನ್ನು ಬಳಸಿಕೊಳ್ಳಬಹುದು."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"ಒಳಬರುವ ಮತ್ತು ಹೊರಹೋಗುವ ಕರೆಗಳ ಕುರಿತ ಡೇಟಾ ಸೇರಿದಂತೆ, ನಿಮ್ಮ ಫೋನ್ನ ಕರೆಯ ಲಾಗ್ ಅನ್ನು ಮಾರ್ಪಡಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅವಕಾಶ ನೀಡುತ್ತದೆ. ದುರುದ್ದೇಶಪೂರಿತ ಅಪ್ಲಿಕೇಶನ್ಗಳು ನಿಮ್ಮ ಕರೆಯ ಲಾಗ್ ಅನ್ನು ಅಳಿಸಲು ಅಥವಾ ಮಾರ್ಪಡಿಸಲು ಇದನ್ನು ಬಳಸಿಕೊಳ್ಳಬಹುದು."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"ದೇಹದ ಸಂವೇದಗಳು (ಹೃದಯದ ರೇಟ್ ಮಾನಿಟರ್ಗಳಂತಹ)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"ದೇಹ ಸೆನ್ಸರ್ಗಳನ್ನು ಪ್ರವೇಶಿಸಿ (ಹೃದಯದ ಬಡಿತ ಮಾನಿಟರ್ಗಳಂತಹ)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"ನಿಮ್ಮ ಹೃದಯ ಬಡಿತದಂತಹ ನಿಮ್ಮ ದೈಹಿಕ ಸ್ಥಿತಿಯನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡುವ ಸೆನ್ಸರ್ಗಳಿಂದ ಡೇಟಾ ಪ್ರವೇಶಿಸಲು ಅಪ್ಲಿಕೇಶನ್ ಅವಕಾಶ ಮಾಡಿಕೊಡುತ್ತದೆ."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"ಕ್ಯಾಲೆಂಡರ್ ಈವೆಂಟ್ಗಳು ಅಲ್ಲದೇ ಗೌಪ್ಯತೆ ಮಾಹಿತಿಯನ್ನು ಓದಿರಿ"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್ನಲ್ಲಿ ಸಂಗ್ರಹಿಸಲಾಗಿರುವ ಸ್ನೇಹಿತರ ಅಥವಾ ಸಹೋದ್ಯೋಗಿಗಳ ಕ್ಯಾಲೆಂಡರ್ ಈವೆಂಟ್ಗಳೂ ಸೇರಿದಂತೆ, ಎಲ್ಲಾ ಕ್ಯಾಲೆಂಡರ್ ಈವೆಂಟ್ಗಳನ್ನು ರೀಡ್ ಮಾಡಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅವಕಾಶ ಕಲ್ಪಿಸಿಕೊಡುತ್ತದೆ. ಇದು ಗೌಪ್ಯತೆ ಮತ್ತು ಸೂಕ್ಷ್ಮತೆಯನ್ನು ಲೆಕ್ಕಿಸದೆಯೇ, ನಿಮ್ಮ ಕ್ಯಾಲೆಂಡರ್ ಡೇಟಾವನ್ನು ಹಂಚಿಕೊಳ್ಳಲು ಮತ್ತು ಉಳಿಸಿಕೊಳ್ಳಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅವಕಾಶ ಕಲ್ಪಿಸಬಹುದು."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"ನಿಮ್ಮ ಫೋನ್ನಲ್ಲಿ ನಿಮ್ಮ ಸ್ನೇಹಿತರು ಅಥವಾ ಸಹೋದ್ಯೋಗಿಗಳ ಈವೆಂಟ್ಗಳೂ ಸೇರಿದಂತೆ, ನೀವು ಮಾರ್ಪಡಿಸಬಹುದಾದ ಈವೆಂಟ್ಗಳನ್ನು ಸೇರಿಸಲು, ತೆಗೆದುಹಾಕಲು, ಬದಲಾಯಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅವಕಾಶ ಮಾಡಿಕೊಡುತ್ತದೆ. ಇದು ಕ್ಯಾಲೆಂಡರ್ ಮಾಲೀಕರಿಂದ ಬಂದಿರುವಂತೆ ಗೋಚರಿಸುವ ಸಂದೇಶಗಳನ್ನು ಕಳುಹಿಸಲು ಇಲ್ಲವೇ ಮಾಲೀಕರ ಗಮನಕ್ಕೆ ತರದೆಯೇ, ಈವೆಂಟ್ಗಳನ್ನು ಮಾರ್ಪಡಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸಬಹುದು."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"ಹೆಚ್ಚುವರಿ ಸ್ಥಾನ ಪೂರೈಕೆದಾರರ ಆದೇಶಗಳನ್ನು ಪ್ರವೇಶಿಸಿ"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"ಹೆಚ್ಚಿನ ಸ್ಥಾನ ಪೂರೈಕೆದಾರ ಆದೇಶಗಳನ್ನು ಪ್ರವೇಶಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅವಕಾಶ ನೀಡುತ್ತದೆ. ಇದು GPS ಅಥವಾ ಇತರ ಸ್ಥಾನ ಮೂಲಗಳ ಕಾರ್ಯಾಚರಣೆಯಲ್ಲಿ ಮಧ್ಯ ಪ್ರವೇಶಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸಬಹುದು."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"ನಿಖರ ಸ್ಥಳ (GPS ಮತ್ತು ನೆಟ್ವರ್ಕ್-ಆಧಾರಿತ)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"ನಿಖರ ಸ್ಥಳವನ್ನು ಪ್ರವೇಶಿಸಿ (GPS ಮತ್ತು ನೆಟ್ವರ್ಕ್-ಆಧಾರಿತ)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"ಗ್ಲೊಬಲ್ ಪೊಸಿಷನಿಂಗ್ ಸಿಸ್ಟಮ್ (GPS) ಅಥವಾ ಸೆಲ್ ಟವರ್ಗಳು ಮತ್ತು Wi-Fi ನಂತಹ ನೆಟ್ವರ್ಕ್ ಸ್ಥಾನ ಮೂಲಗಳನ್ನು ಬಳಸಿಕೊಂಡು ನಿಮ್ಮ ನಿಖರವಾದ ಸ್ಥಾನವನ್ನು ಪಡೆಯಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ. ಅಪ್ಲಿಕೇಶನ್ಗಾಗಿ ಅವುಗಳನ್ನು ಬಳಸಲು ಈ ಸ್ಥಾನ ಸೇವೆಗಳು ಆನ್ ಆಗಿರಬೇಕು ಮತ್ತು ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿ ಲಭ್ಯವಿರಬೇಕು. ನೀವೆಲ್ಲಿರುವಿರಿ ಎಂಬುದನ್ನು ನಿರ್ಧರಿಸಲು ಅಪ್ಲಿಕೇಶನ್ ಇದನ್ನು ಬಳಸಬಹುದು ಮತ್ತು ಹೆಚ್ಚುವರಿ ಬ್ಯಾಟರಿ ಶಕ್ತಿಯನ್ನು ಬಳಸಬಹುದು."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"ಅಂದಾಜು ಸ್ಥಳ (ನೆಟ್ವರ್ಕ್-ಆಧಾರಿತ)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"ಅಂದಾಜು ಸ್ಥಳವನ್ನು ಪ್ರವೇಶಿಸಿ (ನೆಟ್ವರ್ಕ್-ಆಧಾರಿತ)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"ನಿಮ್ಮ ಅಂದಾಜು ಸ್ಥಳವನ್ನು ಪಡೆದುಕೊಳ್ಳಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ. ಈ ಸ್ಥಳವನ್ನು ಸೆಲ್ ಟವರ್ಗಳು ಮತ್ತು Wi-Fi ನಂತಹ ನೆಟ್ವರ್ಕ್ ಸ್ಥಾನದ ಮೂಲಗಳನ್ನು ಬಳಸಿಕೊಂಡು ಸ್ಥಳದ ಸೇವೆಗಳ ಮೂಲಕ ಪಡೆಯಲಾಗಿದೆ. ಅಪ್ಲಿಕೇಶನ್ಗಾಗಿ ಅವುಗಳನ್ನು ಬಳಸಲು ಈ ಸ್ಥಾನ ಸೇವೆಗಳನ್ನು ಆನ್ ಮಾಡಿರಬೇಕು ಮತ್ತು ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿ ಲಭ್ಯವಿರಬೇಕು. ನೀವು ನಿಖರವಾಗಿ ಎಲ್ಲಿರುವಿರಿ ಎಂಬುದನ್ನು ನಿರ್ಧರಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗಳು ಇದನ್ನು ಬಳಸಬಹುದು."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"ನಿಮ್ಮ ಆಡಿಯೊ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಬದಲಾಯಿಸಿ"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"ವಾಲ್ಯೂಮ್ ರೀತಿಯ ಮತ್ತು ಔಟ್ಪುಟ್ಗಾಗಿ ಯಾವ ಸ್ಪೀಕರ್ ಬಳಸಬೇಕು ಎಂಬ ರೀತಿಯ ಜಾಗತಿಕ ಆಡಿಯೊ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಮಾರ್ಪಡಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅವಕಾಶ ಮಾಡಿಕೊಡುತ್ತದೆ."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"ಆಡಿಯೊ ರೆಕಾರ್ಡ್ ಮಾಡಿ"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"ಮೈಕ್ರೋಫೋನ್ ಮೂಲಕ ಆಡಿಯೊ ರೆಕಾರ್ಡ್ ಮಾಡಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅವಕಾಶ ನೀಡುತ್ತದೆ. ಈ ಅನುಮತಿಯು ನಿಮ್ಮ ಖಾತರಿ ಇಲ್ಲದೆಯೇ, ಯಾವುದೇ ಸಮಯದಲ್ಲಿ ಆಡಿಯೊ ರೆಕಾರ್ಡ್ ಮಾಡಿಕೊಳ್ಳಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"ಸಿಮ್ ಸಂವಹನ"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"ಸಿಮ್ಗೆ ಆಜ್ಞೆಗಳನ್ನು ಕಳುಹಿಸಿ"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"ಸಿಮ್ ಗೆ ಆದೇಶಗಳನ್ನು ಕಳುಹಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ. ಇದು ತುಂಬಾ ಅಪಾಯಕಾರಿ."</string>
<string name="permlab_camera" msgid="3616391919559751192">"ಚಿತ್ರಗಳು ಮತ್ತು ವೀಡಿಯೊಗಳನ್ನು ಸೆರೆಹಿಡಿಯಿರಿ"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"ಕ್ಯಾಮರಾ ಮೂಲಕ ಚಿತ್ರಗಳು ಮತ್ತು ವೀಡಿಯೊಗಳನ್ನು ಸೆರೆಹಿಡಿಯಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅವಕಾಶ ಮಾಡಿಕೊಡುತ್ತದೆ. ಈ ಅನುಮತಿಯು ನಿಮ್ಮ ಖಾತರಿ ಇಲ್ಲದೆಯೇ ಯಾವುದೇ ಸಮಯದಲ್ಲಿ ಕ್ಯಾಮರಾವನ್ನು ಬಳಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"ಫೋನ್ನ ಮೂಲಕ ತಿಳಿದಿರುವ ಖಾತೆಗಳ ಪಟ್ಟಿಯನ್ನು ಪಡೆದುಕೊಳ್ಳಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅವಕಾಶ ಮಾಡಿಕೊಡುತ್ತದೆ. ಇದು ನೀವು ಸ್ಥಾಪಿಸಿರುವ ಅಪ್ಲಿಕೇಶನ್ ಮೂಲಕ ರಚಿಸಲಾದ ಯಾವುದೇ ಖಾತೆಯನ್ನು ಒಳಗೊಂಡಿರಬಹುದು."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"ನೆಟ್ವರ್ಕ್ ಸಂಪರ್ಕಗಳನ್ನು ವೀಕ್ಷಿಸಿ"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ಮತ್ತು ಸಂಪರ್ಕಗೊಂಡಿರುವ ಸಂಪರ್ಕಗಳಂತಹ ನೆಟ್ವರ್ಕ್ ಸಂಪರ್ಕಗಳ ಕುರಿತ ಮಾಹಿತಿಯನ್ನು ವೀಕ್ಷಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"ಪೂರ್ಣ ನೆಟ್ವರ್ಕ್ ಪ್ರವೇಶ"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"ಪೂರ್ಣ ನೆಟ್ವರ್ಕ್ ಪ್ರವೇಶವನ್ನು ಹೊಂದಿರಿ"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"ನೆಟ್ವರ್ಕ್ ಸಾಕೆಟ್ಗಳನ್ನು ರಚಿಸಲು ಮತ್ತು ಕಸ್ಟಮ್ ನೆಟ್ವರ್ಕ್ ಪ್ರೊಟೋಕಾಲ್ಗಳನ್ನು ಬಳಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅವಕಾಶ ಮಾಡಿಕೊಡುತ್ತದೆ. ಬ್ರೌಸರ್ ಮತ್ತು ಇತರ ಅಪ್ಲಿಕೇಶನ್ಗಳು ಇಂಟರ್ನೆಟ್ಗೆ ಡೇಟಾ ಕಳುಹಿಸಲು ಮಾರ್ಗವನ್ನುಂಟು ಮಾಡುತ್ತದೆ ಹಾಗಾಗಿ ಇಂಟರ್ನೆಟ್ಗೆ ಡೇಟಾ ಕಳುಹಿಸಲು ಈ ಅನುಮತಿ ಅಗತ್ಯವಿರುವುದಿಲ್ಲ."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"ನೆಟ್ವರ್ಕ್ ಸಂಪರ್ಕತೆಯನ್ನು ಬದಲಾಯಿಸಿ"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"ನೆಟ್ವರ್ಕ್ ಸಂಪರ್ಕದ ಸ್ಥಿತಿಯನ್ನು ಬದಲಾಯಿಸಲು ಅಪ್ಲಿಕೇಶನ್ ಅನುಮತಿಸುತ್ತದೆ."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"ಸ್ಥಳೀಯ ಬ್ಲೂಟೂತ್ ಫೋನ್ ಕಾನ್ಫಿಗರ್ ಮಾಡಲು ಮತ್ತು ಅನ್ವೇಷಿಸಲು ಹಾಗೂ ರಿಮೊಟ್ ಸಾಧನಗಳ ಜೊತೆಗೆ ಜೋಡಿ ಮಾಡಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅವಕಾಶ ಮಾಡಿಕೊಡುತ್ತದೆ."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"WiMAX ನಿಂದ ಸಂಪರ್ಕಗೊಳಿಸಿ ಮತ್ತು ಸಂಪರ್ಕ ಕಡಿತಗೊಳಿಸಿ"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"WiMAX ಸಕ್ರಿಯಗೊಂಡಿದೆಯೇ ಮತ್ತು ಸಂಪರ್ಕಗೊಂಡಿರುವಂತಹ WiMAX ನೆಟ್ವರ್ಕ್ಗಳ ಕುರಿತು ಮಾಹಿತಿಯನ್ನು ನಿರ್ಧರಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅವಕಾಶ ನೀಡುತ್ತದೆ."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX ಸ್ಥಿತಿಯನ್ನು ಬದಲಿಸಿ"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"WiMAX ಸ್ಥಿತಿಯನ್ನು ಬದಲಿಸಿ"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಟ್ಯಾಬ್ಲೆಟ್ಗೆ ಸಂಪರ್ಕಪಡಿಸಲು ಮತ್ತು WiMAX ನೆಟ್ವರ್ಕ್ಗಳಿಂದ ಟ್ಯಾಬ್ಲೆಟ್ ಅನ್ನು ಸಂಪರ್ಕ ಕಡಿತಗೊಳಿಸಲು ಅವಕಾಶ ಮಾಡಿಕೊಡುತ್ತದೆ."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"WiMAX ನೆಟ್ವರ್ಕ್ಗಳಿಂದ ಟಿವಿಯನ್ನು ಸಂಪರ್ಕಪಡಿಸಲು ಮತ್ತು ಕಡಿತಗೊಳಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಫೋನ್ಗೆ ಸಂಪರ್ಕಪಡಿಸಲು ಮತ್ತು WiMAX ನೆಟ್ವರ್ಕ್ಗಳಿಂದ ಫೋನ್ ಸಂಪರ್ಕ ಕಡಿತಗೊಳಿಸಲು ಅವಕಾಶ ಮಾಡಿಕೊಡುತ್ತದೆ."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"ಸ್ಪರ್ಶದ ಪರದೆಯ ಮಾಪನಾಂಕ ನಿರ್ಣಯ ಪ್ಯಾರಾಮೀಟರ್ಗಳನ್ನು ಮಾರ್ಪಡಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ. ಸಾಮಾನ್ಯ ಅಪ್ಲಿಕೇಶನ್ಗಳಿಗೆ ಎಂದಿಗೂ ಅಗತ್ಯವಿರುವುದಿಲ್ಲ."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"DRM ಪ್ರಮಾಣಪತ್ರಗಳನ್ನು ಪ್ರವೇಶಿಸಿ"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"DRM ಪ್ರಮಾಣಪತ್ರಗಳಿಗೆ ಅನುಮತಿ ಕಲ್ಪಿಸಲು ಮತ್ತು ಬಳಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ. ಸಾಮಾನ್ಯ ಅಪ್ಲಿಕೇಶನ್ಗಳಿಗೆ ಎಂದಿಗೂ ಅಗತ್ಯವಿರುವುದಿಲ್ಲ."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Android Beam ವರ್ಗಾವಣೆ ಸ್ಥಿತಿಯನ್ನು ಸ್ವೀಕರಿಸಿ"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"Android ಬೀಮ್ ವರ್ಗಾವಣೆ ಸ್ಥಿತಿ ಸ್ವೀಕರಿಸಿ"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"ಪ್ರಸ್ತುತ Android Beam ವರ್ಗಾವಣೆಗಳ ಕುರಿತ ಮಾಹಿತಿಯನ್ನು ಸ್ವೀಕರಿಸಲು ಈ ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸಿ"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"DRM ಪ್ರಮಾಣಪತ್ರಗಳನ್ನು ತೆಗೆದುಹಾಕಿ"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"DRM ಪ್ರಮಾಣಪತ್ರಗಳನ್ನು ತೆಗೆದುಹಾಕಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ. ಸಾಮಾನ್ಯ ಅಪ್ಲಿಕೇಶನ್ಗಳಿಗೆ ಎಂದಿಗೂ ಅಗತ್ಯವಿರುವುದಿಲ್ಲ."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"ತಪ್ಪಾದ ಪಿನ್ ಕೋಡ್."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"ಅನ್ಲಾಕ್ ಮಾಡಲು, ಮೆನು ನಂತರ 0 ಒತ್ತಿರಿ."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"ತುರ್ತು ಸಂಖ್ಯೆ"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"ಸೇವೆ ಇಲ್ಲ."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"ಯಾವುದೇ ಸೇವೆಯಿಲ್ಲ"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"ಪರದೆ ಲಾಕ್ ಆಗಿದೆ."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"ಅನ್ಲಾಕ್ ಮಾಡಲು ಮೆನು ಒತ್ತಿರಿ ಇಲ್ಲವೇ ತುರ್ತು ಕರೆಯನ್ನು ಮಾಡಿ."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"ಅನ್ಲಾಕ್ ಮಾಡಲು ಮೆನು ಒತ್ತಿರಿ."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"ಬಳಕೆದಾರರ ಮಾರ್ಗಸೂಚಿಯನ್ನು ನೋಡಿ ಅಥವಾ ಗ್ರಾಹಕರ ಸಹಾಯ ಕೇಂದ್ರಕ್ಕೆ ಸಂಪರ್ಕಿಸಿ."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"ಸಿಮ್ ಕಾರ್ಡ್ ಲಾಕ್ ಆಗಿದೆ."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"ಸಿಮ್ ಕಾರ್ಡ್ ಅನ್ಲಾಕ್ ಮಾಡಲಾಗುತ್ತಿದೆ…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"ನಿಮ್ಮ ಅನ್ಲಾಕ್ ನಮೂನೆಯನ್ನುನೀವು <xliff:g id="NUMBER_0">%d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಚಿತ್ರಿಸಿರುವಿರಿ. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"ನಿಮ್ಮ ಪಾಸ್ವರ್ಡ್ ಅನ್ನು ನೀವು <xliff:g id="NUMBER_0">%d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಟೈಪ್ ಮಾಡಿರುವಿರಿ. \n\n <xliff:g id="NUMBER_1">%d</xliff:g> ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"ನಿಮ್ಮ ಪಿನ್ ಅನ್ನು ನೀವು <xliff:g id="NUMBER_0">%d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಟೈಪ್ ಮಾಡಿರುವಿರಿ. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"ನಿಮ್ಮ ಅನ್ಲಾಕ್ ನಮೂನೆಯನ್ನು ನೀವು <xliff:g id="NUMBER_0">%d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಚಿತ್ರಿಸಿರುವಿರಿ. <xliff:g id="NUMBER_1">%d</xliff:g> ಕ್ಕಿಂತ ಹೆಚ್ಚು ಬಾರಿ ವಿಫಲ ಪ್ರಯತ್ನಗಳನ್ನು ಮಾಡಿರುವಿರಿ, Google ಸೈನ್ ಇನ್ ಬಳಸಿಕೊಂಡು ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್ ಅನ್ಲಾಕ್ ಮಾಡಲು ನಿಮ್ಮನ್ನು ಕೇಳಲಾಗುತ್ತದೆ.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"<xliff:g id="NUMBER_0">%d</xliff:g> ಬಾರಿ ನಿಮ್ಮ ಅನ್ಲಾಕ್ ನಮೂನೆಯನ್ನು ನೀವು ತಪ್ಪಾಗಿ ಎಳೆದಿರುವಿರಿ. <xliff:g id="NUMBER_1">%d</xliff:g> ಕ್ಕಿಂತ ಹೆಚ್ಚು ವಿಫಲ ಪ್ರಯತ್ನಗಳ ನಂತರ, ನೀವು ಟಿವಿಯನ್ನು Google ಗೆ ಸೈನ್ ಇನ್ ಮಾಡುವ ಮೂಲಕ ಅನ್ಲಾಕ್ ಮಾಡಲು ಕೇಳಲಾಗುತ್ತದೆ.\n\n<xliff:g id="NUMBER_2">%d</xliff:g> ಸೆಕೆಂಡುಗಳ ನಂತರ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"ನಿಮ್ಮ ಅನ್ಲಾಕ್ ನಮೂನೆಯನ್ನು ನೀವು <xliff:g id="NUMBER_0">%d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಚಿತ್ರಿಸಿರುವಿರಿ. <xliff:g id="NUMBER_1">%d</xliff:g> ಕ್ಕಿಂತ ಹೆಚ್ಚು ಬಾರಿ ವಿಫಲ ಪ್ರಯತ್ನಗಳನ್ನು ಮಾಡಿರುವಿರಿ, Google ಸೈನ್ ಇನ್ ಬಳಸಿಕೊಂಡು ನಿಮ್ಮ ಫೋನ್ ಅನ್ಲಾಕ್ ಮಾಡಲು ನಿಮ್ಮನ್ನು ಕೇಳಲಾಗುತ್ತದೆ.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"ಟ್ಯಾಬ್ಲೆಟ್ ಅನ್ಲಾಕ್ ಮಾಡಲು ನೀವು <xliff:g id="NUMBER_0">%d</xliff:g> ಬಾರಿ ಪ್ರಯತ್ನಿಸಿರುವಿರಿ. <xliff:g id="NUMBER_1">%d</xliff:g> ಹೆಚ್ಚಿನ ವಿಫಲ ಪ್ರಯತ್ನಗಳ ನಂತರ, ಟ್ಯಾಬ್ಲೆಟ್ ಅನ್ನು ಫ್ಯಾಕ್ಟರಿ ಡೀಫಾಲ್ಟ್ಗೆ ಮರು ಹೊಂದಿಸಲಾಗುತ್ತದೆ ಮತ್ತು ಎಲ್ಲಾ ಬಳಕೆದಾರ ಡೇಟಾ ಕಳೆದು ಹೋಗುತ್ತದೆ."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"ನೀವು <xliff:g id="NUMBER_0">%d</xliff:g> ಬಾರಿ ಟಿವಿಯನ್ನು ತಪ್ಪಾಗಿ ಅನ್ಲಾಕ್ ಮಾಡಲು ಪ್ರಯತ್ನಿಸಿರುವಿರಿ. <xliff:g id="NUMBER_1">%d</xliff:g> ಕ್ಕಿಂತ ಹೆಚ್ಚು ಪ್ರಯತ್ನಗಳ ನಂತರ, ಟಿವಿಯನ್ನು ಫ್ಯಾಕ್ಟರಿ ಡೀಫಾಲ್ಟ್ಗೆ ಮರು ಹೊಂದಿಸಲಾಗುವುದು ಮತ್ತು ಎಲ್ಲಾ ಬಳಕೆದಾರ ಡೇಟಾ ಕಳೆದು ಹೋಗುತ್ತದೆ."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"ಫೋನ್ ಅನ್ಲಾಕ್ ಮಾಡಲು ನೀವು <xliff:g id="NUMBER_0">%d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಪ್ರಯತ್ನಿಸಿರುವಿರಿ. <xliff:g id="NUMBER_1">%d</xliff:g> ಹೆಚ್ಚಿನ ವಿಫಲ ಪ್ರಯತ್ನಗಳ ನಂತರ, ಫೋನ್ ಅನ್ನು ಫ್ಯಾಕ್ಟರಿ ಡೀಫಾಲ್ಟ್ಗೆ ಮರು ಹೊಂದಿಸಲಾಗುತ್ತದೆ ಮತ್ತು ಎಲ್ಲಾ ಬಳಕೆದಾರರ ಡೇಟಾ ಕಳೆದು ಹೋಗುತ್ತದೆ."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"ನಿಮ್ಮ ಅನ್ಲಾಕ್ ನಮೂನೆಯನ್ನುನೀವು <xliff:g id="NUMBER_0">%1$d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಚಿತ್ರಿಸಿರುವಿರಿ. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"ನಿಮ್ಮ ಪಾಸ್ವರ್ಡ್ ಅನ್ನು ನೀವು <xliff:g id="NUMBER_0">%1$d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಟೈಪ್ ಮಾಡಿರುವಿರಿ. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"ನಿಮ್ಮ ಪಿನ್ ಅನ್ನು ನೀವು <xliff:g id="NUMBER_0">%1$d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಟೈಪ್ ಮಾಡಿರುವಿರಿ. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"ನಿಮ್ಮ ಅನ್ಲಾಕ್ ನಮೂನೆಯನ್ನು ನೀವು <xliff:g id="NUMBER_0">%1$d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಚಿತ್ರಿಸಿರುವಿರಿ. <xliff:g id="NUMBER_1">%2$d</xliff:g> ಕ್ಕಿಂತ ಹೆಚ್ಚು ಬಾರಿ ವಿಫಲ ಪ್ರಯತ್ನಗಳನ್ನು ಮಾಡಿರುವಿರಿ, Google ಸೈನ್ ಇನ್ ಬಳಸಿಕೊಂಡು ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್ ಅನ್ಲಾಕ್ ಮಾಡಲು ನಿಮ್ಮನ್ನು ಕೇಳಲಾಗುತ್ತದೆ.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"<xliff:g id="NUMBER_0">%1$d</xliff:g> ಬಾರಿ ನಿಮ್ಮ ಅನ್ಲಾಕ್ ನಮೂನೆಯನ್ನು ನೀವು ತಪ್ಪಾಗಿ ಎಳೆದಿರುವಿರಿ. <xliff:g id="NUMBER_1">%2$d</xliff:g> ಕ್ಕಿಂತ ಹೆಚ್ಚು ವಿಫಲ ಪ್ರಯತ್ನಗಳ ನಂತರ, ನೀವು ಟಿವಿಯನ್ನು Google ಗೆ ಸೈನ್ ಇನ್ ಮಾಡುವ ಮೂಲಕ ಅನ್ಲಾಕ್ ಮಾಡಲು ಕೇಳಲಾಗುತ್ತದೆ.\n\n<xliff:g id="NUMBER_2">%3$d</xliff:g> ಸೆಕೆಂಡುಗಳ ನಂತರ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"ನಿಮ್ಮ ಅನ್ಲಾಕ್ ನಮೂನೆಯನ್ನು ನೀವು <xliff:g id="NUMBER_0">%1$d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಚಿತ್ರಿಸಿರುವಿರಿ. <xliff:g id="NUMBER_1">%2$d</xliff:g> ಕ್ಕಿಂತ ಹೆಚ್ಚು ಬಾರಿ ವಿಫಲ ಪ್ರಯತ್ನಗಳನ್ನು ಮಾಡಿರುವಿರಿ, Google ಸೈನ್ ಇನ್ ಬಳಸಿಕೊಂಡು ನಿಮ್ಮ ಫೋನ್ ಅನ್ಲಾಕ್ ಮಾಡಲು ನಿಮ್ಮನ್ನು ಕೇಳಲಾಗುತ್ತದೆ.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"ಟ್ಯಾಬ್ಲೆಟ್ ಅನ್ಲಾಕ್ ಮಾಡಲು ನೀವು <xliff:g id="NUMBER_0">%1$d</xliff:g> ಬಾರಿ ಪ್ರಯತ್ನಿಸಿರುವಿರಿ. <xliff:g id="NUMBER_1">%2$d</xliff:g> ಹೆಚ್ಚಿನ ವಿಫಲ ಪ್ರಯತ್ನಗಳ ನಂತರ, ಟ್ಯಾಬ್ಲೆಟ್ ಅನ್ನು ಫ್ಯಾಕ್ಟರಿ ಡೀಫಾಲ್ಟ್ಗೆ ಮರು ಹೊಂದಿಸಲಾಗುತ್ತದೆ ಮತ್ತು ಎಲ್ಲಾ ಬಳಕೆದಾರ ಡೇಟಾ ಕಳೆದು ಹೋಗುತ್ತದೆ."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"ನೀವು <xliff:g id="NUMBER_0">%1$d</xliff:g> ಬಾರಿ ಟಿವಿಯನ್ನು ತಪ್ಪಾಗಿ ಅನ್ಲಾಕ್ ಮಾಡಲು ಪ್ರಯತ್ನಿಸಿರುವಿರಿ. <xliff:g id="NUMBER_1">%2$d</xliff:g> ಕ್ಕಿಂತ ಹೆಚ್ಚು ಪ್ರಯತ್ನಗಳ ನಂತರ, ಟಿವಿಯನ್ನು ಫ್ಯಾಕ್ಟರಿ ಡೀಫಾಲ್ಟ್ಗೆ ಮರು ಹೊಂದಿಸಲಾಗುವುದು ಮತ್ತು ಎಲ್ಲಾ ಬಳಕೆದಾರ ಡೇಟಾ ಕಳೆದು ಹೋಗುತ್ತದೆ."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"ಫೋನ್ ಅನ್ಲಾಕ್ ಮಾಡಲು ನೀವು <xliff:g id="NUMBER_0">%1$d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಪ್ರಯತ್ನಿಸಿರುವಿರಿ. <xliff:g id="NUMBER_1">%2$d</xliff:g> ಹೆಚ್ಚಿನ ವಿಫಲ ಪ್ರಯತ್ನಗಳ ನಂತರ, ಫೋನ್ ಅನ್ನು ಫ್ಯಾಕ್ಟರಿ ಡೀಫಾಲ್ಟ್ಗೆ ಮರು ಹೊಂದಿಸಲಾಗುತ್ತದೆ ಮತ್ತು ಎಲ್ಲಾ ಬಳಕೆದಾರರ ಡೇಟಾ ಕಳೆದು ಹೋಗುತ್ತದೆ."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"ಟ್ಯಾಬ್ಲೆಟ್ ಅನ್ಲಾಕ್ ಮಾಡಲು ನೀವು <xliff:g id="NUMBER">%d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಪ್ರಯತ್ನಿಸಿರುವಿರಿ. ಟ್ಯಾಬ್ಲೆಟ್ ಅನ್ನು ಇದೀಗ ಫ್ಯಾಕ್ಟರಿ ಡೀಫಾಲ್ಟ್ಗೆ ಮರು ಹೊಂದಿಸಲಾಗುತ್ತದೆ."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"ನೀವು <xliff:g id="NUMBER">%d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಟಿವಿಯನ್ನು ಅನ್ಲಾಕ್ ಮಾಡಲು ಪ್ರಯತ್ನಿಸಿರುವಿರಿ. ಟಿವಿಯನ್ನು ಈಗ ಫ್ಯಾಕ್ಟರಿ ಡೀಫಾಲ್ಟ್ಗೆ ಮರು ಹೊಂದಿಸಲಾಗುವುದು."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"ಫೋನ್ ಅನ್ಲಾಕ್ ಮಾಡಲು ನೀವು <xliff:g id="NUMBER">%d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಪ್ರಯತ್ನಿಸಿರುವಿರಿ. ಫೋನ್ ಅನ್ನು ಇದೀಗ ಫ್ಯಾಕ್ಟರಿ ಡೀಫಾಲ್ಟ್ಗೆ ಮರು ಹೊಂದಿಸಲಾಗುತ್ತದೆ."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"ಪದ"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"ಲಿಂಕ್"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"ಸಾಲು"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"ಫ್ಯಾಕ್ಟರಿ ಪರೀಕ್ಷೆ ವಿಫಲವಾಗಿದೆ"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"FACTORY_TEST ಕ್ರಿಯೆಯು /system/app ನಲ್ಲಿ ಸ್ಥಾಪಿಸಲಾಗಿರುವ ಪ್ಯಾಕೇಜ್ಗಳಿಗೆ ಮಾತ್ರ ಬೆಂಬಲಿಸುತ್ತದೆ."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"FACTORY_TEST ಕ್ರಿಯೆಯನ್ನು ಒದಗಿಸುವಂತಹ ಯಾವುದೇ ಪ್ಯಾಕೇಜ್ ಕಂಡುಬಂದಿಲ್ಲ."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"ಸ್ವರೂಪಗೊಳಿಸುವಿಕೆ..."</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"ಸೇರಿಸಲಾಗಿಲ್ಲ"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"ಯಾವುದೇ ಹೊಂದಾಣಿಕೆಯ ಚಟುವಟಿಕೆಗಳು ಕಂಡುಬಂದಿಲ್ಲ."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"ಮೀಡಿಯಾ ಔಟ್ಪುಟ್ ಅನ್ನು ರೂಟ್ ಮಾಡಿ"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"ಮಾಧ್ಯಮ ಔಟ್ಪುಟ್ ಅನ್ನು ರೂಟ್ ಮಾಡಿ"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"ಇತರ ಬಾಹ್ಯ ಸಾಧನಗಳಿಗೆ ಮೀಡಿಯಾ ಔಟ್ಪುಟ್ ಅನ್ನು ರೂಟ್ ಮಾಡಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅವಕಾಶ ನೀಡುತ್ತದೆ."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"ಸ್ಥಾಪನೆ ಸೆಷನ್ಗಳನ್ನು ಓದಿ"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"ಸ್ಥಾಪನೆ ಸೆಶನ್ಗಳನ್ನು ಓದಿ"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"ಸ್ಥಾಪಿತ ಸೆಷನ್ಗಳನ್ನು ಓದಲು ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಅನುಮತಿಸುತ್ತದೆ. ಸಕ್ರಿಯ ಪ್ಯಾಕೇಜ್ ಸ್ಥಾಪನೆಗಳ ಕುರಿತು ವಿವರಣೆಗಳನ್ನು ವೀಕ್ಷಿಸಲು ಇದು ಅನುಮತಿಸುತ್ತದೆ."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"ಪ್ಯಾಕೇಜ್ಗಳ ಸ್ಥಾಪನೆ ವಿನಂತಿಸಿ"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"ಸ್ಥಾಪನೆ ಪ್ಯಾಕೇಜ್ಗಳನ್ನು ವಿನಂತಿಸಿ"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"ಪ್ಯಾಕೇಜ್ಗಳ ಸ್ಥಾಪನೆಯನ್ನು ವಿನಂತಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"ಜೂಮ್ ನಿಯಂತ್ರಿಸಲು ಎರಡು ಬಾರಿ ಸ್ಪರ್ಶಿಸಿ"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"ವಿಜೆಟ್ ಸೇರಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"ಅಮಾನ್ಯ ಬಳಕೆದಾರಹೆಸರು ಅಥವಾ ಪಾಸ್ವರ್ಡ್."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"ನಿಮ್ಮ ಬಳಕೆದಾರಹೆಸರು ಅಥವಾ ಪಾಸ್ವರ್ಡ್ ಮರೆತಿರುವಿರಾ?\n"<b>"google.com/accounts/recovery"</b>" ಗೆ ಭೇಟಿ ನೀಡಿ."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"ಖಾತೆಯನ್ನು ಪರಿಶೀಲಿಸಲಾಗುತ್ತಿದೆ…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"ನಿಮ್ಮ ಪಿನ್ ಅನ್ನು ನೀವು <xliff:g id="NUMBER_0">%d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಟೈಪ್ ಮಾಡಿರುವಿರಿ. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"ನಿಮ್ಮ ಪಾಸ್ವರ್ಡ್ ಅನ್ನು ನೀವು <xliff:g id="NUMBER_0">%d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಟೈಪ್ ಮಾಡಿರುವಿರಿ. \n\n <xliff:g id="NUMBER_1">%d</xliff:g> ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"ನಿಮ್ಮ ಅನ್ಲಾಕ್ ಪ್ಯಾಟರ್ನ್ ಅನ್ನು ನೀವು <xliff:g id="NUMBER_0">%d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಚಿತ್ರಿಸಿರುವಿರಿ. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"ಟ್ಯಾಬ್ಲೆಟ್ ಅನ್ಲಾಕ್ ಮಾಡಲು ನೀವು <xliff:g id="NUMBER_0">%d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಪ್ರಯತ್ನಿಸಿರುವಿರಿ. <xliff:g id="NUMBER_1">%d</xliff:g> ಕ್ಕೂ ಹೆಚ್ಚಿನ ವಿಫಲ ಪ್ರಯತ್ನಗಳ ಬಳಿಕ, ಟ್ಯಾಬ್ಲೆಟ್ ಅನ್ನು ಫ್ಯಾಕ್ಟರಿ ಡೀಫಾಲ್ಟ್ಗೆ ಮರು ಹೊಂದಿಸಲಾಗುತ್ತದೆ ಮತ್ತು ಎಲ್ಲಾ ಬಳಕೆದಾರರ ಡೇಟಾ ಕಳೆದು ಹೋಗುತ್ತದೆ."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"ನೀವು <xliff:g id="NUMBER_0">%d</xliff:g> ಬಾರಿ ಟಿವಿಯನ್ನು ತಪ್ಪಾಗಿ ಅನ್ಲಾಕ್ ಮಾಡಲು ಪ್ರಯತ್ನಿಸಿರುವಿರಿ. <xliff:g id="NUMBER_1">%d</xliff:g> ಕ್ಕಿಂತ ಹೆಚ್ಚು ಪ್ರಯತ್ನಗಳ ನಂತರ, ಟಿವಿಯನ್ನು ಫ್ಯಾಕ್ಟರಿ ಡೀಫಾಲ್ಟ್ಗೆ ಮರು ಹೊಂದಿಸಲಾಗುವುದು ಮತ್ತು ಎಲ್ಲಾ ಬಳಕೆದಾರ ಡೇಟಾ ಕಳೆದು ಹೋಗುತ್ತದೆ."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"ಫೋನ್ ಅನ್ಲಾಕ್ ಮಾಡಲು ನೀವು <xliff:g id="NUMBER_0">%d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಪ್ರಯತ್ನಿಸಿರುವಿರಿ. <xliff:g id="NUMBER_1">%d</xliff:g> ಕ್ಕೂ ಹೆಚ್ಚಿನ ವಿಫಲ ಪ್ರಯತ್ನಗಳ ನಂತರ, ಫೋನ್ ಅನ್ನು ಫ್ಯಾಕ್ಟರಿ ಡೀಫಾಲ್ಟ್ಗೆ ಮರು ಹೊಂದಿಸಲಾಗುತ್ತದೆ ಮತ್ತು ಬಳಕೆದಾರರ ಎಲ್ಲಾ ಡೇಟಾ ಕಳೆದು ಹೋಗುತ್ತದೆ."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"ನಿಮ್ಮ ಪಿನ್ ಅನ್ನು ನೀವು <xliff:g id="NUMBER_0">%1$d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಟೈಪ್ ಮಾಡಿರುವಿರಿ. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"ನಿಮ್ಮ ಪಾಸ್ವರ್ಡ್ ಅನ್ನು ನೀವು <xliff:g id="NUMBER_0">%1$d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಟೈಪ್ ಮಾಡಿರುವಿರಿ. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"ನಿಮ್ಮ ಅನ್ಲಾಕ್ ಪ್ಯಾಟರ್ನ್ ಅನ್ನು ನೀವು <xliff:g id="NUMBER_0">%1$d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಚಿತ್ರಿಸಿರುವಿರಿ. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"ಟ್ಯಾಬ್ಲೆಟ್ ಅನ್ಲಾಕ್ ಮಾಡಲು ನೀವು <xliff:g id="NUMBER_0">%1$d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಪ್ರಯತ್ನಿಸಿರುವಿರಿ. <xliff:g id="NUMBER_1">%2$d</xliff:g> ಕ್ಕೂ ಹೆಚ್ಚಿನ ವಿಫಲ ಪ್ರಯತ್ನಗಳ ಬಳಿಕ, ಟ್ಯಾಬ್ಲೆಟ್ ಅನ್ನು ಫ್ಯಾಕ್ಟರಿ ಡೀಫಾಲ್ಟ್ಗೆ ಮರು ಹೊಂದಿಸಲಾಗುತ್ತದೆ ಮತ್ತು ಎಲ್ಲಾ ಬಳಕೆದಾರರ ಡೇಟಾ ಕಳೆದು ಹೋಗುತ್ತದೆ."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"ನೀವು <xliff:g id="NUMBER_0">%1$d</xliff:g> ಬಾರಿ ಟಿವಿಯನ್ನು ತಪ್ಪಾಗಿ ಅನ್ಲಾಕ್ ಮಾಡಲು ಪ್ರಯತ್ನಿಸಿರುವಿರಿ. <xliff:g id="NUMBER_1">%2$d</xliff:g> ಕ್ಕಿಂತ ಹೆಚ್ಚು ಪ್ರಯತ್ನಗಳ ನಂತರ, ಟಿವಿಯನ್ನು ಫ್ಯಾಕ್ಟರಿ ಡೀಫಾಲ್ಟ್ಗೆ ಮರು ಹೊಂದಿಸಲಾಗುವುದು ಮತ್ತು ಎಲ್ಲಾ ಬಳಕೆದಾರ ಡೇಟಾ ಕಳೆದು ಹೋಗುತ್ತದೆ."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"ಫೋನ್ ಅನ್ಲಾಕ್ ಮಾಡಲು ನೀವು <xliff:g id="NUMBER_0">%1$d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಪ್ರಯತ್ನಿಸಿರುವಿರಿ. <xliff:g id="NUMBER_1">%2$d</xliff:g> ಕ್ಕೂ ಹೆಚ್ಚಿನ ವಿಫಲ ಪ್ರಯತ್ನಗಳ ನಂತರ, ಫೋನ್ ಅನ್ನು ಫ್ಯಾಕ್ಟರಿ ಡೀಫಾಲ್ಟ್ಗೆ ಮರು ಹೊಂದಿಸಲಾಗುತ್ತದೆ ಮತ್ತು ಬಳಕೆದಾರರ ಎಲ್ಲಾ ಡೇಟಾ ಕಳೆದು ಹೋಗುತ್ತದೆ."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"ಟ್ಯಾಬ್ಲೆಟ್ ಅನ್ಲಾಕ್ ಮಾಡಲು ನೀವು <xliff:g id="NUMBER">%d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಪ್ರಯತ್ನಿಸಿರುವಿರಿ. ಟ್ಯಾಬ್ಲೆಟ್ ಅನ್ನು ಇದೀಗ ಫ್ಯಾಕ್ಟರಿ ಡೀಫಾಲ್ಟ್ಗೆ ಮರು ಹೊಂದಿಸಲಾಗುತ್ತದೆ."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"ನೀವು <xliff:g id="NUMBER">%d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಟಿವಿಯನ್ನು ಅನ್ಲಾಕ್ ಮಾಡಲು ಪ್ರಯತ್ನಿಸಿರುವಿರಿ. ಟಿವಿಯನ್ನು ಈಗ ಫ್ಯಾಕ್ಟರಿ ಡೀಫಾಲ್ಟ್ಗೆ ಮರು ಹೊಂದಿಸಲಾಗುವುದು."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"ಫೋನ್ ಅನ್ಲಾಕ್ ಮಾಡಲು ನೀವು <xliff:g id="NUMBER">%d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಪ್ರಯತ್ನಿಸಿರುವಿರಿ. ಫೋನ್ ಅನ್ನು ಇದೀಗ ಫ್ಯಾಕ್ಟರಿ ಢೀಫಾಲ್ಟ್ಗೆ ಮರು ಹೊಂದಿಸಲಾಗುತ್ತದೆ."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"ನಿಮ್ಮ ಅನ್ಲಾಕ್ ಪ್ಯಾಟರ್ನ್ ಅನ್ನು ನೀವು <xliff:g id="NUMBER_0">%d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಡ್ರಾ ಮಾಡಿರುವಿರಿ. <xliff:g id="NUMBER_1">%d</xliff:g> ಹೆಚ್ಚಿನ ವಿಫಲ ಪ್ರಯತ್ನಗಳ ಬಳಿಕ, ನಿಮ್ಮ ಇಮೇಲ್ ಖಾತೆಯನ್ನು ಬಳಸಿಕೊಂಡು ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್ ಅನ್ಲಾಕ್ ಮಾಡುವಂತೆ ನಿಮ್ಮಲ್ಲಿ ಕೇಳಿಕೊಳ್ಳಲಾಗುತ್ತದೆ.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"<xliff:g id="NUMBER_0">%d</xliff:g> ಬಾರಿ ನಿಮ್ಮ ಅನ್ಲಾಕ್ ನಮೂನೆಯನ್ನು ನೀವು ತಪ್ಪಾಗಿ ಎಳೆದಿರುವಿರಿ. <xliff:g id="NUMBER_1">%d</xliff:g> ಕ್ಕಿಂತ ಹೆಚ್ಚು ವಿಫಲ ಪ್ರಯತ್ನಗಳ ನಂತರ, ನೀವು ಟಿವಿಯನ್ನು ನಿಮ್ಮ ಇಮೇಲ್ ಖಾತೆ ಬಳಸಿಕೊಂಡು ಅನ್ಲಾಕ್ ಮಾಡುವಂತೆ ಕೇಳಲಾಗುತ್ತದೆ.\n\n<xliff:g id="NUMBER_2">%d</xliff:g> ಸೆಕೆಂಡುಗಳ ನಂತರ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"ನಿಮ್ಮ ಅನ್ಲಾಕ್ ಪ್ಯಾಟರ್ನ್ ಅನ್ನು ನೀವು <xliff:g id="NUMBER_0">%d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಡ್ರಾ ಮಾಡಿರುವಿರಿ. <xliff:g id="NUMBER_1">%d</xliff:g> ಹೆಚ್ಚಿನ ವಿಫಲ ಪ್ರಯತ್ನಗಳ ಬಳಿಕ, ನಿಮ್ಮ ಇಮೇಲ್ ಖಾತೆಯನ್ನು ಬಳಸಿಕೊಂಡು ನಿಮ್ಮ ಫೋನ್ ಅನ್ಲಾಕ್ ಮಾಡುವಂತೆ ನಿಮ್ಮಲ್ಲಿ ಕೇಳಿಕೊಳ್ಳಲಾಗುತ್ತದೆ.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"ನಿಮ್ಮ ಅನ್ಲಾಕ್ ಪ್ಯಾಟರ್ನ್ ಅನ್ನು ನೀವು <xliff:g id="NUMBER_0">%1$d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಡ್ರಾ ಮಾಡಿರುವಿರಿ. <xliff:g id="NUMBER_1">%2$d</xliff:g> ಹೆಚ್ಚಿನ ವಿಫಲ ಪ್ರಯತ್ನಗಳ ಬಳಿಕ, ನಿಮ್ಮ ಇಮೇಲ್ ಖಾತೆಯನ್ನು ಬಳಸಿಕೊಂಡು ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್ ಅನ್ಲಾಕ್ ಮಾಡುವಂತೆ ನಿಮ್ಮಲ್ಲಿ ಕೇಳಿಕೊಳ್ಳಲಾಗುತ್ತದೆ.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"<xliff:g id="NUMBER_0">%1$d</xliff:g> ಬಾರಿ ನಿಮ್ಮ ಅನ್ಲಾಕ್ ನಮೂನೆಯನ್ನು ನೀವು ತಪ್ಪಾಗಿ ಎಳೆದಿರುವಿರಿ. <xliff:g id="NUMBER_1">%2$d</xliff:g> ಕ್ಕಿಂತ ಹೆಚ್ಚು ವಿಫಲ ಪ್ರಯತ್ನಗಳ ನಂತರ, ನೀವು ಟಿವಿಯನ್ನು ನಿಮ್ಮ ಇಮೇಲ್ ಖಾತೆ ಬಳಸಿಕೊಂಡು ಅನ್ಲಾಕ್ ಮಾಡುವಂತೆ ಕೇಳಲಾಗುತ್ತದೆ.\n\n<xliff:g id="NUMBER_2">%3$d</xliff:g> ಸೆಕೆಂಡುಗಳ ನಂತರ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"ನಿಮ್ಮ ಅನ್ಲಾಕ್ ಪ್ಯಾಟರ್ನ್ ಅನ್ನು ನೀವು <xliff:g id="NUMBER_0">%1$d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಡ್ರಾ ಮಾಡಿರುವಿರಿ. <xliff:g id="NUMBER_1">%2$d</xliff:g> ಹೆಚ್ಚಿನ ವಿಫಲ ಪ್ರಯತ್ನಗಳ ಬಳಿಕ, ನಿಮ್ಮ ಇಮೇಲ್ ಖಾತೆಯನ್ನು ಬಳಸಿಕೊಂಡು ನಿಮ್ಮ ಫೋನ್ ಅನ್ಲಾಕ್ ಮಾಡುವಂತೆ ನಿಮ್ಮಲ್ಲಿ ಕೇಳಿಕೊಳ್ಳಲಾಗುತ್ತದೆ.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"ತೆಗೆದುಹಾಕು"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"ವಾಲ್ಯೂಮ್ ಅನ್ನು ಶಿಫಾರಸು ಮಾಡಲಾದ ಮಟ್ಟಕ್ಕಿಂತಲೂ ಹೆಚ್ಚು ಮಾಡುವುದೇ?\n\nದೀರ್ಘ ಅವಧಿಯವರೆಗೆ ಹೆಚ್ಚಿನ ವಾಲ್ಯೂಮ್ನಲ್ಲಿ ಆಲಿಸುವುದರಿಂದ ನಿಮ್ಮ ಆಲಿಸುವಿಕೆ ಸಾಮರ್ಥ್ಯಕ್ಕೆ ಹಾನಿಯುಂಟು ಮಾಡಬಹುದು."</string>
@@ -1470,6 +1468,7 @@
<item quantity="other">%d ಗಂಟೆಗೆ</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> ವರೆಗೆ"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> ವರೆಗೆ (ಮುಂದಿನ ಅಲಾರಮ್)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"ನೀವಿದನ್ನು ಆಫ್ ಮಾಡುವವರೆಗೆ"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"ನೀವು ಆಫ್ ಮಾಡುವವರೆಗೂ ಅಡಚಣೆ ಮಾಡಬೇಡಿ"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-ko-watch/strings.xml b/core/res/res/values-ko-watch/strings.xml
index 8602c7e..1572357 100644
--- a/core/res/res/values-ko-watch/strings.xml
+++ b/core/res/res/values-ko-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"앱 <xliff:g id="NUMBER_1">%2$d</xliff:g>개 중 <xliff:g id="NUMBER_0">%1$d</xliff:g>개"</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"센서"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"주소록에 액세스"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"이 시계 위치에 액세스"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"캘린더에 액세스"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"SMS 메시지 보내기 및 보기"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"시계의 사진, 미디어, 파일에 액세스"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"오디오 녹음"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"사진 찍기 및 동영상 녹화"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"전화 걸기 및 관리"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"생체 신호에 관한 센서 데이터에 액세스"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"상태 표시줄에 위치"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"신체 센서(예: 심박수 모니터)에 액세스"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"정확한 위치(GPS 및 네트워크 기반)에 액세스"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"대략적인 위치(네트워크 기반)에 액세스"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"SIM에 명령어 보내기"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"전체 네트워크 액세스 권한 보유"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"프로필 및 기기 소유자 관리"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"WiMAX 상태 변경"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"Android Beam 전송 상태 수신"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"미디어 출력 연결"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"설치 세션 읽기"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"패키지 설치 요청"</string>
</resources>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index f5a6fa4..fbc6ac2 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"신용카드 번호와 비밀번호 등의 개인 데이터를 포함합니다."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"상태 표시줄 사용 중지 또는 수정"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"앱이 상태 표시줄을 사용중지하거나 시스템 아이콘을 추가 및 제거할 수 있도록 허용합니다."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"상태 표시줄"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"상태 표시줄에 위치"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"앱이 상태 표시줄이 되도록 허용합니다."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"상태 표시줄 확장/축소"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"앱이 상태 표시줄을 확장하거나 축소할 수 있도록 허용합니다."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"앱이 WAP 메시지를 수신하고 처리할 수 있도록 허용합니다. 이는 앱이 사용자에게 표시하지 않고 기기로 전송된 메시지를 모니터링 또는 삭제할 수도 있다는 것을 의미합니다."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"실행 중인 앱 검색"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"앱이 현재 실행 중이거나 최근에 실행된 작업에 대한 정보를 검색할 수 있도록 허용합니다. 이 경우 앱이 기기에서 사용되는 다른 앱에 대한 정보를 검색할 수 있습니다."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"프로필 및 기기 소유자 관리"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"프로필 및 기기 소유자 관리"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"앱이 프로필 소유자와 기기 소유자를 설정하도록 허용합니다."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"실행 중인 앱 순서 재지정"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"앱이 사용자의 입력 없이 작업을 포그라운드나 백그라운드로 이동할 수 있도록 허용합니다."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"앱에서 수신 및 발신 통화 데이터를 포함하여 태블릿의 통화 기록을 수정할 수 있도록 허용합니다. 이 경우 악성 앱이 통화 기록을 지우거나 수정할 수 있습니다."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"앱에서 수신 및 발신 통화 데이터를 포함하여 TV의 통화 기록을 수정할 수 있도록 허용합니다. 이 경우 악성 앱이 통화 기록을 삭제하거나 수정할 수도 있습니다."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"앱에서 수신 및 발신 통화 데이터를 포함하여 휴대전화의 통화 기록을 수정할 수 있도록 허용합니다. 이 경우 악성 앱이 통화 기록을 지우거나 수정할 수 있습니다."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"신체 센서(예: 심박수 모니터)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"신체 센서(예: 심박수 모니터)에 액세스"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"앱이 심박수와 같은 신체 상태를 모니터링하는 센서의 데이터에 액세스하도록 허용합니다."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"캘린더 일정 및 기밀정보 읽기"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"앱이 친구나 동료의 일정을 포함하여 태블릿에 저장된 모든 캘린더 일정을 읽을 수 있도록 허용합니다. 이 경우 앱이 비밀유지 또는 기밀성을 무시하고 캘린더 데이터를 공유 또는 저장할 수도 있습니다."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"앱이 친구나 동료의 일정을 포함하여 휴대전화에서 수정할 수 있는 일정을 추가, 삭제, 변경할 수 있도록 허용합니다. 이 경우 앱이 캘린더 소유자가 보내는 것처럼 메시지를 전송하거나 소유자 모르게 일정을 수정할 수도 있습니다."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"추가 위치 제공업체 명령에 액세스"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"앱이 추가 위치 정보 제공 기능의 명령에 액세스하도록 허용합니다. 이 경우 앱이 GPS 또는 기타 위치 소스의 작동을 방해할 수 있습니다."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"정확한 위치(GPS 및 네트워크 기반)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"정확한 위치(GPS 및 네트워크 기반)에 액세스"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"앱에서 GPS 또는 기지국 및 Wi-Fi와 같은 네트워크 위치 제공자를 사용하는 위치 서비스를 통해 내 정확한 위치를 알 수 있도록 합니다. 앱에서 이를 사용하도록 하려면 기기에서 이러한 위치 서비스는 사용하도록 설정해야 합니다. 앱에서 위치 서비스를 사용하여 내 위치를 파악할 수 있으며 배터리 소모량이 증가할 수 있습니다."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"대략적인 위치(네트워크 기반)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"대략적인 위치(네트워크 기반)에 액세스"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"앱에서 나의 대략적인 위치를 알 수 있게 합니다. 이 위치는 기지국 및 Wi-Fi와 같은 네트워크 위치 제공자를 사용하는 위치 서비스를 통해 알 수 있습니다. 앱에서 이를 사용하도록 하려면 기기에서 이러한 위치 서비스를 사용하도록 설정해야 합니다. 앱에서 이를 사용하여 나의 대략적인 위치를 파악할 수 있습니다."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"오디오 설정 변경"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"앱이 음량이나 출력을 위해 사용하는 스피커 등 전체 오디오 설정을 변경할 수 있도록 허용합니다."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"오디오 녹음"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"앱이 마이크로 오디오를 녹음할 수 있도록 허용합니다. 이 권한을 사용하면 앱이 사용자의 확인 없이 언제든지 오디오를 녹음할 수 있습니다."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"SIM 통신"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"SIM에 명령어 보내기"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"앱이 SIM에 명령어를 전송할 수 있도록 허용합니다. 이 기능은 매우 위험합니다."</string>
<string name="permlab_camera" msgid="3616391919559751192">"사진과 동영상 찍기"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"앱이 카메라로 사진과 동영상을 찍을 수 있도록 허용합니다. 이 권한을 사용하면 앱이 언제든지 사용자의 확인 없이 카메라를 사용할 수 있습니다."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"앱이 휴대전화가 알고 있는 계정 목록을 가져올 수 있도록 허용합니다. 이 경우 설치한 애플리케이션에 의해 만들어진 모든 계정을 포함할 수 있습니다."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"네트워크 연결 보기"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"앱이 어떤 네트워크가 존재하며 연결되었는지 등의 네트워크 연결에 대한 정보를 볼 수 있도록 허용합니다."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"완전한 네트워크 액세스"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"전체 네트워크 액세스 권한 보유"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"앱이 네트워크 소켓을 만들고 맞춤 네트워크 프로토콜을 사용할 수 있도록 허용합니다. 브라우저 및 기타 앱이 데이터를 인터넷에 전송하는 수단을 제공하므로, 이 권한이 데이터를 인터넷에 전송하는 데 필요하지 않습니다."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"네트워크 연결 변경"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"앱이 네트워크 연결 상태를 변경할 수 있도록 허용합니다."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"앱이 로컬 블루투스 휴대전화를 설정한 다음 원격 기기를 검색하여 페어링할 수 있도록 허용합니다."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"WiMAX 연결 및 연결 해제"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"앱이 WiMAX를 사용하도록 설정했는지 여부와 연결된 WiMAX 네트워크에 대한 정보를 결정할 수 있도록 허용합니다."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX 상태 변경"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"WiMAX 상태 변경"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"앱이 태블릿을 WiMAX 네트워크에 연결하거나 연결을 끊을 수 있도록 허용합니다."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"앱이 WiMAX 네트워크에서 TV에 연결되거나 TV와의 연결을 해제할 수 있도록 허용합니다."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"앱이 휴대전화를 WiMAX 네트워크에 연결하거나 연결을 끊을 수 있도록 허용합니다."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"앱이 터치 스크린의 보정 매개변수를 수정할 수 있도록 허용합니다. 일반 앱에는 필요하지 않습니다."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"DRM 인증서에 액세스"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"애플리케이션이 DRM 인증서를 프로비저닝하고 사용하도록 허용합니다. 일반 앱에서는 필요하지 않습니다."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Android Beam 전송 상태 수신"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"Android Beam 전송 상태 수신"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"이 애플리케이션이 현재 Android Beam 전송 관련 정보를 수신하도록 허용합니다."</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"DRM 인증서 삭제"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"애플리케이션이 DRM 인증서를 삭제하도록 허용합니다. 일반 앱에서는 필요하지 않습니다."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PIN 코드가 잘못되었습니다."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"잠금해제하려면 메뉴를 누른 다음 0을 누릅니다."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"비상 전화번호"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"서비스 불가"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"서비스 불가"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"화면 잠김"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"비상 전화를 걸거나 잠금해제하려면 메뉴를 누르세요."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"잠금해제하려면 메뉴를 누르세요."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"사용자 가이드를 참조하거나 고객지원팀에 문의하세요."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM 카드가 잠겨 있습니다."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"SIM 카드 잠금해제 중..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"잠금해제 패턴을 <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못 그렸습니다. \n\n<xliff:g id="NUMBER_1">%d</xliff:g>초 후에 다시 시도하세요."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"비밀번호를 <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못 입력했습니다. \n\n<xliff:g id="NUMBER_1">%d</xliff:g>초 후에 다시 시도하세요."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"PIN을 <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못 입력했습니다. \n\n<xliff:g id="NUMBER_1">%d</xliff:g>초 후에 다시 시도하세요."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"잠금해제 패턴을 <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못 그렸습니다. <xliff:g id="NUMBER_1">%d</xliff:g>회 더 실패하면 Google 로그인을 통해 태블릿을 잠금해제해야 합니다.\n\n <xliff:g id="NUMBER_2">%d</xliff:g>초 후에 다시 시도하세요."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"잠금해제 패턴을 <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못 입력했습니다. <xliff:g id="NUMBER_1">%d</xliff:g>회 더 잘못 입력하면 Google 로그인을 통해 TV를 잠금 해제해야 합니다.\n\n <xliff:g id="NUMBER_2">%d</xliff:g>초 후에 다시 시도하세요."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"잠금해제 패턴을 <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못 그렸습니다. <xliff:g id="NUMBER_1">%d</xliff:g>회 더 실패하면 Google 로그인을 통해 휴대전화를 잠금해제해야 합니다.\n\n <xliff:g id="NUMBER_2">%d</xliff:g>초 후에 다시 시도하세요."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"태블릿을 잠금 해제하려는 시도가 <xliff:g id="NUMBER_0">%d</xliff:g>번 잘못되었습니다. <xliff:g id="NUMBER_1">%d</xliff:g>번 더 실패하면 태블릿이 초기화되고 사용자 데이터가 모두 손실됩니다."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"TV 잠금 해제 시도를 <xliff:g id="NUMBER_0">%d</xliff:g>회 실패했습니다. <xliff:g id="NUMBER_1">%d</xliff:g>회 더 실패하면 TV가 초기화되고 모든 사용자 데이터가 삭제됩니다."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"휴대전화를 잠금 해제하려는 시도가 <xliff:g id="NUMBER_0">%d</xliff:g>번 잘못되었습니다. <xliff:g id="NUMBER_1">%d</xliff:g>번 더 실패하면 휴대전화가 초기화되고 사용자 데이터가 모두 손실됩니다."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"잠금해제 패턴을 <xliff:g id="NUMBER_0">%1$d</xliff:g>회 잘못 그렸습니다. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g>초 후에 다시 시도하세요."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"비밀번호를 <xliff:g id="NUMBER_0">%1$d</xliff:g>회 잘못 입력했습니다. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g>초 후에 다시 시도하세요."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"PIN을 <xliff:g id="NUMBER_0">%1$d</xliff:g>회 잘못 입력했습니다. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g>초 후에 다시 시도하세요."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"잠금해제 패턴을 <xliff:g id="NUMBER_0">%1$d</xliff:g>회 잘못 그렸습니다. <xliff:g id="NUMBER_1">%2$d</xliff:g>회 더 실패하면 Google 로그인을 통해 태블릿을 잠금해제해야 합니다.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g>초 후에 다시 시도하세요."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"잠금해제 패턴을 <xliff:g id="NUMBER_0">%1$d</xliff:g>회 잘못 입력했습니다. <xliff:g id="NUMBER_1">%2$d</xliff:g>회 더 잘못 입력하면 Google 로그인을 통해 TV를 잠금 해제해야 합니다.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g>초 후에 다시 시도하세요."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"잠금해제 패턴을 <xliff:g id="NUMBER_0">%1$d</xliff:g>회 잘못 그렸습니다. <xliff:g id="NUMBER_1">%2$d</xliff:g>회 더 실패하면 Google 로그인을 통해 휴대전화를 잠금해제해야 합니다.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g>초 후에 다시 시도하세요."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"태블릿을 잠금 해제하려는 시도가 <xliff:g id="NUMBER_0">%1$d</xliff:g>번 잘못되었습니다. <xliff:g id="NUMBER_1">%2$d</xliff:g>번 더 실패하면 태블릿이 초기화되고 사용자 데이터가 모두 손실됩니다."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"TV 잠금 해제 시도를 <xliff:g id="NUMBER_0">%1$d</xliff:g>회 실패했습니다. <xliff:g id="NUMBER_1">%2$d</xliff:g>회 더 실패하면 TV가 초기화되고 모든 사용자 데이터가 삭제됩니다."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"휴대전화를 잠금 해제하려는 시도가 <xliff:g id="NUMBER_0">%1$d</xliff:g>번 잘못되었습니다. <xliff:g id="NUMBER_1">%2$d</xliff:g>번 더 실패하면 휴대전화가 초기화되고 사용자 데이터가 모두 손실됩니다."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"태블릿을 잠금 해제하려는 시도가 <xliff:g id="NUMBER">%d</xliff:g>번 잘못되었습니다. 태블릿이 초기화됩니다."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"TV 잠금 해제 시도를 <xliff:g id="NUMBER">%d</xliff:g>회 실패했으므로 지금 TV가 초기화됩니다."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"휴대전화를 잠금 해제하려는 시도가 <xliff:g id="NUMBER">%d</xliff:g>번 잘못되었습니다. 휴대전화가 초기화됩니다."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"단어"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"링크"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"행"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="AMPM">%P</xliff:g> <xliff:g id="HOUR">%-l</xliff:g>시"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="AMPM">%p</xliff:g> <xliff:g id="HOUR">%-l</xliff:g>시"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"출고 테스트 불합격"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"FACTORY_TEST 작업은 /system/app 디렉토리에 설치된 패키지에 대해서만 지원됩니다."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"FACTORY_TEST 작업을 제공하는 패키지가 없습니다."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"포맷하는 중…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"삽입하지 않음"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"일치하는 활동이 없습니다."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"미디어 출력 연결"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"미디어 출력 연결"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"앱이 미디어 출력을 기타 외부 기기에 연결할 수 있도록 허용합니다."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"설치 세션 읽기"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"설치 세션 읽기"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"애플리케이션의 설치 세션 읽기를 허용하면, 활성 패키지 설치에 대한 세부 정보를 볼 수 있습니다."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"패키지 설치 요청"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"패키지 설치 요청"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"애플리케이션이 패키지 설치를 요청하도록 허용합니다."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"확대/축소하려면 두 번 터치하세요."</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"위젯을 추가할 수 없습니다."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"사용자 이름 또는 비밀번호가 잘못되었습니다."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"사용자 이름이나 비밀번호를 잊어버렸습니까?\n"<b>"google.com/accounts/recovery"</b>" 페이지를 방문하세요."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"계정 확인 중…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"PIN을 <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못 입력했습니다. \n\n<xliff:g id="NUMBER_1">%d</xliff:g>초 후에 다시 시도해 주세요."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"비밀번호를 <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못 입력했습니다. \n\n<xliff:g id="NUMBER_1">%d</xliff:g>초 후에 다시 시도해 주세요."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"잠금해제 패턴을 <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못 그렸습니다. \n\n<xliff:g id="NUMBER_1">%d</xliff:g>초 후에 다시 시도해 주세요."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"태블릿을 잠금해제하려는 시도가 <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못되었습니다. <xliff:g id="NUMBER_1">%d</xliff:g>회 더 실패하면 태블릿이 초기화되고 사용자 데이터가 모두 사라집니다."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"TV 잠금 해제 시도를 <xliff:g id="NUMBER_0">%d</xliff:g>회 실패했습니다. <xliff:g id="NUMBER_1">%d</xliff:g>회 더 실패하면 TV가 초기화되고 모든 사용자 데이터가 삭제됩니다."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"휴대전화를 잠금해제하려는 시도가 <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못되었습니다. <xliff:g id="NUMBER_1">%d</xliff:g>회 더 실패하면 휴대전화가 초기화되고 사용자 데이터가 모두 사라집니다."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"PIN을 <xliff:g id="NUMBER_0">%1$d</xliff:g>회 잘못 입력했습니다. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g>초 후에 다시 시도해 주세요."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"비밀번호를 <xliff:g id="NUMBER_0">%1$d</xliff:g>회 잘못 입력했습니다. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g>초 후에 다시 시도해 주세요."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"잠금해제 패턴을 <xliff:g id="NUMBER_0">%1$d</xliff:g>회 잘못 그렸습니다. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g>초 후에 다시 시도해 주세요."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"태블릿을 잠금해제하려는 시도가 <xliff:g id="NUMBER_0">%1$d</xliff:g>회 잘못되었습니다. <xliff:g id="NUMBER_1">%2$d</xliff:g>회 더 실패하면 태블릿이 초기화되고 사용자 데이터가 모두 사라집니다."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"TV 잠금 해제 시도를 <xliff:g id="NUMBER_0">%1$d</xliff:g>회 실패했습니다. <xliff:g id="NUMBER_1">%2$d</xliff:g>회 더 실패하면 TV가 초기화되고 모든 사용자 데이터가 삭제됩니다."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"휴대전화를 잠금해제하려는 시도가 <xliff:g id="NUMBER_0">%1$d</xliff:g>회 잘못되었습니다. <xliff:g id="NUMBER_1">%2$d</xliff:g>회 더 실패하면 휴대전화가 초기화되고 사용자 데이터가 모두 사라집니다."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"태블릿을 잠금해제하려는 시도가 <xliff:g id="NUMBER">%d</xliff:g>회 잘못되었습니다. 태블릿이 초기화됩니다."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"TV 잠금 해제 시도를 <xliff:g id="NUMBER">%d</xliff:g>회 실패했으므로 지금 TV가 초기화됩니다."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"휴대전화를 잠금해제하려는 시도가 <xliff:g id="NUMBER">%d</xliff:g>회 잘못되었습니다. 휴대전화가 초기화됩니다."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"잠금해제 패턴을 <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못 그렸습니다. <xliff:g id="NUMBER_1">%d</xliff:g>회 더 실패하면 이메일 계정을 사용하여 태블릿을 잠금해제해야 합니다.\n\n <xliff:g id="NUMBER_2">%d</xliff:g>초 후에 다시 시도해 주세요."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"잠금해제 패턴을 <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못 입력했습니다. 앞으로 <xliff:g id="NUMBER_1">%d</xliff:g>회 더 잘못 입력하면 이메일 계정을 사용해서 TV의 잠금을 해제해야 합니다.\n\n <xliff:g id="NUMBER_2">%d</xliff:g>초 후에 다시 시도하세요."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"잠금해제 패턴을 <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못 그렸습니다. <xliff:g id="NUMBER_1">%d</xliff:g>회 더 실패하면 이메일 계정을 사용하여 휴대전화를 잠금해제해야 합니다.\n\n <xliff:g id="NUMBER_2">%d</xliff:g>초 후에 다시 시도해 주세요."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"잠금해제 패턴을 <xliff:g id="NUMBER_0">%1$d</xliff:g>회 잘못 그렸습니다. <xliff:g id="NUMBER_1">%2$d</xliff:g>회 더 실패하면 이메일 계정을 사용하여 태블릿을 잠금해제해야 합니다.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g>초 후에 다시 시도해 주세요."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"잠금해제 패턴을 <xliff:g id="NUMBER_0">%1$d</xliff:g>회 잘못 입력했습니다. 앞으로 <xliff:g id="NUMBER_1">%2$d</xliff:g>회 더 잘못 입력하면 이메일 계정을 사용해서 TV의 잠금을 해제해야 합니다.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g>초 후에 다시 시도하세요."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"잠금해제 패턴을 <xliff:g id="NUMBER_0">%1$d</xliff:g>회 잘못 그렸습니다. <xliff:g id="NUMBER_1">%2$d</xliff:g>회 더 실패하면 이메일 계정을 사용하여 휴대전화를 잠금해제해야 합니다.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g>초 후에 다시 시도해 주세요."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"삭제"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"권장 수준 이상으로 볼륨을 높이시겠습니까?\n\n높은 볼륨으로 장시간 청취하면 청력에 손상이 올 수 있습니다."</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">1시간 동안</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>까지"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>(다음 알람)까지"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"이 기능을 사용 중지할 때까지"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"알림 일시중지 기능을 사용 중지할 때까지"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-ky-rKG-watch/strings.xml b/core/res/res/values-ky-rKG-watch/strings.xml
index 2fb9047..cf64bc6 100644
--- a/core/res/res/values-ky-rKG-watch/strings.xml
+++ b/core/res/res/values-ky-rKG-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_1">%2$d</xliff:g> ичинен <xliff:g id="NUMBER_0">%1$d</xliff:g> колднм."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Сенсорлор"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"байланыштарыңызга уруксат"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"бул сааттын жайгашкан жерине уруксат"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"жылнаамаңызды пайдалануу"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"SMS билдирүүлөрдү жөнөтүү жана көрсөтүү"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"саатыңыздагы сүрөттөр, медиа жана файлдарга уруксат"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"аудио жаздыруу"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"сүрөт тартуу жана видео жаздыруу"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"телефон чалуу жана аларды башкаруу"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"организмдин абалына көз салган сенсордун дайындарына мүмкүнчүлүк алуу"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"абал тилкесинин милдетин аткаруу"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"дене-бой сенсорлоруна (жүрөктүн кагышын өлчөгүчтөр сыяктуу) уруксат"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"так аныкталган жайгашкан жерге (GPS жана тармактын негизинде) уруксат"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"болжолдуу жайгашкан жерге (тармактын негизинде) уруксат"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"SIM-картага буйруктарды жөнөтүү"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"тармакка толук мүмкүнчүлүк алуу"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"профилди жана түзмөк ээлерин башкаруу"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"WiMAX абалын өзгөртүү"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"Android Beam өткөрүү абалын кабыл алуу"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"медиа чыгарылышын багыттоо"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"орнотуу сеанстарын окуу"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"топтомдорду орнотууга уруксат суроо"</string>
</resources>
diff --git a/core/res/res/values-ky-rKG/strings.xml b/core/res/res/values-ky-rKG/strings.xml
index f20551d..5263dd8 100644
--- a/core/res/res/values-ky-rKG/strings.xml
+++ b/core/res/res/values-ky-rKG/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Кредиттик карта номурлары жана сырсөздөр сыяктуу өздүк берилиштерди камтыйт."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"абал тилкесин өчүрүү же өзгөртүү"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Колдонмого абал тилкесин өчүрүү же тутум сүрөтчөлөрүн кошуу же алып салуу мүмкүнчүлүгүн берет."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"абал тилкеси"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"абал тилкесинин милдетин аткаруу"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Колдонмого абал тилкеси болуу мүмкүнчүлүгүн берет."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"абал тилкесин жайып көрсөтүү/жыйнап коюу"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Колдонмого абал тилкесин жайып көрсөтүү же жыйнап коюу мүмкүнчүлүгүн берет."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Колдонмого WAP билдирүүлөрүн кабыл алууга жана аларды иштетип чыгууга уруксат берет. Бул, колдонмо сизге билгизбестен түзмөгүңүзгө жөнөтүлгөн билдирүүлөрдү мониторлой же жок кыла алат дегенди билдирет."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"иштеп жаткан колдонмолорду түшүрүп алуу"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Колдонмого учурдагы жана акыркы убакытта пайдаланылган колдонмолор тууралуу маалымат алууга уруксат берет. Бул колдонмого түзмөктө кандай колдонмолор колдонулаарын билип алууга жол бериши мүмкүн."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Профилди жана түзмөк ээлерин башкаруу"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"профилди жана түзмөк ээлерин башкаруу"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Колдонмолорго профиль ээлерин жана түзмөк ээсин орнотуу мүмкүнчүлүгүн берет."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"иштеп жаткан колдонмолорду иреттештирүү"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Колдонмо процесстерди фонго же алдыңкы планга жылдыруу уруксатын алат. Колдонмо муну сиздин ырастооңузсуз кыла алат."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Колдонмого планшетиңиздин чалуулар тизмегин, анын ичинде, чыгыш жана кириш чалууларына тиешелүү берилиштерди өзгөртүү уруксатын берет. Зыяндуу колдонмолор муну колдонуп чалуулар тизмегин өзгөртө же жок кыла алышат."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Колдонмого сыналгыңыздын чалуулар таржымалын, ошондой эле келүүчү жана чыгуучу чалуулар тууралуу дайындарды өзгөртүү мүмкүнчүлүгү берилет. Зыянкеч колдонмолор ушуну менен чалуулар таржымалыңызды жок кылып же өзгөртүп коюшу мүмкүн."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Колдонмого телефонуңуздун чалуулар тизмегин, анын ичинде, чыгыш жана кириш чалууларына тиешелүү берилиштерди өзгөртүү уруксатын берет. Зыяндуу колдонмолор муну колдонуп чалуулар тизмегин өзгөртө же жок кыла алышат."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"дене-бой сенсорлору (жүрөктүн кагышын өлчөгүчтөр сыяктуу)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"дене-бой сенсорлоруна (жүрөктүн кагышын өлчөгүчтөр сыяктуу) уруксат"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Колдонмого жүрөгүңүздүн согушу сыяктуу дене-бой абалыңызды көзөмөлдөгөн сенсорлордогу дайындарды көрүп туруу мүмкүнчүлүгүн берет."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"күнбарак иш-аракеттерин жана купуя маалыматтарды окуу"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Колдонмого планшетиңизде сакталган сиздин, досторуңуздун жана кесиптештериңиздин күнбарак окуяларын окуганга уруксат берет. Бул колдонмого күнбарак берилиштерин, алардын купуялуулугана жана маанилүүлүгөн карабастан бөлүшүү же сактоо уруксатын берет."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Колдонмого сиз телефонуңуздан өзгөртө ала турган, сиздин, досторуңуздун же кесиптештериңиздин күнбарак окуяларын кошуу, жок кылуу, өзгөртүү уруксатын берет. Бул, колдонмого күнбарак ээлеринен келген сыяктуу көрүнгөн билдирүүлөрдү жөнөтүү, же ээсине билгизбей окуяларды өзгөртүү уруксатын берет."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"жайгашкан жерди аныктагычтын кошумча буйруктарын пайдалануу"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Колдонмого жайгашкан жерди табуучу кошумча жабдуучулардын буйруктарын колдонуу мүмкүнчүлүгүн берет. Ушуну менен колдонмо GPS\'тин ишине жана башка жайгашкан жерлерди аныктоо кызматтарына кийлигише алат."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"так жайгаштыруу (GPS жана түйүн негизинде)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"так аныкталган жайгашкан жерге (GPS жана тармактын негизинде) уруксат"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Колдонмого Глобалдык Позициялоо Системасын (GPS), же базалык станциялар жана Wi-Fi сыяктуу түйүндүк булактардын жайгашуусунун пайдалануу аркылуу сиздин так жайгашууңузду аныктоого уруксат берет. Колдонмолор муну пайдалана алышы үчүн, жайгаштыруу кызматтары жандырылган жана түзмөгүңүзгө жеткиликтүү болушу керек. Колдонмолор муну сиздин жайгашкан жериңизди аныкташ үчүн пайдаланышы жана кошумча батарей кубаты сарпталышы мүмкүн."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"божомолдуу жайгаштыруу (түйүн негизинде)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"болжолдуу жайгашкан жерге (тармактын негизинде) уруксат"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Колдонмого сиздин болжолдуу жайгашууңузду аныктоо уруксаты берет. Мындай жайгаштыруу базалык станциялар жана Wi-Fi сыяктуу түйүндүк булактардын жайгашуусу аркылуу аныкталат. Колдонмоңуз буларды пайдалана алышы үчүн, мындай жайгаштыруу кызматтары жандырылган жана түзмөгүңүзгө жеткиликтүү болушу керек. Колдонмолор муну сиздин жайгашкан жериңизди болжолдош үчүн пайдаланышы мүмкүн."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"аудио жөндөөлөрүңүздү өзгөртүңүз"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Колдонмого үн деңгээли жана кайсы динамик аркылуу үн чыгарылышы керек сыяктуу түзмөктүн аудио тууралоолорун өзгөртүүгө уруксат берет."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"аудио жаздыруу"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Колдонмого микрофон аркылуу аудио жаздыруу уруксатын берет. Бул уруксат колдонмого сиздин ырастооңузсуз, каалаган убакта аудио жаздыруу уруксатын берет."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"sim-карта менен байланышуу"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"SIM-картага буйруктарды жөнөтүү"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Колдонмого SIM-картага буйруктарды жөнөтүү мүмкүнчүлүгүн берет. Бул абдан кооптуу."</string>
<string name="permlab_camera" msgid="3616391919559751192">"сүрөт жана видео тартуу"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Колдонмого камера аркылуу видео жана сүрөт тартуу уруксатын берет. Бул уруксат, камераны каалаган убакта, сиздин ырастооңузсуз колдонуу уруксатын берет."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Колдонмого телефонго белгилүү эсептердин тизмегин алуу уруксатын берет. Буларга сиз орноткон колдонмолор аркылуу түзүлгөн эсептер кириши мүмкүн."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"түйүн туташууларын көрүү"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Колдонмого желелердин бардыгы жана байланыштар сыяктуу желе маалыматтарын көргөнгө уруксат берет."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"желеге толук жетки алуу"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"тармакка толук мүмкүнчүлүк алуу"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Колдонмого түйүн сокеттерин түзүү жана ылайыкташтырылган түйүн протоколдорун пайдалануу уруксаттарын берет. Серепчи жана башка колдонмолорго интернетке берилиштерди жөнөтүү жолдорун берет, ошондуктан, интернетке берилиштерди жөнөтүү үчүн, бул уруксатты алуу талап кылынбай."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"тармак туташымдуулугун өзгөртүү"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Колдонмого тармактык туташуунун абалын өзгөртүү мүмкүнчүлүгүн берет."</string>
@@ -402,7 +402,8 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Колдонмого жергиликтүү Bluetooth телефонун конфигурациялап, ыраактагы түзмөктөрдү таап, жупташуу мүмкүнчүлүгүн берет."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"WiMAX түйүнүнө туташуу жана андан ажыроо"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Колдонмого WiMAX жандырылгандыгы жана туташкан WiMAX түйүндөрү тууралуу маалыматтарын көрүүгө уруксат берет."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX абалын өзгөртүү"</string>
+ <!-- no translation found for permlab_changeWimaxState (340465839241528618) -->
+ <skip />
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Колдонмого планшетти WiMAX түйүндөрүнө туташтыруу жана ажыратуу уруксаттары берилет."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Колдонмого сыналгыны WiMAX тармактарына туташтырып, алардан ажыратуу мүмкүнчүлүгүн берет."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Колдонмого телефонду WiMAX түйүндөрүнө туташтыруу жана ажыратуу уруксаттары берилет."</string>
@@ -485,7 +486,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Колдонмого сенсордук экрандын калибрлөө параметрлерин өзгөртүү мүмкүнчүлүгүн берет. Кадимки колдонмолорго эч качан талап кылынбайт."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"DRM тастыктамаларына кирүү"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Колдонмого DRM тастыктамаларын ишке киргизип, колдонуу мүмкүнчүлүгүн берет. Кадимки колдонмолорго эч качан талап кылынбайт."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Android Beam өткөрүү абалын алуу"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"Android Beam өткөрүү абалын кабыл алуу"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Бул колдонмого учурдагы Android Beam өткөрүүлөрү жөнүндө маалымат алуу мүмкүнчүлүгүн берет"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"DRM тастыктамаларын алып салуу"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Колдонмого DRM тастыктамаларын алып салуу мүмкүнчүлүгүн берет. Кадимки колдонмолорго эч качан талап кылынбайт."</string>
@@ -650,7 +651,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PIN-код туура эмес."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Кулпусун ачуу үчүн, Менюна андан соң 0 баскычын басыңыз."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Шашылыш чалуу номери"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Байланыш жок."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Байланыш жок"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Экран кулпуланды."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Кулпусун ачып же Шашылыш чалуу аткаруу үчүн менюну басыңыз."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Бөгөттөн чыгаруу үчүн Менюну басыңыз."</string>
@@ -682,15 +683,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Колдонуучунун нускамасын караңыз же Кардарларды тейлөө борборуна кайрылыңыз."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM-карта бөгөттөлгөн."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"SIM-карта бөгөттөн чыгарылууда…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Кулпуну ачуу үлгүсүн <xliff:g id="NUMBER_0">%d</xliff:g> жолу туура эмес тарттыңыз. \n\n <xliff:g id="NUMBER_1">%d</xliff:g> секундадан кийин дагы аракет кылып көрүңүз."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Сырсөзүңүздү <xliff:g id="NUMBER_0">%d</xliff:g> жолу туура эмес тердиңиз. \n\n <xliff:g id="NUMBER_1">%d</xliff:g> секундадан кийин дагы аракет кылып көрүңүз."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"PIN-кодуңузду <xliff:g id="NUMBER_0">%d</xliff:g> жолу туура эмес тердиңиз. \n\n <xliff:g id="NUMBER_1">%d</xliff:g> секундадан кийин дагы аракет кылып көрүңүз."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Кулпуну ачуу үлгүсүн <xliff:g id="NUMBER_0">%d</xliff:g> жолу туура эмес тарттыңыз. Дагы <xliff:g id="NUMBER_1">%d</xliff:g> жолу туура эмес тартсаңыз, планшетиңиздин кулпусун Google\'га кирип ачууга туура келет.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> секундадан кийин дагы аракет кылып көрүңүз."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Кулпуну ачуу үлгүсүн <xliff:g id="NUMBER_0">%d</xliff:g> жолу туура эмес тарттыңыз. Дагы <xliff:g id="NUMBER_1">%d</xliff:g> жолу туура эмес тартсаңыз, сыналгыңыздын кулпусун Google\'га кирип ачууга туура келет.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> секундадан кийин дагы аракет кылып көрүңүз."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Кулпуну ачуу үлгүсүн <xliff:g id="NUMBER_0">%d</xliff:g> жолу туура эмес тарттыңыз. Дагы <xliff:g id="NUMBER_1">%d</xliff:g> жолу туура эмес тартсаңыз, телефонуңуздун кулпусун Google\'га кирип ачууга туура келет.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> секундадан кийин дагы аракет кылып көрүңүз."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Сиз планшетиңизди бөгөттөн чыгарууга <xliff:g id="NUMBER_0">%d</xliff:g> жолу туура эмес аракет кылдыңыз. Дагы <xliff:g id="NUMBER_1">%d</xliff:g> аракеттен кийин, планшет баштапкы абалына келтирилет жана бардык маалыматтар жок кылынат."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Кулпуну ачуу үлгүсүн <xliff:g id="NUMBER_0">%d</xliff:g> жолу туура эмес тарттыңыз. Дагы <xliff:g id="NUMBER_1">%d</xliff:g> жолу туура эмес тартсаңыз, сыналгыңыз баштапкы абалга келтирилип, колдонуучунун бардык дайындары жок болот."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Сиз телефонуңузду бөгөттөн чыгарууга <xliff:g id="NUMBER_0">%d</xliff:g> жолу туура эмес аракет кылдыңыз. Дагы <xliff:g id="NUMBER_1">%d</xliff:g> аракеттен кийин, телефон баштапкы абалына келтирилет жана бардык маалыматтар жок кылынат."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Кулпуну ачуу үлгүсүн <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес тарттыңыз. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> секундадан кийин дагы аракет кылып көрүңүз."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Сырсөзүңүздү <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес тердиңиз. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> секундадан кийин дагы аракет кылып көрүңүз."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"PIN-кодуңузду <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес тердиңиз. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> секундадан кийин дагы аракет кылып көрүңүз."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Кулпуну ачуу үлгүсүн <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес тарттыңыз. Дагы <xliff:g id="NUMBER_1">%2$d</xliff:g> жолу туура эмес тартсаңыз, планшетиңиздин кулпусун Google\'га кирип ачууга туура келет.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундадан кийин дагы аракет кылып көрүңүз."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Кулпуну ачуу үлгүсүн <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес тарттыңыз. Дагы <xliff:g id="NUMBER_1">%2$d</xliff:g> жолу туура эмес тартсаңыз, сыналгыңыздын кулпусун Google\'га кирип ачууга туура келет.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундадан кийин дагы аракет кылып көрүңүз."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Кулпуну ачуу үлгүсүн <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес тарттыңыз. Дагы <xliff:g id="NUMBER_1">%2$d</xliff:g> жолу туура эмес тартсаңыз, телефонуңуздун кулпусун Google\'га кирип ачууга туура келет.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундадан кийин дагы аракет кылып көрүңүз."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Сиз планшетиңизди бөгөттөн чыгарууга <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес аракет кылдыңыз. Дагы <xliff:g id="NUMBER_1">%2$d</xliff:g> аракеттен кийин, планшет баштапкы абалына келтирилет жана бардык маалыматтар жок кылынат."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Кулпуну ачуу үлгүсүн <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес тарттыңыз. Дагы <xliff:g id="NUMBER_1">%2$d</xliff:g> жолу туура эмес тартсаңыз, сыналгыңыз баштапкы абалга келтирилип, колдонуучунун бардык дайындары жок болот."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Сиз телефонуңузду бөгөттөн чыгарууга <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес аракет кылдыңыз. Дагы <xliff:g id="NUMBER_1">%2$d</xliff:g> аракеттен кийин, телефон баштапкы абалына келтирилет жана бардык маалыматтар жок кылынат."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Сиз планшетти бөгөттөн чыгарууга <xliff:g id="NUMBER">%d</xliff:g> жолу туура эмес аракет кылдыңыз. Планшет баштапкы абалына келтирилет."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Сыналгыңыздын кулпусун ачууда <xliff:g id="NUMBER">%d</xliff:g>_0 жолу туура эмес аракет кылдыңыз. Сыналгыңыз баштапкы абалга келтирилет."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Сиз телефонду бөгөттөн чыгарууга <xliff:g id="NUMBER">%d</xliff:g> жолу туура эмес аракет кылдыңыз. Телефон баштапкы абалына келтирилет."</string>
@@ -742,8 +743,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"сөз"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"шилтеме"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"сап"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Заводдук сынак ишке ашкан жок"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"FACTORY_TEST аракети /system/app ичинде орнотулган топтомдор үчүн гана колдоого алынат."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"FACTORY_TEST аракетин камсыздаган эч бир топтом табылган жок."</string>
@@ -1073,11 +1072,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Форматталууда…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Сайылган жок"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Туура келген аракеттер табылбады."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Медиа чыгарылышын багыттоо"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"медиа чыгарылышын багыттоо"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Колдонмого медиа мазмунду башка тышкы түзмөктөргө багыттоо уруксатын берет."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Орнотуу сеанстарын окуу"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"орнотуу сеанстарын окуу"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Колдонмого орнотуу сеанстарын окуу мүмкүнчүлүгүн берет. Ушуну менен, ал жигердүү топтом орнотууларынын чоо-жайын көрө алат."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Топтомдорду орнотууга уруксат суроо"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"орнотуу топтомдорун суроо"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Колдонмо топтомдорду орнотууга уруксат сурай алат."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Чен өлчөмүн көзөмөлдөө үчүн эки жолу тийип коюңуз"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Виджетти кошуу мүмкүн болбоду."</string>
@@ -1285,18 +1284,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Колдонуучу атыңыз же сырсөзүңүз туура эмес."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Колдонуучу атыңызды же сырсөзүңүздү унутуп калдыңызбы?\n"<b>"google.com/accounts/recovery"</b>" дарегине кайрылыңыз."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Эсеп текшерилүүдө…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Сиз PIN-кодуңузду <xliff:g id="NUMBER_0">%d</xliff:g> жолу туура эмес тердиңиз. \n\n <xliff:g id="NUMBER_1">%d</xliff:g> секундадан кийин кайталаңыз."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Сиз сырсөзүңүздү <xliff:g id="NUMBER_0">%d</xliff:g> жолу туура эмес тердиңиз. \n\n <xliff:g id="NUMBER_1">%d</xliff:g> секундадан кийин кайталаңыз."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Сиз бөгөттөн чыгаруу үлгүсүн <xliff:g id="NUMBER_0">%d</xliff:g> жолу туура эмес көрсөттүңүз. \n\n <xliff:g id="NUMBER_1">%d</xliff:g> секундадан кийин кайталаңыз."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Сиз планшетиңизди <xliff:g id="NUMBER_0">%d</xliff:g> жолу ийгиликсиз бөгөттөн чыгаруу аракетин кылдыңыз. Дагы <xliff:g id="NUMBER_1">%d</xliff:g> ийгиликсиз аракеттен кийин, планшет баштапкы абалына кайтарылат жана бардык берилиштериңиз жок кылынат."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Кулпуну ачуу үлгүсүн <xliff:g id="NUMBER_0">%d</xliff:g> жолу туура эмес тарттыңыз. Дагы <xliff:g id="NUMBER_1">%d</xliff:g> жолу туура эмес тартсаңыз, сыналгыңыз баштапкы абалга келтирилип, колдонуучунун бардык дайындары жок болот."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Сиз телефонуңузду <xliff:g id="NUMBER_0">%d</xliff:g> жолу ийгиликсиз бөгөттөн чыгаруу аракетин кылдыңыз. Дагы <xliff:g id="NUMBER_1">%d</xliff:g> ийгиликсиз аракеттен кийин, телефон баштапкы абалына кайтарылат жана бардык берилиштериңиз жок кылынат."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Сиз PIN-кодуңузду <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес тердиңиз. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> секундадан кийин кайталаңыз."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Сиз сырсөзүңүздү <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес тердиңиз. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> секундадан кийин кайталаңыз."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Сиз бөгөттөн чыгаруу үлгүсүн <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес көрсөттүңүз. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> секундадан кийин кайталаңыз."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Сиз планшетиңизди <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу ийгиликсиз бөгөттөн чыгаруу аракетин кылдыңыз. Дагы <xliff:g id="NUMBER_1">%2$d</xliff:g> ийгиликсиз аракеттен кийин, планшет баштапкы абалына кайтарылат жана бардык берилиштериңиз жок кылынат."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Кулпуну ачуу үлгүсүн <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес тарттыңыз. Дагы <xliff:g id="NUMBER_1">%2$d</xliff:g> жолу туура эмес тартсаңыз, сыналгыңыз баштапкы абалга келтирилип, колдонуучунун бардык дайындары жок болот."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Сиз телефонуңузду <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу ийгиликсиз бөгөттөн чыгаруу аракетин кылдыңыз. Дагы <xliff:g id="NUMBER_1">%2$d</xliff:g> ийгиликсиз аракеттен кийин, телефон баштапкы абалына кайтарылат жана бардык берилиштериңиз жок кылынат."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Сиз планшетиңизди <xliff:g id="NUMBER">%d</xliff:g> жолу ийгиликсиз бөгөттөн чыгаруу аракетин кылдыңыз. Планшет баштапкы абалына кайтарылат."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Сыналгыңыздын кулпусун ачууда <xliff:g id="NUMBER">%d</xliff:g>_0 жолу туура эмес аракет кылдыңыз. Сыналгыңыз баштапкы абалга келтирилет."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Сиз телефонуңузду <xliff:g id="NUMBER">%d</xliff:g> жолу ийгиликсиз бөгөттөн чыгаруу аракетин кылдыңыз. Телефон баштапкы абалына кайтарылат."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Сиз бөгөттөн чыгаруу үлгүсүн <xliff:g id="NUMBER_0">%d</xliff:g> жолу туура эмес көрсөттүңүз. Дагы <xliff:g id="NUMBER_1">%d</xliff:g> ийгиликсиз аракеттен кийин, планшетиңизди эмейл эсебиңиз аркылуу бөгөттөн чыгаруу талап кылынат.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> секундадан кийин кайра аракеттениңиз."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Кулпуну ачуу үлгүсүн <xliff:g id="NUMBER_0">%d</xliff:g> жолу туура эмес тарттыңыз. Дагы <xliff:g id="NUMBER_1">%d</xliff:g> жолу туура эмес тартсаңыз, сыналгыңыздын кулпусун электрондук каттоо эсебиңизге кирип ачууга туура келет.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> секундадан кийин дагы аракет кылып көрүңүз."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Сиз бөгөттөн чыгаруу үлгүсүн <xliff:g id="NUMBER_0">%d</xliff:g> жолу туура эмес көрсөттүңүз. Дагы <xliff:g id="NUMBER_1">%d</xliff:g> ийгиликсиз аракеттен кийин, телефонуңузду эмейл эсебиңиз аркылуу бөгөттөн чыгаруу талап кылынат.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> секундадан кийин кайра аракеттениңиз."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Сиз бөгөттөн чыгаруу үлгүсүн <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес көрсөттүңүз. Дагы <xliff:g id="NUMBER_1">%2$d</xliff:g> ийгиликсиз аракеттен кийин, планшетиңизди эмейл эсебиңиз аркылуу бөгөттөн чыгаруу талап кылынат.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундадан кийин кайра аракеттениңиз."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Кулпуну ачуу үлгүсүн <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес тарттыңыз. Дагы <xliff:g id="NUMBER_1">%2$d</xliff:g> жолу туура эмес тартсаңыз, сыналгыңыздын кулпусун электрондук каттоо эсебиңизге кирип ачууга туура келет.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундадан кийин дагы аракет кылып көрүңүз."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Сиз бөгөттөн чыгаруу үлгүсүн <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес көрсөттүңүз. Дагы <xliff:g id="NUMBER_1">%2$d</xliff:g> ийгиликсиз аракеттен кийин, телефонуңузду эмейл эсебиңиз аркылуу бөгөттөн чыгаруу талап кылынат.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундадан кийин кайра аракеттениңиз."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Алып салуу"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Сунушталган деңгээлден да катуулатып уккуңуз келеби?\n\nМузыканы узакка чейин катуу уксаңыз, угууңуз начарлап кетиши мүмкүн."</string>
@@ -1470,6 +1469,7 @@
<item quantity="one">1 саатка</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> чейин"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> чейин (кийинки ойготкуч)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Бул өчүрүлгөнгө чейин"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"\"Тынчымды алба\" режими өчүрүлгөнгө чейин"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-lo-rLA-watch/strings.xml b/core/res/res/values-lo-rLA-watch/strings.xml
index db2f7e4..4fb6671 100644
--- a/core/res/res/values-lo-rLA-watch/strings.xml
+++ b/core/res/res/values-lo-rLA-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"ແອັບ <xliff:g id="NUMBER_0">%1$d</xliff:g> ໃນ <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"ເຊັນເຊີ"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"ເຂົ້າຫາລາຍຊື່ຂອງທ່ານ"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"ເຂົ້າຫາທີ່ຕັ້ງຂອງໂມງນີ້"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"ເຂົ້າຫາປະຕິທິນຂອງທ່ານ"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"ສົ່ງ ແລະ ເບິ່ງຂໍ້ຄວາມ SMS"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"ເຂົ້າເຖິງຮູບຖ່າຍ, ສື່ ແລະ ໄຟລ໌ຢູ່ເທິງໂມງຂອງທ່ານ"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"ບັນທຶກສຽງ"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"ຖ່າຍຮູບ ແລະ ບັນທຶກວິດີໂອ"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"ໂທ ແລະ ຈັດການການໂທລະສັບ"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"ເຂົ້າຫາຂໍ້ມູນເຊັນເຊີກ່ຽວກັບສັນຍານຊີບຂອງທ່ານ"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"ເປັນແຖບສະຖານະ"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"ເຂົ້າຫາເຊັນເຊີກວດຮ່າງກາຍ (ເຊັ່ນ: ຈໍຕິດຕາມອັດຕາການເຕັ້ນຂອງຫົວໃຈ)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"ເຂົ້າຫາທີ່ຕັ້ງທີ່ແນ່ນອນ (ອີງໃສ່ GPS ແລະ ເຄືອຂ່າຍ)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"ເຂົ້າຫາທີ່ຕັ້ງໂດຍປະມານ (ອີງໃສ່ເຄືອຂ່າຍ)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"ສົ່ງຄຳສັ່ງຫາ SIM"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"ມີການເຂົ້າເຖິງເຄືອຂ່າຍເຕັມຮູບແບບ"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"ຈັດການເຈົ້າຂອງໂປຣໄຟລ໌ ແລະ ອຸປະກອນ"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"ປ່ຽນສະຖານະ WiMAX"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"ຮັບສະຖານະການໂອນຂໍ້ມູນ Android Beam"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"ກຳນົດຊ່ອງທາງອອກຂອງສື່"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"ອ່ານເຊສຊັນການຕິດຕັ້ງ"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"ຂໍຕິດຕັ້ງແພັກເກດ"</string>
</resources>
diff --git a/core/res/res/values-lo-rLA/strings.xml b/core/res/res/values-lo-rLA/strings.xml
index 2e58b116..805982a 100644
--- a/core/res/res/values-lo-rLA/strings.xml
+++ b/core/res/res/values-lo-rLA/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ຮວມທັງຂໍ້ມູນສ່ວນໂຕເຊັ່ນ: ເລກບັດເຄຣດິດ ແລະລະຫັດຜ່ານ."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"ປິດການນນຳໃຊ້ ຫຼື ແກ້ໄຂແຖບສະຖານະ"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"ອະນຸຍາດໃຫ້ແອັບຯປິດການເຮັດວຽກຂອງແຖບສະຖານະ ຫຼືເພີ່ມ ແລະລຶບໄອຄອນລະບົບອອກໄດ້."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"ແຖບສະຖານະ"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"ເປັນແຖບສະຖານະ"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"ອະນຸຍາດໃຫ້ແອັບຯເປັນແຖບສະຖານະ."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"ຫຍໍ້/ຂະຫຍາຍ ແຖບສະຖານະ"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"ອະນຸຍາດໃຫ້ແອັບຯ ຂະຫຍາຍ ຫຼືຫຍໍ້ແຖບສະຖານະ."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"ອະນຸຍາດໃຫ້ແອັບຯຮັບ ແລະປະມວນຜົນຂໍ້ຄວາມ WAP. ການອະນຸຍາດນີ້ຮວມເຖິງຄວາມສາມາດໃນການກວດເບິ່ງ ແລະລຶບຂໍ້ຄວາມທີ່ສົ່ງແລ້ວ ໂດຍບໍ່ຕ້ອງສະແດງໃຫ້ທ່ານເຫັນ."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"ດຶງແອັບຯທີ່ເຮັດວຽກຢູ່ມາ"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"ອະນຸຍາດໃຫ້ແອັບຯດຶງຂໍ້ມູນກ່ຽວກັບການເຮັດວຽກໃນປັດຈຸບັນ ແລະຫາກໍຜ່ານມາ. ແອັບຯທີ່ເປັນອັນຕະລາຍອາດຄົ້ນພົບຂໍ້ມູນ ກ່ຽວກັບແອັບພລິເຄຊັນທີ່ໃຊ້ຢູ່ໃນອຸປະກອນໄດ້."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"ຈັດການເຈົ້າຂອງໂປຣໄຟລ໌ ແລະອຸປະກອນ"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"ຈັດການເຈົ້າຂອງໂປຣໄຟລ໌ ແລະ ອຸປະກອນ"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"ອະນຸຍາດໃຫ້ແອັບຕັ້ງເຈົ້າຂອງໂປຣໄຟລ໌ ແລະເຈົ້າຂອງອຸປະກອນ."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"ຮຽງລຳດັບແອັບຯທີ່ກຳລັງເຮັດວຽກຄືນໃໝ່"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"ອະນຸຍາດໃຫ້ແອັບຯຍ້າຍການເຮັດວຽກໄປໃສ່ດ້ານໜ້າ ແລະພື້ນຫຼັງໄດ້. ແອັບຯອາດຈະດຳເນີນການໂດຍບໍ່ຕ້ອງໃຫ້ທ່ານບອກ."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"ອະນຸຍາດໃຫ້ແອັບຯແກ້ໄຂບັນທຶກການໂທຂອງແທັບເລັດ ຮວມທັງຂໍ້ມູນກ່ຽວກັບການໂທອອກ ແລະໂທເຂົ້ານຳ. ແອັບຯທີ່ເປັນອັນຕະລາຍອາດໃຊ້ຄຸນສົມບັດນີ້ເພື່ອລຶບ ຫຼືແກ້ໄຂບັນທຶກການໂທຂອງທ່ານໄດ້."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"ອະນຸຍາດໃຫ້ແອັບແກ້ໄຂບັນທຶກການໂທຂອງໂທລະພາບຂອງທ່ານ ລວມທັງຂໍ້ມູນກ່ຽວກັບການໂທອອກ ແລະໂທເຂົ້ານຳ. ແອັບທີ່ເປັນອັນຕະລາຍອາດໃຊ້ຄຸນສົມບັດນີ້ເພື່ອລຶບ ຫຼືແກ້ໄຂບັນທຶກການໂທຂອງທ່ານໄດ້."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"ອະນຸຍາດໃຫ້ແອັບຯ ແກ້ໄຂລາຍການການໂທໃນໂທລະສັບຂອງທ່ານ, ຮວມທັງຂໍ້ມູນກ່ຽວກັບສາຍໂທເຂົ້າ ແລະການໂທອອກ. ແອັບຯທີ່ເປັນອັນຕະລາຍ ອາດໃຊ້ຄວາມສາມາດນີ້ ເພື່ອລຶບ ຫຼືແກ້ໄຂລາຍການການໂທຂອງທ່ານໄດ້."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"ເຊັນເຊີຮ່າງກາຍ (ເຊັ່ນ: ຕິດຕາມອັດຕາການເຕັ້ນຂອງຫົວໃຈ)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"ເຂົ້າຫາເຊັນເຊີກວດຮ່າງກາຍ (ເຊັ່ນ: ຈໍຕິດຕາມອັດຕາການເຕັ້ນຂອງຫົວໃຈ)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"ອະນຸຍາດໃຫ້ແອັບຯເຂົ້າເຖິງຂໍ້ມູນຈາກເຊັນເຊີທີ່ຕິດຕາມສະພາບຮ່າງການຂອງທ່ານ, ເຊັ່ນ: ອັດຕາການເຕັ້ນຂອງຫົວໃຈຂອງທ່ານ."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"ອ່ານກຳນົດການໃນປະຕິທິນຮວມທັງຂໍ້ມູນຄວາມລັບ"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"ອະນຸຍາດໃຫ້ແອັບຯ ອ່ານການນັດໝາຍທັງໝົດທີ່ມີບັນທຶກໃນແທັບເລັດຂອງທ່ານ, ຮວມທັງຂອງໝູ່ ຫຼືໝູ່ທີ່ເຮັດວຽກນຳກັນໄດ້ ເຊິ່ງອາດເຮັດໃຫ້ແອັບຯສາມາດສົ່ງຕໍ່ ຫຼືບັນທຶກຂໍ້ມູນປະຕິທິນຂອງທ່ານ ບໍ່ວ່າຈະເປັນເລື່ອງຄວາມລັບ ຫຼືເລື່ອງລະອຽດອ່ອນແບບໃດກໍຕາມ."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"ອະນຸຍາດໃຫ້ແອັບຯ ເພີ່ມ, ລຶບ, ປ່ຽນແປງນັດໝາຍທີ່ທ່ານສາມາດແກ້ໄຂໄດ້ໃນໂທລະສັບຂອງທ່ານ, ຮວມທັງຂອງໝູ່ຄູ່ ຫຼືເພື່ອນຮ່ວມວຽກ. ນີ້ອາດເຮັດໃຫ້ແອັບຯສາມາດສົ່ງຂໍ້ຄວາມ ທີ່ເບິ່ງຄືວ່າມາຈາກເຈົ້າຂອງປະຕິທິນ ຫຼືແກ້ໄຂນັດໝາຍໂດຍທີ່ທ່ານບໍ່ໄດ້ຮັບຮູ້ໄດ້."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"ເຂົ້າເຖິງຄຳສັ່ງຜູ່ໃຫ້ບໍລິການພິກັດສະຖານທີ່"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"ອະນຸຍາດໃຫ້ແອັບຯເຂົ້າເຖິງຄຳສັ່ງເພີ່ມເຕີມຂອງຜູ່ໃຫ້ບໍລິການສະຖານທີ່. ນີ້ອາດຈະເປັນການເຮັດໃຫ້ແອັບຯ ລົບກວນການເຮັດວຽກຂອງ GPS ຫຼືແຫລ່ງຂໍ້ມູນສະຖານທີ່ອື່ນໆໄດ້."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"ສະຖານທີ່ແນ່ນອນ (ອ້າງອີງຈາກ GPS ແລະເຄືອຂ່າຍ)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"ເຂົ້າຫາທີ່ຕັ້ງທີ່ແນ່ນອນ (ອີງໃສ່ GPS ແລະ ເຄືອຂ່າຍ)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"ອະນຸຍາດໃຫ້ແອັບຯ ຮັບຕຳແໜ່ງສະຖານທີ່ລະອຽດຂອງທ່ານໂດຍໃຊ້ GPS ຫຼືແຫລ່ງຂໍ້ມູນເຄືອຂ່າຍສະຖານທີ່ເຊັ່ນ: ເສົາສັນຍານມືຖື ແລະ Wi-Fi. ບໍລິການສະຖານທີ່ເຫຼົ່ານີ້ຕ້ອງຖືກເປີດນຳໃຊ້ ແລະແລະມີຂໍ້ມູນໃຫ້ກັບອຸປະກອນຂອງທ່ານ ເພື່ອໃຫ້ແອັບຯໃຊ້ໄດ້. ແອັບຯຕ່າງໆອາດໃຊ້ຂໍ້ມູນນີ້ເພື່ອລະບຸສະຖານທີ່ຢູ່ຂອງທ່ານ ແລະອາດນຳໃຊ້ແບັດເຕີຣີເພີ່ມເຕີມໄດ້."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"ສະຖານທີ່ໂດຍປະມານ (ອ້າງອີງຈາກເຄືອຂ່າຍ)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"ເຂົ້າຫາທີ່ຕັ້ງໂດຍປະມານ (ອີງໃສ່ເຄືອຂ່າຍ)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"ອະນຸຍາດໃຫ້ແອັບຯ ລະບຸສະຖານທີ່ໂດຍປະມານຂອງທ່ານ. ສະຖານທີ່ນີ້ໄດ້ຮັບມາຈາກບໍລິການສະຖານທີ່ ໂດຍອາໃສສະຖານທີ່ເຄືອຂ່າຍເຊັ່ນ: ເສົາສັນຍານ ແລະ Wi-Fi. ບໍລິການສະຖານທີ່ເຫຼົ່ານີ້ຕ້ອງຖືກເປີດໃຊ້ ແລະ ມີໃນອຸປະກອນຂອງທ່ານເພື່ອທີ່ແອັບຯຈະສາມາດໃຊ້ພວກມັນໄດ້. ແອັບຯອາດຈະໃຊ້ຄຸນສົມບັດນີ້ ເພື່ອກວດສອບສະຖານທີ່ໂດຍປະມານຂອງທ່ານ."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"ປ່ຽນການຕັ້ງຄ່າສຽງຂອງທ່ານ"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"ອະນຸຍາດໃຫ້ແອັບຯແກ້ໄຂການຕັ້ງຄ່າສຽງສ່ວນກາງ ເຊັ່ນ: ລະດັບສຽງ ແລະລຳໂພງໃດທີ່ຖືກໃຊ້ສົ່ງສຽງອອກ."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"ບັນທຶກສຽງ"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"ອະນຸຍາດໃຫ້ແອັບຯບັນທຶກສຽງດ້ວຍໄມໂຄຣໂຟນໄດ້. ການອະນຸຍາດນີ້ຈະເຮັດໃຫ້ແອັບຯ ສາມາດບັນທຶກສຽງໄດ້ຕະຫລອດເວລາ ໂດຍບໍ່ຕ້ອງຖ້າການຢືນຢັນຈາກທ່ານ."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"ການສື່ສານຂອງ SIM"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"ສົ່ງຄຳສັ່ງຫາ SIM"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"ອະນຸຍາດໃຫ້ແອັບຯສົ່ງຄຳສັ່ງຫາ SIM. ສິ່ງນີ້ອັນຕະລາຍຫຼາຍ."</string>
<string name="permlab_camera" msgid="3616391919559751192">"ຖ່າຍຮູບ ແລະວິດີໂອ"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"ອະນຸຍາດໃຫ້ແອັບຯຖ່າຍຮູບ ແລະວິດີໂອດ້ວຍກ້ອງຖ່າຍຮູບ. ການອະນຸຍາດນີ້ຈະອານຸຍາດໃຫ້ແອັບຯ ສາມາດໃຊ້ກ້ອງຖ່າຍຮູບໄດ້ຕະຫລອດເວລາ ໂດຍບໍ່ຕ້ອງຖ້າການຢືນຢັນຈາກທ່ານ."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"ອະນຸຍາດໃຫ້ແອັບພລິເຄຊັນ ດຶງຂໍ້ມູນລາຍຊື່ຂອງບັນຊີທີ່ໂທລະສັບມີ ເຊິ່ງອາດຮວມເຖິງບັນຊີທີ່ໃດໆທີ່ສ້າງຂຶ້ນ ໂດຍແອັບພລິເຄຊັນທີ່ທ່ານຕິດຕັ້ງໄວ້."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"ເບິ່ງການເຊື່ອມຕໍ່ເຄືອຂ່າຍ"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"ອະນຸຍາດໃຫ້ແອັບຯ ເບິ່ງຂໍ້ມູນກ່ຽວກັບການເຊື່ອມຕໍ່ເຄືອຂ່າຍ ເຊັ່ນວ່າມີເຄືອຂ່າຍໃດແດ່ ແລະໄດ້ເຊື່ອມຕໍ່ກັບເຄືອຂ່າຍໃດ."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"ເຂົ້າເຖິງເຄືອຂ່າຍເຕັມຮູບແບບ"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"ມີການເຂົ້າເຖິງເຄືອຂ່າຍເຕັມຮູບແບບ"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"ອະນຸຍາດໃຫ້ແອັບຯສ້າງຊັອກເກັດເຄືອຂ່າຍ ແລະໂປຣໂຕຄອນເຄືອຂ່າຍແບບກຳນົດເອງ. ໂປຣແກຣມທ່ອງເວັບ ແລະແອັບພລິເຄຊັນອື່ນໆຈະສົ່ງຂໍ້ມູນສູ່ອິນເຕີເນັດຢູ່ແລ້ວ ດັ່ງນັ້ນການອະນຸຍາດນີ້ຈຶ່ງບໍ່ຈຳເປັນຕ້ອງໃຊ້ ເພື່ອສົ່ງຂໍ້ມູນສູ່ອິນເຕີເນັດ."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"ປ່ຽນການເຊື່ອມຕໍ່ເຄືອຂ່າຍ"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"ອະນຸຍາດໃຫ້ແອັບຯປ່ຽນສະຖານະການເຊື່ອມຕໍ່ຂອງເຄືອຂ່າຍໄດ້."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"ອະນຸຍາດໃຫ້ແອັບຯຕັ້ງຄ່າ Bluetooth ໃນໂທລະສັບ ເພື່ອຊອກຫາ ແລະການເຊື່ອມຕໍ່ກັບອຸປະກອນໄຮ້ສາຍພາຍນອກ."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"ເຊື່ອມຕໍ່ ແລະຕັດການເຊື່ອມຕໍ່ຈາກ WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"ອະນຸຍາດໃຫ້ແອັບຯກວດເບິ່ງວ່າ WiMAX ຖືກເປີດນຳໃຊ້ຢູ່ບໍ່ ແລະຂໍ້ມູນກ່ຽວກັບເຄືອຂ່າຍ WiMAX ອື່ນໆທີ່ກຳລັງເຊື່ອມຕໍ່ຢູ່."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"ປ່ຽນສະຖານະ WiMAX"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"ປ່ຽນສະຖານະ WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"ອະນຸຍາດໃຫ້ແອັບຯເຊື່ອມຕໍ່ ແລະຕັດການເຊື່ອມຕໍ່ແທັບເລັດຈາກເຄືອຂ່າຍ WiMAX."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"ອະນຸຍາດໃຫ້ແອັບເຊື່ອມຕໍ່ໂທລະພາບກັບ ແລະຕັດເຊື່ອມຕໍ່ໂທລະພາບຈາກເຄືອຂ່າຍ WiMAX."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"ອະນຸຍາດໃຫ້ແອັບຯເຊື່ອມຕໍ່ ແລະຕັດການເຊື່ອມຕໍ່ຂອງໂທລະສັບຈາກເຄືອຂ່າຍ WiMax ໄດ້."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"ອະນຸຍາດໃຫ້ແອັບຯແກ້ໄຂຄ່າການວັດແທ້ໜ້າຈໍສຳຜັດ. ແອັບຯທຳມະດາບໍ່ຄວນໃຊ້."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"ເຂົ້າເຖິງໃບຮັບຮອງ DRM"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"ອະນຸຍາດໃຫ້ແອັບພລິເຄຊັນຈັດຫາ ແລະນຳໃຊ້ໃບຮັບຮອງ DRM. ແອັບຯທຳມະດາບໍ່ຄວນຕ້ອງການໃຊ້."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"ຮັບສະຖານະການໂອນຂໍ້ມູນ Android Beam"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"ຮັບສະຖານະການໂອນຂໍ້ມູນ Android Beam"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"ອະນຸຍາດໃຫ້ແອັບພລິເຄຊັນນີ້ຮັບຂໍ້ມູນກ່ຽວກັບການໂອນຂໍ້ມູນ Android Beam ໃນປັດຈຸບັນ"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"ລຶບໃບຮັບຮອງ DRM"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"ອະນຸຍາດໃຫ້ແອັບພລິເຄຊັນລຶບໃບຮັບຮອງ DRM. ແອັບຯທົ່ວໄປບໍ່ຄວນຈຳເປັນຕ້ອງໃຊ້."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"ລະຫັດ PIN ບໍ່ຖືກຕ້ອງ."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"ເພື່ອປົດລັອກ, ໃຫ້ກົດເມນູ ແລ້ວກົດ 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"ເບີໂທສຸກເສີນ"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"ບໍ່ມີບໍລິການ."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"ບໍ່ມີບໍລິການ"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"ລັອກໜ້າຈໍແລ້ວ."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"ກົດ ເມນູ ເພື່ອປົດລັອກ ຫຼື ໂທອອກຫາເບີສຸກເສີນ."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"ກົດ \"ເມນູ\" ເພື່ອປົດລັອກ."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"ເບິ່ງຄູ່ມືຜູ່ໃຊ້ ຫຼືຕິດຕໍ່ສູນບໍລິການລູກຄ້າ."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM card ຖືກລັອກ."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"ກຳລັງປົດລັອກຊິມກາດ..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"ທ່ານແຕ້ມຮູບແບບປົດລັອກບໍ່ຖືກ <xliff:g id="NUMBER_0">%d</xliff:g> ເທື່ອແລ້ວ. \n\nລອງໃໝ່ໃນອີກ <xliff:g id="NUMBER_1">%d</xliff:g> ວິນາທີ."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"ທ່ານພິມລະຫັດຜ່ານຜິດ <xliff:g id="NUMBER_0">%d</xliff:g> ເທື່ອແລ້ວ. \n\nໃຫ້ລອງໃໝ່ອີກຄັ້ງໃນອີກ <xliff:g id="NUMBER_1">%d</xliff:g> ວິນາທີ."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"ທ່ານພິມລະຫັດ PIN ຂອງທ່ານຜິດ <xliff:g id="NUMBER_0">%d</xliff:g> ເທື່ອແລ້ວ. \n\nລອງໃໝ່ໃນອີກ <xliff:g id="NUMBER_1">%d</xliff:g> ວິນາທີ."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"ທ່ານແຕ້ມຮູບແບບປົດລັອກຂອງທ່ານຜິດ <xliff:g id="NUMBER_0">%d</xliff:g> ເທື່ອແລ້ວ. ຫຼັງຈາກຄວາມພະຍາຍາມອີກ <xliff:g id="NUMBER_1">%d</xliff:g> ເທື່ອ ທ່ານຈະຖືກຖາມໃຫ້ປົດລັອກແທັບເລັດຂອງທ່ານ ດ້ວຍການເຂົ້າສູ່ລະບົບຂອງ Google.\n\n ລອງໃໝ່ອີກຄັ້ງໃນ <xliff:g id="NUMBER_2">%d</xliff:g> ວິນາທີ."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"ທ່ານແຕ້ມແບບຮູບປົດລັອກຂອງທ່ານບໍ່ຖືກຕ້ອງ <xliff:g id="NUMBER_0">%d</xliff:g> ຄັ້ງແລ້ວ. ຫຼັງຈາກລອງບໍ່ສຳເລັດອີກ <xliff:g id="NUMBER_1">%d</xliff:g> ຄັ້ງ, ທ່ານຈະຖືກບອກໃຫ້ປົດລັອກໂທລະພາບຂອງທ່ານດ້ວຍການໃຊ້ການລົງຊື່ເຂົ້າໃຊ້ Google ຂອງທ່ານ.\n\n ລອງໃໝ່ອີກໃນ <xliff:g id="NUMBER_2">%d</xliff:g> ວິນາທີ."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"ທ່ານແຕ້ມຮູບແບບປົດລັອກຜິດ <xliff:g id="NUMBER_0">%d</xliff:g> ເທື່ອແລ້ວ. ຫຼັງຈາກແຕ້ມຜິດອີກ <xliff:g id="NUMBER_1">%d</xliff:g> ເທື່ອ, ທ່ານຈະຖືກຖາມໃຫ້ປົດລັອກໂທລະສັບຂອງທ່ານ ດ້ວຍການເຂົ້າສູ່ລະບົບ Google.\n\n ລອງໃໝ່ໃນອີກ <xliff:g id="NUMBER_2">%d</xliff:g> ວິນາທີ."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"ທ່ານພະຍາຍາມປັດລັອກແທັບເລັດຜິດ <xliff:g id="NUMBER_0">%d</xliff:g> ເທື່ອແລ້ວ. ຫຼັງຈາກຜິດອີກ <xliff:g id="NUMBER_1">%d</xliff:g> ເທື່ອ, ແທັບເລັດຈະຖືກຣີເຊັດໃຫ້ເປັນແບບທີ່ມາຈາກໂຮງງານ ແລະຂໍ້ມູນທັງໝົດຈະຖືກຫາຍໄປ."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"ທ່ານພະຍາຍາມປົດລັອກໂທລະພາບບໍ່ຖືກຕ້ອງ <xliff:g id="NUMBER_0">%d</xliff:g> ຄັ້ງແລ້ວ. ຫຼັງຈາກລອງບໍ່ສຳເລັດອີກ <xliff:g id="NUMBER_1">%d</xliff:g> ຄັ້ງ, ໂທລະພາບຈະຖືກຕັ້ງຄ່າເປັນຄ່າມາດຕະຖານໂຮງງານຄືນ ແລະຂໍ້ມູນຜູ້ໃຊ້ທັງໝົດຈະເສຍໄປ."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"ທ່ານໄດ້ພະຍາຍາມປົດລັອກໂທລະສັບເປັນຈຳນວນ <xliff:g id="NUMBER_0">%d</xliff:g> ເທື່ອແລ້ວ. ຫຼັງຈາກການພະຍາຍາມອີກ <xliff:g id="NUMBER_1">%d</xliff:g> ເທື່ອແລ້ວບໍ່ສຳເລັດຜົນ, ໂທລະສັບຈະຖືກຕັ້ງຄ່າໃຫ້ເປັນຄ່າຈາກໂຮງງານ ແລະຂໍ້ມູນທັງໝົດຈະສູນຫາຍໄປ."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"ທ່ານແຕ້ມຮູບແບບປົດລັອກບໍ່ຖືກ <xliff:g id="NUMBER_0">%1$d</xliff:g> ເທື່ອແລ້ວ. \n\nລອງໃໝ່ໃນອີກ <xliff:g id="NUMBER_1">%2$d</xliff:g> ວິນາທີ."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"ທ່ານພິມລະຫັດຜ່ານຜິດ <xliff:g id="NUMBER_0">%1$d</xliff:g> ເທື່ອແລ້ວ. \n\nໃຫ້ລອງໃໝ່ອີກຄັ້ງໃນອີກ <xliff:g id="NUMBER_1">%2$d</xliff:g> ວິນາທີ."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"ທ່ານພິມລະຫັດ PIN ຂອງທ່ານຜິດ <xliff:g id="NUMBER_0">%1$d</xliff:g> ເທື່ອແລ້ວ. \n\nລອງໃໝ່ໃນອີກ <xliff:g id="NUMBER_1">%2$d</xliff:g> ວິນາທີ."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"ທ່ານແຕ້ມຮູບແບບປົດລັອກຂອງທ່ານຜິດ <xliff:g id="NUMBER_0">%1$d</xliff:g> ເທື່ອແລ້ວ. ຫຼັງຈາກຄວາມພະຍາຍາມອີກ <xliff:g id="NUMBER_1">%2$d</xliff:g> ເທື່ອ ທ່ານຈະຖືກຖາມໃຫ້ປົດລັອກແທັບເລັດຂອງທ່ານ ດ້ວຍການເຂົ້າສູ່ລະບົບຂອງ Google.\n\n ລອງໃໝ່ອີກຄັ້ງໃນ <xliff:g id="NUMBER_2">%3$d</xliff:g> ວິນາທີ."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"ທ່ານແຕ້ມແບບຮູບປົດລັອກຂອງທ່ານບໍ່ຖືກຕ້ອງ <xliff:g id="NUMBER_0">%1$d</xliff:g> ຄັ້ງແລ້ວ. ຫຼັງຈາກລອງບໍ່ສຳເລັດອີກ <xliff:g id="NUMBER_1">%2$d</xliff:g> ຄັ້ງ, ທ່ານຈະຖືກບອກໃຫ້ປົດລັອກໂທລະພາບຂອງທ່ານດ້ວຍການໃຊ້ການລົງຊື່ເຂົ້າໃຊ້ Google ຂອງທ່ານ.\n\n ລອງໃໝ່ອີກໃນ <xliff:g id="NUMBER_2">%3$d</xliff:g> ວິນາທີ."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"ທ່ານແຕ້ມຮູບແບບປົດລັອກຜິດ <xliff:g id="NUMBER_0">%1$d</xliff:g> ເທື່ອແລ້ວ. ຫຼັງຈາກແຕ້ມຜິດອີກ <xliff:g id="NUMBER_1">%2$d</xliff:g> ເທື່ອ, ທ່ານຈະຖືກຖາມໃຫ້ປົດລັອກໂທລະສັບຂອງທ່ານ ດ້ວຍການເຂົ້າສູ່ລະບົບ Google.\n\n ລອງໃໝ່ໃນອີກ <xliff:g id="NUMBER_2">%3$d</xliff:g> ວິນາທີ."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"ທ່ານພະຍາຍາມປັດລັອກແທັບເລັດຜິດ <xliff:g id="NUMBER_0">%1$d</xliff:g> ເທື່ອແລ້ວ. ຫຼັງຈາກຜິດອີກ <xliff:g id="NUMBER_1">%2$d</xliff:g> ເທື່ອ, ແທັບເລັດຈະຖືກຣີເຊັດໃຫ້ເປັນແບບທີ່ມາຈາກໂຮງງານ ແລະຂໍ້ມູນທັງໝົດຈະຖືກຫາຍໄປ."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"ທ່ານພະຍາຍາມປົດລັອກໂທລະພາບບໍ່ຖືກຕ້ອງ <xliff:g id="NUMBER_0">%1$d</xliff:g> ຄັ້ງແລ້ວ. ຫຼັງຈາກລອງບໍ່ສຳເລັດອີກ <xliff:g id="NUMBER_1">%2$d</xliff:g> ຄັ້ງ, ໂທລະພາບຈະຖືກຕັ້ງຄ່າເປັນຄ່າມາດຕະຖານໂຮງງານຄືນ ແລະຂໍ້ມູນຜູ້ໃຊ້ທັງໝົດຈະເສຍໄປ."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"ທ່ານໄດ້ພະຍາຍາມປົດລັອກໂທລະສັບເປັນຈຳນວນ <xliff:g id="NUMBER_0">%1$d</xliff:g> ເທື່ອແລ້ວ. ຫຼັງຈາກການພະຍາຍາມອີກ <xliff:g id="NUMBER_1">%2$d</xliff:g> ເທື່ອແລ້ວບໍ່ສຳເລັດຜົນ, ໂທລະສັບຈະຖືກຕັ້ງຄ່າໃຫ້ເປັນຄ່າຈາກໂຮງງານ ແລະຂໍ້ມູນທັງໝົດຈະສູນຫາຍໄປ."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"ທ່ານພະຍາຍາມປົດລັອກແທັບເລັດຜິດ <xliff:g id="NUMBER">%d</xliff:g> ເທື່ອແລ້ວ. ຕອນນີ້ແທັບເລັດຈະຖືກຣີເຊັດເປັນຄ່າຈາກໂຮງງານ."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"ທ່ານພະຍາຍາມປົດລັອກໂທລະພາບບໍ່ຖືກຕ້ອງ <xliff:g id="NUMBER">%d</xliff:g> ຄັ້ງແລ້ວ. ດຽວນີ້ໂທລະພາບຈະຖືກຕັ້ງຄ່າໃໝ່ເປັນຄ່າມາດຕະຖານໂຮງງານ."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"ທ່ານໄດ້ພະຍາຍາມປົດລັອກໂທລະສັບຜິດ <xliff:g id="NUMBER">%d</xliff:g> ເທື່ອແລ້ວ. ໂທລະສັບຈະຖືກຣີເຊັດໃຫ້ເປັນຄ່າທີ່ມາຈາກໂຮງງານ."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"ຄຳສັບ"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"ລິ້ງ"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"ເສັ້ນ"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"ການທົດສອບຈາກໂຮງງານລົ້ມເຫລວ"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"ການເຮັດ FACTORY_TEST ຮອງຮັບສະເພາະແພັກເກດທີ່ຖືກຕິດຕັ້ງໃນ /system/app ເທົ່ານັ້ນ."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"ບໍ່ພົບແພັກເກດທີ່ມີການເຮັດວຽກ FACTORY_TEST."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"ກຳລັງຟໍແມັດ…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"ບໍ່ແຊກໃສ່"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"ບໍ່ພົບກິດຈະກຳທີ່ກົງກັນ."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"ກຳນົດເສັ້ນທາງເອົ້າພຸດຂອງສື່"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"ກຳນົດຊ່ອງທາງອອກຂອງສື່"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"ອະນຸຍາດໃຫ້ແອັບພລິເຄຊັນ ກຳນົດເສັ້ນທາງເອົ້າພຸດຂອງສື່ໄປຫາອຸປະກອນພາຍນອກອື່ນໆ."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"ອ່ານເຊດຊັນການຕິດຕັ້ງ"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"ອ່ານເຊສຊັນການຕິດຕັ້ງ"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"ອະນຸຍາດໃຫ້ແອັບພລິເຄຊັນອ່ານເຊດຊັນການຕິດຕັ້ງໄດ້. ນີ້ຈະອະນຸຍາດໃຫ້ມັນເບິ່ງເຫັນລາຍລະອຽດກ່ຽວກັບການຕິດຕັ້ງແພັກເກດທີ່ເຮັດວຽກຢູ່ໄດ້."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"ຂໍຕິດຕັ້ງແພັກເກດ"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"ຂໍຕິດຕັ້ງແພັກເກດ"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"ອະນຸຍາດໃຫ້ແອັບພລິເຄຊັນຂອງການຕິດຕັ້ງແພັກເກດ."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"ແຕະສອງເທື່ອສຳລັບການຄວບຄຸມການຊູມ"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"ບໍ່ສາມາດເພີ່ມວິດເຈັດໄດ້."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"ຊື່ຜູ່ໃຊ້ ຫຼືລະຫັດຜ່ານບໍ່ຖືກຕ້ອງ."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"ລືມຊື່ຜູ່ໃຊ້ ຫຼືລະຫັດຜ່ານຂອງທ່ານບໍ່?\nໄປທີ່ "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"ກຳລັງກວດສອບບັນຊີ..."</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"ທ່ານພິມລະຫັດ PIN ຂອງທ່ານຜິດ <xliff:g id="NUMBER_0">%d</xliff:g> ເທື່ອແລ້ວ. \n\nກະລຸນາລອງໃໝ່ໃນອີກ <xliff:g id="NUMBER_1">%d</xliff:g> ວິນາທີ."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"ທ່ານພິມລະຫັດຜິດ <xliff:g id="NUMBER_0">%d</xliff:g> ເທື່ອແລ້ວ. \n\nລອງໃໝ່ໃນອີກ <xliff:g id="NUMBER_1">%d</xliff:g> ວິນາທີ."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"ທ່ານແຕ້ມຮູບແບບປົດລັອກຂອງທ່ານຜິດ <xliff:g id="NUMBER_0">%d</xliff:g> ເທື່ອແລ້ວ. \n\nກະລຸນາລອງໃໝ່ໃນອີກ <xliff:g id="NUMBER_1">%d</xliff:g> ວິນາທີ."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"ທ່ານໄດ້ພະຍາຍາມປົດລັອກແທັບເລັດບໍ່ສຳເລັດ <xliff:g id="NUMBER_0">%d</xliff:g> ເທື່ອແລ້ວ. ຫຼັງຈາກຄວາມພະຍາຍາມອີກ <xliff:g id="NUMBER_1">%d</xliff:g> ເທື່ອ ແທັບເລັດຂອງທ່ານຈະຖືກຕັ້ງ ໃຫ້ກັບໄປໃຊ້ຄ່າເລີ່ມຕົ້ນຈາກໂຮງງານຄືນໃໝ່ ແລະຂໍ້ມູນຜູ່ໃຊ້ທັງໝົດຈະສູນຫາຍໄປ."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"ທ່ານພະຍາຍາມປົດລັອກໂທລະພາບບໍ່ຖືກຕ້ອງ <xliff:g id="NUMBER_0">%d</xliff:g> ຄັ້ງແລ້ວ. ຫຼັງຈາກລອງບໍ່ສຳເລັດອີກ <xliff:g id="NUMBER_1">%d</xliff:g> ຄັ້ງ, ໂທລະພາບຈະຖືກຕັ້ງຄ່າເປັນຄ່າມາດຕະຖານໂຮງງານຄືນ ແລະຂໍ້ມູນຜູ້ໃຊ້ທັງໝົດຈະເສຍໄປ."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"ທ່ານໄດ້ພະຍາຍາມປົດລັອກໂທລະສັບບໍ່ສຳເລັດ <xliff:g id="NUMBER_0">%d</xliff:g> ເທື່ອແລ້ວ. ຫຼັງຈາກຄວາມພະຍາຍາມອີກ <xliff:g id="NUMBER_1">%d</xliff:g> ເທື່ອ ໂທລະສັບຂອງທ່ານຈະຖືກຕັ້ງ ໃຫ້ກັບໄປໃຊ້ຄ່າເລີ່ມຕົ້ນຈາກໂຮງງານຄືນໃໝ່ ແລະຂໍ້ມູນຜູ່ໃຊ້ທັງໝົດຈະສູນຫາຍໄປ."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"ທ່ານພິມລະຫັດ PIN ຂອງທ່ານຜິດ <xliff:g id="NUMBER_0">%1$d</xliff:g> ເທື່ອແລ້ວ. \n\nກະລຸນາລອງໃໝ່ໃນອີກ <xliff:g id="NUMBER_1">%2$d</xliff:g> ວິນາທີ."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"ທ່ານພິມລະຫັດຜິດ <xliff:g id="NUMBER_0">%1$d</xliff:g> ເທື່ອແລ້ວ. \n\nລອງໃໝ່ໃນອີກ <xliff:g id="NUMBER_1">%2$d</xliff:g> ວິນາທີ."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"ທ່ານແຕ້ມຮູບແບບປົດລັອກຂອງທ່ານຜິດ <xliff:g id="NUMBER_0">%1$d</xliff:g> ເທື່ອແລ້ວ. \n\nກະລຸນາລອງໃໝ່ໃນອີກ <xliff:g id="NUMBER_1">%2$d</xliff:g> ວິນາທີ."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"ທ່ານໄດ້ພະຍາຍາມປົດລັອກແທັບເລັດບໍ່ສຳເລັດ <xliff:g id="NUMBER_0">%1$d</xliff:g> ເທື່ອແລ້ວ. ຫຼັງຈາກຄວາມພະຍາຍາມອີກ <xliff:g id="NUMBER_1">%2$d</xliff:g> ເທື່ອ ແທັບເລັດຂອງທ່ານຈະຖືກຕັ້ງ ໃຫ້ກັບໄປໃຊ້ຄ່າເລີ່ມຕົ້ນຈາກໂຮງງານຄືນໃໝ່ ແລະຂໍ້ມູນຜູ່ໃຊ້ທັງໝົດຈະສູນຫາຍໄປ."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"ທ່ານພະຍາຍາມປົດລັອກໂທລະພາບບໍ່ຖືກຕ້ອງ <xliff:g id="NUMBER_0">%1$d</xliff:g> ຄັ້ງແລ້ວ. ຫຼັງຈາກລອງບໍ່ສຳເລັດອີກ <xliff:g id="NUMBER_1">%2$d</xliff:g> ຄັ້ງ, ໂທລະພາບຈະຖືກຕັ້ງຄ່າເປັນຄ່າມາດຕະຖານໂຮງງານຄືນ ແລະຂໍ້ມູນຜູ້ໃຊ້ທັງໝົດຈະເສຍໄປ."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"ທ່ານໄດ້ພະຍາຍາມປົດລັອກໂທລະສັບບໍ່ສຳເລັດ <xliff:g id="NUMBER_0">%1$d</xliff:g> ເທື່ອແລ້ວ. ຫຼັງຈາກຄວາມພະຍາຍາມອີກ <xliff:g id="NUMBER_1">%2$d</xliff:g> ເທື່ອ ໂທລະສັບຂອງທ່ານຈະຖືກຕັ້ງ ໃຫ້ກັບໄປໃຊ້ຄ່າເລີ່ມຕົ້ນຈາກໂຮງງານຄືນໃໝ່ ແລະຂໍ້ມູນຜູ່ໃຊ້ທັງໝົດຈະສູນຫາຍໄປ."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"ທ່ານໄດ້ພະຍາຍາມປົດລັອກແທັບເລັດບໍ່ສຳເລັດ <xliff:g id="NUMBER">%d</xliff:g> ເທື່ອແລ້ວ. ຕອນນີ້ແທັບເລັດຈະຖືກຕັ້ງໃຫ້ກັບໄປໃຊ້ຄ່າເລີ່ມຕົ້ນຈາກໂຮງງານ."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"ທ່ານພະຍາຍາມປົດລັອກໂທລະພາບບໍ່ຖືກຕ້ອງ <xliff:g id="NUMBER">%d</xliff:g> ຄັ້ງແລ້ວ. ດຽວນີ້ໂທລະພາບຈະຖືກຕັ້ງຄ່າໃໝ່ເປັນຄ່າມາດຕະຖານໂຮງງານ."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"ທ່ານໄດ້ພະຍາຍາມປົດລັອກໂທລະສັບບໍ່ຖືກ <xliff:g id="NUMBER">%d</xliff:g> ເທື່ອແລ້ວ. ຕອນນີ້ໂທລະສັບຈະຖືກຣີເຊັດເປັນຄ່າຈາກໂຮງງານ."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"ທ່ານແຕ້ມຮູບແບບປົດລັອກຜິດ <xliff:g id="NUMBER_0">%d</xliff:g> ເທື່ອແລ້ວ. ຫຼັງຈາກແຕ້ມຜິດອີກ <xliff:g id="NUMBER_1">%d</xliff:g> ເທື່ອ, ທ່ານຈະຖືກຖາມໃຫ້ປົດລັອກແທັບເລັດຂອງທ່ານ ດ້ວຍການເຂົ້າສູ່ລະບົບໂດຍໃຊ້ອີເມວຂອງທ່ານ.\n\n ກະລຸນາລອງໃໝ່ອີກຄັ້ງໃນອີກ <xliff:g id="NUMBER_2">%d</xliff:g> ວິນາທີ."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"ທ່ານແຕ້ມແບບຮູບປົດລັອກຂອງທ່ານບໍ່ຖືກຕ້ອງ <xliff:g id="NUMBER_0">%d</xliff:g> ຄັ້ງແລ້ວ. ຫຼັງຈາກລອງບໍ່ສຳເລັດອີກ <xliff:g id="NUMBER_1">%d</xliff:g> ຄັ້ງ, ທ່ານຈະຖືກບອກໃຫ້ປົດລັອກໂທລະພາບຂອງທ່ານດ້ວຍການໃຊ້ບັນຊີອີເມວ.\n\n ລອງອີກໃນ <xliff:g id="NUMBER_2">%d</xliff:g> ວິນາທີ."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"ທ່ານແຕ້ມຮູບແບບປົດລັອກຂອງທ່ານຜິດ <xliff:g id="NUMBER_0">%d</xliff:g> ເທື່ອແລ້ວ. ຫຼັງຈາກຄວາມພະຍາຍາມອີກ <xliff:g id="NUMBER_1">%d</xliff:g> ເທື່ອ ທ່ານຈະຖືກຖາມໃຫ້ປົດລັອກໂທລະສັບຂອງທ່ານດ້ວຍບັນຊີອີເມວ.\n\n ລອງໃໝ່ອີກຄັ້ງໃນ <xliff:g id="NUMBER_2">%d</xliff:g> ວິນາທີ."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"ທ່ານແຕ້ມຮູບແບບປົດລັອກຜິດ <xliff:g id="NUMBER_0">%1$d</xliff:g> ເທື່ອແລ້ວ. ຫຼັງຈາກແຕ້ມຜິດອີກ <xliff:g id="NUMBER_1">%2$d</xliff:g> ເທື່ອ, ທ່ານຈະຖືກຖາມໃຫ້ປົດລັອກແທັບເລັດຂອງທ່ານ ດ້ວຍການເຂົ້າສູ່ລະບົບໂດຍໃຊ້ອີເມວຂອງທ່ານ.\n\n ກະລຸນາລອງໃໝ່ອີກຄັ້ງໃນອີກ <xliff:g id="NUMBER_2">%3$d</xliff:g> ວິນາທີ."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"ທ່ານແຕ້ມແບບຮູບປົດລັອກຂອງທ່ານບໍ່ຖືກຕ້ອງ <xliff:g id="NUMBER_0">%1$d</xliff:g> ຄັ້ງແລ້ວ. ຫຼັງຈາກລອງບໍ່ສຳເລັດອີກ <xliff:g id="NUMBER_1">%2$d</xliff:g> ຄັ້ງ, ທ່ານຈະຖືກບອກໃຫ້ປົດລັອກໂທລະພາບຂອງທ່ານດ້ວຍການໃຊ້ບັນຊີອີເມວ.\n\n ລອງອີກໃນ <xliff:g id="NUMBER_2">%3$d</xliff:g> ວິນາທີ."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"ທ່ານແຕ້ມຮູບແບບປົດລັອກຂອງທ່ານຜິດ <xliff:g id="NUMBER_0">%1$d</xliff:g> ເທື່ອແລ້ວ. ຫຼັງຈາກຄວາມພະຍາຍາມອີກ <xliff:g id="NUMBER_1">%2$d</xliff:g> ເທື່ອ ທ່ານຈະຖືກຖາມໃຫ້ປົດລັອກໂທລະສັບຂອງທ່ານດ້ວຍບັນຊີອີເມວ.\n\n ລອງໃໝ່ອີກຄັ້ງໃນ <xliff:g id="NUMBER_2">%3$d</xliff:g> ວິນາທີ."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"ລຶບອອກ"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"ເພີ່ມລະດັບສຽງໃຫ້ເກີນກວ່າລະດັບທີ່ແນະນຳບໍ?\n\nການຮັບຟັງສຽງໃນລະດັບທີ່ສູງເປັນໄລຍະເວລາດົນອາດເຮັດໃຫ້ການຟັງຂອງທ່ານມີບັນຫາໄດ້."</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">ເປັນເວລາ 1 ຊມ</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"ຈົນຮອດ <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"ຈົນກ່ວາ <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (ສັນຍານເຕືອນຕໍ່ໄປ)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"ຈົນກວ່າທ່ານຈະປິດ"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"ຈົນກ່ວາທ່ານປິດຫ້າມລົບກວນ"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-lt-watch/strings.xml b/core/res/res/values-lt-watch/strings.xml
index ce154e70..e7c66fd 100644
--- a/core/res/res/values-lt-watch/strings.xml
+++ b/core/res/res/values-lt-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_0">%1$d</xliff:g> programa iš <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Jutikliai"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"pasiekti kontaktus"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"pasiekti šio laikrodžio vietą"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"pasiekti kalendorių"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"siųsti ir peržiūrėti SMS pranešimus"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"pasiekti laikrodžio nuotraukas, mediją ir failus"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"įrašyti garsą"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"fotografuoti ir filmuoti"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"skambinti ir tvarkyti telefonų skambučius"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"pasiekti jutiklių duomenis apie gyvybinius ženklus"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"būti būsenos juosta"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"pasiekti kūno jutiklius (pvz., pulso dažnio stebėjimo įrenginius)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"pasiekti tikslią vietą (nustatytą atsižvelgiant į GPS ir tinklą)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"pasiekti apytikslę vietą (nustatytą atsižvelgiant į tinklą)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"siųsti komandas į SIM kortelę"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"turėti visateisę tinklo prieigą"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"tvarkyti profilio ir įrenginio savininkus"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"keisti „WiMAX“ būseną"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"gauti „Android“ perdavimo funkcijos perkėlimo būseną"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"nukreipti medijos išvestį"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"skaityti diegimo sesijas"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"pateikti užklausą dėl diegimo paketų"</string>
</resources>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index eb2903f..3d3e333 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -256,7 +256,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Įtraukiami asmeniniai duomenys, pavyzdžiui, kredito kortelių numeriai ir slaptažodžiai."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"išjungti ar keisti būsenos juostą"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Leidžiama programai neleisti būsenos juostos arba pridėti ir pašalinti sistemos piktogramas."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"būsenos juosta"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"būti būsenos juosta"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Leidžiama programai būti būsenos juosta."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"išskleisti / sutraukti būsenos juostą"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Leidžiama programai išskleisti arba sutraukti būsenos juostą."</string>
@@ -284,7 +284,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Leidžiama programai gauti ir apdoroti WAP pranešimus. Šis leidimas apima galimybę stebėti ar ištrinti jums siunčiamus pranešimus jums jų neparodžius."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"nuskaityti vykdomas programas"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Leidžiama programai nuskaityti informaciją apie šiuo ir pastaruoju metu vykdomas užduotis. Taip programa gali atrasti informacijos, kokios programos naudojamos įrenginyje."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Tvarkyti profilio ir įrenginio savininkus"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"tvarkyti profilio ir įrenginio savininkus"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Leisti programai nustatyti profilio savininkus ir įrenginio savininką."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"pertvarkyti vykdomas programas"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Leidžiama programai perkelti užduotis į priekinį planą ir foną. Programa gali tai daryti be jūsų įsikišimo."</string>
@@ -326,7 +326,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Programai leidžiama skaityti planšetinio kompiuterio skambučių žurnalą, įskaitant duomenis apie gaunamuosius ir siunčiamuosius skambučius. Kenkėjiškos programos tai gali naudoti, kad ištrintų ar keistų jūsų skambučių žurnalą."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Programai leidžiama keisti TV skambučių žurnalą, įskaitant duomenis apie gaunamus ir siunčiamus skambučius. Taip kenkėjiškos programos gali ištrinti arba pakeisti skambučių žurnalą."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Programai leidžiama skaityti telefono skambučių žurnalą, įskaitant duomenis apie gaunamuosius ir siunčiamuosius skambučius. Kenkėjiškos programos tai gali naudoti, kad ištrintų ar keistų jūsų skambučių žurnalą."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"kūno jut. (pvz., pulso d. t.)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"pas. k. jut. (pvz., pul. dažn. st. įr.)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Programai leidžiama pasiekti duomenis, gautus iš jutiklių, stebinčių fizinę būseną, pvz., širdies ritmą."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"nuskaito kalendoriaus įvykius ir konfidencialią informaciją"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Leidžiama programai skaityti visus planšetiniame kompiuteryje išsaugotus kalendoriaus įvykius, įskaitant draugų ar bendradarbių įvykius. Dėl to programai gali būti leidžiama bendrinti ar saugoti kalendoriaus duomenis, neatsižvelgiant į konfidencialumą ar privatumą."</string>
@@ -338,15 +338,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Leidžiama programai pridėti, pašalinti ir keisti įvykius, kuriuos galite keisti telefone, įskaitant draugų ir bendradarbių įvykius. Dėl to programa gali siųsti pranešimus, kurie atrodo lyg būtų siunčiami kalendorių savininkų, arba keisti įvykius be savininko žinios."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"pasiekti papildomas vietos teikimo įrankio komandas"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Programai leidžiama pasiekti papildomas vietovės nustatymo paslaugų teikėjų komandas. Dėl to programa gali trukdyti veikti GPS ar kitiems vietovės nustatymo šaltiniams."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"tiksli vieta (pagrįsta pagal GPS ir tinklą)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"pasiekti tikslią vietą (nustatytą atsižvelgiant į GPS ir tinklą)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Leidžiama programai gauti jūsų tikslią vietą naudojant visuotinę vietos nustatymo sistemą (angl. „Global Positioning System“, GPS) arba tinklo vietos šaltinius, pvz., mobiliojo ryšio bokštus ir „Wi-Fi“. Šios vietos paslaugos turi būti įjungtos ir pasiekiamos įrenginyje, kad programa galėtų jas naudoti. Programos gali tai naudoti jūsų vietai nustatyti bei eikvoti papildomą akumuliatoriaus energiją."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"apytikslė vieta (pagrįsta pagal tinklą)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"pasiekti apytikslę vietą (nustatytą atsižvelgiant į tinklą)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Leidžiama programai gauti jūsų apytikslę vietą. Ši vieta gaunama vietos paslaugų naudojant tinklo vietos šaltinius, pvz., mobiliojo ryšio bokštus ir „Wi-Fi“. Šios vietos paslaugos turi būti įjungtos ir pasiekiamos įrenginyje, kad programa galėtų jas naudoti. Programos gali tai naudoti jūsų apytikslei vietai nustatyti."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"keisti garso nustatymus"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Leidžiama programai keisti visuotinius garso nustatymus, pvz., garsumą ir tai, kuris garsiakalbis naudojamas išvesčiai."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"įrašyti garsą"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Leidžiama programai įrašyti garsą naudojant mikrofoną. Šis leidimas suteikia galimybę programai įrašyti garsą bet kada be jūsų patvirtinimo."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"SIM kortelės ryšys"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"siųsti komandas į SIM kortelę"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Programai leidžiama siųsti komandas į SIM kortelę. Tai labai pavojinga."</string>
<string name="permlab_camera" msgid="3616391919559751192">"fotografuoti ir filmuoti"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Leidžiama programai fotografuoti ir filmuoti kamera. Šis leidimas suteikia teisę programai naudoti kamerą bet kada be jūsų patvirtinimo."</string>
@@ -384,7 +384,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Leidžiama programai gauti telefono žinomų paskyrų sąrašą. Gali būti įtrauktos visos paskyros, sukurtos įdiegtomis programomis."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"žiūrėti tinklo ryšius"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Leidžiama programai peržiūrėti informaciją apie tinklo ryšius, pvz., kurie tinklai pasiekiami ir prijungti."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"visateisė tinklo prieiga"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"turėti visateisę tinklo prieigą"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Leidžiama programai kurti tinklo programines jungtis ir naudoti tinkintus tinklo protokolus. Naršyklė ir kitos programos teikia priemones siųsti duomenis į internetą, todėl norint siųsti duomenis į internetą šis leidimas nebūtinas."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"keisti tinklo jungiamumą"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Leidžiama programai keisti tinklo jungiamumo būseną."</string>
@@ -404,7 +404,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Leidžiama programai konfigūruoti vietinį „Bluetooth“ telefoną ir atrasti bei susieti su nuotoliniais įrenginiais."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"prisijungti prie WiMAX ir atsijungti nuo jo"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Leidžiama programai nustatyti, ar įgalintas „WiMAX“, ir informaciją apie visus prijungtus tinklus."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Keisti „WiMAX“ būseną"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"keisti „WiMAX“ būseną"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Leidžia programai prijungti planšetinį kompiuterį prie „WiMAX“ ryšio tinklų ir nuo jų atjungti."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Programai leidžiama prijungti TV prie „WiMAX“ tinklų ir atjungti nuo jų."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Leidžia programai prijungti telefoną prie „WiMAX“ ryšio tinklų ir nuo jų atjungti."</string>
@@ -487,7 +487,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Leidžiama programai keisti jutiklinio ekrano kalibravimo parametrus. Neturėtų prireikti naudojant įprastas programas."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"gali pasiekti DRM sertifikatus"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Programai leidžiama pasiekti ir naudoti DRM sertifikatus. Neturėtų prireikti naudojant įprastas programas."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Gauti „Android“ perdavimo funkcijos perkėlimo būseną"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"gauti „Android“ perdavimo funkcijos perkėlimo būseną"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Programai leidžiama gauti informaciją apie dabartinius „Android“ perdavimo funkcijos perkėlimus"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"pašalinti DRM sertifikatus"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Programai leidžiama pašalinti DRM sertifikatus. Neturėtų prireikti naudojant įprastas programas."</string>
@@ -652,7 +652,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Neteisingas PIN kodas."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Jei norite atrakinti, paspauskite „Meniu“ ir 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Pagalbos numeris"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Nėra paslaugos."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Nėra paslaugos"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Ekranas užrakintas."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Paspauskite „Meniu“, kad atrakintumėte ar skambintumėte pagalbos numeriu."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Paspauskite „Meniu“, jei norite atrakinti."</string>
@@ -684,15 +684,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Žr. naudotojo vadovą arba susisiekite su klientų priežiūros tarnyba."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM kortelė užrakinta."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Atrakinama SD kortelė..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Neteisingai apibrėžėte atrakinimo modelį <xliff:g id="NUMBER_0">%d</xliff:g> k. \n\nBandykite dar kartą po <xliff:g id="NUMBER_1">%d</xliff:g> sek."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Neteisingai įvedėte slaptažodį <xliff:g id="NUMBER_0">%d</xliff:g> k. \n\nBandykite dar kartą po <xliff:g id="NUMBER_1">%d</xliff:g> sek."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"PIN kodą neteisingai įvedėte <xliff:g id="NUMBER_0">%d</xliff:g> k. \n\nBandykite dar kartą po <xliff:g id="NUMBER_1">%d</xliff:g> sek."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Neteisingai nurodėte savo atrakinimo modelį <xliff:g id="NUMBER_0">%d</xliff:g> k. Po dar <xliff:g id="NUMBER_1">%d</xliff:g> nesėkm. band. būsite paprašyti atrakinti planšetinį kompiuterį naudodami „Google“ prisijungimo duomenis.\n\n Bandykite dar kartą po <xliff:g id="NUMBER_2">%d</xliff:g> sek."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Netinkamai nupiešėte atrakinimo piešinį <xliff:g id="NUMBER_0">%d</xliff:g> k. Dar po <xliff:g id="NUMBER_1">%d</xliff:g> nesėkming. bandym. būsite paprašyti atrakinti TV prisijungdami prie „Google“.\n\n Bandykite dar kartą už <xliff:g id="NUMBER_2">%d</xliff:g> sek."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Neteisingai nurodėte savo atrakinimo modelį <xliff:g id="NUMBER_0">%d</xliff:g> k. Po dar <xliff:g id="NUMBER_1">%d</xliff:g> nesėkm. band. būsite paprašyti atrakinti telefoną naudodami „Google“ prisijungimo duomenis.\n\n Bandykite dar kartą po <xliff:g id="NUMBER_2">%d</xliff:g> sek."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"<xliff:g id="NUMBER_0">%d</xliff:g> kart. bandėte netinkamai atrakinti planšetinį kompiuterį. Po dar <xliff:g id="NUMBER_1">%d</xliff:g> nesėkm. band. planšetiniame kompiuteryje bus iš naujo nustatyti numatytieji gamyklos nustatymai ir bus prarasti visi naudotojo duomenys."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Netinkamai bandėte atrakinti TV <xliff:g id="NUMBER_0">%d</xliff:g> k. Dar po <xliff:g id="NUMBER_1">%d</xliff:g> nesėkming. bandym. bus atkurti gamykliniai TV nustatymai, o visi naudotojo duomenys bus prarasti."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"<xliff:g id="NUMBER_0">%d</xliff:g> kart. bandėte netinkamai atrakinti telefoną. Po dar <xliff:g id="NUMBER_1">%d</xliff:g> nesėkm. band. telefone bus iš naujo nustatyti numatytieji gamyklos nustatymai ir bus prarasti visi naudotojo duomenys."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Neteisingai apibrėžėte atrakinimo modelį <xliff:g id="NUMBER_0">%1$d</xliff:g> k. \n\nBandykite dar kartą po <xliff:g id="NUMBER_1">%2$d</xliff:g> sek."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Neteisingai įvedėte slaptažodį <xliff:g id="NUMBER_0">%1$d</xliff:g> k. \n\nBandykite dar kartą po <xliff:g id="NUMBER_1">%2$d</xliff:g> sek."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"PIN kodą neteisingai įvedėte <xliff:g id="NUMBER_0">%1$d</xliff:g> k. \n\nBandykite dar kartą po <xliff:g id="NUMBER_1">%2$d</xliff:g> sek."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Neteisingai nurodėte savo atrakinimo modelį <xliff:g id="NUMBER_0">%1$d</xliff:g> k. Po dar <xliff:g id="NUMBER_1">%2$d</xliff:g> nesėkm. band. būsite paprašyti atrakinti planšetinį kompiuterį naudodami „Google“ prisijungimo duomenis.\n\n Bandykite dar kartą po <xliff:g id="NUMBER_2">%3$d</xliff:g> sek."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Netinkamai nupiešėte atrakinimo piešinį <xliff:g id="NUMBER_0">%1$d</xliff:g> k. Dar po <xliff:g id="NUMBER_1">%2$d</xliff:g> nesėkming. bandym. būsite paprašyti atrakinti TV prisijungdami prie „Google“.\n\n Bandykite dar kartą už <xliff:g id="NUMBER_2">%3$d</xliff:g> sek."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Neteisingai nurodėte savo atrakinimo modelį <xliff:g id="NUMBER_0">%1$d</xliff:g> k. Po dar <xliff:g id="NUMBER_1">%2$d</xliff:g> nesėkm. band. būsite paprašyti atrakinti telefoną naudodami „Google“ prisijungimo duomenis.\n\n Bandykite dar kartą po <xliff:g id="NUMBER_2">%3$d</xliff:g> sek."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"<xliff:g id="NUMBER_0">%1$d</xliff:g> kart. bandėte netinkamai atrakinti planšetinį kompiuterį. Po dar <xliff:g id="NUMBER_1">%2$d</xliff:g> nesėkm. band. planšetiniame kompiuteryje bus iš naujo nustatyti numatytieji gamyklos nustatymai ir bus prarasti visi naudotojo duomenys."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Netinkamai bandėte atrakinti TV <xliff:g id="NUMBER_0">%1$d</xliff:g> k. Dar po <xliff:g id="NUMBER_1">%2$d</xliff:g> nesėkming. bandym. bus atkurti gamykliniai TV nustatymai, o visi naudotojo duomenys bus prarasti."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"<xliff:g id="NUMBER_0">%1$d</xliff:g> kart. bandėte netinkamai atrakinti telefoną. Po dar <xliff:g id="NUMBER_1">%2$d</xliff:g> nesėkm. band. telefone bus iš naujo nustatyti numatytieji gamyklos nustatymai ir bus prarasti visi naudotojo duomenys."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"<xliff:g id="NUMBER">%d</xliff:g> kart. bandėte netinkamai atrakinti planšetinį kompiuterį. Planšetinis kompiuteris bus iš naujo nustatytas į numatytuosius gamyklos nustatymus."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Netinkamai bandėte atrakinti TV <xliff:g id="NUMBER">%d</xliff:g> k. Dabar bus atkurti gamykliniai TV nustatymai."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"<xliff:g id="NUMBER">%d</xliff:g> kart. bandėte netinkamai atrakinti telefoną. Telefonas bus iš naujo nustatytas į numatytuosius gamyklos nustatymus."</string>
@@ -744,8 +744,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"žodis"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"nuoroda"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"eilutė"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Gamyklos bandymas nepavyko"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Veiksmas FACTORY_TEST palaikomas tik paketuose, įdiegtuose /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Nerasta paketo, kuris teiktų FACTORY_TEST veiksmą."</string>
@@ -1087,11 +1085,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Formatuojama…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Neįdėta"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Nerasta atitinkančios veiklos."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Medijos išvesties nukreipimas"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"nukreipti medijos išvestį"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Leidžiama programai nukreipti medijos išvestį į kitus išorinius įrenginius."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Skaityti diegimo seansus"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"skaityti diegimo sesijas"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Leidžiama programai skaityti diegimo seansus. Leidžiama peržiūrėti išsamią aktyvių paketų diegimo informaciją."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Pateikti užklausą dėl paketų diegimo"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"pateikti užklausą dėl diegimo paketų"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Programai leidžiama pateikti užklausą dėl paketų diegimo."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Dukart palieskite, kad valdytumėte mastelio keitimą"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Nepavyko pridėti."</string>
@@ -1301,18 +1299,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Netinkamas naudotojo vardas ar slaptažodis."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Pamiršote naudotojo vardą ar slaptažodį?\nApsilankykite šiuo adresu: "<b>"google.com/accounts/recovery"</b></string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Tikrinama paskyra…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"PIN kodą netinkamai įvedėte <xliff:g id="NUMBER_0">%d</xliff:g> k. \n\nBandykite dar kartą po <xliff:g id="NUMBER_1">%d</xliff:g> sek."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Neteisingai įvedėte slaptažodį <xliff:g id="NUMBER_0">%d</xliff:g> k. \n\nBandykite dar kartą po <xliff:g id="NUMBER_1">%d</xliff:g> sek."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Netinkamai nupiešėte atrakinimo piešinį <xliff:g id="NUMBER_0">%d</xliff:g> k. \n\nBandykite dar kartą po <xliff:g id="NUMBER_1">%d</xliff:g> sek."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"<xliff:g id="NUMBER_0">%d</xliff:g> k. bandėte netinkamai atrakinti planšetinį kompiuterį. Po dar <xliff:g id="NUMBER_1">%d</xliff:g> nesėkm. band. planšetiniame kompiuteryje bus iš naujo nustatyti numatytieji gamyklos nustatymai ir bus prarasti visi naudotojo duomenys."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Netinkamai bandėte atrakinti TV <xliff:g id="NUMBER_0">%d</xliff:g> k. Dar po <xliff:g id="NUMBER_1">%d</xliff:g> nesėkming. bandym. bus atkurti gamykliniai TV nustatymai, o visi naudotojo duomenys bus prarasti."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"<xliff:g id="NUMBER_0">%d</xliff:g> k. bandėte netinkamai atrakinti telefoną. Po dar <xliff:g id="NUMBER_1">%d</xliff:g> nesėkm. band. telefone bus iš naujo nustatyti numatytieji gamyklos nustatymai ir bus prarasti visi naudotojo duomenys."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"PIN kodą netinkamai įvedėte <xliff:g id="NUMBER_0">%1$d</xliff:g> k. \n\nBandykite dar kartą po <xliff:g id="NUMBER_1">%2$d</xliff:g> sek."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Neteisingai įvedėte slaptažodį <xliff:g id="NUMBER_0">%1$d</xliff:g> k. \n\nBandykite dar kartą po <xliff:g id="NUMBER_1">%2$d</xliff:g> sek."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Netinkamai nupiešėte atrakinimo piešinį <xliff:g id="NUMBER_0">%1$d</xliff:g> k. \n\nBandykite dar kartą po <xliff:g id="NUMBER_1">%2$d</xliff:g> sek."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"<xliff:g id="NUMBER_0">%1$d</xliff:g> k. bandėte netinkamai atrakinti planšetinį kompiuterį. Po dar <xliff:g id="NUMBER_1">%2$d</xliff:g> nesėkm. band. planšetiniame kompiuteryje bus iš naujo nustatyti numatytieji gamyklos nustatymai ir bus prarasti visi naudotojo duomenys."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Netinkamai bandėte atrakinti TV <xliff:g id="NUMBER_0">%1$d</xliff:g> k. Dar po <xliff:g id="NUMBER_1">%2$d</xliff:g> nesėkming. bandym. bus atkurti gamykliniai TV nustatymai, o visi naudotojo duomenys bus prarasti."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"<xliff:g id="NUMBER_0">%1$d</xliff:g> k. bandėte netinkamai atrakinti telefoną. Po dar <xliff:g id="NUMBER_1">%2$d</xliff:g> nesėkm. band. telefone bus iš naujo nustatyti numatytieji gamyklos nustatymai ir bus prarasti visi naudotojo duomenys."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"<xliff:g id="NUMBER">%d</xliff:g> k. bandėte netinkamai atrakinti planšetinį kompiuterį. Planšetiniame kompiuteryje bus iš naujo nustatyti numatytieji gamyklos nustatymai."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Netinkamai bandėte atrakinti TV <xliff:g id="NUMBER">%d</xliff:g> k. Dabar bus atkurti gamykliniai TV nustatymai."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"<xliff:g id="NUMBER">%d</xliff:g> k. bandėte netinkamai atrakinti telefoną. Telefone bus iš naujo nustatyti numatytieji gamyklos nustatymai."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Netinkamai nupiešėte atrakinimo piešinį <xliff:g id="NUMBER_0">%d</xliff:g> k. Po dar <xliff:g id="NUMBER_1">%d</xliff:g> nesėkm. band. būsite paprašyti atrakinti planšetinį kompiuterį naudodami „Google“ prisijungimo duomenis.\n\n Bandykite dar kartą po <xliff:g id="NUMBER_2">%d</xliff:g> sek."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Netinkamai nupiešėte atrakinimo piešinį <xliff:g id="NUMBER_0">%d</xliff:g> k. Dar po <xliff:g id="NUMBER_1">%d</xliff:g> nesėkming. bandym. būsite paprašyti atrakinti TV naudodami el. pašto paskyrą.\n\n Bandykite dar kartą už <xliff:g id="NUMBER_2">%d</xliff:g> sek."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Netinkamai nupiešėte atrakinimo piešinį <xliff:g id="NUMBER_0">%d</xliff:g> k. Po dar <xliff:g id="NUMBER_1">%d</xliff:g> nesėkm. band. būsite paprašyti atrakinti telefoną naudodami „Google“ prisijungimo duomenis.\n\n Bandykite dar kartą po <xliff:g id="NUMBER_2">%d</xliff:g> sek."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Netinkamai nupiešėte atrakinimo piešinį <xliff:g id="NUMBER_0">%1$d</xliff:g> k. Po dar <xliff:g id="NUMBER_1">%2$d</xliff:g> nesėkm. band. būsite paprašyti atrakinti planšetinį kompiuterį naudodami „Google“ prisijungimo duomenis.\n\n Bandykite dar kartą po <xliff:g id="NUMBER_2">%3$d</xliff:g> sek."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Netinkamai nupiešėte atrakinimo piešinį <xliff:g id="NUMBER_0">%1$d</xliff:g> k. Dar po <xliff:g id="NUMBER_1">%2$d</xliff:g> nesėkming. bandym. būsite paprašyti atrakinti TV naudodami el. pašto paskyrą.\n\n Bandykite dar kartą už <xliff:g id="NUMBER_2">%3$d</xliff:g> sek."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Netinkamai nupiešėte atrakinimo piešinį <xliff:g id="NUMBER_0">%1$d</xliff:g> k. Po dar <xliff:g id="NUMBER_1">%2$d</xliff:g> nesėkm. band. būsite paprašyti atrakinti telefoną naudodami „Google“ prisijungimo duomenis.\n\n Bandykite dar kartą po <xliff:g id="NUMBER_2">%3$d</xliff:g> sek."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Pašalinti"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Padidinti garsą daugiau nei rekomenduojamas lygis?\n\nIlgai klausydami dideliu garsu galite pažeisti klausą."</string>
@@ -1504,6 +1502,7 @@
<item quantity="other">%d val.</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Iki <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Iki <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (kitas signalas)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Kol išjungsite"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Kol neišjungsite režimo „Netrukdyti“"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-lv-watch/strings.xml b/core/res/res/values-lv-watch/strings.xml
index c44677f..eb3c9b0 100644
--- a/core/res/res/values-lv-watch/strings.xml
+++ b/core/res/res/values-lv-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_0">%1$d</xliff:g>. lietotne no <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Sensori"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"Piekļūt jūsu kontaktpersonu datiem"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"Piekļūt šī pulksteņa atrašanās vietas datiem"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"Piekļūt jūsu kalendāram"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"Sūtīt un skatīt īsziņas"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"Piekļūt fotoattēliem, multivides saturam un failiem pulkstenī"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"Ierakstīt audio"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"Uzņemt attēlus un ierakstīt videoklipus"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"Veikt un pārvaldīt tālruņa zvanus"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"Piekļūt sensoru datiem par jūsu veselības rādījumiem"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"Būt par statusa joslu"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"Piekļūt ķermeņa sensoriem (piemēram, sirdsdarbības monitoriem)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"Noteikt precīzu atrašanās vietu (izmantojot GPS un tīklu)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"Noteikt aptuveno atrašanās vietu (izmantojot tīklu)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"Sūtīt komandas SIM kartei"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"Pilnīga piekļuve tīklam"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"Pārvaldīt profilus un ierīces īpašniekus"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"Mainīt WiMAX statusu"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"Saņemt Android Beam pārsūtīšanas statusu"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"Maršrutēt multivides datu izeju"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"Lasīt instalēšanas sesijas"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"Pieprasīt pakotņu instalēšanu"</string>
</resources>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 7f10900..f233737 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -255,7 +255,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Ietver personas datus, piemēram, kredītkartes numurus un paroles."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"atspējot vai pārveidot statusa joslu"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Ļauj lietotnei atspējot statusa joslu vai pievienot un noņemt sistēmas ikonas."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"statusa josla"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"Būt par statusa joslu"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Ļauj lietotnei būt par statusa joslu."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"izvērst/sakļaut statusa joslu"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Ļauj lietotnei izvērst vai sakļaut statusa joslu."</string>
@@ -283,7 +283,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Ļauj lietotnei saņemt un apstrādāt WAP ziņojumus. Šī atļauja ietver iespēju pārraudzīt vai dzēst jums nosūtītos ziņojumus, neparādot tos jums."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"izgūt izmantotās lietotnes"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Ļauj lietotnei izgūt informāciju par pašreiz un nesen darbinātajiem uzdevumiem. Tādējādi lietotne var atklāt informāciju par ierīcē izmantotajām lietojumprogrammām."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Profilu un ierīces īpašnieku pārvaldība"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"Pārvaldīt profilus un ierīces īpašniekus"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Atļaut lietotnēm iestatīt profilu īpašniekus un ierīces īpašnieku."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"pārkārtot izmantotās lietotnes"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Ļauj lietotnei pārvietot uzdevumus priekšplānā un fonā. Lietotne var to izdarīt bez jūsu apstiprinājuma."</string>
@@ -325,7 +325,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Ļauj lietotnei pārveidot planšetdatora zvanu žurnālu, tostarp ienākošo un izejošo zvanu datus. Ļaunprātīgas lietotnes var to izmantot, lai dzēstu vai pārveidotu savu zvanu žurnālu."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Ļauj lietotnei pārveidot televizora zvanu žurnālu, tostarp ienākošo un izejošo zvanu datus. Ļaunprātīgas lietotnes var to izmantot, lai dzēstu vai pārveidotu zvanu žurnālu."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Ļauj lietotnei pārveidot tālruņa zvanu žurnālu, tostarp ienākošo un izejošo zvanu datus. Ļaunprātīgas lietotnes var to izmantot, lai dzēstu vai pārveidotu savu zvanu žurnālu."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"ķermeņa sensori (piemēram, sirdsdarbības monitori)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"Piekļūt ķermeņa sensoriem (piemēram, sirdsdarbības monitoriem)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Ļauj lietotnei piekļūt to sensoru datiem, kuri pārrauga jūsu fizisko stāvokli (piemēram, sirdsdarbības ātrumu)."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"lasīt kalendāra pasākumus un konfidenciālu informāciju"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Ļauj lietotnei lasīt visus planšetdatorā saglabātos kalendāra notikumus, tostarp draugu vai kolēģu notikumus. Tas var ļaut lietotnei kopīgot vai saglabāt jūsu kalendāra datus, neraugoties uz to konfidencialitāti vai sensitivitāti."</string>
@@ -337,15 +337,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Ļauj lietotnei pievienot, noņemt, mainīt notikumus, kurus varat pārveidot tālrunī, tostarp draugu vai kolēģu notikumus. Tas var ļaut lietotnei sūtīt ziņojumus, norādot, ka tos sūta kalendāru īpašnieki, vai pārveidot notikumus bez īpašnieka atļaujas."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"piekļūt atrašanās vietas nodrošinātāja papildu komandām"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Ļauj lietotnei piekļūt papildu atrašanās vietas noteikšanas nodrošinātāju komandām. Tas var ļaut lietotnei traucēt GPS vai citu atrašanās vietas noteikšanas avotu darbību."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"precīza atrašanās vieta (GPS un tīklā)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"Piekļūt precīzai atrašanās vietai (izmantojot GPS un tīklu)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Ļauj lietotnei iegūt precīzu informāciju par jūsu atrašanās vietu, izmantojot globālo pozicionēšanas sistēmu (GPS) vai tīkla atrašanās vietas pakalpojumus, piemēram, mobilo sakaru torņus un Wi-Fi. Lai lietotne varētu izmantot šos atrašanās vietas pakalpojumus, ierīcē tiem ir jābūt ieslēgtiem un pieejamiem. Lietotnes var izmantot šo atļauju, lai noteiktu jūsu atrašanās vietu, un var patērēt papildu akumulatora enerģiju."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"aptuvena atrašanās vieta (tīklā)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"Piekļūt aptuvenai atrašanās vietai (izmantojot tīklu)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Ļauj lietotnei iegūt informāciju par aptuvenu jūsu atrašanās vietu. Tā tiek noteikta atrašanās vietas pakalpojumos, izmantojot tīkla atrašanās vietas avotus, kā arī mobilo sakaru torņus un Wi-Fi. Lai lietotne varētu izmantot šos atrašanās vietas pakalpojumus, ierīcē tiem ir jābūt ieslēgtiem un pieejamiem. Lietotnes var izmantot šo atļauju, lai noteiktu aptuvenu jūsu atrašanās vietu."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"mainīt audio iestatījumus"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Ļauj lietotnei mainīt globālos audio iestatījumus, piemēram, skaļumu un izejai izmantoto skaļruni."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"ierakstīt audio"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Ļauj lietotnei ierakstīt audio, izmantojot mikrofonu. Šī atļauja ļauj lietotnei ierakstīt audio jebkurā brīdī bez jūsu apstiprinājuma."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"SIM saziņa"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"Sūtīt komandas SIM kartei"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Ļauj lietotnei sūtīt komandas uz SIM karti. Tas ir ļoti bīstami!"</string>
<string name="permlab_camera" msgid="3616391919559751192">"uzņemt attēlus un videoklipus"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Ļauj lietotnei uzņemt attēlus un videoklipus ar kameru. Ar šo atļauju lietotne var jebkurā brīdī izmantot kameru bez jūsu apstiprinājuma."</string>
@@ -383,7 +383,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Ļauj lietotnei iegūt tālrunim zināmo kontu sarakstu. Tas var ietvert jebkādus kontus, ko izveidojušas instalētās lietojumprogrammas."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"skatīt tīkla savienojumus"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Ļauj lietotnei skatīt informāciju par tīkla savienojumiem, piemēram, par to, kādi tīkli pastāv un ar kuriem tīkliem ir izveidots savienojums."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"pilnīga piekļuve tīklam"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"Iegūt pilnu piekļuvi tīklam"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Ļauj lietotnei izveidot tīkla ligzdas un izmantot pielāgotus tīkla protokolus. Pārlūkprogramma un citas lietojumprogrammas nodrošina līdzekļus, kas nepieciešami, lai sūtītu datus internetā, tāpēc šī atļauja nav nepieciešama, lai sūtītu datus internetā."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"mainīt tīkla savienojamību"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Ļauj lietotnei mainīt tīkla savienojamības statusu."</string>
@@ -403,7 +403,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Ļauj lietotnei konfigurēt vietējo Bluetooth tālruni, kā arī atklāt attālas ierīces un savienot tās pārī."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"WiMAX savienojuma izveide un pārtraukšana"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Ļauj lietotnei noteikt, vai WiMAX ir iespējots, un sniedz informāciju par visiem WiMAX tīkliem, ar kuriem ir izveidots savienojums."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX statusa mainīšana"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"WiMAX statusa mainīšana"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Ļauj lietotnei izveidot un pārtraukt planšetdatora savienojumu ar WiMAX tīkliem."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Ļauj lietotnei pievienot televizoru WiMAX tīkliem un atvienot no tiem."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Ļauj lietotnei izveidot un pārtraukt tālruņa savienojumu ar WiMAX tīkliem."</string>
@@ -486,7 +486,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Ļauj lietotnei pārveidot skārienekrāna kalibrēšanas parametrus. Parastām lietotnēm šī atļauja nekad nav nepieciešama."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"Piekļuve digitālā satura tiesību pārvaldības sertifikātiem"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Ļauj lietojumprogrammai nodrošināt un izmantot digitālā satura tiesību pārvaldības sertifikātus. Parastām lietotnēm šī atļauja nekad nav nepieciešama."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Saņemt Android Beam pārsūtīšanas statusu"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"Saņemt Android Beam pārsūtīšanas statusu"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Ļauj šai lietojumprogrammai saņemt informāciju par pašreizēju Android Beam pārsūtīšanu"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"noņemt DRM sertifikātus"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Ļauj lietojumprogrammai noņemt DRM sertifikātus. Parastās lietotnēs tas nebūs nepieciešams."</string>
@@ -651,7 +651,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PIN kods nav pareizs."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Lai atbloķētu, nospiediet Izvēlne, pēc tam 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Ārkārtas numurs"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Nav pakalpojuma."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Nav pakalpojuma"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Ekrāns ir bloķēts."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Nospiediet Izvēlne, lai atbloķētu, vai veiciet ārkārtas zvanu."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Lai atbloķētu, nospiediet vienumu Izvēlne."</string>
@@ -683,15 +683,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Skatiet lietotāja rokasgrāmatu vai sazinieties ar klientu apkalpošanas dienestu."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM karte ir bloķēta."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Notiek SIM kartes atbloķēšana..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Atbloķēšanas kombinācija tika nepareizi uzzīmēta <xliff:g id="NUMBER_0">%d</xliff:g> reizes.\n\nMēģiniet vēlreiz pēc <xliff:g id="NUMBER_1">%d</xliff:g> sekundēm."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Jūs esat ievadījis nepareizu paroli <xliff:g id="NUMBER_0">%d</xliff:g> reizes.\n\nMēģiniet vēlreiz pēc <xliff:g id="NUMBER_1">%d</xliff:g> sekundēm."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Jūs esat ievadījis nepareizu PIN <xliff:g id="NUMBER_0">%d</xliff:g> reizes.\n\nMēģiniet vēlreiz pēc <xliff:g id="NUMBER_1">%d</xliff:g> sekundēm."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Atbloķēšanas kombinācija tika nepareizi uzzīmēta <xliff:g id="NUMBER_0">%d</xliff:g> reizes. Pēc vēl <xliff:g id="NUMBER_1">%d</xliff:g> neveiksmīgiem mēģinājumiem planšetdators būs jāatbloķē, izmantojot pierakstīšanos Google kontā.\n\nMēģiniet vēlreiz pēc <xliff:g id="NUMBER_2">%d</xliff:g> sekundēm."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Grafiskā atslēga tika nepareizi norādīta <xliff:g id="NUMBER_0">%d</xliff:g> reizi(-es). Ja nepareizi norādīsiet to vēl <xliff:g id="NUMBER_1">%d</xliff:g> reizi(-es), televizors būs jāatbloķē, pierakstoties Google kontā.\n\nMēģiniet vēlreiz pēc <xliff:g id="NUMBER_2">%d</xliff:g> sekundes(-ēm)."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Atbloķēšanas kombinācija tika nepareizi uzzīmēta <xliff:g id="NUMBER_0">%d</xliff:g> reizes. Pēc vēl <xliff:g id="NUMBER_1">%d</xliff:g> neveiksmīgiem mēģinājumiem tālrunis būs jāatbloķē, izmantojot pierakstīšanos Google kontā.\n\nMēģiniet vēlreiz pēc <xliff:g id="NUMBER_2">%d</xliff:g> sekundēm."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Jūs nepareizi veicāt planšetdatora atbloķēšanu <xliff:g id="NUMBER_0">%d</xliff:g> reizes. Pēc vēl <xliff:g id="NUMBER_1">%d</xliff:g> neveiksmīga(-iem) mēģinājuma(-iem) planšetdatorā tiks atiestatīti rūpnīcas noklusējuma iestatījumi, un lietotāja dati tiks zaudēti."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Jūs <xliff:g id="NUMBER_0">%d</xliff:g> reizi(-es) nesekmīgi mēģinājāt atbloķēt televizoru. Pēc vēl <xliff:g id="NUMBER_1">%d</xliff:g> nesekmīga(-iem) mēģinājuma(-iem) televizorā tiks veikta rūpnīcas datu atiestatīšana un visi lietotāja dati tiks zaudēti."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Jūs nepareizi veicāt tālruņa atbloķēšanu <xliff:g id="NUMBER_0">%d</xliff:g> reizes. Pēc vēl <xliff:g id="NUMBER_1">%d</xliff:g> neveiksmīga(-iem) mēģinājuma(-iem) tālrunī tiks atiestatīti rūpnīcas noklusējuma iestatījumi, un lietotāja dati tiks zaudēti."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Atbloķēšanas kombinācija tika nepareizi uzzīmēta <xliff:g id="NUMBER_0">%1$d</xliff:g> reizes.\n\nMēģiniet vēlreiz pēc <xliff:g id="NUMBER_1">%2$d</xliff:g> sekundēm."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Jūs esat ievadījis nepareizu paroli <xliff:g id="NUMBER_0">%1$d</xliff:g> reizes.\n\nMēģiniet vēlreiz pēc <xliff:g id="NUMBER_1">%2$d</xliff:g> sekundēm."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Jūs esat ievadījis nepareizu PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> reizes.\n\nMēģiniet vēlreiz pēc <xliff:g id="NUMBER_1">%2$d</xliff:g> sekundēm."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Atbloķēšanas kombinācija tika nepareizi uzzīmēta <xliff:g id="NUMBER_0">%1$d</xliff:g> reizes. Pēc vēl <xliff:g id="NUMBER_1">%2$d</xliff:g> neveiksmīgiem mēģinājumiem planšetdators būs jāatbloķē, izmantojot pierakstīšanos Google kontā.\n\nMēģiniet vēlreiz pēc <xliff:g id="NUMBER_2">%3$d</xliff:g> sekundēm."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Grafiskā atslēga tika nepareizi norādīta <xliff:g id="NUMBER_0">%1$d</xliff:g> reizi(-es). Ja nepareizi norādīsiet to vēl <xliff:g id="NUMBER_1">%2$d</xliff:g> reizi(-es), televizors būs jāatbloķē, pierakstoties Google kontā.\n\nMēģiniet vēlreiz pēc <xliff:g id="NUMBER_2">%3$d</xliff:g> sekundes(-ēm)."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Atbloķēšanas kombinācija tika nepareizi uzzīmēta <xliff:g id="NUMBER_0">%1$d</xliff:g> reizes. Pēc vēl <xliff:g id="NUMBER_1">%2$d</xliff:g> neveiksmīgiem mēģinājumiem tālrunis būs jāatbloķē, izmantojot pierakstīšanos Google kontā.\n\nMēģiniet vēlreiz pēc <xliff:g id="NUMBER_2">%3$d</xliff:g> sekundēm."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Jūs nepareizi veicāt planšetdatora atbloķēšanu <xliff:g id="NUMBER_0">%1$d</xliff:g> reizes. Pēc vēl <xliff:g id="NUMBER_1">%2$d</xliff:g> neveiksmīga(-iem) mēģinājuma(-iem) planšetdatorā tiks atiestatīti rūpnīcas noklusējuma iestatījumi, un lietotāja dati tiks zaudēti."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Jūs <xliff:g id="NUMBER_0">%1$d</xliff:g> reizi(-es) nesekmīgi mēģinājāt atbloķēt televizoru. Pēc vēl <xliff:g id="NUMBER_1">%2$d</xliff:g> nesekmīga(-iem) mēģinājuma(-iem) televizorā tiks veikta rūpnīcas datu atiestatīšana un visi lietotāja dati tiks zaudēti."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Jūs nepareizi veicāt tālruņa atbloķēšanu <xliff:g id="NUMBER_0">%1$d</xliff:g> reizes. Pēc vēl <xliff:g id="NUMBER_1">%2$d</xliff:g> neveiksmīga(-iem) mēģinājuma(-iem) tālrunī tiks atiestatīti rūpnīcas noklusējuma iestatījumi, un lietotāja dati tiks zaudēti."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Jūs nepareizi veicāt planšetdatora atbloķēšanu <xliff:g id="NUMBER">%d</xliff:g> reizes. Planšetdatorā tiks atiestatīti rūpnīcas noklusējuma iestatījumi."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Jūs <xliff:g id="NUMBER">%d</xliff:g> reizi(-es) nesekmīgi mēģinājāt atbloķēt televizoru. Tagad tiks veikta rūpnīcas datu atiestatīšana."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Jūs nepareizi veicāt tālruņa atbloķēšanu <xliff:g id="NUMBER">%d</xliff:g> reizes. Tālrunī tiks atiestatīti rūpnīcas noklusējuma iestatījumi."</string>
@@ -743,8 +743,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"vārds"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"saite"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"rindiņa"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g> (<xliff:g id="AMPM">%P</xliff:g>)"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g> (<xliff:g id="AMPM">%p</xliff:g>)"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Rūpnīcas pārbaude neizdevās"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Darbība FACTORY_TEST tiek atbalstīta tikai pakotnēm, kas ir instalētas šeit: /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Netika atrasts neviena pakotne, kas nodrošina darbību FACTORY_TEST."</string>
@@ -1080,11 +1078,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Notiek formatēšana…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Nav ievietots"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Nav atrasta neviena atbilstoša darbība."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Multivides datu izejas maršrutēšana"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"Maršrutēt multivides datu izeju"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Ļauj lietojumprogrammai maršrutēt multivides datu izeju uz citām ārējām ierīcēm."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Instalēšanas sesiju lasīšana"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"Lasīt instalēšanas sesijas"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Ļauj lietojumprogrammai lasīt instalēšanas sesijas. Tādējādi lietojumprogrammai ir pieejama informācija par aktīvajām pakotņu instalācijām."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Pieprasīt pakotņu instalēšanu"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"Pieprasīt pakotņu instalēšanu"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Ļauj lietojumprogrammai pieprasīt pakotņu instalēšanu."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Pieskarieties divreiz, lai kontrolētu tālummaiņu."</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Nevarēja pievienot logrīku."</string>
@@ -1293,18 +1291,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Nederīgs lietotājvārds vai parole."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Vai aizmirsāt lietotājvārdu vai paroli?\nApmeklējiet vietni "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Notiek konta pārbaude…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Jūs nepareizi ievadījāt PIN <xliff:g id="NUMBER_0">%d</xliff:g> reizes.\n\nMēģiniet vēlreiz pēc <xliff:g id="NUMBER_1">%d</xliff:g> sekundēm."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Jūs nepareizi ievadījāt paroli <xliff:g id="NUMBER_0">%d</xliff:g> reizes.\n\nMēģiniet vēlreiz pēc <xliff:g id="NUMBER_1">%d</xliff:g> sekundēm."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Jūs nepareizi norādījāt atbloķēšanas kombināciju <xliff:g id="NUMBER_0">%d</xliff:g> reizes.\n\nMēģiniet vēlreiz pēc <xliff:g id="NUMBER_1">%d</xliff:g> sekundēm."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Jūs nepareizi veicāt planšetdatora atbloķēšanu <xliff:g id="NUMBER_0">%d</xliff:g> reizes. Pēc vēl <xliff:g id="NUMBER_1">%d</xliff:g> neveiksmīgiem mēģinājumiem planšetdatorā tiks atiestatīti rūpnīcas noklusējuma iestatījumi un lietotāja dati tiks zaudēti."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Jūs <xliff:g id="NUMBER_0">%d</xliff:g> reizi(-es) nesekmīgi mēģinājāt atbloķēt televizoru. Pēc vēl <xliff:g id="NUMBER_1">%d</xliff:g> nesekmīga(-iem) mēģinājuma(-iem) televizorā tiks veikta rūpnīcas datu atiestatīšana un visi lietotāja dati tiks zaudēti."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Jūs nepareizi veicāt tālruņa atbloķēšanu <xliff:g id="NUMBER_0">%d</xliff:g> reizes. Pēc vēl <xliff:g id="NUMBER_1">%d</xliff:g> neveiksmīgiem mēģinājumiem tālrunī tiks atiestatīti rūpnīcas noklusējuma iestatījumi un lietotāja dati tiks zaudēti."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Jūs nepareizi ievadījāt PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> reizes.\n\nMēģiniet vēlreiz pēc <xliff:g id="NUMBER_1">%2$d</xliff:g> sekundēm."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Jūs nepareizi ievadījāt paroli <xliff:g id="NUMBER_0">%1$d</xliff:g> reizes.\n\nMēģiniet vēlreiz pēc <xliff:g id="NUMBER_1">%2$d</xliff:g> sekundēm."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Jūs nepareizi norādījāt atbloķēšanas kombināciju <xliff:g id="NUMBER_0">%1$d</xliff:g> reizes.\n\nMēģiniet vēlreiz pēc <xliff:g id="NUMBER_1">%2$d</xliff:g> sekundēm."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Jūs nepareizi veicāt planšetdatora atbloķēšanu <xliff:g id="NUMBER_0">%1$d</xliff:g> reizes. Pēc vēl <xliff:g id="NUMBER_1">%2$d</xliff:g> neveiksmīgiem mēģinājumiem planšetdatorā tiks atiestatīti rūpnīcas noklusējuma iestatījumi un lietotāja dati tiks zaudēti."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Jūs <xliff:g id="NUMBER_0">%1$d</xliff:g> reizi(-es) nesekmīgi mēģinājāt atbloķēt televizoru. Pēc vēl <xliff:g id="NUMBER_1">%2$d</xliff:g> nesekmīga(-iem) mēģinājuma(-iem) televizorā tiks veikta rūpnīcas datu atiestatīšana un visi lietotāja dati tiks zaudēti."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Jūs nepareizi veicāt tālruņa atbloķēšanu <xliff:g id="NUMBER_0">%1$d</xliff:g> reizes. Pēc vēl <xliff:g id="NUMBER_1">%2$d</xliff:g> neveiksmīgiem mēģinājumiem tālrunī tiks atiestatīti rūpnīcas noklusējuma iestatījumi un lietotāja dati tiks zaudēti."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Jūs nepareizi veicāt planšetdatora atbloķēšanu <xliff:g id="NUMBER">%d</xliff:g> reizes. Planšetdatorā tiks atiestatīti rūpnīcas noklusējuma iestatījumi."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Jūs <xliff:g id="NUMBER">%d</xliff:g> reizi(-es) nesekmīgi mēģinājāt atbloķēt televizoru. Tagad tiks veikta rūpnīcas datu atiestatīšana."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Jūs nepareizi veicāt tālruņa atbloķēšanu <xliff:g id="NUMBER">%d</xliff:g> reizes. Tālrunī tiks atiestatīti rūpnīcas noklusējuma iestatījumi."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Jūs nepareizi norādījāt atbloķēšanas kombināciju <xliff:g id="NUMBER_0">%d</xliff:g> reizes. Pēc vēl <xliff:g id="NUMBER_1">%d</xliff:g> neveiksmīgiem mēģinājumiem planšetdators būs jāatbloķē, izmantojot e-pasta kontu.\n\nMēģiniet vēlreiz pēc <xliff:g id="NUMBER_2">%d</xliff:g> sekundēm."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Grafiskā atslēga tika nepareizi norādīta <xliff:g id="NUMBER_0">%d</xliff:g> reizi(-es). Ja nepareizi norādīsiet to vēl <xliff:g id="NUMBER_1">%d</xliff:g> reizi(-es), televizors būs jāatbloķē, izmantojot e-pasta kontu.\n\nMēģiniet vēlreiz pēc <xliff:g id="NUMBER_2">%d</xliff:g> sekundes(-ēm)."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Jūs nepareizi norādījāt atbloķēšanas kombināciju <xliff:g id="NUMBER_0">%d</xliff:g> reizes. Pēc vēl <xliff:g id="NUMBER_1">%d</xliff:g> neveiksmīgiem mēģinājumiem tālrunis būs jāatbloķē, izmantojot e-pasta kontu.\n\nMēģiniet vēlreiz pēc <xliff:g id="NUMBER_2">%d</xliff:g> sekundēm."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Jūs nepareizi norādījāt atbloķēšanas kombināciju <xliff:g id="NUMBER_0">%1$d</xliff:g> reizes. Pēc vēl <xliff:g id="NUMBER_1">%2$d</xliff:g> neveiksmīgiem mēģinājumiem planšetdators būs jāatbloķē, izmantojot e-pasta kontu.\n\nMēģiniet vēlreiz pēc <xliff:g id="NUMBER_2">%3$d</xliff:g> sekundēm."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Grafiskā atslēga tika nepareizi norādīta <xliff:g id="NUMBER_0">%1$d</xliff:g> reizi(-es). Ja nepareizi norādīsiet to vēl <xliff:g id="NUMBER_1">%2$d</xliff:g> reizi(-es), televizors būs jāatbloķē, izmantojot e-pasta kontu.\n\nMēģiniet vēlreiz pēc <xliff:g id="NUMBER_2">%3$d</xliff:g> sekundes(-ēm)."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Jūs nepareizi norādījāt atbloķēšanas kombināciju <xliff:g id="NUMBER_0">%1$d</xliff:g> reizes. Pēc vēl <xliff:g id="NUMBER_1">%2$d</xliff:g> neveiksmīgiem mēģinājumiem tālrunis būs jāatbloķē, izmantojot e-pasta kontu.\n\nMēģiniet vēlreiz pēc <xliff:g id="NUMBER_2">%3$d</xliff:g> sekundēm."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Noņemt"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Vai palielināt skaļumu virs ieteicamā līmeņa?\n\nIlgstoši klausoties skaņu lielā skaļumā, var tikt bojāta dzirde."</string>
@@ -1487,6 +1485,7 @@
<item quantity="other">%d h</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Līdz <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Līdz plkst. <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (nākamais signāls)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Līdz brīdim, kad izslēgsiet"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Līdz izslēgsiet statusu “Netraucēt”"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-mcc204-mnc04/config.xml b/core/res/res/values-mcc204-mnc04/config.xml
index fb639ca..0f39e42 100755
--- a/core/res/res/values-mcc204-mnc04/config.xml
+++ b/core/res/res/values-mcc204-mnc04/config.xml
@@ -44,14 +44,5 @@
<item>false</item>
</string-array>
- <!-- String containing the apn value for tethering. May be overriden by secure settings
- TETHER_DUN_APN. Value is a comma separated series of strings:
- "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type",
- Or string format of ApnSettingV3.
- note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
- <string-array translatable="false" name="config_tether_apndata">
- <item>[ApnSettingV3]SaskTel Tethering,inet.stm.sk.ca,,,,,,,,,204,04,,DUN,,,true,0,,,,,,,gid,5A</item>
- </string-array>
-
<string translatable="false" name="prohibit_manual_network_selection_in_gobal_mode">true;BAE0000000000000</string>
</resources>
diff --git a/core/res/res/values-mcc302-mnc780/config.xml b/core/res/res/values-mcc302-mnc780/config.xml
index 51abd36..a48f695 100644
--- a/core/res/res/values-mcc302-mnc780/config.xml
+++ b/core/res/res/values-mcc302-mnc780/config.xml
@@ -21,25 +21,6 @@
for different hardware and product builds. -->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Array of ConnectivityManager.TYPE_xxxx values allowable for tethering -->
- <!-- Common options are [1, 4] for TYPE_WIFI and TYPE_MOBILE_DUN or
- <!== [0,1,5,7] for TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI and TYPE_BLUETOOTH -->
- <integer-array translatable="false" name="config_tether_upstream_types">
- <item>1</item>
- <item>4</item>
- <item>7</item>
- <item>9</item>
- </integer-array>
-
- <!-- String containing the apn value for tethering. May be overriden by secure settings
- TETHER_DUN_APN. Value is a comma separated series of strings:
- "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type",
- Or string format of ApnSettingV3.
- note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
- <string-array translatable="false" name="config_tether_apndata">
- <item>SaskTel Tethering,inet.stm.sk.ca,,,,,,,,,302,780,,DUN</item>
- </string-array>
-
<!-- Don't use roaming icon for considered operators -->
<string-array translatable="false" name="config_operatorConsideredNonRoaming">
<item>302</item>
diff --git a/core/res/res/values-mk-rMK-watch/strings.xml b/core/res/res/values-mk-rMK-watch/strings.xml
index 9d7c5bf..e04a195 100644
--- a/core/res/res/values-mk-rMK-watch/strings.xml
+++ b/core/res/res/values-mk-rMK-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Апликац. <xliff:g id="NUMBER_0">%1$d</xliff:g> од <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Сензори"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"пристапи до контактите"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"пристапи до локацијата на часовникот"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"пристапи до календарот"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"испраќај и прикажувај СМС-пораки"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"пристапи до фотографиите, медиумите и датотеките на часовникот"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"снимај аудио"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"фотографирај и снимај видео"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"повикувај и управувај со телефонски повици"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"пристапи до податоците од сензорите за виталните знаци"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"биди статусната лента"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"пристапи до телесните сензори (како монитори за ритам на срце)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"пристапи до прецизната локација (ГПС и врз база на мрежа)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"пристапи до приближната локација (врз база на мрежа)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"испраќај наредби до СИМ-картичката"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"добиј целосен пристап до мрежата"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"управувај со сопствениците на профил и уред"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"промени ја состојбата на WiMAX"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"добиј статус на пренос на Android Beam"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"насочи излез за медиуми"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"читај сесии на инсталирање"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"барај пакети за инсталирање"</string>
</resources>
diff --git a/core/res/res/values-mk-rMK/strings.xml b/core/res/res/values-mk-rMK/strings.xml
index a41be56..62ba912 100644
--- a/core/res/res/values-mk-rMK/strings.xml
+++ b/core/res/res/values-mk-rMK/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Опфаќа лични податоци како што се броеви на кредитни картички и лозинки."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"оневозможи или измени статусна лента"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Дозволува апликацијата да ја оневозможи статусната лента или да додава или отстранува системски икони."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"статусна лента"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"да стане статусна лента"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Дозволува апликацијата да биде статусната лента."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"прошири/собери статусна лента"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Дозволува апликацијата да ја прошири или собере статусната лента."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Овозможува апликацијата да прима и да обработува WAP пораки. Оваа дозвола ја опфаќа способноста за следење или за бришење пораки испратени до вашиот уред без да ви ги прикаже вам."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"обнови активни апликации"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Овозможува апликацијата да поврати информации за тековно и до неодамна активни задачи. Ова може да овозможи апликацијата да открие информации за тоа кои апликации се користат на уредот."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Управувај сопственици на профил и уред"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"управување со сопствениците на профилите и уредите"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Дозволува апликациите до постават сопственици на профили и сопственик на уредот."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"преуреди активни апликации"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Овозможува апликацијата да преместува задачи во преден план и во заднина. Апликацијата може да го прави тоа без вашиот придонес."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Овозможува апликацијата да го менува дневникот на повици на вашиот таблет, вклучувајќи податоци за дојдовни и појдовни повици. Злонамерните апликации може да го искористат ова да го избришат или да го менуваат вашиот дневник на повици."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Дозволува апликацијата да го менува дневникот на повици на вашиот телевизор, вклучувајќи и податоци за дојдовните или појдовните повици. Злонамерните апликации може да го искористат ова за да го избришат или да го менуваат вашиот дневник на повици."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Овозможува апликацијата да го менува дневникот на повици на вашиот телефон, вклучувајќи податоци за дојдовни и појдовни повици. Злонамерните апликации може да го искористат ова да го избришат или да го менуваат вашиот дневник на повици."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"телесни сензори (како монитори за срцев пулс)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"пристап до телесните сензори (како мониторите за пулс)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Дозволува апликацијата да пристапува до податоци од сензори кои ја следат вашата физичка состојба, како на пр. отчукувањата на срцето."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"прочитај настани во календар и доверливи информации"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Овозможува апликацијата да ги чита сите календарски настани што се зачувани на вашиот таблет, вклучувајќи ги и оние на пријатели или соработници. Ова може да овозможи апликацијата да ги споделува или да го зачува вашите податоци од календарот, без оглед на нивната доверливост или чувствителност."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Овозможува апликацијата да додава, отстранува, менува настани кои може да ги менувате на вашиот телефон, вклучувајќи ги и оние на пријатели или соработници. Ова може да овозможи апликацијата да праќа пораки за кои се чини дека доаѓаат од сопственици на календар или да менува настани без знаење на сопствениците."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"пристапи кон наредби на давателот на дополнителна локација"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Овозможува апликацијата да пристапи кон дополнителни наредби на давател на локација. Ова може да овозможи апликацијата да го попечи функционирањето на ГПС или други извори на локација."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"прецизна локација (ГПС и базирана на мрежа)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"пристап до прецизната локација (GPS и врз база на мрежа)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Овозможува апликацијата да ја добие вашата точна локација со користење „Глобален систем за позиционирање (ГПС)“ или извори на локација, како што се мобилни кули и Wi-Fi. Овие услуги за локација мора да се вклучени и достапни за вашиот уред за апликацијата да ги користи. Апликациите може да го користат ова за да утврдат приближно каде се наоѓате и може дополнително да потрошат батерија."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"приближна локација (базирана на мрежа)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"пристап до приближната локација (врз база на мрежа)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Овозможува апликацијата да ја добие вашата приближна локација. Оваа локација е изведена од услугите за локација со користење мрежа на извори на локација, како што се мобилни кули и Wi-Fi. Овие услуги за локација мора да се вклучени и достапни за вашиот уред за апликацијата да ги користи. Апликациите може да го користат ова за да утврдат приближно каде се наоѓате."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"промени аудио подесувања"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Овозможува апликацијата да ги менува глобалните аудио подесувања, како што се јачината на звукот и кој звучник се користи за излез."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"снимај аудио"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Овозможува апликацијата да снима аудио со микрофонот. Оваа дозвола овозможува апликацијата да снима аудио во кое било време без ваша потврда."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"комуникација со СИМ картичка"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"испраќање наредби до СИМ-картичката"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Овозможува апликацијата да испраќа наредби до СИМ картичката. Ова е многу опасно."</string>
<string name="permlab_camera" msgid="3616391919559751192">"снимај слики и видеа"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Овозможува апликацијата да прави фотографии и да снима видеа со камерата. Оваа дозвола овозможува апликацијата да ја користи камерата во кое било време без ваша потврда."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Овозможува апликацијата да го добие списокот со сметки познати на телефонот. Ова може да опфати кои било сметки што ги создале апликациите што сте ги инсталирале."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"прикажи мрежни врски"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Овозможува апликацијата да ги види информациите за мрежните конекции, како на пр., кои мрежи постојат и се поврзани."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"целосен пристап на мрежа"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"добивање целосен пристап до мрежата"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Овозможува апликацијата да создаде мрежни приклучоци и да користи приспособени мрежни протоколи. Прелистувачот и другите апликации обезбедуваат средства за да се испратат податоци на интернет, па оваа дозвола не е потребна за да се испратат податоци на интернет."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"промени мрежно поврзување"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Дозволува апликацијата да ја промени состојбата на мрежната поврзливост."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Дозволува апликацијата да го конфигурира телефонот со локалниот Bluetooth и да открива и да се спарува со уреди на далечина."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"поврзи се и исклучи се од WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Овозможува апликацијата да утврди дали WiMAX е овозможен и информации за кои било поврзани WiMAX мрежи."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Промени состојба на WiMAX"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"промена на состојбата на WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Овозможува апликацијата да го вклучи таблетот на и да го исклучи таблетот од WiMAX мрежи."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Дозволува апликацијата да го поврзе или да го исклучи телевизорот од WiMAX мрежи."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Овозможува апликацијата да го вклучи телефонот на и да го исклучи телефонот од WiMAX мрежи."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Дозволува апликацијата да ги изменува калибрирачките параметри на екранот на допир. Не треба да се користи за стандардни апликации."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"пристап до ДРМ-сертификати"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Дозволува апликацијата да обезбедува и користи ДРМ-сертификати. Не треба да се користи за стандардни апликации."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Примајте статус на трансфер на Android Beam"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"добивање статус на пренос на Android Beam"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Ѝ дозволува на оваа апликација да добива информации за моменталните трансфери на Android Beam"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"отстранување ДРМ-сетификати"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Дозволува апликација да отстранува ДРМ-сертификати. Не треба да се користи за стандардни апликации."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Погрешен ПИН код."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"За да го отклучите, притиснете „Мени“ и потоа „0“."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Број за итни случаи"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Нема услуга."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Нема услуга"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Екранот е заклучен."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Притисни „Мени“ да се отклучи или да направи итен повик."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Притиснете „Мени“ за да се отклучи."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Погледнете го Упатството за корисници или контактирајте со Грижа за корисници."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"СИМ картичката е заклучена."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"СИМ картичката се отклучува..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Погрешно сте ја употребиле вашата шема за отклучување <xliff:g id="NUMBER_0">%d</xliff:g> пати. \n\nОбидете се повторно за <xliff:g id="NUMBER_1">%d</xliff:g> секунди."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Погрешно сте ја впишале вашата лозинка <xliff:g id="NUMBER_0">%d</xliff:g> пати. \n\nОбидете се повторно за <xliff:g id="NUMBER_1">%d</xliff:g> секунди."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Погрешно сте го впишале вашиот ПИН <xliff:g id="NUMBER_0">%d</xliff:g> пати. \n\nОбидете се повторно за <xliff:g id="NUMBER_1">%d</xliff:g> секунди."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Погрешно ја употребивте шемата за отклучување <xliff:g id="NUMBER_0">%d</xliff:g> пати. По уште <xliff:g id="NUMBER_1">%d</xliff:g> неуспешни обиди, ќе побараме да го отклучите таблетот со пријавата за Google.\n\n Обидете се повторно за <xliff:g id="NUMBER_2">%d</xliff:g> секунди."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Неправилно ја исцртавте шемата за отклучување <xliff:g id="NUMBER_0">%d</xliff:g> пати. По уште <xliff:g id="NUMBER_1">%d</xliff:g> неуспешни обиди, ќе биде побарано да го отклучите вашиот телевизор со пријавување на Google.\n\n Обидете се повторно за <xliff:g id="NUMBER_2">%d</xliff:g> секунди."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Погрешно ја употребивте шемата за отклучување <xliff:g id="NUMBER_0">%d</xliff:g> пати. По уште <xliff:g id="NUMBER_1">%d</xliff:g> неуспешни обиди, ќе побараме да го отклучите телефонот со пријавата за Google.\n\n Обидете се повторно за <xliff:g id="NUMBER_2">%d</xliff:g> секунди."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Имавте <xliff:g id="NUMBER_0">%d</xliff:g> неуспешни обиди да го отклучите таблетот. Ви преостануваат уште <xliff:g id="NUMBER_1">%d</xliff:g> неуспешни обиди и таблетот ќе се ресетира на фабричките подесувања и сите податоци на корисникот ќе се изгубат."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Неправилно се обидовте да го отклучите вашиот телевизор <xliff:g id="NUMBER_0">%d</xliff:g> пати. По уште <xliff:g id="NUMBER_1">%d</xliff:g> неуспешни обиди, телевизорот ќе се ресетира на стандардните фабрички вредности и сите податоци на корисникот ќе бидат изгубени."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Имавте <xliff:g id="NUMBER_0">%d</xliff:g> неуспешни обиди да го отклучите телефонот. Ви преостануваат уште <xliff:g id="NUMBER_1">%d</xliff:g> неуспешни обиди и телефонот ќе се ресетира на фабричките подесувања и сите податоци на корисникот ќе се изгубат."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Погрешно сте ја употребиле вашата шема за отклучување <xliff:g id="NUMBER_0">%1$d</xliff:g> пати. \n\nОбидете се повторно за <xliff:g id="NUMBER_1">%2$d</xliff:g> секунди."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Погрешно сте ја впишале вашата лозинка <xliff:g id="NUMBER_0">%1$d</xliff:g> пати. \n\nОбидете се повторно за <xliff:g id="NUMBER_1">%2$d</xliff:g> секунди."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Погрешно сте го впишале вашиот ПИН <xliff:g id="NUMBER_0">%1$d</xliff:g> пати. \n\nОбидете се повторно за <xliff:g id="NUMBER_1">%2$d</xliff:g> секунди."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Погрешно ја употребивте шемата за отклучување <xliff:g id="NUMBER_0">%1$d</xliff:g> пати. По уште <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешни обиди, ќе побараме да го отклучите таблетот со пријавата за Google.\n\n Обидете се повторно за <xliff:g id="NUMBER_2">%3$d</xliff:g> секунди."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Неправилно ја исцртавте шемата за отклучување <xliff:g id="NUMBER_0">%1$d</xliff:g> пати. По уште <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешни обиди, ќе биде побарано да го отклучите вашиот телевизор со пријавување на Google.\n\n Обидете се повторно за <xliff:g id="NUMBER_2">%3$d</xliff:g> секунди."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Погрешно ја употребивте шемата за отклучување <xliff:g id="NUMBER_0">%1$d</xliff:g> пати. По уште <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешни обиди, ќе побараме да го отклучите телефонот со пријавата за Google.\n\n Обидете се повторно за <xliff:g id="NUMBER_2">%3$d</xliff:g> секунди."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Имавте <xliff:g id="NUMBER_0">%1$d</xliff:g> неуспешни обиди да го отклучите таблетот. Ви преостануваат уште <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешни обиди и таблетот ќе се ресетира на фабричките подесувања и сите податоци на корисникот ќе се изгубат."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Неправилно се обидовте да го отклучите вашиот телевизор <xliff:g id="NUMBER_0">%1$d</xliff:g> пати. По уште <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешни обиди, телевизорот ќе се ресетира на стандардните фабрички вредности и сите податоци на корисникот ќе бидат изгубени."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Имавте <xliff:g id="NUMBER_0">%1$d</xliff:g> неуспешни обиди да го отклучите телефонот. Ви преостануваат уште <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешни обиди и телефонот ќе се ресетира на фабричките подесувања и сите податоци на корисникот ќе се изгубат."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Имавте <xliff:g id="NUMBER">%d</xliff:g> неуспешни обиди да го отклучите таблетот. Сега таблетот ќе се ресетира на фабричките подесувања."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Неправилно се обидовте да го отклучите вашиот телевизор <xliff:g id="NUMBER">%d</xliff:g> пати. Телевизорот сега ќе биде ресетиран на стандардните фабрички вредности."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Имавте <xliff:g id="NUMBER">%d</xliff:g> неуспешни обиди да го отклучите телефонот. Сега телефонот ќе се ресетира на фабричките подесувања."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"збор"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"врска"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"линија"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Фабричкото тестирање не успеа"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Дејството на FACTORY_TEST е поддржано само за пакети инсталирани во /систем /апликација."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Не е пронајден пакет што овозможува дејство на FACTORY_TEST."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Се форматира..."</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Не е внесено"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Не се пронајдени соодветни активности."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Насочи излез за медиуми"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"насочување излез за медиуми"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Овозможува апликацијата да насочува излез за медиуми кон други надворешни уреди."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Читај сесии на инсталирање"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"читање сесии на инсталирање"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Дозволува апликација да чита сесии на инсталирање. Тоа овозможува апликацијата да гледа детали за активни инсталации на пакет."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Барај инсталирање пакети"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"барање пакети за инсталирање"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Дозволува апликацијата да бара инсталација на пакети."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Допрете двапати за регулирање на зумирањето"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Не можеше да се додаде виџет."</string>
@@ -1287,18 +1285,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Неважечко корисничко име или лозинка."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Го заборави своето корисничко име или лозинката?\nПосети"<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Сметката се проверува..."</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Погрешно сте го впишале вашиот ПИН <xliff:g id="NUMBER_0">%d</xliff:g> пати. \n\nОбидете се повторно за <xliff:g id="NUMBER_1">%d</xliff:g> секунди."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Погрешно сте ја впишале вашата лозинка <xliff:g id="NUMBER_0">%d</xliff:g> пати. \n\nОбидете се повторно за <xliff:g id="NUMBER_1">%d</xliff:g> секунди."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Погрешно сте ја употребиле вашата шема за отклучување <xliff:g id="NUMBER_0">%d</xliff:g> пати. \n\nОбидете се повторно за <xliff:g id="NUMBER_1">%d</xliff:g> секунди."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Погрешно сте се обиделе да го отклучите телефонот <xliff:g id="NUMBER_0">%d</xliff:g> пати. По <xliff:g id="NUMBER_1">%d</xliff:g> неуспешни обиди, телефонот ќе се ресетира на фабрички стандардни вредности и сите податоци за корисникот ќе се изгубат."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Неправилно се обидовте да го отклучите вашиот телевизор <xliff:g id="NUMBER_0">%d</xliff:g> пати. По уште <xliff:g id="NUMBER_1">%d</xliff:g> неуспешни обиди, телевизорот ќе се ресетира на стандардните фабрички вредности и сите податоци на корисникот ќе бидат изгубени."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Погрешно сте се обиделе да го отклучите телефонот <xliff:g id="NUMBER_0">%d</xliff:g> пати. По <xliff:g id="NUMBER_1">%d</xliff:g> неуспешни обиди, телефонот ќе се ресетира на фабрички стандардни вредности и сите податоци за корисникот ќе се изгубат."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Погрешно сте го впишале вашиот ПИН <xliff:g id="NUMBER_0">%1$d</xliff:g> пати. \n\nОбидете се повторно за <xliff:g id="NUMBER_1">%2$d</xliff:g> секунди."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Погрешно сте ја впишале вашата лозинка <xliff:g id="NUMBER_0">%1$d</xliff:g> пати. \n\nОбидете се повторно за <xliff:g id="NUMBER_1">%2$d</xliff:g> секунди."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Погрешно сте ја употребиле вашата шема за отклучување <xliff:g id="NUMBER_0">%1$d</xliff:g> пати. \n\nОбидете се повторно за <xliff:g id="NUMBER_1">%2$d</xliff:g> секунди."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Погрешно сте се обиделе да го отклучите телефонот <xliff:g id="NUMBER_0">%1$d</xliff:g> пати. По <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешни обиди, телефонот ќе се ресетира на фабрички стандардни вредности и сите податоци за корисникот ќе се изгубат."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Неправилно се обидовте да го отклучите вашиот телевизор <xliff:g id="NUMBER_0">%1$d</xliff:g> пати. По уште <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешни обиди, телевизорот ќе се ресетира на стандардните фабрички вредности и сите податоци на корисникот ќе бидат изгубени."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Погрешно сте се обиделе да го отклучите телефонот <xliff:g id="NUMBER_0">%1$d</xliff:g> пати. По <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешни обиди, телефонот ќе се ресетира на фабрички стандардни вредности и сите податоци за корисникот ќе се изгубат."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Погрешно сте се обиделе да го отклучите таблетот <xliff:g id="NUMBER">%d</xliff:g> пати. Таблетот ќе се ресетира на фабрички стандардни вредности."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Неправилно се обидовте да го отклучите вашиот телевизор <xliff:g id="NUMBER">%d</xliff:g> пати. Телевизорот сега ќе биде ресетиран на стандардните фабрички вредности."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Погрешно сте се обиделе да го отклучите телефонот <xliff:g id="NUMBER">%d</xliff:g> пати. Телефонот ќе се ресетира на фабрички стандардни вредности."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Погрешно сте ја употребиле вашата шема на отклучување <xliff:g id="NUMBER_0">%d</xliff:g> пати. По <xliff:g id="NUMBER_1">%d</xliff:g> неуспешни обиди, ќе побараат од вас да го отклучите таблетот со користење сметка на е-пошта.\n\n Обидете се повторно за <xliff:g id="NUMBER_2">%d</xliff:g> секунди."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Неправилно ја исцртавте шемата за отклучување <xliff:g id="NUMBER_0">%d</xliff:g> пати. По уште <xliff:g id="NUMBER_1">%d</xliff:g> неуспешни обиди, ќе биде побарано да го отклучите вашиот телевизор со помош на вашата сметка за е-пошта.\n\n Обидете се повторно за <xliff:g id="NUMBER_2">%d</xliff:g> секунди."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Погрешно сте ја употребиле вашата шема на отклучување <xliff:g id="NUMBER_0">%d</xliff:g> пати. По <xliff:g id="NUMBER_1">%d</xliff:g> неуспешни обиди, ќе побараат од вас да го отклучите телефонот со користење сметка на е-пошта.\n\n Обидете се повторно за <xliff:g id="NUMBER_2">%d</xliff:g> секунди."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Погрешно сте ја употребиле вашата шема на отклучување <xliff:g id="NUMBER_0">%1$d</xliff:g> пати. По <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешни обиди, ќе побараат од вас да го отклучите таблетот со користење сметка на е-пошта.\n\n Обидете се повторно за <xliff:g id="NUMBER_2">%3$d</xliff:g> секунди."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Неправилно ја исцртавте шемата за отклучување <xliff:g id="NUMBER_0">%1$d</xliff:g> пати. По уште <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешни обиди, ќе биде побарано да го отклучите вашиот телевизор со помош на вашата сметка за е-пошта.\n\n Обидете се повторно за <xliff:g id="NUMBER_2">%3$d</xliff:g> секунди."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Погрешно сте ја употребиле вашата шема на отклучување <xliff:g id="NUMBER_0">%1$d</xliff:g> пати. По <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешни обиди, ќе побараат од вас да го отклучите телефонот со користење сметка на е-пошта.\n\n Обидете се повторно за <xliff:g id="NUMBER_2">%3$d</xliff:g> секунди."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Отстрани"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Да го зголемиме звукот над препорачаното ниво?\n\nСлушањето звуци со голема јачина подолги периоди може да ви го оштети сетилото за слух."</string>
@@ -1472,6 +1470,7 @@
<item quantity="other">За %d ч.</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (следниот аларм)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Додека не го исклучите"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Додека не го исклучите Не вознемирувај"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-ml-rIN-watch/strings.xml b/core/res/res/values-ml-rIN-watch/strings.xml
index 02fe66c..9f93404 100644
--- a/core/res/res/values-ml-rIN-watch/strings.xml
+++ b/core/res/res/values-ml-rIN-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_0">%1$d</xliff:g> / <xliff:g id="NUMBER_1">%2$d</xliff:g> അപ്ലിക്കേഷൻ."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"സെൻസറുകൾ"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"നിങ്ങളുടെ കോൺടാക്റ്റുകൾ ആക്സസ് ചെയ്യുക"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"ഈ വാച്ചിന്റെ ലൊക്കേഷൻ ആക്സസ് ചെയ്യുക"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"നിങ്ങളുടെ കലണ്ടർ ആക്സസ് ചെയ്യുക"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"SMS സന്ദേശങ്ങൾ അയയ്ക്കുകയും കാണുകയും ചെയ്യുക"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"നിങ്ങളുടെ വാച്ചിൽ ഫോട്ടോകളും മീഡിയയും ഫയലുകളും ആക്സസ് ചെയ്യുക"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"ഓഡിയോ റെക്കോർഡ് ചെയ്യുക"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"ചിത്രങ്ങൾ എടുക്കുക, വീഡിയോ റെക്കോർഡ് ചെയ്യുക"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"ഫോൺ വിളിക്കുകയും നിയന്ത്രിക്കുകയും ചെയ്യുക"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"നിങ്ങളുടെ ജീവാധാര ലക്ഷണങ്ങളെ കുറിച്ചുള്ള സെൻസർ വിവരങ്ങൾ ആക്സസ് ചെയ്യുക"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"സ്റ്റാറ്റസ് ബാർ ആയിരിക്കുക"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"ശരീര സെൻസറുകൾ (ഹൃദയമിടിപ്പ് നിരക്ക് മോണിറ്ററുകൾ പോലെ) ആക്സസ് ചെയ്യുക"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"കൃത്യമായ ലൊക്കേഷൻ (GPS - നെറ്റ്വർക്ക് അധിഷ്ഠിതം) ആക്സസ് ചെയ്യുക"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"ഏകദേശ ലൊക്കേഷൻ (നെറ്റ്വർക്ക് അധിഷ്ഠിതം) ആക്സസ് ചെയ്യുക"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"SIM കമാൻഡുകൾ അയയ്ക്കുക"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"പൂർണ്ണ നെറ്റ്വർക്ക് ആക്സസ് ഉണ്ടായിരിക്കുക"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"പ്രൊഫൈൽ, ഉപകരണ ഉടമകളെ മാനേജുചെയ്യുക"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"WiMAX നില മാറ്റുക"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"Android ബീം കൈമാറൽ നില സ്വീകരിക്കുക"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"മീഡിയ ഔട്ട്പുട്ട് റൂട്ടുചെയ്യുക"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"ഇൻസ്റ്റാൾ സെഷനുകൾ റീഡുചെയ്യുക"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"പാക്കേജുകൾ ഇൻസ്റ്റാൾ ചെയ്യാൻ അഭ്യർത്ഥിക്കുക"</string>
</resources>
diff --git a/core/res/res/values-ml-rIN/strings.xml b/core/res/res/values-ml-rIN/strings.xml
index 2bdc9c8..925688d 100644
--- a/core/res/res/values-ml-rIN/strings.xml
+++ b/core/res/res/values-ml-rIN/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ക്രെഡിറ്റ് കാർഡ് നമ്പറുകളും പാസ്വേഡുകളും പോലുള്ള വ്യക്തിഗത ഡാറ്റ ഉൾപ്പെടുന്നു."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"സ്റ്റാറ്റസ് ബാർ പ്രവർത്തനരഹിതമാക്കുക അല്ലെങ്കിൽ പരിഷ്ക്കരിക്കുക"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"നില ബാർ പ്രവർത്തരഹിതമാക്കുന്നതിന് അല്ലെങ്കിൽ സിസ്റ്റം ഐക്കണുകൾ ചേർക്കുന്നതിനും നീക്കംചെയ്യുന്നതിനും അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"സ്റ്റാറ്റസ് ബാർ"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"സ്റ്റാറ്റസ് ബാർ ആയിരിക്കുക"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"അപ്ലിക്കേഷനെ നില ബാർ ആകാൻ അനുവദിക്കുന്നു."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"സ്റ്റാറ്റസ് വിപുലീകരിക്കുക/ചുരുക്കുക"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"നില ബാർ വിപുലീകരിക്കുന്നതിനോ ചുരുക്കുന്നതിനോ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"WAP സന്ദേശങ്ങൾ നേടാനും പ്രോസസ്സുചെയ്യാനും അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. നിങ്ങൾക്ക് അയയ്ക്കുന്ന സന്ദേശങ്ങൾ നിങ്ങൾക്ക് ദൃശ്യമാക്കാതെ തന്നെ നിരീക്ഷിക്കാനോ ഇല്ലാതാക്കാനോ ഉള്ള കഴിവ് ഈ അനുമതികളിൽ ഉൾപ്പെടുന്നു."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"പ്രവർത്തിക്കുന്ന അപ്ലിക്കേഷനുകൾ വീണ്ടെടുക്കുക"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"നിലവിലും സമീപകാലത്തും പ്രവർത്തിക്കുന്ന ടാസ്ക്കുകളെക്കുറിച്ചുള്ള വവിവരങ്ങൾ വീണ്ടെടുക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഇത് ഉപകരണത്തിൽ ഉപയോഗിച്ച അപ്ലിക്കേഷനുകളെക്കുറിച്ചുള്ള വിവരം കണ്ടെത്താൻ അപ്ലിക്കേഷനെ അനുവദിക്കാനിടയുണ്ട്."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"പ്രൊഫൈൽ, ഉപകരണ ഉടമകളെ നിയന്ത്രിക്കുക"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"പ്രൊഫൈൽ, ഉപകരണ ഉടമകളെ മാനേജുചെയ്യുക"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"പ്രൊഫൈൽ ഉടമകളെയും ഉപകരണ ഉടമയെയും സജ്ജമാക്കാൻ അപ്ലിക്കേഷനുകളെ അനുവദിക്കുന്നു."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"പ്രവർത്തിക്കുന്ന അപ്ലിക്കേഷനുകൾ പുനഃക്രമീകരിക്കുക"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"ടാസ്ക്കുകളെ മുന്നിലേക്കോ പശ്ചാത്തലത്തിലേക്കോ നീക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഇത് അപ്ലിക്കേഷൻ നിങ്ങളുടെ ഇടപെടലില്ലാതെ ചെയ്യാനിടയുണ്ട്."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"ഇൻകമിംഗ്, ഔട്ട്ഗോയിംഗ് കോളുകളെക്കുറിച്ചുള്ള ഡാറ്റയുൾപ്പെടുന്ന, നിങ്ങളുടെ ടാബ്ലെറ്റിന്റെ കോൾ ലോഗ് പരിഷ്ക്കരിക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ക്ഷുദ്രകരമായ അപ്ലിക്കേഷനുകൾ നിങ്ങളുടെ കോൾ ലോഗ് മായ്ക്കാനോ പരിഷ്ക്കരിക്കാനോ ഇത് ഉപയോഗിച്ചേക്കാം."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"ഇൻകമിംഗ്, ഔട്ട്ഗോയിംഗ് കോളുകളെക്കുറിച്ചുള്ള വിവരമുൾപ്പെടുന്ന, നിങ്ങളുടെ ടിവിയുടെ കോൾ ലോഗ് പരിഷ്ക്കരിക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ദോഷകരമായ അപ്ലിക്കേഷനുകൾ നിങ്ങളുടെ കോൾ ലോഗ് മായ്ക്കാനോ പരിഷ്ക്കരിക്കാനോ ഇത് ഉപയോഗിച്ചേക്കാം."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"ഇൻകമിംഗ്, ഔട്ട്ഗോയിംഗ് കോളുകളെക്കുറിച്ചുള്ള ഡാറ്റയുൾപ്പെടുന്ന, നിങ്ങളുടെ ഫോണിന്റെ കോൾ ലോഗ് പരിഷ്ക്കരിക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ക്ഷുദ്രകരമായ അപ്ലിക്കേഷനുകൾ നിങ്ങളുടെ കോൾ ലോഗ് മായ്ക്കാനോ പരിഷ്ക്കരിക്കാനോ ഇത് ഉപയോഗിച്ചേക്കാം."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"ശാരീര സെൻസറുകൾ (ഹൃദയമിടിപ്പ് നിരക്ക് മോണിറ്ററുകൾ പോലെ)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"ശരീര സെൻസറുകൾ (ഹൃദയമിടിപ്പ് നിരക്ക് മോണിറ്ററുകൾ പോലെ) ആക്സസ് ചെയ്യുക"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"നിങ്ങളുടെ ഹൃദയമിടിപ്പ് പോലുള്ള ശാരീരികാവസ്ഥ നിരീക്ഷിക്കാൻ സെൻസറുകളിൽ നിന്ന് വിവരം ആക്സസ്സുചെയ്യാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"കലണ്ടർ ഇവന്റുകളും രഹസ്യാത്മക വിവരവും വായിക്കുക"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"നിങ്ങളുടെ ടാബ്ലെറ്റിൽ സംഭരിച്ചിരിക്കുന്ന സുഹൃത്തുക്കളുടെയോ സഹപ്രവർത്തകരുടെയോ ഉൾപ്പെടെ, എല്ലാ കലണ്ടർ ഇവന്റുകളും റീഡുചെയ്യാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഇത് രഹസ്യാത്മകമാണെന്നോ തന്ത്രപ്രധാനമാണെന്നോ പരിഗണിക്കാതെ നിങ്ങളുടെ കലണ്ടർ ഡാറ്റ പങ്കിടാനോ സംരക്ഷിക്കാനോ അപ്ലിക്കേഷനെ അനുവദിക്കാനിടയുണ്ട്."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"സുഹൃത്തുക്കളുടെയും സഹപ്രവർത്തകരുടെയും ഉൾപ്പെടെ, നിങ്ങളുടെ ഫോണിൽ പരിഷ്ക്കരിക്കാനാകുന്ന ഇവന്റുകൾ ചേർക്കാനും നീക്കംചെയ്യാനും മാറ്റാനും അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. കലണ്ടർ ഉടമകളിൽ നിന്നുള്ളതായി തോന്നുന്ന സന്ദേശങ്ങൾ അയയ്ക്കാനോ ഉടമയുടെ അറിവില്ലാതെ ഇവന്റുകൾ പരിഷ്ക്കരിക്കാനോ ഇത് അപ്ലിക്കേഷനെ അനുവദിക്കാനിടയുണ്ട്."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"ലൊക്കേഷൻ ദാതാവിന്റെ അധിക കമാൻഡുകൾ ആക്സസ്സുചെയ്യുക"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"ലൊക്കേഷൻ ദാതാവിന്റെ അധിക കമാൻഡുകൾ ആക്സസ്സുചെയ്യാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഇത് GPS-ന്റെയോ മറ്റ് ലൊക്കേഷൻ ഉറവിടങ്ങളുടെയോ പ്രവർത്തനത്തിൽ ഇടപെടാൻ അപ്ലിക്കേഷനെ അനുവദിക്കാനിടയുണ്ട്."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"കൃത്യമായ ലൊക്കേഷൻ (GPS-ഉം നെറ്റ്വർക്കും അടിസ്ഥാനമാക്കിയുള്ളത്)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"കൃത്യമായ ലൊക്കേഷൻ (GPS - നെറ്റ്വർക്ക് അധിഷ്ഠിതം) ആക്സസ് ചെയ്യുക"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"ഗ്ലോബൽ പൊസിഷനിംഗ് സിസ്റ്റമോ (GPS) സെൽ ടവറുകളും Wi-Fi-യും പോലുള്ള നെറ്റ്വർക്ക് ലൊക്കേഷൻ ഉറവിടങ്ങളോ ഉപയോഗിച്ച് നിങ്ങളുടെ കൃത്യമായ ലൊക്കേഷൻ നേടാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. അപ്ലിക്കേഷനുകൾ അവ ഉപയോഗിക്കാൻ നിങ്ങളുടെ ഉപകരണത്തിൽ ഈ ലൊക്കേഷൻ സേവനങ്ങൾ ഓൺ ചെയ്ത് ലഭ്യമാക്കേണ്ടതുണ്ട്. നിങ്ങൾ എവിടെയാണെന്ന് ഏകദേശം നിർണ്ണയിക്കാൻ അപ്ലിക്കേഷനുകൾ ഇത് ഉപയോഗിക്കാം, അവ കൂടുതൽ ബാറ്ററി പവർ ഉപയോഗിക്കാനിടയുണ്ട്."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"ഏകദേശ ലൊക്കേഷൻ (നെറ്റ്വർക്ക് അടിസ്ഥാനമാക്കിയുള്ളത്)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"ഏകദേശ ലൊക്കേഷൻ (നെറ്റ്വർക്ക് അധിഷ്ഠിതം) ആക്സസ് ചെയ്യുക"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"നിങ്ങളുടെ ഏകദേശ ലൊക്കേഷൻ നേടാൻ അപ്ലിക്കേഷനുകളെ അനുവദിക്കുക. ഈ ലൊക്കേഷനെ സെൽ ടവറുകളും Wi-Fi-യും പോലുള്ള നെറ്റ്വർക്ക് ലൊക്കേഷൻ ഉറവിടങ്ങൾ ഉപയോഗിച്ച് ലൊക്കേഷൻ സേവനങ്ങൾ അനുമാനിക്കുന്നു. അപ്ലിക്കേഷനുകൾ അവ ഉപയോഗിക്കാൻ നിങ്ങളുടെ ഉപകരണത്തിൽ ഈ ലൊക്കേഷൻ സേവനങ്ങൾ ഓൺ ചെയ്ത് ലഭ്യമാക്കേണ്ടതുണ്ട്. നിങ്ങൾ എവിടെയാണെന്ന് ഏകദേശം നിർണ്ണയിക്കാൻ അപ്ലിക്കേഷനുകൾ ഇത് ഉപയോഗിക്കാം."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"നിങ്ങളുടെ ഓഡിയോ ക്രമീകരണങ്ങൾ മാറ്റുക"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"വോളിയവും ഔട്ട്പുട്ടിനായി ഉപയോഗിച്ച സ്പീക്കറും പോലുള്ള ആഗോള ഓഡിയോ ക്രമീകരണങ്ങൾ പരിഷ്ക്കരിക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"ഓഡിയോ റെക്കോർഡ് ചെയ്യുക"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"മൈക്രോഫോൺ ഉപയോഗിച്ച് ഓഡിയോ റെക്കോർഡുചെയ്യാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഈ അനുമതി നിങ്ങളുടെ സ്ഥിരീകരണമില്ലാതെ ഏതുസമയത്തും ഓഡിയോ റെക്കോർഡുചെയ്യാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്ന്ഉ."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"സിം ആശയവിനിമയം"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"SIM-ലേക്ക് കമാൻഡുകൾ അയയ്ക്കുക"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"സിമ്മിലേക്ക് കമാൻഡുകൾ അയയ്ക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഇത് വളരെ അപകടകരമാണ്."</string>
<string name="permlab_camera" msgid="3616391919559751192">"ചിത്രങ്ങളും വീഡിയോകളും എടുക്കുക"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"ക്യാമറ ഉപയോഗിച്ച് ചിത്രങ്ങളും വീഡിയോകളും എടുക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. നിങ്ങളുടെ സ്ഥിരീകരണമില്ലാതെ ഏതുസമയത്തും ക്യാമറ ഉപയോഗിക്കാൻ ഈ അനുമതി അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"ഫോൺ തിരിച്ചറിയുന്ന അക്കൗണ്ടുകളുടെ ലിസ്റ്റ് നേടാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഇതിൽ നിങ്ങൾ ഇൻസ്റ്റാളുചെയ്ത അപ്ലിക്കേഷനുകൾ സൃഷ്ടിച്ച എല്ലാ അക്കൗണ്ടുകളും ഉൾപ്പെടാം."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"നെറ്റ്വർക്ക് കണക്ഷനുകൾ കാണുക"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"ഏതെല്ലാം നെറ്റ്വർക്കുകൾ നിലവിലുണ്ടെന്നതും കണക്റ്റുചെയ്തിട്ടുണ്ടെന്നതും പോലുള്ള നെറ്റ്വർക്ക് കണക്ഷനുകളെക്കുറിച്ചുള്ള വിവരം കാണാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"പൂർണ്ണ നെറ്റ്വർക്ക് ആക്സസ്സ്"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"പൂർണ്ണ നെറ്റ്വർക്ക് ആക്സസ് നൽകുക"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"നെറ്റ്വർക്ക് സോക്കറ്റുകൾ സൃഷ്ടിക്കാനും ഇഷ്ടാനുസൃത നെറ്റ്വർക്ക് പ്രോട്ടോക്കോളുകൾ ഉപയോഗിക്കാനും അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഇന്റർനെറ്റിലേക്ക് ഡാറ്റ അയയ്ക്കുന്നതിനായി ബ്രൗസറും മറ്റ് അപ്ലിക്കേഷനുകളും ഉള്ളതിനാൽ, ഇന്റർനെറ്റിലേക്ക് ഡാറ്റ അയയ്ക്കാൻ ഈ അനുമതി ആവശ്യമില്ല."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"നെറ്റ്വർക്ക് കണക്റ്റിവിറ്റി മാറ്റുക"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"നെറ്റ്വർക്ക് കണക്റ്റിവിറ്റിയുടെ നില മാറ്റുന്നതിന് അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"ഒരു പ്രാദേശിക ബ്ലൂടൂത്ത് ഫോണിനെ കോൺഫിഗർചെയ്യുന്നതിനും വിദൂര ഉപകരണങ്ങളെ കണ്ടെത്തി ജോടിയാക്കുന്നതിനും അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"WiMAX കണക്റ്റുചെയ്യുക, അതിൽ നിന്നും വിച്ഛേദിക്കുക"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"WiMAX പ്രവർത്തനക്ഷമമാണോയെന്നതും കണക്റ്റുചെയ്തിരിക്കുന്ന ഏതെങ്കിലും WiMAX നെറ്റ്വർക്കുകളെക്കുറിച്ചുള്ള വിവരങ്ങളും നിർണ്ണയിക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX നില മാറ്റുക"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"WiMAX നില മാറ്റുക"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"WiMAX നെറ്റ്വർക്കുകളിലേക്ക് ടാബ്ലെറ്റ് കണക്റ്റുചെയ്യാനും അതിൽ നിന്ന് വിച്ഛേദിക്കാനും അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"WiMAX നെറ്റ്വർക്കുകളിൽ നിന്ന് ടിവി കണക്റ്റുചെയ്യുന്നതിനും വിച്ഛേദിക്കുന്നതിനും അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"WiMAX നെറ്റ്വർക്കുകളിലേക്ക് ഫോൺ കണക്റ്റുചെയ്യാനും അതിൽ നിന്ന് വിച്ഛേദിക്കാനും അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"ടച്ച് സ്ക്രീനിന്റെ കാലിബ്രേഷൻ പാരാമീറ്ററുകൾ പരിഷ്ക്കരിക്കുന്നതിന് അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. സാധാരണ അപ്ലിക്കേഷനുകൾക്ക് ഒരിക്കലും ആവശ്യമില്ല."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"DRM സർട്ടിഫിക്കറ്റുകൾക്കുള്ള ആക്സസ്സ്"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"പ്രൊവിഷൻ ചെയ്യുന്നതിനും DRM സർട്ടിഫിക്കറ്റുകൾ ഉപയോഗിക്കുന്നതിനും അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. സാധാരണ അപ്ലിക്കേഷനുകൾക്ക് ഒരിക്കലും ആവശ്യമില്ല."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Android ബീം കൈമാറൽ നില നേടുക"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"Android ബീം കൈമാറൽ നില സ്വീകരിക്കുക"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"നിലവിലെ Android ബീം കൈമാറ്റങ്ങളെക്കുറിച്ച് വിവരങ്ങൾ നേടാൻ ഈ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"DRM സർട്ടിഫിക്കറ്റുകൾ നീക്കം ചെയ്യുക"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"DRM സർട്ടിഫിക്കറ്റുകൾ നീക്കംചെയ്യുന്നതിന് അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. സാധാരണ അപ്ലിക്കേഷനുകൾക്ക് ഒരിക്കലും ആവശ്യമില്ല."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"പിൻ കോഡ് തെറ്റാണ്."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"അൺലോക്ക് ചെയ്യുന്നതിന് മെനു, 0 എന്നിവ അമർത്തുക."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"അടിയന്തര നമ്പർ"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"സേവനമില്ല"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"സേവനമില്ല"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"സ്ക്രീൻ ലോക്കുചെയ്തു."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"അൺലോക്ക് ചെയ്യുന്നതിനായി മെനു അമർത്തുക അല്ലെങ്കിൽ അടിയന്തര കോൾ വിളിക്കുക."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"അൺലോക്കുചെയ്യാൻ മെനു അമർത്തുക."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"ഉപയോക്തൃ ഗൈഡ് കാണുകയോ കസ്റ്റമർ കെയറുമായി ബന്ധപ്പെടുകയോ ചെയ്യുക."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"സിം കാർഡ് ലോക്കുചെയ്തു."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"സിം കാർഡ് അൺലോക്കുചെയ്യുന്നു…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"നിങ്ങളുടെ പാറ്റേൺ <xliff:g id="NUMBER_0">%d</xliff:g> തവണ തെറ്റായി വരച്ചു. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> സെക്കൻഡിനുള്ളിൽ വീണ്ടും ശ്രമിക്കുക."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"നിങ്ങളുടെ പാസ്വേഡ് <xliff:g id="NUMBER_0">%d</xliff:g> തവണ തെറ്റായി ടൈപ്പുചെയ്തു. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> സെക്കൻഡിനുള്ളിൽ വീണ്ടും ശ്രമിക്കുക."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"നിങ്ങളുടെ പിൻ <xliff:g id="NUMBER_0">%d</xliff:g> തവണ തെറ്റായി ടൈപ്പുചെയ്തു. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> സെക്കൻഡിനുള്ളിൽ വീണ്ടും ശ്രമിക്കുക."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"നിങ്ങളുടെ അൺലോക്കുചെയ്യൽ പാറ്റേൺ <xliff:g id="NUMBER_0">%d</xliff:g> തവണ തെറ്റായി വരച്ചു. <xliff:g id="NUMBER_1">%d</xliff:g> തെറ്റായ ശ്രമങ്ങൾക്കുശേഷം, Google സൈൻ ഇൻ ചെയ്യൽ ഉപയോഗിച്ച് നിങ്ങളുടെ ടാബ്ലെറ്റ് അൺലോക്കുചെയ്യുന്നതിന് ആവശ്യപ്പടും.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> സെക്കൻഡിനുള്ളിൽ വീണ്ടും ശ്രമിക്കുക."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"നിങ്ങൾ അൺലോക്കുചെയ്യൽ പാറ്റേൺ <xliff:g id="NUMBER_0">%d</xliff:g> തവണ തെറ്റായി വരച്ചു. <xliff:g id="NUMBER_1">%d</xliff:g>-ൽ കൂടുതൽ പരാജയപ്പെട്ട ശ്രമങ്ങൾക്കുശേഷം, Google സൈൻ ഇൻ ചെയ്യൽ ഉപയോഗിച്ച് നിങ്ങളുടെ ടിവി അൺലോക്കുചെയ്യുന്നതിന് ആവശ്യപ്പടും. \n\n <xliff:g id="NUMBER_2">%d</xliff:g> സെക്കൻഡിനുള്ളിൽ വീണ്ടും ശ്രമിക്കുക."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"നിങ്ങൾ അൺലോക്കുചെയ്യൽ പാറ്റേൺ <xliff:g id="NUMBER_0">%d</xliff:g> തവണ തെറ്റായി വരച്ചു. <xliff:g id="NUMBER_1">%d</xliff:g> തെറ്റായ ശ്രമങ്ങൾക്കുശേഷം, Google സൈൻ ഇൻ ചെയ്യൽ ഉപയോഗിച്ച് നിങ്ങളുടെ ഫോൺ അൺലോക്കുചെയ്യുന്നതിന് ആവശ്യപ്പടും. \n\n <xliff:g id="NUMBER_2">%d</xliff:g> സെക്കൻഡിനുള്ളിൽ വീണ്ടും ശ്രമിക്കുക."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"നിങ്ങൾ ഫോൺ അൺലോക്കുചെയ്യാൻ തവണ <xliff:g id="NUMBER_0">%d</xliff:g> തെറ്റായി ശ്രമിച്ചു. <xliff:g id="NUMBER_1">%d</xliff:g> ശ്രമങ്ങൾ കൂടി വിജയിച്ചില്ലെങ്കിൽ, ടാബ്ലെറ്റ് ഫാക്ടറി സ്ഥിരമായതിലേക്ക് പുനഃസജ്ജികരിക്കുകയും ഉപയോക്തൃ ഡാറ്റയെല്ലാം നഷ്ടപ്പെടുകയും ചെയ്യും."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"ടിവി അൺലോക്കുചെയ്യൽ <xliff:g id="NUMBER_0">%d</xliff:g> തവണ തെറ്റായി ചെയ്തു. <xliff:g id="NUMBER_1">%d</xliff:g>-ൽ കൂടുതൽ പരാജയപ്പെട്ട ശ്രമങ്ങൾക്കുശേഷം, ടിവി ഫാക്ടറി സ്ഥിരതയിലേക്ക് പുനഃസജ്ജീകരിക്കുകയും എല്ലാ ഉപയോക്തൃ വിവരവും നഷ്ടപ്പെടുകയും ചെയ്യും."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"നിങ്ങൾ <xliff:g id="NUMBER_0">%d</xliff:g> തവണ ഫോൺ അൺലോക്കുചെയ്യാൻ തെറ്റായി ശ്രമിച്ചു. <xliff:g id="NUMBER_1">%d</xliff:g> ശ്രമങ്ങൾ കൂടി പരാജയപ്പെടുകയാണെങ്കിൽ, ഫോൺ ഫാക്ടറി സ്ഥിരമായതിലേക്ക് പുനഃസജ്ജികരിക്കുകയും ഉപയോക്തൃ ഡാറ്റയെല്ലാം നഷ്ടപ്പെടുകയും ചെയ്യും."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"നിങ്ങളുടെ പാറ്റേൺ <xliff:g id="NUMBER_0">%1$d</xliff:g> തവണ തെറ്റായി വരച്ചു. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> സെക്കൻഡിനുള്ളിൽ വീണ്ടും ശ്രമിക്കുക."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"നിങ്ങളുടെ പാസ്വേഡ് <xliff:g id="NUMBER_0">%1$d</xliff:g> തവണ തെറ്റായി ടൈപ്പുചെയ്തു. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> സെക്കൻഡിനുള്ളിൽ വീണ്ടും ശ്രമിക്കുക."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"നിങ്ങളുടെ പിൻ <xliff:g id="NUMBER_0">%1$d</xliff:g> തവണ തെറ്റായി ടൈപ്പുചെയ്തു. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> സെക്കൻഡിനുള്ളിൽ വീണ്ടും ശ്രമിക്കുക."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"നിങ്ങളുടെ അൺലോക്കുചെയ്യൽ പാറ്റേൺ <xliff:g id="NUMBER_0">%1$d</xliff:g> തവണ തെറ്റായി വരച്ചു. <xliff:g id="NUMBER_1">%2$d</xliff:g> തെറ്റായ ശ്രമങ്ങൾക്കുശേഷം, Google സൈൻ ഇൻ ചെയ്യൽ ഉപയോഗിച്ച് നിങ്ങളുടെ ടാബ്ലെറ്റ് അൺലോക്കുചെയ്യുന്നതിന് ആവശ്യപ്പടും.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> സെക്കൻഡിനുള്ളിൽ വീണ്ടും ശ്രമിക്കുക."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"നിങ്ങൾ അൺലോക്കുചെയ്യൽ പാറ്റേൺ <xliff:g id="NUMBER_0">%1$d</xliff:g> തവണ തെറ്റായി വരച്ചു. <xliff:g id="NUMBER_1">%2$d</xliff:g>-ൽ കൂടുതൽ പരാജയപ്പെട്ട ശ്രമങ്ങൾക്കുശേഷം, Google സൈൻ ഇൻ ചെയ്യൽ ഉപയോഗിച്ച് നിങ്ങളുടെ ടിവി അൺലോക്കുചെയ്യുന്നതിന് ആവശ്യപ്പടും. \n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> സെക്കൻഡിനുള്ളിൽ വീണ്ടും ശ്രമിക്കുക."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"നിങ്ങൾ അൺലോക്കുചെയ്യൽ പാറ്റേൺ <xliff:g id="NUMBER_0">%1$d</xliff:g> തവണ തെറ്റായി വരച്ചു. <xliff:g id="NUMBER_1">%2$d</xliff:g> തെറ്റായ ശ്രമങ്ങൾക്കുശേഷം, Google സൈൻ ഇൻ ചെയ്യൽ ഉപയോഗിച്ച് നിങ്ങളുടെ ഫോൺ അൺലോക്കുചെയ്യുന്നതിന് ആവശ്യപ്പടും. \n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> സെക്കൻഡിനുള്ളിൽ വീണ്ടും ശ്രമിക്കുക."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"നിങ്ങൾ ഫോൺ അൺലോക്കുചെയ്യാൻ തവണ <xliff:g id="NUMBER_0">%1$d</xliff:g> തെറ്റായി ശ്രമിച്ചു. <xliff:g id="NUMBER_1">%2$d</xliff:g> ശ്രമങ്ങൾ കൂടി വിജയിച്ചില്ലെങ്കിൽ, ടാബ്ലെറ്റ് ഫാക്ടറി സ്ഥിരമായതിലേക്ക് പുനഃസജ്ജികരിക്കുകയും ഉപയോക്തൃ ഡാറ്റയെല്ലാം നഷ്ടപ്പെടുകയും ചെയ്യും."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"ടിവി അൺലോക്കുചെയ്യൽ <xliff:g id="NUMBER_0">%1$d</xliff:g> തവണ തെറ്റായി ചെയ്തു. <xliff:g id="NUMBER_1">%2$d</xliff:g>-ൽ കൂടുതൽ പരാജയപ്പെട്ട ശ്രമങ്ങൾക്കുശേഷം, ടിവി ഫാക്ടറി സ്ഥിരതയിലേക്ക് പുനഃസജ്ജീകരിക്കുകയും എല്ലാ ഉപയോക്തൃ വിവരവും നഷ്ടപ്പെടുകയും ചെയ്യും."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"നിങ്ങൾ <xliff:g id="NUMBER_0">%1$d</xliff:g> തവണ ഫോൺ അൺലോക്കുചെയ്യാൻ തെറ്റായി ശ്രമിച്ചു. <xliff:g id="NUMBER_1">%2$d</xliff:g> ശ്രമങ്ങൾ കൂടി പരാജയപ്പെടുകയാണെങ്കിൽ, ഫോൺ ഫാക്ടറി സ്ഥിരമായതിലേക്ക് പുനഃസജ്ജികരിക്കുകയും ഉപയോക്തൃ ഡാറ്റയെല്ലാം നഷ്ടപ്പെടുകയും ചെയ്യും."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"നിങ്ങൾ <xliff:g id="NUMBER">%d</xliff:g> തവണ ടാബ്ലെറ്റ് അൺലോക്കുചെയ്യാൻ തെറ്റായി ശ്രമിച്ചു. ടാബ്ലെറ്റ് ഇപ്പോൾ ഫാക്ടറി സ്ഥിരമായതിലേക്ക് പുനസജ്ജീകരിക്കും."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"നിങ്ങൾ ടിവി അൺലോക്കുചെയ്യൽ <xliff:g id="NUMBER">%d</xliff:g> തവണ തെറ്റായി ചെയ്തു. ഇപ്പോൾ ടിവി, ഫാക്ടറി സ്ഥിരതയിലേക്ക് പുനഃസജ്ജീകരിക്കും."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"നിങ്ങൾ <xliff:g id="NUMBER">%d</xliff:g> തവണ ഫോൺ അൺലോക്കുചെയ്യാൻ തെറ്റായി ശ്രമിച്ചു. ഫോൺ ഇപ്പോൾ ഫാക്ടറി സ്ഥിരമായതിലേക്ക് പുനസജ്ജീകരിക്കും."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"പദം"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"ലിങ്ക്"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"ലൈൻ"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"ഫാക്ടറി പരിശോധന പരാജയപ്പെട്ടു"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"/system/app എന്നതിൽ ഇൻസ്റ്റാൾ ചെയ്ത പാക്കേജുകൾക്കായി മാത്രം FACTORY_TEST പ്രവർത്തനം പിന്തുണയ്ക്കുന്നു."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"FACTORY_TEST പ്രവർത്തനം നൽകുന്ന പാക്കേജൊന്നും തന്നെ കണ്ടെത്തിയില്ല."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"ഫോർമാറ്റുചെയ്യുന്നു…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"ഇട്ടിട്ടില്ല"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"പൊരുത്തമുള്ള പ്രവർത്തനങ്ങളൊന്നും കണ്ടെത്തിയില്ല."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"മീഡിയ ഔട്ട്പുട്ട് റൂട്ടുചെയ്യുക"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"മീഡിയ ഔട്ട്പുട്ട് റൂട്ടുചെയ്യുക"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"മീഡിയ ഔട്ട്പുട്ടിനെ മറ്റ് ബാഹ്യ ഉപകരണങ്ങളിലേക്ക് റൂട്ടുചെയ്യാൻ ഒരു അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"ഇൻസ്റ്റാൾ സെഷനുകൾ റീഡുചെയ്യുക"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"ഇൻസ്റ്റാൾ സെഷനുകൾ റീഡുചെയ്യുക"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"ഇൻസ്റ്റാൾ ചെയ്ത സെഷനുകൾ റീഡുചെയ്യുന്നതിന് ഒരു അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. സജീവ പാക്കേജ് ഇൻസ്റ്റാളേഷനുകളെക്കുറിച്ചുള്ള വിശദാംശങ്ങൾ കാണുന്നതിന് ഇത് അനുവദിക്കുന്നു."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"പാക്കേജുകൾ ഇൻസ്റ്റാൾ ചെയ്യാൻ അഭ്യർത്ഥിക്കുക"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"പാക്കേജുകൾ ഇൻസ്റ്റാൾ ചെയ്യാൻ അഭ്യർത്ഥിക്കുക"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"പാക്കേജുകളുടെ ഇൻസ്റ്റാളേഷൻ അഭ്യർത്ഥിക്കാൻ ഒരു അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"സൂം ചെയ്യൽ നിയന്ത്രണങ്ങൾക്ക് രണ്ട് തവണ സ്പർശിക്കുക"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"വിജറ്റ് ചേർക്കാനായില്ല."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"ഉപയോക്തൃനാമമോ പാസ്വേഡോ അസാധുവാണ്."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"നിങ്ങളുടെ ഉപയോക്തൃനാമമോ പാസ്വേഡോ മറന്നുപോയോ?\n"<b>"google.com/accounts/recovery"</b>" സന്ദർശിക്കുക."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"അക്കൗണ്ട് പരിശോധിക്കുന്നു…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"നിങ്ങളുടെ പിൻ <xliff:g id="NUMBER_0">%d</xliff:g> തവണ തെറ്റായി ടൈപ്പുചെയ്തു. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> സെക്കൻഡിനുള്ളിൽ വീണ്ടും ശ്രമിക്കുക."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"നിങ്ങളുടെ പാസ്വേഡ് <xliff:g id="NUMBER_0">%d</xliff:g> തവണ തെറ്റായി ടൈപ്പുചെയ്തു. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> സെക്കൻഡിനുള്ളിൽ വീണ്ടും ശ്രമിക്കുക."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"നിങ്ങളുടെ പാറ്റേൺ <xliff:g id="NUMBER_0">%d</xliff:g> തവണ തെറ്റായി വരച്ചു. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> സെക്കൻഡിനുള്ളിൽ വീണ്ടും ശ്രമിക്കുക."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"നിങ്ങൾ ഫോൺ അൺലോക്കുചെയ്യാൻ തവണ <xliff:g id="NUMBER_0">%d</xliff:g> തെറ്റായി ശ്രമിച്ചു. <xliff:g id="NUMBER_1">%d</xliff:g> ശ്രമങ്ങൾ കൂടി വിജയിച്ചില്ലെങ്കിൽ, ടാബ്ലെറ്റ് ഫാക്ടറി ഡിഫോൾട്ടിലേക്ക് പുനഃസജ്ജികരിക്കുകയും ഉപയോക്തൃ ഡാറ്റയെല്ലാം നഷ്ടപ്പെടുകയും ചെയ്യും."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"ടിവി അൺലോക്കുചെയ്യൽ <xliff:g id="NUMBER_0">%d</xliff:g> തവണ തെറ്റായി ചെയ്തു. <xliff:g id="NUMBER_1">%d</xliff:g>-ൽ കൂടുതൽ പരാജയപ്പെട്ട ശ്രമങ്ങൾക്കുശേഷം, ടിവി ഫാക്ടറി സ്ഥിരതയിലേക്ക് പുനഃസജ്ജീകരിക്കുകയും എല്ലാ ഉപയോക്തൃ വിവരവും നഷ്ടപ്പെടുകയും ചെയ്യും."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"നിങ്ങൾ ഫോൺ അൺലോക്കുചെയ്യാൻ തവണ <xliff:g id="NUMBER_0">%d</xliff:g> തെറ്റായി ശ്രമിച്ചു. <xliff:g id="NUMBER_1">%d</xliff:g> ശ്രമങ്ങൾ കൂടി വിജയിച്ചില്ലെങ്കിൽ, ഫോൺ ഫാക്ടറി ഡിഫോൾട്ടിലേക്ക് പുനഃസജ്ജികരിക്കുകയും ഉപയോക്തൃ ഡാറ്റയെല്ലാം നഷ്ടപ്പെടുകയും ചെയ്യും."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"നിങ്ങളുടെ പിൻ <xliff:g id="NUMBER_0">%1$d</xliff:g> തവണ തെറ്റായി ടൈപ്പുചെയ്തു. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> സെക്കൻഡിനുള്ളിൽ വീണ്ടും ശ്രമിക്കുക."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"നിങ്ങളുടെ പാസ്വേഡ് <xliff:g id="NUMBER_0">%1$d</xliff:g> തവണ തെറ്റായി ടൈപ്പുചെയ്തു. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> സെക്കൻഡിനുള്ളിൽ വീണ്ടും ശ്രമിക്കുക."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"നിങ്ങളുടെ പാറ്റേൺ <xliff:g id="NUMBER_0">%1$d</xliff:g> തവണ തെറ്റായി വരച്ചു. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> സെക്കൻഡിനുള്ളിൽ വീണ്ടും ശ്രമിക്കുക."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"നിങ്ങൾ ഫോൺ അൺലോക്കുചെയ്യാൻ തവണ <xliff:g id="NUMBER_0">%1$d</xliff:g> തെറ്റായി ശ്രമിച്ചു. <xliff:g id="NUMBER_1">%2$d</xliff:g> ശ്രമങ്ങൾ കൂടി വിജയിച്ചില്ലെങ്കിൽ, ടാബ്ലെറ്റ് ഫാക്ടറി ഡിഫോൾട്ടിലേക്ക് പുനഃസജ്ജികരിക്കുകയും ഉപയോക്തൃ ഡാറ്റയെല്ലാം നഷ്ടപ്പെടുകയും ചെയ്യും."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"ടിവി അൺലോക്കുചെയ്യൽ <xliff:g id="NUMBER_0">%1$d</xliff:g> തവണ തെറ്റായി ചെയ്തു. <xliff:g id="NUMBER_1">%2$d</xliff:g>-ൽ കൂടുതൽ പരാജയപ്പെട്ട ശ്രമങ്ങൾക്കുശേഷം, ടിവി ഫാക്ടറി സ്ഥിരതയിലേക്ക് പുനഃസജ്ജീകരിക്കുകയും എല്ലാ ഉപയോക്തൃ വിവരവും നഷ്ടപ്പെടുകയും ചെയ്യും."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"നിങ്ങൾ ഫോൺ അൺലോക്കുചെയ്യാൻ തവണ <xliff:g id="NUMBER_0">%1$d</xliff:g> തെറ്റായി ശ്രമിച്ചു. <xliff:g id="NUMBER_1">%2$d</xliff:g> ശ്രമങ്ങൾ കൂടി വിജയിച്ചില്ലെങ്കിൽ, ഫോൺ ഫാക്ടറി ഡിഫോൾട്ടിലേക്ക് പുനഃസജ്ജികരിക്കുകയും ഉപയോക്തൃ ഡാറ്റയെല്ലാം നഷ്ടപ്പെടുകയും ചെയ്യും."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"നിങ്ങൾ ടാബ്ലെറ്റ് അൺലോക്കുചെയ്യാൻ തവണ <xliff:g id="NUMBER">%d</xliff:g> തെറ്റായി ശ്രമിച്ചു. ടാബ്ലെറ്റ് ഇപ്പോൾ ഫാക്ടറി ഡിഫോൾട്ടിലേക്ക് പുനസജ്ജീകരിക്കും."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"നിങ്ങൾ ടിവി അൺലോക്കുചെയ്യൽ <xliff:g id="NUMBER">%d</xliff:g> തവണ തെറ്റായി ചെയ്തു. ഇപ്പോൾ ടിവി, ഫാക്ടറി സ്ഥിരതയിലേക്ക് പുനഃസജ്ജീകരിക്കും."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"നിങ്ങൾ ഫോൺ അൺലോക്കുചെയ്യാൻ തവണ <xliff:g id="NUMBER">%d</xliff:g> തെറ്റായി ശ്രമിച്ചു. ഫോൺ ഇപ്പോൾ ഫാക്ടറി ഡിഫോൾട്ടിലേക്ക് പുനസജ്ജീകരിക്കും."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"നിങ്ങളുടെ അൺലോക്ക് പാറ്റേൺ <xliff:g id="NUMBER_0">%d</xliff:g> തവണ തെറ്റായി വരച്ചു. <xliff:g id="NUMBER_1">%d</xliff:g> ശ്രമങ്ങൾ കൂടി വിജയിച്ചില്ലെങ്കിൽ, ഒരു ഇമെയിൽ അക്കൗണ്ട് ഉപയോഗിച്ച് ടാബ്ലെറ്റ് അൺലോക്ക് ചെയ്യാൻ നിങ്ങളോട് ആവശ്യപ്പെടും.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> സെക്കൻഡിനുള്ള വീണ്ടും ശ്രമിക്കുക."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"നിങ്ങൾ അൺലോക്കുചെയ്യൽ പാറ്റേൺ <xliff:g id="NUMBER_0">%d</xliff:g> തവണ തെറ്റായി വരച്ചു. <xliff:g id="NUMBER_1">%d</xliff:g>-ൽ കൂടുതൽ പരാജയപ്പെട്ട ശ്രമങ്ങൾക്കുശേഷം, ഒരു ഇമെയിൽ അക്കൗണ്ട് ഉപയോഗിച്ച് നിങ്ങളുടെ ടിവി അൺലോക്കുചെയ്യുന്നതിന് ആവശ്യപ്പടും. \n\n <xliff:g id="NUMBER_2">%d</xliff:g> സെക്കൻഡിനുള്ളിൽ വീണ്ടും ശ്രമിക്കുക."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"നിങ്ങളുടെ അൺലോക്ക് പാറ്റേൺ <xliff:g id="NUMBER_0">%d</xliff:g> തവണ തെറ്റായി വരച്ചു. <xliff:g id="NUMBER_1">%d</xliff:g> ശ്രമങ്ങൾ കൂടി വിജയിച്ചില്ലെങ്കിൽ, ഒരു ഇമെയിൽ അക്കൗണ്ട് ഉപയോഗിച്ച് ഫോൺ അൺലോക്ക് ചെയ്യാൻ നിങ്ങളോട് ആവശ്യപ്പെടും.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> സെക്കൻഡിനുള്ള വീണ്ടും ശ്രമിക്കുക."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"നിങ്ങളുടെ അൺലോക്ക് പാറ്റേൺ <xliff:g id="NUMBER_0">%1$d</xliff:g> തവണ തെറ്റായി വരച്ചു. <xliff:g id="NUMBER_1">%2$d</xliff:g> ശ്രമങ്ങൾ കൂടി വിജയിച്ചില്ലെങ്കിൽ, ഒരു ഇമെയിൽ അക്കൗണ്ട് ഉപയോഗിച്ച് ടാബ്ലെറ്റ് അൺലോക്ക് ചെയ്യാൻ നിങ്ങളോട് ആവശ്യപ്പെടും.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> സെക്കൻഡിനുള്ള വീണ്ടും ശ്രമിക്കുക."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"നിങ്ങൾ അൺലോക്കുചെയ്യൽ പാറ്റേൺ <xliff:g id="NUMBER_0">%1$d</xliff:g> തവണ തെറ്റായി വരച്ചു. <xliff:g id="NUMBER_1">%2$d</xliff:g>-ൽ കൂടുതൽ പരാജയപ്പെട്ട ശ്രമങ്ങൾക്കുശേഷം, ഒരു ഇമെയിൽ അക്കൗണ്ട് ഉപയോഗിച്ച് നിങ്ങളുടെ ടിവി അൺലോക്കുചെയ്യുന്നതിന് ആവശ്യപ്പടും. \n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> സെക്കൻഡിനുള്ളിൽ വീണ്ടും ശ്രമിക്കുക."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"നിങ്ങളുടെ അൺലോക്ക് പാറ്റേൺ <xliff:g id="NUMBER_0">%1$d</xliff:g> തവണ തെറ്റായി വരച്ചു. <xliff:g id="NUMBER_1">%2$d</xliff:g> ശ്രമങ്ങൾ കൂടി വിജയിച്ചില്ലെങ്കിൽ, ഒരു ഇമെയിൽ അക്കൗണ്ട് ഉപയോഗിച്ച് ഫോൺ അൺലോക്ക് ചെയ്യാൻ നിങ്ങളോട് ആവശ്യപ്പെടും.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> സെക്കൻഡിനുള്ള വീണ്ടും ശ്രമിക്കുക."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"നീക്കംചെയ്യുക"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"മുകളിൽക്കൊടുത്തിരിക്കുന്ന ശുപാർശചെയ്ത ലെവലിലേക്ക് വോളിയം വർദ്ധിപ്പിക്കണോ?\n\nഉയർന്ന വോളിയത്തിൽ ദീർഘനേരം കേൾക്കുന്നത് നിങ്ങളുടെ ശ്രവണ ശേഷിയെ ദോഷകരമായി ബാധിക്കാം."</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">ഒരു മണിക്കൂറത്തേക്ക്</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> വരെ"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> വരെ (അടുത്ത അലാറം)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"നിങ്ങൾ ഇത് ഓഫാക്കും വരെ"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"\'ശല്ല്യപ്പെടുത്തരുത്\' ഓഫാക്കുന്നതുവരെ"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-mn-rMN-watch/strings.xml b/core/res/res/values-mn-rMN-watch/strings.xml
index 6387aed..ceea490 100644
--- a/core/res/res/values-mn-rMN-watch/strings.xml
+++ b/core/res/res/values-mn-rMN-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_0">%1$d</xliff:g>-ны <xliff:g id="NUMBER_1">%2$d</xliff:g> апп."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Мэдрэгч"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"харилцагчийн хаягт хандах"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"энэ цагийн байршилд хандах"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"Хуанлид хандах"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"SMS мессежийг илгээх, харах"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"Цагныхаа зураг, медиа, файлд хандах"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"дуу хураах"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"зураг авах, бичлэг хийх"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"залгах, дуудлагыг зохион байгуулах"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"таны биеийн байдлын талаарх мэдрэгч бүхий өгөгдөлд хандах"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"статус мөр байхыг зөвшөөрөх"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"бие мэдрэгчид (зүрхний түвшин шалгагч г.м) хандах"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"тодорхой байршилд (GPS, сүлжээнд суурилсан) хандах"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"ойролцоох байршилд (сүлжээнд суурилсан) хандах"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"SIM карт руу комманд илгээх"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"сүлжээнд бүрэн нэвтрэх"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"Профайл, төхөөрөмж эзэмшигчийг зохион байгуулах"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"WiMAX статусыг солих"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"Android Beam дамжуулалтын статусыг хүлээн авах"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"Медиа хадгалах байршлыг тодорхойлох"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"Суулгах үеийг унших"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"Багц суулгахыг хүсэх"</string>
</resources>
diff --git a/core/res/res/values-mn-rMN/strings.xml b/core/res/res/values-mn-rMN/strings.xml
index 3b5531d..ef01668 100644
--- a/core/res/res/values-mn-rMN/strings.xml
+++ b/core/res/res/values-mn-rMN/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Кредит картын дугаар болон нууц үг зэрэг хувийн датаг агуулж байна."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"статус самбарыг идэвхгүй болгох болон өөрчлөх"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Апп нь статус самбарыг идэвхгүй болгох эсвэл систем дүрсийг нэмэх, хасах боломжтой."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"статус самбар"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"статусын хэсэг болох"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Апп нь статус самбар болох боломжтой."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"статус самбарыг нээх/хаах"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Апп нь статус самбарыг дэлгэх болон хаах боломжтой."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Апп нь WAP мессежийг хүлээн авах болон биелүүлэх боломжтой. Энэ зөвшөөрөл нь танд илгээсэн мессежийг танд харуулалгүйгээр хянах эсвэл устгах боломжийг агуулна."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"ажиллаж байгаа апп-г дуудах"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Апп нь одоо ажиллаж байгаа болон сүүлд ажилласан даалгаврын талаарх мэдээллийг авах боломжтой. Ингэснээр апп нь төхөөмж дээрх ямар аппликешнүүд ашиглагдсан талаарх мэдээлийг олох боломжтой."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Профайл болон төхөөрөмжийн эзэмшигчийг удирдах"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"Профайл, төхөөрөмж эзэмшигчийг удирдах"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Аппликейшнд профайл болон төхөөрөмж эзэмшигчийг сонгохыг зөвшөөрөх"</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"ажиллаж байгаа апп-уудыг дахин эрэмбэлэх"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Апп нь даалгавруудыг нүүрлүү болон арлуу зөөх боломжтой. Апп нь энийг таны оролцоогүйгээр хийж болзошгүй"</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Апп нь таны таблетын ирсэн гарсан дуудлага зэргийг агуулсан дуудлагын логыг унших боломжтой. Хортой апп нь энийг ашиглан таны дуудлагын логыг өөрчлөх болон арилгах боломжтой."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Апп-д орж ирсэн болон гадагш хийсэн телевизийн дуудлагын бүртгэлийг өөрчлөхийг зөвшөөрдөг. Хорлонтой апликейшнүүд үүнийг ашиглан таны дуудлагын бүртгэлийг устгах эсвэл өөрчилж болох юм."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Апп нь таны утасны ирсэн гарсан дуудлага зэргийг агуулсан дуудлагын логыг өөрчлөх боломжтой. Хортой апп нь энийг ашиглан таны дуудлагын логыг өөрчлөх болон арилгах боломжтой."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"биеийн сенсор (зүрхний цохилт хянагч гэх мэт)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"биеийн мэдрэгчид хандах (зүрхний хэмнэл шалгагч г.м)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Апп-т таны зүрхний цохилт гэх мэт биеийн байдлыг хянадаг мэдрэгчдийн датанд хандалт хийх боломж олгоно."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"календарийн хуваарийн нууц мэдээллийг унших"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Апп нь таны таблет дээр хадгалагдсан найзууд болон хамтран ажиллагсдын календарийн бүх хуваарийг унших боломжтой. Энэ нь апп-д таны календарийн датаг нууц эсвэл эмзэг эсэхээс нь үл хамааран хуваалцах эсвэл хадгалах боломжийг олгоно."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Апп нь утсан дээр та болон таны найзууд, хамтран ажиллагсдын өөрчилж чадах үйл явдлуудыг нэмэх, хасах болон солих боломжтой. Энэ нь апп-д, календарь эзэмшигчээс ирсэн мэт харагдах мессежийг илгээх эсвэл эзэмшигчид нь мэдэгдэлгүйгээр үйл явдлуудыг өөрчлөх боломжийг олгоно."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"байршил нийлүүлэгчийн нэмэлт тушаалд хандах"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Апп нь байршил нийлүүлэгчийн нэмэлт тушаалд хандах боломжтой. Энэ нь апп-д GPS эсвэл бусад байршлын үйлчилгээний ажиллагаанд нөлөөлөх боломжийг олгоно."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"Тодорхой байршил(GPS болон сүлжээнд суурилсан)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"тодорхой байршилд хандах (GPS, сүлжээнд суурилсан)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Апп нь GPS эсвэл үүрэн цамхаг болон Wi-Fi зэрэг сүлжээний байршлын эх үүсвэрийг ашиглан таны тодорхой байршлыг авах боломжтой. Эдгээр байршлын үйлчилгээнүүд нь асаалттай байх шаардлагатай ба таны төхөөрөмж дээрх апп-ууд ашиглах боломжтой байх шаардлагатай. Апп-ууд энийг ашиглан таныг хаана байгааг тогтоох боломжтой ба батерей зарцуулалт нэмэгдэнэ."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"ойролцоох байршил(сүлжээнд суурилсан)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"ойролцоох байршилд хандах (сүлжээнд суурилсан)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Апп нь таны ойролцоох байршлыг оло боломжтой. Энэ байршил нь үүрэн цамхаг болон Wi-Fi зэрэг сүлжээний байршлын эх сурвалжийг ашигладаг байршлын үйлчилгээнээс олдоно. Эдгээр байршлын үйлчилгээнүүд нь таны төхөөрөмж дээр асаалттай байх шаардлагатай ба апп-д тэдгээрийг ашиглах боломжтой байх шаардлагатай. Апп-д тэдгээрийг ашиглан таны байршлыг ойролцоогоор олох боломжтой."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"Аудио тохиргоо солих"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Апп нь дууны хэмжээ, спикерын гаралтад ашиглагдах глобал аудио тохиргоог өөрчлөх боломжтой."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"аудио бичих"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Апп нь микрофоноор аудио бичих боломжтой. Энэ зөвшөөрөл нь апп-д ямар ч үед таны зөвшөөрөлгүйгээр аудио бичих боломжийг олгоно."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"сим холбоо"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"SIM картад тушаал илгээх"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Апп-д SIM рүү комманд илгээхийг зөвшөөрнө. Энэ маш аюултай."</string>
<string name="permlab_camera" msgid="3616391919559751192">"зураг авах болон видео бичих"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Апп нь камераар зураг авах болон видео бичих боломжтой. Энэ зөвшөөрөл нь апп-д ямар ч үед таны зөвшөөрөлгүйгээр камер ашиглах боломжийг олгоно."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Апп нь утсанд мэдэгдэж байгаа акаунтын жагсаалтыг авах боломжтой. Энд таны суулгасан аппликешнүүдийн үүсгэсэн бүх акаунтууд хамрагдана."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"сүлжээний холболтыг үзэх"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Апп нь сүлжээ байгаа болон холбогдсон эсэх зэрэг сүлжээний холболтын талаарх мэдээллийг харах боломжтой."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"сүлжээнд бүрэн хандах"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"сүлжээнд бүрэн нэвтрэх"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Апп нь сүлжээний сокетыг үүсгэх болон тусгай сүлжээний протокол ашиглах боломжтой. Хөтөч болон бусад аппликешнүүд Интернетээр дата илгээх боломжтой тул энэ зөвшөөрөл нь Интернетээр дата илгээхэд шаардлагагүй."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"сүлжээний холболтыг солих"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Апп нь сүлжээний холболтын статусыг солих боломжтой."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Апп нь утасны дотоод блютүүтыг тохируулах боломжтой ба гадаад төхөөрөмжийг олох болон хос үүсгэх боломжтой."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"WiMAX-д холбогдох болон салах"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Апп нь WiMAX идэвхтэй эсэх болон холбогдсон WiMAX сүлжээний талаар мэдээллийг тодорхойлох боломжтой."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX статусыг өөрчлөх"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"WiMAX статусыг солих"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Апп нь WiMAX сүлжээнд таблетыг холбох болон салгах боломжтой."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Телевизийг WiMAX сүлжээнд холбох, салгахыг апп-д зөвшөөрдөг."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Апп нь WiMAX сүлжээнд утсыг холбох болон салгах боломжтой."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Мэдрэгчтэй дэлгэцний калибрешн параметрийг өөрчлөхийг апп-д зөвшөөрнө. Энгийн апп-д шаардлагагүй."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"хандалтын DRM сертификат"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Аппликешнд DRM сертификатыг ашиглах болон нийлүүлэхийг зөвшөөрнө. Энгийн апп-уудад хэзээ ч ашиглагдахгүй."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Андройд Бийм дамжуулалтын статусыг хүлээн авах"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"Android Beam дамжуулалтын төлөвийг авах"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Одоогийн Андройд Бийм дамжуулалтын мэдээллийг хүлээн авахыг аппликешнд зөвшөөрөх"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"DRM сертификатыг устгах"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Аппликешнд DRM сертификатыг устгахыг зөвшөөрнө. Энгийн апп-уудад хэзээ ч ашиглагдахгүй."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Буруу PIN код."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Тайлах бол Цэсийг дараад 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Яаралтай дугаар"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Үйлчилгээ байхгүй."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Үйлчилгээ байхгүй"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Дэлгэц түгжигдсэн."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Яаралтай дуудлага хийх буюу эсвэл түгжээг тайлах бол цэсийг дарна уу."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Тайлах бол цэсийг дарна уу."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Хэрэглэгчийн зааврыг харах эсвэл Хэрэглэгчдэд Туслах төвтэй холбоо барина уу."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM карт түгжигдсэн."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"SIM картны түгжээг гаргаж байна…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Та тайлах хээг <xliff:g id="NUMBER_0">%d</xliff:g> удаа буруу зурлаа. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> секундын дараа дахин оролдоно уу."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Та нууц үгээ <xliff:g id="NUMBER_0">%d</xliff:g> удаа буруу бичив. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> секундын дараа дахин оролдоно уу."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Та PIN кодоо <xliff:g id="NUMBER_0">%d</xliff:g> удаа буруу бичив. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> секундын дараа дахин оролдоно уу."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Та тайлах хээг <xliff:g id="NUMBER_0">%d</xliff:g> удаа буруу зурлаа. <xliff:g id="NUMBER_1">%d</xliff:g> удаа дахин буруу оруулбал, та таблетаа тайлахын тулд Google нэвтрэлтээ ашиглах шаардлагатай болно.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> секундын дараа дахин оролдоно уу."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Та зурган түгжээгээ <xliff:g id="NUMBER_0">%d</xliff:g> удаа буруу оруулсан байна. Та дахиж <xliff:g id="NUMBER_1">%d</xliff:g> удаа буруу оруулсны дараагаар та телевизийнхээ түгжээг Google акаунтандаа нэвтэрч тайлах шаардлагатай болно.\n\n Та <xliff:g id="NUMBER_2">%d</xliff:g> секундийн дараа дахин оролдоно уу."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Та тайлах хээг <xliff:g id="NUMBER_0">%d</xliff:g> удаа буруу зурлаа. <xliff:g id="NUMBER_1">%d</xliff:g> удаа дахин буруу оролдвол, та таблетаа тайлахын тулд Google нэвтрэлтээ ашиглах шаардлагатай болно.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> секундын дараа дахин оролдоно уу."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Та таблетыг тайлах гэж <xliff:g id="NUMBER_0">%d</xliff:g> удаа буруу оролдлоо. <xliff:g id="NUMBER_1">%d</xliff:g> удаа дахин буруу оролдвол таблет үйлдвэрийн үндсэн утгаараа тохируулагдах ба хэрэглэгчийн дата бүхэлдээ устана."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Та <xliff:g id="NUMBER_0">%d</xliff:g> удаа телевизийнхээ түгжээг тайлах оролдлогыг амжилтгүй хийсэн байна. Та дахиж <xliff:g id="NUMBER_1">%d</xliff:g> удаа буруу оруулсны дараагаар телевизийн тохиргоо үйлдвэрээс гарсан анхны тохиргоонд шилжих бөгөөд хэрэглэгчийн бүх мэдээлэл устах болно."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Та утсыг тайлах гэж <xliff:g id="NUMBER_0">%d</xliff:g> удаа буруу оролдлоо. <xliff:g id="NUMBER_1">%d</xliff:g> удаа дахин буруу оролдвол утас үйлдвэрийн үндсэн утгаараа тохируулагдах ба хэрэглэгчийн дата бүхэлдээ устана."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Та тайлах хээг <xliff:g id="NUMBER_0">%1$d</xliff:g> удаа буруу зурлаа. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> секундын дараа дахин оролдоно уу."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Та нууц үгээ <xliff:g id="NUMBER_0">%1$d</xliff:g> удаа буруу бичив. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> секундын дараа дахин оролдоно уу."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Та PIN кодоо <xliff:g id="NUMBER_0">%1$d</xliff:g> удаа буруу бичив. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> секундын дараа дахин оролдоно уу."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Та тайлах хээг <xliff:g id="NUMBER_0">%1$d</xliff:g> удаа буруу зурлаа. <xliff:g id="NUMBER_1">%2$d</xliff:g> удаа дахин буруу оруулбал, та таблетаа тайлахын тулд Google нэвтрэлтээ ашиглах шаардлагатай болно.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундын дараа дахин оролдоно уу."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Та зурган түгжээгээ <xliff:g id="NUMBER_0">%1$d</xliff:g> удаа буруу оруулсан байна. Та дахиж <xliff:g id="NUMBER_1">%2$d</xliff:g> удаа буруу оруулсны дараагаар та телевизийнхээ түгжээг Google акаунтандаа нэвтэрч тайлах шаардлагатай болно.\n\n Та <xliff:g id="NUMBER_2">%3$d</xliff:g> секундийн дараа дахин оролдоно уу."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Та тайлах хээг <xliff:g id="NUMBER_0">%1$d</xliff:g> удаа буруу зурлаа. <xliff:g id="NUMBER_1">%2$d</xliff:g> удаа дахин буруу оролдвол, та таблетаа тайлахын тулд Google нэвтрэлтээ ашиглах шаардлагатай болно.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундын дараа дахин оролдоно уу."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Та таблетыг тайлах гэж <xliff:g id="NUMBER_0">%1$d</xliff:g> удаа буруу оролдлоо. <xliff:g id="NUMBER_1">%2$d</xliff:g> удаа дахин буруу оролдвол таблет үйлдвэрийн үндсэн утгаараа тохируулагдах ба хэрэглэгчийн дата бүхэлдээ устана."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Та <xliff:g id="NUMBER_0">%1$d</xliff:g> удаа телевизийнхээ түгжээг тайлах оролдлогыг амжилтгүй хийсэн байна. Та дахиж <xliff:g id="NUMBER_1">%2$d</xliff:g> удаа буруу оруулсны дараагаар телевизийн тохиргоо үйлдвэрээс гарсан анхны тохиргоонд шилжих бөгөөд хэрэглэгчийн бүх мэдээлэл устах болно."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Та утсыг тайлах гэж <xliff:g id="NUMBER_0">%1$d</xliff:g> удаа буруу оролдлоо. <xliff:g id="NUMBER_1">%2$d</xliff:g> удаа дахин буруу оролдвол утас үйлдвэрийн үндсэн утгаараа тохируулагдах ба хэрэглэгчийн дата бүхэлдээ устана."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Та таблетыг <xliff:g id="NUMBER">%d</xliff:g> удаа тайлах гэж буруу оролдлоо. Таблет одоо үйлдвэрийн үндсэн утгаараа тохируулагдах болно."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Та телевизийнхээ түгжээг <xliff:g id="NUMBER">%d</xliff:g> удаа буруу оруулсан байна. Телевиз үйлдвэрээс гарсан анхны тохиргоонд шилжих болно."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Та утсыг тайлах гэж <xliff:g id="NUMBER">%d</xliff:g> удаа буруу оролдлоо. Утас одоо үйлдвэрийн үндсэн утгаараа тохируулагдах болно."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"үг"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"холбоос"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"Мөр"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Үйлдлвэрийн тест бүтэлгүйтэв"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"FACTORY_TEST үйлдэл нь зөвхөн /system/app-д суусан багцуудад дэмжигдэнэ."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"FACTORY_TEST үйлдлийг хангах багц олдсонгүй."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Хэлбэршүүлж байна..."</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Оруулаагүй байна"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Таарах активити олдсонгүй."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Медиа гаралтыг чиглүүлэх"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"медиа гаралтын маршрут"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Аппликешн нь медиа гаралтыг бусад гадаад төхөөрөмжрүү чиглүүлэх боломжтой."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Суулгах сешн унших"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"Суулгах харилцан үйлдлийг унших"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Аппликешн-д суулгах сешн уншихыг зөвшөөрнө. Энэ нь идэвхтэй багцуудыг суулгалтын талаар дэлгэрэнгүй мэдээллийг үзэх боломж олгоно."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Багц суулгахыг хүсэх"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"багц суулгахыг хүсэх"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Аппликейшн нь багц суулгахыг хүсэх боломжтой."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Өсгөх контрол дээр хоёр удаа товшино уу"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Виджет нэмж чадсангүй."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Хэрэглэгчийн нэр эсвэл нууц үг буруу."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Хэрэглэгчийн нэр нууц үгээ мартсан уу?\n"<b>"google.com/accounts/recovery"</b>"-д зочилно уу."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Акаунт шалгаж байна…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Та PIN кодоо <xliff:g id="NUMBER_0">%d</xliff:g> удаа буруу бичив. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> секундын дараа дахин оролдоно уу."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Та PIN кодоо <xliff:g id="NUMBER_0">%d</xliff:g> удаа буруу бичив. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> секундын дараа дахин оролдоно уу."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Та тайлах хээг <xliff:g id="NUMBER_0">%d</xliff:g> удаа буруу зурлаа. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> секундын дараа дахин оролдоно уу."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Та таблетыг тайлах гэж <xliff:g id="NUMBER_0">%d</xliff:g> удаа буруу оролдлоо. <xliff:g id="NUMBER_1">%d</xliff:g> удаа дахин буруу оролдвол таблет үйлдвэрийн үндсэн утгаараа тохируулагдах ба хэрэглэгчийн дата бүхэлдээ устана."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Та <xliff:g id="NUMBER_0">%d</xliff:g> удаа телевизийнхээ түгжээг тайлах оролдлогыг амжилтгүй хийсэн байна. Та дахиж <xliff:g id="NUMBER_1">%d</xliff:g> удаа буруу оруулсны дараагаар телевизийн тохиргоо үйлдвэрээс гарсан анхны тохиргоонд шилжих бөгөөд хэрэглэгчийн бүх мэдээлэл устах болно."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Та утсыг тайлах гэж <xliff:g id="NUMBER_0">%d</xliff:g> удаа буруу оролдлоо. <xliff:g id="NUMBER_1">%d</xliff:g> удаа дахин буруу оролдвол утас үйлдвэрийн үндсэн утгаараа тохируулагдах ба хэрэглэгчийн дата бүхэлдээ устана."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Та PIN кодоо <xliff:g id="NUMBER_0">%1$d</xliff:g> удаа буруу бичив. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> секундын дараа дахин оролдоно уу."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Та PIN кодоо <xliff:g id="NUMBER_0">%1$d</xliff:g> удаа буруу бичив. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> секундын дараа дахин оролдоно уу."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Та тайлах хээг <xliff:g id="NUMBER_0">%1$d</xliff:g> удаа буруу зурлаа. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> секундын дараа дахин оролдоно уу."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Та таблетыг тайлах гэж <xliff:g id="NUMBER_0">%1$d</xliff:g> удаа буруу оролдлоо. <xliff:g id="NUMBER_1">%2$d</xliff:g> удаа дахин буруу оролдвол таблет үйлдвэрийн үндсэн утгаараа тохируулагдах ба хэрэглэгчийн дата бүхэлдээ устана."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Та <xliff:g id="NUMBER_0">%1$d</xliff:g> удаа телевизийнхээ түгжээг тайлах оролдлогыг амжилтгүй хийсэн байна. Та дахиж <xliff:g id="NUMBER_1">%2$d</xliff:g> удаа буруу оруулсны дараагаар телевизийн тохиргоо үйлдвэрээс гарсан анхны тохиргоонд шилжих бөгөөд хэрэглэгчийн бүх мэдээлэл устах болно."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Та утсыг тайлах гэж <xliff:g id="NUMBER_0">%1$d</xliff:g> удаа буруу оролдлоо. <xliff:g id="NUMBER_1">%2$d</xliff:g> удаа дахин буруу оролдвол утас үйлдвэрийн үндсэн утгаараа тохируулагдах ба хэрэглэгчийн дата бүхэлдээ устана."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Та таблетыг тайлах гэж <xliff:g id="NUMBER">%d</xliff:g> удаа буруу оролдлоо. Таблет одоо үйлдвэрийн үндсэн утгаараа тохируулагдах болно."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Та телевизийнхээ түгжээг <xliff:g id="NUMBER">%d</xliff:g> удаа буруу оруулсан байна. Телевиз үйлдвэрээс гарсан анхны тохиргоонд шилжих болно."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Та утсыг тайлах гэж <xliff:g id="NUMBER">%d</xliff:g> удаа буруу оролдлоо. Утас одоо үйлдвэрийн үндсэн утгаараа тохируулагдах болно."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Та тайлах хээг <xliff:g id="NUMBER_0">%d</xliff:g> удаа буруу зурлаа. <xliff:g id="NUMBER_1">%d</xliff:g> удаа дахин буруу оруулбал, та таблетаа тайлахын тулд имэйл акаунт шаардлагатай болно.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> секундын дараа дахин оролдоно уу."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Та зурган түгжээг <xliff:g id="NUMBER_0">%d</xliff:g> удаа буруу оруулсан байна. <xliff:g id="NUMBER_1">%d</xliff:g> удаа буруу оруулсны дараагаар та телевизийнхээ түгжээг и-мэйл дансаа ашиглан тайлах хэрэгтэй болно.\n\n Та <xliff:g id="NUMBER_2">%d</xliff:g> секундийн дараа дахин оролдоно уу."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Та тайлах хээг <xliff:g id="NUMBER_0">%d</xliff:g> удаа буруу зурлаа. <xliff:g id="NUMBER_1">%d</xliff:g> удаа дахин буруу оруулбал, та утсаа тайлахын тулд имэйл акаунтаа ашиглах шаардлагатай болно.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> секундын дараа дахин оролдоно уу."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Та тайлах хээг <xliff:g id="NUMBER_0">%1$d</xliff:g> удаа буруу зурлаа. <xliff:g id="NUMBER_1">%2$d</xliff:g> удаа дахин буруу оруулбал, та таблетаа тайлахын тулд имэйл акаунт шаардлагатай болно.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундын дараа дахин оролдоно уу."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Та зурган түгжээг <xliff:g id="NUMBER_0">%1$d</xliff:g> удаа буруу оруулсан байна. <xliff:g id="NUMBER_1">%2$d</xliff:g> удаа буруу оруулсны дараагаар та телевизийнхээ түгжээг и-мэйл дансаа ашиглан тайлах хэрэгтэй болно.\n\n Та <xliff:g id="NUMBER_2">%3$d</xliff:g> секундийн дараа дахин оролдоно уу."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Та тайлах хээг <xliff:g id="NUMBER_0">%1$d</xliff:g> удаа буруу зурлаа. <xliff:g id="NUMBER_1">%2$d</xliff:g> удаа дахин буруу оруулбал, та утсаа тайлахын тулд имэйл акаунтаа ашиглах шаардлагатай болно.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундын дараа дахин оролдоно уу."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Устгах"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Дууг санал болгосноос чанга болгож өсгөх үү?\n\nУрт хугацаанд чанга хөгжим сонсох нь таны сонсголыг муутгаж болно."</string>
@@ -1468,6 +1466,7 @@
<item quantity="one">1 цагийн турш:</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> хүртэл"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> хүртэл (дараагийн сэрүүлэг)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Таныг унтраах хүртэл"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"\"Бүү саад бол\"-ыг унтраах хүртэл"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-mr-rIN-watch/strings.xml b/core/res/res/values-mr-rIN-watch/strings.xml
index 0a60aad..223f8fa 100644
--- a/core/res/res/values-mr-rIN-watch/strings.xml
+++ b/core/res/res/values-mr-rIN-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_1">%2$d</xliff:g> पैकी <xliff:g id="NUMBER_0">%1$d</xliff:g> अॅप"</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"सेन्सर"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"आपल्या संपर्कांमध्ये प्रवेश करा"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"या घड्याळाच्या स्थानामध्ये प्रवेश करा"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"आपल्या कॅलेंडरमध्ये प्रवेश करा"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"SMS संदेश पाठवा आणि पहा"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"आपल्या घड्याळावरील फोटो, मीडिया आणि फायलींमध्ये प्रवेश करा"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"ऑडिओ रेकॉर्ड करा"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"चित्रे घ्या आणि व्हिडिओ रेकॉर्ड करा"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"फोन कॉल करा आणि व्यवस्थापित करा"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"आपल्या महत्त्वाच्या मापनांविषयी सेन्सर डेटामध्ये प्रवेश करा"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"स्टेटस बार होऊ द्या"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"शरीर सेन्सरमध्ये (हृदय गती मॉनिटरसारखे) प्रवेश करा"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"अचूक स्थानामध्ये (GPS आणि नेटवर्क-आधारित) प्रवेश करा"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"अंदाजे स्थानामध्ये (नेटवर्क-आधारित) प्रवेश करा"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"सिमला आदेश पाठवा"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"पूर्ण नेटवर्क प्रवेश आहे"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"प्रोफाईल आणि डिव्हाइस मालक व्यवस्थापित करा"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"WiMAX स्थिती बदला"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"Android बीम स्थानांतरण स्थिती प्राप्त करा"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"मीडिया आउटपुट मार्गस्थ करा"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"स्थापना सत्र वाचा"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"पॅकेज स्थापित करण्यासाठी विनंती करा"</string>
</resources>
diff --git a/core/res/res/values-mr-rIN/strings.xml b/core/res/res/values-mr-rIN/strings.xml
index 5956634..4e43265 100644
--- a/core/res/res/values-mr-rIN/strings.xml
+++ b/core/res/res/values-mr-rIN/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"क्रेडिट कार्ड नंबर आणि संकेतशब्द यासारखा वैयक्तिक डेटा समाविष्ट करते."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"स्टेटस बार अक्षम करा किंवा सुधारित करा"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"स्टेटस बार अक्षम करण्यासाठी किंवा सिस्टीम चिन्हे जोडण्यासाठी आणि काढण्यासाठी अॅप ला अनुमती देते."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"स्टेटस बार"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"स्टेटस बार होऊ द्या"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"स्टेटस बार होण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"स्टेटस बार विस्तृत करा/संकुचित करा"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"स्टेटस बार विस्तृत करण्यासाठी किंवा संक्षिप्त करण्यासाठी अॅप ला अनुमती देते."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"WAP संदेश प्राप्त करण्यास आणि त्यावर प्रक्रिया करण्यासाठी अॅप ला अनुमती देते. ही परवानगी आपल्याला पाठविलेले संदेश आपल्याला न दर्शविता त्यांचे परीक्षण करण्याची आणि ते हटविण्याची क्षमता समाविष्ट करते."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"चालणारे अॅप्स पुनर्प्राप्त करा"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"सध्या आणि अलीकडे चालणार्या कार्यांविषयी माहिती पुनर्प्राप्त करण्यासाठी अॅप ला अनुमती देते. हे डिव्हाइसवर कोणते अनुप्रयोग वापरले जात आहेत त्याविषयी माहिती शोधण्यासाठी अॅप ला अनुमती देऊ शकतात."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"प्रोफाईल आणि डिव्हाइस मालक व्यवस्थापित करा"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"प्रोफाईल आणि डिव्हाइस मालक व्यवस्थापित करा"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"प्रोफाईल मालक आणि डिव्हाइस मालक सेट करण्याची अॅप्सना अनुमती द्या."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"चालणारे अॅप्स पुनर्क्रमित करा"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"समोर आणि पार्श्वभूमीवर कार्ये हलविण्यासाठी अॅप ला अनुमती देते. अॅप हे आपल्या इनपुटशिवाय करू शकतो."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"येणार्या आणि केल्या जाणार्या कॉलविषयीच्या डेटासह, आपल्या टॅब्लेटचा कॉल लॉग सुधारित करण्यासाठी अॅप ला अनुमती देते. दुर्भावनापूर्ण अॅप्स आपला कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"येणार्या आणि केल्या जाणार्या कॉलविषयीच्या डेटासह, आपल्या टीव्हीचा कॉल लॉग सुधारित करण्यासाठी अॅपला अनुमती देते. दुर्भावनापूर्ण अॅप्स आपला कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"येणार्या आणि केल्या जाणार्या कॉलविषयीच्या डेटासह, आपल्या फोनचा कॉल लॉग सुधारित करण्यासाठी अॅप ला अनुमती देते. दुर्भावनापूर्ण अॅप्स आपला कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"(हृदय गती मॉनिटरसारखे) शरीर सेन्सर"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"शरीर सेन्सरमध्ये (हृदय गती मॉनिटरसारखे) प्रवेश करा"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"हृदय गती सारख्या, आपल्या शारीरिक स्थितीचे नियंत्रण करणार्या सेन्सरवरून डेटामध्ये प्रवेश करण्यासाठी अॅपला अनुमती देते."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"कॅलेंडर इव्हेंट तसेच गोपनीय माहिती वाचा"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"मित्र किंवा सहकर्मींसह, आपल्या टॅब्लेटवर संचयित केलेले सर्व कॅलेंडर इव्हेंट वाचण्यासाठी अॅप ला अनुमती देते. यामुळे गोपनीयता किंवा संवेदनशीलता याकडे दुर्लक्ष करून, आपला कॅलेंडर डेटा सामायिक किंवा जतन करण्यासाठी अॅप ला अनुमती देऊ शकते."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"मित्र किंवा सहकर्मी यांच्यासह, आपण आपल्या फोनवर सुधारित करू शकता असे इव्हेंट जोडण्यासाठी, काढण्यासाठी, बदलण्यासाठी अॅप ला अनुमती देते. हे कॅलेंडर मालकांकडून येत असल्याचे दिसणारे संदेश पाठविण्यासाठी किंवा मालकांच्या माहितीशिवाय इव्हेंट सुधारित करण्यासाठी अॅप ला अनुमती देऊ शकते."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"अतिरिक्त स्थान प्रदाता आदेशांवर प्रवेश करा"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"अॅपला अतिरिक्त स्थान प्रदाता आदेशावर प्रवेश करण्याची अनुमती देते. हे कदाचित अॅपला GPS किंवा इतर स्थान स्त्रोत च्या ऑपरेशनमध्ये हस्तक्षेप करण्याची अनुमती देऊ शकते."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"अचूक स्थान (GPS आणि नेटवर्क-आधारित)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"अचूक स्थानामध्ये (GPS आणि नेटवर्क-आधारित) प्रवेश करा"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"सेल टॉवर आणि वाय-फाय सारखी समग्र स्थिती निर्धारण प्रणाली (GPS) किंवा नेटवर्क स्थान स्त्रोत वापरून आपले अचूक स्थान मिळवण्यासाठी अॅप ला अनुमती देते. अॅपला त्या वापरण्यासाठी या स्थान सेवा चालू असणे आणि आपल्या डिव्हाइसवर उपलब्ध असणे आवश्यक आहे. आपण कुठे आहात हे निर्धारित करण्यासाठी अॅप्स याचा वापर करू शकतात आणि अतिरिक्त बॅटरी उर्जा वापरली जाऊ शकते."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"अंदाजे स्थान (नेटवर्क-आधारित)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"अंदाजे स्थानामध्ये (नेटवर्क-आधारित) प्रवेश करा"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"आपले अंदाजे स्थान देण्याची अॅप ला अनुमती देते. हे स्थान सेल टॉवर आणि वाय-फाय सारखे नेटवर्क स्थान स्त्रोत वापरून स्थान सेवांद्वारे मिळवले आहे. अॅपला त्या वापरण्यासाठी या स्थान सेवा चालू असणे आणि आपल्या डिव्हाइसवर उपलब्ध असणे आवश्यक आहे. अॅप्स हे आपण कुठे आहात याचा अंदाज लावण्यासाठी वापरू शकतात."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"आपल्या ऑडिओ सेटिंग्ज बदला"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"व्हॉल्यूम आणि आउटपुटसाठी कोणता स्पीकर वापरला आहे यासारख्या समग्र ऑडिओ सेटिंग्ज सुधारित करण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"ऑडिओ रेकॉर्ड करा"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"मायक्रोफोनसह ऑडिओ रेकॉर्ड करण्यासाठी अॅप ला अनुमती देते. ही परवानगी आपल्या पुष्टिकरणाशिवाय कोणत्याही वेळी ऑडिओ रेकॉर्ड करण्यासाठी अॅप ला अनुमती देते."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"सिम संप्रेषण"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"सिम वर आदेश पाठवा"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"अॅप ला सिम वर आदेश पाठविण्याची अनुमती देते. हे खूप धोकादायक असते."</string>
<string name="permlab_camera" msgid="3616391919559751192">"चित्रे आणि व्हिडिओ घ्या"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"कॅमेर्यासह चित्रे आणि व्हिडिओ घेण्यासाठी अॅप ला अनुमती देते. ही परवानगी आपल्या पुष्टीकरणाशिवाय कोणत्याही वेळी कॅमेरा वापरण्यासाठी अॅप ला परवानगी देते."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"फोनद्वारे ज्ञात खात्यांची सूची मिळवण्यासाठी अॅप ला अनुमती देते. यात आपण स्थापित केलेल्या अनुप्रयोगांद्वारे तयार केलेली कोणतीही खाती समाविष्ट करू शकतात."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"नेटवर्क कनेक्शन पहा"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"कोणती नेटवर्क अस्तित्वात आहेत आणि कनेक्ट केलेली आहेत यासारख्या नेटवर्क कनेक्शनविषयीची माहिती पाहण्यासाठी अॅप ला अनुमती देते."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"पूर्ण नेटवर्क प्रवेश"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"पूर्ण नेटवर्क प्रवेश आहे"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"नेटवर्क सॉकेट तयार करण्यासाठी आणि सानुकूल नेटवर्क प्रोटोकॉल वापरण्यासाठी अॅप ला अनुमती देते. ब्राउझर आणि अन्य अनुप्रयोग म्हणजे इंटरनेटवर डेटा पाठवण्याचा मार्ग, म्हणजे इंटरनेटवर डेटा पाठविण्यासाठी परवानगीची आवश्यकता नसते."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"नेटवर्क कनेक्टिव्हिटी बदला"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"नेटवर्क कनेक्टिव्हिटीची स्थिती बदलण्यासाठी अॅप ला अनुमती देते."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"स्थानिक ब्लूटुथ फोन कॉन्फिगर करण्याकरिता आणि दूरस्थ डिव्हाइसेस शोधण्यासाठी आणि त्यासह जोडण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"WiMAX कनेक्ट करा आणि त्यावरून डिस्कनेक्ट करा"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"WiMAX सक्षम केले आहे किंवा नाही आणि कनेक्ट केलेल्या कोणत्याही WiMAX नेटवर्क विषयीची माहिती निर्धारित करण्यासाठी अॅप ला अनुमती देते."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX स्थिती बदला"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"WiMAX स्थिती बदला"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"WiMAX नेटवर्कवर टॅब्लेट कनेक्ट करण्यास आणि त्यावरून टॅब्लेट डिस्कनेक्ट करण्यास अॅप ला अनुमती देते."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"WiMAX नेटवर्कवरून टीव्ही कनेक्ट करण्यासाठी आणि त्यावरून टीव्ही डिस्कनेक्ट करण्यासाठी अॅपला अनुमती देते."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"WiMAX नेटवर्कवर फोन कनेक्ट करण्यास आणि त्यावरून फोन डिस्कनेक्ट करण्यास अॅप ला अनुमती देते."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"स्पर्श स्क्रीनची मापन प्राचले सुधारित करण्यासाठी अॅप ला अनुमती देते. सामान्य अॅप्स साठी कधीही आवश्यक नसते."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"DRM प्रमाणपत्रांवर प्रवेश करा"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"DRM प्रमाणपत्रांची तरतूद करण्यासाठी आणि वापरण्यासाठी अनुप्रयोगास अनुमती देते. सामान्य अॅप्सकरिता कधीही आवश्यकता नसते."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Android बीम स्थानांतरण स्थिती प्राप्त करा"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"Android बीम स्थानांतरण स्थिती प्राप्त करा"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"वर्तमान Android बीम स्थानांतरणांविषयी माहिती प्राप्त करण्यासाठी या अनुप्रयोगास अनुमती देते"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"DRM प्रमाणपत्रे काढा"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"DRM प्रमाणपत्रे काढण्यासाठी अनुप्रयोगास अनुमती देते. सामान्य अॅप्स साठी कधीही आवश्यकता नसते."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"अयोग्य पिन कोड."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"अनलॉक करण्यासाठी, मेनू दाबा नंतर 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"आणीबाणीचा नंबर"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"सेवा नाही."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"सेवा नाही"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"स्क्रीन लॉक केली."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"अनलॉक करण्यासाठी मेनू दाबा किंवा आणीबाणीचा कॉल करा."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"अनलॉक करण्यासाठी मेनू दाबा."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"वापरकर्ता मार्गदर्शक पहा किंवा कस्टमर केअरशी संपर्क साधा."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"सिम कार्ड लॉक केलेले आहे."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"सिम कार्ड अनलॉक करत आहे…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"आपण आपला अनलॉक नमुना <xliff:g id="NUMBER_0">%d</xliff:g> वेळा अयोग्यरितीने काढला. \n\n <xliff:g id="NUMBER_1">%d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"आपण आपला संकेतशब्द <xliff:g id="NUMBER_0">%d</xliff:g> वेळा अयोग्यरितीने टाइप केला आहे. \n\n <xliff:g id="NUMBER_1">%d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"आपण आपला पिन <xliff:g id="NUMBER_0">%d</xliff:g> वेळा अयोग्यरितीने टाइप केला आहे. \n\n <xliff:g id="NUMBER_1">%d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"आपण आपला अनलॉक नमुना <xliff:g id="NUMBER_0">%d</xliff:g> वेळा चुकीचा रेखांकित केला आहे. <xliff:g id="NUMBER_1">%d</xliff:g> अधिक अयशस्वी प्रयत्नांनंतर, आपल्याला आपले Google साइन इन वापरून आपला टॅब्लेट अनलॉक करण्यास सांगितले जाईल.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"आपण <xliff:g id="NUMBER_0">%d</xliff:g> वेळा आपला अनलॉक नमुना अयोग्यरित्या काढला आहे. आणखी <xliff:g id="NUMBER_1">%d</xliff:g> अयशस्वी प्रयत्नांनंतर, आपल्याला आपले Google साइन इन वापरून आपला टीव्ही अनलॉक करण्यास सांगितले जाईल.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> सेकंदांनी पुन्हा प्रयत्न करा."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"आपण आपला अनलॉक नमुना <xliff:g id="NUMBER_0">%d</xliff:g> वेळा चुकीचा रेखांकित केला आहे. <xliff:g id="NUMBER_1">%d</xliff:g> अधिक अयशस्वी प्रयत्नांनंतर, आपल्याला आपले Google साइन इन वापरून आपला फोन अनलॉक करण्यास सांगितले जाईल.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"आपण <xliff:g id="NUMBER_0">%d</xliff:g> वेळा टॅब्लेट अनलॉक करण्याचे चुकीचे प्रयत्न केले. आणखी <xliff:g id="NUMBER_1">%d</xliff:g> अयशस्वी प्रयत्नांनंतर, टॅब्लेट फॅक्टरी डीफॉल्टवर रीसेट केला जाईल आणि सर्व वापरकर्ता डेटा गमावला जाईल."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"आपण <xliff:g id="NUMBER_0">%d</xliff:g> वेळा टीव्ही अनलॉक करण्याचा अयोग्यरित्या प्रयत्न केला. आणखी <xliff:g id="NUMBER_1">%d</xliff:g> अयशस्वी प्रयत्नांनंतर, टीव्ही फॅक्टरी डीफॉल्टवर रीसेट केला जाईल आणि सर्व वापरकर्ता डेटा गमावेल."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"आपण <xliff:g id="NUMBER_0">%d</xliff:g> वेळा फोन अनलॉक करण्याचे चुकीचे प्रयत्न केले. आणखी <xliff:g id="NUMBER_1">%d</xliff:g> अयशस्वी प्रयत्नांनंतर, फोन फॅक्टरी डीफॉल्टवर रीसेट केला जाईल आणि सर्व वापरकर्ता डेटा गमावला जाईल."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"आपण आपला अनलॉक नमुना <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा अयोग्यरितीने काढला. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"आपण आपला संकेतशब्द <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा अयोग्यरितीने टाइप केला आहे. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"आपण आपला पिन <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा अयोग्यरितीने टाइप केला आहे. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"आपण आपला अनलॉक नमुना <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा चुकीचा रेखांकित केला आहे. <xliff:g id="NUMBER_1">%2$d</xliff:g> अधिक अयशस्वी प्रयत्नांनंतर, आपल्याला आपले Google साइन इन वापरून आपला टॅब्लेट अनलॉक करण्यास सांगितले जाईल.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"आपण <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा आपला अनलॉक नमुना अयोग्यरित्या काढला आहे. आणखी <xliff:g id="NUMBER_1">%2$d</xliff:g> अयशस्वी प्रयत्नांनंतर, आपल्याला आपले Google साइन इन वापरून आपला टीव्ही अनलॉक करण्यास सांगितले जाईल.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकंदांनी पुन्हा प्रयत्न करा."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"आपण आपला अनलॉक नमुना <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा चुकीचा रेखांकित केला आहे. <xliff:g id="NUMBER_1">%2$d</xliff:g> अधिक अयशस्वी प्रयत्नांनंतर, आपल्याला आपले Google साइन इन वापरून आपला फोन अनलॉक करण्यास सांगितले जाईल.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"आपण <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा टॅब्लेट अनलॉक करण्याचे चुकीचे प्रयत्न केले. आणखी <xliff:g id="NUMBER_1">%2$d</xliff:g> अयशस्वी प्रयत्नांनंतर, टॅब्लेट फॅक्टरी डीफॉल्टवर रीसेट केला जाईल आणि सर्व वापरकर्ता डेटा गमावला जाईल."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"आपण <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा टीव्ही अनलॉक करण्याचा अयोग्यरित्या प्रयत्न केला. आणखी <xliff:g id="NUMBER_1">%2$d</xliff:g> अयशस्वी प्रयत्नांनंतर, टीव्ही फॅक्टरी डीफॉल्टवर रीसेट केला जाईल आणि सर्व वापरकर्ता डेटा गमावेल."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"आपण <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा फोन अनलॉक करण्याचे चुकीचे प्रयत्न केले. आणखी <xliff:g id="NUMBER_1">%2$d</xliff:g> अयशस्वी प्रयत्नांनंतर, फोन फॅक्टरी डीफॉल्टवर रीसेट केला जाईल आणि सर्व वापरकर्ता डेटा गमावला जाईल."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"आपण <xliff:g id="NUMBER">%d</xliff:g> वेळा टॅब्लेट अनलॉक करण्याचे चुकीचे प्रयत्न केले. टॅब्लेट आता फॅक्टरी डीफॉल्टवर रीसेट केले जाईल."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"आपण <xliff:g id="NUMBER">%d</xliff:g> वेळा टीव्ही अनलॉक करण्याचा अयोग्यरित्या प्रयत्न केला. टीव्ही आता फॅक्टरी डीफॉल्टवर रीसेट केला जाईल."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"आपण <xliff:g id="NUMBER">%d</xliff:g> वेळा फोन अनलॉक करण्याचे चुकीचे प्रयत्न केले. फोन आता फॅक्टरी डीफॉल्टवर रीसेट केला जाईल."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"शब्द"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"दुवा"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"रेखा"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"फॅक्टरी चाचणी अयशस्वी"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"FACTORY_TEST क्रिया फक्त /सिस्टम/अॅप मध्ये स्थापित केलेल्या पॅकेजसाठी समर्थित आहे."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"FACTORY_TEST क्रिया प्रदान करणारे कोणतेही पॅकेज आढळले नाही."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"फॉर्मेट करीत आहे..."</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"घातले नाही"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"कोणतेही जुळणारे क्रियाकलाप आढळले नाहीत."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"मीडिया आउटपुट मार्गस्थ करा"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"मीडिया आउटपुट मार्गस्थ करा"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"अन्य बाह्य डिव्हाइसेसवरील रूट मीडिया आउटपुट वर अनुप्रयोगास अनुमती देते."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"स्थापना सत्र वाचा"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"स्थापना सत्र वाचा"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"अनुप्रयोगास स्थापना सत्र वाचण्याची अनुमती देते. हे सक्रिय पॅकेज स्थापनांविषयी तपशील पाहाण्याची यास अनुमती देते."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"पॅकेज स्थापित करण्यासाठी विनंती करा"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"पॅकेज स्थापित करण्यासाठी विनंती करा"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"पॅकेजच्या स्थापना करण्यासाठी अनुप्रयोगास अनुमती देते."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"झूम नियंत्रणासाठी दोनदा स्पर्श करा"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"विजेट जोडू शकलो नाही."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"अवैध वापरकर्तानाव किंवा संकेतशब्द."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"आपले वापरकर्तानाव किंवा संकेतशब्द विसरलात?\n "<b>"google.com/accounts/recovery"</b>" ला भेट द्या."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"खाते तपासत आहे…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"आपण आपला पिन <xliff:g id="NUMBER_0">%d</xliff:g> वेळा अयोग्यरितीने टाइप केला आहे. \n\n <xliff:g id="NUMBER_1">%d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"आपण आपला संकेतशब्द <xliff:g id="NUMBER_0">%d</xliff:g> वेळा अयोग्यरितीने टाइप केला आहे. \n\n <xliff:g id="NUMBER_1">%d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"आपण आपला अनलॉक नमुना <xliff:g id="NUMBER_0">%d</xliff:g> वेळा अयोग्यरितीने काढला आहे. \n\n <xliff:g id="NUMBER_1">%d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"आपण <xliff:g id="NUMBER_0">%d</xliff:g> वेळा टॅब्लेट अनलॉक करण्याचा अयोग्यपणे प्रयत्न केला. <xliff:g id="NUMBER_1">%d</xliff:g> आणखी अयशस्वी प्रयत्नांनंतर, टॅब्लेट फॅक्टरी डीफॉल्टवर रीसेट केला जाईल आणि वापरकर्ता डेटा गमावेल."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"आपण <xliff:g id="NUMBER_0">%d</xliff:g> वेळा टीव्ही अनलॉक करण्याचा अयोग्यरित्या प्रयत्न केला. आणखी <xliff:g id="NUMBER_1">%d</xliff:g> अयशस्वी प्रयत्नांनंतर, टीव्ही फॅक्टरी डीफॉल्टवर रीसेट केला जाईल आणि सर्व वापरकर्ता डेटा गमावेल."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"आपण <xliff:g id="NUMBER_0">%d</xliff:g> वेळा फोन अनलॉक करण्याचा अयोग्यपणे प्रयत्न केला. <xliff:g id="NUMBER_1">%d</xliff:g> आणखी अयशस्वी प्रयत्नांनंतर, फोन फॅक्टरी डीफॉल्टवर रीसेट केला जाईल आणि वापरकर्ता डेटा गमावेल."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"आपण आपला पिन <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा अयोग्यरितीने टाइप केला आहे. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"आपण आपला संकेतशब्द <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा अयोग्यरितीने टाइप केला आहे. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"आपण आपला अनलॉक नमुना <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा अयोग्यरितीने काढला आहे. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"आपण <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा टॅब्लेट अनलॉक करण्याचा अयोग्यपणे प्रयत्न केला. <xliff:g id="NUMBER_1">%2$d</xliff:g> आणखी अयशस्वी प्रयत्नांनंतर, टॅब्लेट फॅक्टरी डीफॉल्टवर रीसेट केला जाईल आणि वापरकर्ता डेटा गमावेल."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"आपण <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा टीव्ही अनलॉक करण्याचा अयोग्यरित्या प्रयत्न केला. आणखी <xliff:g id="NUMBER_1">%2$d</xliff:g> अयशस्वी प्रयत्नांनंतर, टीव्ही फॅक्टरी डीफॉल्टवर रीसेट केला जाईल आणि सर्व वापरकर्ता डेटा गमावेल."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"आपण <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा फोन अनलॉक करण्याचा अयोग्यपणे प्रयत्न केला. <xliff:g id="NUMBER_1">%2$d</xliff:g> आणखी अयशस्वी प्रयत्नांनंतर, फोन फॅक्टरी डीफॉल्टवर रीसेट केला जाईल आणि वापरकर्ता डेटा गमावेल."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"आपण <xliff:g id="NUMBER">%d</xliff:g> वेळा टॅब्लेट अनलॉक करण्याचा अयोग्यपणे प्रयत्न केला. टॅब्लेट आता फॅक्टरी डीफॉल्ट वर रीसेट केला जाईल."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"आपण <xliff:g id="NUMBER">%d</xliff:g> वेळा टीव्ही अनलॉक करण्याचा अयोग्यरित्या प्रयत्न केला. टीव्ही आता फॅक्टरी डीफॉल्टवर रीसेट केला जाईल."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"आपण <xliff:g id="NUMBER">%d</xliff:g> वेळा फोन अनलॉक करण्याचा अयोग्यपणे प्रयत्न केला. फोन आता फॅक्टरी डीफॉल्ट वर रीसेट केला जाईल."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"आपण आपला अनलॉक नमुना <xliff:g id="NUMBER_0">%d</xliff:g> वेळा अयोग्यपणे काढला आहे. आणखी <xliff:g id="NUMBER_1">%d</xliff:g> अयशस्वी प्रयत्नांनंतर, आपल्याला ईमेल खाते वापरून आपला टॅब्लेट अनलॉक करण्यास सांगितले जाईल.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"आपण <xliff:g id="NUMBER_0">%d</xliff:g> वेळा आपला अनलॉक नमुना अयोग्यरित्या काढला आहे. आणखी <xliff:g id="NUMBER_1">%d</xliff:g> अयशस्वी प्रयत्नांनंतर, आपल्याला ईमेल खाते वापरून आपला टीव्ही अनलॉक करण्यास सांगितले जाईल.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> सेकंदांनी पुन्हा प्रयत्न करा."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"आपण आपला अनलॉक नमुना <xliff:g id="NUMBER_0">%d</xliff:g> वेळा अयोग्यपणे काढला आहे. आणखी <xliff:g id="NUMBER_1">%d</xliff:g> अयशस्वी प्रयत्नांनंतर, आपल्याला ईमेल खाते वापरून आपला फोन अनलॉक करण्यास सांगितले जाईल.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"आपण आपला अनलॉक नमुना <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा अयोग्यपणे काढला आहे. आणखी <xliff:g id="NUMBER_1">%2$d</xliff:g> अयशस्वी प्रयत्नांनंतर, आपल्याला ईमेल खाते वापरून आपला टॅब्लेट अनलॉक करण्यास सांगितले जाईल.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"आपण <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा आपला अनलॉक नमुना अयोग्यरित्या काढला आहे. आणखी <xliff:g id="NUMBER_1">%2$d</xliff:g> अयशस्वी प्रयत्नांनंतर, आपल्याला ईमेल खाते वापरून आपला टीव्ही अनलॉक करण्यास सांगितले जाईल.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकंदांनी पुन्हा प्रयत्न करा."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"आपण आपला अनलॉक नमुना <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा अयोग्यपणे काढला आहे. आणखी <xliff:g id="NUMBER_1">%2$d</xliff:g> अयशस्वी प्रयत्नांनंतर, आपल्याला ईमेल खाते वापरून आपला फोन अनलॉक करण्यास सांगितले जाईल.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"काढा"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"शिफारस केलेल्या पातळीच्या वर आवाज वाढवायचा?\n\nउच्च आवाजात दीर्घ काळ ऐकण्याने आपल्या श्रवणशक्तीची हानी होऊ शकते."</string>
@@ -1470,6 +1468,7 @@
<item quantity="other">%d तासासाठी</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> पर्यंत"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> पर्यंत (पुढील अलार्म)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"आपण हे बंद करेपर्यंत"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"आपण बंद करेपर्यंत व्यत्यय आणू नका"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-ms-rMY-watch/strings.xml b/core/res/res/values-ms-rMY-watch/strings.xml
index c3f68b8..eeb290c 100644
--- a/core/res/res/values-ms-rMY-watch/strings.xml
+++ b/core/res/res/values-ms-rMY-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Apl <xliff:g id="NUMBER_0">%1$d</xliff:g> daripada <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Penderia"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"akses kenalan anda"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"akses lokasi jam tangan ini"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"akses kalendar anda"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"hantar dan lihat mesej SMS"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"akses foto, media dan fail pada jam tangan anda"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"rakam audio"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"ambil gambar dan rakam video"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"buat dan urus panggilan telefon"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"akses data penderia tentang tanda vital anda"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"jadi bar status"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"akses penderia badan (seperti pemantau kadar denyut jantung)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"akses lokasi tepat (GPS dan berdasarkan rangkaian)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"akses lokasi anggaran (berdasarkan rangkaian)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"hantar perintah ke SIM"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"mempunyai akses rangkaian penuh"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"urus pemilik profil dan peranti"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"tukar keadaan WiMAX"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"terima status pemindahan Pancaran Android"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"halakan output media"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"baca sesi pemasangan"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"minta pakej pemasangan"</string>
</resources>
diff --git a/core/res/res/values-ms-rMY/strings.xml b/core/res/res/values-ms-rMY/strings.xml
index b69010e..a1955bf 100644
--- a/core/res/res/values-ms-rMY/strings.xml
+++ b/core/res/res/values-ms-rMY/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Termasuk data peribadi seperti nombor kad kredit dan kata laluan."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"lumpuhkan atau ubah suai bar status"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Membenarkan apl melumpuhkan bar status atau menambah dan mengalih keluar ikon sistem."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"bar status"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"jadi bar status"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Membenarkan apl menjadi bar status."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"kembangkan/runtuhkan bar status"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Membenarkan apl mengembangkan atau meruntuhkan bar status."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Membenarkan apl menerima dan memproses mesej WAP. Kebenaran ini termasuk keupayaan untuk memantau atau memadam mesej yang dihantar kepada anda tanpa menunjukkannya kepada anda."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"dapatkan semula apl yang sedang dijalankan"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Membenarkan apl mengambil maklumat tentang tugasan yang sedang dan baru berjalan. Ini boleh membenarkan apl untuk menemui maklumat tentang apl mana yang digunakan pada peranti."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Urus pemilik profil dan peranti"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"urus pemilik profil dan peranti"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Membenarkan apl menetapkan pemilik profil dan pemilik peranti."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"susun semula tertib apl yang dijalankan"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Membenarkan apl memindahkan tugasan ke latar depan dan latar belakang. Apl boleh melakukan ini tanpa input anda."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Membenarkan apl untuk mengubah suai panggilan tablet anda, termasuk data tentang panggilan masuk dan keluar. Apl hasad boleh menggunakannya untuk memadam atau mengubah suai log panggilan anda."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Membenarkan apl untuk mengubah suai log panggilan TV anda, termasuk data mengenai panggilan masuk atau keluar. Apl hasad mungkin menggunakan ini untuk memadam atau mengubah suai log panggilan anda."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Membenarkan apl untuk mengubah suai panggilan telefon anda, termasuk data tentang panggilan masuk dan keluar. Apl hasad boleh menggunakannya untuk memadam atau mengubah suai log panggilan anda."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"penderia (spt. denyut jantung)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"akss pndia bdn (spt pmntau kdr dnyt jntg)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Membenarkan apl mengakses data dari penderia yang memantau keadaan fizikal anda, seperti kadar denyutan jantung anda."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"baca acara kalendar serta maklumat sulit"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Membenarkan apl membaca semua acara kalendar yang tersimpan pada tablet anda, termasuk milik rakan atau rakan sekerja. Ini boleh membenarkan apl untuk berkongsi atau menyimpan data kalendar anda, tanpa mengira kerahsiaan atau sensitiviti."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Membenarkan apl menambah, mengalih keluar, mengubah acara yang anda boleh ubah suai pada telefon anda, termasuk milik rakan atau rakan sekerja. Ini boleh membenarkan apl menghantar mesej yang kelihatan seperti datang dari pemilik kalendar atau mengubah suai acara tanpa pengetahuan pemilik."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"akses perintah tambahan pembekal lokasi"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Membenarkan apl mengakses arahan pembekal lokasi tambahan. Ini boleh membenarkan apl untuk campur tangan dengan operasi GPS atau sumber lokasi lain."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"lokasi tepat (GPS dan berasaskan rangkaian)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"akses lokasi tepat (GPS dan berasaskan rangkaian)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Membenarkan apl mendapatkan lokasi tepat anda menggunakan Sistem Kedudukan Global (GPS) atau sumber lokasi rangkaian seperti menara sel dan Wi-Fi. Perkhidmatan lokasi ini mesti dihidupkan dan tersedia pada peranti anda untuk kegunaan apl. Apl boleh menggunakan ini untuk menentukan tempat anda berada dan mungkin menggunakan kuasa bateri tambahan."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"lokasi anggaran (berasaskan rangkaian)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"akses lokasi anggaran (berasaskan rangkaian)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Membenarkan apl mendapatkan lokasi anggaran anda. Lokasi ini diperolehi oleh perkhidmatan lokasi menggunakan sumber lokasi rangkaian seperti menara sel dan Wi-Fi. Perkhidmatan lokasi ini mesti dihidupkan dan tersedia pada peranti anda untuk kegunaan apl. Apl boleh menggunakan ini untuk menentukan secara anggaran tempat anda berada."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"tukar tetapan audio anda"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Membenarkan apl untuk mengubah suai tetapan audio global seperti kelantangan dan pembesar suara mana digunakan untuk output."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"rakam audio"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Membenarkan apl untuk merakam audio menggunakan mikrofon. Kebenaran ini membenarkan apl untuk merakam audio pada bila-bila masa tanpa pengesahan anda."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"komunikasi sim"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"hantar perintah ke SIM"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Membenarkan apl menghantar arahan kepada SIM. Ini amat berbahaya."</string>
<string name="permlab_camera" msgid="3616391919559751192">"ambil gambar dan video"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Membenarkan apl mengambil gambar dan video menggunakan kamera. Kebenaran ini membenarkan apl untuk menggunakan kamera pada bila-bila masa tanpa pengesahan anda."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Membenarkan apl mendapatkan senarai akaun yang dikenali oleh telefon. Ini mungkin termasuk sebarang akaun yang dibuat oleh aplikasi yang telah anda pasang."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"lihat sambungan rangkaian"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Membenarkan apl melihat maklumat tentang sambungan rangkaian seperti rangkaian mana yang wujud dan bersambung."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"akses rangkaian penuh"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"mempunyai akses rangkaian penuh"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Membenarkan apl membuat soket rangkaian dan menggunakan protokol rangkaian tersuai. Penyemak imbas dan aplikasi lain menyediakan cara untuk menghantar data ke internet, jadi kebenaran ini tidak diperlukan untuk menghantar data ke internet."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"tukar kesambungan rangkaian"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Membenarkan apl untuk mengubah keadaan kesambungan rangkaian."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Membenarkan apl mengkonfigurasikan telefon Bluetooth setempat dan menemui serta berpasangan dengan peranti jauh."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"sambung dan putuskan sambungan WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Membenarkan apl menentukan sama ada WiMaX didayakan dan maklumat tentang sebarang rangkaian WiMaX yang disambungkan."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Tukar keadaan WiMAX"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"tukar keadaan WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Membenarkan apl untuk menyambungkan tablet ke dan menyahsambungkan tablet dari rangkaian WiMaX."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Membenarkan apl menyambungkan TV ke dan memutuskan sambungan TV daripada rangkaian WiMAX."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Membenarkan apl untuk menyambungkan telefon ke dan menyahsambung telefon dari rangkaian WiMaX."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Membenarkan apl mengubah suai parameter penentukuran skrin sentuh. Ini tidak sekali-kali diperlukan untuk apl biasa."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"akses sijil DRM"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Membenarkan aplikasi memperuntuk dan menggunakan sijil DRM. Tidak sekali-kali diperlukan untuk apl biasa."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Terima status pemindahan Pancaran Android"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"terima status pemindahan Pancaran Android"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Membenarkan aplikasi ini menerima maklumat mengenai pemindahan Pancaran Android semasa"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"alih keluar sijil DRM"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Membenarkan aplikasi mengalih keluar sijil DRM. Tidak sekali-kali diperlukan untuk apl biasa."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Kod PIN salah."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Untuk membuka kunci, tekan Menu, kemudian 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Nombor kecemasan"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Tiada perkhidmatan."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Tiada perkhidmatan"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Skrin dikunci."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Tekan Menu untuk menyahsekat atau membuat panggilan kecemasan."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Tekan Menu untuk membuka kunci."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Lihat Panduan Pengguna atau hubungi Penjagaan Pelanggan."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"Kad SIM dikunci."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Membuka kunci kad SIM..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Anda telah tersilap melukis corak buka kunci anda sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. \n\nSila cuba lagi dalam <xliff:g id="NUMBER_1">%d</xliff:g> saat."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Anda telah menaip kata laluan yang salah sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. \n\nCuba lagi dalam <xliff:g id="NUMBER_1">%d</xliff:g> saat."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Anda telah menaip PIN yang salah sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. \n\nCuba lagi dalam <xliff:g id="NUMBER_1">%d</xliff:g> saat."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Anda telah tersilap melukis corak buka kunci sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. Selepas <xliff:g id="NUMBER_1">%d</xliff:g> lagi percubaan yang tidak berjaya, anda akan diminta membuka kunci tablet anda menggunakan log masuk Google anda.\n\n Cuba lagi dalam <xliff:g id="NUMBER_2">%d</xliff:g> saat."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Anda telah tersilap lukis corak buka kunci anda sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. Selepas <xliff:g id="NUMBER_1">%d</xliff:g> lagi percubaan yang tidak berjaya, anda akan diminta untuk membuka kunci TV anda menggunakan log masuk Google anda.\n\n Cuba lagi dalam masa <xliff:g id="NUMBER_2">%d</xliff:g> saat."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Anda telah tersilap lukis corak buka kunci sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. Selepas <xliff:g id="NUMBER_1">%d</xliff:g> lagi percubaan yang tidak berjaya, anda akan diminta membuka kunci telefon anda menggunakan log masuk Google anda.\n\n Sila cuba lagi dalam <xliff:g id="NUMBER_2">%d</xliff:g> saat."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Anda telah mencuba untuk membuka kunci tablet dengan salah sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. Selepas <xliff:g id="NUMBER_1">%d</xliff:g> lagi percubaan yang tidak berjaya, tablet akan ditetapkan semula kepada tetapan lalai kilang dan semua data pengguna akan hilang."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Anda telah mencuba membuka kunci TV secara tidak betul sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. Selepas <xliff:g id="NUMBER_1">%d</xliff:g> lagi percubaan yang tidak berjaya, TV akan ditetapkan semula kepada tetapan lalai kilang dan semua data pengguna akan hilang."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Anda telah mencuba untuk membuka kunci telefon dengan salah sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. Selepas <xliff:g id="NUMBER_1">%d</xliff:g> lagi percubaan yang tidak berjaya, telefon akan ditetapkan semula kepada tetapan lalai kilang dan semua data pengguna akan hilang."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Anda telah tersilap melukis corak buka kunci anda sebanyak <xliff:g id="NUMBER_0">%1$d</xliff:g> kali. \n\nSila cuba lagi dalam <xliff:g id="NUMBER_1">%2$d</xliff:g> saat."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Anda telah menaip kata laluan yang salah sebanyak <xliff:g id="NUMBER_0">%1$d</xliff:g> kali. \n\nCuba lagi dalam <xliff:g id="NUMBER_1">%2$d</xliff:g> saat."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Anda telah menaip PIN yang salah sebanyak <xliff:g id="NUMBER_0">%1$d</xliff:g> kali. \n\nCuba lagi dalam <xliff:g id="NUMBER_1">%2$d</xliff:g> saat."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Anda telah tersilap melukis corak buka kunci sebanyak <xliff:g id="NUMBER_0">%1$d</xliff:g> kali. Selepas <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi percubaan yang tidak berjaya, anda akan diminta membuka kunci tablet anda menggunakan log masuk Google anda.\n\n Cuba lagi dalam <xliff:g id="NUMBER_2">%3$d</xliff:g> saat."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Anda telah tersilap lukis corak buka kunci anda sebanyak <xliff:g id="NUMBER_0">%1$d</xliff:g> kali. Selepas <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi percubaan yang tidak berjaya, anda akan diminta untuk membuka kunci TV anda menggunakan log masuk Google anda.\n\n Cuba lagi dalam masa <xliff:g id="NUMBER_2">%3$d</xliff:g> saat."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Anda telah tersilap lukis corak buka kunci sebanyak <xliff:g id="NUMBER_0">%1$d</xliff:g> kali. Selepas <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi percubaan yang tidak berjaya, anda akan diminta membuka kunci telefon anda menggunakan log masuk Google anda.\n\n Sila cuba lagi dalam <xliff:g id="NUMBER_2">%3$d</xliff:g> saat."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Anda telah mencuba untuk membuka kunci tablet dengan salah sebanyak <xliff:g id="NUMBER_0">%1$d</xliff:g> kali. Selepas <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi percubaan yang tidak berjaya, tablet akan ditetapkan semula kepada tetapan lalai kilang dan semua data pengguna akan hilang."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Anda telah mencuba membuka kunci TV secara tidak betul sebanyak <xliff:g id="NUMBER_0">%1$d</xliff:g> kali. Selepas <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi percubaan yang tidak berjaya, TV akan ditetapkan semula kepada tetapan lalai kilang dan semua data pengguna akan hilang."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Anda telah mencuba untuk membuka kunci telefon dengan salah sebanyak <xliff:g id="NUMBER_0">%1$d</xliff:g> kali. Selepas <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi percubaan yang tidak berjaya, telefon akan ditetapkan semula kepada tetapan lalai kilang dan semua data pengguna akan hilang."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Anda telah mencuba untuk membuka kunci tablet secara salah sebanyak <xliff:g id="NUMBER">%d</xliff:g> kali. Tablet kini akan ditetapkan semula ke tetapan lalai kilang."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Anda telah cuba membuka kunci TV secara tidak betul sebanyak <xliff:g id="NUMBER">%d</xliff:g> kali. TV kini akan ditetapkan semula kepada tetapan lalai kilang."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Anda telah mencuba untuk membuka kunci telefon secara salah sebanyak <xliff:g id="NUMBER">%d</xliff:g> kali. Telefon kini akan ditetapkan semula kepada tetapan lalai kilang."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"perkataan"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"pautan"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"baris"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Ujian kilang gagal"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Tindakan FACTORY_TEST hanya disokong untuk pakej yang dipasangkan dalam /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Tiada pakej yang menyediakan tindakan FACTORY_TEST ditemui."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Memformat…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Tidak dimasukkan"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Tiada aktiviti yang sepadan ditemui."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Buat laluan output media"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"halakan output media"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Membenarkan apl untuk membuat laluan output media ke peranti luaran lain."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Baca sesi pemasangan"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"baca sesi pemasangan"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Membenarkan aplikasi membaca sesi pemasangan Ini membenarkan apl melihat butiran mengenai pemasangan pakej yang aktif."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Minta pemasangan pakej"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"minta pakej pemasangan"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Membenarkan aplikasi meminta pemasangan pakej."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Sentuh dua kali untuk mendapatkan kawalan zum"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Tidak dapat menambahkan widget."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Nama pengguna atau kata laluan tidak sah."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Lupa nama pengguna atau kata laluan anda?\nLawati"<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Menyemak akaun…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Anda telah menaip PIN yang salah sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. \n\nCuba lagi dalam <xliff:g id="NUMBER_1">%d</xliff:g> saat."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Anda telah menaip kata laluan yang salah sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. \n\nCuba lagi dalam <xliff:g id="NUMBER_1">%d</xliff:g> saat."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Anda telah tersilap melukis corak buka kunci anda sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. \n\nCuba lagi dalam <xliff:g id="NUMBER_1">%d</xliff:g> saat."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Anda telah mencuba untuk membuka kunci tablet dengan salah sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. Selepas <xliff:g id="NUMBER_1">%d</xliff:g> lagi percubaan yang tidak berjaya, tablet akan ditetapkan semula kepada tetapan lalai kilang dan semua data pengguna akan hilang."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Anda telah cuba membuka kunci TV secara tidak betul sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. Selepas <xliff:g id="NUMBER_1">%d</xliff:g> lagi percubaan yang tidak berjaya, TV akan ditetapkan semula kepada tetapan lalai kilang dan semua data pengguna akan hilang."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Anda telah mencuba untuk membuka kunci telefon dengan salah sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. Selepas <xliff:g id="NUMBER_1">%d</xliff:g> lagi percubaan yang tidak berjaya, telefon akan ditetapkan semula kepada tetapan lalai kilang dan semua data pengguna akan hilang."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Anda telah menaip PIN yang salah sebanyak <xliff:g id="NUMBER_0">%1$d</xliff:g> kali. \n\nCuba lagi dalam <xliff:g id="NUMBER_1">%2$d</xliff:g> saat."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Anda telah menaip kata laluan yang salah sebanyak <xliff:g id="NUMBER_0">%1$d</xliff:g> kali. \n\nCuba lagi dalam <xliff:g id="NUMBER_1">%2$d</xliff:g> saat."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Anda telah tersilap melukis corak buka kunci anda sebanyak <xliff:g id="NUMBER_0">%1$d</xliff:g> kali. \n\nCuba lagi dalam <xliff:g id="NUMBER_1">%2$d</xliff:g> saat."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Anda telah mencuba untuk membuka kunci tablet dengan salah sebanyak <xliff:g id="NUMBER_0">%1$d</xliff:g> kali. Selepas <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi percubaan yang tidak berjaya, tablet akan ditetapkan semula kepada tetapan lalai kilang dan semua data pengguna akan hilang."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Anda telah cuba membuka kunci TV secara tidak betul sebanyak <xliff:g id="NUMBER_0">%1$d</xliff:g> kali. Selepas <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi percubaan yang tidak berjaya, TV akan ditetapkan semula kepada tetapan lalai kilang dan semua data pengguna akan hilang."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Anda telah mencuba untuk membuka kunci telefon dengan salah sebanyak <xliff:g id="NUMBER_0">%1$d</xliff:g> kali. Selepas <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi percubaan yang tidak berjaya, telefon akan ditetapkan semula kepada tetapan lalai kilang dan semua data pengguna akan hilang."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Anda telah mencuba untuk membuka kunci tablet secara salah sebanyak <xliff:g id="NUMBER">%d</xliff:g> kali. Tablet kini akan ditetapkan semula ke tetapan lalai kilang."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Anda telah cuba membuka kunci TV secara tidak betul sebanyak <xliff:g id="NUMBER">%d</xliff:g> kali. TV kini akan ditetapkan semula kepada tetapan lalai kilang."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Anda telah mencuba untuk membuka kunci telefon secara salah sebanyak <xliff:g id="NUMBER">%d</xliff:g> kali. Telefon kini akan ditetapkan semula ke tetapan lalai kilang."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Anda telah tersilap melukis corak buka kunci sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. Selepas <xliff:g id="NUMBER_1">%d</xliff:g> lagi percubaan yang tidak berjaya, anda akan diminta membuka kunci tablet anda menggunakan log masuk Google anda.\n\n Cuba lagi dalam <xliff:g id="NUMBER_2">%d</xliff:g> saat."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Anda telah tersilap lukis corak buka kunci anda sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. Selepas <xliff:g id="NUMBER_1">%d</xliff:g> lagi percubaan yang tidak berjaya, anda akan diminta untuk membuka kunci TV anda menggunakan akaun e-mel.\n\n Cuba lagi dalam masa <xliff:g id="NUMBER_2">%d</xliff:g> saat."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Anda telah tersilap lukis corak buka kunci sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. Selepas <xliff:g id="NUMBER_1">%d</xliff:g> lagi percubaan yang tidak berjaya, anda akan diminta membuka kunci telefon anda menggunakan log masuk Google anda.\n\n Cuba lagi dalam <xliff:g id="NUMBER_2">%d</xliff:g> saat."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Anda telah tersilap melukis corak buka kunci sebanyak <xliff:g id="NUMBER_0">%1$d</xliff:g> kali. Selepas <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi percubaan yang tidak berjaya, anda akan diminta membuka kunci tablet anda menggunakan log masuk Google anda.\n\n Cuba lagi dalam <xliff:g id="NUMBER_2">%3$d</xliff:g> saat."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Anda telah tersilap lukis corak buka kunci anda sebanyak <xliff:g id="NUMBER_0">%1$d</xliff:g> kali. Selepas <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi percubaan yang tidak berjaya, anda akan diminta untuk membuka kunci TV anda menggunakan akaun e-mel.\n\n Cuba lagi dalam masa <xliff:g id="NUMBER_2">%3$d</xliff:g> saat."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Anda telah tersilap lukis corak buka kunci sebanyak <xliff:g id="NUMBER_0">%1$d</xliff:g> kali. Selepas <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi percubaan yang tidak berjaya, anda akan diminta membuka kunci telefon anda menggunakan log masuk Google anda.\n\n Cuba lagi dalam <xliff:g id="NUMBER_2">%3$d</xliff:g> saat."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Alih keluar"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Naikkan kelantangan melebihi paras yang disyokorkan?\n\nMendengar pada kelantangan yang tinggi untuk tempoh yang lama boleh merosakkan pendengaran anda."</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">Selama 1 jam</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Sehingga <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Sehingga <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (penggera akan datang)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Sehingga anda matikan"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Hingga anda mematikan Jangan Ganggu"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-my-rMM-watch/strings.xml b/core/res/res/values-my-rMM-watch/strings.xml
index c4d738a..813c7da 100644
--- a/core/res/res/values-my-rMM-watch/strings.xml
+++ b/core/res/res/values-my-rMM-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_1">%2$d</xliff:g>၏ <xliff:g id="NUMBER_0">%1$d</xliff:g> အသေးစားဆော့ဝဲ"</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"အာရုံခံကိရိယာများ"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"သင့် အဆက်အသွယ်များကို ယူသုံးရန်"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"ဒီနာရီရဲ့ တည်နေရာကို ရယူရန်"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"သင့် ပြက္ခဒိန်ကို ယူသုံးရန်"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"SMS စာတိုများကို ပို့ရန် နှင့် ကြည့်ရန်"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"သင့်နာရီထဲက ဓာတ်ပုံများ၊ မီဒီယာ၊ နှင့် ဖိုင်များကို ရယူသုံးရန်"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"အသံ ဖမ်းရန်"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"ဓာတ်ပုံ ရိုက်ရန် နှင့် ဗွီဒီယို မှတ်တမ်းတင်ရန်"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"ဖုန်းခေါ်ဆိုမှုများ ပြုလုပ်ရန် နှင့် စီမံရန်"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"သင်ရဲ့ အဓိက အသက်ရှင်မှုဆိုင်ရာ အာရုံခံကိရိယာ ဒေတာကို ရယူသုံးစွဲရန်"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"အခြေအနေပြ ဘားဖြစ်ပါစေ"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"ခန္ဓာကိုယ် အာရုံကိရိယာများကို (နှလုံးခုန်နှုန်း မော်နီတာလို)ကို ရယူသုံးရန်"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"တိကျတဲ့ တည်နေရာ (GPS နှင့် ကွန်ရက် အခြေခံ)ကို ရယူသုံးရန်"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"အနီးစပ်ဆုံး တည်နေရာ (ကွန်ရက် အခြေခံ)ကို ရယူသုံးရန်"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"SIM ထံသို့ ညွှန်ကြားချက်များကို ပို့တယ်"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"ကွန်ရက်ကို အပြည့်အဝ ရယူသုံးနိုင်"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"ပရိုဖိုင် နှင့် ကိရိယာ ပိုင်ရှင်များကို စီမံပါ"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"WiMAX အခြေအနေကို ပြောင်းရန်"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"Android ရဲ့ အလင်းတန်းထိုး လွှဲပြောင်းမှု အခြေအနေကို ရယူရန်"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"မီဒီယာ ထွက်ပေါက်ကို လမ်းဖေါ်ပြပေးပါ"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"တပ်ဆင်ရေး လုပ်ကိုင်မှုကို ဖတ်ရန်"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"တပ်ဆင်ရေး အထုပ်များကို တောင်းဆိုပါ"</string>
</resources>
diff --git a/core/res/res/values-my-rMM/strings.xml b/core/res/res/values-my-rMM/strings.xml
index 41528d8..a913e38 100644
--- a/core/res/res/values-my-rMM/strings.xml
+++ b/core/res/res/values-my-rMM/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"အရေးကြီးသော ကိုယ်ရေးအချက်အလက်များဖြစ်တဲ့ ခရက်ဒစ်ကဒ်နံပါတ်များနှင့် စကားဝှက်များ ပါဝင်ပါတယ်."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"အခြေအနေပြဘားအား အလုပ်မလုပ်ခိုင်းရန်သို့မဟုတ် မွမ်းမံရန်"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"appအား အခြေအနေပြ ဘားကို ပိတ်ခွင့် သို့မဟတ် စနစ် အိုင်ကွန်များကို ထည့်ခြင်း ဖယ်ရှားခြင်း ပြုလုပ်ခွင့် ပြုသည်။"</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"အခြေအနေပြနေရာ"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"အခြေအနေပြ ဘားဖြစ်ပါစေ"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"appအား အခြေအနေပြ ဘားဖြစ်ခွင့် ပြုသည်။"</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"အခြေအနေပြဘားအား ချဲ့/ပြန့်ခြင်း"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"appအား အခြေအနေပြ ဘားကို ချဲ့ခွင့် သို့မဟုတ် ခေါက်သိမ်းခွင့် ပြုသည်။"</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"အပလီကေးရှင်းအား WAP စာများ လက်ခံခြင်း၊ ဆောင်ရွက်ခြင်း ခွင့်ပြုပါ။ ဤခွင့်ပြုချက်တွင် အပလီကေးရှင်းအနေဖြင့် သင် လက်ခံရရှိသော စာများအား သင့်အား မပြပဲစောင့်ကြည့်ခွင့်နှင့် ဖျက်ပစ်ခွင့်များ ပါဝင်ပါသည်။"</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"အလုပ်လုပ်နေကြသည့် appများကို ရယူခြင်း"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"အပလီကေးရှင်းအား လက်ရှိနဲ့ လတ်တလော လုပ်ဆောင်ခဲ့သော သတင်းအချက်အလက် အသေးစိတ်အား ထုတ်ယူခွင့်ပြုရန်။ အပလီကေးရှင်းမှ သင် ဘယ် အပလီကေးရှင်းများသုံးရှိကြောင့် တွေ့ရှိနိုင်ပါသည်"</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"ကိုယ်ရေးအချက်အလက်နှင့် စက်ပစ္စည်းပိုင်ရှင်များကို စီမံပါ"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"ကိုယ်ရေးအချက်အလက်နှင့် စက်ပစ္စည်း ပိုင်ရှင်များကို စီမံပါ"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"ကိုယ်ရေးအချက်လက်ပိုင်ရှင်များနှင့်စက်ပစ္စည်းပိုင်ရှင်အား သတ်မှတ်ရန် App အားခွင့်ပြုပါ။"</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"အလုပ်လုပ်နေကြသည့် appများကို ပြန်လည်စီစဉ်ခြင်း"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"အပလီကေးရှင်းအား နောက်ကွယ် နှင့် ရှေ့မှောက်တွင် လက်ရှိ လုပ်ဆောင်နေမှုများအား ဖယ်ခွင့် ပြုပါ။ သင့် ခွင့်ပြုချက်မပါပဲ လုပ်ဆောင်နိုင်ပါလိမ့်မည်"</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"အပလီကေးရှင်းအား သင့်တက်ဘလက်၏ ဖုန်းခေါ်ဆိုမှု မှတ်တမ်း (အဝင်အထွက်ခေါ်ဆိုမှု အချက်အလက်များ) ကို ပြင်ဆင်ခွင့် ပေးခြင်း။ အန္တရာယ်ရှိ အပလီကေးရှင်းများမှ ဤအချက်ကို အသုံးပြု၍ သင့် ဖုန်းခေါ်ဆိုမှု မှတ်တမ်းကို ဖျက်ပစ်ခြင်း၊ ပြင်ဆင်ခြင်းများ ပြုလုပ်နိုင်ပါသည်"</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"အဝင်အထွက်ခေါ်ဆိုမှု အချက်အလက်များ အပါအဝင်၊ သင့်တီဗွီ၏ ဖုန်းခေါ်ဆိုမှု မှတ်တမ်းကို အပလီကေးရှင်းအား ပြင်ဆင်ခွင့်ပေးခြင်း။ အန္တရာယ်ရှိ အပလီကေးရှင်းများမှ ဤအချက်ကို အသုံးပြု၍ သင့် ဖုန်းခေါ်ဆိုမှု မှတ်တမ်းကို ဖျက်ပစ်ခြင်း၊ ပြင်ဆင်ခြင်းများ ပြုလုပ်နိုင်၏။"</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"အပလီကေးရှင်းအား သင့်ဖုန်း၏ ဖုန်းခေါ်ဆိုမှု မှတ်တမ်း (အဝင်အထွက်ခေါ်ဆိုမှု အချက်အလက်များ) ကို ပြင်ဆင်ခွင့် ပေးခြင်း။ အန္တရာယ်ရှိ အပလီကေးရှင်းများမှ ဤအချက်ကို အသုံးပြု၍ သင့် ဖုန်းခေါ်ဆိုမှု မှတ်တမ်းကို ဖျက်ပစ်ခြင်း၊ ပြင်ဆင်ခြင်းများ ပြုလုပ်နိုင်ပါသည်"</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"ခန္ဓာကိုယ် အာရံခံကိရိယာများ (နှလုံးခုန်နှုန်း စောင့်ကြည့်စက် လို)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"ခန္ဓာကိုယ် အာရုံကိရိယာများကို (နှလုံးခုန်နှုန်း မော်နီတာလို)ကို ရယူသုံးရန်"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"သင်၏ နှလုံးခုန်နှုန်းလို ရုပ်ပိုင်း အခြေအနေကို စောင့်ကြပ်သည့် အာရုံခံစက်များထံမှ ဒေတာများကို appအား ရယူသုံးခွင့် ပြုပါ။"</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"ပြက္ခဒိန်အဖြစ်အပျက်များနှင့် လှို့ဝှက်အချက်အလက်များအား ဖတ်ခြင်း"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"အပလီကေးရှင်းအား တက်ဘလက်ထဲတွင် သိမ်းထားသော သူငယ်ချင်းနှင့် လုပ်ဖော်ကိုင်ဘက်များ၏ ပြက္ခဒိန် အဖြစ်အပျက်များအပါအဝင် အားလုံးကို ဖတ်ရှုခွင့်ပြုပါ။ ဒီခွင့်ပြုချက်ကြောင့် အပလီကေးရှင်းမှ ပြက္ခဒိန် အဖြစ်အပျက်များအား လျှို့ဝှက်မှု သို့ ဂရုပြုမှု ကို ထည့်သွင်းမစဉ်းစားပဲ သိမ်းဆည်းခြင်း၊ မျှဝေခြင်း ပြုလုပ်စေနိုင်ပါသည်"</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"အပလီကေးရှင်းအား သင်၏ ဖုန်းတွင် သူငယ်ချင်း အလုပ်ဖော်များ အပါအဝင် သင်၏ ပြောင်းလဲအဖြစ်အပျက်များအား ထည့်ခြင်း၊ ထုတ်ခြင်းအား ခွင့်ပြုရန်။ ဤခွင့်ပြုချက်သည် အပလီကေးရှင်းအား သတင်းများပို့ခြင်းကို ပြက္ခဒိန်ပိုင်ရှင်ဆီမှ လာသလို အနေဖြင့် ပေးပို့ခြင်း သို့မဟုတ် အဖြစ်အပျက်များကို ပိုင်ရှင်မသိပဲ ပြင်ဆင်နိုင်ပါသည်။"</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"တည်နေရာပံ့ပိုးမှုညွှန်ကြားချက်အပိုအား ဝင်ရောက်ကြည့်ခြင်း"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"appအား တည်နေရာ စီမံပေးရေး ညွှန်ကြားချက် အပိုများကို ရယူခွင့်ပြုသည်။ သို့ဖြစ်၍ appသည် GPS သို့မဟုတ် အခြား တည်နေရာ ရင်းမြစ်ကို သုံးကြသူတို့၏ လုပ်ငန်းများကို ဝင်စွက်ခွင့် ပြုနိုင်သည်။"</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"တည်နေရာ အတိအကျ (ဂျီပီအက်စ် နှင့် ကွန်ရက်အခြေခံ)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"တိကျတဲ့ တည်နေရာ (GPS နှင့် ကွန်ရက် အခြေခံ)ကို ရယူသုံးရန်"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"အပလီကေးရှင်းမှ သင့်ရဲ့ တိကျသောနေရာကို ဂျီပီအက်စ် သို့ ဆယ်လူလာတာဝါတိုင်၊ ဝိုင်ဖိုင် အချက်အလက်များ သုံးပြီး ရှာခြင်း ခွင့်ယူပါ။ နေရာပြ ဆားဗစ်များ စက်ပေါ်မှာ ရှိရမှာ ဖြစ်သလို ဖွင့်ထားရမှာလည်း ဖြစ်ပါသည်။ အပလီကေးရှင်းမှ ဒီဆားဗစ်များကို သုံး၍ ရှာဖွေသောကြောင့် ဘက်ထရီ ပိုကုန်နိုင်ပါသည်။"</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"အကြမ်းဖျင်းနေရာ (ကွန်ရက်အခြေခံ)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"အနီးစပ်ဆုံး တည်နေရာ (ကွန်ရက် အခြေခံ)ကို ရယူသုံးရန်"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"သင့်ရဲ့ ပျမ်းမျတည်နေရာကို အပလီကေးရှင်း အား သိခွင့် ပြုရန်။ ဒီ တည်နေရာကို တည်နေရာရှာဖွေရေး ဆားဗစ်မှ မိုဘိုင်း တာဝါတိုင်၊ ဝိုင်ဖိုင် စသည်တို့မှ တဆင့် ရယူပါသည်။ အပလီကေးရှင်း အနေဖြင့် ဒီ ဆားဗစ်များ ရှိနေရန် လိုအပ်ပါသည်။ ဒီအရာများကို အသုံးပြု၍ သင့်နေရာကို သိနိုင်ပါသည်။"</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"သင့်အသံအပြင်အဆင်အားပြောင်းခြင်း"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"အပလီကေးရှင်းအား အသံအတိုးအကျယ်နှင့် အထွက်ကို မည်သည့်စပီကာကို သုံးရန်စသည်ဖြင့် စက်တစ်ခုလုံးနှင့်ဆိုင်သော အသံဆိုင်ရာ ဆက်တင်များ ပြင်ဆင်ခွင့် ပြုရန်"</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"အသံဖမ်းခြင်း"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"အပလီကေးရှင်းအား မိုက်ခရိုဖုန်းဖြင့် အသံသွင်းခွင့် ပြုပါ။ အပလီကေးရှင်းအနေဖြင့် သင့် ခွင့်ပြုချက် မပါပဲ အချိန်မရွေး အသံဖမ်းနိုင်ပါမည်"</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"ဆင်းမ်ကဒ် ဆက်သွယ်ရေး"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"SIM ထံသို့ ညွှန်ကြားချက်များကို ပို့ပါ"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"အပလီကေးရှင်းအား ဆင်းမ်ကဒ်ဆီသို့ အမိန့်များ ပေးပို့ခွင့် ပြုခြင်း။ ဒီ ခွင့်ပြုမှုဟာ အန်တရယ် အလွန် ရှိပါသည်။."</string>
<string name="permlab_camera" msgid="3616391919559751192">"ဓါတ်ပုံနှင့်ဗွီဒီယိုရိုက်ခြင်း"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"အပလီကေးရှင်းအား အလိုအလျောက် ဓာတ်ပုံရိုက်ခွင့်၊ ဗီဒီယို ရိုက်ကူးခွင့် ပြုပါ။ ဒီခွင့်ပြုချက်က အပလီကေးရှင်းကို အချိန်မရွေး ကင်မရာအား ခွင့်ပြုချက် မလိုအပ်ပဲ သုံးခွင့်ပြုပါသည်။"</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"အပလီကေးရှင်းအား ဖုန်းမှ သိရှိထားသော အကောင့်စာရင်းများအား ရယူခွင့်ပေးပါ။ ဒီထဲတွင် သင် ထည့်သွင်းထားသော အပလီကေးရှင်းများမှတဆင့် ပြုလုပ်ထားသော အကောင့်များပါ ပါဝင်နိုင်ပါသည်။"</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"ကွန်ရက် ချိတ်ဆက်မှုများအား ကြည့်ရန်"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"အပလီကေးရှင်းအား မည်သည့်ကွန်ရက်နက်ဝဘ်ရှိသလဲ၊ မည်သည့်ကွန်ရက်နှင့် ချိတ်ဆက်ထားလဲ စသည်ဖြင့် ကွန်ရက်ချိတ်ဆက်မှုများ၏ သတင်းအချက်အလက်များကို ကြည့်ခွင့်ပေးရန်"</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"အပြည့်အ၀ ကွန်ရက်သုံးခွင့်ရရန်"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"ကွန်ရက်ကို အပြည့်အဝ ရယူသုံးနိုင်"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"အပလီကေးရှင်းအား ကွန်ရက်ဆော့ကတ်များ တည်ဆောက်ခွင့်၊ တသီးတသန့် ကွန်ရက် ပရိုတိုကောလ်များ သုံးခွင့် ပြုပါ။ အင်တာနက်မှ အချက်အလက်များ ပေးပို့ခြင်းကို ဘရောက်ဇာနှင့် တခြား အပလီကေးရှင်းများက လုပ်ဆောင်ပေးသောကြောင့် ဒီခွင့်ပြုချက်က အင်တာနက်မှ အချက်အလက် ပေးပို့ခြင်း မလိုအပ်ပါ"</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"ကွန်ယက်ဆက်သွယ်မှုအားပြောင်းခြင်း"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"appအား ကွန်ရက် ချိတ်ဆက်နိုင်စွမ်း အခြေအနေကို ပြောင်းလဲခွင့် ပြုသည်။"</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"appအား ဒေသန္တရ ဘလူးတုသ် ဖုန်းကို စီစဉ်ဖွဲ့စည်းခွင့်ကို၎င်း၊ အဝေးထိန်း ကိရိယာများကို ရှာကြံလျက် ချိတ်တွဲခွင့်ကို၎င်း ပေးထားသည်။"</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"ဝိုင်မက်စ် နှင့် ချိတ်ဆက်ရန်နှင့် ဆက်သွယ်မှု ဖြတ်တောက်ရန်"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"အပလီကေးရှင်းအား ဝိုင်မက်စ် အခြေအနေ ကြည့်ခွင့်ပေးရန် ဥပမာ ဝိုင်မက်စ် ဖွင့်ထား မထား၊ ဝိုင်မက်စ် ချိတ်ဆက်ထားသော ကွန်ရက်အခြေအနေ"</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"ဝိုက်မက်စ် အခြေအနေအား ပြင်ရန်"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"WiMAX အခြေအနေကို ပြောင်းရန် ပြင်ရန်"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"အပလီကေးရှင်းအား တက်ဘလက်ကို ဝိုင်မက်စ် ကွန်ရက်များနဲ့ ဆက်သွယ်ခြင်း၊ ဆက်သွယ်မှု ရပ်ဆိုင်းခြင်းများ လုပ်ခွင့်ပြုပါ"</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"တီဗွီနှင့် ချိတ်ဆက်ရန် app အား ခွင့်ပြုပြီး တီဗွီနှင့် WiMAX ကွန်ယက်များ ချိတ်ဆက်ထားမှုအား ဖြတ်တောက်ပါ။"</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"အပလီကေးရှင်းအား ဖုန်းကို ဝိုင်မက်စ် ကွန်ရက်များနဲ့ ဆက်သွယ်ခြင်း၊ ဆက်သွယ်မှု ရပ်ဆိုင်းခြင်းများ လုပ်ခွင့်ပြုပါ"</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"appအား တို့ထိရေး မျက်နှာပြင် တိုင်းထွာစံညှိမှုကို မွမ်းမံခွင့် ပြုသည်။ သာမန် appများ ဘယ်တော့မှ မလိုအပ်နိုင်ပါ။"</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"DRM လက်မှတ်များကို ရယူသုံးခြင်း"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"အပလီကေးရှင်း တစ်ခုအား စီမံလုပ်ကိုင်ခွင့် DRM လက်မှတ်များ သုံးခွင့် ပြုသည်။ သာမန် appများ အတွက် ဘယ်တော့မှ မလိုအပ်နိုင်ပါ။"</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"အန်ဒရွိုက်၏ အလင်းတန်းထိုး လွှဲပြောင်းမှု အခြေအနေကို ရယူရန်"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"Android ရဲ့ အလင်းတန်းထိုး လွှဲပြောင်းမှု အခြေအနေကို ရယူရန်"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"ဒီအပလီကေးရှင်းအား အန်ဒရွိုက်၏ လက်ရှိ အလင်းတန်းထိုး လွှဲပြောင်းမှု အကြောင်း အချက်အလက်ကို ရယူခွင့် ပြုသည်"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"DRM လက်မှတ်များ ဖယ်ရှားရန်"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"အပလီကေးရှင်းအား DRM လက်မှတ်များကို ဖယ်ရှားခွင့် ပြုသည်။ သာမန် appများ အတွက် ဘယ်တော့မှ မလိုအပ်နိုင်ပါ။"</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"ပင်နံပါတ်မှားနေပါသည်"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"သော့ဖွင့်ရန် Menu ထိုနောက်0ကိုနှိပ်ပါ"</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"အရေးပေါ်နံပါတ်"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"ဆားဗစ် မရှိပါ"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"ဝန်ဆောင်မှု မရှိပါ"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"မျက်နှာပြင်အားသော့ချထားသည်"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"ဖွင့်ရန်သို့မဟုတ်အရေးပေါ်ခေါ်ဆိုခြင်းပြုလုပ်ရန် မီနူးကိုနှိပ်ပါ"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"မီးနူးကို နှိပ်ခြင်းဖြင့် သော့ဖွင့်ပါ"</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"သုံးစွဲသူ လမ်းညွှန်ကို ကြည့်ပါ သို့မဟုတ် ဖောက်သည်များ စောင့်ရှောက်ရေး ဌာနကို ဆက်သွယ်ပါ။"</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"ဆင်းမ်ကဒ် သော့ကျနေပါသည်"</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"ဆင်းမ်ကဒ် ကို သော့ဖွင့်နေပါသည်"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"သင်သည် သော့ဖွင့် ပုံစံကို<xliff:g id="NUMBER_0">%d</xliff:g> ကြိမ် မမှန်မကန် ရေးဆွဲခဲ့သည်။ \n\nထပ်ပြီးတော့ <xliff:g id="NUMBER_1">%d</xliff:g>စက္ကန့် အကြာမှာ စမ်းကြည့်ပါ။"</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"သင်သည် စကားဝှက်ကို <xliff:g id="NUMBER_0">%d</xliff:g> ကြိမ် မမှန်မကန် ရိုက်ခဲ့ပြီ။ \n\n ထပ်ပြီးတော့ <xliff:g id="NUMBER_1">%d</xliff:g> စက္ကန့်အကြာ စမ်းကြည့်ပါ။"</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"သင်သည် သင်၏ PIN <xliff:g id="NUMBER_0">%d</xliff:g>ကို ကြိမ် မမှန်မကန် ရိုက်ခဲ့ပြီ။ \n\n ထပ်ပြီးတော့ <xliff:g id="NUMBER_1">%d</xliff:g> စက္ကန့်အကြာ စမ်းကြည့်ပါ။"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"သင်သည် သော့ဖွင့် ပုံစံကို<xliff:g id="NUMBER_0">%d</xliff:g> ကြိမ် မမှန်မကန် ရေးဆွဲခဲ့သည်။ နောက်ထပ် <xliff:g id="NUMBER_1">%d</xliff:g> မအောင်မြင်သည့် ကြိုးပမ်းမှု နောက်မှာ၊ သင်၏ တက်ဘလက်ကို Google လက်မှတ်ထိုး ဝင်မှုဖြင့် ဖွင့်ရန် တောင်းဆိုခံရမည်။ \n\n ထပ်ပြီး <xliff:g id="NUMBER_2">%d</xliff:g> စက္ကန့် အကြာမှာ စမ်းကြည့်ပါ။"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"တီဗွီဖွင့်ရန် သင် <xliff:g id="NUMBER_0">%d</xliff:g> ကြိမ် မှားယွင်းစွာ ကြိုးစားပြီးဖြစ်၏။ <xliff:g id="NUMBER_1">%d</xliff:g> ကြိမ်ကြိုးစားပြီးနောက် မအောင်မြင်ပါက၊ သင့် Google လက်မှတ်ထိုးဝင်ရောက်သည့် အချက်အလက်ကို သုံးလျက် တီဗွီအား သော့ဖွင့်ရမည်။ \n\n <xliff:g id="NUMBER_2">%d</xliff:g> စက္ကန့်အကြာတွင် ထပ်မံကြိုးစားပါ။"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"သင်သည် သော့ဖွင့် ပုံစံကို <xliff:g id="NUMBER_0">%d</xliff:g> ကြိမ် မမှန်မကန် ရေးဆွဲခဲ့သည်။ နောက်ထပ် <xliff:g id="NUMBER_1">%d</xliff:g> မအောင်မြင်သည့် ကြိုးပမ်းမှု နောက်မှာ၊ သင်၏ ဖုန်းကို Google လက်မှတ်ထိုး ဝင်မှုဖြင့် ဖွင့်ရန် တောင်းဆိုခံရမည်။ \n\n ထပ်ပြီး <xliff:g id="NUMBER_2">%d</xliff:g> စက္ကန့် အကြာမှာ စမ်းကြည့်ပါ။"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"သင့်Tabletအား မှားယွင်းစွာ <xliff:g id="NUMBER_0">%d</xliff:g>ကြိမ်ဖွင့်ရန် ကြိုးစားနေပါသည်။ နောက်ထပ်<xliff:g id="NUMBER_1">%d</xliff:g>ကြိမ်မအောင်မြင်ပါက မူလစက်ရုံ အနေအထားသို့ပြန်လည်ရောက်ရှိကာ အသုံးပြုသူ၏ဒေတာအားလုံးဆုံးရှုံးပါမည်။"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"တီဗွီဖွင့်ရန် သင် <xliff:g id="NUMBER_0">%d</xliff:g> ကြိမ် မှားယွင်းစွာ ကြိုးစားပြီးဖြစ်၏။<xliff:g id="NUMBER_1">%d</xliff:g> ကြိမ်ကြိုးစားပြီးနောက် မအောင်မြင်ပါက၊ တီဗွီသည် စက်ရုံထုတ်အခြေအနေသို့ ပြန်လည်ရောက်ရှိသွားမည်ဖြစ်ပြီး အသုံးပြုသူ၏ အချက်အလက်များ ပျောက်သွားမည်ဖြစ်၏။"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"သင့်ဖုန်းအား မှားယွင်းစွာ <xliff:g id="NUMBER_0">%d</xliff:g>ကြိမ်ဖွင့်ရန် ကြိုးစားနေပါသည်။ နောက်ထပ်<xliff:g id="NUMBER_1">%d</xliff:g>ကြိမ်မအောင်မြင်ပါက မူလစက်ရုံ အနေအထားသို့ပြန်လည်ရောက်ရှိကာ အသုံးပြုသူ၏ဒေတာအားလုံးဆုံးရှုံးပါမည်။"</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"သင်သည် သော့ဖွင့် ပုံစံကို<xliff:g id="NUMBER_0">%1$d</xliff:g> ကြိမ် မမှန်မကန် ရေးဆွဲခဲ့သည်။ \n\nထပ်ပြီးတော့ <xliff:g id="NUMBER_1">%2$d</xliff:g>စက္ကန့် အကြာမှာ စမ်းကြည့်ပါ။"</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"သင်သည် စကားဝှက်ကို <xliff:g id="NUMBER_0">%1$d</xliff:g> ကြိမ် မမှန်မကန် ရိုက်ခဲ့ပြီ။ \n\n ထပ်ပြီးတော့ <xliff:g id="NUMBER_1">%2$d</xliff:g> စက္ကန့်အကြာ စမ်းကြည့်ပါ။"</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"သင်သည် သင်၏ PIN <xliff:g id="NUMBER_0">%1$d</xliff:g>ကို ကြိမ် မမှန်မကန် ရိုက်ခဲ့ပြီ။ \n\n ထပ်ပြီးတော့ <xliff:g id="NUMBER_1">%2$d</xliff:g> စက္ကန့်အကြာ စမ်းကြည့်ပါ။"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"သင်သည် သော့ဖွင့် ပုံစံကို<xliff:g id="NUMBER_0">%1$d</xliff:g> ကြိမ် မမှန်မကန် ရေးဆွဲခဲ့သည်။ နောက်ထပ် <xliff:g id="NUMBER_1">%2$d</xliff:g> မအောင်မြင်သည့် ကြိုးပမ်းမှု နောက်မှာ၊ သင်၏ တက်ဘလက်ကို Google လက်မှတ်ထိုး ဝင်မှုဖြင့် ဖွင့်ရန် တောင်းဆိုခံရမည်။ \n\n ထပ်ပြီး <xliff:g id="NUMBER_2">%3$d</xliff:g> စက္ကန့် အကြာမှာ စမ်းကြည့်ပါ။"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"တီဗွီဖွင့်ရန် သင် <xliff:g id="NUMBER_0">%1$d</xliff:g> ကြိမ် မှားယွင်းစွာ ကြိုးစားပြီးဖြစ်၏။ <xliff:g id="NUMBER_1">%2$d</xliff:g> ကြိမ်ကြိုးစားပြီးနောက် မအောင်မြင်ပါက၊ သင့် Google လက်မှတ်ထိုးဝင်ရောက်သည့် အချက်အလက်ကို သုံးလျက် တီဗွီအား သော့ဖွင့်ရမည်။ \n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> စက္ကန့်အကြာတွင် ထပ်မံကြိုးစားပါ။"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"သင်သည် သော့ဖွင့် ပုံစံကို <xliff:g id="NUMBER_0">%1$d</xliff:g> ကြိမ် မမှန်မကန် ရေးဆွဲခဲ့သည်။ နောက်ထပ် <xliff:g id="NUMBER_1">%2$d</xliff:g> မအောင်မြင်သည့် ကြိုးပမ်းမှု နောက်မှာ၊ သင်၏ ဖုန်းကို Google လက်မှတ်ထိုး ဝင်မှုဖြင့် ဖွင့်ရန် တောင်းဆိုခံရမည်။ \n\n ထပ်ပြီး <xliff:g id="NUMBER_2">%3$d</xliff:g> စက္ကန့် အကြာမှာ စမ်းကြည့်ပါ။"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"သင့်Tabletအား မှားယွင်းစွာ <xliff:g id="NUMBER_0">%1$d</xliff:g>ကြိမ်ဖွင့်ရန် ကြိုးစားနေပါသည်။ နောက်ထပ်<xliff:g id="NUMBER_1">%2$d</xliff:g>ကြိမ်မအောင်မြင်ပါက မူလစက်ရုံ အနေအထားသို့ပြန်လည်ရောက်ရှိကာ အသုံးပြုသူ၏ဒေတာအားလုံးဆုံးရှုံးပါမည်။"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"တီဗွီဖွင့်ရန် သင် <xliff:g id="NUMBER_0">%1$d</xliff:g> ကြိမ် မှားယွင်းစွာ ကြိုးစားပြီးဖြစ်၏။<xliff:g id="NUMBER_1">%2$d</xliff:g> ကြိမ်ကြိုးစားပြီးနောက် မအောင်မြင်ပါက၊ တီဗွီသည် စက်ရုံထုတ်အခြေအနေသို့ ပြန်လည်ရောက်ရှိသွားမည်ဖြစ်ပြီး အသုံးပြုသူ၏ အချက်အလက်များ ပျောက်သွားမည်ဖြစ်၏။"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"သင့်ဖုန်းအား မှားယွင်းစွာ <xliff:g id="NUMBER_0">%1$d</xliff:g>ကြိမ်ဖွင့်ရန် ကြိုးစားနေပါသည်။ နောက်ထပ်<xliff:g id="NUMBER_1">%2$d</xliff:g>ကြိမ်မအောင်မြင်ပါက မူလစက်ရုံ အနေအထားသို့ပြန်လည်ရောက်ရှိကာ အသုံးပြုသူ၏ဒေတာအားလုံးဆုံးရှုံးပါမည်။"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"သင့်Tabletအား မှားယွင်းစွာ <xliff:g id="NUMBER">%d</xliff:g>ကြိမ်ဖွင့်ရန် ကြိုးစားခဲ့ပါသည်။ယခုဖုန်းကိုမူလစက်ရုံအနေအထားသို့ပြန်လည်ရောက်ရှိပါမည်။"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"တီဗွီဖွင့်ရန် သင် <xliff:g id="NUMBER">%d</xliff:g> ကြိမ် မှားယွင်းစွာ ကြိုးစားပြီးဖြစ်၏။ တီဗွီသည် စက်ရုံထုတ်အခြေအနေသို့ ပြန်လည်ရောက်ရှိသွားမည်ဖြစ်၏။"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"သင့်ဖုန်းအား မှားယွင်းစွာ <xliff:g id="NUMBER">%d</xliff:g>ကြိမ်ဖွင့်ရန် ကြိုးစားခဲ့ပါသည်။ ယခုဖုန်းကို မူလစက်ရုံအနေအထားသို့ပြန်လည်ရောက်ရှိပါမည်။"</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"စာလုံး"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"အခြားစာမျက်နှာသို့ကွင်းဆက်"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"စာကြောင်း"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"စက်ရုံစမ်းသပ်ခြင်းမအောင်မြင်ပါ"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"စက်ရုံစမ်းသပ်မှုမှာ စနစ်/အပ်ပလီကေးရှင်း ထည့်သွင်းထားသောpackageများကိုသာ ပံ့ပိုးမှုပေးသည်။."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"စက်ရုံစမ်းသပ်မှုအားလုပ်ဆောင်ရန် မည်သည့်packageမှ မတွေ့ပါ။"</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"ပုံစံပြင်နေစဉ်…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"ဖြည့်စွက်ထားခြင်း မရှိပါ"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"တိုက်ဆိုင်သော ပြုလုပ်ချက် ရှာမတွေ့ပါ"</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"မီဒီယာထွက်ပေါက်အား လမ်းလွှဲပြောင်းခြင်း"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"မီဒီယာ ထွက်ပေါက်ကို လမ်းဖေါ်ပြပေးပါ"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"အပလီကေးရှင်းအား မီဒီယာ ထုတ်လွှတ်မှုကို အခြားပြင်ပ စက်ပစ္စည်းများသို့ လွှဲပြောင်းခွင့်ပြုပါ"</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"တပ်ဆင်ရေး ချိတ်ဆက်မှုများကို ဖတ်ရန်"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"တပ်ဆင်ရေး လုပ်ကိုင်မှုကို ဖတ်ရန်"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"အပလီကေးရှင်းအား တပ်ဆင်ရေး ချိတ်ဆက်မှုများကို ဖတ်ခွင့်ပြုသည်။ ၎င်းသည် ဖွင့်သုံးနေသည့် အထုပ်အား တပ်ဆင်မှုဆိုင်ရာ အသေးိစတ်များကို ကြည့်ရှုခွင့် ပြုသည်။"</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"ပက်ကေ့များ သွင်းယူရန် တောင်းဆိုပါ"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"တပ်ဆင်ရေး အထုပ်များကို တောင်းဆိုပါ"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"ပက်ကေ့များ သွင်းယူခြင်းအတွက် တောင်းဆိုရန် အပ္ပလီကေးရှင်းအား ခွင့်ပြုပါ"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"ချုံ့ချဲ့မှုကို ထိန်းချုပ်ရန် အတွက် နှစ်ကြိမ် ထိပါ"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"ဝဒ်ဂျက်ထည့်လို့ မရပါ"</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"အသုံးပြုသူ အမည် သို့ စကားဝှက်မမှန်ကန်ပါ"</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"သင် သုံးစွဲသူ အမည် သို့ စကားဝှက်အားမေ့နေပါသလား။\n"<b>"google.com/accounts/recovery"</b>" သို့ သွားရောက်ပါ"</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"အကောင့်ကို စစ်ဆေးနေစဉ်..."</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"သင် ပင် နံပါတ်ကို အမှားကို <xliff:g id="NUMBER_0">%d</xliff:g> ကြိမ် ရိုက်ထည့်ပြီးပါပြီ။ \n\n<xliff:g id="NUMBER_1">%d</xliff:g> စက္ကန့် အကြာတွင် ပြန်လည်ကြိုးစားပါ"</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"သင်သည် စကားဝှက်ကို <xliff:g id="NUMBER_0">%d</xliff:g> ကြိမ် မှားရိုက်ပြီးပါပြီ။ \n\n <xliff:g id="NUMBER_1">%d</xliff:g> စက္ကန့်အကြာ ပြန်လည်ကြိုးစားပါ"</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"သင် ပုံစံဆွဲ သော့ဖွင့်ခြင်းကို <xliff:g id="NUMBER_0">%d</xliff:g> ကြိမ်မြောက် မအောင်မြင်ပါ။ \n\n<xliff:g id="NUMBER_1">%d</xliff:g> စက္ကန့်အကြာတွင် ပြန်လည် ကြိုးစားပါ"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"သင့်တက်ဘလက်အား သော့ဖွင့်ရန် မအောင်မြင်သော အကြိမ်ရေ <xliff:g id="NUMBER_0">%d</xliff:g> ကြိုးစားပြီးပါပြီ။ နောက်ထပ် <xliff:g id="NUMBER_1">%d</xliff:g> အကြိမ် မအောင်မြင်ပါက၊ တက်ဘလက်က စက်ရုံထွက် အခြေအနေကို ပြန်လည် ရောက်ရှိသွားမည်ဖြစ်ပြီး ဒေတာအားလုံး ဆုံးရှုံးသွားပါမည်။"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"တီဗွီဖွင့်ရန် သင် <xliff:g id="NUMBER_0">%d</xliff:g> ကြိမ် မှားယွင်းစွာ ကြိုးစားပြီးဖြစ်၏။<xliff:g id="NUMBER_1">%d</xliff:g> ကြိမ်ကြိုးစားပြီးနောက် မအောင်မြင်ပါက၊ တီဗွီသည် စက်ရုံထုတ်အခြေအနေသို့ ပြန်လည်ရောက်ရှိသွားမည်ဖြစ်ပြီး အသုံးပြုသူ၏ အချက်အလက်များ ပျောက်သွားမည်ဖြစ်၏။"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"သင့်ဖုန်းအား သော့ဖွင့်ရန် မအောင်မြင်သော အကြိမ်ရေ <xliff:g id="NUMBER_0">%d</xliff:g> ကြိုးစားပြီးပါပြီ။ နောက်ထပ် <xliff:g id="NUMBER_1">%d</xliff:g> အကြိမ် မအောင်မြင်ပါက၊ ဖုန်းက စက်ရုံထွက် အခြေအနေကို ပြန်လည် ရောက်ရှိသွားမည်ဖြစ်ပြီး ဒေတာအားလုံး ဆုံးရှုံးသွားပါမည်။"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"သင် ပင် နံပါတ်ကို အမှားကို <xliff:g id="NUMBER_0">%1$d</xliff:g> ကြိမ် ရိုက်ထည့်ပြီးပါပြီ။ \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> စက္ကန့် အကြာတွင် ပြန်လည်ကြိုးစားပါ"</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"သင်သည် စကားဝှက်ကို <xliff:g id="NUMBER_0">%1$d</xliff:g> ကြိမ် မှားရိုက်ပြီးပါပြီ။ \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> စက္ကန့်အကြာ ပြန်လည်ကြိုးစားပါ"</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"သင် ပုံစံဆွဲ သော့ဖွင့်ခြင်းကို <xliff:g id="NUMBER_0">%1$d</xliff:g> ကြိမ်မြောက် မအောင်မြင်ပါ။ \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> စက္ကန့်အကြာတွင် ပြန်လည် ကြိုးစားပါ"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"သင့်တက်ဘလက်အား သော့ဖွင့်ရန် မအောင်မြင်သော အကြိမ်ရေ <xliff:g id="NUMBER_0">%1$d</xliff:g> ကြိုးစားပြီးပါပြီ။ နောက်ထပ် <xliff:g id="NUMBER_1">%2$d</xliff:g> အကြိမ် မအောင်မြင်ပါက၊ တက်ဘလက်က စက်ရုံထွက် အခြေအနေကို ပြန်လည် ရောက်ရှိသွားမည်ဖြစ်ပြီး ဒေတာအားလုံး ဆုံးရှုံးသွားပါမည်။"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"တီဗွီဖွင့်ရန် သင် <xliff:g id="NUMBER_0">%1$d</xliff:g> ကြိမ် မှားယွင်းစွာ ကြိုးစားပြီးဖြစ်၏။<xliff:g id="NUMBER_1">%2$d</xliff:g> ကြိမ်ကြိုးစားပြီးနောက် မအောင်မြင်ပါက၊ တီဗွီသည် စက်ရုံထုတ်အခြေအနေသို့ ပြန်လည်ရောက်ရှိသွားမည်ဖြစ်ပြီး အသုံးပြုသူ၏ အချက်အလက်များ ပျောက်သွားမည်ဖြစ်၏။"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"သင့်ဖုန်းအား သော့ဖွင့်ရန် မအောင်မြင်သော အကြိမ်ရေ <xliff:g id="NUMBER_0">%1$d</xliff:g> ကြိုးစားပြီးပါပြီ။ နောက်ထပ် <xliff:g id="NUMBER_1">%2$d</xliff:g> အကြိမ် မအောင်မြင်ပါက၊ ဖုန်းက စက်ရုံထွက် အခြေအနေကို ပြန်လည် ရောက်ရှိသွားမည်ဖြစ်ပြီး ဒေတာအားလုံး ဆုံးရှုံးသွားပါမည်။"</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"သင့်တက်ဘလက် အား သော့ဖွင့်ရန် မအောင်မြင်သော အကြိမ်ရေ <xliff:g id="NUMBER">%d</xliff:g> ကြိုးစားပြီးပါပြီ။ တက်ဘလက်က စက်ရုံထွက် အခြေအနေကို ပြန်လည် ရောက်ရှိသွားပါတော့မည်။"</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"တီဗွီဖွင့်ရန် သင် <xliff:g id="NUMBER">%d</xliff:g> ကြိမ် မှားယွင်းစွာ ကြိုးစားပြီးဖြစ်၏။ တီဗွီသည် စက်ရုံထုတ်အခြေအနေသို့ ပြန်လည်ရောက်ရှိသွားမည်ဖြစ်၏။"</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"သင့်ဖုန်းအား သော့ဖွင့်ရန် မအောင်မြင်သော အကြိမ်ရေ <xliff:g id="NUMBER">%d</xliff:g> ကြိုးစားပြီးပါပြီ။ ဖုန်းက စက်ရုံထွက် အခြေအနေကို ပြန်လည် ရောက်ရှိသွားပါတော့မည်။"</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"သင် ပုံဖော်၍သော့ဖွင့်ခြင်းကို <xliff:g id="NUMBER_0">%d</xliff:g> အကြိမ် မှန်ကန်စွာ မပြုလုပ်နိုင်ပါ။ နောက်ထပ် <xliff:g id="NUMBER_1">%d</xliff:g> အကြိမ် မမှန်ကန်ပါက သင့်တက်ဘလက်အား အီးမေးလ်အသုံးပြု၍ သော့ဖွင့်ရန် တောင်းဆိုပါလိမ့်မည်။ \n\n <xliff:g id="NUMBER_2">%d</xliff:g> စက္ကန့်အကြာတွင် ပြန်လည် ကြိုးစားပါ"</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"ပုံဖော်၍ သော့ဖွင့်ခြင်းအား သင် <xliff:g id="NUMBER_0">%d</xliff:g> ကြိမ် မှားဆွဲပြီးဖြစ်၏။ <xliff:g id="NUMBER_1">%d</xliff:g> ကြိမ် ကြိုးစားပြီးနောက် မအောင်မြင်ပါက၊ သင့် အီးမေးအကောင့်အားသုံးလျက် သော့ဖွင့်ရန် ပြောလိမ့်မည်။ \n\n <xliff:g id="NUMBER_2">%d</xliff:g> စက္ကန့်အကြာတွင် ထပ်မံကြိုးစားပါ။"</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"သင် ပုံဖော်၍သော့ဖွင့်ခြင်းကို <xliff:g id="NUMBER_0">%d</xliff:g> အကြိမ် မှန်ကန်စွာ မပြုလုပ်နိုင်ပါ။ နောက်ထပ် <xliff:g id="NUMBER_1">%d</xliff:g> အကြိမ် မမှန်ကန်ပါက သင့်ဖုန်းအား အီးမေးလ်အသုံးပြု၍ သော့ဖွင့်ရန် တောင်းဆိုပါလိမ့်မည်။ \n\n <xliff:g id="NUMBER_2">%d</xliff:g> စက္ကန့်အကြာတွင် ပြန်လည် ကြိုးစားပါ"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"သင် ပုံဖော်၍သော့ဖွင့်ခြင်းကို <xliff:g id="NUMBER_0">%1$d</xliff:g> အကြိမ် မှန်ကန်စွာ မပြုလုပ်နိုင်ပါ။ နောက်ထပ် <xliff:g id="NUMBER_1">%2$d</xliff:g> အကြိမ် မမှန်ကန်ပါက သင့်တက်ဘလက်အား အီးမေးလ်အသုံးပြု၍ သော့ဖွင့်ရန် တောင်းဆိုပါလိမ့်မည်။ \n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> စက္ကန့်အကြာတွင် ပြန်လည် ကြိုးစားပါ"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"ပုံဖော်၍ သော့ဖွင့်ခြင်းအား သင် <xliff:g id="NUMBER_0">%1$d</xliff:g> ကြိမ် မှားဆွဲပြီးဖြစ်၏။ <xliff:g id="NUMBER_1">%2$d</xliff:g> ကြိမ် ကြိုးစားပြီးနောက် မအောင်မြင်ပါက၊ သင့် အီးမေးအကောင့်အားသုံးလျက် သော့ဖွင့်ရန် ပြောလိမ့်မည်။ \n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> စက္ကန့်အကြာတွင် ထပ်မံကြိုးစားပါ။"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"သင် ပုံဖော်၍သော့ဖွင့်ခြင်းကို <xliff:g id="NUMBER_0">%1$d</xliff:g> အကြိမ် မှန်ကန်စွာ မပြုလုပ်နိုင်ပါ။ နောက်ထပ် <xliff:g id="NUMBER_1">%2$d</xliff:g> အကြိမ် မမှန်ကန်ပါက သင့်ဖုန်းအား အီးမေးလ်အသုံးပြု၍ သော့ဖွင့်ရန် တောင်းဆိုပါလိမ့်မည်။ \n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> စက္ကန့်အကြာတွင် ပြန်လည် ကြိုးစားပါ"</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"ဖယ်ရှားရန်"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"အသံကို အကြံပြုထားသည့် ပမာဏထက် မြှင့်ပေးရမလား?\n\nအသံကို မြင့်သည့် အဆင့်မှာ ကြာရှည်စွာ နားထောင်ခြင်းက သင်၏ နားကို ထိခိုက်စေနိုင်သည်။"</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">၁ နာရီအတွက်</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>အထိ"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> အထိ (လာမည့် နှိုးစက်)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"သင်က ဒါကို ပိတ်မပစ်သည့် အထိ"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"မနှောင့်ယှက်ရန် ကိုသင်ပိတ်သည်အထိ"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-nb-watch/strings.xml b/core/res/res/values-nb-watch/strings.xml
index 249026c..e6da48a 100644
--- a/core/res/res/values-nb-watch/strings.xml
+++ b/core/res/res/values-nb-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"App <xliff:g id="NUMBER_0">%1$d</xliff:g> av <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Sensorer"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"se kontaktene dine"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"få tilgang til posisjonen til denne klokken"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"åpne kalenderen din"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"sende og lese SMS-meldinger"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"få tilgang til bilder, media og filer på klokken din"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"spille inn lyd"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"ta bilder og ta opp video"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"ringe og administrere anrop"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"få tilgang til sensordata om de vitale tegnene dine"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"vise appen i statusfeltet"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"få tilgang til kroppssensorer (for eksempel pulsmålere)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"få tilgang til nøyaktig posisjon (GPS- og nettverksbasert)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"få tilgang til omtrentlig posisjon (nettverksbasert)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"sende kommandoer til SIM-kortet"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"få full nettverkstilgang"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"administrere profiler og enhetseiere"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"endre WiMAX-statusen"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"motta overføringsstatus for Android Beam"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"videresende medieutdata"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"lese installeringsøkter"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"be om installasjon av pakker"</string>
</resources>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index b43314b..74d7c98 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -229,7 +229,7 @@
<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>
- <string name="permgroupdesc_location" msgid="1346617465127855033">"tilgang til enhetens plassering"</string>
+ <string name="permgroupdesc_location" msgid="1346617465127855033">"få tilgang til enhetens plassering"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalender"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"åpne kalenderen din"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Dette omfatter personlige data, som kredittkortnumre og passord."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"deaktivere eller endre statusfeltet"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Lar appen deaktivere statusfeltet eller legge til og fjerne systemikoner."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"statusrad"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"vise appen i statusfeltet"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Gir appen tillatelse til å vises i statusfeltet."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"utvide/slå sammen statusfeltet"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Lar appen utvide eller skjule statuslinjen."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Lar appen motta og behandle WAP-meldinger. Dette betyr at appen kan overvåke eller slette meldinger som er sendt til deg uten at du har sett dem."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"hente apper som kjører"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Lar appen hente informasjon om oppgaver som kjører og som nylig har kjørt. Dette kan tillate appen å oppdage informasjon om hvilke apper som brukes på enheten."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Administrer profiler og enhetseiere"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"administrere profiler og enhetseiere"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Lar apper angi profileierne og enhetseieren."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"Endre rekkefølge på apper som kjører"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Lar appen flytte oppgaver til forgrunnen eller bakgrunnen. Appen kan gjøre dette uten instruksjoner fra deg."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Lar appen endre nettbrettets samtalelogg, inkludert data om innkommende og utgående anrop. Skadelige apper kan utnytte denne tillatelsen til å slette eller endre samtaleloggen din."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Gjør at appen kan endre TV-ens samtalelogg, herunder data om innkommende eller utgående samtaler. Skadelige apper kan bruke dette til å slette eller endre samtaleloggen."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Lar appen endre telefonens samtalelogg, inkludert data om innkommende og utgående anrop. Skadelige apper kan utnytte denne tillatelsen til å slette eller endre samtaleloggen din."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"kroppssensorer (som pulsmålere)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"få tilgang til kroppssensorer (f.eks. pulsmålere)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Gir appen tilgang til data fra sensorer som overvåker den fysiske tilstanden din, for eksempel hjertefrekvensen din."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"lese kalenderhendelser og konfidensiell informasjon"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Lar appen lese alle kalenderaktivitetene lagret på nettbrettet ditt, inkludert aktiviteter for venner eller kolleger. Dette kan gjøre at appen deler eller lagrer kalenderinformasjonen din uavhengig av konfidensialitet og sensitivitet."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Lar appen legge til, fjerne og endre aktiviteter du kan redigere på telefonen din, inkludert aktiviteter for venner eller kolleger. Dette kan gjøre at appen sender meldinger som ser ut som om de kommer fra kalendereiere eller endre aktiviteter uten at eierne vet om det."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"bruke ekstra posisjonskommandoer"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Appen gis tillatelse til å bruke ekstra kommandoer fra posisjonsleverandører. Dette kan gi appen tillatelse til å påvirke bruken av GPS eller andre posisjonskilder."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"presis posisjon (GPS- og nettverksbasert)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"få tilgang til nøyaktig posisjon (GPS- og nettverksbasert)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Lar appen se den nøyaktige posisjonen din ved hjelp av GPS (Global Positioning System) eller posisjonstjenester for nettverk, som for eksempel basestasjoner og Wi-Fi. Disse posisjonstjenestene må være slått på og tilgjengelig for enheten din, for at appen skal kunne bruke dem. Apper kan bruke dette til å fastslå hvor du er, og funksjonen kan medføre økt batteribruk."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"omtrentlig posisjon (nettverksbasert)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"få tilgang til omtrentlig posisjon (nettverksbasert)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Lar appen se den omtrentlige posisjonen din. Denne posisjonen hentes fra posisjonstjenester som benytter posisjonskilder som for eksempel basestasjoner og Wi-Fi. Disse posisjonstjenestene må være slått på og tilgjengelig for enheten din, for at appen skal kunne bruke dem. Apper kan bruke dette til å finne ut omtrent hvor du er."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"endre lydinnstillinger"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Lar appen endre globale lydinnstillinger slik som volum og hvilken høyttaler som brukes for lydavspilling."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"ta opp lyd"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Appen tillates å ta opp lyd med mikrofonen. Det betyr at appen kan ta opp lyd når som helst uten at du har bedt om det."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"sim-kommunikasjon"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"sende kommandoer til SIM-kortet"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Lar appen sende kommandoer til SIM-kortet. Dette er veldig farlig."</string>
<string name="permlab_camera" msgid="3616391919559751192">"ta bilder og videoer"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Appen tillates å ta bilder og filme med kameraet. Det betyr at appen kan bruke kameraet når som helst uten bekreftelse fra deg."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Appen kan hente listen over kontoene telefonen kjenner. Dette kan inkludere kontoer som er opprettet av apper du har installert."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"se nettverkstilkoblinger"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Appen kan se informasjon om nettverkstilkoblinger, slik som hvilke nettverk som finnes og er tilkoblet."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"full nettverkstilgang"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"få full nettverkstilgang"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Appen kan opprette nettverkskontakter og bruke tilpassede nettverksprotokoller. Nettleseren og andre apper gjør det mulig å sende data til Internett, så denne tillatelsen er ikke nødvendig for å kunne sende data til Internett."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"endre nettverkskonnektivitet"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Lar appen endre innstillingene for nettverkstilknytning."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Lar appen konfigurere den lokale Bluetooth-telefonen, samt oppdage og koble sammen med eksterne enheter."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"koble til eller fra WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Lar appen avgjøre hvorvidt WiMAX er aktivert og finne informasjon om eventuelle tilkoblede WiMAX-nettverk."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Endre WiMAX-status"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"endre WiMAX-status"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Lar appen koble nettbrettet til og fra WiMAX-nettverk."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Gjør at appen kobler TV-en til og fra WiMAX-nettverk."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Lar appen koble telefonen til og fra WiMAX-nettverk."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Lar appen endre kalibrasjonsparametrene for berøringsskjermen. Denne tillatelsen bør aldri være nødvendig for vanlige apper."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"tilgang til DRM-sertifikater"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Tillater at en app klargjøre og bruke DRM-sertifikater. Denne tillatelsen bør aldri være nødvendig for vanlige apper."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Motta overføringsstatus for Android Beam"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"motta overføringsstatus for Android Beam"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Lar appen motta informasjon om aktuelle Android Beam-overføringer"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"fjern sertifikater for digital rettighetsadministrasjon"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Gir en app tillatelse til å fjerne sertifikater for digital rettighetsadministrasjon. Skal ikke være nødvendig for vanlige apper."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Feil personlig kode."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"For å låse opp, trykk på menyknappen og deretter 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Nødnummer"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Ingen tjeneste."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Ingen dekning"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Skjermen er låst"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Trykk på menyknappen for å låse opp eller ringe et nødnummer."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Trykk på menyknappen for å låse opp."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Les i brukerhåndboken eller kontakt brukerstøtten."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM-kortet er låst."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Låser opp SIM-kort…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Du har oppgitt feil opplåsingsmønster <xliff:g id="NUMBER_0">%d</xliff:g> ganger.\n\nPrøv på nytt om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Du har skrevet inn feil passord <xliff:g id="NUMBER_0">%d</xliff:g> ganger.\n\nPrøv på nytt om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Du har skrevet inn feil PIN-kode <xliff:g id="NUMBER_0">%d</xliff:g> ganger.\n\nPrøv på nytt om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Du har tegnet inn feil opplåsingsmønster <xliff:g id="NUMBER_0">%d</xliff:g> ganger. Etter ytterligere <xliff:g id="NUMBER_1">%d</xliff:g> mislykkede forsøk, blir du bedt om å låse opp nettbrettet ved hjelp av Google-påloggingsinformasjonen din.\n\nPrøv på nytt om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Du har tegnet inn feil opplåsingsmønster <xliff:g id="NUMBER_0">%d</xliff:g> ganger. Etter <xliff:g id="NUMBER_1">%d</xliff:g> mislykkede forsøk til blir du bedt om å låse opp TV-en ved hjelp av Google-pålogging.\n\n Prøv på nytt om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Du har tegnet inn feil opplåsingsmønster <xliff:g id="NUMBER_0">%d</xliff:g> ganger. Etter ytterligere <xliff:g id="NUMBER_1">%d</xliff:g> mislykkede forsøk, blir du bedt om å låse opp telefonen ved hjelp av Google-påloggingsinformasjonen din.\n\nPrøv på nytt om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Du har foretatt <xliff:g id="NUMBER_0">%d</xliff:g> mislykkede opplåsinger av nettbrettet. Etter <xliff:g id="NUMBER_1">%d</xliff:g> flere mislykkede forsøk, blir nettbrettet tilbakestilt til fabrikkinnstillingene, og alle brukerdata går tapt."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Du har gjort feil <xliff:g id="NUMBER_0">%d</xliff:g> ganger i forsøk på å låse opp TV-en. Etter <xliff:g id="NUMBER_1">%d</xliff:g> mislykkede forsøk til blir TV-en tilbakestilt til fabrikkstandard, slik at alle brukerdataene går tapt."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Du har foretatt <xliff:g id="NUMBER_0">%d</xliff:g> mislykkede opplåsinger av telefonen. Etter <xliff:g id="NUMBER_1">%d</xliff:g> flere mislykkede forsøk, blir telefonen tilbakestilt til fabrikkinnstillingene, og alle brukerdata går tapt."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Du har oppgitt feil opplåsingsmønster <xliff:g id="NUMBER_0">%1$d</xliff:g> ganger.\n\nPrøv på nytt om <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunder."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Du har skrevet inn feil passord <xliff:g id="NUMBER_0">%1$d</xliff:g> ganger.\n\nPrøv på nytt om <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunder."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Du har skrevet inn feil PIN-kode <xliff:g id="NUMBER_0">%1$d</xliff:g> ganger.\n\nPrøv på nytt om <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunder."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Du har tegnet inn feil opplåsingsmønster <xliff:g id="NUMBER_0">%1$d</xliff:g> ganger. Etter ytterligere <xliff:g id="NUMBER_1">%2$d</xliff:g> mislykkede forsøk, blir du bedt om å låse opp nettbrettet ved hjelp av Google-påloggingsinformasjonen din.\n\nPrøv på nytt om <xliff:g id="NUMBER_2">%3$d</xliff:g> sekunder."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Du har tegnet inn feil opplåsingsmønster <xliff:g id="NUMBER_0">%1$d</xliff:g> ganger. Etter <xliff:g id="NUMBER_1">%2$d</xliff:g> mislykkede forsøk til blir du bedt om å låse opp TV-en ved hjelp av Google-pålogging.\n\n Prøv på nytt om <xliff:g id="NUMBER_2">%3$d</xliff:g> sekunder."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Du har tegnet inn feil opplåsingsmønster <xliff:g id="NUMBER_0">%1$d</xliff:g> ganger. Etter ytterligere <xliff:g id="NUMBER_1">%2$d</xliff:g> mislykkede forsøk, blir du bedt om å låse opp telefonen ved hjelp av Google-påloggingsinformasjonen din.\n\nPrøv på nytt om <xliff:g id="NUMBER_2">%3$d</xliff:g> sekunder."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Du har foretatt <xliff:g id="NUMBER_0">%1$d</xliff:g> mislykkede opplåsinger av nettbrettet. Etter <xliff:g id="NUMBER_1">%2$d</xliff:g> flere mislykkede forsøk, blir nettbrettet tilbakestilt til fabrikkinnstillingene, og alle brukerdata går tapt."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Du har gjort feil <xliff:g id="NUMBER_0">%1$d</xliff:g> ganger i forsøk på å låse opp TV-en. Etter <xliff:g id="NUMBER_1">%2$d</xliff:g> mislykkede forsøk til blir TV-en tilbakestilt til fabrikkstandard, slik at alle brukerdataene går tapt."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Du har foretatt <xliff:g id="NUMBER_0">%1$d</xliff:g> mislykkede opplåsinger av telefonen. Etter <xliff:g id="NUMBER_1">%2$d</xliff:g> flere mislykkede forsøk, blir telefonen tilbakestilt til fabrikkinnstillingene, og alle brukerdata går tapt."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Du har foretatt <xliff:g id="NUMBER">%d</xliff:g> mislykkede opplåsinger av nettbrettet. Nettbrettet blir nå tilbakestilt til fabrikkinnstillingene."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Du har gjort feil <xliff:g id="NUMBER">%d</xliff:g> ganger i forsøk på å låse opp TV-en. TV-en blir nå tilbakestilt til fabrikkstandard."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Du har foretatt <xliff:g id="NUMBER">%d</xliff:g> mislykkede opplåsinger av telefonen. Telefonen blir nå tilbakestilt til fabrikkinnstillingene."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"ord"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"link"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"linje"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Fabrikktesten feilet"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"The FACTORY_TEST action is only supported for packages installed in /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"No package was found that provides the FACTORY_TEST action."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Formatering …"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Ikke satt inn"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Finner ingen samsvarende aktiviteter."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Videresending av medieutdata"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"videresende medieutdata"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Lar en app videresende medieutdata til andre eksterne enheter."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"lese installeringsøkter"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"lese installeringsøkter"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Tillater en app å lese installeringsøkter. Dette gjør det mulig for den å se detaljer om aktive pakkeinstallasjoner."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Be om installasjon av pakker"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"be om installasjon av pakker"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Lar apper be om installasjon av pakker."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Trykk to ganger for zoomkontroll"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Kunne ikke legge til modulen."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Ugyldig brukernavn eller passord."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Har du glemt brukernavnet eller passordet?\nGå til "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Sjekker kontoen ..."</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Du har oppgitt feil PIN-kode <xliff:g id="NUMBER_0">%d</xliff:g> ganger. \n\nPrøv på nytt om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Du har tastet inn passordet ditt feil <xliff:g id="NUMBER_0">%d</xliff:g> ganger. \n\nPrøv på nytt om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Du har tegnet opplåsningsmønsteret ditt feil <xliff:g id="NUMBER_0">%d</xliff:g> ganger. \n\nPrøv på nytt om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Du har oppgitt feil opplåsningspassord for nettbrettet <xliff:g id="NUMBER_0">%d</xliff:g> ganger. Etter ytterligere <xliff:g id="NUMBER_1">%d</xliff:g> gale forsøk, tilbakestilles nettbrettet til fabrikkstandard og all data går tapt."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Du har gjort feil <xliff:g id="NUMBER_0">%d</xliff:g> ganger i forsøk på å låse opp TV-en. Etter <xliff:g id="NUMBER_1">%d</xliff:g> mislykkede forsøk til blir TV-en tilbakestilt til fabrikkstandard, slik at alle brukerdataene går tapt."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Du har oppgitt feil opplåsningspassord for telefonen <xliff:g id="NUMBER_0">%d</xliff:g> ganger. Etter ytterligere <xliff:g id="NUMBER_1">%d</xliff:g> gale forsøk, tilbakestilles telefonen til fabrikkstandard og all data går tapt."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Du har oppgitt feil PIN-kode <xliff:g id="NUMBER_0">%1$d</xliff:g> ganger. \n\nPrøv på nytt om <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunder."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Du har tastet inn passordet ditt feil <xliff:g id="NUMBER_0">%1$d</xliff:g> ganger. \n\nPrøv på nytt om <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunder."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Du har tegnet opplåsningsmønsteret ditt feil <xliff:g id="NUMBER_0">%1$d</xliff:g> ganger. \n\nPrøv på nytt om <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunder."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Du har oppgitt feil opplåsningspassord for nettbrettet <xliff:g id="NUMBER_0">%1$d</xliff:g> ganger. Etter ytterligere <xliff:g id="NUMBER_1">%2$d</xliff:g> gale forsøk, tilbakestilles nettbrettet til fabrikkstandard og all data går tapt."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Du har gjort feil <xliff:g id="NUMBER_0">%1$d</xliff:g> ganger i forsøk på å låse opp TV-en. Etter <xliff:g id="NUMBER_1">%2$d</xliff:g> mislykkede forsøk til blir TV-en tilbakestilt til fabrikkstandard, slik at alle brukerdataene går tapt."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Du har oppgitt feil opplåsningspassord for telefonen <xliff:g id="NUMBER_0">%1$d</xliff:g> ganger. Etter ytterligere <xliff:g id="NUMBER_1">%2$d</xliff:g> gale forsøk, tilbakestilles telefonen til fabrikkstandard og all data går tapt."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Du har oppgitt feil opplåsningspassord for nettbrettet <xliff:g id="NUMBER">%d</xliff:g> ganger. Telefonen tilbakestilles nå til fabrikkstandard."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Du har gjort feil <xliff:g id="NUMBER">%d</xliff:g> ganger i forsøk på å låse opp TV-en. TV-en blir nå tilbakestilt til fabrikkstandard."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Du har oppgitt feil opplåsningspassord for telefonen <xliff:g id="NUMBER">%d</xliff:g> ganger. Telefonen tilbakestilles nå til fabrikkstandard."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Du har tegnet opplåsningsmønsteret feil <xliff:g id="NUMBER_0">%d</xliff:g> ganger. Etter ytterligere <xliff:g id="NUMBER_1">%d</xliff:g> gale forsøk, blir du bedt om å låse opp nettbrettet via en e-postkonto.\n\n Prøv på nytt om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Du har tegnet inn feil opplåsingsmønster <xliff:g id="NUMBER_0">%d</xliff:g> ganger. Etter <xliff:g id="NUMBER_1">%d</xliff:g> mislykkede forsøk til blir du bedt om å låse opp TV-en ved hjelp av en e-postkonto.\n\n Prøv på nytt om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Du har tegnet opplåsningsmønsteret feil <xliff:g id="NUMBER_0">%d</xliff:g> ganger. Etter ytterligere <xliff:g id="NUMBER_1">%d</xliff:g> gale forsøk, blir du bedt om å låse opp telefonen via en e-postkonto.\n\n Prøv på nytt om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Du har tegnet opplåsningsmønsteret feil <xliff:g id="NUMBER_0">%1$d</xliff:g> ganger. Etter ytterligere <xliff:g id="NUMBER_1">%2$d</xliff:g> gale forsøk, blir du bedt om å låse opp nettbrettet via en e-postkonto.\n\n Prøv på nytt om <xliff:g id="NUMBER_2">%3$d</xliff:g> sekunder."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Du har tegnet inn feil opplåsingsmønster <xliff:g id="NUMBER_0">%1$d</xliff:g> ganger. Etter <xliff:g id="NUMBER_1">%2$d</xliff:g> mislykkede forsøk til blir du bedt om å låse opp TV-en ved hjelp av en e-postkonto.\n\n Prøv på nytt om <xliff:g id="NUMBER_2">%3$d</xliff:g> sekunder."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Du har tegnet opplåsningsmønsteret feil <xliff:g id="NUMBER_0">%1$d</xliff:g> ganger. Etter ytterligere <xliff:g id="NUMBER_1">%2$d</xliff:g> gale forsøk, blir du bedt om å låse opp telefonen via en e-postkonto.\n\n Prøv på nytt om <xliff:g id="NUMBER_2">%3$d</xliff:g> sekunder."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Fjern"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Vil du øke volumet til over anbefalt nivå?\n\nHvis du hører på et høyt volum over lengre perioder, kan det skade hørselen din."</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">I én time</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Til <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Til <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (neste alarm)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Inntil du slår av funksjonen"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Inntil du slår av Ikke forstyrr"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-ne-rNP-watch/strings.xml b/core/res/res/values-ne-rNP-watch/strings.xml
index 1b4ffc9..759612d 100644
--- a/core/res/res/values-ne-rNP-watch/strings.xml
+++ b/core/res/res/values-ne-rNP-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_1">%2$d</xliff:g> को <xliff:g id="NUMBER_0">%1$d</xliff:g> अनुप्रयोग।"</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"सेन्सरहरू"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"आफ्नो सम्पर्कको पहुँच गर्नुहोस्"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"यो घडीको स्थान पहुँच गर्नुहोस्"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"आफ्नो पात्रोमा पहुँच गर्नुहोस्"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"SMS सन्देशहरू पठाउनुहोस् र हेर्नुहोस्"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"आफ्नो समयमा तस्बिर, मिडिया, र फाइलहरू हेर्नुहोस्"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"अडियो रेकर्ड गर्नुहोस्"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"तस्बिरहरू खिच्नुहोस् र भिडियो रेकर्ड गर्नुहोस्"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"फोन कलहरू गर्नुहोस् र व्यवस्थापन गर्नुहोस्"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"आफ्नो महत्त्वपूर्ण संकेत बारेको सेन्सर डेटा पहुँच गर्नुहोस्"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"वस्तुस्थिति पट्टी हुन"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"शरीरका सेन्सरहरू पहुँच गर्नुहोस् (जस्तै हृदय धड्कन निगरानी)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"सटीक स्थान पहुँच गर्नुहोस् (GPS र नेटवर्क आधारित)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"अनुमानित स्थान पहुँच गर्नुहोस् (नेटवर्क आधारित)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"यस SIM मा आदेशहरू पठाउनहोस्"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"पूर्ण नेटवर्क पहुँच प्राप्त"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"प्रोफाइल र यन्त्र मालिकहरूको व्यवस्थापन गर्नुहोस्"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"WiMAX अवस्था परिवर्तन गर्नुहोस्"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"Android Beam स्थानान्तरण अवस्था प्राप्त गर्नुहोस्"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"मिडिया परिणाम दिशानिर्देश गर्नुहोस्"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"स्थापना सत्रहरू पढ्नुहोस्"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"स्थापना प्याकेजहरू अनुरोध गर्नुहोस्"</string>
</resources>
diff --git a/core/res/res/values-ne-rNP/strings.xml b/core/res/res/values-ne-rNP/strings.xml
index ac52911..6fe9c4d 100644
--- a/core/res/res/values-ne-rNP/strings.xml
+++ b/core/res/res/values-ne-rNP/strings.xml
@@ -87,7 +87,7 @@
<string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"कलर ID पूर्वनिर्धारितदेखि प्रतिबन्धित छैन। अर्को कल: प्रतिबन्धित छ"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2567998633124408552">"कलर ID पूर्वनिर्धारितको लागि रोकावट छैन। अर्को कल: रोकावट छैन"</string>
<string name="serviceNotProvisioned" msgid="8614830180508686666">"सेवाको व्यवस्था छैन।"</string>
- <string name="CLIRPermanent" msgid="3377371145926835671">"तपाईं कलर ID सेटिङ परिवर्तन गर्न सक्नुहुन्न।"</string>
+ <string name="CLIRPermanent" msgid="3377371145926835671">"तपाईँ कलर ID सेटिङ परिवर्तन गर्न सक्नुहुन्न।"</string>
<string name="RestrictedChangedTitle" msgid="5592189398956187498">"प्रतिबन्धित पहुँच परिवर्तन भएको छ"</string>
<string name="RestrictedOnData" msgid="8653794784690065540">"डेटा सेवा रोकिएको छ।"</string>
<string name="RestrictedOnEmergency" msgid="6581163779072833665">"आपतकालीन सेवा रोकिएको छ।"</string>
@@ -198,7 +198,7 @@
<string name="shutdown_confirm" product="tv" msgid="476672373995075359">"तपाईंको TV बन्द हुनेछ।"</string>
<string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"तपाईँको घडी बन्द गरिने छ।"</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"तपाईँको फोन बन्द हुने छ।"</string>
- <string name="shutdown_confirm_question" msgid="2906544768881136183">"के तपाईं बन्द गर्न चाहनुहुन्छ?"</string>
+ <string name="shutdown_confirm_question" msgid="2906544768881136183">"के तपाईँ बन्द गर्न चाहनुहुन्छ?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"सुरक्षित मोडमा पुनःबुट गर्नुहोस्"</string>
<string name="reboot_safemode_confirm" msgid="55293944502784668">"सुरक्षित मोडमा तपाईँ पुनःबुट गर्न चाहनु हुन्छ? तपाईँले स्थापना गरेका सबै तेस्रो पक्षका अनुप्रयोगहरूलाई असक्षम गराउने छ।"</string>
<string name="recent_tasks_title" msgid="3691764623638127888">"नयाँ"</string>
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"व्यक्तिगत डेटा जस्तै क्रेडिट कार्ड नम्बरहरू र पासवर्डहरू समावेश गर्दछ।"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"स्थिति पट्टिलाई अक्षम वा संशोधित गर्नुहोस्"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"स्थिति पट्टि असक्षम पार्न वा प्रणाली आइकनहरू थप्न र हटाउन अनुप्रयोगलाई अनुमति दिन्छ।"</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"स्थिति पट्टि"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"वस्तुस्थिति पट्टी हुन दिनुहोस्"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"अनुप्रयोगलाई स्थिति पट्टि हुन अनुमति दिन्छ।"</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"स्थिति पट्टिलाई विस्तृत/सङ्कुचित गर्नुहोस्"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"अनुप्रयोगलाई स्थिति पट्टि विस्तार वा संकुचन गर्न अनुमति दिन्छ।"</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"WAP सन्देशहरू प्राप्त गर्न र प्रशोधन गर्न अनुप्रयोगलाई अनुमति दिन्छ। यो अनुमतिमा मोनिटर गर्ने वा तपाईँलाई पठाइएका सन्देशहरू तपाईँलाई नदेखाई मेट्ने क्षमता समावेश हुन्छ।"</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"चलिरहेका अनुप्रयोगहरू पुनःबहाली गर्नुहोस्"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"वर्तमानमा र भरखरै चलिरहेका कार्यहरू बारेको सूचना पुनःबहाली गर्न अनुप्रयोगलाई अनुमित दिन्छ। यसले उपकरणमा प्रयोग भएका अनुप्रयोगहरूको बारेमा सूचना पत्ता लगाउन अनुप्रयोगलाई अनुमति दिन सक्छ।"</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"प्रोफाइल र यन्त्र मालिकहरू व्यवस्थापन गर्नुहोस्"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"प्रोफाइल र यन्त्र मालिकहरूको व्यवस्थापन गराउनुहोस्"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"अनुप्रयोगहरूलाई प्रोफाइल र यन्त्र मालिकहरू सेट गर्न अनुमति दिनुहोस्।"</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"चलिरहेका अनुप्रयोगहरूलाई पुनःक्रम गराउनुहोस्"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"कामहरूलाई अग्रभाग र पृष्ठभूमिमा सार्न अनुप्रयोगलाई अनुमति दिन्छ। अनुप्रयोगले यो तपाईँको इनपुट बिना नै गर्न सक्छ।"</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"आगमन तथा बहर्गमन डेटासहित तपाईँको ट्याब्लेटको कल लगको परिमार्जन गर्न अनुप्रयोगलाई अनुमति दिन्छ। खराब अनुप्रयोगहरूले यसलाई तपाईँको कल लग परिमार्जन गर्न वा मेटाउन प्रयोग गर्न सक्छन्।"</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"अनुप्रयोगहरूलाई अनुमति दिन्छ तपाईँको TV को कल लग, आगमन र बहिर्गमन कलका डेटा लगायत, परिमार्जन गर्न। दुस्प्रभावी अनुप्रयोगहरूले यसलाई तपाईँको कल लग मेट्न वा परिमार्जन गर्न सक्छ।"</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"अनुप्रयोगलाई तपाईंको फोनको आउने र बाहिर जाने कलहरूको बारेको डेटा सहित कल लग परिमार्जन गर्न अनुमति दिन्छ। खराब अनुप्रयोगहरूले यसलाई तपाईंको कल लग मेटाउन वा परिमार्जन गर्न प्रयोग गर्न सक्दछ।"</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"शरीर सेन्सर (हृदयदर मोनिटर जस्तै)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"शरीरका सेन्सरहरूमा पहुँच गराउनुहोस् (जस्तै हृदय धड्कन निगरानीहरू)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"तपाईँको हृदय गति जस्तो सेंसर बाट डेटा पहुँचको लागि अनुप्रयोग अनुमति दिन्छ जसले तपाईँको भौतिक अवस्था अनुगमन गर्छ।"</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"गोप्य जानकारी र पात्रो घटनाहरू पढ्नुहोस्"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"अनुप्रयोगलाई तपाईंको ट्याब्लेटमा भण्डारण गरिएका ती साथीहरू वा सहयोगीहरू सहितको पात्राका कार्यक्रमहरू पढ्न अनुमति दिन्छ। यसले गोपनीयता वा संवेदनशीलता बिना पनि अनुप्रयोगलाई तपाईंको पात्राका डेटा साझेदारी गर्न वा बचत गर्न अनुमति दिन्छ।"</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"ती साथीहरू वा सहकर्मीहरूसहित तपाईँको फोनका घटनाहरू जसलाई थप्न, हटाउन र परिवर्तन गर्न अनुप्रयोगलाई अनुमति दिन्छ। पात्रो मालिकबाट देखा परेका वा मालिकको ज्ञान बिना परिवर्तन भएका घटनाहरू सन्देश पठाउन यसले अनुप्रयोगलाई अनुमति दिन सक्छ।"</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"अधिक स्थान प्रदायक आदेशहरू पहुँच गर्नुहोस्"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"अनुप्रयोगलाई अतिरिक्त स्थान प्रदायक आदेशहरू पहुँच गर्न अनुमति दिन्छ। यो अनुप्रयोगलाई GPS वा अन्य स्थान स्रोतहरूको संचालन साथै हस्तक्षेप गर्न अनुमति दिन सक्छ।"</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"सटिक स्थान (GPS र नेटवर्क आधारित)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"सटीक स्थान पहुँच गराउनुहोस् (GPS तथा नेटवर्कमा आधारित)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"अनुप्रयोगले विश्वव्यापी स्थान प्रणाली (GPS) वा सेल टावरहरू र वाइफाइ जस्ता नेटवर्क स्थान स्रोतहरूको प्रयोग गरेर तपाईँको सही स्थान प्राप्त गर्न अनुमति दिन्छ। यी स्थान सेवाहरू खोल्नु पर्छ र अनुप्रयोगहरूका लागि प्रयोग गर्न तपाईँको उपकरणमा उपलब्ध हुनु पर्छ। अनुप्रयोगहरूले तपाईँ कहाँ हुनु हुन्छ भन्ने निर्धारण गर्न यसलाई प्रयोग गर्न सक्छ र यसले अतिरिक्त ब्याट्रि उर्जा खतप गर्न सक्छ।"</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"अनुमानित स्थान (नेटवर्क-आधारित)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"अनुमानित स्थान पहुँच गराउनुहोस् (नेटवर्कमा आधारित)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"अनुप्रयोगलाई तपाईँको अनुमानित स्थान प्राप्त गर्न अनुमति दिन्छ। यो स्थान सेल टावर र वाइ-फाइजस्ता नेटवर्क स्थान स्रोतहरूको प्रोग गरी स्थान सेवाहरूबाट उत्पन्न गरिएको हो। अनुप्रयोगले यी स्थान सेवाहरूको उपयोग गर्नको लागि यी सेवाहरू तपाईँको उपकरणमा चालु र उपलब्ध हुनु आवश्यक छ। अनुप्रयोगहरूले अनुमानित रूपमा तपाईँ कहाँ हुनुहुन्छ भन्ने निर्धारण गर्न यसको प्रयोग गर्न सक्छन्।"</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"तपाईँका अडियो सेटिङहरू परिवर्तन गर्नुहोस्"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"अनुप्रयोगलाई ग्लोबल अडियो सेटिङ्हरू परिमार्जन गर्न अनुमति दिन्छ, जस्तै आवाजको मात्रा र आउटपुटको लागि कुन स्पिकर प्रयोग गर्ने।"</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"अडियो रेकर्ड गर्नुहोस्"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"अनुप्रयोगलाई माइक्रोफोनको साथ अडियो रेकर्ड गर्न अनुमति दिन्छ। यस अनुमतिले तपाईंको पुष्टिकरण बिना कुनै पनि समयमा अडियो रेकर्ड गर्न अनुमति दिन्छ।"</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"sim सञ्चार"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"SIM मा आदेशहरू पठाउन दिनुहोस्"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"SIM लाई आदेश पठाउन अनुप्रयोगलाई अनुमति दिन्छ। यो निकै खतरनाक हुन्छ।"</string>
<string name="permlab_camera" msgid="3616391919559751192">"तस्बिरहरू र भिडियोहरू लिनुहोस्।"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"अनुप्रयोगलाई क्यामेरासँग तस्बिर र भिडियोहरू लिन अनुमति दिन्छ। यस अनुमतिले अनुप्रयोगलाई तपाईंको पुष्टिकरण बिना कुनै पनि समयमा क्यामेरा प्रयोग गर्न स्वीकृति दिन्छ।"</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"फोनलाई थाहा भएका खाताहरूको सूची प्राप्त गर्न अनुप्रयोगलाई अनुमति दिन्छ। यसले तपाईँले स्थापना गर्नु भएका अनुप्रयोगहरूबाट सृजित कुनै खाताहरू समावेश हुन सक्छ।"</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"नेटवर्क जडानहरू हेर्नहोस्"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"अनुप्रयोगलाई नेटवर्क जडानहरू जस्तै कुन नेटवर्कहरू अवस्थित हुन्छन् र जडित छन् जसले हेर्नलाई अनुमति दिन्छ।"</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"पूर्ण नेटवर्क पहुँच"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"पूर्ण नेटवर्क पहुँच प्राप्त छ"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"नेटवर्क सकेटहरू सिर्जना गर्न र कस्टम नेटवर्क प्रोटोकल प्रयोग गर्न अनुप्रयोगलाई अनुमति दिन्छ। ब्राउजर र अन्य अनुप्रयोगहरूले इन्टरनेटमा डेटा पठाउने माध्यम प्रदान गर्छन्, त्यसैले इन्टरनेटमा डेटा पठाउन यो अनुमतिको आवश्यकता पर्दैन।"</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"नेटवर्क जडान परिवर्तन गर्नुहोस्"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"अनुप्रयोगलाई नेटवर्क जडानको स्थिति परिवर्तन गर्न अनुमति दिन्छ।"</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"अनुप्रयोगलाई स्थानीय ब्लुटुथ फोन कन्फिगर गर्न र टाढाका उपकरणहरूसँग खोज गर्न र जोडी गर्न अनुमति दिन्छ।"</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"WiMAXसँग जोड्नुहोस् वा छुटाउनुहोस्"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"अनुप्रयोगलाई वाइम्याक्स सक्षम छ कि छैन र जडान भएको कुनै पनि वाइम्याक्स नेटवर्कहरूको बारेमा जानकारी निर्धारिण गर्न अनुमति दिन्छ।"</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"वाइम्याक्स स्थिति परिवर्तन गर्नुहोस्"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"वाइम्याक्स अवस्था परिवर्तन गर्नुहोस्"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"अनुप्रयोगलाई वाइम्याक्स नेटवर्कहरूबाट ट्याब्लेट जडान गर्न र ट्याब्लेट विच्छेदन गर्न अनुमति दिन्छ।"</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"अनुप्रयोगलाई अनुमति दिन्छ TV लाई जडान गर्न र WiMAX सञ्जालबाट TV को जडान टुटाउन"</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"वाइम्याक्स नेटवर्कहरूसँग फोन जोड्न र छुटाउन अनुप्रयोगलाई अनुमति दिन्छ।"</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"अनुप्रयोगलाई टच स्क्रीनको प्यारामिटरहरू क्यालिब्रेसन परिमार्जन गर्न अनुमति दिन्छ। साधारण अनुप्रयोगहरूको लागि कहिल्यै आवश्यक पर्दैन।"</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"DRM प्रमाणपत्रको पहुँच"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"DRM प्रमाणपत्रहरू प्रावधान र प्रयोग गर्ने निवेदनको अनुमति दिन्छ। साधारण अनुप्रयोगहरूको लागि कहिल्यै पनि आवश्यक पर्दैन।"</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Android Beam स्थानान्तरण अवस्था प्राप्त गर्नुहोस्"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"Android Beam स्थानान्तरण अवस्था प्राप्त गर्नुहोस्"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"यस आवेदनले वर्तमान Android Beam स्थानान्तरण बारेमा जानकारी प्राप्त गर्न अनुमति दिन्छ"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"DRM प्रमाणपत्रहरू हटाउनुहोस्"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"DRM प्रमाणपत्रहरू हटाउन अनुप्रयोगलाई अनुमति दिन्छ। सामान्य अनुप्रयोगहरूको लागि कहिल्यै आवश्यकता पर्दैन।"</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"गलत PIN कोड।"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"अनलक गर्न मेनु थिच्नुहोस् र त्यसपछि ० थिच्नुहोस्।"</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"आपतकालीन नम्बर"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"सेवा छैन।"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"कुनै सेवा छैन"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"स्क्रिन लक गरिएको।"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"अनलक वा आपतकालीन कल गर्न मेनु थिच्नुहोस्।"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"अनलक गर्न मेनु थिच्नुहोस्।"</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"प्रयोगकर्ता निर्देशक वा ग्राहक सेवा सम्पर्क हर्नुहोस्।"</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM कार्ड लक गरिएको छ।"</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"SIM कार्ड अनलक गरिँदै..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"तपाईँले तपाईँको अनलक ढाँचा गलत तरिकाले <xliff:g id="NUMBER_0">%d</xliff:g> पटक खिच्नु भएको छ। \n\n <xliff:g id="NUMBER_1">%d</xliff:g> सेकेन्डमा फेरि कोसिस गर्नुहोस्।"</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"तपाईंले गलत तरिकाले आफ्नो पासवर्ड <xliff:g id="NUMBER_0">%d</xliff:g> पटक टाइप गर्नुभयो। \n\n<xliff:g id="NUMBER_1">%d</xliff:g> सेकेन्डमा फेरि प्रयास गर्नुहोस्।"</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"तपाईँले गलत तरिकाले तपाईँको PIN <xliff:g id="NUMBER_0">%d</xliff:g> पटक टाइप गर्नु भएको छ। \n\n<xliff:g id="NUMBER_1">%d</xliff:g> सेकेन्डमा फेरि प्रयास गर्नुहोस्।"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"तपाईँले तपाईँको अनलक ढाँचा गलत तरिकाले <xliff:g id="NUMBER_0">%d</xliff:g> पटक खिच्नु भएको छ। पछि <xliff:g id="NUMBER_1">%d</xliff:g> थप असफल कोसिसहरू, तपाईँको Google साइन इन प्रयोग गरी तपाईँको ट्याब्लेट अनलक गर्न भनिने छ।\n\n <xliff:g id="NUMBER_2">%d</xliff:g> सेकेन्डमा फरि प्रयास गर्नुहोस्।"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"तपाईंले गलत तरिकाले आफ्नो अनलक ढाँचा झिक्नु भएकोछ <xliff:g id="NUMBER_0">%d</xliff:g> पटक। <xliff:g id="NUMBER_1">%d</xliff:g> थप असफल प्रयासहरू पछि तपाईँलाई आफ्नो TV तपाईँको Google साइन इन प्रयोग गरी अनलक गर्न आग्रह गरिनेछ। \n\n फेरि प्रयास गर्नुहोस् <xliff:g id="NUMBER_2">%d</xliff:g> सेकेन्डमा।"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"तपाईँले <xliff:g id="NUMBER_0">%d</xliff:g> पटक गलत तरिकाले तपाईँको अनलक ढाँचालाई कोर्नु भएको छ। पछि <xliff:g id="NUMBER_1">%d</xliff:g> अरू धेरै असफल कोसिसहरूपछि, तपाईँलाई तपाईँको फोन Google साइन इन प्रयोग गरेर अनलक गर्नको लागि सोधिने छ। \n\n <xliff:g id="NUMBER_2">%d</xliff:g> सेकेन्डमा पुनः प्रयास गर्नुहोस्।"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"तपाईँले <xliff:g id="NUMBER_0">%d</xliff:g> पटक ट्याब्लेटलाई अनलक गर्नको लागि गलत तरिकाले कोशिस गर्नुभएको छ। <xliff:g id="NUMBER_1">%d</xliff:g> अरू धेरै असफल कोसिसहरूपछि, ट्याब्लेट फ्याट्रि पूर्वनिर्धारितमा पुनःसेट हुने छ र सबै प्रयोगकर्ता डेटा हराउने छन्।"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"तपाईंले गलत तरिकाले TV अनलक गर्ने प्रयास गर्नुभएको छ <xliff:g id="NUMBER_0">%d</xliff:g> पटक। <xliff:g id="NUMBER_1">%d</xliff:g> थप असफल प्रयासहरू पछि TV कारखानाको पूर्वनिर्धारित सेटिङमा रिसेट हुनेछ र सबै प्रयोगकर्ता डेटा नष्ट हुनेछन्।"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"तपाईंले गलत तरिकाले <xliff:g id="NUMBER_0">%d</xliff:g> पटक फोन अनलक गर्ने प्रयत्न गर्नुभयो। <xliff:g id="NUMBER_1">%d</xliff:g> बढी असफल प्रयत्नहरू पछि, फोन फ्याक्ट्रि पूर्वनिर्धारितमा पुनःसेट हुने छ र सबै प्रयोगकर्ता डेटा हराउने छन्।"</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"तपाईँले तपाईँको अनलक ढाँचा गलत तरिकाले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक खिच्नु भएको छ। \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकेन्डमा फेरि कोसिस गर्नुहोस्।"</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"तपाईंले गलत तरिकाले आफ्नो पासवर्ड <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक टाइप गर्नुभयो। \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> सेकेन्डमा फेरि प्रयास गर्नुहोस्।"</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"तपाईँले गलत तरिकाले तपाईँको PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक टाइप गर्नु भएको छ। \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> सेकेन्डमा फेरि प्रयास गर्नुहोस्।"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"तपाईँले तपाईँको अनलक ढाँचा गलत तरिकाले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक खिच्नु भएको छ। पछि <xliff:g id="NUMBER_1">%2$d</xliff:g> थप असफल कोसिसहरू, तपाईँको Google साइन इन प्रयोग गरी तपाईँको ट्याब्लेट अनलक गर्न भनिने छ।\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकेन्डमा फरि प्रयास गर्नुहोस्।"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"तपाईंले गलत तरिकाले आफ्नो अनलक ढाँचा झिक्नु भएकोछ <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक। <xliff:g id="NUMBER_1">%2$d</xliff:g> थप असफल प्रयासहरू पछि तपाईँलाई आफ्नो TV तपाईँको Google साइन इन प्रयोग गरी अनलक गर्न आग्रह गरिनेछ। \n\n फेरि प्रयास गर्नुहोस् <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकेन्डमा।"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"तपाईँले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत तरिकाले तपाईँको अनलक ढाँचालाई कोर्नु भएको छ। पछि <xliff:g id="NUMBER_1">%2$d</xliff:g> अरू धेरै असफल कोसिसहरूपछि, तपाईँलाई तपाईँको फोन Google साइन इन प्रयोग गरेर अनलक गर्नको लागि सोधिने छ। \n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकेन्डमा पुनः प्रयास गर्नुहोस्।"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"तपाईँले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक ट्याब्लेटलाई अनलक गर्नको लागि गलत तरिकाले कोशिस गर्नुभएको छ। <xliff:g id="NUMBER_1">%2$d</xliff:g> अरू धेरै असफल कोसिसहरूपछि, ट्याब्लेट फ्याट्रि पूर्वनिर्धारितमा पुनःसेट हुने छ र सबै प्रयोगकर्ता डेटा हराउने छन्।"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"तपाईंले गलत तरिकाले TV अनलक गर्ने प्रयास गर्नुभएको छ <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक। <xliff:g id="NUMBER_1">%2$d</xliff:g> थप असफल प्रयासहरू पछि TV कारखानाको पूर्वनिर्धारित सेटिङमा रिसेट हुनेछ र सबै प्रयोगकर्ता डेटा नष्ट हुनेछन्।"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"तपाईंले गलत तरिकाले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक फोन अनलक गर्ने प्रयत्न गर्नुभयो। <xliff:g id="NUMBER_1">%2$d</xliff:g> बढी असफल प्रयत्नहरू पछि, फोन फ्याक्ट्रि पूर्वनिर्धारितमा पुनःसेट हुने छ र सबै प्रयोगकर्ता डेटा हराउने छन्।"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"तपाईँले ट्यब्लेटलाई अनलक गर्न गलत तरिकाले <xliff:g id="NUMBER">%d</xliff:g> पटक प्रयास गर्नु भएको छ। अब ट्याब्लेटलाई पूर्वनिर्धारित कार्यशालामा पुनःसेट गरिने छ।"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"तपाईंले गलत तरिकाले TV अनलक गर्ने प्रयास गर्नु भएको छ <xliff:g id="NUMBER">%d</xliff:g> पटक। TV अब कारखानाको पूर्वनिर्धारित सेटिङमा रिसेट गरिनेछ।"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"तपाईंले गलत तरिकाले फोन <xliff:g id="NUMBER">%d</xliff:g> पटक अनलक गर्ने प्रयत्न गर्नुभयो। अब फोन फ्याक्ट्रि पूर्वनिर्धारितमा पुनःसेट हुने छ।"</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"शब्द"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"लिङ्क"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"लाइन"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"कार्यशाला परीक्षण असफल भयो।"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"FACTORY_TEST कार्रवाइले /system/app मा स्थापित प्याकेजहरूको लागि मात्र समर्थन गर्छ।"</string>
<string name="factorytest_no_action" msgid="872991874799998561">"कुनै प्याकेज फेला पार्न सकिएन जसले FACTORY_TEST कार्य प्रदान गर्दछ।"</string>
@@ -786,7 +784,7 @@
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"तपाईँको भ्वाइसमेल इनबक्समा सन्देश थप्नको लागि अनुप्रयोगलाई अनुमति दिन्छ।"</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"भूस्थान अनुमतिहरू ब्राउजर परिवर्तन गर्नुहोस्"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"ब्राउजरको भू-स्थान अनुमतिहरू परिमार्जन गर्न अनुप्रयोगलाई अनुमति दिन्छ। खराब अनुप्रयोगहरूले स्थान सूचना मनपरी वेब साइटहरूमा पठाउने अनुमतिको लागि यसलाई प्रयोग गर्न सक्छन्।"</string>
- <string name="save_password_message" msgid="767344687139195790">"के तपाईं ब्राउजरले यो पासवर्ड सम्झेको चाहनुहुन्छ?"</string>
+ <string name="save_password_message" msgid="767344687139195790">"के तपाईँ ब्राउजरले यो पासवर्ड सम्झेको चाहनुहुन्छ?"</string>
<string name="save_password_notnow" msgid="6389675316706699758">"अहिले होइन"</string>
<string name="save_password_remember" msgid="6491879678996749466">"सम्झनुहोस्"</string>
<string name="save_password_never" msgid="8274330296785855105">"कहिल्यै पनि होइन"</string>
@@ -907,13 +905,13 @@
<string name="aerr_process_silence" msgid="4226685530196000222">"साइलेन्स पुनःबुट नभएसम्म <xliff:g id="PROCESS">%1$s</xliff:g> बाट क्र्यास हुन्छ।"</string>
<string name="anr_title" msgid="4351948481459135709"></string>
<string name="anr_activity_application" msgid="1904477189057199066">"<xliff:g id="APPLICATION">%2$s</xliff:g>ले कार्य गरिरहेको छैन।\n\nके तपाईँ यसलाई बन्द गर्न चाहनु हुन्छ?"</string>
- <string name="anr_activity_process" msgid="5776209883299089767">"गतिविधि <xliff:g id="ACTIVITY">%1$s</xliff:g> ले प्रतिक्रिया देखाइरहेको छैन।\n\nके तपाईं यसलाई बन्द गर्न चाहनु हुन्छ?"</string>
+ <string name="anr_activity_process" msgid="5776209883299089767">"गतिविधि <xliff:g id="ACTIVITY">%1$s</xliff:g> ले प्रतिक्रिया देखाइरहेको छैन।\n\nके तपाईँ यसलाई बन्द गर्न चाहनु हुन्छ?"</string>
<string name="anr_application_process" msgid="8941757607340481057">"<xliff:g id="APPLICATION">%1$s</xliff:g> जवाफ दिइरहेको छैन। के तपाईँ यसलाई बन्द गर्न चाहनु हुन्छ?"</string>
<string name="anr_process" msgid="6513209874880517125">"प्रक्रिया <xliff:g id="PROCESS">%1$s</xliff:g>ले कार्य गरिरहेको छैन।\n\nके तपाईँ यसलाई बन्द गर्न चाहनु हुन्छ?"</string>
<string name="force_close" msgid="8346072094521265605">"ठिक छ"</string>
<string name="report" msgid="4060218260984795706">"रिपोर्ट गर्नुहोस्"</string>
<string name="wait" msgid="7147118217226317732">"प्रतीक्षा गर्नुहोस्"</string>
- <string name="webpage_unresponsive" msgid="3272758351138122503">"पृष्ठ गैर जिम्मेवारी भएको छ।\n\nके तपाईं यसलाई बन्द गर्न चाहनुहुन्छ?"</string>
+ <string name="webpage_unresponsive" msgid="3272758351138122503">"पृष्ठ गैर जिम्मेवारी भएको छ।\n\nके तपाईँ यसलाई बन्द गर्न चाहनुहुन्छ?"</string>
<string name="launch_warning_title" msgid="1547997780506713581">"अनुप्रयोग पुनः निर्देशीत"</string>
<string name="launch_warning_replace" msgid="6202498949970281412">"<xliff:g id="APP_NAME">%1$s</xliff:g> अहिले चलिरहेको छ।"</string>
<string name="launch_warning_original" msgid="188102023021668683">"<xliff:g id="APP_NAME">%1$s</xliff:g> वास्तविक सुरुवात भएको थियो।"</string>
@@ -1079,11 +1077,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"फरम्याट गर्दै…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"सम्मिलित छैन"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"कुनै मिल्ने गतिविधि पाइएन।"</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"मिडिया परिणाम दिशानिर्देश गर्नुहोस्"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"मिडिया निकास दिशानिर्देश गराउनुहोस्"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"मिडिया परिणामलाई अन्य बाहिरी उपकरणहरूसँग लैजानको लागि अनुप्रयोगलाई अनुमति दिन्छ।"</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"स्थापना सत्रहरू पढ्नुहोस्"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"स्थापना सत्रहरू पढ्नु दिनुहोस्"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"स्थापित सत्र पढ्न अनुप्रयोगलाई अनुमति दिनुहोस्। यसले सक्रिय प्याकेज प्रतिष्ठानहरू बारेमा विवरण हेर्ने अनुमति दिन्छ।"</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"स्थापना प्याकेजहरू अनुरोध गर्नुहोस्"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"स्थापना प्याकेजहरू अनुरोध गर्नुहोस्"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"प्याकेजहरूको स्थापना अनुरोध गर्न अनुप्रयोगलाई अनुमति दिन्छ।"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"जुम नियन्त्रणको लागि दुई चोटि टच गर्नुहोस्"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"विजेट थप गर्न सकिँदैन।"</string>
@@ -1103,7 +1101,7 @@
<string name="deny" msgid="2081879885755434506">"अस्वीकार गर्नुहोस्"</string>
<string name="permission_request_notification_title" msgid="6486759795926237907">"अनुरोध गरिएको अनुमति"</string>
<string name="permission_request_notification_with_subtitle" msgid="8530393139639560189">\n"खाता <xliff:g id="ACCOUNT">%s</xliff:g>को लागि अनुरोध गरिएको अनुमति।"</string>
- <string name="forward_intent_to_owner" msgid="1207197447013960896">"तपाईं तपाईँको कार्य प्रोफाइल बाहिर यो अनुप्रयोग प्रयोग गरिरहनु भएको छ"</string>
+ <string name="forward_intent_to_owner" msgid="1207197447013960896">"तपाईँ तपाईँको कार्य प्रोफाइल बाहिर यो अनुप्रयोग प्रयोग गरिरहनु भएको छ"</string>
<string name="forward_intent_to_work" msgid="621480743856004612">"तपाईँ आफ्नो कार्य प्रोफाइलमा यो अनुप्रयोग प्रयोग गरिरहनु भएको छ"</string>
<string name="input_method_binding_label" msgid="1283557179944992649">"इनपुट विधि"</string>
<string name="sync_binding_label" msgid="3687969138375092423">"सिङ्क गर्नुहोस्"</string>
@@ -1151,7 +1149,7 @@
<string name="gpsVerifYes" msgid="2346566072867213563">"हो"</string>
<string name="gpsVerifNo" msgid="1146564937346454865">"होइन"</string>
<string name="sync_too_many_deletes" msgid="5296321850662746890">"सीमा नाघेकाहरू मेट्नुहोस्"</string>
- <string name="sync_too_many_deletes_desc" msgid="496551671008694245">"त्यहाँ <xliff:g id="NUMBER_OF_DELETED_ITEMS">%1$d</xliff:g> मेटाइएका आइटमहरू छन् <xliff:g id="TYPE_OF_SYNC">%2$s</xliff:g>को लागि, खाता <xliff:g id="ACCOUNT_NAME">%3$s</xliff:g>। तपाईं के गर्न चाहनु हुन्छ?"</string>
+ <string name="sync_too_many_deletes_desc" msgid="496551671008694245">"त्यहाँ <xliff:g id="NUMBER_OF_DELETED_ITEMS">%1$d</xliff:g> मेटाइएका आइटमहरू छन् <xliff:g id="TYPE_OF_SYNC">%2$s</xliff:g>को लागि, खाता <xliff:g id="ACCOUNT_NAME">%3$s</xliff:g>। तपाईँ के गर्न चाहनु हुन्छ?"</string>
<string name="sync_really_delete" msgid="2572600103122596243">"वस्तुहरू मेट्नुहोस्"</string>
<string name="sync_undo_deletes" msgid="2941317360600338602">"मेटिएकाहरू पूर्ववत बनाउनुहोस्।"</string>
<string name="sync_do_nothing" msgid="3743764740430821845">"अहिलेको लागि केही नगर्नुहोस्"</string>
@@ -1291,18 +1289,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"अमान्य प्रयोगकर्तानाम वा पासवर्ड।"</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"के तपाईँले उपयोगकर्ता नाम वा पासवर्ड बिर्सनुभयो?\n"<b>"google.com/accounts/recovery"</b>" मा जानुहोस्।"</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"खाता जाँच हुँदै…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"तपाईँले गलत तरिकाले तपाईँको PIN <xliff:g id="NUMBER_0">%d</xliff:g> पटक टाइप गर्नु भएको छ। \n\n<xliff:g id="NUMBER_1">%d</xliff:g> सेकेन्डमा फेरि प्रयास गर्नुहोस्।"</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"तपाईँले तपाईँक पासवर्ड <xliff:g id="NUMBER_0">%d</xliff:g> पटक गलत टाइप गर्नुभएको छ। \n\n <xliff:g id="NUMBER_1">%d</xliff:g> सेकेन्डमा फेरि प्रयास गर्नुहोस्।"</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"तपाईँले तपाईँको अनलक ढाँचा गलत तरिकाले <xliff:g id="NUMBER_0">%d</xliff:g> पटक खिच्नु भएको छ। \n\n <xliff:g id="NUMBER_1">%d</xliff:g> सेकेन्डमा फेरि कोसिस गर्नुहोस्।"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"तपाईँले ट्याब्लेटलाई अनलक गर्न गलत तरिकाले <xliff:g id="NUMBER_0">%d</xliff:g> पटक कोसिस गर्नु भएको छ। <xliff:g id="NUMBER_1">%d</xliff:g> पछि थप असफल प्रयासहरू, ट्याब्लेट पूर्वनिर्धारित कार्यशालामा पुनःसेट गरिने छ र सबै प्रयोग डेटा हराउने छ।"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"तपाईंले गलत तरिकाले TV अनलक गर्ने प्रयास गर्नु भएको छ <xliff:g id="NUMBER_0">%d</xliff:g> पटक। <xliff:g id="NUMBER_1">%d</xliff:g> थप असफल प्रयासहरू पछि, TV कारखानाको पूर्वनिर्धारित सेटिङमा रिसेट हुनेछ र सबै प्रयोगकर्ता डेटा नष्ट हुनेछन्।"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"तपाईँले गलतसँग फोनलाई अनलक गर्न <xliff:g id="NUMBER_0">%d</xliff:g> पटक कोसिस गर्नु भयो। <xliff:g id="NUMBER_1">%d</xliff:g> पछि थप असफल कोसिसहरू, फोनलाई पूर्वनिर्धारित कार्यशालामा पुनःसेट गरिने छ र सबै प्रयोग डेटा हराउने छ।"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"तपाईँले गलत तरिकाले तपाईँको PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक टाइप गर्नु भएको छ। \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> सेकेन्डमा फेरि प्रयास गर्नुहोस्।"</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"तपाईँले तपाईँक पासवर्ड <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत टाइप गर्नुभएको छ। \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकेन्डमा फेरि प्रयास गर्नुहोस्।"</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"तपाईँले तपाईँको अनलक ढाँचा गलत तरिकाले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक खिच्नु भएको छ। \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकेन्डमा फेरि कोसिस गर्नुहोस्।"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"तपाईँले ट्याब्लेटलाई अनलक गर्न गलत तरिकाले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक कोसिस गर्नु भएको छ। <xliff:g id="NUMBER_1">%2$d</xliff:g> पछि थप असफल प्रयासहरू, ट्याब्लेट पूर्वनिर्धारित कार्यशालामा पुनःसेट गरिने छ र सबै प्रयोग डेटा हराउने छ।"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"तपाईंले गलत तरिकाले TV अनलक गर्ने प्रयास गर्नु भएको छ <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक। <xliff:g id="NUMBER_1">%2$d</xliff:g> थप असफल प्रयासहरू पछि, TV कारखानाको पूर्वनिर्धारित सेटिङमा रिसेट हुनेछ र सबै प्रयोगकर्ता डेटा नष्ट हुनेछन्।"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"तपाईँले गलतसँग फोनलाई अनलक गर्न <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक कोसिस गर्नु भयो। <xliff:g id="NUMBER_1">%2$d</xliff:g> पछि थप असफल कोसिसहरू, फोनलाई पूर्वनिर्धारित कार्यशालामा पुनःसेट गरिने छ र सबै प्रयोग डेटा हराउने छ।"</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"तपाईँले ट्यब्लेटलाई अनलक गर्न गलत तरिकाले <xliff:g id="NUMBER">%d</xliff:g> पटक प्रयास गर्नु भएको छ। अब ट्याब्लेटलाई पूर्वनिर्धारित कार्यशालामा पुनःसेट गरिने छ।"</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"तपाईंले गलत तरिकाले TV अनलक गर्ने प्रयास गर्नु भएको छ <xliff:g id="NUMBER">%d</xliff:g> पटक। TV अब कारखानाको पूर्वनिर्धारित सेटिङमा रिसेट हुनेछ।"</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"तपाईंले गलत तरिकाले फोन <xliff:g id="NUMBER">%d</xliff:g> पटक अनलक गर्ने प्रयत्न गर्नुभयो। अब फोन फ्याक्ट्रि पूर्वनिर्धारितमा पुनःसेट हुने छ।"</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"तपाईंले गलत तरिकाले आफ्नो अनलक ढाँचा <xliff:g id="NUMBER_0">%d</xliff:g> पटक कोर्नुभयो। <xliff:g id="NUMBER_1">%d</xliff:g> विफल प्रयत्नहरू पछि, तपाईंलाई आफ्नो ट्याब्लेट इमेल खाता प्रयोग गरेर अनलक गर्न सोधिने छ।\n\n फेरि प्रयास गर्नुहोस् <xliff:g id="NUMBER_2">%d</xliff:g> सेकेन्डहरूमा।"</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"तपाईंले गलत तरिकाले आफ्नो अनलक ढाँचालाई झिक्नु भएको छ <xliff:g id="NUMBER_0">%d</xliff:g> पटक। <xliff:g id="NUMBER_1">%d</xliff:g> थप असफल प्रयासहरू पछि तपाईँलाई इमेल खाता खोली तपाईँको TV अनलक गर्नका लागि आग्रह गरिनेछ।\n\n फेरि प्रयास गर्नुहोस् <xliff:g id="NUMBER_2">%d</xliff:g> सेकेन्डमा।"</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"तपाईँले आफ्नो अनलक ढाँचा गलत रूपमा <xliff:g id="NUMBER_0">%d</xliff:g> पटक तान्नु भएको छ। <xliff:g id="NUMBER_1">%d</xliff:g> धेरै असफल प्रयासहरूपछि, तपाईँलाई एउटा इमेल खाताको प्रयोग गरेर तपाईँको फोन अनलक गर्न सोधिने छ।\n\n फेरि <xliff:g id="NUMBER_2">%d</xliff:g> सेकेन्डमा प्रयास गर्नुहोस्।"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"तपाईंले गलत तरिकाले आफ्नो अनलक ढाँचा <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक कोर्नुभयो। <xliff:g id="NUMBER_1">%2$d</xliff:g> विफल प्रयत्नहरू पछि, तपाईंलाई आफ्नो ट्याब्लेट इमेल खाता प्रयोग गरेर अनलक गर्न सोधिने छ।\n\n फेरि प्रयास गर्नुहोस् <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकेन्डहरूमा।"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"तपाईंले गलत तरिकाले आफ्नो अनलक ढाँचालाई झिक्नु भएको छ <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक। <xliff:g id="NUMBER_1">%2$d</xliff:g> थप असफल प्रयासहरू पछि तपाईँलाई इमेल खाता खोली तपाईँको TV अनलक गर्नका लागि आग्रह गरिनेछ।\n\n फेरि प्रयास गर्नुहोस् <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकेन्डमा।"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"तपाईँले आफ्नो अनलक ढाँचा गलत रूपमा <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक तान्नु भएको छ। <xliff:g id="NUMBER_1">%2$d</xliff:g> धेरै असफल प्रयासहरूपछि, तपाईँलाई एउटा इमेल खाताको प्रयोग गरेर तपाईँको फोन अनलक गर्न सोधिने छ।\n\n फेरि <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकेन्डमा प्रयास गर्नुहोस्।"</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"हटाउनुहोस्"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"सिफारिस तहभन्दा आवाज ठुलो गर्नुहुन्छ?\n\nलामो समय सम्म उच्च आवाजमा सुन्दा तपाईँको सुन्ने शक्तिलाई हानी गर्न सक्छ।"</string>
@@ -1476,6 +1474,7 @@
<item quantity="one">१ घन्टाको लागि</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> सम्म"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (अर्को अलार्म) सम्म"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"तपाईँले यसलाई बन्द नगरेसम्म"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"तपाईँले बन्द नगरे सम्म बाधा नपुर्याउँनुहोस्"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-nl-watch/strings.xml b/core/res/res/values-nl-watch/strings.xml
index 967cf90..95baf7b 100644
--- a/core/res/res/values-nl-watch/strings.xml
+++ b/core/res/res/values-nl-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"App <xliff:g id="NUMBER_0">%1$d</xliff:g> van <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Sensoren"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"toegang tot je contacten"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"toegang tot de locatie van dit horloge"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"toegang tot je agenda"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"sms\'jes verzenden en bekijken"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"toegang tot foto\'s, media en bestanden op je horloge"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"audio opnemen"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"foto\'s maken en video opnemen"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"bellen en telefoontjes beheren"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"toegang tot sensorgegevens over je vitale functies"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"de statusbalk zijn"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"toegang tot lichaamssensoren (zoals hartslagmeters)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"toegang tot precieze locatie (GPS- en netwerkgebaseerd)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"toegang tot geschatte locatie (netwerkgebaseerd)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"opdrachten verzenden naar de simkaart"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"volledige netwerktoegang"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"profiel- en apparaateigenaren beheren"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"WiMAX-status wijzigen"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"Android Beam-overdrachtsstatus ontvangen"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"media-uitvoer aansturen"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"installatiesessies lezen"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"installatiepakketten aanvragen"</string>
</resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index a850943..e4f388f 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Omvat persoonlijke gegevens zoals creditcardnummers en wachtwoorden."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"statusbalk uitschakelen of wijzigen"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Hiermee kan de app de statusbalk uitschakelen of systeempictogrammen toevoegen en verwijderen."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"statusbalk"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"de statusbalk zijn"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Hiermee kan de app de statusbalk zijn."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"statusbalk uitvouwen/samenvouwen"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Hiermee kan de app de statusbalk uitvouwen of samenvouwen."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Hiermee kan de app WAP-berichten ontvangen en verwerken. Dit betekent dat de app berichten die naar je apparaat zijn verzonden, kan bijhouden of verwijderen zonder deze aan u weer te geven."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"actieve apps ophalen"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Hiermee kan de app informatie ophalen over actieve en recent uitgevoerde taken. Zo kan de app informatie vinden over welke apps op het apparaat worden gebruikt."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Profiel- en apparaateigenaren beheren"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"profiel- en apparaateigenaren beheren"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Apps toestaan de profieleigenaren en apparaateigenaar in te stellen."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"actieve apps opnieuw rangschikken"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Hiermee kan de app taken naar de voor- en achtergrond verplaatsen. De app kan dit doen zonder om je bevestiging te vragen."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Toestaan dat de app het gesprekkenlijst van je tablet aanpast, waaronder gegevens over inkomende en uitgaande oproepen. Schadelijke apps kunnen hiermee je gesprekkenlijst wissen of aanpassen."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Toestaan dat de app het gesprekkenlijst van je tv aanpast, waaronder gegevens over inkomende en uitgaande oproepen. Schadelijke apps kunnen hiermee je gesprekkenlijst wissen of aanpassen."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Toestaan dat de app het gesprekkenlijst van je telefoon aanpast, waaronder gegevens over inkomende en uitgaande oproepen. Schadelijke apps kunnen hiermee je gesprekkenlijst wissen of aanpassen."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"lichaamssensoren (zoals hartslagmeters)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"toegang tot lichaamssensoren (zoals hartslagmeters)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Hiermee kan de app toegang krijgen tot gegevens van sensoren die je lichamelijke conditie controleren, zoals je hartslag."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"agenda-afspraken en vertrouwelijke informatie lezen"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Hiermee kan de app alle agenda-afspraken lezen die zijn opgeslagen op je tablet, inclusief die van vrienden of collega\'s. De app kan je agenda delen of je agendagegevens opslaan, ongeacht vertrouwelijkheid of gevoeligheid."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Hiermee kan de app afspraken toevoegen, verwijderen en wijzigen die u kunt bewerken op je telefoon, inclusief afspraken van vrienden of collega\'s. Zo kan de app berichten verzenden die afkomstig lijken te zijn van agenda-eigenaren, of afspraken aanpassen zonder medeweten van de eigenaar."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"toegang tot extra opdrachten van locatieaanbieder"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Hiermee kan de app toegang krijgen tot extra opdrachten voor de locatieprovider. De app kan hiermee de werking van GPS of andere locatiebronnen te verstoren."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"precieze locatie (GPS- en netwerkgebaseerd)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"toegang tot precieze locatie (GPS- en netwerkgebaseerd)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Hiermee kan de app je precieze locatie bepalen via GPS (Global Positioning System) of netwerklocatiebronnen zoals zendmasten en wifi. Deze locatieservices moeten zijn ingeschakeld en beschikbaar zijn op je apparaat voordat de app ze kan gebruiken. Apps kunnen dit gebruiken om te bepalen waar u bent en verbruiken mogelijk extra acculading."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"geschatte locatie (netwerkgebaseerd)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"toegang tot geschatte locatie (netwerkgebaseerd)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Hiermee kan de app beschikken over je geschatte locatie. Deze locatie wordt afgeleid van locatieservices die netwerklocatiebronnen zoals zendmasten en wifi gebruiken. Deze locatieservices moeten zijn ingeschakeld en beschikbaar zijn op je apparaat voordat de app ze kan gebruiken. Apps kunnen dit gebruiken om ongeveer te bepalen waar u zich bevindt."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"je audio-instellingen wijzigen"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Hiermee kan de app algemene audio-instellingen wijzigen zoals het volume en welke luidspreker wordt gebruikt voor de uitvoer."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"audio opnemen"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Hiermee kan de app audio opnemen met de microfoon. Met deze toestemming kan de app op elk moment audio opnemen, zonder om je bevestiging te vragen."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"sim-communicatie"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"opdrachten verzenden naar de simkaart"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Hiermee kan de app opdrachten verzenden naar de simkaart. Dit is erg gevaarlijk."</string>
<string name="permlab_camera" msgid="3616391919559751192">"foto\'s en video\'s maken"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Hiermee kan de app foto\'s en video\'s maken met de camera. Met deze toestemming kan de app de camera altijd gebruiken, zonder je bevestiging."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Hiermee krijgt de app toegang tot de lijst met accounts die op de telefoon bekend zijn. Dit kunnen ook accounts zijn die zijn gemaakt door apps die je hebt geïnstalleerd."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"netwerkverbindingen weergeven"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Hiermee kan de app informatie bekijken over netwerkverbindingen, zoals welke netwerken er zijn en welke verbonden zijn."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"volledige netwerktoegang"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"volledige netwerktoegang"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Hiermee kan de app netwerksockets maken en aangepaste netwerkprotocollen gebruiken. De browser en andere apps bieden mogelijkheden om gegevens via internet te verzenden, dus deze toestemming is niet vereist om gegevens via internet te verzenden."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"netwerkverbinding wijzigen"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Hiermee kan de app de status van de netwerkverbinding wijzigen."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Hiermee kan de app de lokale Bluetooth-telefoon configureren en externe apparaten zoeken en koppelen."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"WiMAX-verbinding maken en verbreken"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Hiermee kan de app bepalen of WiMAX is ingeschakeld en informatie bekijken over alle WiMAX-netwerken waarmee verbinding is gemaakt."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX-status wijzigen"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"WiMAX-status wijzigen"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Hiermee kan de app de tablet verbinden met WiMAX-netwerken en de verbinding daarmee verbreken."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Hiermee kan de app een verbinding maken tussen de tv en WiMAX-netwerken en deze verbinding verbreken."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Hiermee kan de app de telefoon verbinden met WiMAX-netwerken en de verbinding daarmee verbreken."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Hiermee kan de app de kalibratieparameters van het aanraakscherm aanpassen. Nooit vereist voor normale apps."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"toegang tot DRM-certificaten"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Toestaan dat een app DRM-certificaten registreert en gebruikt. Nooit vereist voor normale apps."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Android Beam-overdrachtsstatus ontvangen"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"Android Beam-overdrachtsstatus ontvangen"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Hiermee kan deze app informatie over huidige Android Beam-overdrachten ontvangen"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"DRM-certificaten verwijderen"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Toestaan dat een app DRM-certificaten verwijdert. Nooit vereist voor normale apps."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Onjuiste pincode."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Druk op \'Menu\' en vervolgens op 0 om te ontgrendelen."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Alarmnummer"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Geen service"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Geen service"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Scherm vergrendeld."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Druk op \'Menu\' om te ontgrendelen of noodoproep te plaatsen."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Druk op \'Menu\' om te ontgrendelen."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Raadpleeg de gebruikershandleiding of neem contact op met de klantenservice."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM-kaart is vergrendeld."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"SIM-kaart ontgrendelen..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Je hebt je ontgrendelingspatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getekend. \n\nProbeer het opnieuw over <xliff:g id="NUMBER_1">%d</xliff:g> seconden."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Je hebt je wachtwoord <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getypt. \n\nProbeer het opnieuw over <xliff:g id="NUMBER_1">%d</xliff:g> seconden."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Je hebt je pincode <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getypt. \n\nProbeer het opnieuw over <xliff:g id="NUMBER_1">%d</xliff:g> seconden."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Je hebt je ontgrendelingspatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getekend. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen wordt u gevraagd je tablet te ontgrendelen met je aanmeldingsgegevens voor Google.\n\n Probeer het over <xliff:g id="NUMBER_2">%d</xliff:g> seconden opnieuw."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Je hebt je ontgrendelingspatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getekend. Na nog <xliff:g id="NUMBER_1">%d</xliff:g> onjuiste pogingen, wordt je gevraagd je tv te ontgrendelen met je inloggegevens voor Google.\n\n Probeer het opnieuw over <xliff:g id="NUMBER_2">%d</xliff:g> seconden."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Je hebt je ontgrendelingspatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getekend. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen wordt u gevraagd je telefoon te ontgrendelen met je aanmeldingsgegevens voor Google.\n\n Probeer het over <xliff:g id="NUMBER_2">%d</xliff:g> seconden opnieuw."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Je hebt <xliff:g id="NUMBER_0">%d</xliff:g> keer geprobeerd de tablet op een onjuiste manier te ontgrendelen. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen worden de fabrieksinstellingen hersteld op de tablet en gaan alle gebruikersgegevens verloren."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Je hebt op onjuiste wijze <xliff:g id="NUMBER_0">%d</xliff:g> keer geprobeerd de tv te ontgrendelen. Na nog <xliff:g id="NUMBER_1">%d</xliff:g> onjuiste pogingen, wordt de tv hersteld naar de fabriekswaarden en gaan alle gebruikersgegevens verloren."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Je hebt nu <xliff:g id="NUMBER_0">%d</xliff:g> keer geprobeerd de telefoon op een onjuiste manier te ontgrendelen. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen worden de fabrieksinstellingen hersteld op de telefoon en gaan alle gebruikersgegevens verloren."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Je hebt je ontgrendelingspatroon <xliff:g id="NUMBER_0">%1$d</xliff:g> keer onjuist getekend. \n\nProbeer het opnieuw over <xliff:g id="NUMBER_1">%2$d</xliff:g> seconden."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Je hebt je wachtwoord <xliff:g id="NUMBER_0">%1$d</xliff:g> keer onjuist getypt. \n\nProbeer het opnieuw over <xliff:g id="NUMBER_1">%2$d</xliff:g> seconden."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Je hebt je pincode <xliff:g id="NUMBER_0">%1$d</xliff:g> keer onjuist getypt. \n\nProbeer het opnieuw over <xliff:g id="NUMBER_1">%2$d</xliff:g> seconden."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Je hebt je ontgrendelingspatroon <xliff:g id="NUMBER_0">%1$d</xliff:g> keer onjuist getekend. Na nog eens <xliff:g id="NUMBER_1">%2$d</xliff:g> mislukte pogingen wordt u gevraagd je tablet te ontgrendelen met je aanmeldingsgegevens voor Google.\n\n Probeer het over <xliff:g id="NUMBER_2">%3$d</xliff:g> seconden opnieuw."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Je hebt je ontgrendelingspatroon <xliff:g id="NUMBER_0">%1$d</xliff:g> keer onjuist getekend. Na nog <xliff:g id="NUMBER_1">%2$d</xliff:g> onjuiste pogingen, wordt je gevraagd je tv te ontgrendelen met je inloggegevens voor Google.\n\n Probeer het opnieuw over <xliff:g id="NUMBER_2">%3$d</xliff:g> seconden."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Je hebt je ontgrendelingspatroon <xliff:g id="NUMBER_0">%1$d</xliff:g> keer onjuist getekend. Na nog eens <xliff:g id="NUMBER_1">%2$d</xliff:g> mislukte pogingen wordt u gevraagd je telefoon te ontgrendelen met je aanmeldingsgegevens voor Google.\n\n Probeer het over <xliff:g id="NUMBER_2">%3$d</xliff:g> seconden opnieuw."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Je hebt <xliff:g id="NUMBER_0">%1$d</xliff:g> keer geprobeerd de tablet op een onjuiste manier te ontgrendelen. Na nog eens <xliff:g id="NUMBER_1">%2$d</xliff:g> mislukte pogingen worden de fabrieksinstellingen hersteld op de tablet en gaan alle gebruikersgegevens verloren."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Je hebt op onjuiste wijze <xliff:g id="NUMBER_0">%1$d</xliff:g> keer geprobeerd de tv te ontgrendelen. Na nog <xliff:g id="NUMBER_1">%2$d</xliff:g> onjuiste pogingen, wordt de tv hersteld naar de fabriekswaarden en gaan alle gebruikersgegevens verloren."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Je hebt nu <xliff:g id="NUMBER_0">%1$d</xliff:g> keer geprobeerd de telefoon op een onjuiste manier te ontgrendelen. Na nog eens <xliff:g id="NUMBER_1">%2$d</xliff:g> mislukte pogingen worden de fabrieksinstellingen hersteld op de telefoon en gaan alle gebruikersgegevens verloren."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Je hebt <xliff:g id="NUMBER">%d</xliff:g> keer geprobeerd de tablet op een onjuiste manier te ontgrendelen. De fabrieksinstellingen worden nu hersteld op de tablet."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Je hebt op onjuiste wijze <xliff:g id="NUMBER">%d</xliff:g> keer geprobeerd de tv te ontgrendelen. De tv wordt nu hersteld naar de fabrieksinstellingen."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Je hebt <xliff:g id="NUMBER">%d</xliff:g> keer geprobeerd de telefoon op een onjuiste manier te ontgrendelen. De fabrieksinstellingen worden nu hersteld op de telefoon."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"woord"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"link"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"lijn"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Fabriekstest mislukt"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"De actie FACTORY_TEST wordt alleen ondersteund voor pakketten die zijn geïnstalleerd in /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Er is geen pakket gevonden dat de actie FACTORY_TEST levert."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Formatteren…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Niet geplaatst"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Geen overeenkomende activiteiten gevonden."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Media-uitvoer aansturen"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"media-uitvoer aansturen"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Hiermee kan een app media-uitvoer naar andere externe apparaten doorsturen."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Installatiesessies lezen"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"installatiesessies lezen"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Hiermee wordt een app toegestaan installatiesessies te lezen. Zo kan de app informatie bekijken over actieve pakketinstallaties."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Installatiepakketten aanvragen"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"installatiepakketten aanvragen"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Hiermee kan een app installatie van pakketten aanvragen."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Raak twee keer aan voor zoomregeling"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Kan widget niet toevoegen."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Ongeldige gebruikersnaam of wachtwoord."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Bent u je gebruikersnaam of wachtwoord vergeten?\nGa naar "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Account controleren…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Je hebt je pincode <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getypt. \n\nProbeer het opnieuw over <xliff:g id="NUMBER_1">%d</xliff:g> seconden."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Je hebt je wachtwoord <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getypt. \n\nProbeer het opnieuw over <xliff:g id="NUMBER_1">%d</xliff:g> seconden."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Je hebt je ontgrendelingspatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getekend. \n\nProbeer het opnieuw over <xliff:g id="NUMBER_1">%d</xliff:g> seconden."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Je hebt <xliff:g id="NUMBER_0">%d</xliff:g> keer geprobeerd de tablet op een onjuiste manier te ontgrendelen. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen worden de fabrieksinstellingen hersteld op de tablet en gaan alle gebruikersgegevens verloren."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Je hebt op onjuiste wijze <xliff:g id="NUMBER_0">%d</xliff:g> keer geprobeerd de tv te ontgrendelen. Na nog <xliff:g id="NUMBER_1">%d</xliff:g> onjuiste pogingen, wordt de tv hersteld naar de fabriekswaarden en gaan alle gebruikersgegevens verloren."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Je hebt nu <xliff:g id="NUMBER_0">%d</xliff:g> keer geprobeerd de telefoon op een onjuiste manier te ontgrendelen. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen worden de fabrieksinstellingen hersteld op de telefoon en gaan alle gebruikersgegevens verloren."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Je hebt je pincode <xliff:g id="NUMBER_0">%1$d</xliff:g> keer onjuist getypt. \n\nProbeer het opnieuw over <xliff:g id="NUMBER_1">%2$d</xliff:g> seconden."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Je hebt je wachtwoord <xliff:g id="NUMBER_0">%1$d</xliff:g> keer onjuist getypt. \n\nProbeer het opnieuw over <xliff:g id="NUMBER_1">%2$d</xliff:g> seconden."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Je hebt je ontgrendelingspatroon <xliff:g id="NUMBER_0">%1$d</xliff:g> keer onjuist getekend. \n\nProbeer het opnieuw over <xliff:g id="NUMBER_1">%2$d</xliff:g> seconden."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Je hebt <xliff:g id="NUMBER_0">%1$d</xliff:g> keer geprobeerd de tablet op een onjuiste manier te ontgrendelen. Na nog eens <xliff:g id="NUMBER_1">%2$d</xliff:g> mislukte pogingen worden de fabrieksinstellingen hersteld op de tablet en gaan alle gebruikersgegevens verloren."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Je hebt op onjuiste wijze <xliff:g id="NUMBER_0">%1$d</xliff:g> keer geprobeerd de tv te ontgrendelen. Na nog <xliff:g id="NUMBER_1">%2$d</xliff:g> onjuiste pogingen, wordt de tv hersteld naar de fabriekswaarden en gaan alle gebruikersgegevens verloren."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Je hebt nu <xliff:g id="NUMBER_0">%1$d</xliff:g> keer geprobeerd de telefoon op een onjuiste manier te ontgrendelen. Na nog eens <xliff:g id="NUMBER_1">%2$d</xliff:g> mislukte pogingen worden de fabrieksinstellingen hersteld op de telefoon en gaan alle gebruikersgegevens verloren."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Je hebt <xliff:g id="NUMBER">%d</xliff:g> keer geprobeerd de tablet op een onjuiste manier te ontgrendelen. De fabrieksinstellingen worden nu hersteld op de tablet."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Je hebt op onjuiste wijze <xliff:g id="NUMBER">%d</xliff:g> keer geprobeerd de tv te ontgrendelen. De tv wordt nu hersteld naar de fabrieksinstellingen."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Je hebt <xliff:g id="NUMBER">%d</xliff:g> keer geprobeerd de telefoon op een onjuiste manier te ontgrendelen. De fabrieksinstellingen worden nu hersteld op de telefoon."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Je hebt je ontgrendelingspatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getekend. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen wordt u gevraagd je tablet te ontgrendelen via een e-mailaccount.\n\n Probeer het over <xliff:g id="NUMBER_2">%d</xliff:g> seconden opnieuw."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Je hebt je ontgrendelingspatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getekend. Na nog <xliff:g id="NUMBER_1">%d</xliff:g> onjuiste pogingen, wordt je gevraagd je tv te ontgrendelen met een e-mailaccount.\n\n Probeer het opnieuw over <xliff:g id="NUMBER_2">%d</xliff:g> seconden."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Je hebt je ontgrendelingspatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getekend. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen wordt u gevraagd je telefoon te ontgrendelen via een e-mailaccount.\n\n Probeer het over <xliff:g id="NUMBER_2">%d</xliff:g> seconden opnieuw."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Je hebt je ontgrendelingspatroon <xliff:g id="NUMBER_0">%1$d</xliff:g> keer onjuist getekend. Na nog eens <xliff:g id="NUMBER_1">%2$d</xliff:g> mislukte pogingen wordt u gevraagd je tablet te ontgrendelen via een e-mailaccount.\n\n Probeer het over <xliff:g id="NUMBER_2">%3$d</xliff:g> seconden opnieuw."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Je hebt je ontgrendelingspatroon <xliff:g id="NUMBER_0">%1$d</xliff:g> keer onjuist getekend. Na nog <xliff:g id="NUMBER_1">%2$d</xliff:g> onjuiste pogingen, wordt je gevraagd je tv te ontgrendelen met een e-mailaccount.\n\n Probeer het opnieuw over <xliff:g id="NUMBER_2">%3$d</xliff:g> seconden."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Je hebt je ontgrendelingspatroon <xliff:g id="NUMBER_0">%1$d</xliff:g> keer onjuist getekend. Na nog eens <xliff:g id="NUMBER_1">%2$d</xliff:g> mislukte pogingen wordt u gevraagd je telefoon te ontgrendelen via een e-mailaccount.\n\n Probeer het over <xliff:g id="NUMBER_2">%3$d</xliff:g> seconden opnieuw."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Verwijderen"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Volume verhogen tot boven het aanbevolen niveau?\n\nAls u langere tijd op hoog volume naar muziek luistert, raakt je gehoor mogelijk beschadigd."</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">Gedurende 1 u</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Tot <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Tot <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (volgend alarm)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Totdat u dit uitschakelt"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Totdat u \'Niet storen\' uitschakelt"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-pa-rIN-watch/strings.xml b/core/res/res/values-pa-rIN-watch/strings.xml
index e5cb9e1..9fc71f0 100644
--- a/core/res/res/values-pa-rIN-watch/strings.xml
+++ b/core/res/res/values-pa-rIN-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"ਐਪ <xliff:g id="NUMBER_1">%2$d</xliff:g> ਦਾ <xliff:g id="NUMBER_0">%1$d</xliff:g>"</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"ਸੰੰਵੇਦਕ"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"ਤੁਹਾਡੇ ਸੰਪਰਕਾਂ ਤੱਕ ਪਹੁੰਚ ਪ"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"ਇਸ ਘੜੀ ਦੇ ਨਿਰਧਾਰਿਤ ਸਥਾਨ ਤੱਕ ਪਹੁੰਚ"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"ਤੁਹਾਡੇ ਕੈਲੰਡਰ ਤੱਕ ਪਹੁੰਚ"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"SMS ਸੁਨੇਹੇ ਭੇਜੋ ਅਤੇ ਦਿਖਾਓ"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"ਤੁਹਾਡੀ ਘੜੀ ਤੇ ਮੌਜੂਦ ਫੋਟੋਆਂ, ਮੀਡੀਆ ਅਤੇ ਫਾਈਲਾਂ ਤੱਕ ਪਹੁੰਚ"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"ਔਡੀਓ ਰਿਕਾਰਡ ਕਰੋ"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"ਤਸਵੀਰਾਂ ਖਿੱਚੋ ਅਤੇ ਵੀਡੀਓ ਰਿਕਾਰਡ ਕਰੋ"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"ਫ਼ੋਨ ਕਾਲਾਂ ਕਰੋ ਅਤੇ ਉਹਨਾਂ ਨੂੰ ਪ੍ਰਬੰਧਿਤ ਕਰੋ"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"ਆਪਣੇ ਮਹੱਤਵਪੂਰਣ ਲੱਛਣਾਂ ਬਾਰੇ ਸੰਵੇਦਕ ਡਾਟਾ ਤੱਕ ਪਹੁੰਚ"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"ਸਥਿਤੀ ਬਾਰ ਹੋਵੋ"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"ਸਰੀਰ ਸੰਵੇਦਕਾਂ ਤੱਕ ਪਹੁੰਚ (ਜਿਵੇਂ ਦਿਲ ਦੀ ਧੜਕਣ ਦੇ ਨਿਰੀਖਕ)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"ਨਿਯਤ ਨਿਰਧਾਰਿਤ ਸਥਾਨ ਤੱਕ ਪਹੁੰਚ (GPS ਅਤੇ ਨੈਟਵਰਕ-ਆਧਾਰਿਤ)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"ਅਨੁਮਾਨਿਤ ਨਿਰਧਾਰਿਤ ਸਥਾਨ ਤੱਕ ਪਹੁੰਚ (ਨੈਟਵਰਕ-ਆਧਾਰਿਤ)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"SIM ਨੂੰ ਕਮਾਂਡਾਂ ਭੇਜੋ"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"ਪੂਰੀ ਨੈਟਵਰਕ ਪਹੁੰਚ ਪ੍ਰਾਪਤ ਕਰੋ"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"ਪ੍ਰੋਫ਼ਾਈਲ ਅਤੇ ਡਿਵਾਈਸ ਦੇ ਮਾਲਕਾਂ ਨੂੰ ਪ੍ਰਬੰਧਿਤ ਕਰੋ"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"WiMAX ਸਥਿਤੀ ਬਦਲੋ"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"Android Beam ਟ੍ਰਾਂਸਫਰ ਸਥਿਤੀ ਪ੍ਰਾਪਤ ਕਰੋ"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"ਰੂਟ ਮੀਡੀਆ ਆਊਟਪੁਟ"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"ਇੰਸਟੌਲ ਸੈਸ਼ਨ ਪੜ੍ਹੋ"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"ਪੈਕੇਜ ਇੰਸਟੌਲ ਕਰਨ ਦੀ ਬੇਨਤੀ ਕਰੋ"</string>
</resources>
diff --git a/core/res/res/values-pa-rIN/strings.xml b/core/res/res/values-pa-rIN/strings.xml
index e1276f1..96343d8 100644
--- a/core/res/res/values-pa-rIN/strings.xml
+++ b/core/res/res/values-pa-rIN/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ਇਸ ਵਿੱਚ ਨਿੱਜੀ ਡਾਟਾ ਸ਼ਾਮਲ ਹੈ ਜਿਵੇਂ ਕ੍ਰੈਡਿਟ ਕਾਰਡ ਨੰਬਰ ਅਤੇ ਪਾਸਵਰਡ।"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"ਸਥਿਤੀ ਬਾਰ ਅਸਮਰੱਥ ਬਣਾਓ ਜਾਂ ਸੰਸ਼ੋਧਿਤ ਕਰੋ"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"ਐਪ ਨੂੰ ਸਥਿਤੀ ਬਾਰ ਨੂੰ ਅਸਮਰੱਥ ਬਣਾਉਣ ਜਾਂ ਸਿਸਟਮ ਆਈਕਨਾਂ ਨੂੰ ਜੋੜਨ ਅਤੇ ਹਟਾਉਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"ਸਥਿਤੀ ਬਾਰ"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"ਸਥਿਤੀ ਪੱਟੀ ਬਣਨ ਦਿਓ"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"ਐਪ ਨੂੰ ਸਥਿਤੀ ਬਾਰ ਹੋਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"ਸਥਿਤੀ ਬਾਰ ਦਾ ਵਿਸਤਾਰ/ਨਸ਼ਟ ਕਰੋ"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"ਐਪ ਨੂੰ ਸਥਿਤੀ ਬਾਰ ਦਾ ਵਿਸਤਾਰ ਕਰਨ ਜਾਂ ਨਸ਼ਟ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"ਐਪ ਨੂੰ WAP ਸੁਨੇਹੇ ਪ੍ਰਾਪਤ ਕਰਨ ਅਤੇ ਉਹਨਾਂ ਦੀ ਪ੍ਰਕਿਰਿਆ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਸ ਅਨੁਮਤੀ ਵਿੱਚ ਸ਼ਾਮਲ ਹੈ ਐਪ ਦੀ ਤੁਹਾਡੀ ਡਿਵਾਈਸ ਤੇ ਭੇਜੇ ਗਏ ਸੁਨੇਹਿਆਂ ਨੂੰ ਤੁਹਾਨੂੰ ਦਿਖਾਏ ਬਿਨਾਂ ਨਿਰੀਖਣ ਕਰਨ ਅਤੇ ਮਿਟਾਉਣ ਦੀ ਸਮਰੱਥਾ।"</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"ਚੱਲ ਰਹੇ ਐਪਸ ਮੁੜ ਪ੍ਰਾਪਤ ਕਰੋ"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"ਐਪ ਨੂੰ ਵਰਤਮਾਨ ਵਿੱਚ ਅਤੇ ਹੁਣੇ ਜਿਹੇ ਚੱਲ ਰਹੇ ਕੰਮਾਂ ਬਾਰੇ ਵਿਸਤ੍ਰਿਤ ਜਾਣਕਾਰੀ ਮੁੜ ਪ੍ਰਾਪਤ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਹ ਐਪ ਨੂੰ ਇਸ ਬਾਰੇ ਜਾਣਕਾਰੀ ਖੋਜਣ ਦੀ ਆਗਿਆ ਦੇ ਸਕਦਾ ਹੈ ਕਿ ਡਿਵਾਈਸ ਤੇ ਕਿਹੜੀਆਂ ਐਪਲੀਕੇਸ਼ਨਾਂ ਵਰਤੀਆਂ ਜਾਂਦੀਆਂ ਹਨ।"</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"ਪ੍ਰੋਫ਼ਾਈਲ ਅਤੇ ਡਿਵਾਈਸ ਦੇ ਮਾਲਕਾਂ ਨੂੰ ਪ੍ਰਬੰਧਿਤ ਕਰੋ"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"ਪ੍ਰੋਫ਼ਾਈਲ ਅਤੇ ਡਿਵਾਈਸ ਮਾਲਕਾਂ ਨੂੰ ਵਿਵਸਥਿਤ ਕਰੋ"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"ਪ੍ਰੋਫ਼ਾਈਲ ਦੇ ਮਾਲਕ ਅਤੇ ਡਿਵਾਈਸ ਦਾ ਮਾਲਕ ਨੂੰ ਸੈੱਟ ਕਰਨ ਲਈ ਐਪਸ ਨੂੰ ਅਨੁਮਤੀ ਦਿੰਦਾ ਹੈ।"</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"ਚੱਲ ਰਹੇ ਐਪਸ ਨੂੰ ਦੁਬਾਰਾ ਕ੍ਰਮ ਦਿਓ"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"ਐਪ ਨੂੰ ਕੰਮਾਂ ਨੂੰ ਅਗਲੇ ਭਾਗ ਅਤੇ ਪਿਛੋਕੜ ਵਿੱਚ ਮੂਵ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਐਪ ਤੁਹਾਡੇ ਇਨਪੁਟ ਤੋਂ ਬਿਨਾਂ ਇਹ ਕਰ ਸਕਦਾ ਹੈ।"</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"ਐਪ ਨੂੰ ਤੁਹਾਡੀ ਟੈਬਲੇਟ ਦਾ ਕਾਲ ਲੌਗ ਸੰਸ਼ੋਧਿਤ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ, ਇਨਕਮਿੰਗ ਅਤੇ ਆਊਟਗੋਇੰਗ ਕਾਲਾਂ ਬਾਰੇ ਡਾਟਾ ਸਮੇਤ। ਖ਼ਰਾਬ ਐਪਸ ਇਸਦੀ ਵਰਤੋਂ ਤੁਹਾਡੇ ਕਾਲ ਲੌਗ ਨੂੰ ਮਿਟਾਉਣ ਜਾਂ ਸੰਸ਼ੋਧਿਤ ਕਰਨ ਲਈ ਕਰ ਸਕਦੇ ਹਨ।"</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"ਐਪ ਨੂੰ ਤੁਹਾਡੇ TV ਦਾ ਕਾਲ ਲੌਗ ਸੰਸ਼ੋਧਿਤ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ, ਇਨਕਮਿੰਗ ਅਤੇ ਆਊਟਗੋਇੰਗ ਕਾਲਾਂ ਬਾਰੇ ਡਾਟਾ ਸਮੇਤ। ਖ਼ਰਾਬ ਐਪਸ ਇਸਦੀ ਵਰਤੋਂ ਤੁਹਾਡੇ ਕਾਲ ਲੌਗ ਨੂੰ ਮਿਟਾਉਣ ਜਾਂ ਸੰਸ਼ੋਧਿਤ ਕਰਨ ਲਈ ਕਰ ਸਕਦੇ ਹਨ।"</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"ਐਪ ਨੂੰ ਤੁਹਾਡੇ ਫੋਨ ਦਾ ਕਾਲ ਲੌਗ ਸੰਸ਼ੋਧਿਤ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ, ਇਨਕਮਿੰਗ ਅਤੇ ਆਊਟਗੋਇੰਗ ਕਾਲਾਂ ਬਾਰੇ ਡਾਟਾ ਸਮੇਤ। ਖ਼ਰਾਬ ਐਪਸ ਇਸਦੀ ਵਰਤੋਂ ਤੁਹਾਡੇ ਕਾਲ ਲੌਗ ਨੂੰ ਮਿਟਾਉਣ ਜਾਂ ਸੰਸ਼ੋਧਿਤ ਕਰਨ ਲਈ ਕਰ ਸਕਦੇ ਹਨ।"</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"ਸਰੀਰ ਸੰਵੇਦਕ (ਜਿਵੇਂ ਦਿਲ ਦੀ ਧੜਕਣ ਦੇ ਨਿਰੀਖਕ)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"ਸਰੀਰ ਸੰਵੇਦਕਾਂ \'ਤੇ ਪਹੁੰਚ ਕਰੋ (ਜਿਵੇਂ ਦਿਲ ਦੀ ਧੜਕਣ ਦੇ ਨਿਰੀਖਕ)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"ਐਪ ਨੂੰ ਉਹਨਾਂ ਸੰਵੇਦਕਾਂ ਦੇ ਡਾਟਾ ਤੱਕ ਪਹੁੰਚ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ ਜੋ ਤੁਹਾਡੀ ਸਰੀਰਕ ਸਥਿਤੀ ਦਾ ਨਿਰੀਖਣ ਕਰ ਸਕਦੇ ਹਨ, ਜਿਵੇਂ ਤੁਹਾਡੇ ਦਿਲ ਦੀ ਧੜਕਣ।"</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"ਕੈਲੰਡਰ ਇਵੈਂਟਾਂ ਪਲਸ ਗੁਪਤ ਜਾਣਕਾਰੀ ਪੜ੍ਹੋ"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"ਐਪ ਨੂੰ ਤੁਹਾਡੀ ਟੈਬਲੇਟ ਤੇ ਸਟੋਰ ਕੀਤੀਆਂ ਸਾਰੀਆਂ ਕੈਲੰਡਰ ਇਵੈਂਟਾਂ ਪੜ੍ਹਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ, ਦੋਸਤਾਂ ਜਾਂ ਸਹਿਯੋਗੀਆਂ ਦੀਆਂ ਇਵੈਂਟਾਂ ਸਮੇਤ। ਇਹ ਐਪ ਨੂੰ ਤੁਹਾਡਾ ਕੈਲੰਡਰ ਡਾਟਾ ਸ਼ੇਅਰ ਜਾਂ ਸੁਰੱਖਿਅਤ ਕਰਨ ਦੀ ਆਗਿਆ ਦੇ ਸਕਦਾ ਹੈ, ਗੁਪਤਤਾ ਜਾਂ ਸੰਵੇਦਨਸ਼ੀਲਤਾ ਤੇ ਧਿਆਨ ਦਿੱਤੇ ਬਿਨਾਂ।"</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"ਐਪ ਨੂੰ ਉਹ ਇਵੈਂਟਾਂ ਜੋੜਨ, ਹਟਾਉਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ, ਜਿਹਨਾਂ ਨੂੰ ਤੁਸੀਂ ਆਪਣੇ ਫੋਨ ਤੇ ਸੰਸ਼ੋਧਿਤ ਕਰ ਸਕਦੇ ਹੋ, ਦੋਸਤਾਂ ਜਾਂ ਸਹਿਯੋਗੀਆਂ ਦੀਆਂ ਇਵੈਂਟਾਂ ਸਮੇਤ। ਇਹ ਐਪ ਨੂੰ ਮਾਲਕ ਦੀ ਜਾਣਕਾਰੀ ਤੋਂ ਬਿਨਾਂ ਉਹ ਸੁਨੇਹੇ, ਜੋ ਕੈਲੰਡਰ ਮਾਲਕਾਂ ਤੋਂ ਆਉਂਦੇ ਜਾਪਦੇ ਹਨ, ਭੇਜਣ ਦੀ ਜਾਂ ਇਵੈਂਟਾਂ ਸੰਸ਼ੋਧਿਤ ਕਰਨ ਦੀ ਆਗਿਆ ਦੇ ਸਕਦਾ ਹੈ।"</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"ਵਾਧੂ ਨਿਰਧਾਰਿਤ ਸਥਾਨ ਪ੍ਰਦਾਤਾ ਕਮਾਂਡਾਂ ਤੱਕ ਪਹੁੰਚ"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"ਐਪ ਨੂੰ ਵਾਧੂ ਨਿਰਧਾਰਿਤ ਸਥਾਨ ਪ੍ਰਦਾਤਾ ਕਮਾਂਡਾਂ ਤੱਕ ਪਹੁੰਚ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਹ ਐਪ ਨੂੰ GPS ਜਾਂ ਹੋਰ ਨਿਰਧਾਰਿਤ ਸਥਾਨ ਸ੍ਰੋਤਾਂ ਦੇ ਓਪਰੇਸ਼ਨ ਵਿੱਚ ਵਿਘਨ ਪਾਉਣ ਦੀ ਆਗਿਆ ਦੇ ਸਕਦਾ ਹੈ।"</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"ਨਿਯਤ ਨਿਰਧਾਰਿਤ ਸਥਾਨ (GPS ਅਤੇ ਨੈਟਵਰਕ-ਆਧਾਰਿਤ)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"ਸਟੀਕ ਟਿਕਾਣੇ \'ਤੇ ਪਹੁੰਚ ਕਰੋ (GPS ਅਤੇ ਨੈੱਟਵਰਕ-ਆਧਾਰਿਤ)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"ਐਪ ਨੂੰ ਗਲੋਬਲ ਪੋਜੀਸ਼ਨਿੰਗ ਸਿਸਟਮ (GPS) ਜਾਂ ਨੈਟਵਰਕ ਨਿਰਧਾਰਿਤ ਸਥਾਨ ਸ੍ਰੋਤ ਜਿਵੇਂ ਸੈਲ ਟਾਵਰ ਅਤੇ Wi-Fi, ਵਰਤਦੇ ਹੋਏ ਤੁਹਾਡਾ ਨਿਯਤ ਨਿਰਧਾਰਿਤ ਸਥਾਨ ਪ੍ਰਾਪਤ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਹ ਨਿਰਧਾਰਿਤ ਸਥਾਨ ਸੇਵਾਵਾਂ ਚਾਲੂ ਅਤੇ ਐਪ ਨੂੰ ਉਹਨਾਂ ਨੂੰ ਵਰਤਣ ਲਈ ਤੁਹਾਡੀ ਡਿਵਾਈਸ ਤੇ ਉਪਲਬਧ ਹੋਣੀਆਂ ਚਾਹੀਦੀਆਂ ਹਨ। ਐਪਸ ਇਸਦੀ ਵਰਤੋਂ ਇਹ ਨਿਰਧਾਰਿਤ ਕਰਨ ਲਈ ਕਰ ਸਕਦੇ ਹਨ ਕਿ ਤੁਸੀਂ ਕਿੱਥੇ ਹੋ ਅਤੇ ਵਾਧੂ ਬੈਟਰੀ ਪਾਵਰ ਖ਼ਰਚ ਸਕਦੇ ਹਨ।"</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"ਅਨੁਮਾਨਿਤ ਨਿਰਧਾਰਿਤ ਸਥਾਨ (ਨੈਟਵਰਕ-ਆਧਾਰਿਤ)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"ਅੰਦਾਜ਼ਨ ਟਿਕਾਣੇ \'ਤੇ ਪਹੁੰਚ ਕਰੋ (ਨੈੱਟਵਰਕ-ਆਧਾਰਿਤ)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"ਐਪ ਨੂੰ ਤੁਹਾਡਾ ਅਨੁਮਾਨਿਤ ਨਿਰਧਾਰਿਤ ਸਥਾਨ ਪ੍ਰਾਪਤ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਹ ਨਿਰਧਾਰਿਤ ਸਥਾਨ ਨੈਟਵਰਕ ਨਿਰਧਾਰਿਤ ਸਥਾਨ ਸ੍ਰੋਤ ਵਰਤਦੇ ਹੋਏ ਨਿਰਧਾਰਿਤ ਸਥਾਨ ਸੇਵਾਵਾਂ ਰਾਹੀਂ ਪ੍ਰਾਪਤ ਕੀਤਾ ਜਾਂਦਾ ਹੈ ਜਿਵੇਂ ਸੈਲ ਟਾਵਰ ਅਤੇ Wi-Fi. ਇਹ ਨਿਰਧਾਰਿਤ ਸਥਾਨ ਸੇਵਾਵਾਂ ਚਾਲੂ ਅਤੇ ਐਪ ਨੂੰ ਉਹਨਾਂ ਨੂੰ ਵਰਤਣ ਲਈ ਤੁਹਾਡੀ ਡਿਵਾਈਸ ਤੇ ਹੋਣੀਆਂ ਚਾਹੀਦੀਆਂ ਹਨ। ਐਪਸ ਇਸਦੀ ਵਰਤੋਂ ਇਹ ਅਨੁਮਾਨ ਲਗਾਉਣ ਲਈ ਕਰ ਸਕਦੇ ਹਨ ਕਿ ਤੁਸੀਂ ਕਿੱਥੇ ਹੋ।"</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"ਆਪਣੀਆਂ ਔਡੀਓ ਸੈਟਿੰਗਾਂ ਬਦਲੋ"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"ਔਪ ਨੂੰ ਗਲੋਬਲ ਔਡੀਓ ਸੈਟਿੰਗਾਂ ਸੰਸ਼ੋਧਿਤ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ ਜਿਵੇਂ ਵੌਲਯੂਮ ਅਤੇ ਆਊਟਪੁਟ ਲਈ ਕਿਹੜਾ ਸਪੀਕਰ ਵਰਤਿਆ ਜਾਂਦਾ ਹੈ।"</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"ਔਡੀਓ ਰਿਕਾਰਡ ਕਰੋ"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"ਐਪ ਨੂੰ ਮਾਈਕ੍ਰੋਫੋਨ ਨਾਲ ਔਡੀਓ ਰਿਕਾਰਡ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਹ ਅਨੁਮਤੀ ਐਪ ਨੂੰ ਤੁਹਾਡੀ ਪੁਸ਼ਟੀ ਤੋਂ ਬਿਨਾਂ ਕਿਸੇ ਵੀ ਸਮੇਂ ਔਡੀਓ ਰਿਕਾਰਡ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦੀ ਹੈ।"</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"sim ਸੰਚਾਰ"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"SIM ਨੂੰ ਕਮਾਂਡਾਂ ਭੇਜੋ"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"ਐਪ ਨੂੰ SIM ਨੂੰ ਕਮਾਂਡਾਂ ਭੇਜਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਹ ਬਹੁਤ ਘਾਤਕ ਹੈ।"</string>
<string name="permlab_camera" msgid="3616391919559751192">"ਤਸਵੀਰਾਂ ਅਤੇ ਵੀਡੀਓ ਬਣਾਓ"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"ਐਪ ਨੂੰ ਕੈਮਰੇ ਨਾਲ ਤਸਵੀਰਾਂ ਅਤੇ ਵੀਡੀਓ ਲੈਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਹ ਅਨੁਮਤੀ ਐਪ ਨੂੰ ਤੁਹਾਡੀ ਪੁਸ਼ਟੀ ਤੋਂ ਬਿਨਾਂ ਕਿਸੇ ਵੀ ਸਮੇਂ ਕੈਮਰਾ ਵਰਤਣ ਦੀ ਆਗਿਆ ਦਿੰਦੀ ਹੈ।"</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"ਐਪ ਨੂੰ ਫੋਨ ਵੱਲੋਂ ਗਿਆਤ ਖਾਤਿਆਂ ਦੀ ਸੂਚੀ ਪ੍ਰਾਪਤ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਸ ਵਿੱਚ ਤੁਹਾਡੇ ਵੱਲੋਂ ਇੰਸਟੌਲ ਕੀਤੀਆਂ ਐਪਲੀਕੇਸ਼ਨਾਂ ਵੱਲੋਂ ਬਣਾਏ ਗਏ ਕੋਈ ਵੀ ਖਾਤੇ ਸ਼ਾਮਲ ਹੋ ਸਕਦੇ ਹਨ।"</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"ਨੈਟਵਰਕ ਕਨੈਕਸ਼ਨ ਦੇਖੋ"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"ਐਪ ਨੂੰ ਨੈਟਵਰਕ ਕਨੈਕਸ਼ਨਾਂ ਬਾਰੇ ਜਾਣਕਾਰੀ ਦੇਖਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ ਜਿਵੇਂ ਕਿਹੜੇ ਨੈਟਵਰਕ ਮੌਜੂਦ ਹਨ ਅਤੇ ਕਨੈਕਟ ਕੀਤੇ ਹੋਏ ਹਨ।"</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"ਪੂਰੀ ਨੈਟਵਰਕ ਪਹੁੰਚ"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"ਪੂਰੀ ਨੈੱਟਵਰਕ ਪਹੁੰਚ ਪਾਓ"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"ਐਪ ਨੂੰ ਨੈਟਵਰਕ ਸੌਕੇਟ ਬਣਾਉਣ ਅਤੇ ਕਸਟਮ ਨੈਟਵਰਕ ਪ੍ਰੋਟੋਕੋਲ ਵਰਤਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਬ੍ਰਾਊਜ਼ਰ ਅਤੇ ਹੋਰ ਐਪਲੀਕੇਸ਼ਨਾਂ ਇੰਟਰਨੈਟ ਨੂੰ ਡਾਟਾ ਭੇਜਣ ਲਈ ਸਾਧਨ ਮੁਹੱਈਆ ਕਰਦਾ ਹੈ, ਇਸਲਈ ਇੰਟਰਨੈਟ ਡਾਟਾ ਭੇਜਣ ਲਈ ਇਹ ਅਨੁਮਤੀ ਲੁੜੀਂਦੀ ਨਹੀਂ ਹੈ।"</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"ਨੈਟਵਰਕ ਕਨੈਕਟੀਵਿਟੀ ਬਦਲੋ"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"ਐਪ ਨੂੰ ਨੈਟਵਰਕ ਕਨੈਕਟੀਵਿਟੀ ਦੀ ਸਥਿਤੀ ਬਦਲਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"ਐਪ ਨੂੰ ਸਥਾਨਕ Bluetooth ਫੋਨ ਨੂੰ ਕੌਂਫਿਗਰ ਕਰਨ ਅਤੇ ਰਿਮੋਟ ਡਿਵਾਈਸਾਂ ਨੂੰ ਖੋਜਣ ਅਤੇ ਉਹਨਾਂ ਨਾਲ ਪੇਅਰ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"WiMAX ਤੋਂ ਕਨੈਕਟ ਅਤੇ ਡਿਸਕਨੈਕਟ ਕਰੋ"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"ਐਪ ਨੂੰ ਇਹ ਨਿਰਧਾਰਿਤ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ ਕਿ WiMAX ਸਮਰਥਿਤ ਹੈ ਜਾਂ ਨਹੀਂ ਅਤੇ ਕਿਸੇ ਵੀ WiMAX ਨੈਟਵਰਕਾਂ ਬਾਰੇ ਜਾਣਕਾਰੀ ਜੋ ਕਨੈਕਟ ਕੀਤੇ ਹੋਏ ਹਨ।"</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX ਸਥਿਤੀ ਬਦਲੋ"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"WiMAX ਸਥਿਤੀ ਬਦਲੋ"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"ਐਪ ਨੂੰ ਟੈਬਲੇਟ ਨੂੰ ਕਨੈਕਟ ਕਰਨ ਅਤੇ WiMAX ਨੈਟਵਰਕਾਂ ਤੋਂ ਟੈਬਲੇਟ ਨੂੰ ਡਿਸਕਨੈਕਟ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"ਐਪ ਨੂੰ TV ਨੂੰ ਕਨੈਕਟ ਕਰਨ ਅਤੇ WiMAX ਨੈਟਵਰਕਾਂ ਤੋਂ TV ਨੂੰ ਡਿਸਕਨੈਕਟ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"ਐਪ ਨੂੰ ਫੋਨ ਨੂੰ ਕਨੈਕਟ ਕਰਨ ਅਤੇ WiMAX ਨੈਟਵਰਕਾਂ ਤੋਂ ਫੋਨ ਨੂੰ ਡਿਸਕਨੈਕਟ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"ਐਪ ਨੂੰ ਟਚ ਸਕ੍ਰੀਨ ਦੇ ਕੈਲੀਬ੍ਰੇਸ਼ਨ ਪੈਰਾਮੀਟਰਾਂ ਨੂੰ ਸੰਸ਼ੋਧਿਤ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਸਧਾਰਨ ਐਪਸ ਲਈ ਕਦੇ ਵੀ ਲੁੜੀਂਦਾ ਨਹੀਂ ਹੋਵੇਗਾ।"</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"DRM ਸਰਟੀਫਿਕੇਟਾਂ ਤੱਕ ਪਹੁੰਚ"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"ਇੱਕ ਐਪਲੀਕੇਸ਼ਨ ਨੂੰ ਵਿਵਸਥਾ ਕਰਨ ਅਤੇ DRM ਸਰਟੀਫਿਕੇਟ ਵਰਤਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਸਧਾਰਨ ਐਪਸ ਲਈ ਕਦੇ ਵੀ ਲੁੜੀਂਦਾ ਨਹੀਂ ਹੋਵੇਗਾ।"</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Android Beam ਟ੍ਰਾਂਸਫਰ ਸਥਿਤੀ ਪ੍ਰਾਪਤ ਕਰੋ"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"Android ਬੀਮ ਟ੍ਰਾਂਸਫਰ ਸਥਿਤੀ ਪ੍ਰਾਪਤ ਕਰੋ"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"ਇਸ ਐਪਲੀਕੇਸ਼ਨ ਨੂੰ ਮੌਜੂਦਾ Android Beam ਟ੍ਰਾਂਸਫਰਾਂ ਬਾਰੇ ਜਾਣਕਾਰੀ ਪ੍ਰਾਪਤ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"DRM ਸਰਟੀਫਿਕੇਟ ਹਟਾਓ"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"ਇੱਕ ਐਪਲੀਕੇਸ਼ਨ ਨੂੰ DRM ਸਰਟੀਫਿਕੇਟ ਹਟਾਉਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਸਧਾਰਨ ਐਪਸ ਲਈ ਕਦੇ ਵੀ ਲੁੜੀਂਦਾ ਨਹੀਂ ਹੋਵੇਗਾ।"</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"ਗ਼ਲਤ PIN ਕੋਡ।"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"ਅਨਲੌਕ ਕਰਨ ਲਈ, ਪਹਿਲਾਂ ਮੀਨੂ ਫਿਰ 0 ਦਬਾਓ।"</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"ਐਮਰਜੈਂਸੀ ਨੰਬਰ"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"ਕੋਈ ਸੇਵਾ ਨਹੀਂ।"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"ਕੋਈ ਸੇਵਾ ਨਹੀਂ"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"ਸਕ੍ਰੀਨ ਲੌਕ ਕੀਤੀ।"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"ਅਨਲੌਕ ਕਰਨ ਲਈ ਮੀਨੂ ਦਬਾਓ ਜਾਂ ਐਮਰਜੈਂਸੀ ਕਾਲ ਕਰੋ।"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"ਅਨਲੌਕ ਕਰਨ ਲਈ ਮੀਨੂ ਦਬਾਓ।"</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"ਉਪਭੋਗਤਾ ਗਾਈਡ ਦੇਖੋ ਜਾਂ ਗਾਹਕ ਸੇਵਾ ਨੂੰ ਫੋਨ ਕਰੋ।"</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM ਕਾਰਡ ਲੌਕ ਕੀਤਾ ਹੋਇਆ ਹੈ।"</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"SIM ਕਾਰਡ ਅਨਲੌਕ ਕਰ ਰਿਹਾ ਹੈ…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"ਤੁਸੀਂ <xliff:g id="NUMBER_0">%d</xliff:g> ਵਾਰ ਆਪਣਾ ਅਨਲੌਕ ਪੈਟਰਨ ਗ਼ਲਤ ਢੰਗ ਨਾਲ ਡ੍ਰਾ ਕੀਤਾ ਹੈ। \n\n <xliff:g id="NUMBER_1">%d</xliff:g> ਸਕਿੰਟਾਂ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"ਤੁਸੀਂ <xliff:g id="NUMBER_0">%d</xliff:g> ਵਾਰ ਆਪਣਾ ਪਾਸਵਰਡ ਗ਼ਲਤ ਢੰਗ ਨਾਲ ਟਾਈਪ ਕੀਤਾ ਹੈ। \n\n <xliff:g id="NUMBER_1">%d</xliff:g> ਸਕਿੰਟਾਂ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"ਤੁਸੀਂ ਆਪਣਾ PIN <xliff:g id="NUMBER_0">%d</xliff:g> ਵਾਰ ਗ਼ਲਤ ਢੰਗ ਨਾਲ ਟਾਈਪ ਕੀਤਾ ਹੈ। \n\n <xliff:g id="NUMBER_1">%d</xliff:g> ਸਕਿੰਟਾਂ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"ਤੁਸੀਂ <xliff:g id="NUMBER_0">%d</xliff:g> ਵਾਰ ਆਪਣਾ ਅਨਲੌਕ ਪੈਟਰਨ ਗ਼ਲਤ ਢੰਗ ਨਾਲ ਡ੍ਰਾ ਕੀਤਾ ਹੈ। <xliff:g id="NUMBER_1">%d</xliff:g> ਹੋਰ ਅਸਫਲ ਕੋਸ਼ਿਸ਼ਾਂ ਤੋਂ ਬਾਅਦ, ਤੁਹਾਨੂੰ ਆਪਣਾ Google ਸਾਈਨਇਨ ਵਰਤਦੇ ਹੋਏ ਆਪਣੀ ਟੈਬਲੇਟ ਅਨਲੌਕ ਕਰਨ ਲਈ ਕਿਹਾ ਜਾਏਗਾ। \n\n <xliff:g id="NUMBER_2">%d</xliff:g> ਸਕਿੰਟਾਂ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"ਤੁਸੀਂ <xliff:g id="NUMBER_0">%d</xliff:g> ਵਾਰ ਆਪਣਾ ਅਨਲੌਕ ਪੈਟਰਨ ਗ਼ਲਤ ਢੰਗ ਨਾਲ ਡ੍ਰਾ ਕੀਤਾ ਹੈ। <xliff:g id="NUMBER_1">%d</xliff:g> ਹੋਰ ਅਸਫਲ ਕੋਸ਼ਿਸ਼ਾਂ ਤੋਂ ਬਾਅਦ, ਤੁਹਾਨੂੰ ਆਪਣਾ Google ਸਾਈਨਇਨ ਵਰਤਦੇ ਹੋਏ ਆਪਣਾ TV ਅਨਲੌਕ ਕਰਨ ਲਈ ਕਿਹਾ ਜਾਏਗਾ।\n\n <xliff:g id="NUMBER_2">%d</xliff:g> ਸਕਿੰਟਾਂ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"ਤੁਸੀਂ <xliff:g id="NUMBER_0">%d</xliff:g> ਵਾਰ ਆਪਣਾ ਅਨਲੌਕ ਪੈਟਰਨ ਗ਼ਲਤ ਢੰਗ ਨਾਲ ਡ੍ਰਾ ਕੀਤਾ ਹੈ। <xliff:g id="NUMBER_1">%d</xliff:g> ਹੋਰ ਅਸਫਲ ਕੋਸ਼ਿਸ਼ਾਂ ਤੋਂ ਬਾਅਦ, ਤੁਹਾਨੂੰ ਆਪਣਾ Google ਸਾਈਨਇਨ ਵਰਤਦੇ ਹੋਏ ਆਪਣਾ ਫੋਨ ਅਨਲੌਕ ਕਰਨ ਲਈ ਕਿਹਾ ਜਾਏਗਾ।\n\n <xliff:g id="NUMBER_2">%d</xliff:g> ਸਕਿੰਟਾਂ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"ਤੁਸੀਂ <xliff:g id="NUMBER_0">%d</xliff:g> ਵਾਰ ਗ਼ਲਤ ਢੰਗ ਨਾਲ ਟੈਬਲੇਟ ਨੂੰ ਅਨਲੌਕ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕੀਤੀ। <xliff:g id="NUMBER_1">%d</xliff:g> ਵੱਧ ਅਸਫਲ ਕੋਸ਼ਿਸ਼ਾਂ ਤੋਂ ਬਾਅਦ, ਟੈਬਲੇਟ ਫੈਕਟਰੀ ਡਿਫੌਲਟ ਤੇ ਰੀਸੈਟ ਹੋ ਜਾਏਗੀ ਅਤੇ ਸਾਰਾ ਉਪਭੋਗਤਾ ਡਾਟਾ ਨਸ਼ਟ ਹੋ ਜਾਏਗਾ।"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"ਤੁਸੀਂ <xliff:g id="NUMBER_0">%d</xliff:g> ਵਾਰ ਗ਼ਲਤ ਢੰਗ ਨਾਲ TV ਨੂੰ ਅਨਲੌਕ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕੀਤੀ। <xliff:g id="NUMBER_1">%d</xliff:g> ਵੱਧ ਅਸਫਲ ਕੋਸ਼ਿਸ਼ਾਂ ਤੋਂ ਬਾਅਦ, TV ਫੈਕਟਰੀ ਡਿਫੌਲਟ ਤੇ ਰੀਸੈਟ ਹੋ ਜਾਏਗਾ ਅਤੇ ਸਾਰਾ ਉਪਭੋਗਤਾ ਡਾਟਾ ਨਸ਼ਟ ਹੋ ਜਾਏਗਾ।"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"ਤੁਸੀਂ <xliff:g id="NUMBER_0">%d</xliff:g> ਵਾਰ ਗ਼ਲਤ ਢੰਗ ਨਾਲ ਫੋਨ ਨੂੰ ਅਨਲੌਕ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕੀਤੀ। <xliff:g id="NUMBER_1">%d</xliff:g> ਵੱਧ ਅਸਫਲ ਕੋਸ਼ਿਸ਼ਾਂ ਤੋਂ ਬਾਅਦ, ਫੋਨ ਫੈਕਟਰੀ ਡਿਫੌਲਟ ਤੇ ਰੀਸੈਟ ਹੋ ਜਾਏਗਾ ਅਤੇ ਸਾਰਾ ਉਪਭੋਗਤਾ ਡਾਟਾ ਨਸ਼ਟ ਹੋ ਜਾਏਗਾ।"</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"ਤੁਸੀਂ <xliff:g id="NUMBER_0">%1$d</xliff:g> ਵਾਰ ਆਪਣਾ ਅਨਲੌਕ ਪੈਟਰਨ ਗ਼ਲਤ ਢੰਗ ਨਾਲ ਡ੍ਰਾ ਕੀਤਾ ਹੈ। \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> ਸਕਿੰਟਾਂ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"ਤੁਸੀਂ <xliff:g id="NUMBER_0">%1$d</xliff:g> ਵਾਰ ਆਪਣਾ ਪਾਸਵਰਡ ਗ਼ਲਤ ਢੰਗ ਨਾਲ ਟਾਈਪ ਕੀਤਾ ਹੈ। \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> ਸਕਿੰਟਾਂ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"ਤੁਸੀਂ ਆਪਣਾ PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> ਵਾਰ ਗ਼ਲਤ ਢੰਗ ਨਾਲ ਟਾਈਪ ਕੀਤਾ ਹੈ। \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> ਸਕਿੰਟਾਂ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"ਤੁਸੀਂ <xliff:g id="NUMBER_0">%1$d</xliff:g> ਵਾਰ ਆਪਣਾ ਅਨਲੌਕ ਪੈਟਰਨ ਗ਼ਲਤ ਢੰਗ ਨਾਲ ਡ੍ਰਾ ਕੀਤਾ ਹੈ। <xliff:g id="NUMBER_1">%2$d</xliff:g> ਹੋਰ ਅਸਫਲ ਕੋਸ਼ਿਸ਼ਾਂ ਤੋਂ ਬਾਅਦ, ਤੁਹਾਨੂੰ ਆਪਣਾ Google ਸਾਈਨਇਨ ਵਰਤਦੇ ਹੋਏ ਆਪਣੀ ਟੈਬਲੇਟ ਅਨਲੌਕ ਕਰਨ ਲਈ ਕਿਹਾ ਜਾਏਗਾ। \n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> ਸਕਿੰਟਾਂ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"ਤੁਸੀਂ <xliff:g id="NUMBER_0">%1$d</xliff:g> ਵਾਰ ਆਪਣਾ ਅਨਲੌਕ ਪੈਟਰਨ ਗ਼ਲਤ ਢੰਗ ਨਾਲ ਡ੍ਰਾ ਕੀਤਾ ਹੈ। <xliff:g id="NUMBER_1">%2$d</xliff:g> ਹੋਰ ਅਸਫਲ ਕੋਸ਼ਿਸ਼ਾਂ ਤੋਂ ਬਾਅਦ, ਤੁਹਾਨੂੰ ਆਪਣਾ Google ਸਾਈਨਇਨ ਵਰਤਦੇ ਹੋਏ ਆਪਣਾ TV ਅਨਲੌਕ ਕਰਨ ਲਈ ਕਿਹਾ ਜਾਏਗਾ।\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> ਸਕਿੰਟਾਂ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"ਤੁਸੀਂ <xliff:g id="NUMBER_0">%1$d</xliff:g> ਵਾਰ ਆਪਣਾ ਅਨਲੌਕ ਪੈਟਰਨ ਗ਼ਲਤ ਢੰਗ ਨਾਲ ਡ੍ਰਾ ਕੀਤਾ ਹੈ। <xliff:g id="NUMBER_1">%2$d</xliff:g> ਹੋਰ ਅਸਫਲ ਕੋਸ਼ਿਸ਼ਾਂ ਤੋਂ ਬਾਅਦ, ਤੁਹਾਨੂੰ ਆਪਣਾ Google ਸਾਈਨਇਨ ਵਰਤਦੇ ਹੋਏ ਆਪਣਾ ਫੋਨ ਅਨਲੌਕ ਕਰਨ ਲਈ ਕਿਹਾ ਜਾਏਗਾ।\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> ਸਕਿੰਟਾਂ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"ਤੁਸੀਂ <xliff:g id="NUMBER_0">%1$d</xliff:g> ਵਾਰ ਗ਼ਲਤ ਢੰਗ ਨਾਲ ਟੈਬਲੇਟ ਨੂੰ ਅਨਲੌਕ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕੀਤੀ। <xliff:g id="NUMBER_1">%2$d</xliff:g> ਵੱਧ ਅਸਫਲ ਕੋਸ਼ਿਸ਼ਾਂ ਤੋਂ ਬਾਅਦ, ਟੈਬਲੇਟ ਫੈਕਟਰੀ ਡਿਫੌਲਟ ਤੇ ਰੀਸੈਟ ਹੋ ਜਾਏਗੀ ਅਤੇ ਸਾਰਾ ਉਪਭੋਗਤਾ ਡਾਟਾ ਨਸ਼ਟ ਹੋ ਜਾਏਗਾ।"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"ਤੁਸੀਂ <xliff:g id="NUMBER_0">%1$d</xliff:g> ਵਾਰ ਗ਼ਲਤ ਢੰਗ ਨਾਲ TV ਨੂੰ ਅਨਲੌਕ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕੀਤੀ। <xliff:g id="NUMBER_1">%2$d</xliff:g> ਵੱਧ ਅਸਫਲ ਕੋਸ਼ਿਸ਼ਾਂ ਤੋਂ ਬਾਅਦ, TV ਫੈਕਟਰੀ ਡਿਫੌਲਟ ਤੇ ਰੀਸੈਟ ਹੋ ਜਾਏਗਾ ਅਤੇ ਸਾਰਾ ਉਪਭੋਗਤਾ ਡਾਟਾ ਨਸ਼ਟ ਹੋ ਜਾਏਗਾ।"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"ਤੁਸੀਂ <xliff:g id="NUMBER_0">%1$d</xliff:g> ਵਾਰ ਗ਼ਲਤ ਢੰਗ ਨਾਲ ਫੋਨ ਨੂੰ ਅਨਲੌਕ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕੀਤੀ। <xliff:g id="NUMBER_1">%2$d</xliff:g> ਵੱਧ ਅਸਫਲ ਕੋਸ਼ਿਸ਼ਾਂ ਤੋਂ ਬਾਅਦ, ਫੋਨ ਫੈਕਟਰੀ ਡਿਫੌਲਟ ਤੇ ਰੀਸੈਟ ਹੋ ਜਾਏਗਾ ਅਤੇ ਸਾਰਾ ਉਪਭੋਗਤਾ ਡਾਟਾ ਨਸ਼ਟ ਹੋ ਜਾਏਗਾ।"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"ਤੁਸੀਂ <xliff:g id="NUMBER">%d</xliff:g> ਵਾਰ ਗ਼ਲਤ ਢੰਗ ਨਾਲ ਟੈਬਲੇਟ ਨੂੰ ਅਨਲੌਕ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕੀਤੀ। ਹੁਣ ਟੌਬਲੇਟ ਫੈਕਟਰੀ ਡਿਫੌਲਟ ਤੇ ਰੀਸੈਟ ਹੋ ਜਾਏਗੀ।"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"ਤੁਸੀਂ <xliff:g id="NUMBER">%d</xliff:g> ਵਾਰ ਗ਼ਲਤ ਢੰਗ ਨਾਲ TV ਨੂੰ ਅਨਲੌਕ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕੀਤੀ। ਹੁਣ TV ਫੈਕਟਰੀ ਡਿਫੌਲਟ ਤੇ ਰੀਸੈਟ ਹੋ ਜਾਏਗਾ।"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"ਤੁਸੀਂ <xliff:g id="NUMBER">%d</xliff:g> ਵਾਰ ਗ਼ਲਤ ਢੰਗ ਨਾਲ ਫੋਨ ਨੂੰ ਅਨਲੌਕ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕੀਤੀ। ਹੁਣ ਫੋਨ ਫੈਕਟਰੀ ਡਿਫੌਲਟ ਤੇ ਰੀਸੈਟ ਹੋ ਜਾਏਗਾ।"</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"ਸ਼ਬਦ"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"ਲਿੰਕ"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"ਲਾਈਨ"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"ਫੈਕਟਰੀ ਜਾਂਚ ਅਸਫਲ"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"FACTORY_TEST ਕਿਰਿਆ ਕੇਵਲ /ਸਿਸਟਮ/ਐਪ ਵਿੱਚ ਇੰਸਟੌਲ ਕੀਤੇ ਪੈਕੇਜਾਂ ਲਈ ਸਮਰਥਿਤ ਹੈ।"</string>
<string name="factorytest_no_action" msgid="872991874799998561">"ਅਜਿਹਾ ਕੋਈ ਪੈਕੇਜ ਨਹੀਂ ਮਿਲਿਆ ਜੋ FACTORY_TEST ਕਿਰਿਆ ਮੁਹੱਈਆ ਕਰਦਾ ਹੈ।"</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"ਫੌਰਮੈਟ ਕਰ ਰਿਹਾ ਹੈ..."</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"ਰੁਚੀ ਨਹੀਂ"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"ਕੋਈ ਮੇਲ ਖਾਂਦੀਆਂ ਗਤੀਵਿਧੀਆਂ ਨਹੀਂ ਮਿਲੀਆਂ।"</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"ਰੂਟ ਮੀਡੀਆ ਆਊਟਪੁਟ"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"ਰੂਟ ਮੀਡੀਆ ਆਊਟਪੁਟ"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"ਇੱਕ ਐਪਲੀਕੇਸ਼ਨ ਨੂੂੰ ਹੋਰਾਂ ਬਾਹਰੀ ਡਿਵਾਈਸਾਂ ਲਈ ਮੀਡੀਆ ਆਊਟਪੁਟ ਰੂਟ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"ਇੰਸਟੌਲ ਸੈਸ਼ਨ ਪੜ੍ਹੋ"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"ਸਥਾਪਿਤ ਸੈਸ਼ਨਾਂ ਨੂੰ ਪੜ੍ਹੋ"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"ਇੱਕ ਐਪਲੀਕੇਸ਼ਨ ਨੂੰ ਇੰਸਟੌਲ ਸੈਸ਼ਨ ਪੜ੍ਹਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਹ ਇਸਨੂੰ ਸਕਿਰਿਆ ਪੈਕੇਜ ਇੰਸਟੌਲੇਸ਼ਨਾਂ ਬਾਰੇ ਵੇਰਵੇ ਦੇਖਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"ਪੈਕੇਜ ਸਥਾਪਨਾ ਦੀ ਬੇਨਤੀ ਕਰੋ"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"ਪੈਕੇਜ ਸਥਾਪਿਤ ਕਰਨ ਦੀ ਬੇਨਤੀ ਕਰੋ"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"ਪੈਕੇਜ ਦੀ ਸਥਾਪਨਾ ਦੀ ਬੇਨਤੀ ਕਰਨ ਲਈ ਐਪਲੀਕੇਸ਼ਨ ਨੂੰ ਅਨੁਮਤੀ ਦਿੰਦਾ ਹੈ"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"ਜ਼ੂਮ ਨਿਯੰਤਰਣ ਲਈ ਦੋ ਵਾਰ ਛੋਹਵੋ"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"ਵਿਜੇਟ ਨਹੀਂ ਜੋੜ ਸਕਿਆ।"</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"ਅਪ੍ਰਮਾਣਿਕ ਉਪਭੋਗਤਾ ਨਾਮ ਜਾਂ ਪਾਸਵਰਡ।"</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"ਕੀ ਤੁਸੀਂ ਆਪਣਾ ਉਪਭੋਗਤਾ ਨਾਮ ਜਾਂ ਪਾਸਵਰਡ ਭੁੱਲ ਗਏ ਹੋ?\n"<b>"google.com/accounts/recovery"</b>" ਤੇ ਜਾਓ।"</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"ਖਾਤੇ ਦੀ ਜਾਂਚ ਕਰ ਰਿਹਾ ਹੈ…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"ਤੁਸੀਂ ਆਪਣਾ PIN <xliff:g id="NUMBER_0">%d</xliff:g> ਵਾਰ ਗ਼ਲਤ ਢੰਗ ਨਾਲ ਟਾਈਪ ਕੀਤਾ ਹੈ। \n\n <xliff:g id="NUMBER_1">%d</xliff:g> ਸਕਿੰਟਾਂ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"ਤੁਸੀਂ <xliff:g id="NUMBER_0">%d</xliff:g> ਵਾਰ ਆਪਣਾ ਪਾਸਵਰਡ ਗ਼ਲਤ ਢੰਗ ਨਾਲ ਟਾਈਪ ਕੀਤਾ ਹੈ। \n\n <xliff:g id="NUMBER_1">%d</xliff:g> ਸਕਿੰਟਾਂ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"ਤੁਸੀਂ <xliff:g id="NUMBER_0">%d</xliff:g> ਵਾਰ ਆਪਣਾ ਅਨਲੌਕ ਪੈਟਰਨ ਗ਼ਲਤ ਢੰਗ ਨਾਲ ਡ੍ਰਾ ਕੀਤਾ ਹੈ। \n\n <xliff:g id="NUMBER_1">%d</xliff:g> ਸਕਿੰਟਾਂ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"ਤੁਸੀਂ <xliff:g id="NUMBER_0">%d</xliff:g> ਵਾਰ ਗ਼ਲਤ ਢੰਗ ਨਾਲ ਟੈਬਲੇਟ ਨੂੰ ਅਨਲੌਕ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕੀਤੀ। <xliff:g id="NUMBER_1">%d</xliff:g> ਵੱਧ ਅਸਫਲ ਕੋਸ਼ਿਸ਼ਾਂ ਤੋਂ ਬਾਅਦ, ਟੈਬਲੇਟ ਫੈਕਟਰੀ ਡਿਫੌਲਟ ਤੇ ਰੀਸੈਟ ਹੋ ਜਾਏਗੀ ਅਤੇ ਸਾਰਾ ਉਪਭੋਗਤਾ ਡਾਟਾ ਨਸ਼ਟ ਹੋ ਜਾਏਗਾ।"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"ਤੁਸੀਂ <xliff:g id="NUMBER_0">%d</xliff:g> ਵਾਰ ਗ਼ਲਤ ਢੰਗ ਨਾਲ TV ਨੂੰ ਅਨਲੌਕ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕੀਤੀ। <xliff:g id="NUMBER_1">%d</xliff:g> ਵੱਧ ਅਸਫਲ ਕੋਸ਼ਿਸ਼ਾਂ ਤੋਂ ਬਾਅਦ, TV ਫੈਕਟਰੀ ਡਿਫੌਲਟ ਤੇ ਰੀਸੈਟ ਹੋ ਜਾਏਗਾ ਅਤੇ ਸਾਰਾ ਉਪਭੋਗਤਾ ਡਾਟਾ ਨਸ਼ਟ ਹੋ ਜਾਏਗਾ।"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"ਤੁਸੀਂ <xliff:g id="NUMBER_0">%d</xliff:g> ਵਾਰ ਗ਼ਲਤ ਢੰਗ ਨਾਲ ਫੋਨ ਨੂੰ ਅਨਲੌਕ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕੀਤੀ। <xliff:g id="NUMBER_1">%d</xliff:g> ਵੱਧ ਅਸਫਲ ਕੋਸ਼ਿਸ਼ਾਂ ਤੋਂ ਬਾਅਦ, ਫੋਨ ਫੈਕਟਰੀ ਡਿਫੌਲਟ ਤੇ ਰੀਸੈਟ ਹੋ ਜਾਏਗਾ ਅਤੇ ਸਾਰਾ ਉਪਭੋਗਤਾ ਡਾਟਾ ਨਸ਼ਟ ਹੋ ਜਾਏਗਾ।"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"ਤੁਸੀਂ ਆਪਣਾ PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> ਵਾਰ ਗ਼ਲਤ ਢੰਗ ਨਾਲ ਟਾਈਪ ਕੀਤਾ ਹੈ। \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> ਸਕਿੰਟਾਂ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"ਤੁਸੀਂ <xliff:g id="NUMBER_0">%1$d</xliff:g> ਵਾਰ ਆਪਣਾ ਪਾਸਵਰਡ ਗ਼ਲਤ ਢੰਗ ਨਾਲ ਟਾਈਪ ਕੀਤਾ ਹੈ। \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> ਸਕਿੰਟਾਂ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"ਤੁਸੀਂ <xliff:g id="NUMBER_0">%1$d</xliff:g> ਵਾਰ ਆਪਣਾ ਅਨਲੌਕ ਪੈਟਰਨ ਗ਼ਲਤ ਢੰਗ ਨਾਲ ਡ੍ਰਾ ਕੀਤਾ ਹੈ। \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> ਸਕਿੰਟਾਂ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"ਤੁਸੀਂ <xliff:g id="NUMBER_0">%1$d</xliff:g> ਵਾਰ ਗ਼ਲਤ ਢੰਗ ਨਾਲ ਟੈਬਲੇਟ ਨੂੰ ਅਨਲੌਕ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕੀਤੀ। <xliff:g id="NUMBER_1">%2$d</xliff:g> ਵੱਧ ਅਸਫਲ ਕੋਸ਼ਿਸ਼ਾਂ ਤੋਂ ਬਾਅਦ, ਟੈਬਲੇਟ ਫੈਕਟਰੀ ਡਿਫੌਲਟ ਤੇ ਰੀਸੈਟ ਹੋ ਜਾਏਗੀ ਅਤੇ ਸਾਰਾ ਉਪਭੋਗਤਾ ਡਾਟਾ ਨਸ਼ਟ ਹੋ ਜਾਏਗਾ।"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"ਤੁਸੀਂ <xliff:g id="NUMBER_0">%1$d</xliff:g> ਵਾਰ ਗ਼ਲਤ ਢੰਗ ਨਾਲ TV ਨੂੰ ਅਨਲੌਕ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕੀਤੀ। <xliff:g id="NUMBER_1">%2$d</xliff:g> ਵੱਧ ਅਸਫਲ ਕੋਸ਼ਿਸ਼ਾਂ ਤੋਂ ਬਾਅਦ, TV ਫੈਕਟਰੀ ਡਿਫੌਲਟ ਤੇ ਰੀਸੈਟ ਹੋ ਜਾਏਗਾ ਅਤੇ ਸਾਰਾ ਉਪਭੋਗਤਾ ਡਾਟਾ ਨਸ਼ਟ ਹੋ ਜਾਏਗਾ।"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"ਤੁਸੀਂ <xliff:g id="NUMBER_0">%1$d</xliff:g> ਵਾਰ ਗ਼ਲਤ ਢੰਗ ਨਾਲ ਫੋਨ ਨੂੰ ਅਨਲੌਕ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕੀਤੀ। <xliff:g id="NUMBER_1">%2$d</xliff:g> ਵੱਧ ਅਸਫਲ ਕੋਸ਼ਿਸ਼ਾਂ ਤੋਂ ਬਾਅਦ, ਫੋਨ ਫੈਕਟਰੀ ਡਿਫੌਲਟ ਤੇ ਰੀਸੈਟ ਹੋ ਜਾਏਗਾ ਅਤੇ ਸਾਰਾ ਉਪਭੋਗਤਾ ਡਾਟਾ ਨਸ਼ਟ ਹੋ ਜਾਏਗਾ।"</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"ਤੁਸੀਂ <xliff:g id="NUMBER">%d</xliff:g> ਵਾਰ ਗ਼ਲਤ ਢੰਗ ਨਾਲ ਟੈਬਲੇਟ ਨੂੰ ਅਨਲੌਕ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕੀਤੀ। ਹੁਣ ਟੈਬਲੇਟ ਫੈਕਟਰੀ ਡਿਫੌਲਟ ਤੇ ਰੀਸੈਟ ਹੋ ਜਾਏਗੀ।"</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"ਤੁਸੀਂ <xliff:g id="NUMBER">%d</xliff:g> ਵਾਰ ਗ਼ਲਤ ਢੰਗ ਨਾਲ TV ਨੂੰ ਅਨਲੌਕ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕੀਤੀ। ਹੁਣ TV ਫੈਕਟਰੀ ਡਿਫੌਲਟ ਤੇ ਰੀਸੈਟ ਹੋ ਜਾਏਗਾ।"</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"ਤੁਸੀਂ <xliff:g id="NUMBER">%d</xliff:g> ਵਾਰ ਗ਼ਲਤ ਢੰਗ ਨਾਲ ਫੋਨ ਨੂੰ ਅਨਲੌਕ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕੀਤੀ। ਹੁਣ ਫੋਨ ਫੈਕਟਰੀ ਡਿਫੌਲਟ ਤੇ ਰੀਸੈਟ ਹੋ ਜਾਏਗਾ।"</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"ਤੁਸੀਂ <xliff:g id="NUMBER_0">%d</xliff:g> ਵਾਰ ਆਪਣਾ ਅਨਲੌਕ ਪੈਟਰਨ ਗ਼ਲਤ ਢੰਗ ਨਾਲ ਡ੍ਰਾ ਕੀਤਾ ਹੈ। <xliff:g id="NUMBER_1">%d</xliff:g> ਹੋਰ ਅਸਫਲ ਕੋਸ਼ਿਸ਼ਾਂ ਤੋਂ ਬਾਅਦ, ਤੁਹਾਨੂੰ ਇੱਕ ਈਮੇਲ ਖਾਤਾ ਵਰਤਦੇ ਹੋਏ ਆਪਣੀ ਟੈਬਲੇਟ ਅਨਲੌਕ ਕਰਨ ਲਈ ਕਿਹਾ ਜਾਏਗਾ।\n\n <xliff:g id="NUMBER_2">%d</xliff:g> ਸਕਿੰਟਾਂ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"ਤੁਸੀਂ <xliff:g id="NUMBER_0">%d</xliff:g> ਵਾਰ ਆਪਣਾ ਅਨਲੌਕ ਪੈਟਰਨ ਗ਼ਲਤ ਢੰਗ ਨਾਲ ਡ੍ਰਾ ਕੀਤਾ ਹੈ। <xliff:g id="NUMBER_1">%d</xliff:g> ਹੋਰ ਅਸਫਲ ਕੋਸ਼ਿਸ਼ਾਂ ਤੋਂ ਬਾਅਦ, ਤੁਹਾਨੂੰ ਇੱਕ ਈਮੇਲ ਖਾਤਾ ਵਰਤਦੇ ਹੋਏ ਆਪਣਾ TV ਅਨਲੌਕ ਕਰਨ ਲਈ ਕਿਹਾ ਜਾਏਗਾ।\n\n <xliff:g id="NUMBER_2">%d</xliff:g> ਸਕਿੰਟਾਂ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"ਤੁਸੀਂ <xliff:g id="NUMBER_0">%d</xliff:g> ਵਾਰ ਆਪਣਾ ਅਨਲੌਕ ਪੈਟਰਨ ਗ਼ਲਤ ਢੰਗ ਨਾਲ ਡ੍ਰਾ ਕੀਤਾ ਹੈ। <xliff:g id="NUMBER_1">%d</xliff:g> ਹੋਰ ਅਸਫਲ ਕੋਸ਼ਿਸ਼ਾਂ ਤੋਂ ਬਾਅਦ, ਤੁਹਾਨੂੰ ਇੱਕ ਈਮੇਲ ਖਾਤਾ ਵਰਤਦੇ ਹੋਏ ਆਪਣਾ ਫੋਨ ਅਨਲੌਕ ਕਰਨ ਲਈ ਕਿਹਾ ਜਾਏਗਾ।\n\n <xliff:g id="NUMBER_2">%d</xliff:g> ਸਕਿੰਟਾਂ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"ਤੁਸੀਂ <xliff:g id="NUMBER_0">%1$d</xliff:g> ਵਾਰ ਆਪਣਾ ਅਨਲੌਕ ਪੈਟਰਨ ਗ਼ਲਤ ਢੰਗ ਨਾਲ ਡ੍ਰਾ ਕੀਤਾ ਹੈ। <xliff:g id="NUMBER_1">%2$d</xliff:g> ਹੋਰ ਅਸਫਲ ਕੋਸ਼ਿਸ਼ਾਂ ਤੋਂ ਬਾਅਦ, ਤੁਹਾਨੂੰ ਇੱਕ ਈਮੇਲ ਖਾਤਾ ਵਰਤਦੇ ਹੋਏ ਆਪਣੀ ਟੈਬਲੇਟ ਅਨਲੌਕ ਕਰਨ ਲਈ ਕਿਹਾ ਜਾਏਗਾ।\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> ਸਕਿੰਟਾਂ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"ਤੁਸੀਂ <xliff:g id="NUMBER_0">%1$d</xliff:g> ਵਾਰ ਆਪਣਾ ਅਨਲੌਕ ਪੈਟਰਨ ਗ਼ਲਤ ਢੰਗ ਨਾਲ ਡ੍ਰਾ ਕੀਤਾ ਹੈ। <xliff:g id="NUMBER_1">%2$d</xliff:g> ਹੋਰ ਅਸਫਲ ਕੋਸ਼ਿਸ਼ਾਂ ਤੋਂ ਬਾਅਦ, ਤੁਹਾਨੂੰ ਇੱਕ ਈਮੇਲ ਖਾਤਾ ਵਰਤਦੇ ਹੋਏ ਆਪਣਾ TV ਅਨਲੌਕ ਕਰਨ ਲਈ ਕਿਹਾ ਜਾਏਗਾ।\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> ਸਕਿੰਟਾਂ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"ਤੁਸੀਂ <xliff:g id="NUMBER_0">%1$d</xliff:g> ਵਾਰ ਆਪਣਾ ਅਨਲੌਕ ਪੈਟਰਨ ਗ਼ਲਤ ਢੰਗ ਨਾਲ ਡ੍ਰਾ ਕੀਤਾ ਹੈ। <xliff:g id="NUMBER_1">%2$d</xliff:g> ਹੋਰ ਅਸਫਲ ਕੋਸ਼ਿਸ਼ਾਂ ਤੋਂ ਬਾਅਦ, ਤੁਹਾਨੂੰ ਇੱਕ ਈਮੇਲ ਖਾਤਾ ਵਰਤਦੇ ਹੋਏ ਆਪਣਾ ਫੋਨ ਅਨਲੌਕ ਕਰਨ ਲਈ ਕਿਹਾ ਜਾਏਗਾ।\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> ਸਕਿੰਟਾਂ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"ਹਟਾਓ"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"ਕੀ ਵੌਲਯੂਮ ਸਿਫਾਰਿਸ਼ ਕੀਤੇ ਪੱਧਰ ਤੋਂ ਵਧਾਉਣੀ ਹੈ?\n\nਲੰਮੇ ਸਮੇਂ ਤੱਕ ਉੱਚ ਵੌਲਯੂਮ ਤੇ ਸੁਣਨ ਨਾਲ ਤੁਹਾਡੀ ਸੁਣਨ ਸ਼ਕਤੀ ਨੂੰ ਨੁਕਸਾਨ ਪਹੁੰਚ ਸਕਦਾ ਹੈ।"</string>
@@ -1470,6 +1468,7 @@
<item quantity="other">%d ਘੰਟਿਆਂ ਲਈ</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> ਤੱਕ"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> ਤੱਕ (ਅਗਲਾ ਅਲਾਰਮ)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਇਸਨੂੰ ਬੰਦ ਨਹੀਂ ਕਰਦੇ ਹੋ"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ ਨੂੰ ਬੰਦ ਨਹੀਂ ਕਰਦੇ ਹੋ"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-pl-watch/strings.xml b/core/res/res/values-pl-watch/strings.xml
index ff9f7ea..1a855d1 100644
--- a/core/res/res/values-pl-watch/strings.xml
+++ b/core/res/res/values-pl-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Aplikacja <xliff:g id="NUMBER_0">%1$d</xliff:g> z <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Czujniki"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"dostęp do kontaktów"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"dostęp do lokalizacji tego zegarka"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"dostęp do kalendarza"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"wysyłanie i wyświetlanie SMS-ów"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"dostęp do zdjęć, multimediów i plików na Twoim zegarku"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"nagrywanie dźwięku"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"robienie zdjęć i nagrywanie filmów"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"nawiązywanie połączeń telefonicznych i zarządzanie nimi"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"dostęp do danych czujnika podstawowych funkcji życiowych"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"działanie jako pasek stanu"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"dostęp do czujników ciała (np. monitorujących tętno)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"dostęp do dokładnej lokalizacji (na podstawie GPS-u i sieci)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"dostęp do przybliżonej lokalizacji (na podstawie sieci)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"wysyłanie poleceń do karty SIM"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"pełny dostęp do sieci"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"zarządzanie właścicielami profilu i urządzenia"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"zmiana stanu WiMAX"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"uzyskiwanie informacji o stanie transmisji Android Beam"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"kierowanie wyjścia multimediów"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"odczytywanie sesji instalacji"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"żądanie instalacji pakietów"</string>
</resources>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 0ef89a6..ae06e33 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -256,7 +256,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Obejmuje informacje osobiste, takie jak numery kart kredytowych i hasła."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"wyłączanie lub zmienianie paska stanu"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Pozwala aplikacji na wyłączanie paska stanu oraz dodawanie i usuwanie ikon systemowych."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"pasek stanu"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"działanie jako pasek stanu"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Pozwala aplikacji na występowanie na pasku stanu."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"rozwijanie/zwijanie paska stanu"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Pozwala aplikacji na rozwijanie lub zwijanie paska stanu."</string>
@@ -284,7 +284,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Pozwala aplikacji na odbieranie i przetwarzanie wiadomości WAP. To oznacza, że aplikacja będzie mogła bez Twojej wiedzy monitorować i usuwać wiadomości wysyłane do Twojego urządzenia."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"pobieranie uruchomionych aplikacji"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Pozwala aplikacji na pobieranie informacji o aktualnie i niedawno działających zadaniach. Dzięki temu aplikacja może uzyskać informacje o tym, które aplikacje są używane na urządzeniu."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Zarządzanie właścicielami profilu i urządzenia"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"zarządzanie właścicielami profilu i urządzenia"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Zezwala aplikacjom na ustawianie właścicieli profilu i urządzenia."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"zmienianie kolejności uruchomionych aplikacji"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Pozwala aplikacji na przenoszenie zadań między tłem a pierwszym planem. Aplikacja może to robić bez Twojego udziału."</string>
@@ -326,7 +326,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Zezwala aplikacji na modyfikowanie rejestru połączeń tabletu, w tym danych o połączeniach przychodzących i wychodzących. Złośliwe aplikacje mogą wykorzystać tę możliwość, by wyczyścić lub zmodyfikować rejestr połączeń."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Pozwala aplikacji modyfikować rejestr połączeń telewizora, w tym dane o połączeniach przychodzących i wychodzących. Szkodliwe aplikacje mogą to wykorzystać do skasowania lub zmodyfikowania rejestru połączeń."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Zezwala aplikacji na modyfikowanie rejestru połączeń telefonu, w tym danych o połączeniach przychodzących i wychodzących. Złośliwe aplikacje mogą wykorzystać tę możliwość, by wyczyścić lub zmodyfikować rejestr połączeń."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"czujniki ciała (np. monitorujące tętno)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"dostęp do czujników ciała (np. monitorujących tętno)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Pozwala aplikacji na dostęp do danych z czujników, które monitorują Twój stan fizyczny (np. tętno)."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"odczyt wydarzeń w kalendarzu wraz z informacjami poufnymi"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Pozwala aplikacji na odczytywanie wszystkich wydarzeń w kalendarzu zapisanych na tablecie, w tym pochodzących od znajomych i współpracowników. Aplikacja z takim uprawnieniem może udostępniać i zapisywać dane kalendarza niezależnie od ich poufności."</string>
@@ -338,15 +338,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Pozwala aplikacji na dodawanie, usuwanie i zmienianie zdarzeń, które możesz modyfikować na swoim telefonie, w tym pochodzących od znajomych i współpracowników. Aplikacja z tym uprawnieniem może wysyłać wiadomości, które wyglądają jak pochodzące od właścicieli kalendarza, a także modyfikować zdarzenia bez wiedzy właścicieli."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"dostęp do dodatkowych poleceń dostawcy informacji o lokalizacji"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Pozwala aplikacji na dostęp do dodatkowych poleceń dostawcy informacji o lokalizacji. Aplikacje z tym uprawnieniem mogą wpływać na działanie GPS-u lub innych źródeł lokalizacji."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"dokładna lokalizacja (na podstawie sygnału GPS i sieci)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"dostęp do dokładnej lokalizacji (na podstawie GPS-u i sieci)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Zezwala aplikacji na określanie dokładnej lokalizacji dzięki sygnałowi GPS lub źródłom lokalizacji sieciowej, takim jak wieże sieci komórkowych i sieci Wi-Fi. Te usługi lokalizacyjne muszą być włączone i dostępne dla urządzenia, by aplikacja mogła z nich korzystać. Gdy to uprawnienie jest aktywne, aplikacje mogą określać Twoje położenie. Pamiętaj jednak, że telefon zużywa wtedy więcej energii."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"przybliżona lokalizacja (na podstawie sieci)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"dostęp do przybliżonej lokalizacji (na podstawie sieci)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Zezwala aplikacji na określanie przybliżonej lokalizacji. Jest ona odczytywana z usług lokalizacyjnych wykorzystujących źródła lokalizacji sieciowej, takie jak wieże sieci komórkowych i sieci Wi-Fi. Te usługi lokalizacyjne muszą być włączone i dostępne dla urządzenia, by aplikacja mogła z nich korzystać. Gdy to uprawnienie jest aktywne, aplikacje mogą określać Twoje przybliżone położenie."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"zmienianie ustawień audio"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Pozwala aplikacji na modyfikowanie globalnych ustawień dźwięku, takich jak głośność oraz urządzenie wyjściowe."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"nagrywanie dźwięku"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Pozwala aplikacji na nagrywanie dźwięku przez mikrofon. Aplikacja z tym uprawnieniem może nagrywać dźwięk w dowolnym momencie bez Twojego potwierdzenia."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"komunikacja z kartą SIM"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"wysyłanie poleceń do karty SIM"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Pozwala aplikacji na wysyłanie poleceń do karty SIM. To bardzo niebezpieczne."</string>
<string name="permlab_camera" msgid="3616391919559751192">"wykonywanie zdjęć i filmów wideo"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Pozwala aplikacji na robienie zdjęć i nagrywanie filmów przy użyciu aparatu. Aplikacja z tym uprawnieniem może użyć aparatu w dowolnym momencie bez Twojego potwierdzenia."</string>
@@ -384,7 +384,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Pozwala aplikacji na uzyskanie listy kont zapisanych w telefonie. Może ona obejmować wszystkie konta utworzone przez zainstalowane aplikacje."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"wyświetlanie połączeń sieciowych"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Pozwala aplikacji na dostęp do informacji o połączeniach sieciowych – np. o dostępnych i połączonych sieciach."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"pełny dostęp do sieci"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"pełny dostęp do sieci"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Pozwala aplikacji na tworzenie gniazd sieciowych i używanie niestandardowych protokołów sieciowych. Przeglądarka i inne aplikacje zapewniają metody wysyłania danych do internetu, więc w ich przypadku to uprawnienie nie jest potrzebne."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"zmienianie połączeń sieci"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Pozwala aplikacji na zmianę stanu łączności sieciowej."</string>
@@ -404,7 +404,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Pozwala aplikacji na konfigurowanie lokalnego telefonu z funkcją Bluetooth oraz na wykrywanie urządzeń zdalnych i parowanie z nimi."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"łączenie się i rozłączanie z siecią WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Pozawala aplikacji określić, czy obsługa WiMAX jest włączona, oraz uzyskać informacje o wszystkich podłączonych sieciach WiMAX."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"zmienianie stanu WiMAX"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"zmiana stanu WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Pozwala aplikacji na nawiązywanie i kończenie połączeń z sieciami WiMAX w tablecie."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Pozwala aplikacji nawiązywać i kończyć połączenia telewizora z sieciami WiMAX."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Pozwala aplikacji na nawiązywanie i kończenie połączeń z sieciami WiMAX w telefonie."</string>
@@ -487,7 +487,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Zezwala aplikacji na modyfikowanie parametrów kalibracji ekranu dotykowego. Nieprzeznaczone dla zwykłych aplikacji."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"dostęp do certyfikatów DRM"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Zezwala aplikacji na dodanie i używanie certyfikatów DRM. Nieprzeznaczone dla zwykłych aplikacji."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Uzyskiwanie informacji o stanie transmisji Android Beam"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"uzyskiwanie informacji o stanie transmisji Android Beam"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Zezwala tej aplikacji na otrzymywanie informacji o aktualnych transmisjach Android Beam"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"usuwanie certyfikatów DRM"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Zezwala aplikacji na usuwanie certyfikatów DRM. Nie powinno być nigdy potrzebne w zwykłych aplikacjach."</string>
@@ -652,7 +652,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Błędny kod PIN"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Aby odblokować, naciśnij Menu, a następnie 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Numer alarmowy"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Brak usługi"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Brak usługi"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Ekran zablokowany."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Naciśnij Menu, aby odblokować lub wykonać połączenie alarmowe."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Naciśnij Menu, aby odblokować."</string>
@@ -684,15 +684,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Zapoznaj się z instrukcją obsługi lub skontaktuj się z działem obsługi klienta."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"Karta SIM jest zablokowana."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Odblokowywanie karty SIM..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Wzór odblokowania został nieprawidłowo narysowany <xliff:g id="NUMBER_0">%d</xliff:g> razy. \n\nSpróbuj ponownie za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Po raz <xliff:g id="NUMBER_0">%d</xliff:g> zostało wpisane nieprawidłowe hasło. \n\nSpróbuj ponownie za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Po raz <xliff:g id="NUMBER_0">%d</xliff:g> został wpisany nieprawidłowy PIN. \n\nSpróbuj ponownie za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Wzór odblokowania został <xliff:g id="NUMBER_0">%d</xliff:g> razy narysowany nieprawidłowo. Po <xliff:g id="NUMBER_1">%d</xliff:g> kolejnych próbach zakończonych niepowodzeniem konieczne będzie odblokowanie tabletu przy użyciu danych logowania na konto Google.\n\n Spróbuj ponownie za <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Wzór odblokowania narysowałeś niepoprawnie <xliff:g id="NUMBER_0">%d</xliff:g> razy. Po jeszcze <xliff:g id="NUMBER_1">%d</xliff:g> nieudanych próbach trzeba będzie odblokować telewizor przy użyciu danych logowania konta Google.\n\n Spróbuj ponownie za <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Wzór odblokowania został <xliff:g id="NUMBER_0">%d</xliff:g> razy narysowany nieprawidłowo. Po <xliff:g id="NUMBER_1">%d</xliff:g> kolejnych próbach zakończonych niepowodzeniem konieczne będzie odblokowanie telefonu przy użyciu danych logowania na konto Google.\n\n Spróbuj ponownie za <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Próbowano <xliff:g id="NUMBER_0">%d</xliff:g> razy nieprawidłowo odblokować tablet. Po kolejnych <xliff:g id="NUMBER_1">%d</xliff:g> nieudanych próbach tablet zostanie zresetowany do ustawień fabrycznych, a wszystkie dane użytkownika zostaną utracone."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Podałeś nieprawidłowe dane odblokowania telewizora <xliff:g id="NUMBER_0">%d</xliff:g> razy. Po jeszcze <xliff:g id="NUMBER_1">%d</xliff:g> nieudanych próbach telewizor zostanie zresetowany do stanu fabrycznego, a wszystkie dane użytkownika zostaną skasowane."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Próbowano <xliff:g id="NUMBER_0">%d</xliff:g> razy nieprawidłowo odblokować telefon. Po kolejnych <xliff:g id="NUMBER_1">%d</xliff:g> nieudanych próbach telefon zostanie zresetowany do ustawień fabrycznych, a wszystkie dane użytkownika zostaną utracone."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Wzór odblokowania został nieprawidłowo narysowany <xliff:g id="NUMBER_0">%1$d</xliff:g> razy. \n\nSpróbuj ponownie za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Po raz <xliff:g id="NUMBER_0">%1$d</xliff:g> zostało wpisane nieprawidłowe hasło. \n\nSpróbuj ponownie za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Po raz <xliff:g id="NUMBER_0">%1$d</xliff:g> został wpisany nieprawidłowy PIN. \n\nSpróbuj ponownie za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Wzór odblokowania został <xliff:g id="NUMBER_0">%1$d</xliff:g> razy narysowany nieprawidłowo. Po <xliff:g id="NUMBER_1">%2$d</xliff:g> kolejnych próbach zakończonych niepowodzeniem konieczne będzie odblokowanie tabletu przy użyciu danych logowania na konto Google.\n\n Spróbuj ponownie za <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Wzór odblokowania narysowałeś niepoprawnie <xliff:g id="NUMBER_0">%1$d</xliff:g> razy. Po jeszcze <xliff:g id="NUMBER_1">%2$d</xliff:g> nieudanych próbach trzeba będzie odblokować telewizor przy użyciu danych logowania konta Google.\n\n Spróbuj ponownie za <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Wzór odblokowania został <xliff:g id="NUMBER_0">%1$d</xliff:g> razy narysowany nieprawidłowo. Po <xliff:g id="NUMBER_1">%2$d</xliff:g> kolejnych próbach zakończonych niepowodzeniem konieczne będzie odblokowanie telefonu przy użyciu danych logowania na konto Google.\n\n Spróbuj ponownie za <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Próbowano <xliff:g id="NUMBER_0">%1$d</xliff:g> razy nieprawidłowo odblokować tablet. Po kolejnych <xliff:g id="NUMBER_1">%2$d</xliff:g> nieudanych próbach tablet zostanie zresetowany do ustawień fabrycznych, a wszystkie dane użytkownika zostaną utracone."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Podałeś nieprawidłowe dane odblokowania telewizora <xliff:g id="NUMBER_0">%1$d</xliff:g> razy. Po jeszcze <xliff:g id="NUMBER_1">%2$d</xliff:g> nieudanych próbach telewizor zostanie zresetowany do stanu fabrycznego, a wszystkie dane użytkownika zostaną skasowane."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Próbowano <xliff:g id="NUMBER_0">%1$d</xliff:g> razy nieprawidłowo odblokować telefon. Po kolejnych <xliff:g id="NUMBER_1">%2$d</xliff:g> nieudanych próbach telefon zostanie zresetowany do ustawień fabrycznych, a wszystkie dane użytkownika zostaną utracone."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Próbowano <xliff:g id="NUMBER">%d</xliff:g> razy nieprawidłowo odblokować tablet. Tablet zostanie teraz zresetowany do ustawień fabrycznych."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Podałeś nieprawidłowe dane odblokowania telewizora <xliff:g id="NUMBER">%d</xliff:g> razy. Zostanie on teraz zresetowany do stanu fabrycznego."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Próbowano <xliff:g id="NUMBER">%d</xliff:g> razy nieprawidłowo odblokować telefon. Telefon zostanie teraz zresetowany do ustawień fabrycznych."</string>
@@ -744,8 +744,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"słowo"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"link"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"wiersz"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Nieudany test fabryczny"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Czynność FACTORY_TEST jest obsługiwana tylko dla pakietów zainstalowanych w katalogu /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Nie znaleziono żadnego pakietu, który zapewnia działanie FACTORY_TEST."</string>
@@ -1087,11 +1085,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Formatuję…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Nie podłączono"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Nie znaleziono pasujących działań."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Kierowanie wyjścia multimediów"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"kierowanie wyjścia multimediów"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Pozwala aplikacji na kierowanie wyjściowych danych multimedialnych do innych urządzeń zewnętrznych."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Odczyt sesji instalacji"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"odczytywanie sesji instalacji"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Pozwala aplikacji odczytywać sesje instalacji. Umożliwia to jej na poznanie szczegółów aktywnych instalacji pakietów."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Żądanie instalacji pakietów"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"żądanie instalacji pakietów"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Zezwala aplikacji żądanie instalacji pakietów."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Dotknij dwukrotnie, aby sterować powiększeniem."</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Nie można dodać widżetu."</string>
@@ -1301,18 +1299,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Nieprawidłowa nazwa użytkownika lub hasło."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Nie pamiętasz nazwy użytkownika lub hasła?\nWejdź na "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Sprawdzam konto"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Wpisałeś nieprawidłowy kod PIN <xliff:g id="NUMBER_0">%d</xliff:g> razy. \n\nSpróbuj ponownie za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Po raz <xliff:g id="NUMBER_0">%d</xliff:g> wpisałeś nieprawidłowe hasło. \n\nSpróbuj ponownie za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Narysowałeś nieprawidłowy wzór odblokowania <xliff:g id="NUMBER_0">%d</xliff:g> razy. \n\nSpróbuj ponownie za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Po raz <xliff:g id="NUMBER_0">%d</xliff:g> próbowałeś nieprawidłowo odblokować tablet. Po kolejnych <xliff:g id="NUMBER_1">%d</xliff:g> nieudanych próbach tablet zostanie zresetowany do ustawień fabrycznych, a wszystkie dane użytkownika zostaną utracone."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Podałeś nieprawidłowe dane odblokowania telewizora <xliff:g id="NUMBER_0">%d</xliff:g> razy. Po jeszcze <xliff:g id="NUMBER_1">%d</xliff:g> nieudanych próbach telewizor zostanie zresetowany do stanu fabrycznego, a wszystkie dane użytkownika zostaną skasowane."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Po raz <xliff:g id="NUMBER_0">%d</xliff:g> próbowałeś nieprawidłowo odblokować telefon. Po kolejnych <xliff:g id="NUMBER_1">%d</xliff:g> nieudanych próbach telefon zostanie zresetowany do ustawień fabrycznych, a wszystkie dane użytkownika zostaną utracone."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Wpisałeś nieprawidłowy kod PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> razy. \n\nSpróbuj ponownie za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Po raz <xliff:g id="NUMBER_0">%1$d</xliff:g> wpisałeś nieprawidłowe hasło. \n\nSpróbuj ponownie za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Narysowałeś nieprawidłowy wzór odblokowania <xliff:g id="NUMBER_0">%1$d</xliff:g> razy. \n\nSpróbuj ponownie za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Po raz <xliff:g id="NUMBER_0">%1$d</xliff:g> próbowałeś nieprawidłowo odblokować tablet. Po kolejnych <xliff:g id="NUMBER_1">%2$d</xliff:g> nieudanych próbach tablet zostanie zresetowany do ustawień fabrycznych, a wszystkie dane użytkownika zostaną utracone."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Podałeś nieprawidłowe dane odblokowania telewizora <xliff:g id="NUMBER_0">%1$d</xliff:g> razy. Po jeszcze <xliff:g id="NUMBER_1">%2$d</xliff:g> nieudanych próbach telewizor zostanie zresetowany do stanu fabrycznego, a wszystkie dane użytkownika zostaną skasowane."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Po raz <xliff:g id="NUMBER_0">%1$d</xliff:g> próbowałeś nieprawidłowo odblokować telefon. Po kolejnych <xliff:g id="NUMBER_1">%2$d</xliff:g> nieudanych próbach telefon zostanie zresetowany do ustawień fabrycznych, a wszystkie dane użytkownika zostaną utracone."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Po raz <xliff:g id="NUMBER">%d</xliff:g> próbowałeś nieprawidłowo odblokować tablet. Tablet zostanie teraz zresetowany do ustawień fabrycznych."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Podałeś nieprawidłowe dane odblokowania telewizora <xliff:g id="NUMBER">%d</xliff:g> razy. Zostanie on teraz zresetowany do stanu fabrycznego."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Po raz <xliff:g id="NUMBER">%d</xliff:g> próbowałeś nieprawidłowo odblokować telefon. Telefon zostanie teraz zresetowany do ustawień fabrycznych."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Po raz <xliff:g id="NUMBER_0">%d</xliff:g> nieprawidłowo narysowałeś wzór odblokowania. Po kolejnych <xliff:g id="NUMBER_1">%d</xliff:g> nieudanych próbach konieczne będzie odblokowanie tabletu przy użyciu danych logowania na konto Google.\n\n Spróbuj ponownie za <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Wzór odblokowania narysowałeś niepoprawnie <xliff:g id="NUMBER_0">%d</xliff:g> razy. Po jeszcze <xliff:g id="NUMBER_1">%d</xliff:g> nieudanych próbach trzeba będzie odblokować telewizor przy użyciu konta e-mail.\n\n Spróbuj ponownie za <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Po raz <xliff:g id="NUMBER_0">%d</xliff:g> nieprawidłowo narysowałeś wzór odblokowania. Po kolejnych <xliff:g id="NUMBER_1">%d</xliff:g> nieudanych próbach konieczne będzie odblokowanie telefonu przy użyciu danych logowania na konto Google.\n\n Spróbuj ponownie za <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Po raz <xliff:g id="NUMBER_0">%1$d</xliff:g> nieprawidłowo narysowałeś wzór odblokowania. Po kolejnych <xliff:g id="NUMBER_1">%2$d</xliff:g> nieudanych próbach konieczne będzie odblokowanie tabletu przy użyciu danych logowania na konto Google.\n\n Spróbuj ponownie za <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Wzór odblokowania narysowałeś niepoprawnie <xliff:g id="NUMBER_0">%1$d</xliff:g> razy. Po jeszcze <xliff:g id="NUMBER_1">%2$d</xliff:g> nieudanych próbach trzeba będzie odblokować telewizor przy użyciu konta e-mail.\n\n Spróbuj ponownie za <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Po raz <xliff:g id="NUMBER_0">%1$d</xliff:g> nieprawidłowo narysowałeś wzór odblokowania. Po kolejnych <xliff:g id="NUMBER_1">%2$d</xliff:g> nieudanych próbach konieczne będzie odblokowanie telefonu przy użyciu danych logowania na konto Google.\n\n Spróbuj ponownie za <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Usuń"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Zwiększyć głośność ponad zalecany poziom?\n\nSłuchanie głośno przez długi czas może uszkodzić Twój słuch."</string>
@@ -1504,6 +1502,7 @@
<item quantity="one">1 godz.</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (następny alarm)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Dopóki nie wyłączysz"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Do wyłączenia Nie przeszkadzać"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-pt-rBR-watch/strings.xml b/core/res/res/values-pt-rBR-watch/strings.xml
index a5e4a11..e205326 100644
--- a/core/res/res/values-pt-rBR-watch/strings.xml
+++ b/core/res/res/values-pt-rBR-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"App <xliff:g id="NUMBER_0">%1$d</xliff:g> de <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Sensores"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"acessar seus contatos"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"acessar a localização deste relógio"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"acessar sua agenda"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"enviar e ver mensagens SMS"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"acessar fotos, mídia e arquivos do seu relógio"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"grave áudio"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"tire fotos e grave vídeos"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"fazer e gerenciar chamadas telefônicas"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"acessar dados do sensor sobre seus sinais vitais"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"ser a barra de status"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"acessar os sensores corporais (como monitores de frequência cardíaca)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"acessar a localização precisa (GPS e com base na rede)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"acessar a localização aproximada (com base na rede)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"enviar comandos para o SIM"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"ter acesso total à rede"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"gerenciar proprietários de perfis e de dispositivos"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"alterar estado do WiMAX"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"receber status de transferência do Android Beam"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"rotear saída de mídia"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"ler sessões de instalação"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"solicitar pacotes de instalação"</string>
</resources>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 2f6c8bf..4d88275 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Inclui dados pessoais, como números de cartão de crédito e senhas."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"desativar ou modificar a barra de status"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Permite que o app desative a barra de status ou adicione e remova ícones do sistema."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"barra de status"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"ser a barra de status"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Permite que o app seja a barra de status."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"expandir/recolher barra de status"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Permite que o app expanda ou recolha a barra de status."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Permite que o app receba e processe mensagens WAP. Esta permissão inclui a capacidade de monitorar ou excluir mensagens enviadas para você sem mostrá-las para você."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"recuperar apps em execução"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Permite que o app obtenha informações sobre tarefas em execução atuais e recentes. Pode permitir que o app descubra informações sobre os apps usados no dispositivo."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Gerenciar proprietários de perfis e de dispositivos"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"gerenciar proprietários de perfis e de dispositivos"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Permitir que os apps definam os proprietários de perfis e de dispositivos."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"reordenar os apps em execução"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Permite que o app mova tarefas para o primeiro plano e o plano de fundo, sem sua intervenção."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Permite que o app modifique o registro de chamadas de seu tablet, incluindo dados sobre chamadas recebidas e efetuadas. Apps maliciosos podem usar esta permissão para apagar ou modificar seu registro de chamadas."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Permite que o app modifique o registro de chamadas da sua TV, incluindo dados sobre chamadas recebidas e efetuadas. Apps maliciosos podem usá-lo para apagar ou modificar seu registro de chamadas."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Permite que o app modifique o registro de chamadas de seu telefone, incluindo dados sobre chamadas recebidas e efetuadas. Apps maliciosos podem usar esta permissão para apagar ou modificar seu registro de chamadas."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"sensores corporais"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"acessar sensores corporais (como monitores de frequência cardíaca)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Permite que o app acesse dados de sensores que monitoram sua condição física, como a frequência cardíaca."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"ler compromissos e informações confidenciais"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Permite que o app leia todos os eventos do calendário armazenados no tablet, incluindo os de amigos ou colegas de trabalho. Pode permitir que o app compartilhe ou salve os dados do calendário, independentemente de sua confidencialidade."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Permite que o app adicione, remova e altere eventos que você pode modificar em seu telefone, incluindo os de amigos e colegas de trabalho. Isso pode permitir que o app envie mensagens que parecem ser de autoria do proprietário do calendário, ou modifique eventos sem conhecimento do proprietário."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"acessar comandos extras do provedor de localização"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Permite que o app acesse comandos do provedor não relacionados à localização. Isso pode permitir que o app interfira no funcionamento do GPS ou de outras fontes de localização."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"localização precisa (GPS e com base na rede)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"acessar localização precisa (GPS e com base na rede)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Permite que o app acesse sua localização exata por meio do sistema de posicionamento global (GPS) ou de fontes de localização da rede, como torres de celulares e redes Wi-Fi. Esses serviços de localização devem estar ativados e disponíveis para que sejam usados pelo app. O app pode usar esta permissão para determinar onde você está, além de consumir mais bateria."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"localização aproximada (com base na rede)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"acessar localização aproximada (com base na rede)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Permite que o app acesse sua localização aproximada por meio do sistema de posicionamento global (GPS) ou de fontes de localização da rede, como torres de celulares e redes Wi-Fi. Esses serviços de localização devem estar ativados e disponíveis para que sejam usados pelo app. O app pode usar esta permissão para determinar aproximadamente onde você está."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"alterar as suas configurações de áudio"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Permite que o app modifique configurações de áudio globais como volume e alto-falantes de saída."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"gravar áudio"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Permite que o app grave áudio com o microfone. Esta permissão autoriza o app a gravar áudio a qualquer momento, sem sua confirmação."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"comunicação com sim"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"enviar comandos para o SIM"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Permite que o app envie comandos ao SIM. Muito perigoso."</string>
<string name="permlab_camera" msgid="3616391919559751192">"tirar fotos e gravar vídeos"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Permite que o app tire fotos e filme vídeos com a câmera. Esta permissão autoriza o app a usar a câmera a qualquer momento sem sua confirmação."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Permite que o app obtenha a lista de contas conhecidas pelo telefone. Isso pode incluir todas as contas criadas pelos apps instalados."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"ver conexões de rede"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Permite que o app acesse informações sobre conexões de rede, como as redes existentes e conectadas."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"acesso total à rede"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"ter acesso total à rede"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Permite que o app crie soquetes de rede e utilize protocolos de rede personalizados. O navegador e outros apps fornecem meios de enviar dados para a Internet, e por isso esta permissão não é necessária para enviar os dados."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"alterar conectividade da rede"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Permite que o app altere o estado de conectividade de rede."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Permite que um app configure o telefone Bluetooth local, descubra e emparelhe com dispositivos remotos."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"conectar e desconectar do WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Permite que o app determine se o WiMAX está ativado e acesse informações sobre as redes WiMAX conectadas."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Alterar estado do WiMAX"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"alterar estado do WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Permite que o app conecte e desconecte o tablet de redes WiMAX."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Permite que o app se conecte à TV e desconecte-a de redes WiMAX."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Permite que o app conecte e desconecte o telefone de redes WiMAX."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Permite que o app modifique os parâmetros de calibragem da tela sensível ao toque. Não deve ser necessário para apps normais."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"acessar certificados de DRM"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Permite que o app provisione e use certificados de DRM. Não deve ser necessário para apps comuns."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Receber status de transferência do Android Beam"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"receber status de transferência do Android Beam"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Permite que este app receba informações sobre as atuais transferências do Android Beam"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"remover certificados de DRM"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Permite que um app remova certificados de DRM. Não deve ser necessário para apps comuns."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Código PIN incorreto."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Para desbloquear, pressione Menu e, em seguida, 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Número de emergência"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Sem serviço."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Sem serviço"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Tela bloqueada."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Pressione Menu para desbloquear ou fazer uma chamada de emergência."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Pressione Menu para desbloquear."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Consulte o Guia do usuário ou entre em contato com o Serviço de atendimento ao cliente."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"O cartão SIM está bloqueado."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Desbloqueando o cartão SIM…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Você desenhou seu padrão de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. \n\nTente novamente em <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Você digitou sua senha incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. \n\nTente novamente em <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Você digitou seu PIN incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes.\n\nTente novamente em <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Você desenhou sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Se fizer mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas incorretas, será solicitado que você use o login do Google para desbloquear seu tablet.\n\n Tente novamente em <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Você desenhou seu padrão de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Depois de mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas sem sucesso, será pedido que você desbloqueie sua TV usando seu login do Google.\n\n Tente novamente em <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Você desenhou sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Se fizer mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas incorretas, será solicitado que você use o login do Google para desbloquear.\n\n Tente novamente em <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Você tentou desbloquear incorretamente o tablet <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Após mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas malsucedidas, o tablet será redefinido para o padrão de fábrica e todos os dados do usuário serão perdidos."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Você tentou desbloquear a TV de forma incorreta <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Depois de mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas sem sucesso, a TV será redefinida para os padrões de fábrica e todos os dados do usuário serão perdidos."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Você tentou desbloquear incorretamente o telefone <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Após mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas malsucedidas, o telefone será redefinido para o padrão de fábrica e todos os dados do usuário serão perdidos."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Você desenhou seu padrão de desbloqueio incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. \n\nTente novamente em <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Você digitou sua senha incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. \n\nTente novamente em <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Você digitou seu PIN incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes.\n\nTente novamente em <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Você desenhou sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Se fizer mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas incorretas, será solicitado que você use o login do Google para desbloquear seu tablet.\n\n Tente novamente em <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Você desenhou seu padrão de desbloqueio incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Depois de mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas sem sucesso, será pedido que você desbloqueie sua TV usando seu login do Google.\n\n Tente novamente em <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Você desenhou sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Se fizer mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas incorretas, será solicitado que você use o login do Google para desbloquear.\n\n Tente novamente em <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Você tentou desbloquear incorretamente o tablet <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Após mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas malsucedidas, o tablet será redefinido para o padrão de fábrica e todos os dados do usuário serão perdidos."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Você tentou desbloquear a TV de forma incorreta <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Depois de mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas sem sucesso, a TV será redefinida para os padrões de fábrica e todos os dados do usuário serão perdidos."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Você tentou desbloquear incorretamente o telefone <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Após mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas malsucedidas, o telefone será redefinido para o padrão de fábrica e todos os dados do usuário serão perdidos."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Você tentou desbloquear incorretamente o tablet <xliff:g id="NUMBER">%d</xliff:g> vezes. O tablet será redefinido para o padrão de fábrica."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Você tentou desbloquear a TV de forma incorreta <xliff:g id="NUMBER">%d</xliff:g> vezes. A TV será redefinida agora para os padrões de fábrica."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Você tentou desbloquear incorretamente o telefone <xliff:g id="NUMBER">%d</xliff:g> vezes. O telefone será redefinido para o padrão de fábrica."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"palavra"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"link"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"linha"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Falha no teste de fábrica"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"A ação FACTORY_TEST é suportada apenas para pacotes instalados em /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Nenhum pacote que forneça a ação FACTORY_TEST foi encontrado."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Formatando..."</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Não inserida"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Nenhum atividade correspondente foi encontrada."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Rotear saída de mídia"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"rotear saída de mídia"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Permite que um app faça o roteamento de saída de mídia para outros dispositivos externos."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Ler sessões de instalação"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"ler sessões de instalação"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Permite que um app leia sessões de instalação. Isso permite que ele veja detalhes sobre as instalações de pacote ativas."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Solicitar instalação de pacotes"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"solicitar pacotes de instalação"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Permite que um app solicite a instalação de pacotes."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Toque duas vezes para controlar o zoom"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Não foi possível adicionar widget."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Nome de usuário ou senha inválidos."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Esqueceu seu nome de usuário ou senha?\nAcesse "<b>"google.com.br/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Verificando a conta..."</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Você digitou seu PIN incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. \n\nTente novamente em <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Você digitou sua senha incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. \n\nTente novamente em <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Você desenhou sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. \n\nTente novamente em <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Você tentou desbloquear incorretamente o tablet <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Após mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas malsucedidas, o tablet será redefinido para o padrão de fábrica e todos os dados do usuário serão perdidos."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Você tentou desbloquear a TV de forma incorreta <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Depois de mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas sem sucesso, a TV será redefinida para os padrões de fábrica e todos os dados do usuário serão perdidos."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Você tentou desbloquear incorretamente o telefone <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Após mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas malsucedidas, o telefone será redefinido para o padrão de fábrica e todos os dados do usuário serão perdidos."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Você digitou seu PIN incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. \n\nTente novamente em <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Você digitou sua senha incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. \n\nTente novamente em <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Você desenhou sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. \n\nTente novamente em <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Você tentou desbloquear incorretamente o tablet <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Após mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas malsucedidas, o tablet será redefinido para o padrão de fábrica e todos os dados do usuário serão perdidos."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Você tentou desbloquear a TV de forma incorreta <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Depois de mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas sem sucesso, a TV será redefinida para os padrões de fábrica e todos os dados do usuário serão perdidos."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Você tentou desbloquear incorretamente o telefone <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Após mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas malsucedidas, o telefone será redefinido para o padrão de fábrica e todos os dados do usuário serão perdidos."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Você tentou desbloquear incorretamente o tablet <xliff:g id="NUMBER">%d</xliff:g> vezes. O tablet será redefinido para o padrão de fábrica."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Você tentou desbloquear a TV de forma incorreta <xliff:g id="NUMBER">%d</xliff:g> vezes. A TV será redefinida agora para os padrões de fábrica."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Você tentou desbloquear incorretamente o telefone <xliff:g id="NUMBER">%d</xliff:g> vezes. O telefone será redefinido para o padrão de fábrica."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Você desenhou sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Se fizer mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas incorretas, será solicitado que você use o login do Google para desbloquear seu tablet.\n\n Tente novamente em <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Você desenhou seu padrão de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Depois de mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas sem sucesso, será pedido que você desbloqueie sua TV usando uma conta de e-mail.\n\n Tente novamente em <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Você desenhou sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Se fizer mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas incorretas, será solicitado que você use o login do Google para desbloquear.\n\n Tente novamente em <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Você desenhou sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Se fizer mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas incorretas, será solicitado que você use o login do Google para desbloquear seu tablet.\n\n Tente novamente em <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Você desenhou seu padrão de desbloqueio incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Depois de mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas sem sucesso, será pedido que você desbloqueie sua TV usando uma conta de e-mail.\n\n Tente novamente em <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Você desenhou sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Se fizer mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas incorretas, será solicitado que você use o login do Google para desbloquear.\n\n Tente novamente em <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Remover"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Aumentar o volume acima do nível recomendado?\n\nOuvir em volume alto por longos períodos pode danificar sua audição."</string>
@@ -1470,6 +1468,7 @@
<item quantity="other">Por %d horas</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Até às <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Até <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (próximo alarme)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Até você desativar"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Até que você desative \"Não perturbe\""</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-pt-rPT-watch/strings.xml b/core/res/res/values-pt-rPT-watch/strings.xml
index 0dd354a..c992bd6 100644
--- a/core/res/res/values-pt-rPT-watch/strings.xml
+++ b/core/res/res/values-pt-rPT-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Aplicação <xliff:g id="NUMBER_0">%1$d</xliff:g> de <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Sensores"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"aceder aos contactos"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"aceder à localização deste relógio"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"aceder ao calendário"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"enviar e ver mensagens SMS"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"aceder a fotos, a multimédia e a ficheiros no relógio"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"gravar áudio"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"tirar fotos e gravar vídeos"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"fazer e gerir chamadas"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"aceder a dados do sensor acerca dos seus sinais vitais"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"ser apresentada na barra de estado"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"aceder aos sensores corporais (como monitores do ritmo cardíaco)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"aceder à localização exata (baseada no GPS e na rede)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"aceder à localização aproximada (baseada na rede)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"enviar comandos para o SIM"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"ter acesso total à rede"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"gerir proprietários de perfis e de dispositivos"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"alterar estado do WiMAX"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"receber estado de transferências do Android Beam"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"encaminhar saída de som multimédia"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"ler sessões de instalação"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"solicitar a instalação de pacotes"</string>
</resources>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index e14303d..863b6e8 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Inclui dados pessoais, como números de cartões de crédito e palavras-passe."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"desativar ou modificar barra de estado"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Permite à aplicação desativar a barra de estado ou adicionar e remover ícones do sistema."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"barra de estado"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"ser apresentada na barra de estado"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Permite que a aplicação seja apresentada na barra de estado."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"expandir/fechar barra de estado"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Permite à aplicação expandir ou fechar a barra de estado."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Permite que a aplicação receba e processe mensagens WAP. Esta autorização inclui a capacidade de monitorizar ou eliminar mensagens enviadas para si sem as apresentar."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"obter aplicações em execução"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Permite que a aplicação recupere informações acerca de tarefas executadas atual e recentemente. Isto pode permitir que a aplicação descubra informações acerca de quais as aplicações utilizadas no dispositivo."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Gerir proprietários de perfis e do dispositivo"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"gerir proprietários de perfis e de dispositivos"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Permite que as aplicações definam proprietários de perfis e o proprietário do dispositivo."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"reordenar as aplicações em execução"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Permite que a aplicação mova tarefas para primeiro e segundo plano. A aplicação poderá fazê-lo sem qualquer entrada do utilizador."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Permite à aplicação modificar o registo de chamadas do tablet, incluindo os dados sobre as chamadas recebidas e efetuadas. As aplicações maliciosas podem utilizar esta funcionalidade para apagar ou modificar o registo de chamadas."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Permite à aplicação modificar o registo de chamadas da sua TV, incluindo os dados sobre as chamadas recebidas e efetuadas. As aplicações maliciosas podem utilizar esta funcionalidade para apagar ou modificar o registo de chamadas."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Permite à aplicação modificar o registo de chamadas do telemóvel, incluindo os dados sobre as chamadas recebidas e efetuadas. As aplicações maliciosas podem utilizar esta funcionalidade para apagar ou modificar o seu registo de chamadas."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"sensores corporais (como monitores do ritmo cardíaco)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"aceder a sensores corp. (como monit. do ritmo cardíaco)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Permite que a aplicação aceda a dados de sensores que monitorizam a sua condição física, como o ritmo cardíaco."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"ler eventos do calendário, para além de informações confidenciais"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Permite que a aplicação leia todos os eventos do calendário guardados no tablet, incluindo os de amigos ou colegas de trabalho. Pode permitir que a aplicação partilhe ou guarde dados do calendário, independentemente da confidencialidade ou sensibilidade."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Permite que a aplicação adicione, remova e altere eventos que pode modificar no telemóvel, incluindo eventos relacionados com amigos ou colegas de trabalho. Pode permitir que a aplicação envie mensagens que parecem ser enviadas pelos proprietários dos calendários ou modifique eventos sem o conhecimento do proprietário."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"aceder a comandos adicionais do fornecedor de localização"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Permite que a aplicação aceda a comandos adicionais do fornecedor de localização. Esta opção pode permitir que a aplicação interfira com o funcionamento do GPS ou de outras fontes de localização."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"localização exata (baseada no GPS e na rede)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"aceder à localização exata (baseada no GPS e na rede)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Permite que a aplicação obtenha a sua localização exata através do Sistema de Posicionamento Global (GPS) ou das fontes de localização da rede, tais como torres de telemóvel e Wi-Fi. Estes serviços de localização têm de estar ativados e disponíveis no dispositivo para que a aplicação os utilize. As aplicações poderão utilizá-los para determinar a sua localização e poderão consumir mais energia da bateria."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"localização aproximada (baseada na rede)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"aceder à localização aproximada (baseada na rede)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Permite que a aplicação obtenha a sua localização aproximada. Esta localização é gerada pelos serviços de localização, que utilizam fontes de localização da rede, tais como torres de telemóvel e Wi-Fi. Estes serviços de localização têm de estar ativados e disponíveis no dispositivo para que a aplicação os utilize. As aplicações poderão utilizá-los para determinar a sua localização aproximada."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"alterar as suas definições de áudio"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Permite que a aplicação modifique definições de áudio globais, tais como o volume e qual o altifalante utilizado para a saída de som."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"gravar áudio"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Permite que a aplicação grave áudio com o microfone. Esta autorização permite que a aplicação grave áudio em qualquer altura sem a confirmação do utilizador."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"comunicação com o SIM"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"enviar comandos para o SIM"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Permite que a aplicação envie comandos para o SIM. Esta ação é muito perigosa."</string>
<string name="permlab_camera" msgid="3616391919559751192">"tirar fotografias e vídeos"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Permite que a aplicação tire fotografias e grave vídeos com a câmara. Esta autorização permite que a aplicação utilize a câmara sem a sua confirmação em qualquer altura."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Permite que a aplicação obtenha a lista de contas reconhecidas pelo telemóvel. Pode incluir qualquer conta criada pelas aplicações instaladas."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"ver ligações de rede"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Permite que a aplicação visualize informações acerca das ligações de rede como, por exemplo, que redes que existem e estão ligadas."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"acesso total à rede"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"ter acesso total à rede"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Permite que a aplicação crie ligações de rede e utilize protocolos de rede personalizados. O navegador e outras aplicações fornecem meios para enviar dados para a Internet, pelo que esta autorização não é necessária para enviar dados para a Internet."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"mudar conectividade de rede"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Permite que a aplicação altere o estado de conectividade da rede."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Permite que a aplicação configure o telemóvel Bluetooth local, bem como descobrir e emparelhar com dispositivos remotos."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"ligar e desligar do WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Permite que a aplicação determine se o WiMAX está ativado e aceda a informações acerca de qualquer rede WiMAX que esteja ligada."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Alterar estado do WiMAX"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"alterar estado do WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Permite que a aplicação ligue e desligue o tablet de redes WiMAX."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Permite à aplicação ligar e desligar a TV de redes WiMAX."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Permite que a aplicação ligue e desligue o telemóvel de redes WiMAX."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Permite à aplicação modificar os parâmetros de calibragem do ecrã tátil. Esta funcionalidade nunca deverá ser necessária para aplicações normais."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"Aceder a certificados DRM"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Permite que uma aplicação forneça e utilize certificados DRM. Nunca deverá ser necessário para aplicações normais."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Receber estado de transferência do Android Beam"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"receber estado de transferência do Android Beam"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Permite que esta aplicação receba informações acerca das transferências atuais do Android Beam"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"remover certificados DRM"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Permite que uma aplicação remova certificados DRM. Nunca deverá ser necessário para aplicações normais."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Código PIN incorreto."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Para desbloquear, prima Menu e, em seguida, 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Número de emergência"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Nenhum serviço"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Sem rede móvel"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Ecrã bloqueado."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Prima Menu para desbloquear ou efectuar uma chamada de emergência."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Prima Menu para desbloquear."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Consulte o Manual de utilizador ou contacte a Assistência a Clientes."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"O cartão SIM está bloqueado."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"A desbloquear cartão SIM..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Desenhou a sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. \n\nTente novamente dentro de <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Escreveu a sua palavra-passe incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. \n\n Tente novamente dentro de <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Escreveu o seu número PIN incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. \n\n Tente novamente dentro de <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Desenhou a sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Após outras <xliff:g id="NUMBER_1">%d</xliff:g> tentativas sem sucesso, ser-lhe-á pedido para desbloquear o tablet com as suas credenciais de início de sessão do Google.\n\n Tente novamente dentro de <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"O utilizador desenhou incorretamente a sua sequência de desbloqueio <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Após mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas sem êxito, é-lhe pedido que desbloqueie a sua TV através do seu início de sessão Google.\n\n Tente novamente dentro de <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Desenhou a sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Após outras <xliff:g id="NUMBER_1">%d</xliff:g> tentativas sem sucesso, ser-lhe-á pedido para desbloquear o telemóvel com as suas credenciais de início de sessão do Google.\n\n Tente novamente dentro de <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Tentou desbloquear o tablet <xliff:g id="NUMBER_0">%d</xliff:g> vezes de forma incorreta. Após mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativa(s) sem êxito, as definições de origem do tablet serão repostas e todos os dados de utilizador serão perdidos."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"O utilizador tentou desbloquear incorretamente a TV <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Após mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas sem êxito, a TV é reposta para as predefinições de fábrica e todos os dados do utilizador são perdidos."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Tentou desbloquear o telemóvel <xliff:g id="NUMBER_0">%d</xliff:g> vezes de forma incorreta. Após mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativa(s) sem êxito, as definições de origem do telemóvel serão repostas e todos os dados de utilizador serão perdidos."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Desenhou a sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. \n\nTente novamente dentro de <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Escreveu a sua palavra-passe incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. \n\n Tente novamente dentro de <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Escreveu o seu número PIN incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. \n\n Tente novamente dentro de <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Desenhou a sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Após outras <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas sem sucesso, ser-lhe-á pedido para desbloquear o tablet com as suas credenciais de início de sessão do Google.\n\n Tente novamente dentro de <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"O utilizador desenhou incorretamente a sua sequência de desbloqueio <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Após mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas sem êxito, é-lhe pedido que desbloqueie a sua TV através do seu início de sessão Google.\n\n Tente novamente dentro de <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Desenhou a sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Após outras <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas sem sucesso, ser-lhe-á pedido para desbloquear o telemóvel com as suas credenciais de início de sessão do Google.\n\n Tente novamente dentro de <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Tentou desbloquear o tablet <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes de forma incorreta. Após mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativa(s) sem êxito, as definições de origem do tablet serão repostas e todos os dados de utilizador serão perdidos."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"O utilizador tentou desbloquear incorretamente a TV <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Após mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas sem êxito, a TV é reposta para as predefinições de fábrica e todos os dados do utilizador são perdidos."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Tentou desbloquear o telemóvel <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes de forma incorreta. Após mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativa(s) sem êxito, as definições de origem do telemóvel serão repostas e todos os dados de utilizador serão perdidos."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Tentou desbloquear o tablet <xliff:g id="NUMBER">%d</xliff:g> vezes de forma incorreta, pelo que serão repostas as respetivas definições de origem."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"O utilizador tentou desbloquear incorretamente a TV <xliff:g id="NUMBER">%d</xliff:g> vezes. A TV será agora reposta para as predefinições de fábrica."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Tentou desbloquear o telemóvel <xliff:g id="NUMBER">%d</xliff:g> vezes de forma incorreta, pelo que serão repostas as respetivas definições de origem."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"palavra"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"link"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"linha"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"O teste de fábrica falhou"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"A acção FACTORY_TEST apenas é suportada para pacotes instalados em /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Não foi localizado qualquer pacote que forneça a acção FACTORY_TEST."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"A formatar..."</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Não inserido"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Não foi encontrada nenhuma atividade correspondente."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Encaminhar saída de som multimédia"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"encaminhar saída de som multimédia"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Permite que a aplicação encaminhe a saída de som multimédia para outros dispositivos externos."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Ler sessões de instalação"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"ler sessões de instalação"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Permite que uma aplicação leia sessões de instalação. Isto permite que veja detalhes acerca de instalações de pacotes ativas."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Solicitar a instalação de pacotes"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"solicitar pacotes de instalação"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Permite que uma aplicação solicite a instalação de pacotes."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Toque duas vezes para controlar o zoom"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Não foi possível adicionar widget."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Nome de utilizador ou palavra-passe inválidos."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Esqueceu-se do nome de utilizador ou da palavra-passe?\nAceda a "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"A verificar a conta…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Escreveu o PIN incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. \n\nTente novamente dentro de <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Escreveu a palavra-passe incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. \n\nTente novamente dentro de <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Desenhou a sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. \n\nTente novamente dentro de <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Tentou desbloquear o tablet <xliff:g id="NUMBER_0">%d</xliff:g> vezes de forma incorreta. Depois de mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas sem êxito, as definições de origem do telemóvel serão repostas e todos os dados do utilizador serão perdidos."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"O utilizador tentou desbloquear incorretamente a TV <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Após mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas sem êxito, a TV é reposta para as predefinições de fábrica e todos os dados do utilizador são perdidos."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Tentou desbloquear o telemóvel <xliff:g id="NUMBER_0">%d</xliff:g> vezes de forma incorreta. Depois de mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas sem êxito, as definições de origem do telemóvel serão repostas e todos os dados do utilizador serão perdidos."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Escreveu o PIN incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. \n\nTente novamente dentro de <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Escreveu a palavra-passe incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. \n\nTente novamente dentro de <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Desenhou a sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. \n\nTente novamente dentro de <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Tentou desbloquear o tablet <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes de forma incorreta. Depois de mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas sem êxito, as definições de origem do telemóvel serão repostas e todos os dados do utilizador serão perdidos."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"O utilizador tentou desbloquear incorretamente a TV <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Após mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas sem êxito, a TV é reposta para as predefinições de fábrica e todos os dados do utilizador são perdidos."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Tentou desbloquear o telemóvel <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes de forma incorreta. Depois de mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas sem êxito, as definições de origem do telemóvel serão repostas e todos os dados do utilizador serão perdidos."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Tentou desbloquear o tablet <xliff:g id="NUMBER">%d</xliff:g> vezes de forma incorreta, pelo que será reposta a predefinição de fábrica."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"O utilizador tentou desbloquear incorretamente a TV <xliff:g id="NUMBER">%d</xliff:g> vezes. A TV será agora reposta para as predefinições de fábrica."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Tentou desbloquear o telemóvel <xliff:g id="NUMBER">%d</xliff:g> vezes de forma incorreta, pelo que será reposta a predefinição de fábrica."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Desenhou a sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Depois de mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas sem sucesso, ser-lhe-á pedido para desbloquear o tablet através de uma conta de email.\n\n Tente novamente dentro de <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"O utilizador desenhou incorretamente a sua sequência de desbloqueio <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Após mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas sem êxito, é-lhe pedido que desbloqueie a sua TV através de uma conta de email.\n\n Tente novamente dentro de <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Desenhou a sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Depois de mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas sem sucesso, ser-lhe-á pedido para desbloquear o telemóvel através de uma conta de email.\n\n Tente novamente dentro de <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Desenhou a sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Depois de mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas sem sucesso, ser-lhe-á pedido para desbloquear o tablet através de uma conta de email.\n\n Tente novamente dentro de <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"O utilizador desenhou incorretamente a sua sequência de desbloqueio <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Após mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas sem êxito, é-lhe pedido que desbloqueie a sua TV através de uma conta de email.\n\n Tente novamente dentro de <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Desenhou a sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Depois de mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas sem sucesso, ser-lhe-á pedido para desbloquear o telemóvel através de uma conta de email.\n\n Tente novamente dentro de <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" - "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Remover"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Aumentar o volume acima do nível recomendado?\n\nOuvir com um volume elevado durante longos períodos poderá ser prejudicial para a sua audição."</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">Durante 1 h</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Até às <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Até às <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (próximo alarme)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Até que o utilizador desative"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Até desativar Não incomodar"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-pt-watch/strings.xml b/core/res/res/values-pt-watch/strings.xml
index a5e4a11..e205326 100644
--- a/core/res/res/values-pt-watch/strings.xml
+++ b/core/res/res/values-pt-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"App <xliff:g id="NUMBER_0">%1$d</xliff:g> de <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Sensores"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"acessar seus contatos"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"acessar a localização deste relógio"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"acessar sua agenda"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"enviar e ver mensagens SMS"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"acessar fotos, mídia e arquivos do seu relógio"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"grave áudio"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"tire fotos e grave vídeos"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"fazer e gerenciar chamadas telefônicas"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"acessar dados do sensor sobre seus sinais vitais"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"ser a barra de status"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"acessar os sensores corporais (como monitores de frequência cardíaca)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"acessar a localização precisa (GPS e com base na rede)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"acessar a localização aproximada (com base na rede)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"enviar comandos para o SIM"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"ter acesso total à rede"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"gerenciar proprietários de perfis e de dispositivos"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"alterar estado do WiMAX"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"receber status de transferência do Android Beam"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"rotear saída de mídia"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"ler sessões de instalação"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"solicitar pacotes de instalação"</string>
</resources>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 2f6c8bf..4d88275 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Inclui dados pessoais, como números de cartão de crédito e senhas."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"desativar ou modificar a barra de status"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Permite que o app desative a barra de status ou adicione e remova ícones do sistema."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"barra de status"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"ser a barra de status"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Permite que o app seja a barra de status."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"expandir/recolher barra de status"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Permite que o app expanda ou recolha a barra de status."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Permite que o app receba e processe mensagens WAP. Esta permissão inclui a capacidade de monitorar ou excluir mensagens enviadas para você sem mostrá-las para você."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"recuperar apps em execução"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Permite que o app obtenha informações sobre tarefas em execução atuais e recentes. Pode permitir que o app descubra informações sobre os apps usados no dispositivo."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Gerenciar proprietários de perfis e de dispositivos"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"gerenciar proprietários de perfis e de dispositivos"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Permitir que os apps definam os proprietários de perfis e de dispositivos."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"reordenar os apps em execução"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Permite que o app mova tarefas para o primeiro plano e o plano de fundo, sem sua intervenção."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Permite que o app modifique o registro de chamadas de seu tablet, incluindo dados sobre chamadas recebidas e efetuadas. Apps maliciosos podem usar esta permissão para apagar ou modificar seu registro de chamadas."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Permite que o app modifique o registro de chamadas da sua TV, incluindo dados sobre chamadas recebidas e efetuadas. Apps maliciosos podem usá-lo para apagar ou modificar seu registro de chamadas."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Permite que o app modifique o registro de chamadas de seu telefone, incluindo dados sobre chamadas recebidas e efetuadas. Apps maliciosos podem usar esta permissão para apagar ou modificar seu registro de chamadas."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"sensores corporais"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"acessar sensores corporais (como monitores de frequência cardíaca)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Permite que o app acesse dados de sensores que monitoram sua condição física, como a frequência cardíaca."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"ler compromissos e informações confidenciais"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Permite que o app leia todos os eventos do calendário armazenados no tablet, incluindo os de amigos ou colegas de trabalho. Pode permitir que o app compartilhe ou salve os dados do calendário, independentemente de sua confidencialidade."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Permite que o app adicione, remova e altere eventos que você pode modificar em seu telefone, incluindo os de amigos e colegas de trabalho. Isso pode permitir que o app envie mensagens que parecem ser de autoria do proprietário do calendário, ou modifique eventos sem conhecimento do proprietário."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"acessar comandos extras do provedor de localização"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Permite que o app acesse comandos do provedor não relacionados à localização. Isso pode permitir que o app interfira no funcionamento do GPS ou de outras fontes de localização."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"localização precisa (GPS e com base na rede)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"acessar localização precisa (GPS e com base na rede)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Permite que o app acesse sua localização exata por meio do sistema de posicionamento global (GPS) ou de fontes de localização da rede, como torres de celulares e redes Wi-Fi. Esses serviços de localização devem estar ativados e disponíveis para que sejam usados pelo app. O app pode usar esta permissão para determinar onde você está, além de consumir mais bateria."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"localização aproximada (com base na rede)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"acessar localização aproximada (com base na rede)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Permite que o app acesse sua localização aproximada por meio do sistema de posicionamento global (GPS) ou de fontes de localização da rede, como torres de celulares e redes Wi-Fi. Esses serviços de localização devem estar ativados e disponíveis para que sejam usados pelo app. O app pode usar esta permissão para determinar aproximadamente onde você está."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"alterar as suas configurações de áudio"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Permite que o app modifique configurações de áudio globais como volume e alto-falantes de saída."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"gravar áudio"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Permite que o app grave áudio com o microfone. Esta permissão autoriza o app a gravar áudio a qualquer momento, sem sua confirmação."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"comunicação com sim"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"enviar comandos para o SIM"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Permite que o app envie comandos ao SIM. Muito perigoso."</string>
<string name="permlab_camera" msgid="3616391919559751192">"tirar fotos e gravar vídeos"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Permite que o app tire fotos e filme vídeos com a câmera. Esta permissão autoriza o app a usar a câmera a qualquer momento sem sua confirmação."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Permite que o app obtenha a lista de contas conhecidas pelo telefone. Isso pode incluir todas as contas criadas pelos apps instalados."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"ver conexões de rede"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Permite que o app acesse informações sobre conexões de rede, como as redes existentes e conectadas."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"acesso total à rede"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"ter acesso total à rede"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Permite que o app crie soquetes de rede e utilize protocolos de rede personalizados. O navegador e outros apps fornecem meios de enviar dados para a Internet, e por isso esta permissão não é necessária para enviar os dados."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"alterar conectividade da rede"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Permite que o app altere o estado de conectividade de rede."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Permite que um app configure o telefone Bluetooth local, descubra e emparelhe com dispositivos remotos."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"conectar e desconectar do WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Permite que o app determine se o WiMAX está ativado e acesse informações sobre as redes WiMAX conectadas."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Alterar estado do WiMAX"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"alterar estado do WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Permite que o app conecte e desconecte o tablet de redes WiMAX."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Permite que o app se conecte à TV e desconecte-a de redes WiMAX."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Permite que o app conecte e desconecte o telefone de redes WiMAX."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Permite que o app modifique os parâmetros de calibragem da tela sensível ao toque. Não deve ser necessário para apps normais."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"acessar certificados de DRM"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Permite que o app provisione e use certificados de DRM. Não deve ser necessário para apps comuns."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Receber status de transferência do Android Beam"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"receber status de transferência do Android Beam"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Permite que este app receba informações sobre as atuais transferências do Android Beam"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"remover certificados de DRM"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Permite que um app remova certificados de DRM. Não deve ser necessário para apps comuns."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Código PIN incorreto."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Para desbloquear, pressione Menu e, em seguida, 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Número de emergência"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Sem serviço."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Sem serviço"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Tela bloqueada."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Pressione Menu para desbloquear ou fazer uma chamada de emergência."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Pressione Menu para desbloquear."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Consulte o Guia do usuário ou entre em contato com o Serviço de atendimento ao cliente."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"O cartão SIM está bloqueado."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Desbloqueando o cartão SIM…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Você desenhou seu padrão de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. \n\nTente novamente em <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Você digitou sua senha incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. \n\nTente novamente em <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Você digitou seu PIN incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes.\n\nTente novamente em <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Você desenhou sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Se fizer mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas incorretas, será solicitado que você use o login do Google para desbloquear seu tablet.\n\n Tente novamente em <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Você desenhou seu padrão de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Depois de mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas sem sucesso, será pedido que você desbloqueie sua TV usando seu login do Google.\n\n Tente novamente em <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Você desenhou sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Se fizer mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas incorretas, será solicitado que você use o login do Google para desbloquear.\n\n Tente novamente em <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Você tentou desbloquear incorretamente o tablet <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Após mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas malsucedidas, o tablet será redefinido para o padrão de fábrica e todos os dados do usuário serão perdidos."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Você tentou desbloquear a TV de forma incorreta <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Depois de mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas sem sucesso, a TV será redefinida para os padrões de fábrica e todos os dados do usuário serão perdidos."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Você tentou desbloquear incorretamente o telefone <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Após mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas malsucedidas, o telefone será redefinido para o padrão de fábrica e todos os dados do usuário serão perdidos."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Você desenhou seu padrão de desbloqueio incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. \n\nTente novamente em <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Você digitou sua senha incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. \n\nTente novamente em <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Você digitou seu PIN incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes.\n\nTente novamente em <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Você desenhou sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Se fizer mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas incorretas, será solicitado que você use o login do Google para desbloquear seu tablet.\n\n Tente novamente em <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Você desenhou seu padrão de desbloqueio incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Depois de mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas sem sucesso, será pedido que você desbloqueie sua TV usando seu login do Google.\n\n Tente novamente em <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Você desenhou sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Se fizer mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas incorretas, será solicitado que você use o login do Google para desbloquear.\n\n Tente novamente em <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Você tentou desbloquear incorretamente o tablet <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Após mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas malsucedidas, o tablet será redefinido para o padrão de fábrica e todos os dados do usuário serão perdidos."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Você tentou desbloquear a TV de forma incorreta <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Depois de mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas sem sucesso, a TV será redefinida para os padrões de fábrica e todos os dados do usuário serão perdidos."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Você tentou desbloquear incorretamente o telefone <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Após mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas malsucedidas, o telefone será redefinido para o padrão de fábrica e todos os dados do usuário serão perdidos."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Você tentou desbloquear incorretamente o tablet <xliff:g id="NUMBER">%d</xliff:g> vezes. O tablet será redefinido para o padrão de fábrica."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Você tentou desbloquear a TV de forma incorreta <xliff:g id="NUMBER">%d</xliff:g> vezes. A TV será redefinida agora para os padrões de fábrica."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Você tentou desbloquear incorretamente o telefone <xliff:g id="NUMBER">%d</xliff:g> vezes. O telefone será redefinido para o padrão de fábrica."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"palavra"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"link"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"linha"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Falha no teste de fábrica"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"A ação FACTORY_TEST é suportada apenas para pacotes instalados em /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Nenhum pacote que forneça a ação FACTORY_TEST foi encontrado."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Formatando..."</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Não inserida"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Nenhum atividade correspondente foi encontrada."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Rotear saída de mídia"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"rotear saída de mídia"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Permite que um app faça o roteamento de saída de mídia para outros dispositivos externos."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Ler sessões de instalação"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"ler sessões de instalação"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Permite que um app leia sessões de instalação. Isso permite que ele veja detalhes sobre as instalações de pacote ativas."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Solicitar instalação de pacotes"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"solicitar pacotes de instalação"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Permite que um app solicite a instalação de pacotes."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Toque duas vezes para controlar o zoom"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Não foi possível adicionar widget."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Nome de usuário ou senha inválidos."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Esqueceu seu nome de usuário ou senha?\nAcesse "<b>"google.com.br/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Verificando a conta..."</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Você digitou seu PIN incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. \n\nTente novamente em <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Você digitou sua senha incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. \n\nTente novamente em <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Você desenhou sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. \n\nTente novamente em <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Você tentou desbloquear incorretamente o tablet <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Após mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas malsucedidas, o tablet será redefinido para o padrão de fábrica e todos os dados do usuário serão perdidos."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Você tentou desbloquear a TV de forma incorreta <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Depois de mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas sem sucesso, a TV será redefinida para os padrões de fábrica e todos os dados do usuário serão perdidos."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Você tentou desbloquear incorretamente o telefone <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Após mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas malsucedidas, o telefone será redefinido para o padrão de fábrica e todos os dados do usuário serão perdidos."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Você digitou seu PIN incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. \n\nTente novamente em <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Você digitou sua senha incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. \n\nTente novamente em <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Você desenhou sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. \n\nTente novamente em <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Você tentou desbloquear incorretamente o tablet <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Após mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas malsucedidas, o tablet será redefinido para o padrão de fábrica e todos os dados do usuário serão perdidos."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Você tentou desbloquear a TV de forma incorreta <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Depois de mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas sem sucesso, a TV será redefinida para os padrões de fábrica e todos os dados do usuário serão perdidos."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Você tentou desbloquear incorretamente o telefone <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Após mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas malsucedidas, o telefone será redefinido para o padrão de fábrica e todos os dados do usuário serão perdidos."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Você tentou desbloquear incorretamente o tablet <xliff:g id="NUMBER">%d</xliff:g> vezes. O tablet será redefinido para o padrão de fábrica."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Você tentou desbloquear a TV de forma incorreta <xliff:g id="NUMBER">%d</xliff:g> vezes. A TV será redefinida agora para os padrões de fábrica."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Você tentou desbloquear incorretamente o telefone <xliff:g id="NUMBER">%d</xliff:g> vezes. O telefone será redefinido para o padrão de fábrica."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Você desenhou sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Se fizer mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas incorretas, será solicitado que você use o login do Google para desbloquear seu tablet.\n\n Tente novamente em <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Você desenhou seu padrão de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Depois de mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas sem sucesso, será pedido que você desbloqueie sua TV usando uma conta de e-mail.\n\n Tente novamente em <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Você desenhou sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Se fizer mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas incorretas, será solicitado que você use o login do Google para desbloquear.\n\n Tente novamente em <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Você desenhou sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Se fizer mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas incorretas, será solicitado que você use o login do Google para desbloquear seu tablet.\n\n Tente novamente em <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Você desenhou seu padrão de desbloqueio incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Depois de mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas sem sucesso, será pedido que você desbloqueie sua TV usando uma conta de e-mail.\n\n Tente novamente em <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Você desenhou sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Se fizer mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas incorretas, será solicitado que você use o login do Google para desbloquear.\n\n Tente novamente em <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Remover"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Aumentar o volume acima do nível recomendado?\n\nOuvir em volume alto por longos períodos pode danificar sua audição."</string>
@@ -1470,6 +1468,7 @@
<item quantity="other">Por %d horas</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Até às <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Até <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (próximo alarme)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Até você desativar"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Até que você desative \"Não perturbe\""</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-ro-watch/strings.xml b/core/res/res/values-ro-watch/strings.xml
index 9a8e4e0..dc50565 100644
--- a/core/res/res/values-ro-watch/strings.xml
+++ b/core/res/res/values-ro-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Aplic. <xliff:g id="NUMBER_0">%1$d</xliff:g> din <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Senzori"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"acceseze persoanele de contact"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"să acceseze locația acestui ceas"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"acceseze calendarul"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"trimită și să vadă mesajele SMS"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"să acceseze fotografiile, conținutul media și fișierele de pe ceas"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"înregistreze sunet"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"fotografieze și să înregistreze imagini"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"inițieze să și gestioneze apeluri telefonice"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"acceseze datele de la senzori despre semnele vitale"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"să fie bara de stare"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"să acceseze senzorii corporali (cum ar fi monitoarele cardiace)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"să acceseze locația exactă (bazată pe GPS și pe rețea)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"să acceseze locația aproximativă (bazată pe rețea)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"să trimită comenzi către SIM"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"să aibă acces deplin la rețea"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"să gestioneze profilul și proprietarii dispozitivului"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"să schimbe starea WiMAX"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"să primească starea transferului prin Android Beam"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"să direcționeze rezultatele media"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"să citească sesiunile de instalare"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"să solicite pachete de instalare"</string>
</resources>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 1faa14f..9af1378b 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -201,7 +201,7 @@
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"Telefonul dvs. se va închide."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Doriți să închideţi?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Reporniţi în modul sigur"</string>
- <string name="reboot_safemode_confirm" msgid="55293944502784668">"Doriți să reporniţi în modul sigur? Astfel vor fi dezactivate toate aplicațiile terţă parte pe care le-aţi instalat. Acestea vor fi restabilite când reporniţi din nou."</string>
+ <string name="reboot_safemode_confirm" msgid="55293944502784668">"Doriți să reporniţi în modul sigur? Astfel vor fi dezactivate toate aplicațiile terţă parte pe care le-ați instalat. Acestea vor fi restabilite când reporniţi din nou."</string>
<string name="recent_tasks_title" msgid="3691764623638127888">"Recente"</string>
<string name="no_recent_tasks" msgid="8794906658732193473">"Nu există aplicații recente."</string>
<string name="global_actions" product="tablet" msgid="408477140088053665">"Opţiuni tablet PC"</string>
@@ -255,7 +255,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Include date personale, cum ar fi numere ale cardurilor de credit sau parole."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"dezactivare sau modificare bare de stare"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Permite aplicației să dezactiveze bara de stare sau să adauge și să elimine pictograme de sistem."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"bară de stare"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"să fie bara de stare"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Permite aplicației să fie bară de stare."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"extindere/restrângere bară de stare"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Permite aplicației să extindă sau să restrângă bara de stare."</string>
@@ -283,7 +283,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Permite aplicației să primească și să proceseze mesaje WAP. Această permisiune include capacitatea de a monitoriza sau șterge mesajele care v-au fost trimise fără a vi le arăta."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"preluare aplicații care rulează"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Permite aplicației să preia informațiile despre activităţile care rulează în prezent și care au rulat recent. În acest fel, aplicația poate descoperi informații despre aplicațiile care sunt utilizate pe dispozitiv."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Gestionează proprietarii de profiluri și proprietarul dispozitivului"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"să gestioneze profilul și proprietarii dispozitivului"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Permite aplicațiilor să seteze proprietarii de profiluri și proprietarul dispozitivului."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"reordonare aplicații care rulează"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Permite aplicației să mute activităţile în prim-plan și în fundal. Aplicaţia poate face acest lucru fără aportul dvs."</string>
@@ -325,7 +325,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Permite aplicației să modifice jurnalul de apeluri al tabletei dvs., inclusiv datele despre apelurile primite sau efectuate. Aplicaţiile rău intenţionate pot utiliza această permisiune pentru a șterge sau pentru a modifica jurnalul dvs. de apeluri."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Permite aplicației să modifice jurnalul de apeluri al televizorului, inclusiv datele despre apelurile primite sau efectuate. Aplicațiile rău-intenționate pot utiliza această permisiune pentru a șterge sau pentru a modifica jurnalul de apeluri."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Permite aplicației să modifice jurnalul de apeluri al telefonului dvs., inclusiv datele despre apelurile primite sau efectuate. Aplicaţiile rău intenţionate pot utiliza această permisiune pentru a șterge sau pentru a modifica jurnalul dvs. de apeluri."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"senzori (ex.: senzori de ritm cardiac)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"să acceseze senzorii corporali (cum ar fi monitoarele cardiace)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Permite aplicației să acceseze date de la senzorii care vă monitorizează starea fizică, cum ar fi ritmul cardiac."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"citirea evenimentelor din calendar și a informaţiilor confidenţiale"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Permite aplicației să citească toate evenimentele din calendar stocate pe tabletă, inclusiv cele ale prietenilor sau colegilor. Acest lucru poate permite aplicației să distribuie sau să salveze datele din calendar, indiferent dacă acestea sunt confidenţiale sau sensibile."</string>
@@ -337,15 +337,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Permite aplicației să adauge, să elimine și să modifice evenimentele pe care le puteți modifica pe telefon, inclusiv cele ale prietenilor sau colegilor dvs. În acest fel, aplicația poate trimite mesaje care par să vină de la proprietarii calendarelor sau să modifice evenimentele fără știrea proprietarilor."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"accesare comenzi suplimentare ale furnizorului locației"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Permite aplicației să acceseze comenzi suplimentare pentru furnizorul locației. Aplicația ar putea să utilizeze această permisiune pentru a influența operațiile GPS sau ale altor surse de locații."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"locaţia exactă (bazată pe rețea și GPS)"</string>
- <string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Permite aplicației să obţină locaţia dvs. exactă utilizând sistemul GPS (Global Positioning System) sau surse de localizare prin rețele, cum ar fi cele prin turn de celule și Wi-Fi. Pentru a fi utilizate de aplicație, aceste servicii de localizare trebuie să fie activate și disponibile pe dispozitivul dvs. Aplicaţiile pot utiliza această permisiune pentru a determina locaţia dvs. și pot să consume mai multă energie a bateriei."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"locaţia aproximativă (bazată pe rețea)"</string>
- <string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Permite aplicației să obţină locaţia dvs. aproximativă. Această locație este dedusă de serviciile de localizare utilizând surse de localizare prin rețele, cum ar fi cele prin turn de celule și Wi-Fi. Pentru a fi utilizate de aplicație, aceste servicii de localizare trebuie să fie activate și disponibile pe dispozitivul dvs. Aplicaţiile pot utiliza această permisiune pentru a determina locaţia dvs. aproximativă."</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"să acceseze locația exactă (bazată pe GPS și pe rețea)"</string>
+ <string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Permite aplicației să obţină locația dvs. exactă utilizând sistemul GPS (Global Positioning System) sau surse de localizare prin rețele, cum ar fi cele prin turn de celule și Wi-Fi. Pentru a fi utilizate de aplicație, aceste servicii de localizare trebuie să fie activate și disponibile pe dispozitivul dvs. Aplicaţiile pot utiliza această permisiune pentru a determina locația dvs. și pot să consume mai multă energie a bateriei."</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"să acceseze locația aproximativă (bazată pe rețea)"</string>
+ <string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Permite aplicației să obţină locația dvs. aproximativă. Această locație este dedusă de serviciile de localizare utilizând surse de localizare prin rețele, cum ar fi cele prin turn de celule și Wi-Fi. Pentru a fi utilizate de aplicație, aceste servicii de localizare trebuie să fie activate și disponibile pe dispozitivul dvs. Aplicaţiile pot utiliza această permisiune pentru a determina locația dvs. aproximativă."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"modificare setări audio"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Permite aplicației să modifice setările audio globale, cum ar fi volumul și difuzorul care este utilizat pentru ieșire."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"înregistreze sunet"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Permite aplicației să efectueze înregistrări audio cu ajutorul microfonului. Cu această permisiune aplicația efectuează oricând înregistrări audio fără confirmare."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"comunicare cu cardul SIM"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"să trimită comenzi către SIM"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Permite aplicației să trimită comenzi pe cardul SIM. Această permisiune este foarte periculoasă."</string>
<string name="permlab_camera" msgid="3616391919559751192">"realizarea de fotografii și videoclipuri"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Permite aplicației să realizeze fotografii și videoclipuri cu camera foto. Cu această permisiune aplicația utilizează camera foto oricând și fără confirmare."</string>
@@ -358,7 +358,7 @@
<string name="permlab_accessImsCallService" msgid="3574943847181793918">"accesează serviciul de apelare IMS"</string>
<string name="permdesc_accessImsCallService" msgid="8992884015198298775">"Permite aplicației să folosească serviciul IMS pentru apeluri, fără intervenția dvs."</string>
<string name="permlab_readPhoneState" msgid="9178228524507610486">"citeşte starea și identitatea telefonului"</string>
- <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Permite aplicației să acceseze funcţiile de telefon ale dispozitivului. Cu această permisiune aplicația stabilește numărul de telefon și ID-urile de dispozitiv, dacă un apel este activ, precum și numărul de la distanţă conectat printr-un apel."</string>
+ <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Permite aplicației să acceseze funcţiile de telefon ale dispozitivului. Cu această permisiune aplicația stabilește numărul de telefon și ID-urile de dispozitiv, dacă un apel este activ, precum și numărul de la distanță conectat printr-un apel."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"împiedicarea computerului tablet PC să intre în repaus"</string>
<string name="permlab_wakeLock" product="tv" msgid="2601193288949154131">"împiedică intrarea televizorului în stare de inactivitate"</string>
<string name="permlab_wakeLock" product="default" msgid="573480187941496130">"împiedicare intrare telefon în repaus"</string>
@@ -378,19 +378,19 @@
<string name="permdesc_setTimeZone" product="tv" msgid="888864653946175955">"Permite aplicației să modifice fusul orar al televizorului."</string>
<string name="permdesc_setTimeZone" product="default" msgid="4499943488436633398">"Permite aplicației să schimbe fusul orar al telefonului."</string>
<string name="permlab_getAccounts" msgid="1086795467760122114">"găseşte conturi pe dispozitiv"</string>
- <string name="permdesc_getAccounts" product="tablet" msgid="2741496534769660027">"Permite aplicației să obţină lista de conturi cunoscute de tabletă. Aceasta poate include conturile create de aplicațiile pe care le-aţi instalat."</string>
+ <string name="permdesc_getAccounts" product="tablet" msgid="2741496534769660027">"Permite aplicației să obţină lista de conturi cunoscute de tabletă. Aceasta poate include conturile create de aplicațiile pe care le-ați instalat."</string>
<string name="permdesc_getAccounts" product="tv" msgid="4190633395633907543">"Permite aplicației să obțină lista de conturi cunoscute de televizor. Aceasta poate include conturile create de aplicațiile pe care le-ați instalat."</string>
- <string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Permite aplicației să obţină lista de conturi cunoscute de telefon. Aceasta poate include conturile create de aplicațiile pe care le-aţi instalat."</string>
+ <string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Permite aplicației să obţină lista de conturi cunoscute de telefon. Aceasta poate include conturile create de aplicațiile pe care le-ați instalat."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"vizualizează conexiunile la rețea"</string>
- <string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Permite aplicației să vadă informaţiile despre conexiunile la rețea, cum ar fi reţelele existente și cele care sunt conectate."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"acces deplin la rețea"</string>
+ <string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Permite aplicației să vadă informațiile despre conexiunile la rețea, cum ar fi reţelele existente și cele care sunt conectate."</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"să aibă acces deplin la rețea"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Permite aplicației să creeze socluri de rețea și să utilizeze protocoale de rețea personalizate. Browserul și alte aplicații oferă mijloacele de trimitere a datelor pe internet, astfel încât această permisiune nu este necesară pentru trimiterea datelor pe internet."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"modificare conectivitate în rețea"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Permite aplicației să modifice starea de conectivitate la rețea."</string>
<string name="permlab_changeTetherState" msgid="5952584964373017960">"modificare conectivitate tethering"</string>
<string name="permdesc_changeTetherState" msgid="1524441344412319780">"Permite aplicației să modifice starea de conectivitate prin tethering la rețea."</string>
<string name="permlab_accessWifiState" msgid="5202012949247040011">"vizualizează conexiunile Wi-Fi"</string>
- <string name="permdesc_accessWifiState" msgid="5002798077387803726">"Permite aplicației să vadă informaţiile despre reţelele Wi-Fi, de ex. dacă o rețea Wi-Fi este activată, precum și numele dispozitivelor conectate la rețeaua Wi-Fi."</string>
+ <string name="permdesc_accessWifiState" msgid="5002798077387803726">"Permite aplicației să vadă informațiile despre reţelele Wi-Fi, de ex. dacă o rețea Wi-Fi este activată, precum și numele dispozitivelor conectate la rețeaua Wi-Fi."</string>
<string name="permlab_changeWifiState" msgid="6550641188749128035">"se conectează și se deconectează de la Wi-Fi"</string>
<string name="permdesc_changeWifiState" msgid="7137950297386127533">"Permite aplicației să se conecteze și să se deconecteze de la punctele de acces Wi-Fi, precum și să efectueze modificări în configuraţia dispozitivului pentru reţelele Wi-Fi."</string>
<string name="permlab_changeWifiMulticastState" msgid="1368253871483254784">"permitere recepţionare difuzare multiplă Wi-Fi"</string>
@@ -398,12 +398,12 @@
<string name="permdesc_changeWifiMulticastState" product="tv" msgid="9031975661145014160">"Permite aplicației să primească pachetele trimise către toate dispozitivele dintr-o rețea Wi-Fi, utilizând adrese cu difuzare multiplă, nu doar televizorul dvs. Această funcție utilizează mai multă energie decât modul fără difuzare multiplă."</string>
<string name="permdesc_changeWifiMulticastState" product="default" msgid="6851949706025349926">"Permite aplicației să primească pachetele trimise către toate dispozitivele dintr-o rețea Wi-Fi, utilizând adrese cu difuzare multiplă, nu doar telefonul dvs. Această funcție utilizează mai multă energie decât modul fără difuzare multiplă."</string>
<string name="permlab_bluetoothAdmin" msgid="6006967373935926659">"accesează setările Bluetooth"</string>
- <string name="permdesc_bluetoothAdmin" product="tablet" msgid="6921177471748882137">"Permite aplicației să configureze tableta Bluetooth locală, să descopere și să se împerecheze cu dispozitive la distanţă."</string>
+ <string name="permdesc_bluetoothAdmin" product="tablet" msgid="6921177471748882137">"Permite aplicației să configureze tableta Bluetooth locală, să descopere și să se împerecheze cu dispozitive la distanță."</string>
<string name="permdesc_bluetoothAdmin" product="tv" msgid="3373125682645601429">"Permite aplicației să configureze televizorul Bluetooth local, precum și să descopere și să se asocieze cu dispozitive la distanță."</string>
- <string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Permite aplicației să configureze telefonul Bluetooth local, să descopere și să se împerecheze cu dispozitive la distanţă."</string>
+ <string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Permite aplicației să configureze telefonul Bluetooth local, să descopere și să se împerecheze cu dispozitive la distanță."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"se conectează și se deconectează de la WiMAX"</string>
- <string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Permite aplicației să stabilească dacă o rețea WiMAX este activată și să vadă informaţiile cu privire la toate reţelele WiMAX conectate."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Schimbaţi starea WiMAX"</string>
+ <string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Permite aplicației să stabilească dacă o rețea WiMAX este activată și să vadă informațiile cu privire la toate reţelele WiMAX conectate."</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"schimbaţi starea WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Permite aplicației să conecteze și să deconecteze tableta la și de la reţelele WiMAX."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Permite aplicației să conecteze și să deconecteze televizorul la și de la rețelele WiMAX."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Permite aplicației să conecteze și să deconecteze telefonul la și de la reţelele WiMAX."</string>
@@ -486,7 +486,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Permite aplicației să modifice parametrii de calibrare a ecranului tactil. Nu ar trebui să fie necesară pentru aplicațiile obișnuite."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"accesează certificatele DRM"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Permite unei aplicații să furnizeze și să utilizeze certificate DRM. Nu ar trebui să fie necesară pentru aplicațiile obișnuite."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Primiți starea transferului prin Android Beam"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"să primească starea transferului prin Android Beam"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Permite acestei aplicații să primească informații despre transferurile actuale Android Beam"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"eliminarea certificatelor DRM"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Permite unei aplicații să elimine certificatele DRM. Nu ar trebui să fie necesară pentru aplicațiile obișnuite."</string>
@@ -499,7 +499,7 @@
<string name="policylab_limitPassword" msgid="4497420728857585791">"Setați reguli pentru parolă"</string>
<string name="policydesc_limitPassword" msgid="2502021457917874968">"Stabiliți lungimea și tipul de caractere permise pentru parolele și codurile PIN de blocare a ecranului."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Monitorizați încercările de deblocare a ecranului"</string>
- <string name="policydesc_watchLogin" product="tablet" msgid="3215729294215070072">"Monitorizaţi numărul de parole incorecte introduse la deblocarea ecranului și blocaţi tableta sau ştergeţi datele acesteia dacă sunt introduse prea multe parole incorecte."</string>
+ <string name="policydesc_watchLogin" product="tablet" msgid="3215729294215070072">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocaţi tableta sau ștergeți datele acesteia dacă sunt introduse prea multe parole incorecte."</string>
<string name="policydesc_watchLogin" product="TV" msgid="2707817988309890256">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați televizorul sau ștergeți toate datele acestuia dacă sunt introduse prea multe parole incorecte."</string>
<string name="policydesc_watchLogin" product="default" msgid="5712323091846761073">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați telefonul sau ștergeți toate datele acestuia dacă sunt introduse prea multe parole incorecte."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="4280246270601044505">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați tableta sau ștergeți toate datele acestui utilizator dacă se introduc prea multe parole incorecte."</string>
@@ -651,7 +651,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Cod PIN incorect."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Pentru a debloca, apăsaţi Meniu, apoi 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Număr de urgență"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Fără serviciu."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Fără semnal"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Ecranul este blocat."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Apăsați Meniu pentru a debloca sau pentru a efectua apeluri de urgență."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Apăsaţi Meniu pentru deblocare."</string>
@@ -677,21 +677,21 @@
<string name="lockscreen_transport_stop_description" msgid="5907083260651210034">"Opriți"</string>
<string name="lockscreen_transport_rew_description" msgid="6944412838651990410">"Derulaţi"</string>
<string name="lockscreen_transport_ffw_description" msgid="42987149870928985">"Derulaţi rapid înainte"</string>
- <string name="emergency_calls_only" msgid="6733978304386365407">"Numai apeluri de urgenţă"</string>
+ <string name="emergency_calls_only" msgid="6733978304386365407">"Numai apeluri de urgență"</string>
<string name="lockscreen_network_locked_message" msgid="143389224986028501">"Rețea blocată"</string>
<string name="lockscreen_sim_puk_locked_message" msgid="7441797339976230">"Cardul SIM este blocat cu codul PUK."</string>
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Consultaţi Ghidul de utilizare sau contactaţi Serviciul de relaţii cu clienţii."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"Cardul SIM este blocat."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Se deblochează cardul SIM..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Aţi desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%d</xliff:g> ori. \n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%d</xliff:g> (de) secunde."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Aţi introdus incorect parola de <xliff:g id="NUMBER_0">%d</xliff:g> ori. \n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%d</xliff:g> (de) secunde."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Aţi introdus incorect codul PIN de <xliff:g id="NUMBER_0">%d</xliff:g> ori.\n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%d</xliff:g> (de) secunde."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Aţi desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%d</xliff:g> încercări nereuşite, vi se va solicita să deblocaţi tableta cu ajutorul datelor de conectare la Google.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%d</xliff:g> (de) secunde."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%d</xliff:g> încercări nereușite, vi se va solicita să deblocați televizorul cu ajutorul datelor de conectare la Google.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%d</xliff:g> (de) secunde."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Aţi desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%d</xliff:g> încercări nereuşite, vi se va solicita să deblocaţi telefonul cu ajutorul datelor de conectare la Google.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%d</xliff:g> (de) secunde."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Aţi efectuat <xliff:g id="NUMBER_0">%d</xliff:g> încercări incorecte de deblocare a tabletei. După încă <xliff:g id="NUMBER_1">%d</xliff:g> încercări nereuşite, aceasta va fi resetată la setările prestabilite din fabrică, iar toate datele de utilizator vor fi pierdute."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Ați efectuat <xliff:g id="NUMBER_0">%d</xliff:g> încercări incorecte de deblocare a televizorului. După încă <xliff:g id="NUMBER_1">%d</xliff:g> încercări nereușite, televizorul va reveni la setările prestabilite din fabrică, iar toate datele de utilizator se vor pierde."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Aţi efectuat <xliff:g id="NUMBER_0">%d</xliff:g> încercări incorecte de deblocare a telefonului. După încă <xliff:g id="NUMBER_1">%d</xliff:g> încercări nereuşite, acesta va fi resetat la setările prestabilite din fabrică, iar toate datele de utilizator vor fi pierdute."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Aţi desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> (de) secunde."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Aţi introdus incorect parola de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> (de) secunde."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Aţi introdus incorect codul PIN de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori.\n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> (de) secunde."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Aţi desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereuşite, vi se va solicita să deblocaţi tableta cu ajutorul datelor de conectare la Google.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> (de) secunde."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, vi se va solicita să deblocați televizorul cu ajutorul datelor de conectare la Google.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> (de) secunde."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Aţi desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereuşite, vi se va solicita să deblocaţi telefonul cu ajutorul datelor de conectare la Google.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> (de) secunde."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Aţi efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a tabletei. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereuşite, aceasta va fi resetată la setările prestabilite din fabrică, iar toate datele de utilizator vor fi pierdute."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Ați efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a televizorului. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, televizorul va reveni la setările prestabilite din fabrică, iar toate datele de utilizator se vor pierde."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Aţi efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a telefonului. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereuşite, acesta va fi resetat la setările prestabilite din fabrică, iar toate datele de utilizator vor fi pierdute."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Aţi efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a tabletei. Tableta va fi acum resetată la setările prestabilite din fabrică."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Ați efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a televizorului. Televizorul va reveni acum la setările prestabilite din fabrică."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Aţi efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a telefonului. Acesta va fi acum resetat la setările prestabilite din fabrică."</string>
@@ -716,7 +716,7 @@
<string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Modelul a fost desenat"</string>
<string name="lockscreen_access_pattern_area" msgid="400813207572953209">"Zonă model."</string>
<string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d din %3$d."</string>
- <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Adăugaţi un widget."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Adăugați un widget."</string>
<string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Gol"</string>
<string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Zona de deblocare a fost extinsă."</string>
<string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"Zona de deblocare a fost restrânsă."</string>
@@ -743,11 +743,9 @@
<string name="granularity_label_word" msgid="7075570328374918660">"cuvânt"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"link"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"rând"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Testarea de fabrică nu a reuşit"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Acţiunea FACTORY_TEST este acceptată doar pentru pachetele instalate în /system/app."</string>
- <string name="factorytest_no_action" msgid="872991874799998561">"Nu s-a găsit niciun pachet care să ofere acţiunea FACTORY_TEST."</string>
+ <string name="factorytest_no_action" msgid="872991874799998561">"Nu s-a găsit niciun pachet care să ofere acțiunea FACTORY_TEST."</string>
<string name="factorytest_reboot" msgid="6320168203050791643">"Reporniți"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"La pagina de la „<xliff:g id="TITLE">%s</xliff:g>” apare:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
@@ -785,8 +783,8 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Permite aplicației să seteze o alarmă într-o aplicație de ceas cu alarmă instalată. Este posibil ca unele aplicații de ceas cu alarmă să nu implementeze această funcție."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"adăugare mesagerie vocală"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Permite aplicației să adauge mesaje în Mesaje primite în mesageria vocală."</string>
- <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"modificare permisiuni pentru locaţia geografică a browserului"</string>
- <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Permite aplicației să modifice permisiunile privind locaţia geografică a browserului. Aplicaţiile rău intenţionate pot utiliza această permisiune pentru a permite trimiterea informaţiilor privind locaţia către site-uri web arbitrare."</string>
+ <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"modificare permisiuni pentru locația geografică a browserului"</string>
+ <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Permite aplicației să modifice permisiunile privind locația geografică a browserului. Aplicaţiile rău intenţionate pot utiliza această permisiune pentru a permite trimiterea informaţiilor privind locația către site-uri web arbitrare."</string>
<string name="save_password_message" msgid="767344687139195790">"Doriți ca browserul să reţină această parolă?"</string>
<string name="save_password_notnow" msgid="6389675316706699758">"Nu acum"</string>
<string name="save_password_remember" msgid="6491879678996749466">"Reţineţi"</string>
@@ -806,8 +804,8 @@
<string name="searchview_description_submit" msgid="2688450133297983542">"Trimiteți interogarea"</string>
<string name="searchview_description_voice" msgid="2453203695674994440">"Căutare vocală"</string>
<string name="enable_explore_by_touch_warning_title" msgid="7460694070309730149">"Activați Explorați prin atingere?"</string>
- <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="8655887539089910577">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> doreşte să activeze funcţia Explorați prin atingere. Când această funcție este activată, puteți auzi sau vedea descrieri pentru ceea ce se află sub degetul dvs. sau puteți efectua gesturi pentru a interacţiona cu tableta."</string>
- <string name="enable_explore_by_touch_warning_message" product="default" msgid="2708199672852373195">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> doreşte să activeze funcţia Explorați prin atingere. Când această funcție este activată, puteți auzi sau vedea descrieri pentru ceea ce se află sub degetul dvs. sau puteți efectua gesturi pentru a interacţiona cu telefonul."</string>
+ <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="8655887539089910577">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> doreşte să activeze funcția Explorați prin atingere. Când această funcție este activată, puteți auzi sau vedea descrieri pentru ceea ce se află sub degetul dvs. sau puteți efectua gesturi pentru a interacţiona cu tableta."</string>
+ <string name="enable_explore_by_touch_warning_message" product="default" msgid="2708199672852373195">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> doreşte să activeze funcția Explorați prin atingere. Când această funcție este activată, puteți auzi sau vedea descrieri pentru ceea ce se află sub degetul dvs. sau puteți efectua gesturi pentru a interacţiona cu telefonul."</string>
<string name="oneMonthDurationPast" msgid="7396384508953779925">"cu 1 lună în urmă"</string>
<string name="beforeOneMonthDurationPast" msgid="909134546836499826">"Cu mai mult de 1 lună în urmă"</string>
<plurals name="last_num_days" formatted="false" msgid="5104533550723932025">
@@ -848,7 +846,7 @@
<item quantity="one">O oră</item>
</plurals>
<string name="VideoView_error_title" msgid="3534509135438353077">"Problemă video"</string>
- <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Acest fişier video nu este valid pentru a fi transmis în flux către acest dispozitiv."</string>
+ <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Acest fișier video nu este valid pentru a fi transmis în flux către acest dispozitiv."</string>
<string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Nu puteți reda acest videoclip"</string>
<string name="VideoView_error_button" msgid="2822238215100679592">"OK"</string>
<string name="relative_time" msgid="1818557177829411417">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -867,7 +865,7 @@
<string name="copyUrl" msgid="2538211579596067402">"Copiați adresa URL"</string>
<string name="selectTextMode" msgid="1018691815143165326">"Selectați text"</string>
<string name="textSelectionCABTitle" msgid="5236850394370820357">"Selectare text"</string>
- <string name="addToDictionary" msgid="4352161534510057874">"Adăugaţi în dicţionar"</string>
+ <string name="addToDictionary" msgid="4352161534510057874">"Adăugați în dicţionar"</string>
<string name="deleteText" msgid="6979668428458199034">"Ștergeți"</string>
<string name="inputMethod" msgid="1653630062304567879">"Metodă de intrare"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Acţiuni pentru text"</string>
@@ -897,8 +895,8 @@
<string name="alwaysUse" msgid="4583018368000610438">"Se utilizează în mod prestabilit pentru această acţiune."</string>
<string name="use_a_different_app" msgid="8134926230585710243">"Utilizați altă aplicație"</string>
<string name="clearDefaultHintMsg" msgid="3252584689512077257">"Ștergeți setările prestabilite din Setări de sistem > Aplicații > Descărcate."</string>
- <string name="chooseActivity" msgid="7486876147751803333">"Alegeţi o acţiune"</string>
- <string name="chooseUsbActivity" msgid="6894748416073583509">"Alegeţi o aplicație pentru dispozitivul USB"</string>
+ <string name="chooseActivity" msgid="7486876147751803333">"Alegeți o acţiune"</string>
+ <string name="chooseUsbActivity" msgid="6894748416073583509">"Alegeți o aplicație pentru dispozitivul USB"</string>
<string name="noApplications" msgid="2991814273936504689">"Această acţiune nu poate fi efectuată de nicio aplicație."</string>
<string name="aerr_title" msgid="1905800560317137752"></string>
<string name="aerr_application" msgid="932628488013092776">"Din păcate, <xliff:g id="APPLICATION">%1$s</xliff:g> s-a oprit."</string>
@@ -933,14 +931,14 @@
<string name="heavy_weight_switcher_title" msgid="7153167085403298169">"Comutaţi între aplicații?"</string>
<string name="heavy_weight_switcher_text" msgid="7022631924534406403">"O altă aplicație rulează deja și trebuie oprită înainte a putea porni o aplicație nouă."</string>
<string name="old_app_action" msgid="493129172238566282">"Reveniţi la <xliff:g id="OLD_APP">%1$s</xliff:g>"</string>
- <string name="old_app_description" msgid="2082094275580358049">"Nu porniţi aplicația nouă."</string>
+ <string name="old_app_description" msgid="2082094275580358049">"Nu porniți aplicația nouă."</string>
<string name="new_app_action" msgid="5472756926945440706">"Porniţi <xliff:g id="OLD_APP">%1$s</xliff:g>"</string>
- <string name="new_app_description" msgid="1932143598371537340">"Opriți vechea aplicație fără să salvaţi."</string>
+ <string name="new_app_description" msgid="1932143598371537340">"Opriți vechea aplicație fără să salvați."</string>
<string name="dump_heap_notification" msgid="2618183274836056542">"<xliff:g id="PROC">%1$s</xliff:g> a depășit limita de memorie"</string>
<string name="dump_heap_notification_detail" msgid="2075673362317481664">"Datele privind memoria au fost culese; atingeți pentru a trimite"</string>
<string name="dump_heap_title" msgid="5864292264307651673">"Trimiteți datele privind memoria?"</string>
<string name="dump_heap_text" msgid="4809417337240334941">"Procesul <xliff:g id="PROC">%1$s</xliff:g> și-a depășit limita de memorie de <xliff:g id="SIZE">%2$s</xliff:g>. Sunt disponibile datele privind memoria, pe care le puteți trimite dezvoltatorului. Atenție: aceste date privind memoria pot conține informațiile personale la care aplicația are acces."</string>
- <string name="sendText" msgid="5209874571959469142">"Alegeţi o acţiune pentru text"</string>
+ <string name="sendText" msgid="5209874571959469142">"Alegeți o acţiune pentru text"</string>
<string name="volume_ringtone" msgid="6885421406845734650">"Volum sonerie"</string>
<string name="volume_music" msgid="5421651157138628171">"Volum media"</string>
<string name="volume_music_hint_playing_through_bluetooth" msgid="9165984379394601533">"Redare prin Bluetooth"</string>
@@ -1007,7 +1005,7 @@
<string name="sms_premium_short_code_details" msgid="7869234868023975"><b>"Acest lucru va genera costuri în contul dvs. mobil."</b></string>
<string name="sms_short_code_confirm_allow" msgid="4458878637111023413">"Trimiteți"</string>
<string name="sms_short_code_confirm_deny" msgid="2927389840209170706">"Anulați"</string>
- <string name="sms_short_code_remember_choice" msgid="5289538592272218136">"Doresc să se reţină opţiunea"</string>
+ <string name="sms_short_code_remember_choice" msgid="5289538592272218136">"Doresc să se reţină opțiunea"</string>
<string name="sms_short_code_remember_undo_instruction" msgid="4960944133052287484">"Puteți modifica ulterior în Setări > Aplicații"</string>
<string name="sms_short_code_confirm_always_allow" msgid="3241181154869493368">"Permiteți întotdeauna"</string>
<string name="sms_short_code_confirm_never_allow" msgid="446992765774269673">"Nu permiteți niciodată"</string>
@@ -1080,11 +1078,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Se formatează…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Nu este introdus"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Nu s-a găsit nicio activitate potrivită."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Direcționează rezultatele media"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"să direcționeze rezultatele media"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Permite unei aplicații să direcționeze rezultate media către alte dispozitive externe."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Citirea sesiunilor de instalare"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"să citească sesiunile de instalare"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Permite unei aplicații accesul la citirea sesiunilor de instalare. Aceasta poate vedea detalii despre instalările de pachete active."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Solicită instalarea pachetelor"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"să solicite pachete de instalare"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Permite unei aplicații să solicite instalarea pachetelor."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Atingeți de două ori pentru a mări/micşora"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Nu s-a putut adăuga widgetul."</string>
@@ -1096,7 +1094,7 @@
<string name="ime_action_previous" msgid="1443550039250105948">"Înapoi"</string>
<string name="ime_action_default" msgid="2840921885558045721">"Executați"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Formaţi numărul\nutilizând <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="create_contact_using" msgid="4947405226788104538">"Creaţi contactul\nutilizând <xliff:g id="NUMBER">%s</xliff:g>"</string>
+ <string name="create_contact_using" msgid="4947405226788104538">"Creați contactul\nutilizând <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="grant_credentials_permission_message_header" msgid="2106103817937859662">"Următoarele aplicații solicită permisiunea de a accesa contul dvs. acum și în viitor."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Permiteți această solicitare?"</string>
<string name="grant_permissions_header_text" msgid="6874497408201826708">"Solicitare de acces"</string>
@@ -1121,7 +1119,7 @@
<string name="vpn_lockdown_connected" msgid="8202679674819213931">"Conectat(ă) la reţeaua VPN activată permanent"</string>
<string name="vpn_lockdown_error" msgid="6009249814034708175">"Eroare de rețea VPN activată permanent"</string>
<string name="vpn_lockdown_config" msgid="6415899150671537970">"Atingeți pentru a configura"</string>
- <string name="upload_file" msgid="2897957172366730416">"Alegeţi un fişier"</string>
+ <string name="upload_file" msgid="2897957172366730416">"Alegeți un fișier"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Nu au fost găsite fișiere"</string>
<string name="reset" msgid="2448168080964209908">"Resetaţi"</string>
<string name="submit" msgid="1602335572089911941">"Trimiteți"</string>
@@ -1157,25 +1155,25 @@
<string name="sync_really_delete" msgid="2572600103122596243">"Ștergeți elementele"</string>
<string name="sync_undo_deletes" msgid="2941317360600338602">"Anulați aceste ştergeri"</string>
<string name="sync_do_nothing" msgid="3743764740430821845">"Nu trebuie să luați nicio măsură deocamdată"</string>
- <string name="choose_account_label" msgid="5655203089746423927">"Alegeţi un cont"</string>
- <string name="add_account_label" msgid="2935267344849993553">"Adăugaţi un cont"</string>
- <string name="add_account_button_label" msgid="3611982894853435874">"Adăugaţi un cont"</string>
+ <string name="choose_account_label" msgid="5655203089746423927">"Alegeți un cont"</string>
+ <string name="add_account_label" msgid="2935267344849993553">"Adăugați un cont"</string>
+ <string name="add_account_button_label" msgid="3611982894853435874">"Adăugați un cont"</string>
<string name="number_picker_increment_button" msgid="2412072272832284313">"Creșteți"</string>
- <string name="number_picker_decrement_button" msgid="476050778386779067">"Reduceţi"</string>
+ <string name="number_picker_decrement_button" msgid="476050778386779067">"Reduceți"</string>
<string name="number_picker_increment_scroll_mode" msgid="3073101067441638428">"Atingeți și țineți apăsat <xliff:g id="VALUE">%s</xliff:g>."</string>
<string name="number_picker_increment_scroll_action" msgid="9101473045891835490">"Glisaţi în sus pentru a creşte și în jos pentru a reduce."</string>
<string name="time_picker_increment_minute_button" msgid="8865885114028614321">"Creșteți valoarea pentru minute"</string>
- <string name="time_picker_decrement_minute_button" msgid="6246834937080684791">"Reduceţi valoarea pentru minute"</string>
+ <string name="time_picker_decrement_minute_button" msgid="6246834937080684791">"Reduceți valoarea pentru minute"</string>
<string name="time_picker_increment_hour_button" msgid="3652056055810223139">"Creșteți valoarea pentru oră"</string>
- <string name="time_picker_decrement_hour_button" msgid="1377479863429214792">"Reduceţi valoarea pentru oră"</string>
+ <string name="time_picker_decrement_hour_button" msgid="1377479863429214792">"Reduceți valoarea pentru oră"</string>
<string name="time_picker_increment_set_pm_button" msgid="4147590696151230863">"Setaţi valoarea PM"</string>
<string name="time_picker_decrement_set_am_button" msgid="8302140353539486752">"Setaţi valoarea AM"</string>
<string name="date_picker_increment_month_button" msgid="5369998479067934110">"Creșteți valoarea pentru lună"</string>
- <string name="date_picker_decrement_month_button" msgid="1832698995541726019">"Reduceţi valoarea pentru lună"</string>
+ <string name="date_picker_decrement_month_button" msgid="1832698995541726019">"Reduceți valoarea pentru lună"</string>
<string name="date_picker_increment_day_button" msgid="7130465412308173903">"Creșteți valoarea pentru zi"</string>
- <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Reduceţi valoarea pentru zi"</string>
+ <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Reduceți valoarea pentru zi"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"Creșteți valoarea pentru an"</string>
- <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Reduceţi valoarea pentru an"</string>
+ <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Reduceți valoarea pentru an"</string>
<string name="date_picker_prev_month_button" msgid="2858244643992056505">"Luna trecută"</string>
<string name="date_picker_next_month_button" msgid="5559507736887605055">"Luna viitoare"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
@@ -1185,7 +1183,7 @@
<string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Schimbarea modului"</string>
<string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
<string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
- <string name="activitychooserview_choose_application" msgid="2125168057199941199">"Alegeţi o aplicație"</string>
+ <string name="activitychooserview_choose_application" msgid="2125168057199941199">"Alegeți o aplicație"</string>
<string name="activitychooserview_choose_application_error" msgid="8624618365481126668">"Nu s-a putut lansa <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
<string name="shareactionprovider_share_with" msgid="806688056141131819">"Permiteți accesul pentru"</string>
<string name="shareactionprovider_share_with_application" msgid="5627411384638389738">"Permiteți accesul pentru <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
@@ -1195,7 +1193,7 @@
<string name="keyboard_password_character_no_headset" msgid="2859873770886153678">"Punct."</string>
<string name="action_bar_home_description" msgid="5293600496601490216">"Navigaţi la ecranul de pornire"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Navigaţi în sus"</string>
- <string name="action_menu_overflow_description" msgid="2295659037509008453">"Mai multe opţiuni"</string>
+ <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>
<string name="storage_internal" msgid="4891916833657929263">"Stocare internă"</string>
@@ -1204,7 +1202,7 @@
<string name="storage_usb_drive" msgid="6261899683292244209">"Unitate USB"</string>
<string name="storage_usb_drive_label" msgid="4501418548927759953">"Unitate USB <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb" msgid="3017954059538517278">"Dsipozitiv de stocare USB"</string>
- <string name="extract_edit_menu_button" msgid="8940478730496610137">"Editaţi"</string>
+ <string name="extract_edit_menu_button" msgid="8940478730496610137">"Editați"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"Avertisment de utiliz. a datelor"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"Atingeți pt. a afişa utiliz./set."</string>
<string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Ați atins limita de date 2G-3G"</string>
@@ -1234,7 +1232,7 @@
<string name="sha256_fingerprint" msgid="4391271286477279263">"Amprentă SHA-256:"</string>
<string name="sha1_fingerprint" msgid="7930330235269404581">"Amprentă SHA-1:"</string>
<string name="activity_chooser_view_see_all" msgid="4292569383976636200">"Afişaţi-le pe toate"</string>
- <string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"Alegeţi activitatea"</string>
+ <string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"Alegeți activitatea"</string>
<string name="share_action_provider_share_with" msgid="5247684435979149216">"Distribuiţi pentru"</string>
<string name="sending" msgid="3245653681008218030">"Se trimite..."</string>
<string name="launchBrowserDefault" msgid="2057951947297614725">"Lansaţi browserul?"</string>
@@ -1293,18 +1291,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Nume de utilizator sau parolă nevalide."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Aţi uitat numele de utilizator sau parola?\nAccesați "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Se verifică contul…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Aţi introdus incorect codul PIN de <xliff:g id="NUMBER_0">%d</xliff:g> ori.\n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%d</xliff:g> (de) secunde."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Aţi introdus incorect parola de <xliff:g id="NUMBER_0">%d</xliff:g> ori. \n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%d</xliff:g> (de) secunde."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Aţi desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%d</xliff:g> ori. \n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%d</xliff:g> (de) secunde."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Aţi efectuat <xliff:g id="NUMBER_0">%d</xliff:g> încercări incorecte de deblocare a tabletei. După încă <xliff:g id="NUMBER_1">%d</xliff:g> încercări nereuşite, aceasta va fi resetată la setările prestabilite din fabrică, iar toate datele de utilizator se vor pierde."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Ați efectuat <xliff:g id="NUMBER_0">%d</xliff:g> încercări incorecte de deblocare a televizorului. După încă <xliff:g id="NUMBER_1">%d</xliff:g> încercări nereușite, televizorul va reveni la setările prestabilite din fabrică, iar toate datele de utilizator se vor pierde."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Aţi efectuat <xliff:g id="NUMBER_0">%d</xliff:g> încercări incorecte de deblocare a telefonului. După încă <xliff:g id="NUMBER_1">%d</xliff:g> încercări nereuşite, acesta va fi resetat la setările prestabilite din fabrică, iar toate datele de utilizator se vor pierde."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Aţi introdus incorect codul PIN de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori.\n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> (de) secunde."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Aţi introdus incorect parola de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> (de) secunde."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Aţi desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> (de) secunde."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Aţi efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a tabletei. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereuşite, aceasta va fi resetată la setările prestabilite din fabrică, iar toate datele de utilizator se vor pierde."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Ați efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a televizorului. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, televizorul va reveni la setările prestabilite din fabrică, iar toate datele de utilizator se vor pierde."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Aţi efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a telefonului. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereuşite, acesta va fi resetat la setările prestabilite din fabrică, iar toate datele de utilizator se vor pierde."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Aţi efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a tabletei. Tableta va fi acum resetată la setările prestabilite din fabrică."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Ați efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a televizorului. Televizorul va reveni acum la setările prestabilite din fabrică."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Aţi efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a telefonului. Telefonul va fi acum resetat la setările prestabilite din fabrică."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Aţi desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%d</xliff:g> încercări nereuşite, vi se va solicita să deblocaţi tableta cu ajutorul unui cont de e-mail.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%d</xliff:g> (de) secunde."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%d</xliff:g> încercări nereușite, vi se va solicita să deblocați televizorul cu ajutorul unui cont de e-mail.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%d</xliff:g> (de) secunde."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Aţi desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%d</xliff:g> încercări nereuşite, vi se va solicita să deblocaţi telefonul cu ajutorul unui cont de e-mail.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%d</xliff:g> (de) secunde."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Aţi desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereuşite, vi se va solicita să deblocaţi tableta cu ajutorul unui cont de e-mail.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> (de) secunde."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, vi se va solicita să deblocați televizorul cu ajutorul unui cont de e-mail.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> (de) secunde."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Aţi desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereuşite, vi se va solicita să deblocaţi telefonul cu ajutorul unui cont de e-mail.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> (de) secunde."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Eliminaţi"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Ridicați volumul mai sus de nivelul recomandat?\n\nAscultarea la volum ridicat pe perioade lungi de timp vă poate afecta auzul."</string>
@@ -1487,6 +1485,7 @@
<item quantity="one">Pentru 1 h</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Până la <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Până la <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (următoarea alarmă)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Până la dezactivare"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Până când dezactivați „Nu deranja”"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-ru-watch/strings.xml b/core/res/res/values-ru-watch/strings.xml
index 5dd5630..29f3516 100644
--- a/core/res/res/values-ru-watch/strings.xml
+++ b/core/res/res/values-ru-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Приложение <xliff:g id="NUMBER_0">%1$d</xliff:g> из <xliff:g id="NUMBER_1">%2$d</xliff:g>"</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Датчики"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"доступ к контактам"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"доступ к местоположению часов"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"доступ к календарю"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"отправка и просмотр SMS-сообщений"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"доступ к фото, медиа и файлам на часах"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"запись аудио"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"фото- и видеосъемка"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"осуществление телефонных звонков и управление ими"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"доступ к данным датчиков о состоянии организма"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"замена строки состояния"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"доступ к датчикам (например, пульсометру)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"доступ к точному местоположению (на основе GPS и данных сети)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"доступ к примерному местоположению (на основе данных сети)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"отправка команд SIM-карте"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"неограниченный доступ к Интернету"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"управление профилями и владельцами"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"изменение статуса WiMAX"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"получение статуса передачи Android Beam"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"перенаправление мультимедийных данных"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"чтение данных о сеансах установки"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"разрешение на установку пакетов"</string>
</resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 0621bbe..8a64091 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -256,7 +256,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"В том числе личные данные, например номера кредитных карт и пароли."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"Отключение/изменение строки состояния"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Приложение сможет отключать строку состояния, а также добавлять и удалять системные значки."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"Строка состояния"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"Замена строки состояния"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Приложение сможет заменять собой строку состояния."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"Разворачивание/сворачивание строки состояния"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Приложение сможет разворачивать и сворачивать строку состояния."</string>
@@ -284,7 +284,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Приложение сможет получать и обрабатывать WAP-сообщения. Это значит, что оно сможет отслеживать и удалять отправленные на ваше устройство сообщения, не показывая их."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"Получение данных о запущенных приложениях"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Приложение сможет получать информацию о недавно запущенных и выполняемых задачах, а следовательно, и о приложениях, используемых на устройстве."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Управление профилями и владельцами"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"Управление профилями и владельцами"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Приложения смогут определять владельцев профилей и владельца устройства."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"Упорядочивание запущенных приложений"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Приложение сможет переключать активный и фоновый режимы выполнения задач без вашего ведома."</string>
@@ -326,7 +326,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Приложение сможет вносить изменения в список вызовов планшетного ПК и данные о входящих и исходящих звонках. Вредоносные приложения смогут воспользоваться этим для удаления или изменения информации о звонках."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Изменение списка вызовов телевизора и данных о входящих и исходящих звонках. Вредоносные приложения смогут воспользоваться этим для удаления или изменения информации о звонках."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Приложение сможет вносить изменения в список вызовов телефона и данные о входящих и исходящих звонках. Вредоносные приложения смогут воспользоваться этим для удаления или изменения информации о звонках."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"датчики (например, пульсометр)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"Датчики (например, пульсометр)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Приложение сможет получить доступ к данным датчиков, размещенных на теле, например измеряющих частоту сердцебиения."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"Просмотр в календаре мероприятий и конфиденциальных данных"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Приложение сможет просматривать мероприятия в календаре устройства, в том числе добавленные друзьями или коллегами, а также передавать и сохранять данные календаря независимо от настроек конфиденциальности."</string>
@@ -338,15 +338,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Приложение сможет добавлять, удалять и изменять мероприятия, доступные для редактирования на вашем телефоне, включая мероприятия, добавленные другими людьми. Так приложение сможет рассылать сообщения от имени владельца календаря и изменять мероприятия без его ведома."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"Доступ к дополнительным командам управления источниками геоданных"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Доступ к дополнительным командам управления источниками геоданных и вмешательство в работу системы GPS или других источников геоданных."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"Точное местоположение (на основе сети и сигналов GPS)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"Доступ к точному местоположению (по координатам сети и спутникам GPS)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Разрешает приложению получать данные о вашем точном местоположении с помощью глобального позиционирования (GPS), вышек сотовой связи и точек доступа Wi-Fi. Эти службы должны быть включены на устройстве, а приложению должно быть разрешено их использовать. Это может вести к дополнительному расходу заряда батареи."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"Примерное местоположение (на основе сети)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"Доступ к примерному местоположению (по координатам сети)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Разрешает приложению получать данные о вашем примерном местоположении с помощью служб определения местоположения, вышек сотовой связи и точек доступа Wi-Fi. Эти службы должны быть включены на устройстве, а приложению должно быть разрешено их использовать."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"Изменение настроек аудио"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Приложение сможет изменять системные настройки звука, например уровень громкости и активный динамик."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"Запись аудио"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Приложение сможет записывать аудио с помощью микрофона в любое время без уведомления."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"Обращение к SIM-карте"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"Отправка команд SIM-карте"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Приложение сможет отправлять команды SIM-карте (данное разрешение представляет большую угрозу)."</string>
<string name="permlab_camera" msgid="3616391919559751192">"Фото- и видеосъемка"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Приложение сможет снимать фотографии и видеоролики с помощью камеры в любое время без вашего разрешения."</string>
@@ -384,7 +384,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Приложение сможет получить список всех используемых на устройстве аккаунтов, в том числе созданных установленными приложениями."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"Просмотр сетевых подключений"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Приложение сможет просматривать информацию о сетевых подключениях, например о том, какие сети доступны и к каким из них вы подключены."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"Неограниченный доступ в Интернет"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"Неограниченный доступ к Интернету"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Приложение сможет создавать сетевые сокеты и использовать различные сетевые протоколы. Так как браузер и другие приложения обеспечивают средства для отправки данных в Интернет, это разрешение предоставлять не обязательно."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"Изменение сетевых настроек"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Приложение сможет изменять состояние подключения к сети."</string>
@@ -404,7 +404,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Приложение сможет настраивать параметры локального телефона с поддержкой Bluetooth, а также обнаруживать удаленные устройства и выполнять сопряжение с ними."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"подключать/отключать сеть WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Приложение сможет определять, активирован ли WiMAX, а также получать информацию о подключенных сетях WiMAX."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Изменение статуса WiMAX"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"Изменение статуса WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Приложение сможет подключать устройство к сетям WiMAX и отключать его от них."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Подключение телевизора к сетям WiMAX и его отключение от них."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Приложение сможет подключать устройство к сетям WiMAX и отключать его от них."</string>
@@ -487,7 +487,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Приложение сможет изменять параметры калибровки сенсорного экрана. Это разрешение обычно используется только специальными приложениями."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"Доступ к сертификатам DRM"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Приложение сможет синхронизировать и использовать сертификаты DRM (разрешение актуально только для специальных приложений)."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Получение статуса передачи Android Beam"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"Получение статуса передачи Android Beam"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Получение информации о текущих передачах Android Beam."</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"удаление сертификатов DRM"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Удаление сертификатов DRM. Большинству приложений это разрешение не требуется."</string>
@@ -652,7 +652,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Неверный PIN-код."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Для разблокировки нажмите \"Меню\", а затем 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Экстренная служба"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Нет сигнала"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Нет сигнала"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Экран заблокирован."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Нажмите \"Меню\", чтобы разблокировать экран или вызвать службу экстренной помощи."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Для разблокировки нажмите \"Меню\"."</string>
@@ -684,15 +684,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Ознакомьтесь с руководством пользователя или свяжитесь со службой поддержки."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM-карта заблокирована"</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Разблокировка SIM-карты…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Вы <xliff:g id="NUMBER_0">%d</xliff:g> раз неверно указали графический ключ. \n\nПовтор через <xliff:g id="NUMBER_1">%d</xliff:g> сек."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Вы <xliff:g id="NUMBER_0">%d</xliff:g> раз неверно указали пароль. \n\nПовтор через <xliff:g id="NUMBER_1">%d</xliff:g> сек."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Вы <xliff:g id="NUMBER_0">%d</xliff:g> раз неверно указали PIN-код. \n\nПовтор через <xliff:g id="NUMBER_1">%d</xliff:g> сек."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Вы <xliff:g id="NUMBER_0">%d</xliff:g> раз неверно указали графический ключ. После <xliff:g id="NUMBER_1">%d</xliff:g> неверных попыток для разблокировки планшетного ПК потребуется войти в аккаунт Google. \n\n Повтор через <xliff:g id="NUMBER_2">%d</xliff:g> сек."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Вы неправильно ввели графический ключ несколько раз (<xliff:g id="NUMBER_0">%d</xliff:g>). Осталось попыток: <xliff:g id="NUMBER_1">%d</xliff:g>. После этого вам будет предложено разблокировать телевизор с помощью аккаунта Google.\n\nПовторите попытку через <xliff:g id="NUMBER_2">%d</xliff:g> сек."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Вы <xliff:g id="NUMBER_0">%d</xliff:g> раз неверно указали графический ключ. После <xliff:g id="NUMBER_1">%d</xliff:g> неверных попыток для разблокировки телефона потребуется войти в аккаунт Google. \n\n Повтор через <xliff:g id="NUMBER_2">%d</xliff:g> сек."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Вы <xliff:g id="NUMBER_0">%d</xliff:g> раз не смогли разблокировать планшетный ПК. После <xliff:g id="NUMBER_1">%d</xliff:g> неверных попыток будут восстановлены заводские настройки, что приведет к удалению всех пользовательских данных."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Вы неправильно ввели графический ключ несколько раз (<xliff:g id="NUMBER_0">%d</xliff:g>). Осталось попыток: <xliff:g id="NUMBER_1">%d</xliff:g>. После этого настройки телевизора будут сброшены, а все пользовательские данные – удалены."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Вы <xliff:g id="NUMBER_0">%d</xliff:g> раз не смогли разблокировать телефон. После <xliff:g id="NUMBER_1">%d</xliff:g> неверных попыток будут восстановлены заводские настройки, что приведет к удалению всех пользовательских данных."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Вы <xliff:g id="NUMBER_0">%1$d</xliff:g> раз неверно указали графический ключ. \n\nПовтор через <xliff:g id="NUMBER_1">%2$d</xliff:g> сек."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Вы <xliff:g id="NUMBER_0">%1$d</xliff:g> раз неверно указали пароль. \n\nПовтор через <xliff:g id="NUMBER_1">%2$d</xliff:g> сек."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Вы <xliff:g id="NUMBER_0">%1$d</xliff:g> раз неверно указали PIN-код. \n\nПовтор через <xliff:g id="NUMBER_1">%2$d</xliff:g> сек."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Вы <xliff:g id="NUMBER_0">%1$d</xliff:g> раз неверно указали графический ключ. После <xliff:g id="NUMBER_1">%2$d</xliff:g> неверных попыток для разблокировки планшетного ПК потребуется войти в аккаунт Google. \n\n Повтор через <xliff:g id="NUMBER_2">%3$d</xliff:g> сек."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Вы неправильно ввели графический ключ несколько раз (<xliff:g id="NUMBER_0">%1$d</xliff:g>). Осталось попыток: <xliff:g id="NUMBER_1">%2$d</xliff:g>. После этого вам будет предложено разблокировать телевизор с помощью аккаунта Google.\n\nПовторите попытку через <xliff:g id="NUMBER_2">%3$d</xliff:g> сек."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Вы <xliff:g id="NUMBER_0">%1$d</xliff:g> раз неверно указали графический ключ. После <xliff:g id="NUMBER_1">%2$d</xliff:g> неверных попыток для разблокировки телефона потребуется войти в аккаунт Google. \n\n Повтор через <xliff:g id="NUMBER_2">%3$d</xliff:g> сек."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Вы <xliff:g id="NUMBER_0">%1$d</xliff:g> раз не смогли разблокировать планшетный ПК. После <xliff:g id="NUMBER_1">%2$d</xliff:g> неверных попыток будут восстановлены заводские настройки, что приведет к удалению всех пользовательских данных."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Вы неправильно ввели графический ключ несколько раз (<xliff:g id="NUMBER_0">%1$d</xliff:g>). Осталось попыток: <xliff:g id="NUMBER_1">%2$d</xliff:g>. После этого настройки телевизора будут сброшены, а все пользовательские данные – удалены."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Вы <xliff:g id="NUMBER_0">%1$d</xliff:g> раз не смогли разблокировать телефон. После <xliff:g id="NUMBER_1">%2$d</xliff:g> неверных попыток будут восстановлены заводские настройки, что приведет к удалению всех пользовательских данных."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Вы <xliff:g id="NUMBER">%d</xliff:g> раз не смогли разблокировать планшетный ПК. Будут восстановлены заводские настройки."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Вы неправильно ввели графический ключ несколько раз (<xliff:g id="NUMBER">%d</xliff:g>). Настройки телевизора будут сброшены."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Вы <xliff:g id="NUMBER">%d</xliff:g> раз не смогли разблокировать телефон. Будут восстановлены заводские настройки."</string>
@@ -744,8 +744,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"слово"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"ссылка"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"строка"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Не удалось провести стандартный тест"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Действие FACTORY_TEST поддерживается только для пакетов, установленных в /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Пакет, обеспечивающий действие FACTORY_TEST, не найден."</string>
@@ -1087,11 +1085,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Форматирование…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Отсутствует"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Подходящих действий не найдено."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Перенаправление мультимедийных данных"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"Перенаправление вывода мультимедиа"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Приложение сможет направлять поток мультимедиа на другие внешние устройства."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Чтение данных сеансов установки"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"Чтение данных сеансов установки"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Чтение данных текущих сеансов установки пакетов."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Разрешение на установку пакетов"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"Запрос пакетов установки"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Приложение сможет запрашивать разрешения на установку пакетов."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Нажмите дважды для изменения масштаба"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Не удалось добавить виджет."</string>
@@ -1301,18 +1299,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Неверное имя пользователя или пароль."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Забыли имя пользователя или пароль?\nПерейдите на страницу "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Проверка данных…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Вы <xliff:g id="NUMBER_0">%d</xliff:g> раз неверно указали PIN-код. \n\nПовтор через <xliff:g id="NUMBER_1">%d</xliff:g> сек."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Вы <xliff:g id="NUMBER_0">%d</xliff:g> раз неверно указали пароль.\n\nПовтор через <xliff:g id="NUMBER_1">%d</xliff:g> сек."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Вы <xliff:g id="NUMBER_0">%d</xliff:g> раз неверно указали графический ключ.\n\nПовтор через <xliff:g id="NUMBER_1">%d</xliff:g> сек."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Вы <xliff:g id="NUMBER_0">%d</xliff:g> раз не смогли разблокировать планшетный ПК. После <xliff:g id="NUMBER_1">%d</xliff:g> неверных попыток будут восстановлены заводские настройки, что приведет к удалению всех пользовательских данных."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Вы неправильно ввели графический ключ несколько раз (<xliff:g id="NUMBER_0">%d</xliff:g>). Осталось попыток: <xliff:g id="NUMBER_1">%d</xliff:g>. После этого настройки телевизора будут сброшены, а все пользовательские данные – удалены."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Вы <xliff:g id="NUMBER_0">%d</xliff:g> раз не смогли разблокировать телефон. После <xliff:g id="NUMBER_1">%d</xliff:g> неверных попыток будут восстановлены заводские настройки, что приведет к удалению всех пользовательских данных."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Вы <xliff:g id="NUMBER_0">%1$d</xliff:g> раз неверно указали PIN-код. \n\nПовтор через <xliff:g id="NUMBER_1">%2$d</xliff:g> сек."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Вы <xliff:g id="NUMBER_0">%1$d</xliff:g> раз неверно указали пароль.\n\nПовтор через <xliff:g id="NUMBER_1">%2$d</xliff:g> сек."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Вы <xliff:g id="NUMBER_0">%1$d</xliff:g> раз неверно указали графический ключ.\n\nПовтор через <xliff:g id="NUMBER_1">%2$d</xliff:g> сек."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Вы <xliff:g id="NUMBER_0">%1$d</xliff:g> раз не смогли разблокировать планшетный ПК. После <xliff:g id="NUMBER_1">%2$d</xliff:g> неверных попыток будут восстановлены заводские настройки, что приведет к удалению всех пользовательских данных."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Вы неправильно ввели графический ключ несколько раз (<xliff:g id="NUMBER_0">%1$d</xliff:g>). Осталось попыток: <xliff:g id="NUMBER_1">%2$d</xliff:g>. После этого настройки телевизора будут сброшены, а все пользовательские данные – удалены."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Вы <xliff:g id="NUMBER_0">%1$d</xliff:g> раз не смогли разблокировать телефон. После <xliff:g id="NUMBER_1">%2$d</xliff:g> неверных попыток будут восстановлены заводские настройки, что приведет к удалению всех пользовательских данных."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Вы <xliff:g id="NUMBER">%d</xliff:g> раз не смогли разблокировать планшетный ПК. Будут восстановлены заводские настройки."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Вы неправильно ввели графический ключ несколько раз (<xliff:g id="NUMBER">%d</xliff:g>). Настройки телевизора будут сброшены."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Вы <xliff:g id="NUMBER">%d</xliff:g> раз не смогли разблокировать телефон. Будут восстановлены заводские настройки."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Вы <xliff:g id="NUMBER_0">%d</xliff:g> раз неверно указали графический ключ. После <xliff:g id="NUMBER_1">%d</xliff:g> неверных попыток для разблокировки планшетного ПК потребуется войти в аккаунт Google.\n\nПовтор через <xliff:g id="NUMBER_2">%d</xliff:g> сек."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Вы неправильно ввели графический ключ несколько раз (<xliff:g id="NUMBER_0">%d</xliff:g>). Осталось попыток: <xliff:g id="NUMBER_1">%d</xliff:g>. После этого вам будет предложено разблокировать телевизор с помощью аккаунта электронной почты.\n\nПовторите попытку через <xliff:g id="NUMBER_2">%d</xliff:g> сек."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Вы <xliff:g id="NUMBER_0">%d</xliff:g> раз неверно указали графический ключ. После <xliff:g id="NUMBER_1">%d</xliff:g> неверных попыток для разблокировки телефона потребуется войти в аккаунт Google.\n\nПовтор через <xliff:g id="NUMBER_2">%d</xliff:g> сек."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Вы <xliff:g id="NUMBER_0">%1$d</xliff:g> раз неверно указали графический ключ. После <xliff:g id="NUMBER_1">%2$d</xliff:g> неверных попыток для разблокировки планшетного ПК потребуется войти в аккаунт Google.\n\nПовтор через <xliff:g id="NUMBER_2">%3$d</xliff:g> сек."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Вы неправильно ввели графический ключ несколько раз (<xliff:g id="NUMBER_0">%1$d</xliff:g>). Осталось попыток: <xliff:g id="NUMBER_1">%2$d</xliff:g>. После этого вам будет предложено разблокировать телевизор с помощью аккаунта электронной почты.\n\nПовторите попытку через <xliff:g id="NUMBER_2">%3$d</xliff:g> сек."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Вы <xliff:g id="NUMBER_0">%1$d</xliff:g> раз неверно указали графический ключ. После <xliff:g id="NUMBER_1">%2$d</xliff:g> неверных попыток для разблокировки телефона потребуется войти в аккаунт Google.\n\nПовтор через <xliff:g id="NUMBER_2">%3$d</xliff:g> сек."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Удалить"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Установить громкость выше рекомендуемого уровня?\n\nВоздействие громкого звука в течение долгого времени может привести к повреждению слуха."</string>
@@ -1504,6 +1502,7 @@
<item quantity="other">На %d часа</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Следующий сигнал в <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Пока я не отключу"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Пока вы не отключите режим \"Не беспокоить\""</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-si-rLK-watch/strings.xml b/core/res/res/values-si-rLK-watch/strings.xml
index aad82a0..3df0775 100644
--- a/core/res/res/values-si-rLK-watch/strings.xml
+++ b/core/res/res/values-si-rLK-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_0">%1$d</xliff:g> හි <xliff:g id="NUMBER_1">%2$d</xliff:g> යෙදුම."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"සංවේදක"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"ඔබේ සම්බන්ධතාවලට පිවිසීම"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"මෙම ඔරලෝසුවෙහි ස්ථානයට පිවිසීම"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"ඔබේ දින දර්ශනයට පිවිසීම"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"SMS පණිවිඩ යැවීම සහ බැලීම"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"ඔබේ ඔරලෝසුවේ ඇති ඡායාරූප, මාධ්ය සහ ගොනුවලට පිවිසීම"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"ශ්රව්ය පටිගත කිරීම"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"පින්තූර ගැනීම සහ වීඩියෝ පටිගත කිරීම"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"දුරකථන ඇමතුම් සිදු කිරීම සහ කළමනාකරණය කිරීම"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"ඔබේ ජෛව ලක්ෂණ පිළිබඳ සංවේදක දත්ත වෙත පිවිසීම"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"තත්ත්ව තීරුව බවට පත්වීම"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"දේහ සංවේදකවලට (හෘද ස්පන්දන වේග මොනිටර වැනි) පිවිසීම"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"නිවැරදි ස්ථානයට (GPS සහ ජාලය පදනම් කරගත්) පිවිසීම"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"ආසන්නතම ස්ථානයට (ජාලය-පාදක වූ) පිවිසීම"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"SIM වෙත විධාන යැවීම"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"සම්පූර්ණ ජාල ප්රවේශය තබා ගැනීම"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"පැතිකඩ සහ උපාංග හිමිකරුවන් කළමනාකරණය කිරීම"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"WiMAX තත්ත්වය වෙනස් කිරීම"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"Android කදම්බ හුවමාරු තත්ත්වය ලබා ගැනීම"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"මාධ්ය ප්රතිදානය මාර්ගගත කිරීම"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"ස්ථාපන සැසි කියවීම"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"ස්ථාපන පැකේජ ඉල්ලීම"</string>
</resources>
diff --git a/core/res/res/values-si-rLK/strings.xml b/core/res/res/values-si-rLK/strings.xml
index 582f912..c6f0d08 100644
--- a/core/res/res/values-si-rLK/strings.xml
+++ b/core/res/res/values-si-rLK/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ණයවරපත් අංක සහ මුරපද වැනි පුද්ගලික දත්ත ඇතුළත් වේ."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"තත්ව තීරුව අබල කරන්න හෝ වෙනස් කරන්න"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"තත්ව තීරුව අක්රිය කිරීමට හෝ පද්ධති නිරූපක එකතු හෝ ඉවත් කිරීමට යෙදුමට අවසර දේ."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"තත්ව තීරුව"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"තත්ත්ව තීරුව බවට පත්වීම"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"තත්ව තීරුව වීමට යෙදුමට අවසර දෙන්න."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"තත්ව තීරුව දිග හැරීම/හැකිලීම"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"තත්ව තීරුව දිග හැරීමට හෝ හැකිළීමට යෙදුමට අවසර දෙන්න."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"WAP පණිවිඩ ලැබීමට සහ ක්රියාවලි කිරීමට යෙදුමට අවසර දෙන්න. මෙම අවසරයෙහි ඔබව ඒවාට පෙන්වීමකින් තොරව ඔබට පණිවිඩ නිරීක්ෂණයට හෝ මැකීමට හැකියාව ඇතුළත් වේ."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"ධාවනය වන යෙදුම් ලබාගැනීම"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"දැනට සහ මෑත ක්රියාත්මක කාර්යයන් පිළිබඳ විස්තරාත්මක තොරතුරු සොයා ලබාගැනීමට යෙදුමට ඉඩ දෙන්න. මෙය කුමන යෙදුම් උපාංගයේ භාවිතා කරන්නේද යන තොරතුරු යෙදුම්වලට සොයා ගැනීමට ඉඩ දිය හැක."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"පැතිකඩ සහ උපාංග හිමිකරුවන් කළමනාකරණය කරන්න"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"පැතිකඩ සහ උපාංග හිමිකරුවන් කළමනාකරණය කිරීම"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"යෙදුම්වලට පැතිකඩ හිමියන් සහ උපාංග හිමිකරු සැකසීමට ඉඩ දෙයි."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"ධාවනය වන යෙදුම් නැවත අනුපිළිවෙලට සැකසීම"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"පෙරබිමට හෝ පසුබිමට සිදුවීම් ගෙනයාමට යෙදුමට අවසර දෙන්න. ඔබගේ ආදානයකින් තොරව යෙදුම මෙය සිදුකරයි."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"ලැබෙන ඇමතුම් සහ පිටවන ඇමතුම් දත්ත ඇතුළත්ව ඔබගේ ටැබ්ලටයේ ඇමතුම් ලොගය වෙනස් කිරීමට යෙදුමට අවසර දෙන්න. ඔබගේ ඇමතුම් ලොගය මැකීමට හෝ වෙනස් කිරීමට අනිෂ්ට යෙදුම් මෙය භාවිතා කෙරේ."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"ලැබෙන ඇමතුම් සහ පිටවන ඇමතුම් දත්ත ඇතුළත්ව ඔබගේ ටැබ්ලටයේ ඇමතුම් ලොගය වෙනස් කිරීමට යෙදුමට අවසර දෙන්න. ඔබගේ ඇමතුම් ලොගය මැකීමට හෝ වෙනස් කිරීමට අනිෂ්ට යෙදුම් මෙය භාවිතා කෙරේ."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"පැමිණෙන සහ පිටවෙන ඇමතුම් දත්ත ඇතුළුව ඔබගේ දුරකථනයේ ඇමතුම් ලොගය වෙනස් කිරීමට යෙදුමට අවසර දෙන්න. ඔබගේ ඇමතුම් ලොගය මැකීමට හෝ වෙනස් කිරීමට අනිෂ්ට යෙදුම් මෙය භාවිත කල හැක."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"සිරුර සංවේදකයන් (හෘද ස්පන්දන වේගය නිරීක්ෂණය කිරීම වැනි)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"දේහ සංවේදකවලට (හෘද ස්පන්දන වේග මොනිටර වැනි) පිවිසීම"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"හෘද ස්පන්දන වේගය වැනි ඔබගේ ශාරීරික තත්ත්වය නිරීක්ෂණය කරන සංවේදක වලින් දත්ත ලබාගැනීමට යෙදුමට ඉඩ දෙන්න."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"දින දර්ශනයේ සිදුවීම් සහ රහසිගත තොරතුරු කියවීම"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"ඔබගේ ටැබ්ලටය තුල ගබඩා කර ඇති මිතුරන්ගේ සහ එක්ව ක්රියාකරන්නන්ගේ ද ඇතුළුව සියලුම දින දර්ශන සිද්ධි කියවීමට යෙදුමට අවසර දෙන්න. මෙය රහස්යභාවය හෝ සංවේදීතාවය නොසලකා ඔබගේ දින දර්ශන දත්ත බෙදා ගැනීමට හෝ සුරැකීමට යෙදුමට අවසර දෙන්න."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"ඔබගේ යහළුවන් හෝ සමකාලීනයන් ඇතුළත් ඔබගේ දුරකථනයේ ඔබට වෙනස් කළ හැකි සිදු වීම් එකතු කිරීමට, ඉවත් කිරීමට, වෙනස් කිරීමට යෙදුමට අවසර දෙන්න. මෙමගින් දින දර්ශන හිමිකරුවන්ගෙන් පැමිණෙන සේ පෙනෙන පණිවිඩ යැවීමට හෝ හිමිකරුගේ දැනුමකින් තොරව සිදුවීම් වෙනස් කිරීමට යෙදුමට අවසර දෙයි."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"අමතර ස්ථාන සැපයුම්කරු විධාන වෙත ප්රවේශ වීම"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"ස්ථානය සපයන අමතර අණ වලට ප්රවේශය කිරීමට යෙදුමට අවසර දෙන්න. GPS ක්රියාවන් හෝ වෙනත් ස්ථාන මූලාශ්ර සමඟ මැදිහත් වීමට මෙයින් යෙදුමට ඉඩ ලැබේ."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"නිවැරදි ස්ථානය (GPS සහ ජාලය පදනම් කරගත්)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"නිවැරදි ස්ථානයට (GPS සහ ජාලය පදනම් කරගත්) පිවිසීම"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"ගෝලීය ස්ථානීය පද්ධතිය (GPS) හෝ සෙල් කුළුණු සහ Wi-Fi වැනි ජාල ස්ථානීය ප්රභව භාවිතයෙන් ඔබගේ නිවැරදි ස්ථානය ලබාගැනීමට යෙදුම අවසර දෙන්න. යෙදුම් වලට ස්ථානීය සේවා භාවිතා කිරීමට ඒවා සක්රිය විය යුතු වේ. ඔබව සොයා ගැනීමට යෙදුම් මෙය භාවිතා කරන අතර අමතර බැටරි බලයක්ද පරිභෝජනය කරයි."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"ආසන්නතම ස්ථානය (ජාලය-පාදක වූ)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"ආසන්නතම ස්ථානයට (ජාලය-පාදක වූ) පිවිසීම"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"ඔබගේ දළ ස්ථානය ලබාගැනීමට යෙදුමට අවසර දෙන්න. සන්නේවේදන කුළුණු සහ Wi-Fi ආදී ජාල ස්ථාන මූලාශ්ර භාවිත කරන ස්ථාන සේවා විසින් මෙම ස්ථානය ව්යුත්පන්න කර ඇත. යෙදුමට භාවිතය සඳහා මෙම ස්ථාන සේවා සක්රිය කළ යුතු අතර ඔබගේ උපාංගය සඳහා පැවතිය යුතුය. ඔබ සිටින තැන දළව හඳුනා ගැනීමට යෙදුම් වලට මෙය භාවිත කළ හැකිය."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"ඔබගේ ශ්රව්ය සැකසීම් වෙනස් කරන්න"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"ශබ්දය ආදී ගෝලීය ශබ්ද සැකසීම් වෙනස් කිරීමට සහ ප්රතිදානය සඳහා භාවිත කරන්නේ කුමන නාදකය දැයි තේරීමට යෙදුමට අවසර දෙන්න."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"ශබ්ද පටිගත කරන්න"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"මයික්රොෆෝනය මඟින් ශබ්ද පටිගත කිරීමට යෙදුමට අවසර දෙන්න. මෙම අවසරය මඟින් යෙදුමට ඕනෑම වේලාවක ඔබගේ අනුදැනුමකින් තොරව ශබ්ද පටිගත කිරීමට ඉඩ ලබා දේ."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"සිම් සන්නිවේදනය"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"SIM වෙත විධාන යැවීම"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"SIM වෙත විධාන ගෙන යාමට යෙදුමට අවසර දෙයි. මෙය ඉතා භයානක වේ."</string>
<string name="permlab_camera" msgid="3616391919559751192">"පින්තූර සහ වීඩියෝ ගන්න"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"කැමරාවෙන් පින්තූර ගැනීමට සහ වීඩියෝ කිරීමට යෙදුමට අවසර දෙන්න. මෙම අවසරය මඟින් ඔබගේ අනුදැනුමකින් තොරව ඕනෑම වේලාවකදී කැමරාව භාවිතා කිරීමට යෙදුමට අවසර දෙන්න."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"දුරකථනය විසින් දන්නා ගිණුම් ලැයිස්තුවක් ලබාගැනීමට යෙදුමට අවසර දෙන්න. ඔබ ස්ථාපනය කොට ඇති යෙදුම් විසින් සාදා ඇති ගිණුම් මීට ඇතුළත් වේ."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"ජාල සම්බන්ධතාවයන් බැලීම"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"කුමන ජාල පවතින්නේ ද සහ සම්බන්ධිත ද ආදී ජාල සබඳතා ගැන තොරතුරු බැලීමට යෙදුමට අවසර දෙයි."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"සම්පූර්ණ ජාල ප්රවේශය"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"සම්පූර්ණ ජාල ප්රවේශය තබා ගැනීම"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"උපකරණයට ජාල කෙවනියන් සැදීමට සහ ජාල ප්රොටෝකෝල අභිරුචි භාවිතා කිරීමට උපකරණයට ඉඩ දෙන්න. අන්තර්ජාලයට දත්ත යැවීමට විධියන් බ්රව්සරය සහ අනෙකුත් යෙදුම් සපයයි, එනිසා මෙම අවසරය දත්ත අන්තර්ජාලයට යැවීමට අවශ්ය නොවේ."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"ජාල සම්බන්ධතාව වෙනස් කිරීම"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"ජාල සම්බන්ධතාවයේ තත්වය වෙනස් කිරීමට යෙදුමට අවසර දෙන්න."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"දුරකථනයේ පෙදෙසි බ්ලූටූත් වින්යාස කිරීමට, සහ දුරස්ථ උපාංග ගවේෂණයට සහ යුගල වීමට යෙදුමට අවසර දෙයි."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"WiMAX වෙතට සම්බන්ධ කරන්න හෝ විසන්ධි කරන්න"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"WiMAX සබල බව සහ සම්බන්ධිත ඕනෑම WiMAX ජාලයක තොරතුරු නිශ්චය කිරීමට යෙදුමට අවසර දෙයි."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX තත්වය වෙනස් කරන්න"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"WiMAX තත්වය වෙනස් කිරීම"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"ටැබ්ලටය WiMAX ජාල වෙත සම්බන්ධ කිරීමට සහ විසන්ධි කිරීමට යෙදුමට අවසර දෙන්න."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"WiMAX ජාල වෙතින් රූපවාහිනිය සම්බන්ධ කරන්න සහ රූපවාහිනිය විසන්ධි කරන්න යෙදුමට ඉඩ දෙන්න."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"WiMAX ජාලයන්ට දුරකථනය සම්බන්ධ කිරීමට සහ විසන්ධි කිරීමට යෙදුමට අවසර දෙන්න."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"යෙදුම් වලට ස්පර්ශ තිරයේ ක්රමාංකන පරාමිති වෙනස් කිරීමට ඉඩ දේ. සාමාන්ය යෙදුම් වලට කිසිදා අවශ්ය නොවේ."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"DRM සහතික ප්රවේශය"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"යෙදුමකට DRM සහතික වෙන් කර භාවිතා කිරීමට ඉඩ දේ. සාමාන්ය යෙදුම් වලට කිසිදා අවශ්ය නොවේ."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Android බීම් හුවමාරු තත්ත්වය ලබාගන්න"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"Android කදම්බ හුවමාරු තත්ත්වය ලබා ගැනීම"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"දැනට තිබෙන Android බීම් හුවමාරු පිළිබඳ තොරතුරු ලබාගැනීමට මෙම යෙදුමට ඉඩ දෙන්න"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"DRM සහතික ඉවත් කරන්න"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"යෙදුමකට DRM සහතික ඉවත් කිරීමට ඉඩ දේ. සාමාන්ය යෙදුම් වලට කිසිදා අවශ්ය නොවේ."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"වැරදි PIN කේතයකි."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"අගුළු ඇරීමට, මෙනුව ඔබා පසුව 0 ද ඔබන්න."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"හදිසි ඇමතුම් අංකය"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"සේවාව නැත."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"සේවාව නැත"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"තිරය අගුළු දමා ඇත."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"අගුළු හැරීමට මෙනුව ඔබන්න හෝ හදිසි ඇමතුම ලබාගන්න."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"අගුළු හැරීමට මෙනු ඔබන්න."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"පරිශීලක උපදේශය බලන්න හෝ පරිභෝගික සේවාව අමතන්න."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM පත අගුළු දමා ඇත."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"SIM පත අගුළු අරිමින්..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"ඔබ <xliff:g id="NUMBER_0">%d</xliff:g> වාරයක් අගුළු ඇරීමේ රටාව වැරදියට ඇඳ ඇත. \n\nතත්පර <xliff:g id="NUMBER_1">%d</xliff:g> ක් ඇතුළත නැවත උත්සාහ කරන්න."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"ඔබ මුරපදය වාර <xliff:g id="NUMBER_0">%d</xliff:g> ක් වැරදියට ටයිප්කොට ඇත. \n\nතත්පර <xliff:g id="NUMBER_1">%d</xliff:g> කින් නැවත උත්සහ කරන්න."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"ඔබ PIN අංකය <xliff:g id="NUMBER_0">%d</xliff:g> වාරයක් වැරදියට ටයිප් කොට ඇත.\n\n තත්පර <xliff:g id="NUMBER_1">%d</xliff:g> ක් ඇතුළත නැවත උත්සාහ කරන්න."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"ඔබ වැරදියට <xliff:g id="NUMBER_0">%d</xliff:g> වතාවක් ඔබගේ අගුළු හැරීමේ රටාව ඇඳ ඇත. අසාර්ථක උත්සහ කිරීම් <xliff:g id="NUMBER_1">%d</xliff:g> න් පසුව, ඔබගේ Google පුරනය වීම් භාවිතයෙන් ඔබගේ ටැබ්ලටය අගුළු හැරීමට ඔබගෙන් අසයි.\n\n තත්පර <xliff:g id="NUMBER_2">%d</xliff:g> පසුව නැවත උත්සහ කරන්න."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"ඔබ ඔබගේ අගුළු හැරීමේ මෝස්තරය <xliff:g id="NUMBER_0">%d</xliff:g> වරක් වැරදියට ඇඳ ඇත. තවත් අසාර්ථක උත්සාහ <xliff:g id="NUMBER_1">%d</xliff:g> කින් පසුව, ඔබට Google පුරනය භාවිතයෙන් ඔබගේ රූපවාහිනිය අගුළු හැරීමට සිදු වනු ඇත.\n\n තත්පර <xliff:g id="NUMBER_2">%d</xliff:g> කින් පසුව නැවත උත්සාහ කරන්න."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"ඔබ වැරදියට <xliff:g id="NUMBER_0">%d</xliff:g> වතාවක් ඔබගේ අගුළු හැරීමේ රටාව ඇඳ ඇත. අසාර්ථක උත්සහ කිරීම් <xliff:g id="NUMBER_1">%d</xliff:g> න් පසුව, ඔබගේ Google පුරනය වීම භාවිතයෙන් ඔබගේ දුරකථනය අගුළු හැරීමට ඔබගෙන් අසනු ඇත.\n\n තත්පර <xliff:g id="NUMBER_2">%d</xliff:g> පසුව නැවත උත්සහ කරන්න."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"ඔබ ටැබ්ලටය අගුළු හැරීමට වැරදියට අවස්ථා <xliff:g id="NUMBER_0">%d</xliff:g> ක් උත්සාහ කර ඇත. අවස්ථා <xliff:g id="NUMBER_1">%d</xliff:g> ක් අසාර්ථකව උත්සහ කිරීමකින් පසුව, ටැබ්ලටය කර්මාන්ත ශාලා මුල් තත්වයට නැවත පත් වන අතර සියලු පරිශීලක දත්ත නැති වෙයි."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"ඔබ රූපවාහිනිය අගුළු හැරීමට <xliff:g id="NUMBER_0">%d</xliff:g> වරක් වැරදිව උත්සාහ කර ඇත. තවත් වැරදි උත්සාහ <xliff:g id="NUMBER_1">%d</xliff:g> කට පසුව, රූපවාහිනිය කර්මාන්තශාලා සුපුරුද්දට නැවත සකස් කෙරෙනු ඇති අතර, සියලුම පරිශීලක දත්ත නැති වී යනු ඇත."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"ඔබ දුරකථනය අගුළු ඇරීමට වාර <xliff:g id="NUMBER_0">%d</xliff:g> කදී වැරදී ප්රයත්නයන් ගෙන තිබේ. තවත් අසාර්ථක ප්රයත්න <xliff:g id="NUMBER_1">%d</xliff:g> කින් පසුව, දුරකථනය කර්මාන්තශාලාවේ පෙරනිමියට යළි පිහිටුවන අතර සියලුම පරිශීලක දත්ත නැති වී යයි."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"ඔබ <xliff:g id="NUMBER_0">%1$d</xliff:g> වාරයක් අගුළු ඇරීමේ රටාව වැරදියට ඇඳ ඇත. \n\nතත්පර <xliff:g id="NUMBER_1">%2$d</xliff:g> ක් ඇතුළත නැවත උත්සාහ කරන්න."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"ඔබ මුරපදය වාර <xliff:g id="NUMBER_0">%1$d</xliff:g> ක් වැරදියට ටයිප්කොට ඇත. \n\nතත්පර <xliff:g id="NUMBER_1">%2$d</xliff:g> කින් නැවත උත්සහ කරන්න."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"ඔබ PIN අංකය <xliff:g id="NUMBER_0">%1$d</xliff:g> වාරයක් වැරදියට ටයිප් කොට ඇත.\n\n තත්පර <xliff:g id="NUMBER_1">%2$d</xliff:g> ක් ඇතුළත නැවත උත්සාහ කරන්න."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"ඔබ වැරදියට <xliff:g id="NUMBER_0">%1$d</xliff:g> වතාවක් ඔබගේ අගුළු හැරීමේ රටාව ඇඳ ඇත. අසාර්ථක උත්සහ කිරීම් <xliff:g id="NUMBER_1">%2$d</xliff:g> න් පසුව, ඔබගේ Google පුරනය වීම් භාවිතයෙන් ඔබගේ ටැබ්ලටය අගුළු හැරීමට ඔබගෙන් අසයි.\n\n තත්පර <xliff:g id="NUMBER_2">%3$d</xliff:g> පසුව නැවත උත්සහ කරන්න."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"ඔබ ඔබගේ අගුළු හැරීමේ මෝස්තරය <xliff:g id="NUMBER_0">%1$d</xliff:g> වරක් වැරදියට ඇඳ ඇත. තවත් අසාර්ථක උත්සාහ <xliff:g id="NUMBER_1">%2$d</xliff:g> කින් පසුව, ඔබට Google පුරනය භාවිතයෙන් ඔබගේ රූපවාහිනිය අගුළු හැරීමට සිදු වනු ඇත.\n\n තත්පර <xliff:g id="NUMBER_2">%3$d</xliff:g> කින් පසුව නැවත උත්සාහ කරන්න."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"ඔබ වැරදියට <xliff:g id="NUMBER_0">%1$d</xliff:g> වතාවක් ඔබගේ අගුළු හැරීමේ රටාව ඇඳ ඇත. අසාර්ථක උත්සහ කිරීම් <xliff:g id="NUMBER_1">%2$d</xliff:g> න් පසුව, ඔබගේ Google පුරනය වීම භාවිතයෙන් ඔබගේ දුරකථනය අගුළු හැරීමට ඔබගෙන් අසනු ඇත.\n\n තත්පර <xliff:g id="NUMBER_2">%3$d</xliff:g> පසුව නැවත උත්සහ කරන්න."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"ඔබ ටැබ්ලටය අගුළු හැරීමට වැරදියට අවස්ථා <xliff:g id="NUMBER_0">%1$d</xliff:g> ක් උත්සාහ කර ඇත. අවස්ථා <xliff:g id="NUMBER_1">%2$d</xliff:g> ක් අසාර්ථකව උත්සහ කිරීමකින් පසුව, ටැබ්ලටය කර්මාන්ත ශාලා මුල් තත්වයට නැවත පත් වන අතර සියලු පරිශීලක දත්ත නැති වෙයි."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"ඔබ රූපවාහිනිය අගුළු හැරීමට <xliff:g id="NUMBER_0">%1$d</xliff:g> වරක් වැරදිව උත්සාහ කර ඇත. තවත් වැරදි උත්සාහ <xliff:g id="NUMBER_1">%2$d</xliff:g> කට පසුව, රූපවාහිනිය කර්මාන්තශාලා සුපුරුද්දට නැවත සකස් කෙරෙනු ඇති අතර, සියලුම පරිශීලක දත්ත නැති වී යනු ඇත."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"ඔබ දුරකථනය අගුළු ඇරීමට වාර <xliff:g id="NUMBER_0">%1$d</xliff:g> කදී වැරදී ප්රයත්නයන් ගෙන තිබේ. තවත් අසාර්ථක ප්රයත්න <xliff:g id="NUMBER_1">%2$d</xliff:g> කින් පසුව, දුරකථනය කර්මාන්තශාලාවේ පෙරනිමියට යළි පිහිටුවන අතර සියලුම පරිශීලක දත්ත නැති වී යයි."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"ටැබ්ලටයේ අගුළු ඇරීමට ඔබ වැරදි ප්රයත්න <xliff:g id="NUMBER">%d</xliff:g> වාරයක් ගෙන ඇත. දැන් ටැබ්ලටය කර්මාන්තශාලා සුපුරුද්ද වෙත යළි පිහිටුවීම කෙරේ."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"ඔබ රූපවාහිනිය අගුළු හැරීමට <xliff:g id="NUMBER">%d</xliff:g> වරක් වැරදිව උත්සාහ කර ඇත. දැන් රූපවාහිනිය කර්මාන්තශාලා සුපුරුද්දට නැවත සකස් කෙරෙනු ඇත."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"ඔබ දුරකථනය අගුළු ඇරීමට වාර <xliff:g id="NUMBER">%d</xliff:g> කදී වැරදී ප්රයත්නයන් ගෙන තිබේ. දැන් දුරකථනය කර්මාන්තශාලා පෙරනිමියට පිහිටුවනු ලබයි."</string>
@@ -744,8 +744,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"වචනය"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"සබැඳිය"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"රේඛාව"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"කර්මාන්ත ශාලා පරීක්ෂණය අසාර්ථකයි"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"FACTORY_TEST ක්රියාව /system/app හි ස්ථාපිත පැකේජ සඳහා පමණක් සහය දක්වයි."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"FACTORY_TEST ක්රියාව ලබාදෙන පැකේජයක් සොයාගත නොහැකි විය."</string>
@@ -1075,11 +1073,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"ෆෝමැට් කරමින්…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"ඇතුළු කර නැත"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"ගැලපෙන ක්රියාකාරකම් හමු නොවුණි."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"මාධ්ය ප්රතිදානයේ මාර්ගගත කිරීම"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"මාධ්ය ප්රතිදානය මාර්ගගත කිරීම"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"වෙනත් බාහිර උපාංග වෙත මාධ්ය ප්රතිදානය යැවීමට යෙදුමට අවසර දෙන්න."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"ස්ථාපන සැසියන් කියවන්න"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"ස්ථාපන සැසි කියවීම"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"ස්ථාපන සැසිය කියවීමට යෙදුමට ඉඩ දෙන්න. සක්රිය පැකේජ ස්ථාපනය පිළිබඳ විස්තර බැලීමට එයට මෙයින් ඉඩ දෙයි."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"ස්ථාපන පැකේජ ඉල්ලීම"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"ස්ථාපන පැකේජ ඉල්ලීම"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"ස්ථාපන පැකේජ ඉල්ලීමට යෙදුමකට අවසර දීම."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"විශාලන පාලනය සඳහා දෙවරක් ස්පර්ශ කරන්න"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"විජටය එකතු කිරීමට නොහැකි විය."</string>
@@ -1287,18 +1285,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"වලංගු නොවන පරිශීලක නාමයක් හෝ මුරපදයක්."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"ඔබගේ පරිශීලක නාමය හෝ මුරපදය අමතකද?\n "<b>"google.com/accounts/recovery"</b>" වෙත යන්න."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"ගිණුම පරීක්ෂා කරමින්…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"ඔබ PIN අංකය <xliff:g id="NUMBER_0">%d</xliff:g> වාරයක් වැරදියට ටයිප් කොට ඇත.\n\n තත්පර <xliff:g id="NUMBER_1">%d</xliff:g> ක් ඇතුළත නැවත උත්සාහ කරන්න."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"<xliff:g id="NUMBER_0">%d</xliff:g> වතාවක් ඔබගේ මුරපදය ඔබ වැරදියට ටයිප් කර ඇත. \n\nතත්පර <xliff:g id="NUMBER_1">%d</xliff:g> ට පසුව නැවත උත්සහ කරන්න."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"ඔබ <xliff:g id="NUMBER_0">%d</xliff:g> වාරයක් අගුළු ඇරීමේ රටාව වැරදියට ඇඳ ඇත. \n\nතත්පර <xliff:g id="NUMBER_1">%d</xliff:g> ක් ඇතුළත නැවත උත්සාහ කරන්න."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"ඔබ ටැබ්ලටය අගුළු හැරීමට වැරදියට අවස්ථා <xliff:g id="NUMBER_0">%d</xliff:g> ක් උත්සාහ කර ඇත. අවස්ථා <xliff:g id="NUMBER_1">%d</xliff:g> ක් අසාර්ථකව උත්සහ කිරීමකින් පසුව, කර්මාන්ත ශාලා මුල් තත්වයට නැවත පත් වන අතර සියලු පරිශීලක දත්ත නැති වෙයි."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"ඔබ රූපවාහිනිය අගුළු හැරීමට <xliff:g id="NUMBER_0">%d</xliff:g> වරක් වැරදිව උත්සාහ කර ඇත. තවත් වැරදි උත්සාහ <xliff:g id="NUMBER_1">%d</xliff:g> කට පසුව, රූපවාහිනිය කර්මාන්තශාලා සුපුරුද්දට නැවත සකස් කෙරෙනු ඇති අතර, සියලුම පරිශීලක දත්ත නැති වී යනු ඇත."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"ඔබ දුරකථනය අගුළු ඇරීමට වාර <xliff:g id="NUMBER_0">%d</xliff:g> කදී වැරදී ප්රයත්නයන් ගෙන තිබේ. තවත් අසාර්ථක ප්රයත්න <xliff:g id="NUMBER_1">%d</xliff:g> කින් පසුව, දුරකථනය කර්මාන්ත ශාලාවේ සුපුරුද්දට යළි පිහිටුවන අතර සියලුම පරිශීලක දත්ත නැති වී යයි."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"ඔබ PIN අංකය <xliff:g id="NUMBER_0">%1$d</xliff:g> වාරයක් වැරදියට ටයිප් කොට ඇත.\n\n තත්පර <xliff:g id="NUMBER_1">%2$d</xliff:g> ක් ඇතුළත නැවත උත්සාහ කරන්න."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"<xliff:g id="NUMBER_0">%1$d</xliff:g> වතාවක් ඔබගේ මුරපදය ඔබ වැරදියට ටයිප් කර ඇත. \n\nතත්පර <xliff:g id="NUMBER_1">%2$d</xliff:g> ට පසුව නැවත උත්සහ කරන්න."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"ඔබ <xliff:g id="NUMBER_0">%1$d</xliff:g> වාරයක් අගුළු ඇරීමේ රටාව වැරදියට ඇඳ ඇත. \n\nතත්පර <xliff:g id="NUMBER_1">%2$d</xliff:g> ක් ඇතුළත නැවත උත්සාහ කරන්න."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"ඔබ ටැබ්ලටය අගුළු හැරීමට වැරදියට අවස්ථා <xliff:g id="NUMBER_0">%1$d</xliff:g> ක් උත්සාහ කර ඇත. අවස්ථා <xliff:g id="NUMBER_1">%2$d</xliff:g> ක් අසාර්ථකව උත්සහ කිරීමකින් පසුව, කර්මාන්ත ශාලා මුල් තත්වයට නැවත පත් වන අතර සියලු පරිශීලක දත්ත නැති වෙයි."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"ඔබ රූපවාහිනිය අගුළු හැරීමට <xliff:g id="NUMBER_0">%1$d</xliff:g> වරක් වැරදිව උත්සාහ කර ඇත. තවත් වැරදි උත්සාහ <xliff:g id="NUMBER_1">%2$d</xliff:g> කට පසුව, රූපවාහිනිය කර්මාන්තශාලා සුපුරුද්දට නැවත සකස් කෙරෙනු ඇති අතර, සියලුම පරිශීලක දත්ත නැති වී යනු ඇත."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"ඔබ දුරකථනය අගුළු ඇරීමට වාර <xliff:g id="NUMBER_0">%1$d</xliff:g> කදී වැරදී ප්රයත්නයන් ගෙන තිබේ. තවත් අසාර්ථක ප්රයත්න <xliff:g id="NUMBER_1">%2$d</xliff:g> කින් පසුව, දුරකථනය කර්මාන්ත ශාලාවේ සුපුරුද්දට යළි පිහිටුවන අතර සියලුම පරිශීලක දත්ත නැති වී යයි."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"ටැබ්ලටයේ අගුළු ඇරීමට ඔබ වැරදි ප්රයත්න <xliff:g id="NUMBER">%d</xliff:g> වාරයක් ගෙන ඇත. දැන් ටැබ්ලටය කර්මාන්ත ශාලා සුපුරුද්ද වෙත යළි පිහිටුවීම කෙරේ."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"ඔබ රූපවාහිනිය අගුළු හැරීමට <xliff:g id="NUMBER">%d</xliff:g> වරක් වැරදිව උත්සාහ කර ඇත. දැන් රූපවාහිනිය කර්මාන්තශාලා සුපුරුද්දට නැවත සකස් කෙරෙනු ඇත."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"ඔබ දුරකථනය අගුළු ඇරීමට වාර <xliff:g id="NUMBER">%d</xliff:g> කදී වැරදී ප්රයන්තයන් ගෙන තිබේ. දැන් දුරකථනය කර්මාන්තශාලා සුපුරුද්දට පිහිටුවනු ලබයි."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"ඔබ අගුළු ඇරිමේ රටාව <xliff:g id="NUMBER_0">%d</xliff:g> වතාවක් වැරදියට ඇඳ ඇත. තවත් අසාර්ථක උත්සාහ <xliff:g id="NUMBER_1">%d</xliff:g> කින් පසුව, ඊ-තැපැල් ගිණුම භාවිතා කරමින් ඔබගේ ටැබ්ලටයේ අගුළු ඇරීමට ඔබට පවසනු ඇත.\n\n නැවත තත්පර <xliff:g id="NUMBER_2">%d</xliff:g> කින් උත්සාහ කරන්න."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"ඔබ ඔබගේ අගුළු හැරීමේ මෝස්තරය <xliff:g id="NUMBER_0">%d</xliff:g> වරක් වැරදියට ඇඳ ඇත. තවත් අසාර්ථක උත්සාහ <xliff:g id="NUMBER_1">%d</xliff:g> කින් පසුව, ඔබට ඊ-තැපැල් ගිණුමක් භාවිතයෙන් ඔබගේ රූපවාහිනිය අගුළු හැරීමට සිදු වනු ඇත.\n\n තත්පර <xliff:g id="NUMBER_2">%d</xliff:g> කින් පසුව නැවත උත්සාහ කරන්න."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"ඔබ වැරදියට <xliff:g id="NUMBER_0">%d</xliff:g> වතාවක් ඔබගේ අගුළු හැරීමේ රටාව ඇඳ ඇත. අසාර්ථක උත්සහ කිරීම් <xliff:g id="NUMBER_1">%d</xliff:g> න් පසුව, ඔබගේ ඊ-තැපැල් ලිපිනය භාවිතයෙන් ඔබගේ දුරකථනය අගුළු හැරීමට ඔබගෙන් අසයි.\n\n තත්පර <xliff:g id="NUMBER_2">%d</xliff:g> න් පසුව නැවත උත්සහ කරන්න."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"ඔබ අගුළු ඇරිමේ රටාව <xliff:g id="NUMBER_0">%1$d</xliff:g> වතාවක් වැරදියට ඇඳ ඇත. තවත් අසාර්ථක උත්සාහ <xliff:g id="NUMBER_1">%2$d</xliff:g> කින් පසුව, ඊ-තැපැල් ගිණුම භාවිතා කරමින් ඔබගේ ටැබ්ලටයේ අගුළු ඇරීමට ඔබට පවසනු ඇත.\n\n නැවත තත්පර <xliff:g id="NUMBER_2">%3$d</xliff:g> කින් උත්සාහ කරන්න."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"ඔබ ඔබගේ අගුළු හැරීමේ මෝස්තරය <xliff:g id="NUMBER_0">%1$d</xliff:g> වරක් වැරදියට ඇඳ ඇත. තවත් අසාර්ථක උත්සාහ <xliff:g id="NUMBER_1">%2$d</xliff:g> කින් පසුව, ඔබට ඊ-තැපැල් ගිණුමක් භාවිතයෙන් ඔබගේ රූපවාහිනිය අගුළු හැරීමට සිදු වනු ඇත.\n\n තත්පර <xliff:g id="NUMBER_2">%3$d</xliff:g> කින් පසුව නැවත උත්සාහ කරන්න."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"ඔබ වැරදියට <xliff:g id="NUMBER_0">%1$d</xliff:g> වතාවක් ඔබගේ අගුළු හැරීමේ රටාව ඇඳ ඇත. අසාර්ථක උත්සහ කිරීම් <xliff:g id="NUMBER_1">%2$d</xliff:g> න් පසුව, ඔබගේ ඊ-තැපැල් ලිපිනය භාවිතයෙන් ඔබගේ දුරකථනය අගුළු හැරීමට ඔබගෙන් අසයි.\n\n තත්පර <xliff:g id="NUMBER_2">%3$d</xliff:g> න් පසුව නැවත උත්සහ කරන්න."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"ඉවත් කරන්න"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"නිර්දේශිතයි මට්ටමට වඩා ශබ්දය වැඩිද?\n\nදිගු කාලයක් සඳහා ඉහළ ශබ්දයක් ඇසීමෙන් ඇතැම් විට ඔබගේ ඇසීමට හානි විය හැක."</string>
@@ -1472,6 +1470,7 @@
<item quantity="other">පැය %d ක් සඳහා</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> තෙක්"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> තෙක් (ඊළඟ එලාමය)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"ඔබ මෙය අක්රිය කරන තුරු"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"බාධා නොකරන්න ඔබ අක්රිය කරන තුරු"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-sk-watch/strings.xml b/core/res/res/values-sk-watch/strings.xml
index 765b390..ec12e4b 100644
--- a/core/res/res/values-sk-watch/strings.xml
+++ b/core/res/res/values-sk-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Aplikácia <xliff:g id="NUMBER_0">%1$d</xliff:g> z <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Senzory"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"prístup ku kontaktom"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"prístup k polohe týchto hodiniek"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"prístup ku kalendáru"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"posielanie a zobrazovanie správ SMS"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"prístup k fotkám, médiám a súborom v hodinkách"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"zaznamenávanie zvuku"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"fotenie a zaznamenávanie videí"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"telefonovanie a správa hovorov"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"prístup k údajom senzorov o životných funkciách"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"vydávanie sa za stavový riadok"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"prístup k telesným senzorom (ako sú snímače tepu)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"prístup k presnej polohe (pomocou GPS a siete)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"prístup k približnej polohe (pomocou siete)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"posielanie príkazov do SIM karty"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"úplný prístup k sieti"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"správa vlastníkov profilov a zariadení"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"zmena stavu pripojenia WiMAX"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"príjem stavu prenosov Android Beam"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"smerovanie výstupu médií"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"čítanie inštalačných relácií"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"odosielanie žiadostí o inštaláciu balíčkov"</string>
</resources>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 8432348..0ecb92d 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -256,7 +256,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Sledovanie zahŕňa osobné údaje ako sú čísla kreditných kariet a heslá."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"zakázanie alebo zmeny stavového riadka"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Umožňuje aplikácii vypnúť stavový riadok alebo pridať a odstrániť systémové ikony."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"stavový riadok"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"vydávanie sa za stavový riadok"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Umožňuje aplikácii fungovať ako stavový riadok."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"rozbalenie a zbalenie stavového riadka"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Umožňuje aplikácii rozbaliť alebo zbaliť stavový riadok."</string>
@@ -284,7 +284,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Umožňuje aplikácii prijímať a spracovávať správy WAP. Toto povolenie zahŕňa možnosť sledovať vaše správy alebo ich odstrániť bez toho, aby sa vám zobrazili."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"načítať spustené aplikácie"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Umožňuje aplikácii načítať informácie o aktuálne či nedávno spustených úlohách. Toto povolenie môže aplikácii umožniť objaviť informácie o tom, ktoré aplikácie sa na zariadení používajú."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Správa vlastníkov profilov a zariadenia"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"správa vlastníkov profilov a zariadení"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Povolí aplikáciám nastaviť vlastníkov profilov a vlastníka zariadenia."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"zmeniť poradie spustených aplikácií"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Umožňuje aplikácii presunúť úlohy do popredia alebo do pozadia. Aplikácia tak môže urobiť bez vášho zásahu."</string>
@@ -326,7 +326,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Umožňuje aplikácii upravovať denník hovorov vo vašom tablete vrátane údajov o prichádzajúcich a odchádzajúcich hovoroch. Škodlivé aplikácie to môžu zneužiť na vymazanie alebo úpravu vášho denníka hovorov."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Umožňuje aplikácii upravovať denník hovorov vo vašom televízore vrátane údajov o prichádzajúcich a odchádzajúcich hovoroch. Škodlivé aplikácie to môžu zneužiť na vymazanie alebo úpravu vášho denníka hovorov."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Umožňuje aplikácii upravovať denník hovorov vo vašom telefóne vrátane údajov o prichádzajúcich a odchádzajúcich hovoroch. Škodlivé aplikácie to môžu zneužiť na vymazanie alebo úpravu vášho denníka hovorov."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"telesné senzory (napr. snímače tepu)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"prístup k telesným senzorom (ako sú snímače tepu)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Umožňuje aplikácii získať prístup k údajom senzorov monitorujúcich vašu fyzickú kondíciu (napríklad pulz)."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"čítať udalosti v kalendári a dôverné informácie"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Umožňuje aplikácii čítať všetky udalosti kalendára uložené v tablete vrátane udalostí priateľov alebo spolupracovníkov. Aplikácii to umožní zdieľať alebo ukladať údaje kalendára bez ohľadu na dôvernosť či citlivosť týchto údajov."</string>
@@ -338,15 +338,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Umožňuje aplikácii pridávať, odstraňovať alebo meniť udalosti, ktoré môžete v telefóne upravovať, a to vrátane udalostí priateľov a spolupracovníkov. Toto povolenie umožňuje aplikácii odosielať správy, ktoré budú zdanlivo prichádzať od vlastníkov kalendára, alebo upravovať udalosti bez vedomia vlastníkov."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"prístup k ďalším príkazom poskytovateľa polohy"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Umožňuje aplikácii pristupovať k ďalším príkazom poskytovateľa informácií o polohe. Aplikácii to môže umožniť zasahovať do činnosti systému GPS alebo iných zdrojov informácií o polohe."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"zistiť presnú polohu (pomocou GPS a siete)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"prístup k presnej polohe (pomocou GPS a siete)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Umožňuje aplikácii získať vašu presnú polohu pomocou systému GPS (Global Positioning System) alebo zdrojov určenia polohy siete, napríklad mobilných veží a sietí Wi-Fi. Tieto služby určovania polohy musia byť na vašom zariadení zapnuté a dostupné, inak ich aplikácia nebude môcť využívať. Aplikácie môžu tieto služby využívať na určenie vašej polohy. Tieto služby môžu zvýšiť spotrebu batérie."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"zistiť približnú polohu (pomocou siete)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"prístup k približnej polohe (pomocou siete)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Umožňuje aplikácii získať vašu približnú polohu. Táto poloha je odvodená zo služieb určovania polohy pomocou zdrojov určenia polohy siete, napríklad mobilných veží a sietí Wi-Fi. Tieto služby určovania polohy musia byť na vašom zariadení zapnuté a dostupné, inak ich aplikácia nebude môcť využívať. Aplikácie môžu tieto služby využívať na určenie vašej približnej polohy."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"meniť nastavenia zvuku"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Umožňuje aplikácii upraviť globálne nastavenia zvuku, ako je hlasitosť, alebo určiť, z ktorého reproduktora bude zvuk vychádzať."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"nahrávať zvuk"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Umožňuje aplikácii zaznamenávať zvuk pomocou mikrofónu. Toto povolenie umožňuje aplikácii zaznamenávať zvuk kedykoľvek bez vášho potvrdenia."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"komunikácia so SIM kartou"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"posielanie príkazov do SIM karty"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Umožňuje aplikácii odosielať príkazy na SIM kartu. Toto je veľmi nebezpečné povolenie."</string>
<string name="permlab_camera" msgid="3616391919559751192">"fotiť a nakrúcať videá"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Umožňuje aplikácii fotografovať a nahrávať videá pomocou fotoaparátu. Toto povolenie umožňuje aplikácii používať fotoaparát kedykoľvek a bez vášho potvrdenia."</string>
@@ -384,7 +384,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Umožňuje aplikácii získať zoznam účtov v telefóne. Môžu sem patriť akékoľvek účty vytvorené aplikáciami, ktoré ste nainštalovali."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"zobraziť sieťové pripojenia"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Umožňuje aplikácii zobraziť informácie o sieťových pripojeniach, ako napríklad o tom, ktoré siete sú k dispozícii a ktoré sú pripojené."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"plný prístup k sieti"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"úplný prístup k sieti"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Umožňuje aplikácii vytvárať sokety siete a používať vlastné protokoly siete. Toto povolenie sa pri odosielaní údajov na internet nevyžaduje, pretože prostriedky na odosielanie údajov na internet poskytujú prehliadač a iné aplikácie."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"zmena sieťového pripojenia"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Umožňuje aplikácii zmeniť stav sieťového pripojenia zdieľaného pomocou tetheringu."</string>
@@ -404,7 +404,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Umožňuje aplikácii konfigurovať miestny telefón s rozhraním Bluetooth, vyhľadávať a spárovať vzdialené zariadenia."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"pripojiť a odpojiť od WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Umožňuje aplikácii určiť, či je povolený štandard WiMAX, a tiež informácie o všetkých pripojených sieťach WiMAX."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Meniť stav pripojenia WiMAX"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"zmeniť stav WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Umožňuje aplikácii pripojiť tablet k sieťam WiMAX a odpojiť ho od nich."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Umožňuje aplikácii pripojiť televízor k sieťam WiMAX a odpojiť ho z nich."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Umožňuje aplikácii pripojiť telefón k sieťam WiMAX a odpojiť ho od nich."</string>
@@ -487,7 +487,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Umožňuje aplikácii upraviť parametre kalibrácie dotykovej obrazovky. Bežné aplikácie by toto povolenie nemali nikdy potrebovať."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"prístup k certifikátom DRM"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Umožňuje aplikácii vydávať a používať certifikáty DRM. Bežné aplikácie by toto povolenie nemali nikdy potrebovať."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Prijímať stav prenosu funkcie Android Beam"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"príjem stavu prenosov Android Beam"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Povoľuje tejto aplikácii prijímať informácie o aktuálnych prenosoch funkcie Android Beam"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"odstránenie certifikátov DRM"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Umožňuje aplikácii odstraňovať certifikáty DRM. Bežné aplikácie by toto povolenie nemali nikdy potrebovať."</string>
@@ -652,7 +652,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Nesprávny kód PIN."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Ak chcete telefón odomknúť, stlačte Menu a následne 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Číslo tiesňového volania"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Žiadny signál"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Žiadny signál"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Obrazovka je uzamknutá."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Ak chcete odomknúť telefón alebo uskutočniť tiesňové volanie, stlačte Menu."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Telefón odomknete stlačením tlačidla Menu."</string>
@@ -684,15 +684,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Prečítajte si Príručku používateľa alebo kontaktujte podporu zákazníka."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM karta je uzamknutá."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Prebieha odomykanie SIM karty..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"<xliff:g id="NUMBER_0">%d</xliff:g>-krát ste použili nesprávny bezpečnostný vzor. \n\nSkúste to znova o <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"<xliff:g id="NUMBER_0">%d</xliff:g>-krát ste zadali nesprávne heslo. \n\nSkúste to znova o <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"<xliff:g id="NUMBER_0">%d</xliff:g>-krát ste zadali nesprávny kód PIN. \n\nSkúste to znova o <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"<xliff:g id="NUMBER_0">%d</xliff:g>-krát ste nesprávne nakreslili svoj bezpečnostný vzor. Po ďalších neúspešných pokusoch (<xliff:g id="NUMBER_1">%d</xliff:g>) budete vyzvaní odomknúť tablet pomocou prihlasovacích údajov služby Google.\n\n Skúste to znova o <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Vzor na odomknutie ste nesprávne nakreslili <xliff:g id="NUMBER_0">%d</xliff:g>-krát. Po ďalších neúspešných pokusoch (počet: <xliff:g id="NUMBER_1">%d</xliff:g>) sa zobrazí výzva na odomknutie vášho televízora pomocou prihlásenia Google.\n\n Skúste to znova o <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"<xliff:g id="NUMBER_0">%d</xliff:g>-krát ste nesprávne nakreslili svoj bezpečnostný vzor. Po <xliff:g id="NUMBER_1">%d</xliff:g> ďalších neúspešných pokusoch sa zobrazí výzva na odomknutie telefónu pomocou prihlasovacích údajov Google.\n\n Skúste to znova o <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Tablet ste sa pokúsili odomknúť nesprávnym spôsobom <xliff:g id="NUMBER_0">%d</xliff:g>-krát. Po <xliff:g id="NUMBER_1">%d</xliff:g> ďalších neúspešných pokusoch sa v tablete obnovia predvolené výrobné nastavenia a všetky používateľské údaje budú stratené."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Televízor ste sa pokúsili nesprávne odomknúť <xliff:g id="NUMBER_0">%d</xliff:g>-krát. Po ďalších neúspešných pokusoch (počet: <xliff:g id="NUMBER_1">%d</xliff:g>) sa obnovia výrobné nastavenia televízora a prídete o všetky údaje používateľa."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Telefón ste sa pokúsili odomknúť nesprávnym spôsobom <xliff:g id="NUMBER_0">%d</xliff:g>-krát. Po <xliff:g id="NUMBER_1">%d</xliff:g> ďalších neúspešných pokusoch sa v telefóne obnovia predvolené výrobné nastavenia a všetky používateľské údaje budú stratené."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"<xliff:g id="NUMBER_0">%1$d</xliff:g>-krát ste použili nesprávny bezpečnostný vzor. \n\nSkúste to znova o <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"<xliff:g id="NUMBER_0">%1$d</xliff:g>-krát ste zadali nesprávne heslo. \n\nSkúste to znova o <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"<xliff:g id="NUMBER_0">%1$d</xliff:g>-krát ste zadali nesprávny kód PIN. \n\nSkúste to znova o <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"<xliff:g id="NUMBER_0">%1$d</xliff:g>-krát ste nesprávne nakreslili svoj bezpečnostný vzor. Po ďalších neúspešných pokusoch (<xliff:g id="NUMBER_1">%2$d</xliff:g>) budete vyzvaní odomknúť tablet pomocou prihlasovacích údajov služby Google.\n\n Skúste to znova o <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Vzor na odomknutie ste nesprávne nakreslili <xliff:g id="NUMBER_0">%1$d</xliff:g>-krát. Po ďalších neúspešných pokusoch (počet: <xliff:g id="NUMBER_1">%2$d</xliff:g>) sa zobrazí výzva na odomknutie vášho televízora pomocou prihlásenia Google.\n\n Skúste to znova o <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"<xliff:g id="NUMBER_0">%1$d</xliff:g>-krát ste nesprávne nakreslili svoj bezpečnostný vzor. Po <xliff:g id="NUMBER_1">%2$d</xliff:g> ďalších neúspešných pokusoch sa zobrazí výzva na odomknutie telefónu pomocou prihlasovacích údajov Google.\n\n Skúste to znova o <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Tablet ste sa pokúsili odomknúť nesprávnym spôsobom <xliff:g id="NUMBER_0">%1$d</xliff:g>-krát. Po <xliff:g id="NUMBER_1">%2$d</xliff:g> ďalších neúspešných pokusoch sa v tablete obnovia predvolené výrobné nastavenia a všetky používateľské údaje budú stratené."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Televízor ste sa pokúsili nesprávne odomknúť <xliff:g id="NUMBER_0">%1$d</xliff:g>-krát. Po ďalších neúspešných pokusoch (počet: <xliff:g id="NUMBER_1">%2$d</xliff:g>) sa obnovia výrobné nastavenia televízora a prídete o všetky údaje používateľa."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Telefón ste sa pokúsili odomknúť nesprávnym spôsobom <xliff:g id="NUMBER_0">%1$d</xliff:g>-krát. Po <xliff:g id="NUMBER_1">%2$d</xliff:g> ďalších neúspešných pokusoch sa v telefóne obnovia predvolené výrobné nastavenia a všetky používateľské údaje budú stratené."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Tablet ste sa pokúsili odomknúť nesprávnym spôsobom <xliff:g id="NUMBER">%d</xliff:g>-krát. V tablete sa teraz obnovia predvolené výrobné nastavenia."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Televízor ste sa pokúsili nesprávne odomknúť <xliff:g id="NUMBER">%d</xliff:g>-krát. Teraz v ňom budú obnovené výrobné nastavenia."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Telefón ste sa pokúsili odomknúť nesprávnym spôsobom <xliff:g id="NUMBER">%d</xliff:g>-krát. V telefóne sa teraz obnovia predvolené výrobné nastavenia."</string>
@@ -744,8 +744,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"slovo"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"odkaz"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"riadok"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Továrenský test zlyhal"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Test FACTORY_TEST je možné uskutočniť iba pri balíčkoch nainštalovaných v priečinku /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Nebol nájdený žiadny balíček umožňujúci test FACTORY_TEST."</string>
@@ -1087,11 +1085,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Prebieha formátovanie..."</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Nie je zapojené"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Nenašli sa žiadne zodpovedajúce aktivity."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Smerovanie výstupu médií"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"smerovanie výstupu médií"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Umožňuje aplikácii smerovať výstup médií do ďalších externých zariadení."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Čítať relácie inštalácií"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"čítanie inštalačných relácií"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Toto povolenie umožňuje aplikácii čítať relácie inštalácií a zobraziť tak podrobnosti o aktívnych inštaláciách balíkov."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Vyžiadať inštaláciu balíkov"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"odosielanie žiadostí o inštaláciu balíkov"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Umožňuje aplikácii vyžiadať inštaláciu balíkov."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Ovládacie prvky lupy zobrazíte dvojitým dotknutím"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Miniaplikáciu sa nepodarilo pridať."</string>
@@ -1301,18 +1299,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Neplatné používateľské meno alebo heslo."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Zabudli ste svoje používateľské meno alebo heslo?\n Navštívte stránky "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Prebieha kontrola účtu..."</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"<xliff:g id="NUMBER_0">%d</xliff:g>-krát ste zadali nesprávny kód PIN. \n\nSkúste to znova o <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"<xliff:g id="NUMBER_0">%d</xliff:g>-krát ste zadali nesprávne heslo. \n\nSkúste to znova o <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"<xliff:g id="NUMBER_0">%d</xliff:g>-krát ste použili nesprávny bezpečnostný vzor. \n\nSkúste to znova o <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Tablet ste sa pokúsili odomknúť nesprávnym spôsobom <xliff:g id="NUMBER_0">%d</xliff:g>-krát. Po <xliff:g id="NUMBER_1">%d</xliff:g> ďalších neúspešných pokusoch sa v tablete obnovia predvolené výrobné nastavenia a všetky používateľské údaje budú stratené."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Televízor ste sa pokúsili nesprávne odomknúť <xliff:g id="NUMBER_0">%d</xliff:g>-krát. Po ďalších neúspešných pokusoch (počet: <xliff:g id="NUMBER_1">%d</xliff:g>) sa obnovia výrobné nastavenia televízora a prídete o všetky údaje používateľa."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Telefón ste sa pokúsili odomknúť nesprávnym spôsobom <xliff:g id="NUMBER_0">%d</xliff:g>-krát. Po <xliff:g id="NUMBER_1">%d</xliff:g> ďalších neúspešných pokusoch sa v telefóne obnovia predvolené výrobné nastavenia a všetky používateľské údaje budú stratené."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"<xliff:g id="NUMBER_0">%1$d</xliff:g>-krát ste zadali nesprávny kód PIN. \n\nSkúste to znova o <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"<xliff:g id="NUMBER_0">%1$d</xliff:g>-krát ste zadali nesprávne heslo. \n\nSkúste to znova o <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"<xliff:g id="NUMBER_0">%1$d</xliff:g>-krát ste použili nesprávny bezpečnostný vzor. \n\nSkúste to znova o <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Tablet ste sa pokúsili odomknúť nesprávnym spôsobom <xliff:g id="NUMBER_0">%1$d</xliff:g>-krát. Po <xliff:g id="NUMBER_1">%2$d</xliff:g> ďalších neúspešných pokusoch sa v tablete obnovia predvolené výrobné nastavenia a všetky používateľské údaje budú stratené."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Televízor ste sa pokúsili nesprávne odomknúť <xliff:g id="NUMBER_0">%1$d</xliff:g>-krát. Po ďalších neúspešných pokusoch (počet: <xliff:g id="NUMBER_1">%2$d</xliff:g>) sa obnovia výrobné nastavenia televízora a prídete o všetky údaje používateľa."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Telefón ste sa pokúsili odomknúť nesprávnym spôsobom <xliff:g id="NUMBER_0">%1$d</xliff:g>-krát. Po <xliff:g id="NUMBER_1">%2$d</xliff:g> ďalších neúspešných pokusoch sa v telefóne obnovia predvolené výrobné nastavenia a všetky používateľské údaje budú stratené."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Tablet ste sa pokúsili odomknúť nesprávnym spôsobom <xliff:g id="NUMBER">%d</xliff:g>-krát. V tablete sa teraz obnovia predvolené výrobné nastavenia."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Televízor ste sa pokúsili nesprávne odomknúť <xliff:g id="NUMBER">%d</xliff:g>-krát. Teraz sa v ňom obnovia výrobné nastavenia."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Telefón ste sa pokúsili odomknúť nesprávnym spôsobom <xliff:g id="NUMBER">%d</xliff:g>-krát. V telefóne sa teraz obnovia predvolené výrobné nastavenia."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"<xliff:g id="NUMBER_0">%d</xliff:g>-krát ste nesprávne nakreslili svoj bezpečnostný vzor. Po ďalších <xliff:g id="NUMBER_1">%d</xliff:g> neúspešných pokusoch sa zobrazí výzva na odomknutie tabletu pomocou e-mailového účtu.\n\n Skúste to znova o <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Vzor na odomknutie ste nakreslili nesprávne <xliff:g id="NUMBER_0">%d</xliff:g>-krát. Po ďalších neúspešných pokusoch (počet: <xliff:g id="NUMBER_1">%d</xliff:g>) sa zobrazí výzva na odomknutie televízora pomocou e-mailového účtu.\n\n Skúste to znova o <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"<xliff:g id="NUMBER_0">%d</xliff:g>-krát ste nesprávne nakreslili svoj bezpečnostný vzor. Po <xliff:g id="NUMBER_1">%d</xliff:g> ďalších neúspešných pokusoch sa zobrazí výzva na odomknutie telefónu pomocou e-mailového účtu.\n\n Skúste to znova o <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"<xliff:g id="NUMBER_0">%1$d</xliff:g>-krát ste nesprávne nakreslili svoj bezpečnostný vzor. Po ďalších <xliff:g id="NUMBER_1">%2$d</xliff:g> neúspešných pokusoch sa zobrazí výzva na odomknutie tabletu pomocou e-mailového účtu.\n\n Skúste to znova o <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Vzor na odomknutie ste nakreslili nesprávne <xliff:g id="NUMBER_0">%1$d</xliff:g>-krát. Po ďalších neúspešných pokusoch (počet: <xliff:g id="NUMBER_1">%2$d</xliff:g>) sa zobrazí výzva na odomknutie televízora pomocou e-mailového účtu.\n\n Skúste to znova o <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"<xliff:g id="NUMBER_0">%1$d</xliff:g>-krát ste nesprávne nakreslili svoj bezpečnostný vzor. Po <xliff:g id="NUMBER_1">%2$d</xliff:g> ďalších neúspešných pokusoch sa zobrazí výzva na odomknutie telefónu pomocou e-mailového účtu.\n\n Skúste to znova o <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Odstrániť"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Zvýšiť hlasitosť nad odporúčanú úroveň?\n\nDlhodobé počúvanie pri vysokej hlasitosti môže poškodiť váš sluch."</string>
@@ -1504,6 +1502,7 @@
<item quantity="one">Na 1 h</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (ďalší budík)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Dokým túto funkciu nevypnete"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Dokým nevypnete stav Nerušiť"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-sl-watch/strings.xml b/core/res/res/values-sl-watch/strings.xml
index f138326..42c6c1a 100644
--- a/core/res/res/values-sl-watch/strings.xml
+++ b/core/res/res/values-sl-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_0">%1$d</xliff:g>. aplikac. od <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Tipala"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"dostop do stikov"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"dostop do lokacije te ure"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"dostop do koledarja"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"pošiljanje in ogled sporočil SMS"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"dostop do fotografij, predstavnosti in datotek v uri"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"snemanje zvoka"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"fotografiranje in snemanje videoposnetkov"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"opravljanje in upravljanje telefonskih klicev"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"dostop do podatkov tipala o vaših vitalnih znakih"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"postane vrstica stanja"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"dostop do tipal telesnih funkcij (npr. merilnikov srčnega utripa)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"dostop do natančne lokacije (na podlagi podatkov GPS in omrežja)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"dostop do približne lokacije (na podlagi podatkov omrežja)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"pošiljanje ukazov na kartico SIM"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"poln dostop do omrežja"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"upravljanje lastnikov profilov in lastnika naprave"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"sprememba stanja omrežja WiMAX"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"prejemanje stanja prenosov s funkcijo Android Beam"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"preusmeritev predstavnosti"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"branje sej namestitev"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"zahtevanje paketov za namestitev"</string>
</resources>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index c5ca3c2..a5c1013 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -256,7 +256,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Vključuje osebne podatke, kot so številke kreditnih kartic in gesla."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"onemogočanje ali spreminjanje vrstice stanja"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Aplikacijam omogoča onemogočenje vrstice stanja ali dodajanje in odstranjevanje ikon sistema."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"vrstica stanja"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"postane vrstica stanja"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Aplikaciji omogoča, da postane vrstica stanja."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"razširjanje/strnjevanje vrstice stanja"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Aplikaciji omogoča razširjanje ali strnjevanje vrstice stanja."</string>
@@ -284,7 +284,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Aplikaciji omogoča prejemanje in obdelavo sporočil WAP. S tem lahko aplikacija nadzoruje ali izbriše sporočila, poslana v napravo, ne da bi vam jih pokazala."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"dobivanje aplikacij, ki se izvajajo"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Aplikaciji omogoča prejemanje podatkov o trenutnih in nedavno izvajajočih se opravilih. S tem lahko aplikacija odkrije podatke o aplikacijah, ki se uporabljajo v napravi."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Upravljanje lastnikov profilov in lastnika naprave."</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"upravljanje lastnikov profilov in lastnikov naprave"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Dovolite aplikacijam, da nastavijo lastnike profilov in lastnika naprave."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"preurejanje aplikacij, ki se izvajajo"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Aplikaciji omogoča premikanje opravil v ospredje in ozadje. Aplikacija lahko to naredi brez vašega nadzora."</string>
@@ -326,7 +326,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Aplikaciji dovoli spreminjanje dnevnika klicev v tabličnem računalniku, vključno s podatki o dohodnih in odhodnih klicih. Zlonamerne aplikacije lahko tako izbrišejo ali spreminjajo vaš dnevnik klicev."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Aplikaciji dovoljuje spreminjanje dnevnika klicev v televizorju, vključno s podatki o dohodnih in odhodnih klicih. Zlonamerne aplikacije lahko tako izbrišejo ali spreminjajo vaš dnevnik klicev."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Aplikaciji dovoli spreminjanje dnevnika klicev v telefonu, vključno s podatki o dohodnih in odhodnih klicih. Zlonamerne aplikacije lahko tako izbrišejo ali spreminjajo vaš dnevnik klicev."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"tipala telesnih funkcij (npr. merilniki srčnega utripa)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"dostop do tipal telesnih funkcij (npr. merilnikov srčnega utripa)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Aplikaciji omogoča dostop do podatkov tipal, ki nadzirajo vaše fizično stanje, med drugim vaš srčni utrip."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"branje dogodkov v koledarju in zaupnih podatkov"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Aplikaciji omogoča branje vseh dogodkov v koledarju, ki so shranjeni v tabličnem računalniku, vključno z dogodki prijateljev in sodelavcev. S tem lahko aplikacija objavi ali shrani podatke v koledarju, ne glede na njihovo zaupnost ali občutljivost."</string>
@@ -338,15 +338,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Aplikaciji omogoča dodajanje, odstranjevanje in spreminjanje dogodkov, ki jih je mogoče spreminjati v telefonu, vključno z dogodki prijateljev in sodelavcev. S tem lahko aplikacija pošilja sporočila, za katera je videti, da jih pošiljajo lastniki koledarjev, ali spreminjajo dogodke brez vednosti lastnikov."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"dostopanje do ukazov ponudnika dodatnih lokacij"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Aplikaciji omogoča dostop do dodatnih ukazov ponudnika lokacij. S tem lahko aplikacija moti delovanje sistema GPS ali drugih virov lokacije."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"natančna lokacija (na podlagi podatkov GPS in omrežja)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"dostop do natančne lokacije (na podlagi podatkov GPS in omrežja)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Aplikaciji omogoča, da določi vašo natančno lokacijo na podlagi sistema GPS ali omrežnih lokacijskih virov, kot so bazne postaje mobilne telefonije ali Wi-Fi. Te lokacijske storitve morajo biti vklopljene in na voljo v napravi, če želite, da jih aplikacija uporablja. Aplikacije lahko na podlagi tega določijo vašo lokacijo in še dodatno izpraznijo akumulator."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"približna lokacija (na podlagi omrežja)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"dostop do približne lokacije (na podlagi podatkov omrežja)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Aplikaciji omogoča, da določi vašo približno lokacijo na podlagi lokacijskih storitev z omrežnimi lokacijskimi viri, kot so bazne postaje mobilne telefonije in Wi-Fi. Te lokacijske storitve morajo biti vklopljene in na voljo v napravi, če želite, da jih aplikacija uporablja. Aplikacije lahko na podlagi tega določijo vašo približno lokacijo."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"spreminjanje nastavitev zvoka"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Aplikaciji omogoča spreminjanje splošnih zvočnih nastavitev, na primer glasnost in kateri zvočnik se uporablja."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"snemanje zvoka"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Aplikaciji omogoča snemanje zvoka z mikrofonom. S tem dovoljenjem lahko aplikacija kadar koli snema zvok brez vaše potrditve."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"komuniciranje s kartico SIM"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"pošiljanje ukazov na kartico SIM"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Aplikaciji dovoli pošiljanje ukazov kartici SIM. To je lahko zelo nevarno."</string>
<string name="permlab_camera" msgid="3616391919559751192">"fotografiranje in snemanje videoposnetkov"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Aplikaciji omogoča fotografiranje in snemanje videoposnetkov s kamero. S tem dovoljenjem lahko aplikacija kadar koli uporablja kamero brez vaše potrditve."</string>
@@ -384,7 +384,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Aplikaciji omogoča prejemanje seznama računov, ki jih pozna telefon.To lahko vključuje račune, ki so jih ustvarile nameščene aplikacije."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"prikaz omrežnih povezav"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Aplikaciji omogoča ogled podatkov o omrežnih povezavah, na primer o obstoječih in povezanih omrežjih."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"poln dostop do omrežja"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"poln dostop do omrežja"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Aplikaciji omogoča ustvarjanje vtičnic omrežja in uporabo omrežnih protokolov po meri. Brskalnik in druge aplikacije omogočajo pošiljanje podatkov v internet, zato to dovoljenje ni potrebno za pošiljanje podatkov v internet."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"spreminjanje povezljivosti omrežja"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Aplikaciji omogoča spreminjanje stanja povezljivosti omrežja."</string>
@@ -404,7 +404,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Aplikaciji omogoča konfiguriranje lokalnega telefona s tehnologijo Bluetooth ter odkrivanje oddaljenih naprav in povezovanje z njimi."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"vzpostavitev povezave z omrežjem WiMax in prekinitev povezave z njim"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Aplikaciji omogoča, da ugotovi, ali je WiMAX omogočen, in ogled podatkov o povezanih omrežjih WiMAX."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Sprememba stanja omrežja WiMAX"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"sprememba stanja omrežja WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Aplikaciji omogoča, da vzpostavi povezavo med tabličnim računalnikom in omrežjem WiMAX ter jo prekine."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Aplikaciji dovoljuje, da televizor poveže v omrežja WiMAX in prekine povezavo televizorja s temi omrežji."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Aplikaciji omogoča, da vzpostavi povezavo med telefonom in omrežjem WiMAX ter jo prekine."</string>
@@ -487,7 +487,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Aplikaciji dovoli spreminjanje parametrov za umerjanje zaslona na dotik. Tega ni treba nikoli uporabiti za navadne aplikacije."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"dostop do potrdil za upravljanje digitalnih pravic"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Aplikaciji omogoča pripravo in uporabo potrdil za upravljanje digitalnih pravic. To naj ne bi bilo nikoli potrebno za običajne aplikacije."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Prejemanje stanja prenosov s funkcijo Android Beam"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"prejemanje stanja prenosov s funkcijo Android Beam"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Omogoči tej aplikaciji prejemanje podatkov o trenutnih prenosih s funkcijo Android Beam"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"odstranjevanje potrdil za upravljanje digitalnih pravic"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Aplikaciji omogoča odstranjevanje potrdil za upravljanje digitalnih pravic. Tega ni treba nikoli uporabiti za navadne aplikacije."</string>
@@ -652,7 +652,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Napačna koda PIN."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Če želite telefon odkleniti, pritisnite meni in nato 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Številka za klic v sili"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Ni storitve."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Ni signala"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Zaslon je zaklenjen."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Če želite odkleniti napravo ali opraviti klic v sili, pritisnite meni."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Če želite odkleniti, pritisnite meni."</string>
@@ -684,15 +684,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Preberite uporabniški priročnik ali se obrnite na oddelek za skrb za stranke."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"Kartica SIM je zaklenjena."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Odklepanje kartice SIM ..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Vzorec za odklepanje ste nepravilno narisali <xliff:g id="NUMBER_0">%d</xliff:g>-krat. \n\nPoskusite znova čez <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Geslo ste <xliff:g id="NUMBER_0">%d</xliff:g>-krat vnesli napačno. \n\nZnova poskusite čez <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"PIN ste <xliff:g id="NUMBER_0">%d</xliff:g>-krat vnesli napačno. \n\nZnova poskusite čez <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Vzorec za odklepanje ste nepravilno vnesli <xliff:g id="NUMBER_0">%d</xliff:g>-krat. Po <xliff:g id="NUMBER_1">%d</xliff:g> neuspešnih poskusih boste pozvani, da odklenete tablični računalnik z Googlovimi podatki za prijavo.\n\nPoskusite znova čez <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"<xliff:g id="NUMBER_0">%d</xliff:g>-krat ste nepravilno narisali vzorec za odklepanje. Če vam ne uspe še <xliff:g id="NUMBER_1">%d</xliff:g>-krat, boste pozvani k odklepanju televizorja z geslom za Google.\n\n Poskusite znova čez <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Vzorec za odklepanje ste nepravilno vnesli <xliff:g id="NUMBER_0">%d</xliff:g>-krat. Po <xliff:g id="NUMBER_1">%d</xliff:g> neuspešnih poskusih boste pozvani, da odklenete telefon z Googlovimi podatki za prijavo.\n\nPoskusite znova čez <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Tablični računalnik ste poskusili <xliff:g id="NUMBER_0">%d</xliff:g>-krat nepravilno odkleniti. Če poskusite še <xliff:g id="NUMBER_1">%d</xliff:g>-krat in ne uspete, bo ponastavljen na privzete tovarniške nastavitve, vsi uporabniški podatki pa bodo izbrisani."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"<xliff:g id="NUMBER_0">%d</xliff:g>-krat ste nepravilno poskusili odkleniti televizor. Če vam ne uspe še <xliff:g id="NUMBER_1">%d</xliff:g>-krat, bo televizor ponastavljen na tovarniške nastavitve in vsi uporabniški podatki bodo izgubljeni."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Telefon ste poskusili <xliff:g id="NUMBER_0">%d</xliff:g>-krat nepravilno odkleniti. Če poskusite še <xliff:g id="NUMBER_1">%d</xliff:g>-krat in ne uspete, bo ponastavljen na privzete tovarniške nastavitve, vsi uporabniški podatki pa bodo izgubljeni."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Vzorec za odklepanje ste nepravilno narisali <xliff:g id="NUMBER_0">%1$d</xliff:g>-krat. \n\nPoskusite znova čez <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Geslo ste <xliff:g id="NUMBER_0">%1$d</xliff:g>-krat vnesli napačno. \n\nZnova poskusite čez <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"PIN ste <xliff:g id="NUMBER_0">%1$d</xliff:g>-krat vnesli napačno. \n\nZnova poskusite čez <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Vzorec za odklepanje ste nepravilno vnesli <xliff:g id="NUMBER_0">%1$d</xliff:g>-krat. Po <xliff:g id="NUMBER_1">%2$d</xliff:g> neuspešnih poskusih boste pozvani, da odklenete tablični računalnik z Googlovimi podatki za prijavo.\n\nPoskusite znova čez <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"<xliff:g id="NUMBER_0">%1$d</xliff:g>-krat ste nepravilno narisali vzorec za odklepanje. Če vam ne uspe še <xliff:g id="NUMBER_1">%2$d</xliff:g>-krat, boste pozvani k odklepanju televizorja z geslom za Google.\n\n Poskusite znova čez <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Vzorec za odklepanje ste nepravilno vnesli <xliff:g id="NUMBER_0">%1$d</xliff:g>-krat. Po <xliff:g id="NUMBER_1">%2$d</xliff:g> neuspešnih poskusih boste pozvani, da odklenete telefon z Googlovimi podatki za prijavo.\n\nPoskusite znova čez <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Tablični računalnik ste poskusili <xliff:g id="NUMBER_0">%1$d</xliff:g>-krat nepravilno odkleniti. Če poskusite še <xliff:g id="NUMBER_1">%2$d</xliff:g>-krat in ne uspete, bo ponastavljen na privzete tovarniške nastavitve, vsi uporabniški podatki pa bodo izbrisani."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"<xliff:g id="NUMBER_0">%1$d</xliff:g>-krat ste nepravilno poskusili odkleniti televizor. Če vam ne uspe še <xliff:g id="NUMBER_1">%2$d</xliff:g>-krat, bo televizor ponastavljen na tovarniške nastavitve in vsi uporabniški podatki bodo izgubljeni."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Telefon ste poskusili <xliff:g id="NUMBER_0">%1$d</xliff:g>-krat nepravilno odkleniti. Če poskusite še <xliff:g id="NUMBER_1">%2$d</xliff:g>-krat in ne uspete, bo ponastavljen na privzete tovarniške nastavitve, vsi uporabniški podatki pa bodo izgubljeni."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Tablični računalnik ste poskusili <xliff:g id="NUMBER">%d</xliff:g>-krat nepravilno odkleniti, zato bo zdaj ponastavljen na privzete tovarniške nastavitve."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"<xliff:g id="NUMBER">%d</xliff:g>-krat ste nepravilno poskusili odkleniti televizor. Televizor bo ponastavljen na tovarniške nastavitve."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Telefon ste poskusili <xliff:g id="NUMBER">%d</xliff:g>-krat nepravilno odkleniti, zato bo ponastavljen na privzete tovarniške nastavitve."</string>
@@ -744,8 +744,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"beseda"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"povezava"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"vrstica"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Tovarniški preskus ni uspel"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Dejanje FACTORY_TEST je podprto le za pakete, nameščene v razdelku /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Ni bilo najdenega paketa, ki omogoča dejanje FACTORY_TEST."</string>
@@ -1087,11 +1085,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Formatiranje ..."</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Ni vstavljeno"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Ni ustreznih dejavnosti."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Preusmeritev predstavnosti"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"preusmeritev predstavnosti"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Aplikaciji omogoča preusmerjanje predstavnosti v druge zunanje naprave."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Branje sej namestitev"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"branje sej namestitev"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Aplikaciji omogoča branje sej namestitev. Tako lahko bere podrobnosti o aktivnih namestitvah paketov."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Zahtevanje paketov za namestitev"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"zahtevanje paketov za namestitev"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Aplikaciji omogoča zahtevanje namestitve paketov."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Dvakrat se dotaknite za nadzor povečave/pomanjšave"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Pripomočka ni bilo mogoče dodati."</string>
@@ -1301,18 +1299,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Neveljavno uporabniško ime ali geslo."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Ali ste pozabili uporabniško ime ali geslo?\nObiščite "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Preverjanje računa ..."</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"PIN ste <xliff:g id="NUMBER_0">%d</xliff:g>-krat vnesli napačno. \n\nZnova poskusite čez <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Geslo ste <xliff:g id="NUMBER_0">%d</xliff:g>-krat vnesli napačno. \n\nZnova poskusite čez <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Vzorec za odklepanje ste nepravilno narisali <xliff:g id="NUMBER_0">%d</xliff:g>-krat. \n\nPoskusite znova čez <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Tablični računalnik ste poskusili <xliff:g id="NUMBER_0">%d</xliff:g>-krat napačno odkleniti. Če poskusite še <xliff:g id="NUMBER_1">%d</xliff:g>-krat in ne uspete, bo ponastavljen na privzete tovarniške nastavitve in vsi uporabniški podatki bodo izgubljeni."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"<xliff:g id="NUMBER_0">%d</xliff:g>-krat ste nepravilno poskusili odkleniti televizor. Če vam ne uspe še <xliff:g id="NUMBER_1">%d</xliff:g>-krat, bo televizor ponastavljen na tovarniške nastavitve in vsi uporabniški podatki bodo izgubljeni."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Telefon ste poskusili <xliff:g id="NUMBER_0">%d</xliff:g>-krat napačno odkleniti. Če poskusite še <xliff:g id="NUMBER_1">%d</xliff:g>-krat in ne uspete, bo ponastavljen na privzete tovarniške nastavitve in vsi uporabniški podatki bodo izgubljeni."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"PIN ste <xliff:g id="NUMBER_0">%1$d</xliff:g>-krat vnesli napačno. \n\nZnova poskusite čez <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Geslo ste <xliff:g id="NUMBER_0">%1$d</xliff:g>-krat vnesli napačno. \n\nZnova poskusite čez <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Vzorec za odklepanje ste nepravilno narisali <xliff:g id="NUMBER_0">%1$d</xliff:g>-krat. \n\nPoskusite znova čez <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Tablični računalnik ste poskusili <xliff:g id="NUMBER_0">%1$d</xliff:g>-krat napačno odkleniti. Če poskusite še <xliff:g id="NUMBER_1">%2$d</xliff:g>-krat in ne uspete, bo ponastavljen na privzete tovarniške nastavitve in vsi uporabniški podatki bodo izgubljeni."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"<xliff:g id="NUMBER_0">%1$d</xliff:g>-krat ste nepravilno poskusili odkleniti televizor. Če vam ne uspe še <xliff:g id="NUMBER_1">%2$d</xliff:g>-krat, bo televizor ponastavljen na tovarniške nastavitve in vsi uporabniški podatki bodo izgubljeni."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Telefon ste poskusili <xliff:g id="NUMBER_0">%1$d</xliff:g>-krat napačno odkleniti. Če poskusite še <xliff:g id="NUMBER_1">%2$d</xliff:g>-krat in ne uspete, bo ponastavljen na privzete tovarniške nastavitve in vsi uporabniški podatki bodo izgubljeni."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Tablični računalnik ste poskusili <xliff:g id="NUMBER">%d</xliff:g>-krat napačno odkleniti, zato bo ponastavljen na privzete tovarniške nastavitve."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"<xliff:g id="NUMBER">%d</xliff:g>-krat ste nepravilno poskusili odkleniti televizor. Televizor bo ponastavljen na tovarniške nastavitve."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Telefon ste poskusili <xliff:g id="NUMBER">%d</xliff:g>-krat napačno odkleniti, zato bo ponastavljen na privzete tovarniške nastavitve."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Vzorec za odklepanje ste <xliff:g id="NUMBER_0">%d</xliff:g>-krat napačno vnesli. Po nadaljnjih <xliff:g id="NUMBER_1">%d</xliff:g> neuspešnih poskusih boste pozvani, da tablični računalnik odklenete z e-poštnim računom.\n\nPoskusite znova čez <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"<xliff:g id="NUMBER_0">%d</xliff:g>-krat ste nepravilno narisali vzorec za odklepanje. Če vam ne uspe še <xliff:g id="NUMBER_1">%d</xliff:g>-krat, boste pozvani k odklepanju televizorja z e-poštnim računom.\n\n Poskusite znova čez <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Vzorec za odklepanje ste <xliff:g id="NUMBER_0">%d</xliff:g>-krat napačno vnesli. Po nadaljnjih <xliff:g id="NUMBER_1">%d</xliff:g> neuspešnih poskusih boste pozvani, da odklenete telefon z Googlovimi podatki za prijavo.\n\nPoskusite znova čez <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Vzorec za odklepanje ste <xliff:g id="NUMBER_0">%1$d</xliff:g>-krat napačno vnesli. Po nadaljnjih <xliff:g id="NUMBER_1">%2$d</xliff:g> neuspešnih poskusih boste pozvani, da tablični računalnik odklenete z e-poštnim računom.\n\nPoskusite znova čez <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"<xliff:g id="NUMBER_0">%1$d</xliff:g>-krat ste nepravilno narisali vzorec za odklepanje. Če vam ne uspe še <xliff:g id="NUMBER_1">%2$d</xliff:g>-krat, boste pozvani k odklepanju televizorja z e-poštnim računom.\n\n Poskusite znova čez <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Vzorec za odklepanje ste <xliff:g id="NUMBER_0">%1$d</xliff:g>-krat napačno vnesli. Po nadaljnjih <xliff:g id="NUMBER_1">%2$d</xliff:g> neuspešnih poskusih boste pozvani, da odklenete telefon z Googlovimi podatki za prijavo.\n\nPoskusite znova čez <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Odstrani"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Ali želite povečati glasnost nad priporočeno raven?\n\nDolgotrajno poslušanje pri veliki glasnosti lahko poškoduje sluh."</string>
@@ -1504,6 +1502,7 @@
<item quantity="other">%d h</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (naslednji alarm)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Dokler tega ne izklopite"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Dokler ne izklopite načina »ne moti«"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-sq-rAL-watch/strings.xml b/core/res/res/values-sq-rAL-watch/strings.xml
index 7e36a32..673a7c9 100644
--- a/core/res/res/values-sq-rAL-watch/strings.xml
+++ b/core/res/res/values-sq-rAL-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Aplikacioni <xliff:g id="NUMBER_0">%1$d</xliff:g> nga <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Sensorët"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"qasu te kontaktet e tua"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"qasu te vendndodhja e kësaj ore"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"qasu te kalendari yt"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"dërgo dhe shiko mesazhet SMS"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"qasu te fotografitë, përmbajtjet audio-vizuale dhe skedarët në orën tënde të dorës"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"regjistro audio"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"bëj fotografi dhe regjistro video"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"kryej dhe menaxho telefonata"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"qasu tek të dhënat e sensorëve rreth shenjave të tua jetësore"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"të bëhet shiriti i statusit"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"qasu te sensorët e trupit (si monitorimet e rrahjeve të zemrës)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"qasu te vendndodhja e përpiktë (në bazë të GPS-së dhe rrjetit)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"qasu te vendndodhja e përafërt (bazuar në rrjet)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"dërgo komanda te karta SIM"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"qasu plotësisht në rrjet"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"menaxho zotëruesit e profilit dhe të pajisjes"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"ndrysho gjendjen WiMAX"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"merr statusin e transferimit përmes \"Dërgimit me rreze të Android\""</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"kalo daljet e përmbajtjes audio-vizuale"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"lexo sesionet e instalimit"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"kërko paketat e instalimit"</string>
</resources>
diff --git a/core/res/res/values-sq-rAL/strings.xml b/core/res/res/values-sq-rAL/strings.xml
index 7df0f86..82f8b3b 100644
--- a/core/res/res/values-sq-rAL/strings.xml
+++ b/core/res/res/values-sq-rAL/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Përfshi të dhënat personale si numrat e kartave të kreditit si dhe fjalëkalimet."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"çaktivizo ose modifiko shiritin e statusit"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Lejon aplikacionin të çaktivizojë shiritin e statusit dhe të heqë ikonat e sistemit."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"shiriti i statusit"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"të bëhet shiriti i statusit"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Lejon aplikacionin të bëhet shiriti i statusit."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"zgjero ose shpalos shiritin e statusit"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Lejon aplikacionin të zgjerojë ose shpalosë shiritin e statusit."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Lejon aplikacionin të marrë dhe përpunojë mesazhe WAP. Kjo do të thotë se aplikacioni mund të monitorojë ose fshijë mesazhe të dërguara në pajisjen tënde, pa t\'i treguar ato."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"gjej aplikacionet në punë"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Lejon aplikacionin të gjejë informacione mbi detyrat aktuale dhe të fundit në punë. Kjo mund të lejojë aplikacionin të zbulojë informacione rreth aplikacioneve të përdorura në pajisje."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Menaxho pronarët e profilit dhe të pajisjes"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"menaxho zotëruesit e profilit dhe të pajisjes"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Lejo aplikacionet që të caktojnë pronarët e profilit dhe pronarin e pajisjes"</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"ri-poziciono aplikacionet në punë"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Lejon aplikacionin të zhvendosë detyra në plan të parë dhe në sfond. Aplikacioni mund ta bëjë këtë pa hyrjen tënde."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Lejon aplikacionin të modifikojë ditarin e telefonatave të tabletit tënd, përfshirë të dhëna rreth telefonatave hyrëse dhe dalëse. Aplikacione keqdashëse mund ta përdorin këtë leje për të fshirë ose modifikuar ditarin tënd të telefonatave."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Lejon aplikacionin të modifikojë ditarin e telefonatave të televizorit tënd, përfshirë të dhëna rreth telefonatave hyrëse dhe dalëse. Aplikacione keqdashëse mund ta përdorin këtë leje për të fshirë ose modifikuar ditarin tënd të telefonatave."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Lejon aplikacionin të modifikojë ditarin e telefonatave të telefonit tënd, përfshirë të dhënat rreth telefonatave hyrëse dhe dalëse. Aplikacionet keqdashëse mund ta përdorin këtë për të fshirë ose modifikuar ditarin tënd të telefonatave."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"ndjesorët e trupit (si monitorimet e rrahjeve të zemrës)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"qasu te sensorët e trupit (si monitorimet e rrahjeve të zemrës)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Lejon aplikacionin të ketë qasje në të dhënat nga ndjesorë që monitorojnë gjendjen tënde fizike, siç janë rrahjet e zemrës."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"lexo ngjarjet kalendarike si dhe informacionin e privatësisë së tyre"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Lejon aplikacionin të lexojë të gjitha ngjarjet kalendarike të ruajtura në tabletin tënd, përfshirë ato të miqve ose kolegëve. Kjo mund ta lejojë aplikacionin të ndajë ose ruajë të dhënat e tua të kalendarit, pavarësisht privatësisë ose ndjeshmërisë së tyre."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Lejon aplikacionin të shtojë, heqë, ndryshojë ngjarje që mund t\'i modifikosh në telefonin tënd, përfshi ato të miqve ose kolegëve. Kjo mund ta lejojë aplikacionin të dërgojë mesazhe që duken se vijnë nga zotëruesit e kalendarëve, ose të modifikojë ngjarje pa dijeninë e zotëruesve."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"qasje në komandat shtesë të ofruesit të vendndodhjes"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Lejon aplikacionin të ketë qasje në komandat shtesë të ofruesit për vendndodhjen. Kjo mund ta lejojë aplikacionin të ndërhyjë në operacionin e GPS-së apo të burimeve të tjera për vendndodhjen."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"vendndodhja e përpiktë (në bazë të GPS-së dhe rrjetit)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"qasu te vendndodhja e përpiktë (në bazë të GPS-së dhe rrjetit)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Lejon aplikacionin të gjejë vendndodhjen tënde të saktë duke përdorur \"Sistemin global të pozicionimit\" (GPS) ose burime të vendndodhjes së rrjetit sikurse antena të operatorëve celularë apo lidhje Wi-Fi. Këto shërbime vendndodhjeje duhet të aktivizohen dhe të ofrohen për pajisjen tënde, në mënyrë që të përdoren nga aplikacioni. Aplikacionet mund ta përdorin këtë për të përcaktuar vendndodhjen momentale, edhe pse ky funksion mund të kërkojë energji shtesë të baterisë."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"vendndodhja e përafërt (bazuar në rrjet)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"qasu te vendndodhja e përafërt (bazuar në rrjet)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Lejon aplikacionin të gjejë vendndodhjen tënde të përafërt. Përcaktimi i saj rrjedh nga shërbimet që përdorin burimet e vendndodhjes së rrjetit siç janë antenat e operatorëve celularë apo lidhje Wi-Fi. Këto shërbime vendndodhjeje duhet të aktivizohen dhe të ofrohen për pajisjen tënde, në mënyrë që të përdoren nga aplikacioni. Aplikacionet mund ta përdorin këtë për të përcaktuar afërsisht se ku ndodhesh."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"ndrysho cilësimet e audios"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Lejon aplikacionin të modifikojë cilësimet globale të audios siç është volumi dhe se cili altoparlant përdoret për daljen."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"regjistro audio"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Lejon aplikacionin të regjistrojë klipe audio me mikrofon. Kjo leje i mundëson aplikacionit të regjistrojë në çdo kohë pa konfirmimin tënd."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"komunikimi i kartës SIM"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"dërgo komanda te karta SIM"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Lejon aplikacionin t\'i dërgojë komanda kartës SIM. Kjo është shumë e rrezikshme."</string>
<string name="permlab_camera" msgid="3616391919559751192">"bëj fotografi dhe video"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Lejon aplikacion të krijojë fotografi dhe video me kamerën. Kjo leje mundëson përdorimin e kamerës në çdo kohë pa konfirmimin tënd."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Lejon aplikacionin të marrë listën e llogarive të njohura nga telefoni. Kjo mund të përfshijë çdo llogari të krijuar nga aplikacionet që ke instaluar."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"shiko lidhjet e rrjetit"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Lejon aplikacionin të shohë informacionin rreth lidhjeve të rrjetit, se çfarë rrjetesh ekzistojnë dhe janë të lidhur."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"qasje e plotë në rrjet"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"qasu plotësisht në rrjet"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Lejon aplikacionin të krijojë fole rrjeti dhe të përdorë protokolle të personalizuara në të. Shfletuesi dhe aplikacionet e tjera sigurojnë mënyra për të dërguar të dhëna në internet, prandaj kjo leje nuk kërkohet për të dërguar të dhëna në internet."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"ndrysho lidhjen e rrjetit"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Lejon aplikacionin të ndryshojë gjendjen e lidhjes së rrjetit."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Lejon aplikacionin të konfigurojë telefonin lokal \"bluetooth\" dhe të zbulojë e çiftohet me pajisjet në distancë."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"lidhu dhe shkëputu nga WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Lejon aplikacionin të përcaktojë nëse WiMAX është i aktivizuar si dhe të mësojë informacion rreth ndonjë rrjeti të lidhur WiMAX."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Ndrysho gjendjen WiMAX"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"ndrysho gjendjen WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Lejon aplikacionin ta lidhë tabletin dhe ta shkëpusë atë nga rrjetet WiMAX."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Lejon aplikacionin të lidhet me televizorin dhe ta shkëpusë atë nga rrjetet WiMAX."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Lejon aplikacionin të lidhet me telefonin dhe ta shkëpusë atë nga rrjetet WiMAX."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Lejon aplikacionin të modifikojë parametrat e kalibrimit të ekranit me prekje. Nuk nevojitet për aplikacionet normale."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"qasje në certifikatat DRM"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Lejon një aplikacion të përgatisë dhe përdorë certifikatat DRM. Nuk nevojitet për aplikacionet normale."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Merr statusin e transferimit të \"rrezes android\""</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"merr statusin e transferimit përmes \"Dërgimit me rreze të Android\""</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Lejon këtë aplikacion të marrë informacion rreth transferimeve aktuale \"Rrezja android\" (Android Beam)"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"hiq certifikatat DRM"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Lejon një aplikacion të heqë certifikatat DRM. Nuk nevojitet për aplikacionet normale."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Kodi PIN është i pasaktë."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Për të shkyçur, shtyp \"Meny\" pastaj 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Numri i urgjencës"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Nuk ka shërbim."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Nuk ka shërbim"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Ekrani është i kyçur."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Shtyp \"Meny\" për të shkyçur ose për të kryer telefonatë urgjence."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Shtyp \"Meny\" për të shkyçur."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Shiko \"Manualin e përdorimit\" ose kontakto \"Kujdesin ndaj klientit\"."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"Karta SIM është e kyçur."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Po shkyç kartën SIM…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Ke tentuar <xliff:g id="NUMBER_0">%d</xliff:g> herë pa sukses për të vizatuar motivin tënd.\n\nProvo sërish për <xliff:g id="NUMBER_1">%d</xliff:g> sekonda."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"E ke shkruar <xliff:g id="NUMBER_0">%d</xliff:g> herë gabim fjalëkalimin. \n\nProvo sërish për <xliff:g id="NUMBER_1">%d</xliff:g> sekonda."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"E ke shkruar <xliff:g id="NUMBER_0">%d</xliff:g> herë gabimisht PIN-in tënd.\n\nProvo sërish për <xliff:g id="NUMBER_1">%d</xliff:g> sekonda."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Ke vizatuar <xliff:g id="NUMBER_0">%d</xliff:g> herë gabimisht motivin tënd të shkyçjes. Pas <xliff:g id="NUMBER_1">%d</xliff:g> tentativave të tjera të pasuksesshme, do të të kërkohet ta shkyçësh tabletin duke përdorur identifikimin në Google.\n\n Provo sërish për <xliff:g id="NUMBER_2">%d</xliff:g> sekonda."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"E ke vizatuar <xliff:g id="NUMBER_0">%d</xliff:g> herë gabimisht motivin tënd të shkyçjes. Pas <xliff:g id="NUMBER_1">%d</xliff:g> tentativave të tjera të pasuksesshme do të të kërkohet ta shkyçësh televizorin duke përdorur identifikimin në Google.\n\n Provo sërish për <xliff:g id="NUMBER_2">%d</xliff:g> sekonda."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Ke vizatuar <xliff:g id="NUMBER_0">%d</xliff:g> herë gabimisht motivin tënd të shkyçjes. Pas <xliff:g id="NUMBER_1">%d</xliff:g> tentativave të tjera të pasuksesshme, do të të kërkohet ta shkyçësh telefonin duke përdorur identifikimin në Google.\n\n Provo sërish për <xliff:g id="NUMBER_2">%d</xliff:g> sekonda."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Ke tentuar <xliff:g id="NUMBER_0">%d</xliff:g> herë pa sukses për ta shkyçur tabletin. Pas <xliff:g id="NUMBER_1">%d</xliff:g> tentativave të tjera të pasuksesshme, tableti do të rivendoset në gjendjen e fabrikës dhe të gjitha të dhënat e përdoruesit do të humben."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Ke tentuar <xliff:g id="NUMBER_0">%d</xliff:g> herë pa sukses për ta shkyçur televizorin. Pas <xliff:g id="NUMBER_1">%d</xliff:g> tentativave të tjera të pasuksesshme, televizori do të rivendoset në gjendjen e fabrikës dhe të gjitha të dhënat e përdoruesit do të humben."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Ke tentuar <xliff:g id="NUMBER_0">%d</xliff:g> herë pa sukses për ta shkyçur telefonin. Pas <xliff:g id="NUMBER_1">%d</xliff:g> tentativave të tjera të pasuksesshme, telefoni do të rivendoset në gjendjen e fabrikës dhe të gjitha të dhënat e përdoruesit do të humben."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Ke tentuar <xliff:g id="NUMBER_0">%1$d</xliff:g> herë pa sukses për të vizatuar motivin tënd.\n\nProvo sërish për <xliff:g id="NUMBER_1">%2$d</xliff:g> sekonda."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"E ke shkruar <xliff:g id="NUMBER_0">%1$d</xliff:g> herë gabim fjalëkalimin. \n\nProvo sërish për <xliff:g id="NUMBER_1">%2$d</xliff:g> sekonda."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"E ke shkruar <xliff:g id="NUMBER_0">%1$d</xliff:g> herë gabimisht PIN-in tënd.\n\nProvo sërish për <xliff:g id="NUMBER_1">%2$d</xliff:g> sekonda."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Ke vizatuar <xliff:g id="NUMBER_0">%1$d</xliff:g> herë gabimisht motivin tënd të shkyçjes. Pas <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativave të tjera të pasuksesshme, do të të kërkohet ta shkyçësh tabletin duke përdorur identifikimin në Google.\n\n Provo sërish për <xliff:g id="NUMBER_2">%3$d</xliff:g> sekonda."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"E ke vizatuar <xliff:g id="NUMBER_0">%1$d</xliff:g> herë gabimisht motivin tënd të shkyçjes. Pas <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativave të tjera të pasuksesshme do të të kërkohet ta shkyçësh televizorin duke përdorur identifikimin në Google.\n\n Provo sërish për <xliff:g id="NUMBER_2">%3$d</xliff:g> sekonda."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Ke vizatuar <xliff:g id="NUMBER_0">%1$d</xliff:g> herë gabimisht motivin tënd të shkyçjes. Pas <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativave të tjera të pasuksesshme, do të të kërkohet ta shkyçësh telefonin duke përdorur identifikimin në Google.\n\n Provo sërish për <xliff:g id="NUMBER_2">%3$d</xliff:g> sekonda."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Ke tentuar <xliff:g id="NUMBER_0">%1$d</xliff:g> herë pa sukses për ta shkyçur tabletin. Pas <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativave të tjera të pasuksesshme, tableti do të rivendoset në gjendjen e fabrikës dhe të gjitha të dhënat e përdoruesit do të humben."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Ke tentuar <xliff:g id="NUMBER_0">%1$d</xliff:g> herë pa sukses për ta shkyçur televizorin. Pas <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativave të tjera të pasuksesshme, televizori do të rivendoset në gjendjen e fabrikës dhe të gjitha të dhënat e përdoruesit do të humben."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Ke tentuar <xliff:g id="NUMBER_0">%1$d</xliff:g> herë pa sukses për ta shkyçur telefonin. Pas <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativave të tjera të pasuksesshme, telefoni do të rivendoset në gjendjen e fabrikës dhe të gjitha të dhënat e përdoruesit do të humben."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Ke tentuar <xliff:g id="NUMBER">%d</xliff:g> herë pa sukses për ta shkyçur tabletin tënd. Tableti tani do të rivendoset në gjendje fabrike."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Ke tentuar <xliff:g id="NUMBER">%d</xliff:g> herë ta shkyçësh pa sukses televizorin tënd. Televizori do të rivendoset tani si në gjendjen e fabrikës."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Ke tentuar <xliff:g id="NUMBER">%d</xliff:g> herë ta shkyçësh pa sukses telefonin tënd. Telefoni do të rivendoset tani si në gjendjen e fabrikës."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"fjalë"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"lidhje"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"linjë"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Testimi i fabrikës dështoi"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Veprimi i TESTIT_TË_FABRIKËS mbështetet vetëm për paketat e instaluara në /sistem/apl."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Nuk u gjet asnjë paketë që siguron veprimin e TESTIT_TË_FABRIKËS."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Po formaton…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Nuk është futur"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Nuk u gjet asnjë aktivitet që përputhet."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Kalo daljet e medias"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"kalo daljet e përmbajtjes audio-vizuale"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Lejon një aplikacion të kalojë daljet mediatike në pajisje të tjera të jashtme."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Lexo sesionet e instalimit"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"lexo sesionet e instalimit"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Lejon një aplikacion të lexojë sesionet e instalimit. Kjo e lejon atë të shohë detaje rreth instalimeve të paketave aktive."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Kërko paketat e instalimit"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"kërko paketat e instalimit"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Lejon që një aplikacion të kërkojë instalimin e paketave."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Prek dy herë për të kontrolluar zmadhimin"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Nuk mundi të shtonte miniaplikacion."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Emër përdoruesi ose fjalëkalim i pavlefshëm."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Harrove emrin e përdoruesit apo fjalëkalimin?\nVizito "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Po kontrollon llogarinë…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"E ke shkruar <xliff:g id="NUMBER_0">%d</xliff:g> herë gabimisht PIN-in tënd.\n\n Provo sërish për <xliff:g id="NUMBER_1">%d</xliff:g> sekonda."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"E ke shkruar <xliff:g id="NUMBER_0">%d</xliff:g> herë gabimisht fjalëkalimin.\n\nProvo sërish për <xliff:g id="NUMBER_1">%d</xliff:g> sekonda."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Ke tentuar <xliff:g id="NUMBER_0">%d</xliff:g> herë pa sukses për të vizatuar motivin tënd. \n\nProvo sërish për <xliff:g id="NUMBER_1">%d</xliff:g> sekonda."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Ke tentuar <xliff:g id="NUMBER_0">%d</xliff:g> herë pa sukses për ta shkyçur tabletin tënd. Pas <xliff:g id="NUMBER_1">%d</xliff:g> tentativave të tjera të pasuksesshme, tableti do të rivendoset në gjendje fabrike dhe të gjitha të dhënat e përdoruesit do të humbasin."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Ke tentuar <xliff:g id="NUMBER_0">%d</xliff:g> herë pa sukses për ta shkyçur televizorin. Pas <xliff:g id="NUMBER_1">%d</xliff:g> tentativave të tjera të pasuksesshme, televizori do të rivendoset në gjendjen e fabrikës dhe të gjitha të dhënat e përdoruesit do të humben."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Ke tentuar <xliff:g id="NUMBER_0">%d</xliff:g> herë pa sukses për ta shkyçur telefonin tënd. Pas <xliff:g id="NUMBER_1">%d</xliff:g> tentativave të tjera të pasuksesshme, telefoni do të rivendoset në gjendje fabrike dhe të gjitha të dhënat e përdoruesit do të humbasin."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"E ke shkruar <xliff:g id="NUMBER_0">%1$d</xliff:g> herë gabimisht PIN-in tënd.\n\n Provo sërish për <xliff:g id="NUMBER_1">%2$d</xliff:g> sekonda."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"E ke shkruar <xliff:g id="NUMBER_0">%1$d</xliff:g> herë gabimisht fjalëkalimin.\n\nProvo sërish për <xliff:g id="NUMBER_1">%2$d</xliff:g> sekonda."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Ke tentuar <xliff:g id="NUMBER_0">%1$d</xliff:g> herë pa sukses për të vizatuar motivin tënd. \n\nProvo sërish për <xliff:g id="NUMBER_1">%2$d</xliff:g> sekonda."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Ke tentuar <xliff:g id="NUMBER_0">%1$d</xliff:g> herë pa sukses për ta shkyçur tabletin tënd. Pas <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativave të tjera të pasuksesshme, tableti do të rivendoset në gjendje fabrike dhe të gjitha të dhënat e përdoruesit do të humbasin."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Ke tentuar <xliff:g id="NUMBER_0">%1$d</xliff:g> herë pa sukses për ta shkyçur televizorin. Pas <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativave të tjera të pasuksesshme, televizori do të rivendoset në gjendjen e fabrikës dhe të gjitha të dhënat e përdoruesit do të humben."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Ke tentuar <xliff:g id="NUMBER_0">%1$d</xliff:g> herë pa sukses për ta shkyçur telefonin tënd. Pas <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativave të tjera të pasuksesshme, telefoni do të rivendoset në gjendje fabrike dhe të gjitha të dhënat e përdoruesit do të humbasin."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Ke tentuar <xliff:g id="NUMBER">%d</xliff:g> herë pa sukses për ta shkyçur tabletin tënd. Tableti tani do të rivendoset në gjendje fabrike."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Ke tentuar <xliff:g id="NUMBER">%d</xliff:g> herë ta shkyçësh pa sukses televizorin tënd. Televizori do të rivendoset tani si në gjendjen e fabrikës."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Ke tentuar <xliff:g id="NUMBER">%d</xliff:g> herë ta shkyçësh pa sukses telefonin tënd. Telefoni do të rivendoset tani si në gjendjen e fabrikës."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"E ke vizatuar gabimisht motivin tënd të shkyçjes <xliff:g id="NUMBER_0">%d</xliff:g> herë. Pas <xliff:g id="NUMBER_1">%d</xliff:g> tentativave të tjera të pasuksesshme do të të kërkohet ta shkyçësh tabletin duke përdorur një llogari mail-i.\n\n Provo sërish për <xliff:g id="NUMBER_2">%d</xliff:g> sekonda."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Ke vizatuar <xliff:g id="NUMBER_0">%d</xliff:g> herë pa sukses motivin tënd. Pas <xliff:g id="NUMBER_1">%d</xliff:g> tentativave të tjera të pasuksesshme, do të të duhet ta shkyçësh televizorin duke përdorur një llogari mail-i.\n\n Provo sërish për <xliff:g id="NUMBER_2">%d</xliff:g> sekonda."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Ke vizatuar <xliff:g id="NUMBER_0">%d</xliff:g> herë pa sukses motivin tënd. Pas <xliff:g id="NUMBER_1">%d</xliff:g> tentativave të tjera të pasuksesshme, do të të duhet ta shkyçësh telefonin duke përdorur një llogari mail-i.\n\n Provo sërish për <xliff:g id="NUMBER_2">%d</xliff:g> sekonda."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"E ke vizatuar gabimisht motivin tënd të shkyçjes <xliff:g id="NUMBER_0">%1$d</xliff:g> herë. Pas <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativave të tjera të pasuksesshme do të të kërkohet ta shkyçësh tabletin duke përdorur një llogari mail-i.\n\n Provo sërish për <xliff:g id="NUMBER_2">%3$d</xliff:g> sekonda."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Ke vizatuar <xliff:g id="NUMBER_0">%1$d</xliff:g> herë pa sukses motivin tënd. Pas <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativave të tjera të pasuksesshme, do të të duhet ta shkyçësh televizorin duke përdorur një llogari mail-i.\n\n Provo sërish për <xliff:g id="NUMBER_2">%3$d</xliff:g> sekonda."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Ke vizatuar <xliff:g id="NUMBER_0">%1$d</xliff:g> herë pa sukses motivin tënd. Pas <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativave të tjera të pasuksesshme, do të të duhet ta shkyçësh telefonin duke përdorur një llogari mail-i.\n\n Provo sërish për <xliff:g id="NUMBER_2">%3$d</xliff:g> sekonda."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" - "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Hiq"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Të ngrihet volumi mbi nivelin e rekomanduar?\n\nDëgjimi me volum të lartë për periudha të gjata mund të dëmtojë dëgjimin."</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">Për 1 orë</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Deri në <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Deri në <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (alarmi tjetër)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Deri sa ta çaktivizosh këtë"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Deri sa të çaktivizosh gjendjen \"Mos shqetëso\""</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-sr-watch/strings.xml b/core/res/res/values-sr-watch/strings.xml
index afeae38..3f86380 100644
--- a/core/res/res/values-sr-watch/strings.xml
+++ b/core/res/res/values-sr-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Апликација <xliff:g id="NUMBER_0">%1$d</xliff:g> од <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Сензори"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"приступ контактима"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"приступ локацији овог сата"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"приступ календару"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"слање и преглед SMS порука"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"приступ сликама, медијским и другим датотекама на сату"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"снимање аудио снимака"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"снимање слика и видео снимака"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"упућивање телефонских позива и управљање њима"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"приступ подацима сензора о виталним функцијама"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"функционисање као статусне траке"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"приступ сензорима на телу (попут монитора за праћење пулса)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"приступ прецизној локацији (утврђена преко мреже и GPS-а)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"приступ приближној локацији (утврђена преко мреже)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"слање команди на SIM"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"има пун мрежни приступ"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"управљање власницима профила и уређаја"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"мењање WiMAX статуса"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"пријем статуса пребацивања помоћу Android пребацивања"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"усмеравање излаза медија"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"читање сесија инсталирања"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"захтевање пакета за инсталирање"</string>
</resources>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 5bb2d78..bab8944 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -255,7 +255,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Обухвата личне податке као што су бројеви кредитних картица и лозинке."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"онемогућавање или измена статусне траке"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Дозвољава апликацији да онемогући статусну траку или да додаје и уклања системске иконе."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"статусна трака"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"функционисање као статусна трака"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Дозвољава апликацији да функционише као статусна трака."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"проширење/скупљање статусне траке"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Дозвољава апликацији да проширује или скупља статусну траку."</string>
@@ -283,7 +283,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Дозвољава апликацији да прима и обрађује WAP поруке. Ова дозвола укључује могућност праћења или брисања порука које вам се шаљу, а које вам се не приказују."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"преузимање покренутих апликација"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Дозвољава апликацији да преузима информације о актуелним и недавно покренутим задацима. Ово може да омогући апликацији да открије информације о томе које се апликације користе на уређају."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Управљај власницима профила и уређаја"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"управљање власницима профила и уређаја"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Дозвољава апликацији да подеси власнике профила и власника уређаја."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"промена редоследа покренутих апликација"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Дозвољава апликацији да премешта задатке у први план и у позадину. Апликација може да ради ово без вашег уноса."</string>
@@ -325,7 +325,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Дозвољава апликацији да мења евиденцију позива на таблету, укључујући податке о долазним и одлазним позивима. Злонамерне апликације могу ово да користе да би брисале или мењале евиденцију позива."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Дозвољава апликацији да мења евиденцију позива на ТВ-у, укључујући податке о долазним и одлазним позивима. Злонамерне апликације могу ово да користе да би брисале или мењале евиденцију позива."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Дозвољава апликацији да мења евиденцију позива на телефону, укључујући податке о долазним и одлазним позивима. Злонамерне апликације могу ово да користе да би брисале или мењале евиденцију позива."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"телесни сензори (нпр. срчани монитор)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"приступ сензорима на телу (попут монитора за праћење пулса)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Дозвољава апликацији да приступа подацима са сензора који надгледају физичку кондицију, као што је број откуцаја срца."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"читање календарских догађаја и поверљивих информација"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Дозвољава апликацији да чита све догађаје календара ускладиштене на таблету, укључујући догађаје пријатеља или колега. Ово може да омогући апликацији да дели или чува податке календара, без обзира на поверљивост или осетљивост."</string>
@@ -337,15 +337,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Дозвољава апликацији да додаје, уклања и мења догађаје које можете да измените на телефону, укључујући догађаје пријатеља и колега. Ово може да омогући апликацији да шаље поруке које изгледају као да их шаљу власници календара или мења догађаје без знања власника."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"приступ додатним командама добављача локације"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Омогућава апликацији да приступа додатним командама даваоца услуга локације. То може да омогући апликацији да утиче на рад GPS-а или других извора локације."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"прецизна локација (заснована на GPS-у и мрежи)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"приступ прецизној локацији (утврђена преко мреже и GPS-а)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Дозвољава апликацији да преузме прецизне податке о вашој локацији помоћу глобалног система позиционирања (GPS) или мрежних извора локација као што су мобилни предајници и Wi-Fi мреже. Ове услуге лоцирања морају да буду укључене и доступне уређају да би апликација могла да их користи. Апликације на основу овога могу да одреде где се приближно налазите и могу додатно да троше батерију."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"приближна локација (заснована на мрежи)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"приступ приближној локацији (утврђена преко мреже)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Дозвољава апликацији да преузме податке о вашој приближној локацији. Податке о овој локацији обезбеђују услуге лоцирања помоћу мрежних извора локација као што су мобилни предајници и Wi-Fi мреже. Ове услуге лоцирања морају да буду укључене и доступне уређају да би апликација могла да их користи. Апликације на основу овога могу да одреде где се приближно налазите."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"промена аудио подешавања"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Дозвољава апликацији да мења глобална аудио подешавања као што су јачина звука и избор звучника који се користи као излаз."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"снимање аудио записа"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Дозвољава апликацији да снима звук помоћу микрофона. Ова дозвола омогућава апликацији да снима звук у било ком тренутку без ваше потврде."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"Комуникација са SIM картицом"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"слање команди на SIM"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Омогућава апликацији да шаље команде SIM картици. То је веома опасно."</string>
<string name="permlab_camera" msgid="3616391919559751192">"снимање фотографија и видео снимака"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Дозвољава апликацији да снима слике и видео снимке камером. Ова дозвола омогућава апликацији да у било ком тренутку користи камеру без ваше потврде."</string>
@@ -383,7 +383,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Дозвољава апликацији да преузима листу налога познатих телефону. Ово може да обухвата било које налоге које праве апликације које инсталирате."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"преглед мрежних веза"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Дозвољава апликацији да прегледа информације о мрежним везама као што су информације о томе које мреже постоје и које мреже су повезане."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"пун мрежни приступ"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"има пун мрежни приступ"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Дозвољава апликацији да прави мрежне прикључке и користи прилагођене мрежне протоколе. Прегледач и друге апликације омогућавају слање података на Интернет, па ова дозвола није потребна за слање података на Интернет."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"промена везе са мрежом"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Дозвољава апликацији да мења статус повезивања са мрежом."</string>
@@ -403,7 +403,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Дозвољава апликацији да конфигурише локални Bluetooth телефон, као и да открије даљинске уређаје и упари се са њима."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"повезивање и прекид везе са WiMAX-ом"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Дозвољава апликацији да утврди да ли је WiMAX омогућен, као и информације о било којим повезаним WiMAX мрежама."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Промени WiMAX статус"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"промени WiMAX статуса"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Дозвољава апликацији да повезује таблет са WiMAX мрежама и прекида везе са њима."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Дозвољава апликацији да повезује ТВ са WiMAX мрежама и да раскида ту везу."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Дозвољава апликацији да повезује телефон са WiMAX мрежама и прекида везе са њима."</string>
@@ -486,7 +486,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Дозвољава апликацији да модификује параметре калибрације додирног екрана. Не би требало да буде потребно за нормалне апликације."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"приступ DRM сертификатима"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Дозвољава апликацији да додељује и користи DRM сертификате. Никада не би требало да се користи за уобичајене апликације."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Пријем статуса пребацивања помоћу Android пребацивања"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"пријем статуса пребацивања помоћу Android пребацивања"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Дозвољава овој апликацији да прима информације о актуелним пребацивањима помоћу Android пребацивања"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"уклањај DRM сертификате"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Дозвољава апликацији да уклања DRM сертификате. Никада не би требало да се користи за обичне апликације."</string>
@@ -651,7 +651,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PIN кôд је нетачан."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Да бисте откључали, притисните „Мени“, а затим 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Број за хитне случајеве"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Нема услуге."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Мобилна мрежа није доступна"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Екран је закључан."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Притисните „Мени“ да бисте откључали телефон или упутите хитан позив."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Притисните „Мени“ за откључавање."</string>
@@ -683,15 +683,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Погледајте Кориснички водич или контактирајте Корисничку подршку."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM картица је закључана."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Откључавање SIM картице…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"<xliff:g id="NUMBER_0">%d</xliff:g> пута сте неправилно нацртали шаблон за откључавање. \n\nПокушајте поново за <xliff:g id="NUMBER_1">%d</xliff:g> секунде(и)."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"<xliff:g id="NUMBER_0">%d</xliff:g> пута сте погрешно унели лозинку. \n\nПокушајте поново за <xliff:g id="NUMBER_1">%d</xliff:g> секунде(и)."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"<xliff:g id="NUMBER_0">%d</xliff:g> пута сте погрешно унели PIN. \n\nПокушајте поново за <xliff:g id="NUMBER_1">%d</xliff:g> секунде(и)."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"<xliff:g id="NUMBER_0">%d</xliff:g> пута сте нетачно унели шаблон за откључавање. Након још <xliff:g id="NUMBER_1">%d</xliff:g> несупешна(их) покушаја, од вас ће бити затражено да откључате таблет помоћу података за пријављивање на Google.\n\n Покушајте поново за <xliff:g id="NUMBER_2">%d</xliff:g> секунде(и)."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Неисправно сте нацртали шаблон за откључавање <xliff:g id="NUMBER_0">%d</xliff:g> пута. После још <xliff:g id="NUMBER_1">%d</xliff:g> неуспешна(их) покушаја од вас ће бити затражено да откључате ТВ помоћу података за пријављивање на Google.\n\n Покушајте поново за <xliff:g id="NUMBER_2">%d</xliff:g> сек."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"<xliff:g id="NUMBER_0">%d</xliff:g> пута сте нетачно унели шаблон за откључавање. Након још <xliff:g id="NUMBER_1">%d</xliff:g> несупешна(их) покушаја, од вас ће бити затражено да откључате телефон помоћу података за пријављивање на Google.\n\n Покушајте поново за <xliff:g id="NUMBER_2">%d</xliff:g> секунде(и)."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Неправилно сте покушали да откључате таблет <xliff:g id="NUMBER_0">%d</xliff:g> пута. Након још неуспешних покушаја (<xliff:g id="NUMBER_1">%d</xliff:g>) таблет ће бити ресетован на фабричка подешавања и сви кориснички подаци ће бити изгубљени."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Покушали сте да откључате ТВ нетачно <xliff:g id="NUMBER_0">%d</xliff:g> пута. После још <xliff:g id="NUMBER_1">%d</xliff:g> неуспешна(их) покушаја ТВ ће бити ресетован на подразумевана фабричка подешавања и сви кориснички подаци ће бити изгубљени."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Неисправно сте покушали да откључате телефон <xliff:g id="NUMBER_0">%d</xliff:g> пута. Након још неуспешних покушаја (<xliff:g id="NUMBER_1">%d</xliff:g>) телефон ће бити ресетован на фабричка подешавања и сви кориснички подаци ће бити изгубљени."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"<xliff:g id="NUMBER_0">%1$d</xliff:g> пута сте неправилно нацртали шаблон за откључавање. \n\nПокушајте поново за <xliff:g id="NUMBER_1">%2$d</xliff:g> секунде(и)."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"<xliff:g id="NUMBER_0">%1$d</xliff:g> пута сте погрешно унели лозинку. \n\nПокушајте поново за <xliff:g id="NUMBER_1">%2$d</xliff:g> секунде(и)."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"<xliff:g id="NUMBER_0">%1$d</xliff:g> пута сте погрешно унели PIN. \n\nПокушајте поново за <xliff:g id="NUMBER_1">%2$d</xliff:g> секунде(и)."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"<xliff:g id="NUMBER_0">%1$d</xliff:g> пута сте нетачно унели шаблон за откључавање. Након још <xliff:g id="NUMBER_1">%2$d</xliff:g> несупешна(их) покушаја, од вас ће бити затражено да откључате таблет помоћу података за пријављивање на Google.\n\n Покушајте поново за <xliff:g id="NUMBER_2">%3$d</xliff:g> секунде(и)."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Неисправно сте нацртали шаблон за откључавање <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. После још <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешна(их) покушаја од вас ће бити затражено да откључате ТВ помоћу података за пријављивање на Google.\n\n Покушајте поново за <xliff:g id="NUMBER_2">%3$d</xliff:g> сек."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"<xliff:g id="NUMBER_0">%1$d</xliff:g> пута сте нетачно унели шаблон за откључавање. Након још <xliff:g id="NUMBER_1">%2$d</xliff:g> несупешна(их) покушаја, од вас ће бити затражено да откључате телефон помоћу података за пријављивање на Google.\n\n Покушајте поново за <xliff:g id="NUMBER_2">%3$d</xliff:g> секунде(и)."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Неправилно сте покушали да откључате таблет <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. Након још неуспешних покушаја (<xliff:g id="NUMBER_1">%2$d</xliff:g>) таблет ће бити ресетован на фабричка подешавања и сви кориснички подаци ће бити изгубљени."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Покушали сте да откључате ТВ нетачно <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. После још <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешна(их) покушаја ТВ ће бити ресетован на подразумевана фабричка подешавања и сви кориснички подаци ће бити изгубљени."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Неисправно сте покушали да откључате телефон <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. Након још неуспешних покушаја (<xliff:g id="NUMBER_1">%2$d</xliff:g>) телефон ће бити ресетован на фабричка подешавања и сви кориснички подаци ће бити изгубљени."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Неисправно сте покушали да откључате таблет <xliff:g id="NUMBER">%d</xliff:g> пута. Таблет ће сада бити враћен на подразумевана фабричка подешавања."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Покушали сте да откључате ТВ нетачно <xliff:g id="NUMBER">%d</xliff:g> пута. ТВ ће сада бити ресетован на подразумевана фабричка подешавања."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Неисправно сте покушали да откључате телефон <xliff:g id="NUMBER">%d</xliff:g> пута. Телефон ће сада бити враћен на подразумевана фабричка подешавања."</string>
@@ -743,8 +743,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"реч"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"веза"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"ред"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Фабричко тестирање није успело"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Радња FACTORY_TEST је подржана само за пакете инсталиране у директоријуму /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Није пронађен ниједан пакет који обезбеђује радњу FACTORY_TEST."</string>
@@ -1080,11 +1078,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Форматира се..."</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Није уметнут"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Није пронађена ниједна подударна активност."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Усмеравање излаза медија"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"усмеравање излаза медија"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Дозвољава апликацији да усмерава излаз медија на друге спољне уређаје."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Читај сесије инсталирања"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"читање сесија инсталирања"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Дозвољава апликацији да чита сесије инсталирања. То јој дозвољава да види детаље о активним инсталацијама пакета."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Захтевај инсталирање пакета"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"захтевање пакета за инсталирање"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Омогућава да апликација захтева инсталацију пакета."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Додирните двапут да бисте контролисали зум"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Није могуће додати виџет."</string>
@@ -1293,18 +1291,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Неважеће корисничко име или лозинка."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Заборавили сте корисничко име или лозинку?\nПосетите адресу "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Провера налога…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Унели сте нетачни PIN <xliff:g id="NUMBER_0">%d</xliff:g> пута. \n\nПокушајте поново за <xliff:g id="NUMBER_1">%d</xliff:g> секунде(и)."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Унели сте нетачну лозинку <xliff:g id="NUMBER_0">%d</xliff:g> пута. \n\nПокушајте поново за <xliff:g id="NUMBER_1">%d</xliff:g> секунде(и)."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Нацртали сте шаблон за откључавање нетачно <xliff:g id="NUMBER_0">%d</xliff:g> пута. \n\nПокушајте поново за <xliff:g id="NUMBER_1">%d</xliff:g> секунде(и)."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Покушали сте да откључате таблет нетачно <xliff:g id="NUMBER_0">%d</xliff:g> пута. Након још <xliff:g id="NUMBER_1">%d</xliff:g> неуспешна(их) покушаја таблет ће бити ресетован на фабричка подешавања и сви кориснички подаци ће бити изгубљени."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Покушали сте да откључате ТВ нетачно <xliff:g id="NUMBER_0">%d</xliff:g> пута. После још <xliff:g id="NUMBER_1">%d</xliff:g> неуспешна(их) покушаја ТВ ће бити ресетован на подразумевана фабричка подешавања и сви кориснички подаци ће бити изгубљени."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Покушали сте да откључате телефон нетачно <xliff:g id="NUMBER_0">%d</xliff:g> пута. После још <xliff:g id="NUMBER_1">%d</xliff:g> неуспешна(их) покушаја телефон ће бити ресетован на фабричка подешавања и сви кориснички подаци ће бити изгубљени."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Унели сте нетачни PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. \n\nПокушајте поново за <xliff:g id="NUMBER_1">%2$d</xliff:g> секунде(и)."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Унели сте нетачну лозинку <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. \n\nПокушајте поново за <xliff:g id="NUMBER_1">%2$d</xliff:g> секунде(и)."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Нацртали сте шаблон за откључавање нетачно <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. \n\nПокушајте поново за <xliff:g id="NUMBER_1">%2$d</xliff:g> секунде(и)."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Покушали сте да откључате таблет нетачно <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. Након још <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешна(их) покушаја таблет ће бити ресетован на фабричка подешавања и сви кориснички подаци ће бити изгубљени."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Покушали сте да откључате ТВ нетачно <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. После још <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешна(их) покушаја ТВ ће бити ресетован на подразумевана фабричка подешавања и сви кориснички подаци ће бити изгубљени."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Покушали сте да откључате телефон нетачно <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. После још <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешна(их) покушаја телефон ће бити ресетован на фабричка подешавања и сви кориснички подаци ће бити изгубљени."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Покушали сте да откључате таблет нетачно <xliff:g id="NUMBER">%d</xliff:g> пута. Таблет ће сада бити враћен на подразумевана фабричка подешавања."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Покушали сте да откључате ТВ нетачно <xliff:g id="NUMBER">%d</xliff:g> пута. ТВ ће сада бити ресетован на подразумевана фабричка подешавања."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Покушали сте да откључате телефон нетачно <xliff:g id="NUMBER">%d</xliff:g> пута. Телефон ће сада бити враћен на подразумевана фабричка подешавања."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Нацртали сте шаблон за откључавање нетачно <xliff:g id="NUMBER_0">%d</xliff:g> пута. После још <xliff:g id="NUMBER_1">%d</xliff:g> неуспешна(их) покушаја, од вас ће бити затражено да откључате таблет помоћу налога е-поште.\n\nПокушајте поново за <xliff:g id="NUMBER_2">%d</xliff:g> секунде(и)."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Неисправно сте нацртали шаблон за откључавање <xliff:g id="NUMBER_0">%d</xliff:g> пута. После још <xliff:g id="NUMBER_1">%d</xliff:g> неуспешна(их) покушаја, од вас ће бити затражено да откључате ТВ помоћу налога е-поште.\n\n Покушајте поново за <xliff:g id="NUMBER_2">%d</xliff:g> сек."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Нацртали сте шаблон за откључавање нетачно <xliff:g id="NUMBER_0">%d</xliff:g> пута. После још <xliff:g id="NUMBER_1">%d</xliff:g> неуспешна(их) покушаја, од вас ће бити затражено да откључате телефон помоћу налога е-поште.\n\nПокушајте поново за <xliff:g id="NUMBER_2">%d</xliff:g> секунде(и)."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Нацртали сте шаблон за откључавање нетачно <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. После још <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешна(их) покушаја, од вас ће бити затражено да откључате таблет помоћу налога е-поште.\n\nПокушајте поново за <xliff:g id="NUMBER_2">%3$d</xliff:g> секунде(и)."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Неисправно сте нацртали шаблон за откључавање <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. После још <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешна(их) покушаја, од вас ће бити затражено да откључате ТВ помоћу налога е-поште.\n\n Покушајте поново за <xliff:g id="NUMBER_2">%3$d</xliff:g> сек."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Нацртали сте шаблон за откључавање нетачно <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. После још <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешна(их) покушаја, од вас ће бити затражено да откључате телефон помоћу налога е-поште.\n\nПокушајте поново за <xliff:g id="NUMBER_2">%3$d</xliff:g> секунде(и)."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Уклони"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Желите да појачате звук изнад препорученог нивоа?\n\nСлушање гласне музике дуже време може да вам оштети слух."</string>
@@ -1487,6 +1485,7 @@
<item quantity="other">За %d с</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (следећи аларм)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Док не искључите"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Док не искључите режим Не узнемиравај"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-sv-watch/strings.xml b/core/res/res/values-sv-watch/strings.xml
index 439b464..e6da48a 100644
--- a/core/res/res/values-sv-watch/strings.xml
+++ b/core/res/res/values-sv-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"App <xliff:g id="NUMBER_0">%1$d</xliff:g> av <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Sensorer"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"få tillgång till dina kontakter"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"få tillgång till klockans plats"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"få tillgång till din kalender"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"skicka och visa sms"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"få åtkomst till foton, media och filer på klockan"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"spela in ljud"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"ta bilder och spela in video"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"ringa och hantera telefonsamtal"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"få åtkomst till sensordata om dina vitalparametrar"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"visas i statusfältet"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"få åtkomst till kroppssensorer (till exempel pulsmätare)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"få åtkomst till din exakta position (GPS- och nätverksbaserad)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"få åtkomst till din ungefärliga position (nätverksbaserad)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"skicka kommandon till SIM-kortet"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"få fullständig nätverksåtkomst"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"hantera profil- och enhetsägare"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"ändra WiMAX-status"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"ta emot status för Android Beam-överföring"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"dirigera medieuppspelning"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"läsa installationssessioner"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"begära installationspaket"</string>
</resources>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index e803cbb..3909c6b 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Omfattar personuppgifter som kreditkortsnummer och lösenord."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"inaktivera eller ändra statusfält"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Tillåter att appen inaktiverar statusfältet eller lägger till och tar bort systemikoner."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"statusfält"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"visas i statusfältet"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Tillåter att appen visas i statusfältet."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"expandera/komprimera statusfält"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Tillåter att appen expanderar eller komprimerar statusfältet."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Tillåter att appen tar emot och hanterar WAP-meddelanden. Med den här behörigheten kan appen övervaka eller ta bort meddelanden som skickats till dig utan att visa dem för dig."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"hämta appar som körs"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Tillåter att appen hämtar information om nyligen körda och pågående aktiviteter. Detta kan innebära att appen tillåts ta reda på vilka appar som används på enheten."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Hantera profil- och enhetsägare"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"hantera profil- och enhetsägare"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Tillåter att appar anger profilägare och enhetsägare."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"byt ordning på appar som körs"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Tillåter att appen flyttar aktiviteter till förgrunden eller bakgrunden. Appen kan göra detta utan åtgärd från dig."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Tillåter att appen gör ändringar i pekdatorns samtalslista, inklusive i uppgifter om inkommande och utgående samtal. Skadliga program kan använda detta för att radera eller ändra din samtalslista."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Tillåter att appen gör ändringar i TV:ns samtalslista, inklusive i uppgifter om inkommande och utgående samtal. Skadliga appar kan använda detta för att rensa eller ändra din samtalslista."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Tillåter att appen gör ändringar i mobilens samtalslista, inklusive i uppgifter om inkommande och utgående samtal. Skadliga program kan använda detta för att radera eller ändra din samtalslista."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"kroppssens. (för hjärtat m.m.)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"få åtkomst till kroppssensorer (till exempel pulsmätare)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Ger appen åtkomst till information från sensorer om ditt fysiska tillstånd, till exempel din puls."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"läsa kalenderuppgifter plus konfidentiell information"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Tillåter att appen läser alla kalenderuppgifter som sparats på surfplattan, inklusive dina vänners eller kollegors uppgifter. Med den här behörigheten kan appen tillåtas att dela eller spara kalenderuppgifter även om de är sekretessbelagda eller känsliga."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Tillåter att appen lägger till, tar bort och ändrar sådana händelser som du kan ändra på mobilen, inklusive dina vänners eller kollegors uppgifter. Detta kan innebära att appen tillåts skicka meddelanden som ser ut att komma från kalenderns ägare eller ändra händelser utan ägarens vetskap."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"få åtkomst till extra kommandon för platsleverantör"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Tillåter att appen får åtkomst till extra kommandon för platsleverantör. Detta kan innebära att appen tillåts störa funktionen för GPS eller andra platskällor."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"exakt plats (GPS- och nätverksbaserad)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"få åtkomst till din exakta position (GPS- och nätverksbaserad)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Tillåter att appen känner av din ungefärliga position med hjälp av GPS eller platstjänster som mobilmaster och Wi-Fi. Platstjänsterna måste vara aktiverade och tillgängliga på enheten för att appen ska kunna använda dem. Appar kan använda detta för att avgöra ungefär var du befinner dig."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"ungefärlig position (nätverksbaserad)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"få åtkomst till din ungefärliga position (nätverksbaserad)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Tillåter att appen känner av din ungefärliga position. Platsen avgörs genom platstjänster med hjälp av nätverksplatskällor som mobilmaster och Wi-Fi. Platstjänsterna måste vara aktiverade och tillgängliga på enheten för att appen ska kunna använda dem. Appar kan använda detta för att avgöra ungefär var du befinner dig."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"ändra dina ljudinställningar"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Tillåter att appen ändrar globala ljudinställningar som volym och vilken högtalarutgång som används."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"spela in ljud"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Tillåter att appen spelar in ljud med mikrofonen. Med den här behörigheten tillåts appen att spela in ljud när som helst utan ditt godkännande."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"SIM-kommunikation"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"skicka kommandon till SIM-kortet"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Tillåter att appen skickar kommandon till SIM-kortet. Detta är mycket farligt."</string>
<string name="permlab_camera" msgid="3616391919559751192">"ta bilder och spela in videoklipp"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Tillåter att appen tar bilder och spelar in videor med kameran. Med den här behörigheten tillåts appen att använda kameran när som helst utan ditt godkännande."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Tillåter att appen hämtar en lista över alla kända konton på mobilen. Detta kan inkludera konton som har skapats av appar som du har installerat."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"visa nätverksanslutningar"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Tillåter att appen kommer åt information om nätverksanslutningarna, till exempel vilka nätverk som finns och är anslutna."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"fullständig nätverksåtkomst"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"få fullständig nätverksåtkomst"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Tillåter att appen skapar nätverksuttag och använder anpassade nätverksprotokoll. Webbläsaren och andra appar gör det möjligt att skicka data till Internet, så den här behörigheten krävs inte för att skicka data till Internet."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"ändra nätverksanslutning"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Tillåter att appen ändrar statusen för en nätverksanslutning."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Tillåter att appen konfigurerar den lokala Bluetooth-mobilen samt upptäcker och parkopplar den med fjärranslutna enheter."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"ansluta till och koppla från WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Tillåter att appen avgör om WiMAX är aktiverat och kommer åt information om eventuella anslutna WiMAX-nätverk."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"ändra WiMAX-status"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"ändra WiMAX-status"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Tillåter att appen ansluter surfplattan till eller kopplar från WiMAX-nätverk."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Tillåter att appen ansluter TV:n till och kopplar från TV:n från WiMAX-nätverk."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Tillåter att appen ansluter mobilen till eller kopplar från WiMAX-nätverk."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Tillåter att appen ändrar kalibreringsparametrarna för pekskärmen. Detta behövs aldrig för vanliga appar."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"tillgång till DRM-certifikat"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Tillåter att en app tillhandahåller och använder DRM-certifikat. Behövs inte för vanliga appar."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Ta emot status för Android Beam-överföring"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"ta emot status för Android Beam-överföring"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Tillåter att appen tar emot information om aktuella Android Beam-överföringar"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"ta bort DRM-certifikat"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Tillåter ett program att ta bort DRM-certifikat. Behövs inte för vanliga appar."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Fel PIN-kod."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Tryck på Menu och sedan på 0 om du vill låsa upp."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Nödsamtalsnummer"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Ingen tjänst."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Ingen tjänst"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Skärmen har låsts."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Tryck på Menu om du vill låsa upp eller ringa nödsamtal."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Tryck på Menu om du vill låsa upp."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Se användarhandboken eller kontakta kundtjänst."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM-kortet är låst."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Låser upp SIM-kort…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. \n\nFörsök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Du har angett fel lösenord <xliff:g id="NUMBER_0">%d</xliff:g> gånger. \n\nFörsök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Du har angett fel lösenord <xliff:g id="NUMBER_0">%d</xliff:g> gånger. \n\nFörsök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter <xliff:g id="NUMBER_1">%d</xliff:g> försök till ombeds du att låsa upp surfplattan med din Google-inloggning.\n\n Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Du har ritat fel mönster för upplåsning <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök blir du ombedd att låsa upp TV:n genom att logga in på Google.\n\n Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter <xliff:g id="NUMBER_1">%d</xliff:g> försök till ombeds du att låsa upp mobilen med uppgifterna som du använder när du loggar in på Google.\n\n Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Du har försökt låsa upp surfplattan på fel sätt <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök till kommer surfplattan att återställas till fabriksinställningarna. Du förlorar då alla användardata."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Du har misslyckats med att låsa upp TV:n <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök kommer TV:n att återställas till standardinställningarna och alla användaruppgifter kommer att gå förlorade."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Du har försökt låsa upp mobilen på fel sätt <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök till kommer mobilen att återställas till fabriksinställningarna. Du förlorar då alla användardata."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%1$d</xliff:g> gånger. \n\nFörsök igen om <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunder."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Du har angett fel lösenord <xliff:g id="NUMBER_0">%1$d</xliff:g> gånger. \n\nFörsök igen om <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunder."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Du har angett fel lösenord <xliff:g id="NUMBER_0">%1$d</xliff:g> gånger. \n\nFörsök igen om <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunder."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%1$d</xliff:g> gånger. Efter <xliff:g id="NUMBER_1">%2$d</xliff:g> försök till ombeds du att låsa upp surfplattan med din Google-inloggning.\n\n Försök igen om <xliff:g id="NUMBER_2">%3$d</xliff:g> sekunder."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Du har ritat fel mönster för upplåsning <xliff:g id="NUMBER_0">%1$d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%2$d</xliff:g> misslyckade försök blir du ombedd att låsa upp TV:n genom att logga in på Google.\n\n Försök igen om <xliff:g id="NUMBER_2">%3$d</xliff:g> sekunder."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%1$d</xliff:g> gånger. Efter <xliff:g id="NUMBER_1">%2$d</xliff:g> försök till ombeds du att låsa upp mobilen med uppgifterna som du använder när du loggar in på Google.\n\n Försök igen om <xliff:g id="NUMBER_2">%3$d</xliff:g> sekunder."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Du har försökt låsa upp surfplattan på fel sätt <xliff:g id="NUMBER_0">%1$d</xliff:g> gånger. Efter <xliff:g id="NUMBER_1">%2$d</xliff:g> misslyckade försök till kommer surfplattan att återställas till fabriksinställningarna. Du förlorar då alla användardata."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Du har misslyckats med att låsa upp TV:n <xliff:g id="NUMBER_0">%1$d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%2$d</xliff:g> misslyckade försök kommer TV:n att återställas till standardinställningarna och alla användaruppgifter kommer att gå förlorade."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Du har försökt låsa upp mobilen på fel sätt <xliff:g id="NUMBER_0">%1$d</xliff:g> gånger. Efter <xliff:g id="NUMBER_1">%2$d</xliff:g> misslyckade försök till kommer mobilen att återställas till fabriksinställningarna. Du förlorar då alla användardata."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Du har försökt låsa upp surfplattan på fel sätt <xliff:g id="NUMBER">%d</xliff:g> gånger. Surfplattan återställs nu till fabriksinställningarna."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Du har misslyckats med att låsa upp TV:n <xliff:g id="NUMBER">%d</xliff:g> gånger. TV:n kommer nu att återställas till standardinställningarna."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Du har försökt låsa upp mobilen på fel sätt <xliff:g id="NUMBER">%d</xliff:g> gånger. Mobilen återställs nu till fabriksinställningarna."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"ord"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"länk"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"rad"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Det gick fel vid fabrikstestet"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Åtgärden FACTORY_TEST stöds endast för paket som har installerats i /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Vi hittade inget paket som erbjuder åtgärden FACTORY_TEST."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Formaterar ..."</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Inte isatt"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Det gick inte att hitta några matchande aktiviteter."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Omdirigera medieuppspelning"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"dirigera medieuppspelning"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Tillåter att appen omdirigerar medieuppspelningar till andra externa enheter."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Läsa installationssessioner"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"läsa installationssessioner"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Tillåt appen att läsa installationssessioner. Det ger den tillgång till uppgifter om aktiva paketinstallationer."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Begär installationspaket"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"begära installationspaket"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Tillåter att en app begär paketinstallation."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Tryck två gånger för zoomkontroll"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Det gick inte att lägga till widgeten."</string>
@@ -1182,7 +1180,7 @@
<string name="shareactionprovider_share_with" msgid="806688056141131819">"Dela med"</string>
<string name="shareactionprovider_share_with_application" msgid="5627411384638389738">"Dela med <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
<string name="content_description_sliding_handle" msgid="415975056159262248">"Skärmlåsfunktion. Tryck och dra."</string>
- <string name="description_target_unlock_tablet" msgid="3833195335629795055">"Lås upp genom att dra."</string>
+ <string name="description_target_unlock_tablet" msgid="3833195335629795055">"Lås upp genom att svepa."</string>
<string name="keyboard_headset_required_to_hear_password" msgid="7011927352267668657">"Anslut mikrofonlurar om du vill att lösenordet ska läsas upp."</string>
<string name="keyboard_password_character_no_headset" msgid="2859873770886153678">"Punkt."</string>
<string name="action_bar_home_description" msgid="5293600496601490216">"Visa startsidan"</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Ogiltigt användarnamn eller lösenord."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Har du glömt ditt användarnamn eller lösenord?\nBesök "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Kontot kontrolleras …"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Du har angett fel lösenord <xliff:g id="NUMBER_0">%d</xliff:g> gånger. \n\nFörsök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Du har angett fel lösenord <xliff:g id="NUMBER_0">%d</xliff:g> gånger. \n\nFörsök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. \n\nFörsök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Du har försökt låsa upp mobilen på fel sätt <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök återställs surfplattan till fabriksinställningarna. Du förlorar då alla användardata."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Du har misslyckats med att låsa upp TV:n <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök kommer TV:n att återställas till standardinställningarna och alla användaruppgifter kommer att gå förlorade."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Du har försökt låsa upp mobilen på fel sätt <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök återställs mobilen till fabriksinställningarna. Du förlorar då alla användardata."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Du har angett fel lösenord <xliff:g id="NUMBER_0">%1$d</xliff:g> gånger. \n\nFörsök igen om <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunder."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Du har angett fel lösenord <xliff:g id="NUMBER_0">%1$d</xliff:g> gånger. \n\nFörsök igen om <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunder."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%1$d</xliff:g> gånger. \n\nFörsök igen om <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunder."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Du har försökt låsa upp mobilen på fel sätt <xliff:g id="NUMBER_0">%1$d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%2$d</xliff:g> misslyckade försök återställs surfplattan till fabriksinställningarna. Du förlorar då alla användardata."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Du har misslyckats med att låsa upp TV:n <xliff:g id="NUMBER_0">%1$d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%2$d</xliff:g> misslyckade försök kommer TV:n att återställas till standardinställningarna och alla användaruppgifter kommer att gå förlorade."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Du har försökt låsa upp mobilen på fel sätt <xliff:g id="NUMBER_0">%1$d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%2$d</xliff:g> misslyckade försök återställs mobilen till fabriksinställningarna. Du förlorar då alla användardata."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Du har försökt låsa upp surfplattan på fel sätt <xliff:g id="NUMBER">%d</xliff:g> gånger. Surfplattan återställs nu till fabriksinställningarna."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Du har misslyckats med att låsa upp TV:n <xliff:g id="NUMBER">%d</xliff:g> gånger. TV:n kommer nu att återställas till standardinställningarna."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Du har försökt låsa upp mobilen på fel sätt <xliff:g id="NUMBER">%d</xliff:g> gånger. Mobilen återställs nu till fabriksinställningarna."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> försök ombeds du låsa upp surfplattan med ett e-postkonto.\n\n Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Du har ritat fel mönster för upplåsning <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök blir du ombedd att låsa upp TV:n via ett e-postkonto.\n\n Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> försök ombeds du låsa upp mobilen med hjälp av ett e-postkonto.\n\n Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%1$d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%2$d</xliff:g> försök ombeds du låsa upp surfplattan med ett e-postkonto.\n\n Försök igen om <xliff:g id="NUMBER_2">%3$d</xliff:g> sekunder."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Du har ritat fel mönster för upplåsning <xliff:g id="NUMBER_0">%1$d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%2$d</xliff:g> misslyckade försök blir du ombedd att låsa upp TV:n via ett e-postkonto.\n\n Försök igen om <xliff:g id="NUMBER_2">%3$d</xliff:g> sekunder."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%1$d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%2$d</xliff:g> försök ombeds du låsa upp mobilen med hjälp av ett e-postkonto.\n\n Försök igen om <xliff:g id="NUMBER_2">%3$d</xliff:g> sekunder."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Ta bort"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Vill du höja volymen över den rekommenderade nivån?\n\nAtt lyssna med stark volym långa stunder åt gången kan skada hörseln."</string>
@@ -1414,7 +1412,7 @@
</plurals>
<string name="restr_pin_try_later" msgid="973144472490532377">"Försök igen senare"</string>
<string name="immersive_cling_title" msgid="8394201622932303336">"Visar på fullskärm"</string>
- <string name="immersive_cling_description" msgid="3482371193207536040">"Dra nedåt från skärmens överkant för att avsluta."</string>
+ <string name="immersive_cling_description" msgid="3482371193207536040">"Svep nedåt från skärmens överkant för att avsluta."</string>
<string name="immersive_cling_positive" msgid="5016839404568297683">"OK"</string>
<string name="done_label" msgid="2093726099505892398">"Klart"</string>
<string name="hour_picker_description" msgid="6698199186859736512">"Cirkelreglage för timmar"</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">I en 1 tim</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Till kl. <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Till <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (nästa alarm)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Tills du inaktiverar detta"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Tills du inaktiverar Stör ej"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-sw-watch/strings.xml b/core/res/res/values-sw-watch/strings.xml
index 3cad827..38c667d 100644
--- a/core/res/res/values-sw-watch/strings.xml
+++ b/core/res/res/values-sw-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Programu ya <xliff:g id="NUMBER_0">%1$d</xliff:g> kati ya <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Vihisi"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"fikia anwani zako"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"tambua mahali saa hii ilipo"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"fikia kalenda yako"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"tuma na uangalie ujumbe wa SMS"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"fikia picha, maudhui na faili kwenye saa yako"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"rekodi sauti"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"piga picha na urekodi video"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"piga na udhibiti simu"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"fikia data ya kihisi kuhusu alama zako muhimu"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"kuwa sehemu ya arifa"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"fikia vihisi vya mwili (kama vifuatiliaji vya mapigo ya moyo)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"fikia mahali halisi (inategemea mtandao na GPS)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"fikia mahali karibu na hapo (inategemea mtandao)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"tuma amri kwenye SIM"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"pata ufikiaji kamili wa mtandao"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"simamia wamiliki wa wasifu na vifaa"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"badilisha hali ya WiMAX"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"pokea hali ya uhamisho wa Boriti ya Android"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"tuma njia ya kutoa maudhui"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"soma vipindi vya kusakinisha"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"omba ruhusa ya kusakinisha vifurushi"</string>
</resources>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index af04a46..c9cdd8d 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -256,7 +256,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Inajumuisha data binafsi kama vile nambari za kadi ya mkopo na manenosiri."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"zima au rekebisha mwambaa hali"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Inaruhusu programu kulemaza upau wa hali au kuongeza na kutoa ikoni za mfumo."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"mwamba hali"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"kuwa sehemu ya arifa"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Inaruhusu programu kuwa upau wa hali."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"panua/kunja mwambaa hali"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Inaruhusu programu kupanua au kukunja upau wa hali."</string>
@@ -284,7 +284,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Inaruhusu programu kupokea na kuchakata ujumbe wa WAP. Idhini hii inajumuisha uwezo wa kuchunguza na kufuta ujumbe uliotumwa kwako bila ya kukuonyesha."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"rudisha programu zinazoendeshwa"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Inaruhusu programu kurudisha taarifa kuhusu kazi zinazoendeshwa sasa na hivi karibuni. Hii inaweza kuruhusu programu kugundua taarifa kuhusu ni programu zipi zinazotumika kwenye kifaa."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Simamia wamiliki wa wasifu na kifaa"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"simamia wamiliki wa wasifu na vifaa"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Huruhusu programu kuweka wamiliki wa wasifu na mmiliki wa kifaa."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"Agiza tena programu za kuendeshwa"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Inaruhusu programu kusongesha kazi hadi kwenye mandhari-mbele na mandari nyuma. Programu inaweza kufanya haya bila ya maingizo yako."</string>
@@ -326,7 +326,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Huruhusu programu kurekebisha rajisi ya kompyuta kibao yako, ikiwa ni pamoja na simu zinazoingia na kutoka. Huenda programu hasidi zikatumia hii ili kufuta au kurekebisha rajisi ya simu yako."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Huruhusu programu kurekebisha rajisi ya runinga yako, ikiwa ni pamoja na data ya simu zinazoingia na kutoka. Huenda programu hasidi zikatumia hii ili kufuta au kurekebisha rajisi ya simu yako."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Huruhusu programu kurekebisha rajisi ya simu yako, ikiwa ni pamoja na simu zinazoingia na kutoka. Huenda programu hasidi zikatumia hii ili kufuta au kurekebisha rajisi ya simu yako."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"vipima hali ya mwili (kama mpigo wa moyo)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"fikia vihisi vya mwili (kama vifuatiliaji vya mapigo ya moyo)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Huruhusu programu kufikia data kutoka vihisi vinavyofuatilia hali yako ya kimwili, kama vile mapigo ya moyo."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"soma matukio ya kalenda pamoja na maelezo ya siri"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Inaruhusu programu kusoma matukio yote ya kalenda yaliohifadhiwa kwenye kompyuta kibao yako, yakijumuisha yale ya marafiki au wafanyakazi wenza. Hii inaweza kuruhusu programu kushiriki au kuhifadhi data yako ya kaelnda, bila kujali usiri au unyeti."</string>
@@ -338,15 +338,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Inaruhusu programu kuongeza, kuondoa, kubadilisha matukio ambayo unaweza kurekebisha kwenye simu yako, yakijumulisha yale ya marafiki na wafanyakazi wenza. Hii inaweza kuruhusu programu kutuma ujumbe unaonekana kuwa unatoka kwa mmiliki wa kalenda, au kurekebisha matukio bila mmiliki kujia."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"fikia amri za ziada za mtoa huduma ya mahali"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Ruhusu programu kufikia amri za ziada za mtoa huduma za mahali. Hii huenda ikaruhusu programu ikatize matumizi ya GPS au vyanzo vingine vya eneo."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"kutambua eneo sahihi (GPS na mtandao)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"fikia mahali halisi (inategemea mtandao na GPS)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Inaruhusu programu kupata eneo lako sahihi kwa kutumia Mfumo wa Mkao Ulimwenguni (GPS) au vyanzo vya mtandao vya eneo kama vile minara na Wi-Fi. Lazima huduma hizi za eneo ziwashwe na kupatikana kwenye kifaa chako ili programu izitumie. Huenda programu zikatumia hii kutambua ulipo, na zinaweza kutumia kawi ya ziada ya betri."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"kukadiria eneo (kwenye mtandao)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"fikia mahali karibu na hapo (inategemea mtandao)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Inaruhusu programu kupata eneo lako la kukadiria. Eneo hili linatokana na huduma za maeneo kwa kutumia vyanzo vya mtandao vya eneo kama vile minara na Wi-Fi. Lazima huduma hizi za eneo ziwashwe na kupatikana kwenye kifaa chako ili programu izitumie. Huenda programu zikatumia hii kutambua ulipo kwa kukadiria."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"badilisha mipangilio yako ya sauti"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Inaruhusu programu kurekebisha mipangilio ya sauti kila mahali kama vile sauti na ni kipaza sauti kipi ambacho kinatumika kwa kutoa."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"kurekodi sauti"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Inaruhusu programu kurekodi sauti kwa kinasa sauti. Idhini hii inaruhusu programu kurekodi sauti wakati wowote bila ya uthibitisho wako."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"mawasiliano ya sim"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"tuma amri kwenye SIM"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Huruhusu programu kutuma amri kwa SIM. Hii ni hatari sana."</string>
<string name="permlab_camera" msgid="3616391919559751192">"Kupiga picha na kurekodi video"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Inaruhusu programu kupiga picha na video kwa kamera. Kibali hiki kinaruhusu programu kutumia kamera kwa wakati wowote bila uthibitisho wako."</string>
@@ -384,7 +384,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Inaruhusu programu kupata orodha ya akaunti zinazojulikana kwa simu. Hii inaweza kujumuisha akaunti zozote zilizoundwa na programu ambazo umesakinisha."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"kuona mitandao"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Inaruhusu programu kuona taarifa kuhusu miunganisho ya mtandao kama vile mitandao ipi iliyopo na imeunganishwa."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"ufikiaji kamili wa mtandao"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"pata ufikiaji kamili wa mtandao"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Inaruhusu programu kuunda soketi za mtandao na kutumia itifaki za mtandao maalum. Kivinajri na programu nyingine zilizotolewa zinamaanisha kutuma data kwenye mtandao, kwa hivyo kibali hiki hakihitajiki kutuma data kwenye mtandao."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"kubadilisha muunganisho wa mtandao"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Inaruhusu programu kubadilisha hali ya muunganisho wa mtandao."</string>
@@ -404,7 +404,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Inaruhusu programu kusanidi simu ya karibu ya Bluetooth, na kutambua na kuoanisha na vifaa vya mbali."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"unganisha na uukate muunaganisho kutoka kwenye WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Inaruhusu programu kuthibitisha ikiwa WiMAX imewezeshwa na taarifa kuhusu mitandao yoyote ya WiMAX ambayo imeunganishwa."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Badilisha hali ya WiMAX"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"badilisha hali ya WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Inaruhusu programu kuunganisha kompyuta kibao, na kukata kompyuta kibao kutoka mitandao ya WiMAX."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Huruhusu programu kuunganisha runinga kwenye na kuondoa runinga kutoka mitandao ya WiMAX."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Inaruhusu programu kuunganisha simu kwenye, na kukata simu kutoka mitandao ya WiMAX."</string>
@@ -487,7 +487,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Huruhusu programu kubadilisha vigezo vya urekebishaji vya skrini ya kugusa. Havipaswi kuhitajika kamwe kwa programu za kawaida."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"fikia vyeti vya DRM"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Huruhusu programu kwa utoaji na matumizi ya vyeti vya DRM. Havifahi kuhitajika kwa ajili ya programu za kawaida."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Pokea hali ya uhamisho wa Boriti ya Android"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"pokea hali ya usambazaji wa Boriti ya Android"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Huruhusu programu hii kupokea taarifa kuhusu uhamisho wa Boriti ya Android"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"ondoa vyeti vya DRM"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Huruhusu programu kuondoa vyeti vya DRM. Haipaswi kuhitajika kwa programu za kawaida kamwe."</string>
@@ -652,7 +652,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Msimbo wa PIN usio sahihi."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Ili kufungua, bofya Menyu kisha 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Nambari ya dharura"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Hakuna huduma"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Hakuna huduma"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"skrini imefungwa."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Bonyeza Menyu ili kufungua au kupiga simu ya dharura."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Bonyeza Menyu ili kufungua."</string>
@@ -684,15 +684,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Angalia Mwongozo wa Mtumiaji au wasiliana na Huduma ya Wateja."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM kadi imefungwa."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Inafungua SIM kadi..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Umekosea katika kuchora ruwaza yako ya kufungua mara <xliff:g id="NUMBER_0">%d</xliff:g>. \n\n Jaribu tena kwa sekunde <xliff:g id="NUMBER_1">%d</xliff:g>."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Umekosea mara <xliff:g id="NUMBER_0">%d</xliff:g> katika kuingiza nenosiri lako. \n\n Jaribu tena katika sekunde <xliff:g id="NUMBER_1">%d</xliff:g>."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Umekosea katika kuingiza PIN yako mara <xliff:g id="NUMBER_0">%d</xliff:g>. \n\n Jaribu tena katika sekunde <xliff:g id="NUMBER_1">%d</xliff:g>."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Umekosea katika kuweka mchoro wako wa kufungua mara <xliff:g id="NUMBER_0">%d</xliff:g>. Baada ya majaribio <xliff:g id="NUMBER_1">%d</xliff:g> bila kufaulu, utaulizwa kufungua kompyuta yako ndogo kwa kuingia kwa Google.\n\n Jaribu tena katika sekunde <xliff:g id="NUMBER_2">%d</xliff:g>."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Umekosea kuchora mchoro wa kufungua mara <xliff:g id="NUMBER_0">%d</xliff:g>. Ukikosea majaribio mengine <xliff:g id="NUMBER_1">%d</xliff:g>, utaombwa ufungue runinga yako ukitumia Google.\n\n Jaribu tena baada ya sekunde <xliff:g id="NUMBER_2">%d</xliff:g>."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Umekosea kuchora mchoro wako wa kufungua mara <xliff:g id="NUMBER_0">%d</xliff:g>. Baada ya majaribio <xliff:g id="NUMBER_1">%d</xliff:g> yasiyofaulu, utaulizwa kufungua simu kupitia kuingia Google.\n\n Jaribu tena katika sekunde <xliff:g id="NUMBER_2">%d</xliff:g>."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Umejaribu kufungua kompyuta ndogo kwa njia isiyo sahihi mara <xliff:g id="NUMBER_0">%d</xliff:g>. Baada ya zaidi ya <xliff:g id="NUMBER_1">%d</xliff:g> majaribio yasiyofanikiwa, kompyuta ndogo itawekwa upya kwa kiwanda chaguo-msingi na data yote ya mtumiaji itapotea."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Umekosea majaribio ya kufungua runinga mara <xliff:g id="NUMBER_0">%d</xliff:g>. Ukikosea majaribio <xliff:g id="NUMBER_1">%d</xliff:g> zaidi, runinga itarejeshwa katika hali iliyotoka nayo kiwandani na data yote ya watumiaji itafutwa."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Umejaribu kufungua simu kwa njia isiyo sahihi mara <xliff:g id="NUMBER_0">%d</xliff:g>. Baada ya majaribio <xliff:g id="NUMBER_1">%d</xliff:g> zaidi yasiyofanikiwa, simu itawekwa upya kwa kiwanda chaguo-msingi na data yote ya mtumiaji itapotea."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Umekosea katika kuchora ruwaza yako ya kufungua mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. \n\n Jaribu tena kwa sekunde <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Umekosea mara <xliff:g id="NUMBER_0">%1$d</xliff:g> katika kuingiza nenosiri lako. \n\n Jaribu tena katika sekunde <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Umekosea katika kuingiza PIN yako mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. \n\n Jaribu tena katika sekunde <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Umekosea katika kuweka mchoro wako wa kufungua mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. Baada ya majaribio <xliff:g id="NUMBER_1">%2$d</xliff:g> bila kufaulu, utaulizwa kufungua kompyuta yako ndogo kwa kuingia kwa Google.\n\n Jaribu tena katika sekunde <xliff:g id="NUMBER_2">%3$d</xliff:g>."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Umekosea kuchora mchoro wa kufungua mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. Ukikosea majaribio mengine <xliff:g id="NUMBER_1">%2$d</xliff:g>, utaombwa ufungue runinga yako ukitumia Google.\n\n Jaribu tena baada ya sekunde <xliff:g id="NUMBER_2">%3$d</xliff:g>."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Umekosea kuchora mchoro wako wa kufungua mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. Baada ya majaribio <xliff:g id="NUMBER_1">%2$d</xliff:g> yasiyofaulu, utaulizwa kufungua simu kupitia kuingia Google.\n\n Jaribu tena katika sekunde <xliff:g id="NUMBER_2">%3$d</xliff:g>."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Umejaribu kufungua kompyuta ndogo kwa njia isiyo sahihi mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. Baada ya zaidi ya <xliff:g id="NUMBER_1">%2$d</xliff:g> majaribio yasiyofanikiwa, kompyuta ndogo itawekwa upya kwa kiwanda chaguo-msingi na data yote ya mtumiaji itapotea."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Umekosea majaribio ya kufungua runinga mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. Ukikosea majaribio <xliff:g id="NUMBER_1">%2$d</xliff:g> zaidi, runinga itarejeshwa katika hali iliyotoka nayo kiwandani na data yote ya watumiaji itafutwa."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Umejaribu kufungua simu kwa njia isiyo sahihi mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. Baada ya majaribio <xliff:g id="NUMBER_1">%2$d</xliff:g> zaidi yasiyofanikiwa, simu itawekwa upya kwa kiwanda chaguo-msingi na data yote ya mtumiaji itapotea."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Umejaribu kufungua kompyuta ndogo kwa njia isiyo sahihi mara <xliff:g id="NUMBER">%d</xliff:g>. Kompyuta ndogo haitaweza kuwekwa upya kwa kiwanda chaguo-msingi."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Umekosea majaribio ya kufungua runinga mara <xliff:g id="NUMBER">%d</xliff:g>. Sasa runinga itarejeshwa katika mipangilio iliyotoka nayo kiwandani."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Umejaribu kufungua kompyuta ndogo kwa njia isiyo sahihi mara <xliff:g id="NUMBER">%d</xliff:g>. Kompyuta ndogo haitaweza kuwekwa upya kwa kiwanda chaguo-msingi."</string>
@@ -744,8 +744,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"neno"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"kiungo"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"mstari"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Jaribio la kiwanda limeshindikana"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Tendo la JARIBIO_LA KIWANDA linahimiliwa tu kwa furushi zilizosakinishwa katika /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Hakuna furushi lililopatikana ambalo linatoa tendo la JARIBIO_LA KIWANDA."</string>
@@ -1075,11 +1073,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Inaumbiza..."</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Haijaingizwa"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Hakuna shughuli zinazolingana zilizopatikana."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Fuatalia utoaji wa habari"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"sambaza data ya maudhui"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Inaruhusu programu kufuatilia utoaji wa habari kwa vifaa vingine vya nje."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Soma vipindi vya kusanikisha"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"soma vipindi vya kusakinisha"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Huruhusu programu kusoma vipindi vya kusanikisha. Hii huiruhusu kuona maelezo kuhusu usanikishaji wa programu unaoendelea."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Omba idhini ya kusakinisha vifurushi"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"omba ruhusa ya kusakinisha vifurushi"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Huruhusu programu kuomba idhini ya kusakinisha vifurushi."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Gusa mara mbili kwa udhibiti cha kuza"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Haikuweza kuongeza wijeti."</string>
@@ -1287,18 +1285,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Jina la mtumiaji au nenosiri batili."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Je, umesahau jina lako la mtumiaji au nenosiri?\nTembela "<b>"Bgoogle.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Inakagua akaunti…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Umeingiza nenosiri lako kwa makosa mara <xliff:g id="NUMBER_0">%d</xliff:g>. \n\n Jaribu tena baada ya sekunde <xliff:g id="NUMBER_1">%d</xliff:g>."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Umeingiza nenosiri lako kwa makosa mara <xliff:g id="NUMBER_0">%d</xliff:g>. \n\n Jaribu tena baada ya sekunde <xliff:g id="NUMBER_1">%d</xliff:g>."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Umechora ruwaza yako ya kufunga kwa makosa mara <xliff:g id="NUMBER_0">%d</xliff:g>. \n\n Jaribu tena baada ya sekunde <xliff:g id="NUMBER_1">%d</xliff:g>."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Umejaribu kufungua kompyuta ndogo kwa njia isiyo sahihi mara <xliff:g id="NUMBER_0">%d</xliff:g>. Baada ya majaribio <xliff:g id="NUMBER_1">%d</xliff:g> zaidi yasiyofaulu, kompyuta ndogo itarejeshwa katika mfumo chaguo-msingi ilivyotoka kiwandani data yote ya mtumiaji itapotea."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Umekosea majaribio ya kufungua runinga mara <xliff:g id="NUMBER_0">%d</xliff:g>. Ukikosea majaribio mengine <xliff:g id="NUMBER_1">%d</xliff:g>, runinga itarejeshwa katika hali iliyotoka nayo kiwandani na data yote ya watumiaji itafutwa."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Umejaribu kufungua simu kwa njia isiyo sahihi mara <xliff:g id="NUMBER_0">%d</xliff:g>. Baada ya majaribio <xliff:g id="NUMBER_1">%d</xliff:g> zaidi yasiyofaulu, simu itarejeshwa katika mfumo chaguo-msingi ilivyotoka kiwandani na data yote ya mtumiaji itapotea."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Umeingiza nenosiri lako kwa makosa mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. \n\n Jaribu tena baada ya sekunde <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Umeingiza nenosiri lako kwa makosa mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. \n\n Jaribu tena baada ya sekunde <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Umechora ruwaza yako ya kufunga kwa makosa mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. \n\n Jaribu tena baada ya sekunde <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Umejaribu kufungua kompyuta ndogo kwa njia isiyo sahihi mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. Baada ya majaribio <xliff:g id="NUMBER_1">%2$d</xliff:g> zaidi yasiyofaulu, kompyuta ndogo itarejeshwa katika mfumo chaguo-msingi ilivyotoka kiwandani data yote ya mtumiaji itapotea."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Umekosea majaribio ya kufungua runinga mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. Ukikosea majaribio mengine <xliff:g id="NUMBER_1">%2$d</xliff:g>, runinga itarejeshwa katika hali iliyotoka nayo kiwandani na data yote ya watumiaji itafutwa."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Umejaribu kufungua simu kwa njia isiyo sahihi mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. Baada ya majaribio <xliff:g id="NUMBER_1">%2$d</xliff:g> zaidi yasiyofaulu, simu itarejeshwa katika mfumo chaguo-msingi ilivyotoka kiwandani na data yote ya mtumiaji itapotea."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Umejaribu kufungua kompyuta ndogo kwa njia isiyo sahihi mara <xliff:g id="NUMBER">%d</xliff:g>. Sasa kompyuta ndogo itarejeshwa katika mfumo chaguo-msingi ilivyotoka kiwandani."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Umekosea majaribio ya kufungua runinga mara <xliff:g id="NUMBER">%d</xliff:g>. Sasa runinga itarejeshwa katika hali iliyotoka nayo kiwandani."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Umejaribu kufungua simu kwa njia isiyo sahihi mara <xliff:g id="NUMBER">%d</xliff:g>. Sasa simu itarejeshwa katika mfumo chaguo-msingi ilivyotoka kiwandani."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Umekosea katika kuweka mchoro wako wa kufungua mara <xliff:g id="NUMBER_0">%d</xliff:g>. Baada ya majaribio <xliff:g id="NUMBER_1">%d</xliff:g> bila kufaulu, utaombwa kufungua kompyuta yako ndogo kwa kutumia akaunti yako ya barua pepe.\n\n Jaribu tena baada ya sekunde <xliff:g id="NUMBER_2">%d</xliff:g>."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Umekosea kuchora mchoro wa kufungua mara <xliff:g id="NUMBER_0">%d</xliff:g>. Ukikosea majaribio mengine <xliff:g id="NUMBER_1">%d</xliff:g>, utaombwa ufungue runinga yako ukitumia akaunti ya barua pepe.\n\n Jaribu tena baada ya sekunde <xliff:g id="NUMBER_2">%d</xliff:g>."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Umekosea kuchora mchoro wako wa kufungua mara <xliff:g id="NUMBER_0">%d</xliff:g>. Baada ya majaribio <xliff:g id="NUMBER_1">%d</xliff:g> yasiyofaulu, utaombwa kufungua simu yako kwa kutumia akaunti ya barua pepe.\n\n Jaribu tena baada ya sekunde <xliff:g id="NUMBER_2">%d</xliff:g>."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Umekosea katika kuweka mchoro wako wa kufungua mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. Baada ya majaribio <xliff:g id="NUMBER_1">%2$d</xliff:g> bila kufaulu, utaombwa kufungua kompyuta yako ndogo kwa kutumia akaunti yako ya barua pepe.\n\n Jaribu tena baada ya sekunde <xliff:g id="NUMBER_2">%3$d</xliff:g>."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Umekosea kuchora mchoro wa kufungua mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. Ukikosea majaribio mengine <xliff:g id="NUMBER_1">%2$d</xliff:g>, utaombwa ufungue runinga yako ukitumia akaunti ya barua pepe.\n\n Jaribu tena baada ya sekunde <xliff:g id="NUMBER_2">%3$d</xliff:g>."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Umekosea kuchora mchoro wako wa kufungua mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. Baada ya majaribio <xliff:g id="NUMBER_1">%2$d</xliff:g> yasiyofaulu, utaombwa kufungua simu yako kwa kutumia akaunti ya barua pepe.\n\n Jaribu tena baada ya sekunde <xliff:g id="NUMBER_2">%3$d</xliff:g>."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Ondoa"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Ungependa kupandisha sauti zaidi ya kiwango kinachopendekezwa?\n\nKusikiliza kwa sauti ya juu kwa muda mrefu kunaweza kuharibu uwezo wako wa kusikia."</string>
@@ -1472,6 +1470,7 @@
<item quantity="one">Kwa saa 1 </item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Hadi <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Mpaka <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (kengele inayofuata)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Hadi utakapozima hili"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Hadi utakapozima Usinisumbue"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-ta-rIN-watch/strings.xml b/core/res/res/values-ta-rIN-watch/strings.xml
index 629ca27..737f6d0 100644
--- a/core/res/res/values-ta-rIN-watch/strings.xml
+++ b/core/res/res/values-ta-rIN-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"பயன்பாடு: <xliff:g id="NUMBER_0">%1$d</xliff:g> / <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"உணர்விகள்"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"தொடர்புகளை அணுகும்"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"இந்த வாட்சின் இருப்பிடத்தை அணுகும்"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"கேலெண்டரை அணுகும்"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"SMS செய்திகளை அனுப்பும் மற்றும் பார்க்கும்"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"உங்கள் வாட்சில் உள்ள படங்கள், மீடியா மற்றும் கோப்புகளை அணுகும்"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"ஆடியோவைப் பதிவுசெய்யும்"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"படங்களை எடுக்கும், வீடியோவைப் பதிவுசெய்யும்"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"மொபைல் அழைப்புகளைச் செய்யும், நிர்வகிக்கும்"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"உங்கள் உடலியக்கக் குறிகள் பற்றிய உணர்வித் தரவை அணுகும்"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"நிலைப் பட்டியில் இருக்கும்"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"உடல் உணர்விகளை (இதயத் துடிப்பு மானிட்டர்கள் போன்றவை) அணுகும்"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"துல்லியமான இருப்பிடத்தை அணுகும் (GPS மற்றும் நெட்வொர்க் அடிப்படையில்)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"தோராயமான இருப்பிடத்தை அணுகும் (நெட்வொர்க் அடிப்படையில்)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"கட்டளைகளை சிம்மிற்கு அனுப்பும்"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"முழுமையான நெட்வொர்க் அணுகலைக் கொண்டிருக்கும்"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"சுயவிவரத்தையும் சாதன உரிமையாளர்களையும் நிர்வகிக்கும்"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"WiMAX நிலையை மாற்றும்"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"Android பீம் பரிமாற்ற நிலையைப் பெறும்"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"மீடியா அவுட்புட்டை ரூட் செய்யும்"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"நிறுவல் அமர்வுகளைப் படிக்கும்"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"நிறுவல் தொகுப்புகளைக் கோரும்"</string>
</resources>
diff --git a/core/res/res/values-ta-rIN/strings.xml b/core/res/res/values-ta-rIN/strings.xml
index 569fe82..67b85583 100644
--- a/core/res/res/values-ta-rIN/strings.xml
+++ b/core/res/res/values-ta-rIN/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"கிரெடிட் கார்டு எண்கள் மற்றும் கடவுச்சொற்கள் போன்ற தனிப்பட்ட தகவலும் உள்ளடங்கும்."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"நிலைப் பட்டியை முடக்குதல் அல்லது மாற்றுதல்"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"நிலைப் பட்டியை முடக்க அல்லது முறைமையில் ஐகான்களைச் சேர்க்க மற்றும் அகற்ற பயன்பாட்டை அனுமதிக்கிறது."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"நிலைப் பட்டி"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"நிலைப் பட்டியில் இருக்கும்"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"நிலைப் பட்டியில் இருக்க பயன்பாட்டை அனுமதிக்கிறது."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"நிலைப் பட்டியை விரிவாக்குதல்/சுருக்குதல்"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"நிலைப் பட்டியை விரிவாக்க அல்லது சுருக்க, பயன்பாட்டை அனுமதிக்கிறது."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"WAP செய்திகளைப் பெற, செயற்படுத்தப் பயன்பாட்டை அனுமதிக்கிறது. உங்களுக்கு அனுப்பப்படும் செய்திகளை உங்களுக்குக் காட்டாமல் கண்காணிக்க அல்லது நீக்குவதற்கான திறன் இந்த அனுமதியில் உள்ளடங்கும்."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"இயங்கும் பயன்பாடுகளை மீட்டெடுத்தல்"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"நடப்பில் மற்றும் சமீபத்தில் இயங்கும் காரியங்களின் தகவலைப் பெற பயன்பாட்டை அனுமதிக்கிறது. சாதனத்தில் எந்தப் பயன்பாடுகள் பயன்படுத்தப்படுகின்றன என்பது குறித்த தகவலைக் கண்டறிய பயன்பாட்டை இது அனுமதிக்கலாம்."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"சுயவிவரத்தையும் சாதன உரிமையாளர்களையும் நிர்வகிக்கவும்"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"சுயவிவரத்தையும் சாதன உரிமையாளர்களையும் நிர்வகித்தல்"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"சுயவிவர உரிமையாளர்களையும் சாதன உரிமையாளரையும் அமைக்க, பயன்பாடுகளை அனுமதிக்கிறது."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"இயங்கும் பயன்பாடுகளை மறுவரிசைப்படுத்தல்"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"பின்புலத்திலும், முன்புலத்திலும் காரியங்களை நகர்த்த பயன்பாட்டை அனுமதிக்கிறது. உங்கள் உள்ளீடு இல்லாமலே பயன்பாடு இதைச் செய்யலாம்."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"உள்வரும் மற்றும் வெளிச்செல்லும் அழைப்புகள் குறித்த தகவல் உள்பட உங்கள் டேப்லெட்டின் அழைப்புப் பதிவைத் திருத்துவதற்குப் பயன்பாட்டை அனுமதிக்கிறது. உங்கள் அழைப்பின் பதிவை அழிக்க அல்லது திருத்த தீங்கு விளைவிக்கும் பயன்பாடுகள் இதைப் பயன்படுத்தலாம்."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"உள்வரும் மற்றும் வெளிச்செல்லும் அழைப்புகள் குறித்த தகவல் உள்ளிட்ட உங்கள் டிவியின் அழைப்பு பதிவைத் திருத்த, பயன்பாட்டை அனுமதிக்கிறது. உங்கள் அழைப்பு பதிவை அழிக்க அல்லது திருத்த தீங்கு விளைவிக்கும் பயன்பாடுகள் இதைப் பயன்படுத்தலாம்."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"உள்வரும் மற்றும் வெளிச்செல்லும் அழைப்புகள் குறித்த தகவல் உள்பட உங்கள் மொபைல் அழைப்புப் பதிவைத் திருத்துவதற்குப் பயன்பாட்டை அனுமதிக்கிறது. உங்கள் அழைப்பின் பதிவை அழிக்க அல்லது திருத்த தீங்கு விளைவிக்கும் பயன்பாடுகள் இதைப் பயன்படுத்தலாம்."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"உடல் உணர்விகள் (இதயத்துடிப்பு கண்காணித்தல் போன்றவை)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"உடல் உணர்விகளை (இதயத் துடிப்பு மானிட்டர்கள் போன்றவை) அணுகுதல்"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"உங்கள் இதயத்துடிப்பு விகிதம் போன்ற உங்கள் உடல்நிலையைக் கண்காணிக்கும் உணர்விகளில் இருந்து தரவை அணுக பயன்பாடுகளை அனுமதிக்கும்."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"கேலெண்டர் நிகழ்வுகளையும், ரகசிய தகவலையும் படித்தல்"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"நண்பர்கள் அல்லது சகப் பணியாளர்கள் ஆகியோரின் நிகழ்வுகளையும் சேர்த்து, உங்கள் டேப்லெட்டில் சேமிக்கப்பட்ட எல்லா கேலெண்டர் நிகழ்வுகளையும் படிக்க பயன்பாட்டை அனுமதிக்கிறது. உங்கள் கேலெண்டர் தரவின் ரகசியத்தன்மை அல்லது முக்கியத்துவத்தைப் பொருட்படுத்தாமல் அதனைப் பகிர அல்லது சேமிக்க பயன்பாட்டை இது அனுமதிக்கிறது."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"நண்பர்கள் அல்லது சகப் பணியாளர்கள் உள்பட உங்கள் மொபைலில் நீங்கள் திருத்தக்கூடிய நிகழ்வுகளைச் சேர்க்கவும், அகற்றவும், மேலும் மாற்றவும் பயன்பாட்டை அனுமதிக்கிறது. இது கேலெண்டர் உரிமையாளர்களிடமிருந்து வரும் செய்திகளை அனுப்பவும் அல்லது உரிமையாளரின் ஒப்புதல் இல்லாமல் நிகழ்வுகளைத் திருத்தவும் பயன்பாட்டை அனுமதிக்கலாம்."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"கூடுதல் இட வழங்குநரின் கட்டளைகளின் அணுகல்"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"கூடுதல் இட வழங்குநர் கட்டளைகளை அணுகப் பயன்பாட்டை அனுமதிக்கிறது. இது, GPS அல்லது பிற இருப்பிட மூலங்களின் செயல்பாட்டை இடைமறிக்க பயன்பாட்டை அனுமதிக்கலாம்."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"துல்லியமான இருப்பிடம் (GPS மற்றும் நெட்வொர்க் சார்ந்தது)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"துல்லியமான இருப்பிடத்தை அணுகுதல் (GPS மற்றும் நெட்வொர்க் அடிப்படையில்)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"க்ளோபல் பொசிஷனிங் சிஸ்டம் (GPS) அல்லது செல் கோபுரங்கள் மற்றும் வைஃபை போன்ற நெட்வொர்க் இருப்பிடச் சேவைகளைப் பயன்படுத்தி உங்கள் துல்லியமான இருப்பிடத்தைப் பெறப் பயன்பாட்டை அனுமதிக்கிறது. இந்த இருப்பிடச் சேவைகள் கண்டிப்பாக இயக்கப்பட்டு, பயன்பாடு பயன்படுத்துவதற்கு அவை உங்கள் சாதனத்தில் கிடைக்க வேண்டும். நீங்கள் எங்கிருக்கிறீர்கள் என்பதைத் தீர்மானிக்கப் பயன்பாடுகள் இதைப் பயன்படுத்தலாம், மேலும் இது கூடுதல் பேட்டரி சக்தியை உபயோகிக்கலாம்."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"தோராயமான இருப்பிடம் (நெட்வொர்க் சார்ந்தது)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"தோராயமான இருப்பிடத்தை அணுகுதல் (நெட்வொர்க் அடிப்படையில்)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"உங்கள் தோராயமான இருப்பிடத்தைப் பெற பயன்பாட்டை அனுமதிக்கிறது. செல் கோபுரங்கள் மற்றும் வைஃபை போன்ற நெட்வொர்க் இருப்பிடச் சேவைகளைப் பயன்படுத்தி இருப்பிடச் சேவைகள் மூலம் இந்த இருப்பிடம் பெறப்படுகிறது. இந்த இருப்பிடச் சேவைகள் கண்டிப்பாக இயக்கப்பட்டு, பயன்பாடு பயன்படுத்துவதற்கு அவை உங்கள் சாதனத்தில் கிடைக்க வேண்டும். நீங்கள் எங்கிருக்கிறீர்கள் என்பதைத் தோராயமாகத் தீர்மானிக்கப் பயன்பாடுகள் இதைப் பயன்படுத்தலாம்."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"எனது ஆடியோ அமைப்புகளை மாற்றுதல்"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"ஒலியளவு மற்றும் வெளியீட்டிற்கு ஸ்பீக்கர்கள் பயன்படுத்தப்படுவது போன்ற ஒட்டுமொத்த ஆடியோ அமைப்புகளைக் கட்டுப்படுத்தப் பயன்பாட்டை அனுமதிக்கிறது."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"ஆடியோவைப் பதிவுசெய்தல்"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"மைக்ரோஃபோன் மூலம் ஆடியோவைப் பதிவுசெய்ய பயன்பாட்டை அனுமதிக்கிறது. உங்கள் உறுதிப்படுத்தல் இல்லாமல் எந்நேரத்திலும் ஆடியோவைப் பதிவுசெய்ய இந்த அனுமதி பயன்பாட்டை அனுமதிக்கிறது."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"சிம் தகவல்தொடர்பு"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"கட்டளைகளை சிம்மிற்கு அனுப்புதல்"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"சிம் க்குக் கட்டளைகளை அனுப்ப பயன்பாட்டை அனுமதிக்கிறது. இது மிகவும் ஆபத்தானதாகும்."</string>
<string name="permlab_camera" msgid="3616391919559751192">"படங்கள் மற்றும் வீடியோக்களை எடுத்தல்"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"கேமரா மூலமாகப் படங்களையும், வீடியோக்களையும் எடுக்க பயன்பாட்டை அனுமதிக்கிறது. உங்கள் உறுதிப்படுத்தல் இன்றி கேமராவை எந்நேரத்திலும் பயன்படுத்தப் பயன்பாட்டை இது அனுமதிக்கிறது."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"மொபைல் மூலம் அறியப்பட்ட கணக்குகளின் பட்டியலைப் பெற பயன்பாட்டை அனுமதிக்கிறது. நீங்கள் நிறுவிய பயன்பாடுகள் மூலம் உருவாக்கப்பட்ட எல்லா கணக்குகளும் இதில் உள்ளடங்கலாம்."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"நெட்வொர்க் இணைப்புகளைக் காட்டு"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"தற்போது இருக்கும் நெட்வொர்க்குகள் எவை மற்றும் இணைக்கப்பட்டுள்ளவை எவை போன்ற நெட்வொர்க் இணைப்புகள் குறித்த தகவலைப் பார்க்கப் பயன்பாட்டை அனுமதிக்கிறது."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"முழுமையான நெட்வொர்க் அணுகல்"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"முழுமையான நெட்வொர்க் அணுகலைக் கொண்டிருக்கும்"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"நெட்வொர்க் சாக்கெட்டுகளை உருவாக்கவும் மற்றும் தனிப்பயன் நெட்வொர்க் நெறிமுறைகளைப் பயன்படுத்தவும் பயன்பாட்டை அனுமதிக்கிறது. இணையத்தில் தரவை அனுப்ப உலாவியும், பிற பயன்பாடுகளும் இருப்பதால், இணையத்திற்குத் தரவை அனுப்ப இந்த அனுமதி தேவையில்லை."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"பிணைய இணைப்புத்தன்மையை மாற்றுதல்"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"நெட்வொர்க் இணைப்பின் நிலையை மாற்ற, பயன்பாட்டை அனுமதிக்கிறது."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"மொபைலில் அக புளூடூத் ஐ உள்ளமைக்க, தொலைநிலை சாதனங்களைக் கண்டறிந்து இணைக்க, பயன்பாட்டை அனுமதிக்கிறது."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"WiMAX உடன் இணை மற்றும் அதனுடனான தொடர்பைத் துண்டி"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"WiMAX இணைக்கப்பட்டுள்ளதா என்பதையும், இணைக்கப்பட்டுள்ள WiMAX நெட்வொர்க்குகள் ஏதேனும் குறித்த தகவலைத் தீர்மானிக்கவும் பயன்பாட்டை அனுமதிக்கிறது."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX நிலையை மாற்றவும்"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"WiMAX நிலையை மாற்றுதல்"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"WiMAX நெட்வொர்க்குகளில் டேப்லெட்டை இணைக்கவும், அவற்றிலிருந்து துண்டிக்கவும் பயன்பாட்டை அனுமதிக்கிறது."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"டிவியுடன் இணைக்க மற்றும் WiMAX நெட்வொர்க்குகளிலிருந்து டிவியைத் துண்டிக்க, பயன்பாட்டை அனுமதிக்கிறது."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"WiMAX நெட்வொர்க்குகளில் மொபைலை இணைக்கவும், அவற்றிலிருந்து துண்டிக்கவும் பயன்பாட்டை அனுமதிக்கிறது."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"தொடுதல் திரையின் அளவு திருத்த அளபுருக்களை மாற்ற, பயன்பாட்டை அனுமதிக்கிறது. சாதாரண பயன்பாடுகளுக்குத் தேவைப்படாது."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"DRM சான்றிதழ்களை அணுகுதல்"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"DRM சான்றிதழ்களை வழங்க மற்றும் பயன்படுத்த, பயன்பாட்டை அனுமதிக்கிறது. சாதாரண பயன்பாடுகளுக்கு எப்போதும் தேவைப்படாது."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Android பீம் பரிமாற்ற நிலையைப் பெறுக"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"Android பீம் பரிமாற்ற நிலையைப் பெறுதல்"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"நடப்பு Android பீம் பரிமாற்றங்கள் குறித்த தகவலைப் பெற, பயன்பாட்டை அனுமதிக்கிறது"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"DRM சான்றிதழ்களை அகற்று"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"DRM சான்றிதழ்களை அகற்ற, பயன்பாட்டை அனுமதிக்கிறது. சாதாரண பயன்பாடுகளுக்கு எப்போதுமே தேவைப்படாது."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"தவறான பின் குறியீடு."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"தடைநீக்க, மெனுவை அழுத்தி பின்பு 0 ஐ அழுத்தவும்."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"அவசர எண்"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"சேவை இல்லை."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"சேவை இல்லை"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"திரை பூட்டப்பட்டுள்ளது."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"தடைநீக்க மெனுவை அழுத்தவும் அல்லது அவசர அழைப்பை மேற்கொள்ளவும்."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"திறக்க, மெனுவை அழுத்தவும்."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"பயனர் கையேட்டைப் பார்க்கவும் அல்லது வாடிக்கையாளர் சேவையைத் தொடர்புகொள்ளவும்."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"சிம் கார்டு பூட்டப்பட்டுள்ளது."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"சிம் கார்டைத் திறக்கிறது..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"திறப்பதற்கான வடிவத்தை <xliff:g id="NUMBER_0">%d</xliff:g> முறை தவறாக வரைந்துள்ளீர்கள். \n\n<xliff:g id="NUMBER_1">%d</xliff:g> வினாடிகள் கழித்து முயற்சிக்கவும்."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"உங்கள் கடவுச்சொல்லை <xliff:g id="NUMBER_0">%d</xliff:g> முறை தவறாக உள்ளிட்டீர்கள். \n\n<xliff:g id="NUMBER_1">%d</xliff:g> வினாடிகள் கழித்து முயற்சிக்கவும்."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"உங்கள் பின்னை <xliff:g id="NUMBER_0">%d</xliff:g> முறை தவறாக உள்ளிட்டீர்கள். \n\n<xliff:g id="NUMBER_1">%d</xliff:g> வினாடிகள் கழித்து முயற்சிக்கவும்."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"திறப்பதற்கான வடிவத்தை <xliff:g id="NUMBER_0">%d</xliff:g> முறை தவறாக வரைந்துள்ளீர்கள். இன்னும் <xliff:g id="NUMBER_1">%d</xliff:g> முறை தவறாக வரைந்தால், உங்கள் Google உள்நுழைவைப் பயன்படுத்தி டேப்லெட்டைத் திறக்குமாறு கேட்கப்படுவீர்கள். \n\n <xliff:g id="NUMBER_2">%d</xliff:g> வினாடிகள் கழித்து முயற்சிக்கவும்."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"திறப்பதற்கான வடிவத்தை <xliff:g id="NUMBER_0">%d</xliff:g> முறை தவறாக வரைந்துள்ளீர்கள். இன்னும் <xliff:g id="NUMBER_1">%d</xliff:g> முறை தவறாக வரைந்தால், Google உள்நுழைவைப் பயன்படுத்தி டிவியைத் திறக்கும்படி கேட்கப்படுவீர்கள்.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> வினாடிகள் கழித்து முயலவும்."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"திறப்பதற்கான வடிவத்தை <xliff:g id="NUMBER_0">%d</xliff:g> முறை தவறாக வரைந்துள்ளீர்கள். இன்னும் <xliff:g id="NUMBER_1">%d</xliff:g> முறை தவறாக வரைந்தால், உங்கள் Google உள்நுழைவைப் பயன்படுத்தி மொபைலைத் திறக்குமாறு கேட்கப்படுவீர்கள். \n\n <xliff:g id="NUMBER_2">%d</xliff:g> வினாடிகள் கழித்து முயற்சிக்கவும்."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"டேப்லெட்டைத் தடைநீக்க <xliff:g id="NUMBER_0">%d</xliff:g> முறை தவறாக முயற்சித்துள்ளீர்கள். இன்னும் <xliff:g id="NUMBER_1">%d</xliff:g> தோல்வி முயற்சிகளுக்குப் பிறகு, டேப்லெட்டானது ஆரம்ப இயல்புநிலைக்கு மீட்டமைக்கப்பட்டு, எல்லா பயனர் தரவும் இழக்கப்படும்."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"டிவியை <xliff:g id="NUMBER_0">%d</xliff:g> முறை தவறாகத் திறக்க முயற்சித்துள்ளீர்கள். இன்னும் <xliff:g id="NUMBER_1">%d</xliff:g> முறை தவறாக முயற்சித்தால், டிவி ஆரம்பநிலைக்கு மீட்டமைக்கப்படுவதுடன், எல்லா பயனர் தரவும் அழிக்கப்படும்."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"தொலைபேசியைத் தடைநீக்க <xliff:g id="NUMBER_0">%d</xliff:g> முறை தவறாக முயற்சித்துள்ளீர்கள். இன்னும் <xliff:g id="NUMBER_1">%d</xliff:g> தோல்வி முயற்சிகளுக்குப் பிறகு, தொலைபேசியானது ஆரம்ப இயல்புநிலைக்கு மீட்டமைக்கப்பட்டு, எல்லா பயனர் தரவும் இழக்கப்படும்."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"திறப்பதற்கான வடிவத்தை <xliff:g id="NUMBER_0">%1$d</xliff:g> முறை தவறாக வரைந்துள்ளீர்கள். \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> வினாடிகள் கழித்து முயற்சிக்கவும்."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"உங்கள் கடவுச்சொல்லை <xliff:g id="NUMBER_0">%1$d</xliff:g> முறை தவறாக உள்ளிட்டீர்கள். \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> வினாடிகள் கழித்து முயற்சிக்கவும்."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"உங்கள் பின்னை <xliff:g id="NUMBER_0">%1$d</xliff:g> முறை தவறாக உள்ளிட்டீர்கள். \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> வினாடிகள் கழித்து முயற்சிக்கவும்."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"திறப்பதற்கான வடிவத்தை <xliff:g id="NUMBER_0">%1$d</xliff:g> முறை தவறாக வரைந்துள்ளீர்கள். இன்னும் <xliff:g id="NUMBER_1">%2$d</xliff:g> முறை தவறாக வரைந்தால், உங்கள் Google உள்நுழைவைப் பயன்படுத்தி டேப்லெட்டைத் திறக்குமாறு கேட்கப்படுவீர்கள். \n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> வினாடிகள் கழித்து முயற்சிக்கவும்."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"திறப்பதற்கான வடிவத்தை <xliff:g id="NUMBER_0">%1$d</xliff:g> முறை தவறாக வரைந்துள்ளீர்கள். இன்னும் <xliff:g id="NUMBER_1">%2$d</xliff:g> முறை தவறாக வரைந்தால், Google உள்நுழைவைப் பயன்படுத்தி டிவியைத் திறக்கும்படி கேட்கப்படுவீர்கள்.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> வினாடிகள் கழித்து முயலவும்."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"திறப்பதற்கான வடிவத்தை <xliff:g id="NUMBER_0">%1$d</xliff:g> முறை தவறாக வரைந்துள்ளீர்கள். இன்னும் <xliff:g id="NUMBER_1">%2$d</xliff:g> முறை தவறாக வரைந்தால், உங்கள் Google உள்நுழைவைப் பயன்படுத்தி மொபைலைத் திறக்குமாறு கேட்கப்படுவீர்கள். \n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> வினாடிகள் கழித்து முயற்சிக்கவும்."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"டேப்லெட்டைத் தடைநீக்க <xliff:g id="NUMBER_0">%1$d</xliff:g> முறை தவறாக முயற்சித்துள்ளீர்கள். இன்னும் <xliff:g id="NUMBER_1">%2$d</xliff:g> தோல்வி முயற்சிகளுக்குப் பிறகு, டேப்லெட்டானது ஆரம்ப இயல்புநிலைக்கு மீட்டமைக்கப்பட்டு, எல்லா பயனர் தரவும் இழக்கப்படும்."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"டிவியை <xliff:g id="NUMBER_0">%1$d</xliff:g> முறை தவறாகத் திறக்க முயற்சித்துள்ளீர்கள். இன்னும் <xliff:g id="NUMBER_1">%2$d</xliff:g> முறை தவறாக முயற்சித்தால், டிவி ஆரம்பநிலைக்கு மீட்டமைக்கப்படுவதுடன், எல்லா பயனர் தரவும் அழிக்கப்படும்."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"தொலைபேசியைத் தடைநீக்க <xliff:g id="NUMBER_0">%1$d</xliff:g> முறை தவறாக முயற்சித்துள்ளீர்கள். இன்னும் <xliff:g id="NUMBER_1">%2$d</xliff:g> தோல்வி முயற்சிகளுக்குப் பிறகு, தொலைபேசியானது ஆரம்ப இயல்புநிலைக்கு மீட்டமைக்கப்பட்டு, எல்லா பயனர் தரவும் இழக்கப்படும்."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"நீங்கள் டேப்லெட்டைத் தடைநீக்க <xliff:g id="NUMBER">%d</xliff:g> முறை தவறாக முயற்சித்துள்ளீர்கள். டேப்லெட் இப்போது ஆரம்ப இயல்புநிலைக்கு மீட்டமைக்கப்படும்."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"டிவியை <xliff:g id="NUMBER">%d</xliff:g> முறை தவறாகத் திறக்க முயற்சித்துள்ளீர்கள். இப்போது டிவி ஆரம்பநிலைக்கு மீட்டமைக்கப்படும்."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"நீங்கள் தொலைபேசியைத் தடைநீக்க <xliff:g id="NUMBER">%d</xliff:g> முறை தவறாக முயற்சித்துள்ளீர்கள். தொலைபேசி இப்போது ஆரம்ப இயல்புநிலைக்கு மீட்டமைக்கப்படும்."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"சொல்"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"இணைப்பு"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"வரி"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"ஆரம்பச் சோதனையில் தோல்வி"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"/system/app இல் நிறுவப்பட்ட தொகுப்புகளுக்கு மட்டுமே FACTORY_TEST செயல் ஆதரிக்கப்படும்."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"FACTORY_TEST செயலை வழங்கும் எந்தத் தொகுப்பும் கண்டறியப்படவில்லை."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"மீட்டமைக்கிறது…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"செருகப்படவில்லை"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"பொருந்தும் செயல்பாடுகள் கண்டறியப்படவில்லை."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"மீடியா அவுட்புட்டை வழிசெலுத்துதல்"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"மீடியா அவுட்புட்டை ரூட் செய்தல்"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"மீடியாவைப் பிற வெளிப்புறச் சாதனங்களுக்கு வெளியீடாக வழிகாட்ட பயன்பாட்டை அனுமதிக்கிறது."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"நிறுவல் அமர்வுகளைப் படிக்கலாம்"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"நிறுவல் அமர்வுகளைப் படித்தல்"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"நிறுவல் அமர்வுகளைப் படிக்க, பயன்பாட்டை அனுமதிக்கிறது. இது செயல்படும் தொகுப்பு நிறுவல்களைப் பற்றிய விவரங்களைப் பார்க்க அனுமதிக்கிறது."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"தொகுப்புகளின் நிறுவலைக் கோருதல்"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"நிறுவல் தொகுப்புகளைக் கோருதல்"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"தொகுப்புகளின் நிறுவலைக் கோர, பயன்பாட்டை அனுமதிக்கும்."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"அளவை மாற்றும் கட்டுப்பாடுகளுக்கு இருமுறை தொடவும்"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"விட்ஜெட்டைச் சேர்க்க முடியவில்லை."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"தவறான பயனர்பெயர் அல்லது கடவுச்சொல்."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"உங்கள் பயனர்பெயர் அல்லது கடவுச்சொல்லை மறந்துவிட்டீர்களா?\n"<b>"google.com/accounts/recovery"</b>" ஐப் பார்வையிடவும்."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"கணக்கைச் சரிபார்க்கிறது…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"உங்கள் பின்னை <xliff:g id="NUMBER_0">%d</xliff:g> முறை தவறாக உள்ளிட்டீர்கள். \n\n<xliff:g id="NUMBER_1">%d</xliff:g> வினாடிகள் கழித்து முயற்சிக்கவும்."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"உங்கள் கடவுச்சொல்லை <xliff:g id="NUMBER_0">%d</xliff:g> முறை தவறாக உள்ளிட்டீர்கள். \n\n<xliff:g id="NUMBER_1">%d</xliff:g> வினாடிகள் கழித்து முயற்சிக்கவும்."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"திறப்பதற்கான வடிவத்தை <xliff:g id="NUMBER_0">%d</xliff:g> முறை தவறாக வரைந்துள்ளீர்கள். \n\n<xliff:g id="NUMBER_1">%d</xliff:g> வினாடிகளில் மீண்டும் முயற்சிக்கவும்."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"டேப்லெட்டைத் திறக்க <xliff:g id="NUMBER_0">%d</xliff:g> முறை தவறாக முயற்சித்துள்ளீர்கள். இன்னும் <xliff:g id="NUMBER_1">%d</xliff:g> தோல்வி முயற்சிகளுக்குப் பிறகு, டேப்லெட்டானது ஆரம்பநிலைக்கு மீட்டமைக்கப்பட்டு, எல்லா பயனர் தரவையும் இழப்பீர்கள்."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"டிவியை <xliff:g id="NUMBER_0">%d</xliff:g> முறை தவறாகத் திறக்க முயற்சித்துள்ளீர்கள். இன்னும் <xliff:g id="NUMBER_1">%d</xliff:g> முறை தவறாக முயற்சித்தால், டிவி ஆரம்பநிலைக்கு மீட்டமைக்கப்படுவதுடன், எல்லா பயனர் தரவும் அழிக்கப்படும்."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"மொபைலைத் திறக்க <xliff:g id="NUMBER_0">%d</xliff:g> முறை தவறாக முயற்சித்துள்ளீர்கள். இன்னும் <xliff:g id="NUMBER_1">%d</xliff:g> தோல்வி முயற்சிகளுக்குப் பிறகு,மொபைலானது ஆரம்பநிலைக்கு மீட்டமைக்கப்பட்டு, எல்லா பயனர் தரவையும் இழப்பீர்கள்."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"உங்கள் பின்னை <xliff:g id="NUMBER_0">%1$d</xliff:g> முறை தவறாக உள்ளிட்டீர்கள். \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> வினாடிகள் கழித்து முயற்சிக்கவும்."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"உங்கள் கடவுச்சொல்லை <xliff:g id="NUMBER_0">%1$d</xliff:g> முறை தவறாக உள்ளிட்டீர்கள். \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> வினாடிகள் கழித்து முயற்சிக்கவும்."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"திறப்பதற்கான வடிவத்தை <xliff:g id="NUMBER_0">%1$d</xliff:g> முறை தவறாக வரைந்துள்ளீர்கள். \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> வினாடிகளில் மீண்டும் முயற்சிக்கவும்."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"டேப்லெட்டைத் திறக்க <xliff:g id="NUMBER_0">%1$d</xliff:g> முறை தவறாக முயற்சித்துள்ளீர்கள். இன்னும் <xliff:g id="NUMBER_1">%2$d</xliff:g> தோல்வி முயற்சிகளுக்குப் பிறகு, டேப்லெட்டானது ஆரம்பநிலைக்கு மீட்டமைக்கப்பட்டு, எல்லா பயனர் தரவையும் இழப்பீர்கள்."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"டிவியை <xliff:g id="NUMBER_0">%1$d</xliff:g> முறை தவறாகத் திறக்க முயற்சித்துள்ளீர்கள். இன்னும் <xliff:g id="NUMBER_1">%2$d</xliff:g> முறை தவறாக முயற்சித்தால், டிவி ஆரம்பநிலைக்கு மீட்டமைக்கப்படுவதுடன், எல்லா பயனர் தரவும் அழிக்கப்படும்."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"மொபைலைத் திறக்க <xliff:g id="NUMBER_0">%1$d</xliff:g> முறை தவறாக முயற்சித்துள்ளீர்கள். இன்னும் <xliff:g id="NUMBER_1">%2$d</xliff:g> தோல்வி முயற்சிகளுக்குப் பிறகு,மொபைலானது ஆரம்பநிலைக்கு மீட்டமைக்கப்பட்டு, எல்லா பயனர் தரவையும் இழப்பீர்கள்."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"டேப்லெட்டைத் திறக்க <xliff:g id="NUMBER">%d</xliff:g> முறை தவறாக முயற்சித்துள்ளீர்கள். டேப்லெட் இப்போது ஆரம்பநிலைக்கு மீட்டமைக்கப்படும்."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"டிவியை <xliff:g id="NUMBER">%d</xliff:g> முறை தவறாகத் திறக்க முயற்சித்துள்ளீர்கள். இப்போது டிவி ஆரம்பநிலைக்கு மீட்டமைக்கப்படும்."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"மொபைலைத் திறக்க <xliff:g id="NUMBER">%d</xliff:g> முறை தவறாக முயற்சித்துள்ளீர்கள். மொபைல் இப்போது ஆரம்பநிலைக்கு மீட்டமைக்கப்படும்."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"திறப்பதற்கான வடிவத்தை <xliff:g id="NUMBER_0">%d</xliff:g> முறை தவறாக வரைந்துள்ளீர்கள். மேலும் <xliff:g id="NUMBER_1">%d</xliff:g> தோல்வி முயற்சிகளுக்குப் பிறகு, மின்னஞ்சல் கணக்கைப் பயன்படுத்தி உங்கள் டேப்லெட்டைத் திறக்க கேட்கப்படுவீர்கள்.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> வினாடிகளில் மீண்டும் முயற்சிக்கவும்."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"திறப்பதற்கான வடிவத்தை <xliff:g id="NUMBER_0">%d</xliff:g> முறை தவறாக வரைந்துள்ளீர்கள். இன்னும் <xliff:g id="NUMBER_1">%d</xliff:g> முறை தவறாக வரைந்தால், மின்னஞ்சல் கணக்கைப் பயன்படுத்தி டிவியைத் திறக்கும்படி கேட்கப்படுவீர்கள்.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> வினாடிகள் கழித்து முயலவும்."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"திறப்பதற்கான வடிவத்தை <xliff:g id="NUMBER_0">%d</xliff:g> முறை தவறாக வரைந்துள்ளீர்கள். மேலும் <xliff:g id="NUMBER_1">%d</xliff:g> தோல்வி முயற்சிகளுக்குப் பிறகு, மின்னஞ்சல் கணக்கைப் பயன்படுத்தி உங்கள் மொபைலைத் திறக்கக் கேட்கப்படுவீர்கள்.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> வினாடிகள் கழித்து முயற்சிக்கவும்."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"திறப்பதற்கான வடிவத்தை <xliff:g id="NUMBER_0">%1$d</xliff:g> முறை தவறாக வரைந்துள்ளீர்கள். மேலும் <xliff:g id="NUMBER_1">%2$d</xliff:g> தோல்வி முயற்சிகளுக்குப் பிறகு, மின்னஞ்சல் கணக்கைப் பயன்படுத்தி உங்கள் டேப்லெட்டைத் திறக்க கேட்கப்படுவீர்கள்.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> வினாடிகளில் மீண்டும் முயற்சிக்கவும்."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"திறப்பதற்கான வடிவத்தை <xliff:g id="NUMBER_0">%1$d</xliff:g> முறை தவறாக வரைந்துள்ளீர்கள். இன்னும் <xliff:g id="NUMBER_1">%2$d</xliff:g> முறை தவறாக வரைந்தால், மின்னஞ்சல் கணக்கைப் பயன்படுத்தி டிவியைத் திறக்கும்படி கேட்கப்படுவீர்கள்.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> வினாடிகள் கழித்து முயலவும்."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"திறப்பதற்கான வடிவத்தை <xliff:g id="NUMBER_0">%1$d</xliff:g> முறை தவறாக வரைந்துள்ளீர்கள். மேலும் <xliff:g id="NUMBER_1">%2$d</xliff:g> தோல்வி முயற்சிகளுக்குப் பிறகு, மின்னஞ்சல் கணக்கைப் பயன்படுத்தி உங்கள் மொபைலைத் திறக்கக் கேட்கப்படுவீர்கள்.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> வினாடிகள் கழித்து முயற்சிக்கவும்."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"அகற்று"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"பரிந்துரைத்த அளவை விட ஒலியை அதிகரிக்கவா?\n\nநீண்ட நேரத்திற்கு அதிகளவில் ஒலி கேட்பது கேட்கும் திறனைப் பாதிக்கலாம்."</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">1 மணிநேரத்திற்கு</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> வரை"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> மணி (அடுத்த அலாரம்) வரை"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"இதை முடக்கும்வரை"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"தொந்தரவு செய்ய வேண்டாம் என்பதை முடக்கும் வரை"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-te-rIN-watch/strings.xml b/core/res/res/values-te-rIN-watch/strings.xml
index f642807..f729eaa 100644
--- a/core/res/res/values-te-rIN-watch/strings.xml
+++ b/core/res/res/values-te-rIN-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_1">%2$d</xliff:g>లో <xliff:g id="NUMBER_0">%1$d</xliff:g>వ అనువర్తనం."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"సెన్సార్లు"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"మీ పరిచయాలను ప్రాప్యత చేస్తుంది"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"ఈ గడియారం స్థానాన్ని ప్రాప్యత చేస్తుంది"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"మీ క్యాలెండర్ను ప్రాప్యత చేస్తుంది"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"SMS సందేశాలను పంపుతుంది మరియు చూస్తుంది"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"మీ గడియారంలోని ఫోటోలు, మీడియా మరియు ఫైల్లను ప్రాప్యత చేస్తుంది"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"ఆడియోను రికార్డ్ చేస్తుంది"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"చిత్రాలను తీస్తుంది మరియు వీడియోను రికార్డ్ చేస్తుంది"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"ఫోన్ కాల్లు చేస్తుంది మరియు నిర్వహిస్తుంది"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"మీ అత్యంత కీలకమైన గుర్తుల గురించి సెన్సార్ డేటాని ప్రాప్యత చేస్తుంది"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"స్థితి పట్టీగా ఉంటుంది"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"శరీర సెన్సార్లను (హృదయ స్పందన రేటు మానిటర్ల వంటివి) ప్రాప్యత చేస్తుంది"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"ఖచ్చితమైన స్థానాన్ని (GPS మరియు నెట్వర్క్-ఆధారితం) ప్రాప్యత చేస్తుంది"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"సమీప స్థానాన్ని (నెట్వర్క్-ఆధారితం) ప్రాప్యత చేస్తుంది"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"SIMకి ఆదేశాలు పంపుతుంది"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"పూర్తి నెట్వర్క్ ప్రాప్యతను కలిగి ఉంటుంది"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"ప్రొఫైల్ మరియు పరికరం యజమానులను నిర్వహిస్తుంది"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"WiMAX స్థితిని మారుస్తుంది"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"Android Beam బదిలీ స్థితిని స్వీకరిస్తుంది"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"మీడియా అవుట్పుట్ మార్గం నిర్దేశిస్తుంది"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"ఇన్స్టాల్ సెషన్లను చదువుతుంది"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"ఇన్స్టాల్ ప్యాకేజీలను అభ్యర్థిస్తుంది"</string>
</resources>
diff --git a/core/res/res/values-te-rIN/strings.xml b/core/res/res/values-te-rIN/strings.xml
index 77588b3..97789e1 100644
--- a/core/res/res/values-te-rIN/strings.xml
+++ b/core/res/res/values-te-rIN/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"క్రెడిట్ కార్డు నంబర్లు మరియు పాస్వర్డ్ల వంటి వ్యక్తిగత డేటాను కలిగి ఉంటుంది."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"స్థితి బార్ను నిలిపివేయడం లేదా సవరించడం"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"స్థితి బార్ను నిలిపివేయడానికి లేదా సిస్టమ్ చిహ్నాలను జోడించడానికి మరియు తీసివేయడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"స్థితి పట్టీ"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"స్థితి పట్టీగా ఉండటం"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"స్థితి బార్ ఉండేలా చేయడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"స్థితి పట్టీని విస్తరింపజేయడం/కుదించడం"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"స్థితి బార్ను విస్తరింపజేయడానికి లేదా కుదించడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"WAP సందేశాలను స్వీకరించడానికి మరియు ప్రాసెస్ చేయడానికి అనువర్తనాన్ని అనుమతిస్తుంది. ఈ అనుమతి మీకు పంపబడిన సందేశాలను మీకు చూపకుండానే పర్యవేక్షించగల లేదా తొలగించగల సామర్థ్యాన్ని కలిగి ఉంటుంది."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"అమలవుతున్న అనువర్తనాలను పునరుద్ధరించడం"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"ప్రస్తుతం మరియు ఇటీవల అమలవుతున్న విధుల గురించి వివరణాత్మక సమాచారాన్ని తిరిగి పొందడానికి అనువర్తనాన్ని అనుమతిస్తుంది. ఇది పరికరంలో ఉపయోగించబడిన అనువర్తనాల గురించి సమాచారాన్ని కనుగొనడానికి అనువర్తనాన్ని అనుమతించవచ్చు."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"ప్రొఫైల్ మరియు పరికరం యజమానులను నిర్వహించడానికి అనుమతి"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"ప్రొఫైల్ మరియు పరికర యజమానులను నిర్వహించడం"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"ప్రొఫైల్ యజమానులను మరియు పరికరం యజమానిని సెట్ చేయడానికి అనువర్తనాలను అనుమతిస్తుంది."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"అమలవుతున్న అనువర్తనాలను మళ్లీ క్రమం చేయడం"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"విధులను ముందుకు మరియు నేపథ్యానికి తరలించడానికి అనువర్తనాన్ని అనుమతిస్తుంది. అనువర్తనం మీ ప్రమేయం లేకుండానే దీన్ని చేయవచ్చు."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"ఇన్కమింగ్ మరియు అవుట్గోయింగ్ కాల్ల గురించిన డేటాతో సహా మీ టాబ్లెట్ యొక్క కాల్ లాగ్ను సవరించడానికి అనువర్తనాన్ని అనుమతిస్తుంది. హానికరమైన అనువర్తనాలు మీ కాల్ లాగ్ను ఎరేజ్ చేయడానికి లేదా సవరించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"ఇన్కమింగ్ మరియు అవుట్గోయింగ్ కాల్లకు సంబంధించిన డేటాతో సహా మీ టీవీ కాల్ లాగ్ను సవరించడానికి అనువర్తనాన్ని అనుమతిస్తుంది. హానికరమైన అనువర్తనాలు మీ కాల్ లాగ్ను తీసివేయడానికి లేదా సవరించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"ఇన్కమింగ్ మరియు అవుట్గోయింగ్ కాల్ల గురించిన డేటాతో సహా మీ ఫోన్ యొక్క కాల్ లాగ్ను సవరించడానికి అనువర్తనాన్ని అనుమతిస్తుంది. హానికరమైన అనువర్తనాలు మీ కాల్ లాగ్ను ఎరేజ్ చేయడానికి లేదా సవరించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"శరీర సెన్సార్లు (హృదయ స్పందన మానిటర్లు వంటివి)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"శరీర సెన్సార్లను (గుండె స్పందన రేటు మానిటర్ల వంటివి) ప్రాప్యత చేయడం"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"మీ శారీరక పరిస్థితిని అనగా మీ గుండె స్పందన రేటు వంటి వాటిని పర్యవేక్షించే సెన్సార్ల నుండి డేటాను ప్రాప్యత చేయడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"క్యాలెండర్ ఈవెంట్లతో పాటు గోప్యమైన సమాచారాన్ని చదవడం"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"స్నేహితులు లేదా సహోద్యోగులకు సంబంధించిన ఈవెంట్లతో సహా మీ టాబ్లెట్లో నిల్వ చేయబడిన అన్ని క్యాలెండర్ ఈవెంట్లను చదవడానికి అనువర్తనాన్ని అనుమతిస్తుంది. ఇది గోప్యత లేదా తీవ్రతతో సంబంధం లేకుండా మీ క్యాలెండర్ డేటాను భాగస్వామ్యం చేయడానికి లేదా సేవ్ చేయడానికి అనువర్తనాన్ని అనుమతించవచ్చు."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"స్నేహితులు లేదా సహోద్యోగులకు సంబంధించిన ఈవెంట్లతో సహా మీరు మీ ఫోన్లో సవరించగల ఈవెంట్లను జోడించడానికి, తీసివేయడానికి, మార్చడానికి అనువర్తనాన్ని అనుమతిస్తుంది. ఇది క్యాలెండర్ యజమానుల నుండి వచ్చినట్లుగా కనిపించే రీతిలో సందేశాలను పంపడానికి లేదా యజమానికి తెలియకుండానే ఈవెంట్లను సవరించడానికి అనువర్తనాన్ని అనుమతించవచ్చు."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"అదనపు స్థాన ప్రదాత ఆదేశాలను ప్రాప్యత చేయడం"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"అదనపు స్థాన ప్రదాత ఆదేశాలను ప్రాప్యత చేయడానికి అనువర్తనాన్ని అనుమతిస్తుంది. ఇది GPS లేదా ఇతర స్థాన మూలాల నిర్వహణలో అనువర్తనం ప్రమేయం ఉండేలా అనుమతించవచ్చు."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"ఖచ్చితమైన స్థానం (GPS మరియు నెట్వర్క్-ఆధారితం)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"ఖచ్చితమైన స్థానాన్ని (GPS మరియు నెట్వర్క్-ఆధారితం) ప్రాప్యత చేయడం"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"గ్లోబల్ పొజిషనింగ్ సిస్టమ్ (GPS) లేదా సెల్ టవర్లు మరియు Wi-Fi వంటి నెట్వర్క్ స్థాన మూలాలను ఉపయోగించి మీ ఖచ్చితమైన స్థానాన్ని పొందడానికి అనువర్తనాన్ని అనుమతిస్తుంది. ఈ స్థాన సేవలను అనువర్తనం ఉపయోగించడానికి తప్పనిసరిగా ప్రారంభించబడి ఉండాలి మరియు మీ పరికరానికి అందుబాటులో ఉండాలి. అనువర్తనాలు మీరు ఉన్న ప్రాంతాన్ని కనుగొనడానికి దీన్ని ఉపయోగించవచ్చు మరియు అదనపు బ్యాటరీ శక్తిని వినియోగించవచ్చు."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"సామీప్య స్థానం (నెట్వర్క్-ఆధారితం)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"ఇంచుమించు స్థానాన్ని (నెట్వర్క్-ఆధారితం) ప్రాప్యత చేయడం"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"మీ సామీప్య స్థానాన్ని పొందడానికి అనువర్తనాన్ని అనుమతిస్తుంది. సెల్ టవర్లు మరియు Wi-Fi వంటి నెట్వర్క్ స్థాన మూలాలను ఉపయోగించి స్థాన సేవల ద్వారా ఈ స్థానం కనుగొనబడుతుంది. ఈ స్థాన సేవలను అనువర్తనం ఉపయోగించడానికి తప్పనిసరిగా ప్రారంభించబడి ఉండాలి మరియు మీ పరికరానికి అందుబాటులో ఉండాలి. అనువర్తనాలు మీరు ఉన్న ప్రాంతాన్ని సుమారుగా గుర్తించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"మీ ఆడియో సెట్టింగ్లను మార్చడం"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"వాల్యూమ్ మరియు అవుట్పుట్ కోసం ఉపయోగించాల్సిన స్పీకర్ వంటి సార్వజనీన ఆడియో సెట్టింగ్లను సవరించడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"ఆడియోను రికార్డ్ చేయడం"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"మైక్రోఫోన్తో ఆడియోను రికార్డ్ చేయడానికి అనువర్తనాన్ని అనుమతిస్తుంది. ఈ అనుమతి మీ నిర్ధారణ లేకుండానే ఎప్పుడైనా ఆడియోను రికార్డ్ చేయడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"సిమ్ కమ్యూనికేషన్"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"SIMకి ఆదేశాలను పంపడం"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"సిమ్కు ఆదేశాలను పంపడానికి అనువర్తనాన్ని అనుమతిస్తుంది. ఇది చాలా ప్రమాదకరం."</string>
<string name="permlab_camera" msgid="3616391919559751192">"చిత్రాలు మరియు వీడియోలు తీయడం"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"కెమెరాతో చిత్రాలు మరియు వీడియోలను తీయడానికి అనువర్తనాన్ని అనుమతిస్తుంది. ఈ అనుమతి మీ నిర్ధారణ లేకుండానే ఎప్పుడైనా కెమెరాను ఉపయోగించడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"ఫోన్కు తెలిసిన ఖాతాల జాబితాను పొందడానికి అనువర్తనాన్ని అనుమతిస్తుంది. దీనిలో మీరు ఇన్స్టాల్ చేసిన అనువర్తనాల ద్వారా సృష్టించబడిన ఖాతాలు ఏవైనా ఉండవచ్చు."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"నెట్వర్క్ కనెక్షన్లను వీక్షించడం"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"ఏ నెట్వర్క్లు ఉన్నాయి మరియు కనెక్ట్ చేయబడ్డాయి వంటి నెట్వర్క్ కనెక్షన్ల గురించి సమాచారాన్ని వీక్షించడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"పూర్తి నెట్వర్క్ ప్రాప్యత"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"పూర్తి నెట్వర్క్ ప్రాప్యతను కలిగి ఉండటం"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"నెట్వర్క్ సాకెట్లను సృష్టించడానికి మరియు అనుకూల నెట్వర్క్ ప్రోటోకాల్లను ఉపయోగించడానికి అనువర్తనాన్ని అనుమతిస్తుంది. బ్రౌజర్ మరియు ఇతర అనువర్తనాలు ఇంటర్నెట్కు డేటా పంపడానికి మార్గాలను అందిస్తాయి, కనుక ఇంటర్నెట్కు డేటా పంపడానికి ఈ అనుమతి అవసరం లేదు."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"నెట్వర్క్ కనెక్టివిటీని మార్చడం"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"నెట్వర్క్ కనెక్టివిటీ యొక్క స్థితిని మార్చడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"స్థానిక బ్లూటూత్ ఫోన్ను కాన్ఫిగర్ చేయడానికి మరియు రిమోట్ పరికరాలతో దాన్ని కనుగొనడానికి మరియు జత చేయడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"WiMAXకు కనెక్ట్ చేయడం మరియు దాని నుండి డిస్కనెక్ట్ చేయడం"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Wi-Fi ప్రారంభించబడిందా, లేదా మరియు కనెక్ట్ చేయబడిన WiMAX నెట్వర్క్ల గురించి సమాచారాన్ని కనుగొనడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX స్థితిని మార్చండి"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"WiMAX స్థితిని మార్చడం"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"WiMAX నెట్వర్క్లకు టాబ్లెట్ను కనెక్ట్ చేయడానికి మరియు వాటి నుండి టాబ్లెట్ను డిస్కనెక్ట్ చేయడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"టీవీని WiMAX నెట్వర్క్లకు కనెక్ట్ చేయడానికి మరియు వాటి నుండి టీవీని డిస్కనెక్ట్ చేయడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"WiMAX నెట్వర్క్లకు ఫోన్ను కనెక్ట్ చేయడానికి మరియు వాటి నుండి ఫోన్ను డిస్కనెక్ట్ చేయడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"టచ్ స్క్రీన్ యొక్క క్రమాంకన పరామితులను సవరించడానికి అనువర్తనాన్ని అనుమతిస్తుంది. సాధారణ అనువర్తనాలకు ఎప్పటికీ అవసరం ఉండదు."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"DRM ప్రమాణపత్రాలను ప్రాప్యత చేయడం"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"DRM ప్రమాణపత్రాలను కేటాయించడానికి మరియు ఉపయోగించడానికి అనువర్తనాన్ని అనుమతిస్తుంది. సాధారణ అనువర్తనాలకు ఎప్పటికీ అవసరం ఉండదు."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Android Beam బదిలీ స్థితిని స్వీకరించండి"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"Android Beam బదిలీ స్థితిని స్వీకరించడం"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"ప్రస్తుత Android Beam బదిలీలకు సంబంధించిన సమాచారాన్ని స్వీకరించడానికి ఈ అనువర్తనాన్ని అనుమతిస్తుంది"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"DRM ప్రమాణపత్రాలను తీసివేయడం"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"DRM ప్రమాణపత్రాలను తీసివేయడానికి అనువర్తనాన్ని అనుమతిస్తుంది. సాధారణ అనువర్తనాలకు ఎప్పటికీ అవసరం ఉండదు."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"చెల్లని పిన్ కోడ్."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"అన్లాక్ చేయడానికి, మెను ఆపై 0ని నొక్కండి."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"అత్యవసర నంబర్"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"సేవ లేదు."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"సేవ లేదు"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"స్క్రీన్ లాక్ చేయబడింది."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"అన్లాక్ చేయడానికి లేదా అత్యవసర కాల్ చేయడానికి మెను నొక్కండి."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"అన్లాక్ చేయడానికి మెను నొక్కండి."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"వినియోగదారు గైడ్ను చూడండి లేదా కస్టమర్ కేర్ను సంప్రదించండి."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"సిమ్ కార్డు లాక్ చేయబడింది."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"సిమ్ కార్డును అన్లాక్ చేస్తోంది…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"మీరు మీ అన్లాక్ నమూనాను <xliff:g id="NUMBER_0">%d</xliff:g> సార్లు తప్పుగా గీసారు. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"మీరు మీ పాస్వర్డ్ను <xliff:g id="NUMBER_0">%d</xliff:g> సార్లు తప్పుగా టైప్ చేసారు. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"మీరు మీ పిన్ను <xliff:g id="NUMBER_0">%d</xliff:g> సార్లు తప్పుగా టైప్ చేసారు. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"మీరు మీ అన్లాక్ నమూనాని <xliff:g id="NUMBER_0">%d</xliff:g> సార్లు తప్పుగా గీసారు. మరో <xliff:g id="NUMBER_1">%d</xliff:g> విజయవంతం కాని ప్రయత్నాల తర్వాత, మీరు మీ Google సైన్ఇన్ను ఉపయోగించి మీ టాబ్లెట్ను అన్లాక్ చేయడానికి అడగబడతారు.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"మీరు మీ అన్లాక్ నమూనాను <xliff:g id="NUMBER_0">%d</xliff:g> సార్లు తప్పుగా గీశారు. మరో <xliff:g id="NUMBER_1">%d</xliff:g> ప్రయత్నాలు విఫలమైతే, మీరు మీ Google సైన్ ఇన్ను ఉపయోగించి మీ టీవీని అన్లాక్ చేయమని అడుగుతాము.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"మీరు మీ అన్లాక్ నమూనాని <xliff:g id="NUMBER_0">%d</xliff:g> సార్లు తప్పుగా గీసారు. మరో <xliff:g id="NUMBER_1">%d</xliff:g> విజయవంతం కాని ప్రయత్నాల తర్వాత, మీరు మీ Google సైన్ఇన్ను ఉపయోగించి మీ ఫోన్ను అన్లాక్ చేయడానికి అడగబడతారు.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"మీరు టాబ్లెట్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER_0">%d</xliff:g> సార్లు తప్పుగా ప్రయత్నించారు. మరో <xliff:g id="NUMBER_1">%d</xliff:g> వైఫల్య ప్రయత్నాల తర్వాత, టాబ్లెట్ ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది మరియు మొత్తం వినియోగదారు డేటాను కోల్పోవడం సంభవిస్తుంది."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"మీరు టీవీని అన్లాక్ చేయడానికి <xliff:g id="NUMBER_0">%d</xliff:g> సార్లు చెల్లని ప్రయత్నాలు చేశారు. మరో <xliff:g id="NUMBER_1">%d</xliff:g> ప్రయత్నాలు విఫలమైతే, టీవీ ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది మరియు మొత్తం వినియోగదారు డేటాను కోల్పోతారు."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"మీరు ఫోన్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER_0">%d</xliff:g> సార్లు తప్పుగా ప్రయత్నించారు. మరో <xliff:g id="NUMBER_1">%d</xliff:g> వైఫల్య ప్రయత్నాల తర్వాత, ఫోన్ ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది మరియు మొత్తం వినియోగదారు డేటాను కోల్పోవడం సంభవిస్తుంది."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"మీరు మీ అన్లాక్ నమూనాను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"మీరు మీ పాస్వర్డ్ను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా టైప్ చేసారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"మీరు మీ పిన్ను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా టైప్ చేసారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"మీరు మీ అన్లాక్ నమూనాని <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విజయవంతం కాని ప్రయత్నాల తర్వాత, మీరు మీ Google సైన్ఇన్ను ఉపయోగించి మీ టాబ్లెట్ను అన్లాక్ చేయడానికి అడగబడతారు.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"మీరు మీ అన్లాక్ నమూనాను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీశారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> ప్రయత్నాలు విఫలమైతే, మీరు మీ Google సైన్ ఇన్ను ఉపయోగించి మీ టీవీని అన్లాక్ చేయమని అడుగుతాము.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"మీరు మీ అన్లాక్ నమూనాని <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విజయవంతం కాని ప్రయత్నాల తర్వాత, మీరు మీ Google సైన్ఇన్ను ఉపయోగించి మీ ఫోన్ను అన్లాక్ చేయడానికి అడగబడతారు.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"మీరు టాబ్లెట్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా ప్రయత్నించారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> వైఫల్య ప్రయత్నాల తర్వాత, టాబ్లెట్ ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది మరియు మొత్తం వినియోగదారు డేటాను కోల్పోవడం సంభవిస్తుంది."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"మీరు టీవీని అన్లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు చెల్లని ప్రయత్నాలు చేశారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> ప్రయత్నాలు విఫలమైతే, టీవీ ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది మరియు మొత్తం వినియోగదారు డేటాను కోల్పోతారు."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"మీరు ఫోన్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా ప్రయత్నించారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> వైఫల్య ప్రయత్నాల తర్వాత, ఫోన్ ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది మరియు మొత్తం వినియోగదారు డేటాను కోల్పోవడం సంభవిస్తుంది."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"మీరు టాబ్లెట్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> సార్లు తప్పుగా ప్రయత్నించారు. టాబ్లెట్ ఇప్పుడు ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"మీరు టీవీని అన్లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> సార్లు చెల్లని ప్రయత్నాలు చేశారు. టీవీ ఇప్పుడు ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"మీరు ఫోన్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> సార్లు తప్పుగా ప్రయత్నించారు. ఫోన్ ఇప్పుడు ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"పదం"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"లింక్"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"పంక్తి"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"ఫ్యాక్టరీ పరీక్ష విఫలమైంది"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"/system/appలో ఇన్స్టాల్ చేయబడిన ప్యాకేజీల కోసం మాత్రమే FACTORY_TEST చర్యకు మద్దతు ఉంటుంది."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"FACTORY_TEST చర్యను అందించే ప్యాకేజీ ఏదీ కనుగొనబడలేదు."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"ఫార్మాట్ చేస్తోంది..."</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"చొప్పించబడలేదు"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"సరిపోలే కార్యాచరణలు కనుగొనబడలేదు."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"ప్రసార మాధ్యమ అవుట్పుట్ను మళ్లించడం"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"మీడియా అవుట్పుట్ను మళ్లించడం"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"మీడియా అవుట్పుట్ను ఇతర బాహ్య పరికరాలకు మళ్లించడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"ఇన్స్టాల్ సెషన్లను చదవడం"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"ఇన్స్టాల్ సెషన్లను చదవడం"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"ఇన్స్టాల్ సెషన్లను చదవడానికి అనువర్తనాన్ని అనుమతిస్తుంది. ఇది సక్రియ ప్యాకేజీ ఇన్స్టాలేషన్ల గురించి వివరాలను చూడటానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"ఇన్స్టాల్ ప్యాకేజీలను అభ్యర్థించడానికి అనుమతి"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"ఇన్స్టాల్ ప్యాకేజీలను అభ్యర్థించడం"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"ప్యాకేజీల ఇన్స్టాలేషన్ అభ్యర్థించడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"జూమ్ నియంత్రణ కోసం రెండుసార్లు తాకండి"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"విడ్జెట్ను జోడించడం సాధ్యపడలేదు."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"చెల్లని వినియోగదారు పేరు లేదా పాస్వర్డ్."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"మీ వినియోగదారు పేరు లేదా పాస్వర్డ్ను మర్చిపోయారా?\n"<b>"google.com/accounts/recovery"</b>"ని సందర్శించండి."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"ఖాతాను తనిఖీ చేస్తోంది…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"మీరు మీ పిన్ను <xliff:g id="NUMBER_0">%d</xliff:g> సార్లు తప్పుగా టైప్ చేసారు. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"మీరు మీ పాస్వర్డ్ను <xliff:g id="NUMBER_0">%d</xliff:g> సార్లు తప్పుగా టైప్ చేసారు. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"మీరు మీ అన్లాక్ నమూనాను <xliff:g id="NUMBER_0">%d</xliff:g> సార్లు తప్పుగా గీసారు. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"మీరు టాబ్లెట్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER_0">%d</xliff:g> చెల్లని ప్రయత్నాలు చేసారు. మరో <xliff:g id="NUMBER_1">%d</xliff:g> విఫల ప్రయత్నాల తర్వాత, టాబ్లెట్ ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది మరియు మొత్తం వినియోగదారు డేటాను కోల్పోవడం సంభవిస్తుంది."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"మీరు టీవీని అన్లాక్ చేయడానికి <xliff:g id="NUMBER_0">%d</xliff:g> సార్లు చెల్లని ప్రయత్నాలు చేశారు. మరో <xliff:g id="NUMBER_1">%d</xliff:g> ప్రయత్నాలు విఫలమైతే, టీవీ ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది మరియు మొత్తం వినియోగదారు డేటాను కోల్పోతారు."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"మీరు ఫోన్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER_0">%d</xliff:g> చెల్లని ప్రయత్నాలు చేసారు. మరో <xliff:g id="NUMBER_1">%d</xliff:g> విఫల ప్రయత్నాల తర్వాత, ఫోన్ ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది మరియు మొత్తం వినియోగదారు డేటాను కోల్పోవడం సంభవిస్తుంది."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"మీరు మీ పిన్ను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా టైప్ చేసారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"మీరు మీ పాస్వర్డ్ను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా టైప్ చేసారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"మీరు మీ అన్లాక్ నమూనాను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"మీరు టాబ్లెట్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> చెల్లని ప్రయత్నాలు చేసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, టాబ్లెట్ ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది మరియు మొత్తం వినియోగదారు డేటాను కోల్పోవడం సంభవిస్తుంది."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"మీరు టీవీని అన్లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు చెల్లని ప్రయత్నాలు చేశారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> ప్రయత్నాలు విఫలమైతే, టీవీ ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది మరియు మొత్తం వినియోగదారు డేటాను కోల్పోతారు."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"మీరు ఫోన్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> చెల్లని ప్రయత్నాలు చేసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, ఫోన్ ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది మరియు మొత్తం వినియోగదారు డేటాను కోల్పోవడం సంభవిస్తుంది."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"మీరు టాబ్లెట్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> చెల్లని ప్రయత్నాలు చేసారు. టాబ్లెట్ ఇప్పుడు ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"మీరు టీవీని అన్లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> సార్లు చెల్లని ప్రయత్నాలు చేశారు. టీవీ ఇప్పుడు ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"మీరు ఫోన్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> చెల్లని ప్రయత్నాలు చేసారు. ఫోన్ ఇప్పుడు ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"మీరు మీ అన్లాక్ నమూనాను <xliff:g id="NUMBER_0">%d</xliff:g> సార్లు తప్పుగా గీసారు. మరో <xliff:g id="NUMBER_1">%d</xliff:g> విఫల ప్రయత్నాల తర్వాత, ఇమెయిల్ ఖాతాను ఉపయోగించి మీ టాబ్లెట్ను అన్లాక్ చేయాల్సిందిగా మిమ్మల్ని అడుగుతారు.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"మీరు మీ అన్లాక్ నమూనాను <xliff:g id="NUMBER_0">%d</xliff:g> సార్లు తప్పుగా గీశారు. మరో <xliff:g id="NUMBER_1">%d</xliff:g> ప్రయత్నాలు విఫలమైతే, ఇమెయిల్ ఖాతాను ఉపయోగించి మీ టీవీని అన్లాక్ చేయమని మిమ్మల్ని అడుగుతాము.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"మీరు మీ అన్లాక్ నమూనాను <xliff:g id="NUMBER_0">%d</xliff:g> సార్లు తప్పుగా గీసారు. మరో <xliff:g id="NUMBER_1">%d</xliff:g> విఫల ప్రయత్నాల తర్వాత, ఇమెయిల్ ఖాతాను ఉపయోగించి మీ ఫోన్ను అన్లాక్ చేయాల్సిందిగా మిమ్మల్ని అడుగుతారు.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"మీరు మీ అన్లాక్ నమూనాను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, ఇమెయిల్ ఖాతాను ఉపయోగించి మీ టాబ్లెట్ను అన్లాక్ చేయాల్సిందిగా మిమ్మల్ని అడుగుతారు.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"మీరు మీ అన్లాక్ నమూనాను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీశారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> ప్రయత్నాలు విఫలమైతే, ఇమెయిల్ ఖాతాను ఉపయోగించి మీ టీవీని అన్లాక్ చేయమని మిమ్మల్ని అడుగుతాము.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"మీరు మీ అన్లాక్ నమూనాను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, ఇమెయిల్ ఖాతాను ఉపయోగించి మీ ఫోన్ను అన్లాక్ చేయాల్సిందిగా మిమ్మల్ని అడుగుతారు.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"తీసివేయి"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"వాల్యూమ్ను సిఫార్సు చేయబడిన స్థాయి కంటే ఎక్కువగా పెంచాలా?\n\nసుదీర్ఘ వ్యవధుల పాటు అధిక వాల్యూమ్లో వినడం వలన మీ వినికిడి శక్తి దెబ్బ తినవచ్చు."</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">1 గం పాటు</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> వరకు"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (తదుపరి అలారం) వరకు"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"మీరు దీన్ని ఆఫ్ చేసే వరకు"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"మీరు అంతరాయం కలిగించవద్దు ఎంపిక ఆఫ్ చేసే వరకు"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-th-watch/strings.xml b/core/res/res/values-th-watch/strings.xml
index 98f938d..26f1f02 100644
--- a/core/res/res/values-th-watch/strings.xml
+++ b/core/res/res/values-th-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"แอป <xliff:g id="NUMBER_0">%1$d</xliff:g> จาก <xliff:g id="NUMBER_1">%2$d</xliff:g> แอป"</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"เซ็นเซอร์"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"เข้าถึงรายชื่อติดต่อ"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"เข้าถึงตำแหน่งของนาฬิกานี้"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"เข้าถึงปฏิทิน"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"ส่งและดูข้อความ SMS"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"เข้าถึงรูปภาพ สื่อ และไฟล์ในนาฬิกา"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"บันทึกเสียง"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"ถ่ายภาพและบันทึกวิดีโอ"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"โทรและจัดการการโทร"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"เข้าถึงข้อมูลเซ็นเซอร์เกี่ยวกับสัญญาณชีพของคุณ"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"เป็นแถบสถานะ"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"เข้าถึงเซ็นเซอร์สำหรับร่างกาย (เช่น เครื่องติดตามดูอัตราการเต้นของหัวใจ)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"เข้าถึงตำแหน่งที่แม่นยำ (อิงจาก GPS และเครือข่าย)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"เข้าถึงตำแหน่งโดยประมาณ (อิงจากเครือข่าย)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"ส่งคำสั่งไปยังซิม"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"มีสิทธิ์เข้าถึงเครือข่ายเต็มรูปแบบ"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"จัดการเจ้าของโปรไฟล์และอุปกรณ์"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"เปลี่ยนสถานะของ WiMAX"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"รับสถานะการโอน Android Beam"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"กำหนดเส้นทางเอาต์พุตของสื่อ"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"อ่านเซสชันการติดตั้ง"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"ขอติดตั้งแพ็กเกจ"</string>
</resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index b2df636..013da3d 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"รวมถึงข้อมูลส่วนบุคคล เช่น หมายเลขบัตรเครดิตและรหัสผ่าน"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"ปิดการใช้งานหรือแก้ไขแถบสถานะ"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"อนุญาตให้แอปพลิเคชันปิดใช้งานแถบสถานะหรือเพิ่มและนำไอคอนระบบออก"</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"แถบสถานะ"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"เป็นแถบสถานะ"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"อนุญาตให้แอปพลิเคชันเป็นแถบสถานะ"</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"ขยาย/ยุบแถบสถานะ"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"อนุญาตให้แอปพลิเคชันขยายหรือยุบแถบสถานะ"</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"อนุญาตให้แอปพลิเคชันรับและประมวลผลข้อความ WAP การอนุญาตนี้รวมถึงความสามารถในการตรวจสอบหรือลบข้อความที่ส่งมาให้คุณโดยไม่ต้องแสดงให้คุณเห็น"</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"เรียกแอปพลิเคชันที่ทำงานอยู่"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"อนุญาตให้แอปพลิเคชันเรียกดูข้อมูลเกี่ยวกับงานที่ดำเนินการอยู่ในขณะนี้และเมื่อเร็วๆ นี้ ซึ่งอาจทำให้แอปพลิเคชันสามารถค้นข้อมูลได้ว่าอุปกรณ์นี้ใช้แอปพลิเคชันใดบ้าง"</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"จัดการเจ้าของโปรไฟล์และอุปกรณ์"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"จัดการเจ้าของโปรไฟล์และอุปกรณ์"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"อนุญาตให้แอปตั้งค่าเจ้าของโปรไฟล์และเจ้าของอุปกรณ์"</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"จัดลำดับแอปพลิเคชันที่ทำงานอยู่ใหม่"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"อนุญาตให้แอปพลิเคชันย้ายงานไปยังส่วนหน้าและพื้นหลัง แอปพลิเคชันอาจดำเนินการโดยไม่รอคำสั่งจากคุณ"</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"อนุญาตให้แอปแก้ไขประวัติการโทรจากแท็บเล็ตของคุณ รวมถึงข้อมูลเกี่ยวกับสายเรียกเข้าและการโทรออก แอปที่เป็นอันตรายอาจใช้สิ่งนี้เพื่อลบหรือแก้ไขประวัติการโทรของคุณ"</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"อนุญาตให้แอปแก้ไขประวัติการโทรของทีวี รวมถึงข้อมูลเกี่ยวกับสายเรียกเข้าและโทรออก แอปที่เป็นอันตรายอาจใช้สิทธิ์นี้เพื่อลบหรือแก้ไขประวัติการโทรได้"</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"อนุญาตให้แอปแก้ไขประวัติการโทรจากโทรศัพท์ของคุณ รวมถึงข้อมูลเกี่ยวกับสายเรียกเข้าและการโทรออก แอปที่เป็นอันตรายอาจใช้สิ่งนี้เพื่อลบหรือแก้ไขประวัติการโทรของคุณ"</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"เซ็นเซอร์ร่างกาย (เช่น วัดอัตราการเต้นของหัวใจ)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"เข้าถึงเซ็นเซอร์ร่างกาย (เช่น ตัววัดอัตราการเต้นของหัวใจ)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"อนุญาตให้แอปเข้าถึงข้อมูลจากเซ็นเซอร์ที่ตรวจสอบสภาพทางกายภาพ เช่น อัตราการเต้นของหัวใจ"</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"อ่านกิจกรรมบนปฏิทินรวมถึงข้อมูลที่เป็นความลับ"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"อนุญาตให้แอปพลิเคชันอ่านกิจกรรมในปฏิทินทั้งหมดที่จัดเก็บไว้ในแท็บเล็ตของคุณ ซึ่งรวมถึงกิจกรรมของเพื่อนหรือเพื่อนร่วมงานด้วย ซึ่งอาจทำให้แอปพลิเคชันสามารถแชร์หรือบันทึกข้อมูลในปฏิทินของคุณได้ไม่ว่าจะมีการรักษาข้อมูลที่เป็นความลับหรือหรือข้อมูลที่อ่อนไหวแบบใดก็ตาม"</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"อนุญาตให้แอปพลิเคชันเพิ่ม ลบ เปลี่ยนกิจกรรมที่คุณสามารถเปลี่ยนแปลงในโทรศัพท์ได้ รวมถึงกิจกรรมของเพื่อนหรือเพื่อนร่วมงานด้วย การอนุญาตนี้อาจทำให้แอปพลิเคชันสามารถส่งข้อความที่มาจากเจ้าของปฏิทิน หรือเปลี่ยนแปลงกิจกรรมโดยที่เจ้าของไม่ทราบ"</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"เข้าถึงคำสั่งของโปรแกรมแจ้งตำแหน่งพิเศษ"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"อนุญาตให้แอปเข้าถึงคำสั่งของผู้ให้บริการตำแหน่งเพิ่มเติม ซึ่งอาจทำให้แอปสามารถแทรกแซงการทำงานของ GPS หรือต้นทางของตำแหน่งอื่นๆ ได้"</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"ตำแหน่งที่แม่นยำ (อิงตาม GPS และเครือข่าย)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"เข้าถึงตำแหน่งที่แม่นยำ (อิงจาก GPS และเครือข่าย)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"อนุญาตให้แอปพลิเคชันรับตำแหน่งที่แม่นยำของคุณโดยใช้ Global Positioning System (GPS) หรือต้นทางของตำแหน่งในเครือข่ายอย่างเช่น เสาสัญญาณมือถือ และ WiFi บริการตำแหน่งเหล่านี้จะต้องถูกเปิดใช้งานอยู่สำหรับอุปกรณ์ของคุณเพื่อให้แอปพลิเคชันสามารถใช้งานได้ แอปพลิเคชันสามารถใช้บริการตำแหน่งนี้เพื่อตัดสินว่าคุณอยู่ ณ จุดใด และอาจใช้พลังงานแบตเตอรี่มากกว่าปกติ"</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"ตำแหน่งโดยประมาณ (อิงตามเครือข่าย)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"เข้าถึงตำแหน่งโดยประมาณ (อิงจากเครือข่าย)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"อนุญาตให้แอปพลิเคชันรับตำแหน่งโดยประมาณของคุณ บริการตำแหน่งจะดึงตำแหน่งขึ้นมาโดยใช้ต้นทางของตำแหน่งอย่างเช่น เสาสัญญาณมือถือ และ WiFi บริการตำแหน่งเหล่านี้จะต้องถูกเปิดใช้งานอยู่สำหรับอุปกรณ์ของคุณเพื่อให้แอปพลิเคชันสามารถใช้งานได้ แอปพลิเคชันสามารถใช้บริการตำแหน่งนี้เพื่อตัดสินอย่างคร่าวๆ ว่าคุณอยู่ ณ จุดใด"</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"เปลี่ยนการตั้งค่าเสียงของคุณ"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"อนุญาตให้แอปพลิเคชันปรับเปลี่ยนการตั้งค่าเสียงทั้งหมดได้ เช่น ระดับเสียงและลำโพงที่จะใช้งาน"</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"บันทึกเสียง"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"อนุญาตให้แอปพลิเคชันบันทึกเสียงด้วยไมโครโฟน การอนุญาตนี้ทำให้แอปพลิเคชันสามารถบันทึกเสียงได้ทุกเมื่อโดยไม่ต้องรอการยืนยันจากคุณ"</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"การสื่อสารกับ SIM"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"ส่งคำสั่งไปยังซิม"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"อนุญาตให้แอปส่งคำสั่งไปยัง SIM ซึ่งอันตรายมาก"</string>
<string name="permlab_camera" msgid="3616391919559751192">"ถ่ายภาพและวิดีโอ"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"อนุญาตให้แอปพลิเคชันถ่ายภาพและวิดีโอด้วยกล้องถ่ายรูปนี้ การอนุญาตนี้จะทำให้แอปพลิเคชันสามารถใช้กล้องถ่ายรูปได้ทุกเมื่อโดยไม่ต้องรอการยืนยันจากคุณ"</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"อนุญาตให้แอปพลิเคชันรับรายการบัญชีที่โทรศัพท์รู้จัก ซึ่งอาจรวมถึงบัญชีใดๆ ก็ตามที่แอปพลิเคชันซึ่งคุณติดตั้งไว้ได้สร้างขึ้น"</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"ดูการเชื่อมต่อเครือข่าย"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"อนุญาตให้แอปพลิเคชันดูข้อมูลเกี่ยวกับการเชื่อมต่อเครือข่าย เช่น มีเครือข่ายใดอยู่บ้าง และมีการเชื่อมต่อเครือข่ายใด"</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"การเข้าถึงเครือข่ายเต็มรูปแบบ"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"มีสิทธิ์เข้าถึงเครือข่ายเต็มรูปแบบ"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"อนุญาตให้แอปพลิเคชันสร้างซ็อกเก็ตเครือข่ายและใช้โปรโตคอลเครือข่ายที่กำหนดเอง เบราว์เซอร์และแอปพลิเคชันอื่นๆ ให้วิธีการในการส่งข้อมูลไปยังอินเทอร์เน็ต จึงไม่จำเป็นต้องได้รับการอนุญาตนี้เพื่อที่จะส่งข้อมูลไปยังอินเทอร์เน็ต"</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"เปลี่ยนการเชื่อมต่อเครือข่าย"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"อนุญาตให้แอปพลิเคชันเปลี่ยนแปลงสถานะการเชื่อมต่อของเครือข่าย"</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"อนุญาตให้แอปพลิเคชันกำหนดค่าโทรศัพท์บลูทูธในตัวเครื่อง ตลอดจนค้นหาและจับคู่กับอุปกรณ์ที่อยู่ระยะไกล"</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"เชื่อมต่อและเลิกเชื่อมต่อจาก WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"อนุญาตให้แอปพลิเคชันตรวจสอบว่า WiMAX เปิดใช้งานอยู่หรือไม่และข้อมูลเกี่ยวกับเครือข่าย WiMAX ใดๆ ที่เชื่อมต่ออยู่"</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"เปลี่ยนสถานะของ WiMAX"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"เปลี่ยนสถานะของ WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"อนุญาตให้แอปพลิเคชันเชื่อมต่อและยกเลิกการเชื่อมต่อแท็บเล็ตกับเครือข่าย WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"อนุญาตให้แอปเชื่อมต่อทีวีและยกเลิกการเชื่อมต่อทีวีจากเครือข่าย WiMAX"</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"อนุญาตให้แอปพลิเคชันเชื่อมต่อและยกเลิกการเชื่อมต่อโทรศัพท์กับเครือข่าย WiMAX"</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"อนุญาตให้แอปสามารถปรับพารามิเตอร์การเทียบมาตรฐานของหน้าจอสัมผัส ไม่ควรใช้สำหรับแอปทั่วไป"</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"เข้าถึงใบรับรอง DRM"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"ช่วยให้แอปพลิเคชันสามารถจัดสรรและใช้ใบรับรอง DRM ได้ ไม่จำเป็นสำหรับแอปปกติทั่วไป"</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"รับสถานะการโอน Android Beam"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"รับสถานะการโอน Android Beam"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"อนุญาตให้แอปพลิเคชันนี้รับข้อมูลเกี่ยวกับการโอน Android Beam ปัจจุบัน"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"นำใบรับรอง DRM ออก"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"อนุญาตให้แอปพลิเคชันนำใบรับรอง DRM ออก แอปทั่วไปไม่จำเป็นต้องใช้"</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"รหัส PIN ไม่ถูกต้อง"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"หากต้องการปลดล็อก กด เมนู ตามด้วย 0"</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"หมายเลขฉุกเฉิน"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"ไม่มีบริการ"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"ไม่มีบริการ"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"หน้าจอถูกล็อก"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"กด เมนู เพื่อปลดล็อกหรือโทรฉุกเฉิน"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"กด เมนู เพื่อปลดล็อก"</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"ดูคู่มือผู้ใช้หรือติดต่อศูนย์บริการลูกค้า"</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"ซิมการ์ดถูกล็อก"</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"กำลังปลดล็อกซิมการ์ด…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"คุณวาดรูปแบบการปลดล็อกไม่ถูกต้องไป <xliff:g id="NUMBER_0">%d</xliff:g> ครั้งแล้ว\n\nโปรดลองอีกครั้งในอีก <xliff:g id="NUMBER_1">%d</xliff:g> วินาที"</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"คุณพิมพ์รหัสผ่านไม่ถูกต้องไป <xliff:g id="NUMBER_0">%d</xliff:g> ครั้งแล้ว\n\nลองอีกครั้งใน <xliff:g id="NUMBER_1">%d</xliff:g> วินาที"</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"คุณพิมพ์ PIN ไม่ถูกต้องไป <xliff:g id="NUMBER_0">%d</xliff:g> ครั้งแล้ว\n\nลองอีกครั้งใน <xliff:g id="NUMBER_1">%d</xliff:g> วินาที"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"คุณวาดรูปแบบการปลดล็อกไม่ถูกต้องไป <xliff:g id="NUMBER_0">%d</xliff:g> ครั้งแล้ว หากทำไม่สำเร็จอีก <xliff:g id="NUMBER_1">%d</xliff:g> ครั้ง ระบบจะขอให้คุณปลดล็อกแท็บเล็ตโดยใช้การลงชื่อเข้าใช้ Google\n\nโปรดลองอีกครั้งในอีก <xliff:g id="NUMBER_2">%d</xliff:g> วินาที"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"คุณวาดรูปแบบการปลดล็อกไม่ถูกต้อง <xliff:g id="NUMBER_0">%d</xliff:g> ครั้ง หากวาดไม่ถูกต้องอีก <xliff:g id="NUMBER_1">%d</xliff:g> ครั้ง ระบบจะขอให้คุณปลดล็อกทีวีโดยการลงชื่อเช้าใช้ Google\n\n โปรดลองอีกครั้งใน <xliff:g id="NUMBER_2">%d</xliff:g> วินาที"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"คุณวาดรูปแบบการปลดล็อกไม่ถูกต้อง <xliff:g id="NUMBER_0">%d</xliff:g> ครั้ง หากทำไม่สำเร็จอีก <xliff:g id="NUMBER_1">%d</xliff:g> ครั้ง ระบบจะขอให้คุณปลดล็อกโทรศัพท์โดยใช้การลงชื่อเข้าใช้ Google\n\nโปรดลองอีกครั้งในอีก <xliff:g id="NUMBER_2">%d</xliff:g> วินาที"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"คุณพยายามปลดล็อกแท็บเล็ตอย่างไม่ถูกต้องแล้ว <xliff:g id="NUMBER_0">%d</xliff:g> ครั้ง หากการพยายามไม่สำเร็จอีก <xliff:g id="NUMBER_1">%d</xliff:g> ครั้ง แท็บเล็ตจะถูกรีเซ็ตเป็นค่าเริ่มต้นจากโรงงานและข้อมูลทั้งหมดของผู้ใช้จะหายไป"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"คุณพยายามปลดล็อกทีวีอย่างไม่ถูกต้อง <xliff:g id="NUMBER_0">%d</xliff:g> ครั้ง หากปลดล็อกไม่ถูกต้องอีก <xliff:g id="NUMBER_1">%d</xliff:g> ครั้ง ทีวีจะรีเซ็ตเป็นค่าเริ่มต้นจากโรงงาน และข้อมูลผู้ใช้ทั้งหมดจะหายไป"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"คุณพยายามปลดล็อกโทรศัพท์อย่างไม่ถูกต้องแล้ว <xliff:g id="NUMBER_0">%d</xliff:g> ครั้ง หากการพยายามไม่สำเร็จอีก <xliff:g id="NUMBER_1">%d</xliff:g> ครั้ง โทรศัพท์จะถูกรีเซ็ตเป็นค่าเริ่มต้นจากโรงงานและข้อมูลทั้งหมดของผู้ใช้จะหายไป"</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"คุณวาดรูปแบบการปลดล็อกไม่ถูกต้องไป <xliff:g id="NUMBER_0">%1$d</xliff:g> ครั้งแล้ว\n\nโปรดลองอีกครั้งในอีก <xliff:g id="NUMBER_1">%2$d</xliff:g> วินาที"</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"คุณพิมพ์รหัสผ่านไม่ถูกต้องไป <xliff:g id="NUMBER_0">%1$d</xliff:g> ครั้งแล้ว\n\nลองอีกครั้งใน <xliff:g id="NUMBER_1">%2$d</xliff:g> วินาที"</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"คุณพิมพ์ PIN ไม่ถูกต้องไป <xliff:g id="NUMBER_0">%1$d</xliff:g> ครั้งแล้ว\n\nลองอีกครั้งใน <xliff:g id="NUMBER_1">%2$d</xliff:g> วินาที"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"คุณวาดรูปแบบการปลดล็อกไม่ถูกต้องไป <xliff:g id="NUMBER_0">%1$d</xliff:g> ครั้งแล้ว หากทำไม่สำเร็จอีก <xliff:g id="NUMBER_1">%2$d</xliff:g> ครั้ง ระบบจะขอให้คุณปลดล็อกแท็บเล็ตโดยใช้การลงชื่อเข้าใช้ Google\n\nโปรดลองอีกครั้งในอีก <xliff:g id="NUMBER_2">%3$d</xliff:g> วินาที"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"คุณวาดรูปแบบการปลดล็อกไม่ถูกต้อง <xliff:g id="NUMBER_0">%1$d</xliff:g> ครั้ง หากวาดไม่ถูกต้องอีก <xliff:g id="NUMBER_1">%2$d</xliff:g> ครั้ง ระบบจะขอให้คุณปลดล็อกทีวีโดยการลงชื่อเช้าใช้ Google\n\n โปรดลองอีกครั้งใน <xliff:g id="NUMBER_2">%3$d</xliff:g> วินาที"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"คุณวาดรูปแบบการปลดล็อกไม่ถูกต้อง <xliff:g id="NUMBER_0">%1$d</xliff:g> ครั้ง หากทำไม่สำเร็จอีก <xliff:g id="NUMBER_1">%2$d</xliff:g> ครั้ง ระบบจะขอให้คุณปลดล็อกโทรศัพท์โดยใช้การลงชื่อเข้าใช้ Google\n\nโปรดลองอีกครั้งในอีก <xliff:g id="NUMBER_2">%3$d</xliff:g> วินาที"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"คุณพยายามปลดล็อกแท็บเล็ตอย่างไม่ถูกต้องแล้ว <xliff:g id="NUMBER_0">%1$d</xliff:g> ครั้ง หากการพยายามไม่สำเร็จอีก <xliff:g id="NUMBER_1">%2$d</xliff:g> ครั้ง แท็บเล็ตจะถูกรีเซ็ตเป็นค่าเริ่มต้นจากโรงงานและข้อมูลทั้งหมดของผู้ใช้จะหายไป"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"คุณพยายามปลดล็อกทีวีอย่างไม่ถูกต้อง <xliff:g id="NUMBER_0">%1$d</xliff:g> ครั้ง หากปลดล็อกไม่ถูกต้องอีก <xliff:g id="NUMBER_1">%2$d</xliff:g> ครั้ง ทีวีจะรีเซ็ตเป็นค่าเริ่มต้นจากโรงงาน และข้อมูลผู้ใช้ทั้งหมดจะหายไป"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"คุณพยายามปลดล็อกโทรศัพท์อย่างไม่ถูกต้องแล้ว <xliff:g id="NUMBER_0">%1$d</xliff:g> ครั้ง หากการพยายามไม่สำเร็จอีก <xliff:g id="NUMBER_1">%2$d</xliff:g> ครั้ง โทรศัพท์จะถูกรีเซ็ตเป็นค่าเริ่มต้นจากโรงงานและข้อมูลทั้งหมดของผู้ใช้จะหายไป"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"คุณพยายามปลดล็อกแท็บเล็ตอย่างไม่ถูกต้องแล้ว <xliff:g id="NUMBER">%d</xliff:g> ครั้ง ขณะนี้แท็บเล็ตจะถูกรีเซ็ตเป็นค่าเริ่มต้นจากโรงงาน"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"คุณได้พยายามปลดล็อกทีวีอย่างไม่ถูกต้อง <xliff:g id="NUMBER">%d</xliff:g> ครั้ง ตอนนี้ทีวีจะรีเซ็ตเป็นค่าเริ่มต้นจากโรงงาน"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"คุณพยายามปลดล็อกโทรศัพท์อย่างไม่ถูกต้องแล้ว <xliff:g id="NUMBER">%d</xliff:g> ครั้ง ขณะนี้โทรศัพท์จะถูกรีเซ็ตเป็นค่าเริ่มต้นจากโรงงาน"</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"คำ"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"ลิงก์"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"บรรทัด"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"การทดสอบจากโรงงานล้มเหลว"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"การทำงาน FACTORY_TEST ได้รับการสนับสนุนเฉพาะสำหรับแพ็คเก็จที่ติดตั้งใน /system/app เท่านั้น"</string>
<string name="factorytest_no_action" msgid="872991874799998561">"ไม่พบแพ็กเกจที่มีการทำงาน FACTORY_TEST"</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"กำลังฟอร์แมต…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"ไม่ได้ใส่ไว้"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"ไม่พบกิจกรรมที่ตรงกัน"</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"กำหนดเส้นทางเอาต์พุตของสื่อ"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"กำหนดเส้นทางเอาต์พุตของสื่อ"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"อนุญาตให้แอปพลิเคชันกำหนดเส้นทางเอาต์พุตของสื่อไปยังอุปกรณ์ภายนอกอื่นๆ"</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"อ่านเซสชันการติดตั้ง"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"อ่านเซสชันการติดตั้ง"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"อนุญาตให้แอปพลิเคชันอ่านเซสชันการติดตั้ง ซึ่งจะอนุญาตให้อ่านรายละเอียดเกี่ยวกับการติดตั้งแพ็กเกจที่ใช้งาน"</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"ขอติดตั้งแพ็กเกจ"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"ขอติดตั้งแพ็กเกจ"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"อนุญาตให้แอปพลิเคชันขอการติดตั้งแพ็กเกจ"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"แตะสองครั้งเพื่อควบคุมการซูม"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"ไม่สามารถเพิ่มวิดเจ็ต"</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"ชื่อผู้ใช้หรือรหัสผ่านไม่ถูกต้อง"</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"หากลืมชื่อผู้ใช้หรือรหัสผ่าน\nโปรดไปที่ "<b>"google.com/accounts/recovery"</b></string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"กำลังตรวจสอบบัญชี…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"คุณพิมพ์ PIN ไม่ถูกต้องไป <xliff:g id="NUMBER_0">%d</xliff:g> ครั้งแล้ว \n\nโปรดลองอีกครั้งใน <xliff:g id="NUMBER_1">%d</xliff:g> วินาที"</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"คุณพิมพ์รหัสผ่านไม่ถูกต้องไป <xliff:g id="NUMBER_0">%d</xliff:g> ครั้งแล้ว \n\nโปรดลองอีกครั้งใน <xliff:g id="NUMBER_1">%d</xliff:g> วินาที"</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"คุณวาดรูปแบบการปลดล็อกไม่ถูกต้องไป <xliff:g id="NUMBER_0">%d</xliff:g> ครั้งแล้ว \n\nโปรดลองอีกครั้งใน <xliff:g id="NUMBER_1">%d</xliff:g> วินาที"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"คุณพยายามปลดล็อกแท็บเล็ตอย่างไม่ถูกต้อง <xliff:g id="NUMBER_0">%d</xliff:g> ครั้งแล้ว หากพยายามแล้วไม่สำเร็จอีก <xliff:g id="NUMBER_1">%d</xliff:g> ครั้ง แท็บเล็ตจะถูกรีเซ็ตเป็นค่าเริ่มต้นจากโรงงานและข้อมูลผู้ใช้ทั้งหมดจะหายไป"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"คุณพยายามปลดล็อกทีวีอย่างไม่ถูกต้อง <xliff:g id="NUMBER_0">%d</xliff:g> ครั้ง หากปลดล็อกไม่ถูกต้องอีก <xliff:g id="NUMBER_1">%d</xliff:g> ครั้ง ทีวีจะรีเซ็ตเป็นค่าเริ่มต้นจากโรงงาน และข้อมูลผู้ใช้ทั้งหมดจะหายไป"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"คุณพยายามปลดล็อกโทรศัพท์อย่างไม่ถูกต้อง <xliff:g id="NUMBER_0">%d</xliff:g> ครั้งแล้ว หากพยายามแล้วไม่สำเร็จอีก <xliff:g id="NUMBER_1">%d</xliff:g> ครั้ง โทรศัพท์จะถูกรีเซ็ตเป็นค่าเริ่มต้นจากโรงงานและข้อมูลผู้ใช้ทั้งหมดจะหายไป"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"คุณพิมพ์ PIN ไม่ถูกต้องไป <xliff:g id="NUMBER_0">%1$d</xliff:g> ครั้งแล้ว \n\nโปรดลองอีกครั้งใน <xliff:g id="NUMBER_1">%2$d</xliff:g> วินาที"</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"คุณพิมพ์รหัสผ่านไม่ถูกต้องไป <xliff:g id="NUMBER_0">%1$d</xliff:g> ครั้งแล้ว \n\nโปรดลองอีกครั้งใน <xliff:g id="NUMBER_1">%2$d</xliff:g> วินาที"</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"คุณวาดรูปแบบการปลดล็อกไม่ถูกต้องไป <xliff:g id="NUMBER_0">%1$d</xliff:g> ครั้งแล้ว \n\nโปรดลองอีกครั้งใน <xliff:g id="NUMBER_1">%2$d</xliff:g> วินาที"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"คุณพยายามปลดล็อกแท็บเล็ตอย่างไม่ถูกต้อง <xliff:g id="NUMBER_0">%1$d</xliff:g> ครั้งแล้ว หากพยายามแล้วไม่สำเร็จอีก <xliff:g id="NUMBER_1">%2$d</xliff:g> ครั้ง แท็บเล็ตจะถูกรีเซ็ตเป็นค่าเริ่มต้นจากโรงงานและข้อมูลผู้ใช้ทั้งหมดจะหายไป"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"คุณพยายามปลดล็อกทีวีอย่างไม่ถูกต้อง <xliff:g id="NUMBER_0">%1$d</xliff:g> ครั้ง หากปลดล็อกไม่ถูกต้องอีก <xliff:g id="NUMBER_1">%2$d</xliff:g> ครั้ง ทีวีจะรีเซ็ตเป็นค่าเริ่มต้นจากโรงงาน และข้อมูลผู้ใช้ทั้งหมดจะหายไป"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"คุณพยายามปลดล็อกโทรศัพท์อย่างไม่ถูกต้อง <xliff:g id="NUMBER_0">%1$d</xliff:g> ครั้งแล้ว หากพยายามแล้วไม่สำเร็จอีก <xliff:g id="NUMBER_1">%2$d</xliff:g> ครั้ง โทรศัพท์จะถูกรีเซ็ตเป็นค่าเริ่มต้นจากโรงงานและข้อมูลผู้ใช้ทั้งหมดจะหายไป"</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"คุณพยายามปลดล็อกแท็บเล็ตอย่างไม่ถูกต้อง <xliff:g id="NUMBER">%d</xliff:g> ครั้งแล้ว ขณะนี้แท็บเล็ตจะถูกรีเซ็ตเป็นค่าเริ่มต้นจากโรงงาน"</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"คุณได้พยายามปลดล็อกทีวีอย่างไม่ถูกต้อง <xliff:g id="NUMBER">%d</xliff:g> ครั้ง ตอนนี้ทีวีจะรีเซ็ตเป็นค่าเริ่มต้นจากโรงงาน"</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"คุณพยายามปลดล็อกโทรศัพท์อย่างไม่ถูกต้อง <xliff:g id="NUMBER">%d</xliff:g> ครั้งแล้ว ขณะนี้โทรศัพท์จะถูกรีเซ็ตเป็นค่าเริ่มต้นจากโรงงาน"</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"คุณวาดรูปแบบการปลดล็อกไม่ถูกต้อง <xliff:g id="NUMBER_0">%d</xliff:g> ครั้งแล้ว หากทำไม่สำเร็จอีก <xliff:g id="NUMBER_1">%d</xliff:g> ครั้ง ระบบจะขอให้คุณปลดล็อกแท็บเล็ตโดยใช้บัญชีอีเมล\n\n โปรดลองอีกครั้งใน <xliff:g id="NUMBER_2">%d</xliff:g> วินาที"</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"คุณวาดรูปแบบการปลดล็อกไม่ถูกต้อง <xliff:g id="NUMBER_0">%d</xliff:g> ครั้ง หากวาดไม่ถูกต้องอีก <xliff:g id="NUMBER_1">%d</xliff:g> ครั้ง ระบบจะขอให้คุณปลดล็อกทีวีด้วยบัญชีอีเมล\n\n ลองอีกครั้งใน <xliff:g id="NUMBER_2">%d</xliff:g> วินาที"</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"คุณวาดรูปแบบการปลดล็อกไม่ถูกต้อง <xliff:g id="NUMBER_0">%d</xliff:g> ครั้งแล้ว หากทำไม่สำเร็จอีก <xliff:g id="NUMBER_1">%d</xliff:g> ครั้ง ระบบจะขอให้คุณปลดล็อกโทรศัพท์โดยใช้ับัญชีอีเมล\n\n โปรดลองอีกครั้งในอีก <xliff:g id="NUMBER_2">%d</xliff:g> วินาที"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"คุณวาดรูปแบบการปลดล็อกไม่ถูกต้อง <xliff:g id="NUMBER_0">%1$d</xliff:g> ครั้งแล้ว หากทำไม่สำเร็จอีก <xliff:g id="NUMBER_1">%2$d</xliff:g> ครั้ง ระบบจะขอให้คุณปลดล็อกแท็บเล็ตโดยใช้บัญชีอีเมล\n\n โปรดลองอีกครั้งใน <xliff:g id="NUMBER_2">%3$d</xliff:g> วินาที"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"คุณวาดรูปแบบการปลดล็อกไม่ถูกต้อง <xliff:g id="NUMBER_0">%1$d</xliff:g> ครั้ง หากวาดไม่ถูกต้องอีก <xliff:g id="NUMBER_1">%2$d</xliff:g> ครั้ง ระบบจะขอให้คุณปลดล็อกทีวีด้วยบัญชีอีเมล\n\n ลองอีกครั้งใน <xliff:g id="NUMBER_2">%3$d</xliff:g> วินาที"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"คุณวาดรูปแบบการปลดล็อกไม่ถูกต้อง <xliff:g id="NUMBER_0">%1$d</xliff:g> ครั้งแล้ว หากทำไม่สำเร็จอีก <xliff:g id="NUMBER_1">%2$d</xliff:g> ครั้ง ระบบจะขอให้คุณปลดล็อกโทรศัพท์โดยใช้ับัญชีอีเมล\n\n โปรดลองอีกครั้งในอีก <xliff:g id="NUMBER_2">%3$d</xliff:g> วินาที"</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"ลบ"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"นี่เป็นการเพิ่มระดับเสียงเกินระดับที่แนะนำ\n\nการฟังเสียงดังเป็นเวลานานอาจทำให้การได้ยินของคุณบกพร่องได้"</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">เป็นเวลา 1 ชม.</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"จนถึงเวลา <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"จนถึงเวลา <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (การปลุกครั้งถัดไป)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"จนกว่าคุณจะปิดฟังก์ชันนี้"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"จนกว่าคุณจะปิดห้ามรบกวน"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-tl-watch/strings.xml b/core/res/res/values-tl-watch/strings.xml
index c38891e..e15d77d 100644
--- a/core/res/res/values-tl-watch/strings.xml
+++ b/core/res/res/values-tl-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"App <xliff:g id="NUMBER_0">%1$d</xliff:g> ng <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Mga Sensor"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"i-access ang iyong mga contact"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"i-access ang lokasyon ng relong ito"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"i-access ang iyong kalendaryo"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"magpadala at tumingin ng mga mensaheng SMS"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"i-access ang mga larawan, media at mga file sa iyong relo"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"mag-record ng audio"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"kumuha ng mga larawan at mag-record ng video"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"tumawag sa telepono at mamahala ng mga tawag sa telepono"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"i-access ang data ng sensor tungkol sa iyong mahahalagang senyales"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"maging status bar"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"i-access ang mga sensor sa katawan (tulad ng mga monitor ng bilis ng tibok ng puso)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"i-access ang tumpak na lokasyon (batay sa GPS at network)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"i-access ang tinatantyang lokasyon (batay sa network)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"magpadala ng mga command sa SIM"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"magkaroon ng ganap na access sa network"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"pamahalaan ang mga may-ari ng profile at device"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"baguhin ang status ng WiMAX"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"tanggapin ang status ng paglilipat ng Android Beam"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"iruta ang output ng media"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"basahin ang mga session ng pag-install"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"humiling ng mga package sa pag-install"</string>
</resources>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 78c9358c..8c6c37d 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"May kasamang personal na data tulad ng mga numero ng credit card at password."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"huwag paganahin o baguhin ang status bar"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Pinapayagan ang app na huwag paganahin ang status bar o magdagdag at mag-alis ng mga icon ng system."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"status bar"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"maging status bar"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Pinapayagan ang app na maging status bar."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"palawakin/tiklupin ang status bar"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Pinapayagan ang app na palawakin o tiklupin ang status bar."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Pinapayagan ang app na tumanggap at magproseso ng mga mensaheng WAP. Kabilang sa pahintulot na ito ang kakayahang sumubaybay o magtanggal ang app ng mga mensaheng ipinapadala sa iyo nang hindi ipinapakita ang mga ito sa iyo."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"bawiin ang tumatakbong apps"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Pinapayagan ang app na kumuha ng impormasyon tungkol sa mga kasalukuyan at kamakailang gumaganang gawain. Maaari nitong payagan ang app na tumuklas ng impormasyon tungkol sa kung aling mga application ang ginagamit sa device."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Pamahalaan ang mga may-ari ng profile at device"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"pamahalaan ang mga may-ari ng profile at device"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Nagbibigay-daan sa mga app na itakda ang mga may-ari ng profile at ang may-ari ng device."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"muling isaayos ang tumatakbong apps"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Pinapayagan ang app na maglipat ng mga gawain sa foreground at background. Maaari itong gawin ng app nang wala ng iyong input."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Binibigyan-daan ang app na baguhin ang log ng tawag ng iyong tablet, kabilang ang data tungkol sa mga paparating at papalabas na tawag. Maaari itong gamitin ng nakakahamak na apps upang burahin o baguhin ang iyong log ng tawag."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Binibigyan-daan ang app na baguhin ang log ng tawag ng iyong TV, kabilang ang data tungkol sa mga paparating at papalabas na tawag. Maaari itong gamitin ng mga nakakahamak na app upang burahin o baguhin ang iyong log ng tawag."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Binibigyan-daan ang app na baguhin ang log ng tawag ng iyong telepono, kabilang ang data tungkol sa mga paparating at papalabas na tawag. Maaari itong gamitin ng nakakahamak na apps upang burahin o baguhin ang iyong log ng tawag."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"mga sensor sa katawan (gaya ng mga heart rate monitor)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"i-access ang mga sensor sa katawan (tulad ng mga monitor ng bilis ng tibok ng puso)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Pinapayagan ang app na i-access ang data mula sa mga sensor na sumusubaybay sa iyong pisikal na kundisyon, tulad ng iyong heart rate."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"magbasa ng mga kaganapan sa kalendaryo kasama ang kumpedensyal na impormasyon"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Pinapayagan ang app na basahin ang lahat ng kaganapan sa kalendaryo na naka-imbak sa iyong tablet, kabilang iyong sa mga kaibigan o katrabaho. Maaari nitong payagan ang app na ibahagi o i-save ang data ng iyong kalendaryo, ano pa man ang katayuan ng pagiging kumpedensyal o sensitibo nito."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Pinapayagan ang app na magdagdag, mag-alis, magbago ng mga kaganapang maaari mong baguhin sa iyong telepono, kabilang iyong sa mga kaibigan o katrabaho. Maaari nitong payagan ang app na magpadala ng mga mensaheng lumililitaw na mula sa mga may-ari ng kalendaryo, o magbago ng mga kaganapan nang hindi nalalaman ng mga may-ari."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"i-access ang mga dagdag na command ng provider ng lokasyon"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Nagbibigay-daan sa app na mag-access ng mga karagdagang command ng provider ng lokasyon. Maaari nitong bigyang-daan ang app na gambalain ang pagpapatakbo ng GPS o ng iba pang mga pinagmulan ng lokasyon."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"tumpak na lokasyon (batay sa GPS at network)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"i-access ang tumpak na lokasyon (batay sa GPS at network)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Binibigyang-daan ang app na makuha ang iyong tumpak na lokasyon gamit ang Global Positioning System (GPS) o network ng mga pinagmulan ng lokasyon gaya ng mga cell tower at Wi-Fi. Dapat ay naka-on ang mga serbisyo ng lokasyon na ito at available sa iyong device upang magamit ng app ang mga ito. Maaaring gamitin ito ng apps upang matukoy kung nasaan ka, at maaaring gumamit ng karagdagang power ng baterya."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"tinatayang lokasyon (batay sa network)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"i-access ang tinatantyang lokasyon (batay sa network)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Binibigyang-daan ang app na makuha ang iyong tinatayang lokasyon. Ang lokasyong ito ay nagmula ng mga serbisyo ng lokasyon na gumagamit ng network ng mga pinagmulan ng lokasyon gaya ng mga cell tower at Wi-Fi. Dapat ay naka-on ang mga serbisyo ng lokasyon na ito at available sa iyong device upang magamit ng app ang mga ito. Maaaring gamitin ito ng apps upang matukoy ang tinatayang lokasyon na kinaroroonan mo."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"baguhin ang mga setting ng iyong audio"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Pinapayagan ang app na baguhin ang mga pandaigdigang setting ng audio gaya ng volume at kung aling speaker ang ginagamit para sa output."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"mag-record ng audio"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Pinapayagan ang app na mag-record ng audio gamit ang mikropono. Pinapayagan ng pahintulot na ito ang app na mag-record ng audio anumang oras nang wala ng iyong kumpirmasyon."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"pag-uusap sa sim"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"magpadala ng mga command sa SIM"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Pinapahintulutang magpadala ang app ng mga command sa SIM. Napakapanganib nito."</string>
<string name="permlab_camera" msgid="3616391919559751192">"kumuha ng mga larawan at video"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Pinapayagan ang app na kumuha ng mga larawan at video gamit ang camera. Pinapayagan ng pahintulot na ito ang app na gamitin ang camera anumang oras nang wala ng iyong kumpirmasyon."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Pinapayagan ang app na kunin ang listahan ng mga account na alam ng telepono. Maaari itong kabilangan ng anumang mga account na nililikha ng mga application na iyong na-install."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"tingnan ang mga koneksyon sa network"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Pinapayagan ang app na tumingin ng impormasyon tungkol sa mga koneksyon ng network gaya ng kung aling mga network ang umiiral at nakakonekta."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"ganap na access sa network"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"magkaroon ng ganap na access sa network"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Pinapayagan ang app na lumikha ng mga network socket at gumamit ng mga custom na network protocol. Nagbibigay ang browser at iba pang mga application ng mga paraan ng pagpapadala ng data sa internet, kaya hindi kinakailangan ang pahintulot na ito upang magpadala ng data sa internet."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"baguhin ang pagkakakonekta ng network"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Pinapayagan ang app na baguhin ang katayuan ng pagkakakonekta ng network."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Pinapayagan ang app na i-configure ang lokal na Bluetooth na telepono, at tumuklas ng at ipares sa mga malayuang device."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"kumonekta at magdiskonekta mula sa WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Pinapayagan ang app na tukuyin kung pinapagana ang WiMAX at impormasyon tungkol sa anumang mga WiMAX network na nakakonekta."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Baguhin ang katayuan ng WiMAX"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"baguhin ang katayuan ng WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Pinapayagan ang app na ikonekta ang tablet at idiskonekta ang tablet mula sa mga WiMAX network."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Nagbibigay-daan sa app na ikonekta ang TV sa at putulin ang koneksyon ng TV mula sa mga WiMAX network."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Pinapayagan ang app na ikonekta ang telepono at idiskonekta ang telepono mula sa mga WiMAX network."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Pinapayagan ang app na baguhin ang mga parameter sa pag-calibrate ng touch screen. Hindi dapat kailanganin sa normal na apps."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"access sa Mga DRM certificate"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Nagbibigay-daan sa isang application na makapagbigay at gumamit ng mga DRM certficate. Hindi dapat kailanman kailanganin para sa mga normal na app."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Tanggapin ang status ng paglilipat ng Android Beam"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"tanggapin ang status ng paglilipat ng Android Beam"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Pinapayagan ang application na ito na tumanggap ng impormasyon tungkol sa mga kasalukuyang paglilipat ng Android Beam"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"alisin ang mga DRM certificate"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Nagbibigay-daan sa isang application na alisin ang mga DRM certficate. Hindi dapat kailanman kailanganin para sa karaniwang apps."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Maling PIN code."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Upang i-unlock, pindutin ang Menu pagkatapos ay 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Pang-emergency na numero"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Walang serbisyo."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Walang serbisyo"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Naka-lock ang screen."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Pindutin ang Menu upang i-unlock o magsagawa ng pang-emergency na tawag."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Pindutin ang Menu upang i-unlock."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Tingnan ang Gabay ng User o makipag-ugnay sa Pangangalaga sa Customer."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"Naka-lock ang SIM card."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Ina-unlock ang SIM card…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Mali mong naguhit ang iyong pattern sa pag-unlock nang <xliff:g id="NUMBER_0">%d</xliff:g> (na) beses. \n\nSubukang muli sa loob ng <xliff:g id="NUMBER_1">%d</xliff:g> (na) segundo."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Na-type mo nang mali ang iyong password nang <xliff:g id="NUMBER_0">%d</xliff:g> (na) beses. \n\nSubukang muli sa loob ng <xliff:g id="NUMBER_1">%d</xliff:g> (na) segundo."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Na-type mo nang mali ang iyong PIN nang <xliff:g id="NUMBER_0">%d</xliff:g> (na) beses. \n\nSubukang muli sa loob ng <xliff:g id="NUMBER_1">%d</xliff:g> (na) segundo."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Naiguhit mo nang hindi tama ang iyong pattern sa pag-unlock nang <xliff:g id="NUMBER_0">%d</xliff:g> (na) beses. Pagkatapos ng <xliff:g id="NUMBER_1">%d</xliff:g> pang hindi matagumpay na pagtatangka, hihilingin sa iyong i-unlock ang iyong tablet gamit ang iyong pag-sign-in sa Google.\n\n Subukang muli sa loob ng <xliff:g id="NUMBER_2">%d</xliff:g> (na) segundo."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Naiguhit mo nang mali ang iyong pattern sa pag-unlock nang <xliff:g id="NUMBER_0">%d</xliff:g> (na) beses. Pagkatapos ng <xliff:g id="NUMBER_1">%d</xliff:g> pang hindi matagumpay na pagtatangka, sasabihan kang i-unlock ang iyong TV sa pamamagitan ng pag-sign in sa Google.\n\n Subukang muli sa <xliff:g id="NUMBER_2">%d</xliff:g> (na) segundo."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Naguhit mo nang mali ang iyong pattern sa pag-unlock nang <xliff:g id="NUMBER_0">%d</xliff:g> (na) beses. Pagkatapos ng <xliff:g id="NUMBER_1">%d</xliff:g> pang hindi matagumpay na pagtatangka, hihilingin sa iyong i-unlock ang iyong telepono gamit ang iyong pag-sign-in sa Google.\n\n Subukang muli sa loob ng <xliff:g id="NUMBER_2">%d</xliff:g> (na) segundo."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Mali mong tinangkang ma-unlock ang tablet nang <xliff:g id="NUMBER_0">%d</xliff:g> (na) beses. Pagkatapos ng <xliff:g id="NUMBER_1">%d</xliff:g> pang mga hindi matagumpay na pagtatangka, mare-reset ang tablet sa factory default at mawawala ang lahat ng data ng user."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Mali nang <xliff:g id="NUMBER_0">%d</xliff:g> (na) beses ang iyong pagtatangkang i-unlock ang TV. Pagkatapos ng <xliff:g id="NUMBER_1">%d</xliff:g> pang hindi matagumpay na pagtatangka, mare-reset sa default ng factory ang TV at mawawala ang lahat ng data ng user."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Mali mong tinangkang ma-unlock ang telepono nang <xliff:g id="NUMBER_0">%d</xliff:g> (na) beses. Pagkatapos ng <xliff:g id="NUMBER_1">%d</xliff:g> pang mga hindi matagumpay na pagtatangka, mare-reset ang telepono sa factory default at mawawala ang lahat ng data ng user."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Mali mong naguhit ang iyong pattern sa pag-unlock nang <xliff:g id="NUMBER_0">%1$d</xliff:g> (na) beses. \n\nSubukang muli sa loob ng <xliff:g id="NUMBER_1">%2$d</xliff:g> (na) segundo."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Na-type mo nang mali ang iyong password nang <xliff:g id="NUMBER_0">%1$d</xliff:g> (na) beses. \n\nSubukang muli sa loob ng <xliff:g id="NUMBER_1">%2$d</xliff:g> (na) segundo."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Na-type mo nang mali ang iyong PIN nang <xliff:g id="NUMBER_0">%1$d</xliff:g> (na) beses. \n\nSubukang muli sa loob ng <xliff:g id="NUMBER_1">%2$d</xliff:g> (na) segundo."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Naiguhit mo nang hindi tama ang iyong pattern sa pag-unlock nang <xliff:g id="NUMBER_0">%1$d</xliff:g> (na) beses. Pagkatapos ng <xliff:g id="NUMBER_1">%2$d</xliff:g> pang hindi matagumpay na pagtatangka, hihilingin sa iyong i-unlock ang iyong tablet gamit ang iyong pag-sign-in sa Google.\n\n Subukang muli sa loob ng <xliff:g id="NUMBER_2">%3$d</xliff:g> (na) segundo."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Naiguhit mo nang mali ang iyong pattern sa pag-unlock nang <xliff:g id="NUMBER_0">%1$d</xliff:g> (na) beses. Pagkatapos ng <xliff:g id="NUMBER_1">%2$d</xliff:g> pang hindi matagumpay na pagtatangka, sasabihan kang i-unlock ang iyong TV sa pamamagitan ng pag-sign in sa Google.\n\n Subukang muli sa <xliff:g id="NUMBER_2">%3$d</xliff:g> (na) segundo."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Naguhit mo nang mali ang iyong pattern sa pag-unlock nang <xliff:g id="NUMBER_0">%1$d</xliff:g> (na) beses. Pagkatapos ng <xliff:g id="NUMBER_1">%2$d</xliff:g> pang hindi matagumpay na pagtatangka, hihilingin sa iyong i-unlock ang iyong telepono gamit ang iyong pag-sign-in sa Google.\n\n Subukang muli sa loob ng <xliff:g id="NUMBER_2">%3$d</xliff:g> (na) segundo."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Mali mong tinangkang ma-unlock ang tablet nang <xliff:g id="NUMBER_0">%1$d</xliff:g> (na) beses. Pagkatapos ng <xliff:g id="NUMBER_1">%2$d</xliff:g> pang mga hindi matagumpay na pagtatangka, mare-reset ang tablet sa factory default at mawawala ang lahat ng data ng user."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Mali nang <xliff:g id="NUMBER_0">%1$d</xliff:g> (na) beses ang iyong pagtatangkang i-unlock ang TV. Pagkatapos ng <xliff:g id="NUMBER_1">%2$d</xliff:g> pang hindi matagumpay na pagtatangka, mare-reset sa default ng factory ang TV at mawawala ang lahat ng data ng user."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Mali mong tinangkang ma-unlock ang telepono nang <xliff:g id="NUMBER_0">%1$d</xliff:g> (na) beses. Pagkatapos ng <xliff:g id="NUMBER_1">%2$d</xliff:g> pang mga hindi matagumpay na pagtatangka, mare-reset ang telepono sa factory default at mawawala ang lahat ng data ng user."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Mali mong tinangkang ma-unlock ang tablet nang <xliff:g id="NUMBER">%d</xliff:g> (na) beses. Mare-reset na ngayon ang tablet sa factory default."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Mali nang <xliff:g id="NUMBER">%d</xliff:g> (na) beses ang iyong pagtatangkang i-unlock ang TV. Mare-reset na ngayon ang TV sa default ng factory."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Mali mong tinangkang ma-unlock ang telepono nang <xliff:g id="NUMBER">%d</xliff:g> (na) beses. Mare-reset na ang telepono sa factory default."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"salita"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"link"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"linya"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Nabigo ang factory na pagsubok"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Suportado lang ang pagkilos na FACTORY_TEST para sa mga package na naka-install sa /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Walang nakitang package na nagbibigay ng pagkilos na FACTORY_TEST."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Fino-format…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Hindi nakapasok"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Walang nahanap na mga tumutugmang aktibidad."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"I-route ang output ng media"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"iruta ang output ng media"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Pinapayagan ang application na mag-route ng output ng media sa iba pang mga panlabas na device."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Basahin ang mga session ng pag-install"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"basahin ang mga session ng pag-install"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Pinapayagan ang isang application na magbasa ng mga session ng pag-install. Nagbibigay-daan ito upang makita ang mga detalye tungkol sa mga aktibong pag-install ng package."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Humiling ng mga package sa pag-install"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"humiling ng mga package sa pag-install"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Pinapayagan ang isang application na hilingin ang pag-install ng mga package."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Pindutin nang dalawang beses para sa pagkontrol ng zoom"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Hindi maidagdag ang widget."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Di-wastong username o password."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Nakalimutan ang iyong username o password?\nBisitahin ang "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Tinitingnan ang account…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Na-type mo nang hindi tama ang iyong PIN nang <xliff:g id="NUMBER_0">%d</xliff:g> (na) beses. \n\nSubukang muli sa loob ng <xliff:g id="NUMBER_1">%d</xliff:g> (na) segundo."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Na-type mo nang hindi tama ang iyong password nang <xliff:g id="NUMBER_0">%d</xliff:g> (na) beses. \n\nSubukang muli sa loob ng <xliff:g id="NUMBER_1">%d</xliff:g> (na) segundo."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Naguhit mo nang hindi tama ang iyong pattern sa pag-unlock nang <xliff:g id="NUMBER_0">%d</xliff:g> (na) beses. \n\nSubukang muli sa loob ng <xliff:g id="NUMBER_1">%d</xliff:g> (na) segundo."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Tinangka mo sa hindi tamang paraan na i-unlock ang tabelt nang <xliff:g id="NUMBER_0">%d</xliff:g> (na) beses. Pagkatapos ng <xliff:g id="NUMBER_1">%d</xliff:g> pang hindi matagumpay na pagtatangka, ire-reset ang tablet sa factory default at mawawala ang lahat ng data ng user."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Mali nang <xliff:g id="NUMBER_0">%d</xliff:g> (na) beses ang iyong pagtatangkang i-unlock ang TV. Pagkatapos ng <xliff:g id="NUMBER_1">%d</xliff:g> pang hindi matagumpay na pagtatangka, mare-reset sa default ng factory ang TV at mawawala ang lahat ng data ng user."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Tinangka mo sa hindi tamang paraan na i-unlock ang telepono nang <xliff:g id="NUMBER_0">%d</xliff:g> (na) beses. Pagkatapos ng <xliff:g id="NUMBER_1">%d</xliff:g> pang hindi matagumpay na pagtatangka, ire-reset ang telepono sa factory default at mawawala ang lahat ng data ng user."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Na-type mo nang hindi tama ang iyong PIN nang <xliff:g id="NUMBER_0">%1$d</xliff:g> (na) beses. \n\nSubukang muli sa loob ng <xliff:g id="NUMBER_1">%2$d</xliff:g> (na) segundo."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Na-type mo nang hindi tama ang iyong password nang <xliff:g id="NUMBER_0">%1$d</xliff:g> (na) beses. \n\nSubukang muli sa loob ng <xliff:g id="NUMBER_1">%2$d</xliff:g> (na) segundo."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Naguhit mo nang hindi tama ang iyong pattern sa pag-unlock nang <xliff:g id="NUMBER_0">%1$d</xliff:g> (na) beses. \n\nSubukang muli sa loob ng <xliff:g id="NUMBER_1">%2$d</xliff:g> (na) segundo."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Tinangka mo sa hindi tamang paraan na i-unlock ang tabelt nang <xliff:g id="NUMBER_0">%1$d</xliff:g> (na) beses. Pagkatapos ng <xliff:g id="NUMBER_1">%2$d</xliff:g> pang hindi matagumpay na pagtatangka, ire-reset ang tablet sa factory default at mawawala ang lahat ng data ng user."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Mali nang <xliff:g id="NUMBER_0">%1$d</xliff:g> (na) beses ang iyong pagtatangkang i-unlock ang TV. Pagkatapos ng <xliff:g id="NUMBER_1">%2$d</xliff:g> pang hindi matagumpay na pagtatangka, mare-reset sa default ng factory ang TV at mawawala ang lahat ng data ng user."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Tinangka mo sa hindi tamang paraan na i-unlock ang telepono nang <xliff:g id="NUMBER_0">%1$d</xliff:g> (na) beses. Pagkatapos ng <xliff:g id="NUMBER_1">%2$d</xliff:g> pang hindi matagumpay na pagtatangka, ire-reset ang telepono sa factory default at mawawala ang lahat ng data ng user."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Tinangka mo sa hindi tamang paraan na i-unlock ang tablet nang <xliff:g id="NUMBER">%d</xliff:g> (na) beses. Ire-reset na ngayon ang tablet sa factory default."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Mali nang <xliff:g id="NUMBER">%d</xliff:g> (na) beses ang iyong pagtatangkang i-unlock ang TV. Mare-reset na ngayon ang TV sa default ng factory."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Tinangka mo sa hindi tamang paraan na i-unlock ang telepono nang <xliff:g id="NUMBER">%d</xliff:g> (na) beses. Ire-reset na ngayon ang telepono sa factory default."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Naguhit mo nang hindi tama ang iyong pattern sa pag-unlock nang <xliff:g id="NUMBER_0">%d</xliff:g> (na) beses. Pagkatapos ng <xliff:g id="NUMBER_1">%d</xliff:g> pang hindi matagumpay na pagtatangka, hihilingin sa iyong i-unlock ang tablet mo gamit ang isang email account.\n\n Subukang muli sa loob ng <xliff:g id="NUMBER_2">%d</xliff:g> (na) segundo."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Naiguhit mo nang mali ang iyong pattern sa pag-unlock nang <xliff:g id="NUMBER_0">%d</xliff:g> (na) beses. Pagkatapos ng <xliff:g id="NUMBER_1">%d</xliff:g> pang hindi matagumpay na pagtatangka, sasabihan kang i-unlock ang iyong TV gamit ang isang email account.\n\n Subukang muli sa <xliff:g id="NUMBER_2">%d</xliff:g> (na) segundo."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Naguhit mo nang hindi tama ang iyong pattern sa pag-unlock nang <xliff:g id="NUMBER_0">%d</xliff:g> (na) beses. Pagkatapos ng <xliff:g id="NUMBER_1">%d</xliff:g> pang hindi matagumpay na pagtatangka, hihilingin sa iyong i-unlock ang telepono mo gamit ang isang email account.\n\n Subukang muli sa loob ng <xliff:g id="NUMBER_2">%d</xliff:g> (na) segundo."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Naguhit mo nang hindi tama ang iyong pattern sa pag-unlock nang <xliff:g id="NUMBER_0">%1$d</xliff:g> (na) beses. Pagkatapos ng <xliff:g id="NUMBER_1">%2$d</xliff:g> pang hindi matagumpay na pagtatangka, hihilingin sa iyong i-unlock ang tablet mo gamit ang isang email account.\n\n Subukang muli sa loob ng <xliff:g id="NUMBER_2">%3$d</xliff:g> (na) segundo."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Naiguhit mo nang mali ang iyong pattern sa pag-unlock nang <xliff:g id="NUMBER_0">%1$d</xliff:g> (na) beses. Pagkatapos ng <xliff:g id="NUMBER_1">%2$d</xliff:g> pang hindi matagumpay na pagtatangka, sasabihan kang i-unlock ang iyong TV gamit ang isang email account.\n\n Subukang muli sa <xliff:g id="NUMBER_2">%3$d</xliff:g> (na) segundo."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Naguhit mo nang hindi tama ang iyong pattern sa pag-unlock nang <xliff:g id="NUMBER_0">%1$d</xliff:g> (na) beses. Pagkatapos ng <xliff:g id="NUMBER_1">%2$d</xliff:g> pang hindi matagumpay na pagtatangka, hihilingin sa iyong i-unlock ang telepono mo gamit ang isang email account.\n\n Subukang muli sa loob ng <xliff:g id="NUMBER_2">%3$d</xliff:g> (na) segundo."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Alisin"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Lakasan ang volume nang lagpas sa inirerekomendang antas?\n\nMaaaring mapinsala ng pakikinig sa malakas na volume sa loob ng mahahabang panahon ang iyong pandinig."</string>
@@ -1470,6 +1468,7 @@
<item quantity="other">Sa loob ng %d na oras</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Hanggang <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Hanggang <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (susunod na alarm)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Hanggang sa i-off mo ito"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Hanggang sa i-off mo ang Huwag Istorbohin"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-tr-watch/strings.xml b/core/res/res/values-tr-watch/strings.xml
index 4dbf664..665268a 100644
--- a/core/res/res/values-tr-watch/strings.xml
+++ b/core/res/res/values-tr-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Uygulama <xliff:g id="NUMBER_0">%1$d</xliff:g> / <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Sensörler"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"kişilerinize erişme"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"bu saatin konum bilgilerine erişme"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"takviminize erişme"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"SMS iletileri gönderme ve görüntüleme"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"saatinizdeki fotoğraflara, medyaya ve dosyalara erişme"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"ses kaydetme"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"fotoğraf çekme ve video kaydetme"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"telefon aramaları yapma ve çağrıları yönetme"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"hayati belirtilerinizle ilgili sensör verilerine erişme"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"durum çubuğunda olma"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"vücut sensörlerine erişme (nabız takip cihazları gibi)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"konumunuza hassas olarak erişme (GPS ve ağ tabanlı)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"konumunuza yaklaşık olarak erişme (ağ tabanlı)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"SIM karta komut gönderme"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"tam ağ erişimine sahip olma"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"profili ve cihaz sahiplerini yönetme"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"WiMAX durumunu değiştirme"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"Android Beam aktarım durumunu alma"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"medya çıkışını yönlendirme"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"yükleme oturumlarını okuma"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"paketleri yükleme isteğinde bulunma"</string>
</resources>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index bf046e3..20f6191 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Kredi kartı ve şifre gibi kişisel bilgiler içerir."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"durum çubuğunu devre dışı bırak veya değiştir"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Uygulamaya, durum çubuğunu devre dışı bırakma ve sistem simgelerini ekleyip kaldırma izni verir."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"durum çubuğu"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"durum çubuğunda olma"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Uygulamaya, durum çubuğu olma izni verir."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"durum çubuğunu genişlet/daralt"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Uygulamaya, durum çubuğunu genişletip daraltma izni verir."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Uygulamaya WAP mesajlarını alma ve işleme izni verir. Buna, size gönderilen mesajları takip edip size göstermeden silebilme izni de dahildir."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"çalışan uygulamaları al"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Uygulamaya o anda ve son çalışan görevler hakkında bilgi alma izni verir. Bu izin, uygulamanın cihaz tarafından kullanılan uygulamalar hakkında bilgi elde etmesine olanak sağlayabilir."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Profil ve cihaz sahiplerini yönetme"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"profili ve cihaz sahiplerini yönetme"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Uygulamaların, profil sahiplerini ve cihaz sahibini ayarlamasına izin verir."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"çalışan uygulamaları yeniden sırala"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Uygulamaya, görevleri ön plana ve arka plana taşıma izni verir. Uygulama bunu sizden bir giriş olmadan yapabilir."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Uygulamaya tabletinizin çağrı günlüğünde (gelen ve giden çağrılarla ilgili veriler dahil olmak üzere) değişiklik yapma izni verir. Kötü amaçlı uygulamalar bu izni kullanarak çağrı günlüğünüzü silebilir veya değiştirebilir."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Uygulamaya, tabletinizin çağrı kaydında (gelen ve giden çağrılarla ilgili veriler dahil olmak üzere) değişiklik yapma izni verir. Kötü amaçlı uygulamalar bu izni kullanarak çağrı kaydınızı silebilir veya değiştirebilir."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Uygulamaya telefonunuzun çağrı günlüğünde (gelen ve giden çağrılarla ilgili veriler dahil olmak üzere) değişiklik yapma izni verir. Kötü amaçlı uygulamalar bu izni kullanarak çağrı günlüğünüzü silebilir veya değiştirebilir."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"vücut sensörleri (kalp atış hızı takip cihazları gibi)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"vücut sensörlerine erişme (nabız takip cihazları gibi)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Uygulamanın, nabzınız gibi fiziksel durumunuzu izleyen sensörlerin gönderdiği verilere erişmesine izin verir."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"takvim etkinliklerini ve gizli bilgileri oku"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Uygulamaya, arkadaşlarınızın ve iş arkadaşlarınızın etkinlikleri de olmak üzere tabletinizde depolanan tüm takvim etkinliklerini okuma izni verir. Bu izin, uygulamanın takvim verilerinizi gizliliğine ve hassaslığına bakmaksızın paylaşmasına ve kaydetmesine olanak sağlayabilir."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Uygulamaya, arkadaşlarınızın veya iş arkadaşlarınızın etkinlikleri de dahil olmak üzere telefonunuzda değiştirebileceğiniz etkinlikleri ekleme, kaldırma ve değiştirme izni verir. Bu izin, uygulamanın takvim sahiplerinden geliyormuş gibi görünen iletiler göndermesine veya etkinlikleri sahibinden habersiz olarak değiştirmesine olanak sağlar."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"ek konum sağlayıcı komutlarına eriş"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Uygulamanın, ekstra konum sağlayıcı komutlarına erişmesine izin verir. Bu izin, uygulamanın GPS veya diğer konum kaynaklarının çalışmasını kesmesine olanak sağlayabilir."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"hassas konum (GPS ve ağ tabanlı)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"konum bilgilerine hassas olarak erişme (GPS ve ağ tabanlı)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Uygulamaya, Küresel Konumlandırma Sistemi (GPS) veya baz istasyonları ve Kablosuz bağlantı gibi ağ konum kaynaklarını kullanarak konumunuzu hassas bir şekilde belirleme izni verir. Uygulamaların bu konum hizmetlerini kullanabilmesi için bunların cihazınızda açık ve kullanılabilir olması gerekir. Uygulamalar bulunduğunuz yeri belirlemek için bu izni kullanabilirler ve bu kullanım fazladan pil tüketimine neden olabilir."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"yaklaşık konum (ağ tabanlı)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"konum bilgilerine yaklaşık olarak erişme (ağ tabanlı)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Uygulamaya, yaklaşık konumunuzu belirleme izni verir. Bu konum bilgisi, baz istasyonları ve Kablosuz bağlantı gibi ağ konum kaynaklarını kullanan konum hizmetlerinden elde edilir. Uygulamaların bu konum hizmetlerini kullanabilmesi için bunların cihazınızda açık ve kullanılabilir olması gerekir. Uygulamalar bulunduğunuz yeri yaklaşık olarak belirlemek için bu izni kullanabilirler."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"ses ayarlarınızı değiştirin"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Uygulamaya ses düzeyi ve ses çıkışı için kullanılan hoparlör gibi genel ses ayarlarını değiştirme izni verir."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"ses kaydet"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Uygulamaya mikrofonla ses kaydetme izni verir. Bu izin, uygulamanın istediği zaman onayınız olmadan ses kaydetmesine olanak sağlar."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"sim iletişimi"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"SIM karta komut gönderme"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Uygulamanın SIM karta komut göndermesine izin verir. Bu izin çok tehlikelidir."</string>
<string name="permlab_camera" msgid="3616391919559751192">"resim çekme ve görüntü kaydetme"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Uygulamaya kamerayla fotoğraf ve video çekme izni verir. Bu izin, uygulamanın sizin onayınız olmadan istediği zaman kamerayı kullanmasına olanak sağlar."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Uygulamaya telefon tarafından bilinen hesapların listesini alma izni verir. Bu liste, yüklediğiniz uygulamalar tarafından oluşturulan tüm hesapları içerebilir."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"ağ bağlantılarını görüntüleme"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Uygulamaya, hangi ağların bulunduğu ve hangilerinin bağlı olduğu gibi ağ bağlantılarıyla ilgili bilgileri görüntüleme izni verir."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"tam ağ erişimi"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"tam ağ erişimine sahip olma"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Uygulamaya ağ yuvaları oluşturma ve özel ağ protokolleri kullanma izni verir. Tarayıcı ve diğer uygulamalar İnternet\'e veri gönderilmesi için araç sağlarlar, bu nedenle bu izin, İnternet\'e veri göndermek için gerekli değildir."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"ağ bağlantısını değiştirme"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Uygulamaya, ağ bağlantısının durumunu değiştirme izni verir."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Uygulamaya, yerel Bluetooth telefonunu yapılandırma ve uzak cihazları keşfedip bunlarla eşleşme izni verir."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"WiMAX\'e bağlan veya WiMAX bağlantısını kes"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Uygulamaya, WiMAX\'in etkin olup olmadığını belirleme ve bağlı tüm WiMAX ağlarıyla ilgili bilgilere erişme izni verir."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX durumunu değiştir"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"WiMAX durumunu değiştir"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Uygulamaya, tableti WiMAX ağlarına bağlanma veya bağlantıyı kesme izni verir."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Uygulamaya TV\'yi WiMAX ağlarına bağlama ve TV\'nin WiMAX ağları ile bağlantısını kesme izni verir."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Uygulamaya, telefonu WiMAX ağlarına bağlanma veya bağlantıyı kesme izni verir."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Uygulamaya, dokunmatik ekranın kalibrasyon parametrelerini değiştirme izni verir. Normal uygulamalar için hiçbir zaman gerekmez."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"DRM sertifikalarına eriş"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Bir uygulamanın DRM sertifikaları için temel hazırlık yapmasına ve bunları kullanmasına izin verir. Normal uygulamalar için hiçbir zaman gerekmez."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Android Beam aktarım durumunu al"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"Android Beam aktarım durumunu alma"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Bu uygulamanın mevcut Android Beam aktarımlarıyla ilgili bilgi almasına izin verir"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"DRM sertifikalarını kaldırma"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Uygulamaya, DRM sertifikalarını kaldırma izin verir. Normal uygulamalar için asla gerekmemelidir."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Yanlış PIN kodu."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Kilidi açmak için önce Menü\'ye, sonra 0\'a basın."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Acil durum numarası"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Hizmet yok."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Servis yok"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Ekran kilitli."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Kilidi açmak veya acil çağrı yapmak için Menü\'ye basın."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Kilidi açmak için Menü\'ye basın."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Kullanıcı Rehberi\'ne bakın veya Müşteri Hizmetleri\'ne başvurun."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM kart kilitli."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"SIM kart kilidi açılıyor…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Kilit açma deseninizi <xliff:g id="NUMBER_0">%d</xliff:g> kez yanlış çizdiniz. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> saniye içinde tekrar deneyin."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Şifrenizi <xliff:g id="NUMBER_0">%d</xliff:g> kez yanlış yazdınız. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> saniye içinde tekrar deneyin."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"PIN kodunuzu <xliff:g id="NUMBER_0">%d</xliff:g> kez yanlış girdiniz. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> saniye içinde tekrar deneyin."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Kilit açma deseninizi <xliff:g id="NUMBER_0">%d</xliff:g> kez yanlış çizdiniz. <xliff:g id="NUMBER_1">%d</xliff:g> başarısız denemeden sonra, tabletinizi, Google oturum açma bilgilerinizi kullanarak açmanız istenir.\n\n<xliff:g id="NUMBER_2">%d</xliff:g> saniye içinde tekrar deneyin."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Kilit açma deseninizi <xliff:g id="NUMBER_0">%d</xliff:g> kez hatalı bir şekilde çizdiniz. <xliff:g id="NUMBER_1">%d</xliff:g> başarısız deneme daha yaparsanız TV\'nizin kilidini Google oturum açma bilgilerinizi kullanarak açmanız istenecektir.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> saniye içinde tekrar deneyin."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Kilit açma deseninizi <xliff:g id="NUMBER_0">%d</xliff:g> kez yanlış çizdiniz. <xliff:g id="NUMBER_1">%d</xliff:g> başarısız denemeden sonra telefonunuzu Google oturum açma bilgilerinizi kullanarak açmanız istenir.\n\n Lütfen <xliff:g id="NUMBER_2">%d</xliff:g> saniye içinde tekrar deneyin."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Tablet kilidini <xliff:g id="NUMBER_0">%d</xliff:g> defa yanlış bir şekilde açmaya çalıştınız. <xliff:g id="NUMBER_1">%d</xliff:g> defa daha başarısız deneme yapılırsa, tablet fabrika varsayılanına sıfırlanır ve tüm kullanıcı verileri kaybedilir."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"TV\'nizin kilidini <xliff:g id="NUMBER_0">%d</xliff:g> kez hatalı bir şekilde açmaya çalıştınız. <xliff:g id="NUMBER_1">%d</xliff:g> başarısız deneme daha yaparsanız TV\'niz fabrika ayalarına sıfırlanacak ve tüm kullanıcı verileri kaybolacaktır."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Telefonun kilidini <xliff:g id="NUMBER_0">%d</xliff:g> defa yanlış bir şekilde açmaya çalıştınız. <xliff:g id="NUMBER_1">%d</xliff:g> defa daha başarısız deneme yapılırsa, telefon fabrika varsayılanına sıfırlanır ve tüm kullanıcı verileri kaybedilir."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Kilit açma deseninizi <xliff:g id="NUMBER_0">%1$d</xliff:g> kez yanlış çizdiniz. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> saniye içinde tekrar deneyin."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Şifrenizi <xliff:g id="NUMBER_0">%1$d</xliff:g> kez yanlış yazdınız. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> saniye içinde tekrar deneyin."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"PIN kodunuzu <xliff:g id="NUMBER_0">%1$d</xliff:g> kez yanlış girdiniz. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> saniye içinde tekrar deneyin."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Kilit açma deseninizi <xliff:g id="NUMBER_0">%1$d</xliff:g> kez yanlış çizdiniz. <xliff:g id="NUMBER_1">%2$d</xliff:g> başarısız denemeden sonra, tabletinizi, Google oturum açma bilgilerinizi kullanarak açmanız istenir.\n\n<xliff:g id="NUMBER_2">%3$d</xliff:g> saniye içinde tekrar deneyin."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Kilit açma deseninizi <xliff:g id="NUMBER_0">%1$d</xliff:g> kez hatalı bir şekilde çizdiniz. <xliff:g id="NUMBER_1">%2$d</xliff:g> başarısız deneme daha yaparsanız TV\'nizin kilidini Google oturum açma bilgilerinizi kullanarak açmanız istenecektir.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> saniye içinde tekrar deneyin."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Kilit açma deseninizi <xliff:g id="NUMBER_0">%1$d</xliff:g> kez yanlış çizdiniz. <xliff:g id="NUMBER_1">%2$d</xliff:g> başarısız denemeden sonra telefonunuzu Google oturum açma bilgilerinizi kullanarak açmanız istenir.\n\n Lütfen <xliff:g id="NUMBER_2">%3$d</xliff:g> saniye içinde tekrar deneyin."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Tablet kilidini <xliff:g id="NUMBER_0">%1$d</xliff:g> defa yanlış bir şekilde açmaya çalıştınız. <xliff:g id="NUMBER_1">%2$d</xliff:g> defa daha başarısız deneme yapılırsa, tablet fabrika varsayılanına sıfırlanır ve tüm kullanıcı verileri kaybedilir."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"TV\'nizin kilidini <xliff:g id="NUMBER_0">%1$d</xliff:g> kez hatalı bir şekilde açmaya çalıştınız. <xliff:g id="NUMBER_1">%2$d</xliff:g> başarısız deneme daha yaparsanız TV\'niz fabrika ayalarına sıfırlanacak ve tüm kullanıcı verileri kaybolacaktır."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Telefonun kilidini <xliff:g id="NUMBER_0">%1$d</xliff:g> defa yanlış bir şekilde açmaya çalıştınız. <xliff:g id="NUMBER_1">%2$d</xliff:g> defa daha başarısız deneme yapılırsa, telefon fabrika varsayılanına sıfırlanır ve tüm kullanıcı verileri kaybedilir."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Tablet kilidini <xliff:g id="NUMBER">%d</xliff:g> defa yanlış bir şekilde açmaya çalıştınız. Tablet şimdi fabrika varsayılanına sıfırlanacak."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"TV\'nizin kilidini <xliff:g id="NUMBER">%d</xliff:g> kez hatalı bir şekilde açmaya çalıştınız. TV\'niz şimdi fabrika ayarlarına sıfırlanacaktır."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Telefon kilidini <xliff:g id="NUMBER">%d</xliff:g> defa yanlış bir şekilde açmaya çalıştınız. Telefon şimdi fabrika varsayılanına sıfırlanacak."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"kelime"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"bağlantı"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"satır"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Fabrika testi yapılamadı"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"FACTORY_TEST işlemi yalnızca /system/app dizinine yüklenmiş paketler için desteklenir."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"FACTORY_TEST işlemini sağlayan hiçbir paket bulunamadı."</string>
@@ -1006,14 +1004,14 @@
<string name="sms_short_code_confirm_never_allow" msgid="446992765774269673">"Asla İzin Verme"</string>
<string name="sim_removed_title" msgid="6227712319223226185">"SIM kart çıkarıldı"</string>
<string name="sim_removed_message" msgid="5450336489923274918">"Hücresel ağ, geçerli bir SIM kart takıp cihazınızı yeniden başlatıncaya kadar kullanılamayacak."</string>
- <string name="sim_done_button" msgid="827949989369963775">"Tamamlandı"</string>
+ <string name="sim_done_button" msgid="827949989369963775">"Bitti"</string>
<string name="sim_added_title" msgid="3719670512889674693">"SIM kart eklendi"</string>
<string name="sim_added_message" msgid="7797975656153714319">"Hücresel ağa erişmek için cihazınızı yeniden başlatın."</string>
<string name="sim_restart_button" msgid="4722407842815232347">"Yeniden başlat"</string>
<string name="time_picker_dialog_title" msgid="8349362623068819295">"Saati ayarlayın"</string>
<string name="date_picker_dialog_title" msgid="5879450659453782278">"Tarihi ayarlayın"</string>
<string name="date_time_set" msgid="5777075614321087758">"Ayarla"</string>
- <string name="date_time_done" msgid="2507683751759308828">"Tamamlandı"</string>
+ <string name="date_time_done" msgid="2507683751759308828">"Bitti"</string>
<string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ff33b5e5">"YENİ: "</font></string>
<string name="perms_description_app" msgid="5139836143293299417">"Sağlayan: <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
<string name="no_permissions" msgid="7283357728219338112">"İzin gerektirmez"</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Biçimlendiriliyor..."</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Eklenmedi"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Eşleşen hiçbir etkinlik bulunamadı."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Medya çıktısını yönlendir"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"medya çıkışını yönlendirme"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Uygulamaya medya çıktısını başka harici cihazlara yönlendirme izni verir."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Yükleme oturumlarını okuma"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"yükleme oturumlarını okuma"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Bir uygulamanın yükleme oturumlarını okumasına izin verir. Bu, etkin paket yüklemeleriyle ilgili ayrıntıların görülmesine olanak tanır."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Paketleri yükleme isteğinde bulunma"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"paket yükleme isteğinde bulunma"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Uygulamaya, paketleri yükleme isteğinde bulunma izni verir."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Yakınlaştırma denetimi için iki kez dokunun"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Widget eklenemedi."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Geçersiz kullanıcı adı veya şifre."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Kullanıcı adınızı veya şifrenizi mi unuttunuz?\n"<b>"google.com/accounts/recovery"</b>" adresini ziyaret edin."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Hesap denetleniyor…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"PIN kodunuzu <xliff:g id="NUMBER_0">%d</xliff:g> kez yanlış girdiniz. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> saniye içinde tekrar deneyin."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Şifrenizi <xliff:g id="NUMBER_0">%d</xliff:g> kez yanlış yazdınız. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> saniye içinde tekrar deneyin."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Kilit açma deseninizi <xliff:g id="NUMBER_0">%d</xliff:g> kez yanlış çizdiniz. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> saniye içinde tekrar deneyin."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Tablet kilidini <xliff:g id="NUMBER_0">%d</xliff:g> defa yanlış bir şekilde açmaya çalıştınız. <xliff:g id="NUMBER_1">%d</xliff:g> defa daha başarısız deneme yapılırsa, tablet fabrika varsayılan değerine sıfırlanır ve tüm kullanıcı verileri kaybedilir."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"TV\'nizin kilidini <xliff:g id="NUMBER_0">%d</xliff:g> kez hatalı bir şekilde açmaya çalıştınız. <xliff:g id="NUMBER_1">%d</xliff:g> başarısız deneme daha yaparsanız TV\'niz fabrika ayalarına sıfırlanacak ve tüm kullanıcı verileri kaybolacaktır."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Telefonun kilidini <xliff:g id="NUMBER_0">%d</xliff:g> defa yanlış bir şekilde açmaya çalıştınız. <xliff:g id="NUMBER_1">%d</xliff:g> defa daha başarısız deneme yapılırsa, telefon fabrika varsayılan değerine sıfırlanır ve tüm kullanıcı verileri kaybedilir."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"PIN kodunuzu <xliff:g id="NUMBER_0">%1$d</xliff:g> kez yanlış girdiniz. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> saniye içinde tekrar deneyin."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Şifrenizi <xliff:g id="NUMBER_0">%1$d</xliff:g> kez yanlış yazdınız. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> saniye içinde tekrar deneyin."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Kilit açma deseninizi <xliff:g id="NUMBER_0">%1$d</xliff:g> kez yanlış çizdiniz. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> saniye içinde tekrar deneyin."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Tablet kilidini <xliff:g id="NUMBER_0">%1$d</xliff:g> defa yanlış bir şekilde açmaya çalıştınız. <xliff:g id="NUMBER_1">%2$d</xliff:g> defa daha başarısız deneme yapılırsa, tablet fabrika varsayılan değerine sıfırlanır ve tüm kullanıcı verileri kaybedilir."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"TV\'nizin kilidini <xliff:g id="NUMBER_0">%1$d</xliff:g> kez hatalı bir şekilde açmaya çalıştınız. <xliff:g id="NUMBER_1">%2$d</xliff:g> başarısız deneme daha yaparsanız TV\'niz fabrika ayalarına sıfırlanacak ve tüm kullanıcı verileri kaybolacaktır."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Telefonun kilidini <xliff:g id="NUMBER_0">%1$d</xliff:g> defa yanlış bir şekilde açmaya çalıştınız. <xliff:g id="NUMBER_1">%2$d</xliff:g> defa daha başarısız deneme yapılırsa, telefon fabrika varsayılan değerine sıfırlanır ve tüm kullanıcı verileri kaybedilir."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Tablet kilidini <xliff:g id="NUMBER">%d</xliff:g> defa yanlış bir şekilde açmaya çalıştınız. Tablet şimdi fabrika varsayılanına sıfırlanacak."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"TV\'nizin kilidini <xliff:g id="NUMBER">%d</xliff:g> kez hatalı bir şekilde açmaya çalıştınız. TV\'niz şimdi fabrika ayarlarına sıfırlanacaktır."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Telefon kilidini <xliff:g id="NUMBER">%d</xliff:g> defa yanlış bir şekilde açmaya çalıştınız. Telefon şimdi fabrika varsayılanına sıfırlanacak."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Kilit açma deseninizi <xliff:g id="NUMBER_0">%d</xliff:g> kez yanlış çizdiniz. <xliff:g id="NUMBER_1">%d</xliff:g> başarısız denemeden sonra, tabletinizi bir e-posta hesabı kullanarak açmanız istenir.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> saniye içinde tekrar deneyin."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Kilit açma deseninizi <xliff:g id="NUMBER_0">%d</xliff:g> kez yanlış çizdiniz. <xliff:g id="NUMBER_1">%d</xliff:g> başarısız deneme daha yaparsanız TV\'nizin kilidini bir e-posta hesabı kullanarak açmanız istenecektir.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> saniye içinde tekrar deneyin."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Kilit açma deseninizi <xliff:g id="NUMBER_0">%d</xliff:g> kez yanlış çizdiniz. <xliff:g id="NUMBER_1">%d</xliff:g> başarısız denemeden sonra telefonunuzu bir e-posta hesabı kullanarak açmanız istenir.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> saniye içinde tekrar deneyin."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Kilit açma deseninizi <xliff:g id="NUMBER_0">%1$d</xliff:g> kez yanlış çizdiniz. <xliff:g id="NUMBER_1">%2$d</xliff:g> başarısız denemeden sonra, tabletinizi bir e-posta hesabı kullanarak açmanız istenir.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> saniye içinde tekrar deneyin."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Kilit açma deseninizi <xliff:g id="NUMBER_0">%1$d</xliff:g> kez yanlış çizdiniz. <xliff:g id="NUMBER_1">%2$d</xliff:g> başarısız deneme daha yaparsanız TV\'nizin kilidini bir e-posta hesabı kullanarak açmanız istenecektir.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> saniye içinde tekrar deneyin."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Kilit açma deseninizi <xliff:g id="NUMBER_0">%1$d</xliff:g> kez yanlış çizdiniz. <xliff:g id="NUMBER_1">%2$d</xliff:g> başarısız denemeden sonra telefonunuzu bir e-posta hesabı kullanarak açmanız istenir.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> saniye içinde tekrar deneyin."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Kaldır"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Ses seviyesi önerilen düzeyin üzerine yükseltilsin mi?\n\nUzun süre yüksek ses seviyesinde dinlemek işitme duyunuza zarar verebilir."</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">1 saat için</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Şu saate kadar: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (sonraki alarma) saatine kadar"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Siz bunu kapatana kadar"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Rahatsız Etmeyin ayarını kapatana kadar"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-uk-watch/strings.xml b/core/res/res/values-uk-watch/strings.xml
index 63ddea9..62d23d9 100644
--- a/core/res/res/values-uk-watch/strings.xml
+++ b/core/res/res/values-uk-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Додаток <xliff:g id="NUMBER_0">%1$d</xliff:g> з <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Датчики"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"отримувати доступ до контактів"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"отримувати геодані з годинника"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"отримувати доступ до календаря"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"надсилати та переглядати SMS"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"отримувати доступ до фотографій, медіа-вмісту й інших файлів на годиннику"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"записувати аудіо"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"фотографувати та записувати відео"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"телефонувати та керувати дзвінками"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"отримувати життєві показники з датчиків"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"відображатися як рядок стану"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"отримувати дані з датчиків на тілі (наприклад, з пульсометра)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"отримувати дані про точне місцезнаходження (на основі GPS і мережі)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"отримувати дані про приблизне місцезнаходження (на основі мережі)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"надсилати команди на SIM-карту"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"отримувати повний доступ до мережі"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"керувати власниками профілів і пристроїв"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"змінювати стан WiMAX"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"отримувати інформацію про стан функції Передавання даних Android"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"вибирати маршрути виводу медіа-вмісту"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"отримувати дані про сеанси встановлення"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"надсилати запити на пакети встановлення"</string>
</resources>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 1aa1a51..871153f 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -256,7 +256,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Включає особисті дані, як-от номери кредитних карток і паролі."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"вимикати чи змін. рядок стану"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Дозволяє програмі вимикати рядок стану чи додавати та видаляти піктограми системи."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"рядок стану"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"відображатися як рядок стану"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Дозволяє програмі бути рядком стану."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"розгорнути/згорн. рядок стану"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Дозволяє програмі розгортати чи згортати рядок стану."</string>
@@ -284,7 +284,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Дозволяє програмі отримувати й обробляти WAP-повідомлення. Це означає, що програма може відстежувати чи видаляти повідомлення, надіслані на ваш пристрій, навіть не показуючи їх вам."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"отримувати запущені програми"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Дозволяє програмі отримувати інформацію про поточні й останні запущені завдання. Це може дозволити програмі виявляти інформацію про програми, які використовуються на пристрої."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Керування власниками профілю та пристрою"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"керувати власниками профілів і пристроїв"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Додатки можуть вибирати власника профілю та пристрою."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"змінювати порядок запущених програм"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Дозволяє програмі переміщувати завдання в активні чи фонові вікна. Програма може робити це без вашого відома."</string>
@@ -326,7 +326,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Дозволяє програмі змінювати журнал викликів вашого планшетного ПК, включно з даними про вхідні та вихідні дзвінки. Шкідливі програми можуть використовувати це для стирання або зміни вашого журналу викликів."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Додаток може змінювати журнал викликів телевізора, зокрема дані про вхідні та вихідні дзвінки. Шкідливі додатки можуть використовувати це, щоб стирати чи змінювати ваш журнал викликів."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Дозволяє програмі змінювати журнал викликів вашого телефону, включно з даними про вхідні та вихідні дзвінки. Шкідливі програми можуть використовувати це для стирання або зміни вашого журналу викликів."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"датчики на тілі (як-от пульсометр)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"отримувати дані з датчиків на тілі (наприклад, з пульсометра)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Додаток має доступ до даних із датчиків, які відстежують фізичний стан, зокрема пульс."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"читати події календаря, а також конфіденційну інформацію"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Дозволяє програмі читати всі події календаря, збережені в планшетному ПК, включно з подіями друзів або співробітників. Це може дозволити програмі надсилати або зберігати дані календаря, незалежно від конфіденційності або закритості."</string>
@@ -338,15 +338,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Дозволяє програмі додавати, видаляти та змінювати події, які можна редагувати на телефоні, включно з подіями друзів або співробітників. Це може дозволити програмі надсилати повідомлення, які надходитимуть ніби від власників календарів, або змінювати події без відома власників."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"отр. дост. до додат. команд пров. місцезн."</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Додаток отримуватиме доступ до додаткових команд постачальника геоданих. Можливе втручання додатка в роботу GPS чи інших джерел геоданих."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"точне місцезнаходження (на основі GPS і мережі)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"отримувати дані про точне місцезнаходження (на основі GPS і мережі)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Дозволяє програмі дізнатися ваше точне місцезнаходження за допомогою системи глобального позиціонування (GPS) або мережевих джерел даних про місцезнаходження, як-от антен мобільного зв’язку та Wi-Fi. Щоб програма могла використовувати служби локації, вони мають бути ввімкнені та доступні для вашого пристрою. Програми можуть використовувати це, щоб визначити ваше приблизне місцезнаходження, і додатково споживати заряд акумулятора."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"приблизне місцезнаходження (на основі мережі)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"отримувати дані про приблизне місцезнаходження (на основі мережі)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Дозволяє програмі дізнатися ваше приблизне місцезнаходження. Місцезнаходження визначається службами локації за допомогою мережевих джерел даних про місцезнаходження, як-от антен мобільного зв’язку та Wi-Fi. Щоб програма могла використовувати служби локації, вони мають бути ввімкнені та доступні для вашого пристрою. Програми можуть використовувати це, щоб визначити ваше приблизне місцезнаходження."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"змінювати налаштув-ня звуку"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Дозволяє програмі змінювати загальні налаштування звуку, як-от гучність і динамік, який використовується для виводу сигналу."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"запис-ти аудіо"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Дозволяє програмі записувати звук за допомогою мікрофона. Такий дозвіл дає програмі змогу будь-коли записувати звук без вашого підтвердження."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"комунікація із SIM-картою"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"надсилати команди на SIM-карту"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Дозволяє програмі надсилати команди на SIM-карту. Це дуже небезпечно."</string>
<string name="permlab_camera" msgid="3616391919559751192">"фотограф. та знімати відео"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Дозволяє програмі фотографувати та знімати відео за допомогою камери. Такий дозвіл дає програмі змогу будь-коли використовувати камеру без вашого підтвердження."</string>
@@ -384,7 +384,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Дозволяє програмі отримувати список облікових записів, відомих телефону. Він може включати всі облікові записи, створені встановленими програмами."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"переглядати мережеві з’єднання"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Дозволяє програмі переглядати інформацію про з’єднання з мережами, як-от дані про наявні та під’єднані мережі."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"повний доступ до мережі"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"отримувати повний доступ до мережі"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Додаток може створювати сокети мережі та використовувати спеціальні мережеві протоколи. Оскільки веб-переглядач та інші додатки самостійно реалізують функції надсилання даних в Інтернет, цей дозвіл надавати не обов’язково."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"змінюв. підключення до мережі"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Дозволяє програмі змінювати стан під’єднання до мережі."</string>
@@ -404,7 +404,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Дозволяє програмі налаштовувати телефон із локальним Bluetooth, а також знаходити віддалені пристрої та створювати з ними пару."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"під’єднуватися та від’єднуватися від WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Дозволяє програмі визначати, чи ввімкнено WiMAX, а також переглядати інформацію про будь-які під’єднані мережі WiMAX."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Змінити стан WiMAX"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"змінювати стан WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Дозволяє програмі під’єднувати планшетний ПК до мереж WiMAX і від’єднувати його від них."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Додаток може під’єднувати телевізор до мереж WiMAX і від’єднувати його від них."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Дозволяє програмі під’єднувати телефон до мереж WiMAX і від’єднувати його від них."</string>
@@ -487,7 +487,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Програма може змінювати параметри калібрування сенсорного екрана. Ніколи не застосовується для звичайних програм."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"отримувати доступ до сертифікатів DRM"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Дозволяє додатку надавати та використовувати сертифікати DRM. Ніколи не застосовується для звичайних додатків."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Отримувати інформацію про стан функції Передавання даних Android."</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"отримувати дані про стан функції Передавання даних Android"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Додаток може отримувати інформацію про поточне передавання даних за допомогою функції Передавання даних Android"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"видаляти сертифікати DRM"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Власник може видаляти сертифікати DRM. Ніколи не застосовується для звичайних додатків."</string>
@@ -652,7 +652,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Неправильний PIN-код."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Щоб розбл., натисн. меню та 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Аварійний номер"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Зв’язку немає."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Немає зв’язку"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Екран заблоков."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Натис. меню, щоб розбл. чи зробити авар. виклик."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Натисн. меню, щоб розбл."</string>
@@ -684,15 +684,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Перегляньте посібник користувача чи зверніться до служби підтримки."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM-карту заблок-но."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Розблокув. SIM-карти…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Ключ розблокування неправильно намальовано стільки разів: <xliff:g id="NUMBER_0">%d</xliff:g>. \n\nПовторіть спробу через <xliff:g id="NUMBER_1">%d</xliff:g> сек."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Пароль неправильно введено стільки разів: <xliff:g id="NUMBER_0">%d</xliff:g>.\n\nПовторіть спробу через <xliff:g id="NUMBER_1">%d</xliff:g> сек."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"PIN-код неправильно введено стільки разів: <xliff:g id="NUMBER_0">%d</xliff:g>.\n\nПовторіть спробу через <xliff:g id="NUMBER_1">%d</xliff:g> сек."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Ключ розблокування неправильно намальовано стільки разів: <xliff:g id="NUMBER_0">%d</xliff:g>. Ваш планшетний ПК потрібно буде розблокувати за допомогою входу в Google після ще стількох неуспішних спроб: <xliff:g id="NUMBER_1">%d</xliff:g>.\n\n Повторіть спробу через <xliff:g id="NUMBER_2">%d</xliff:g> сек."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Ви неправильно намалювали ключ розблокування стільки разів: <xliff:g id="NUMBER_0">%d</xliff:g>. Залишилося спроб: <xliff:g id="NUMBER_1">%d</xliff:g>. У разі невдачі з’явиться запит розблокувати телевізор за допомогою входу в Google.\n\n Повторіть спробу через <xliff:g id="NUMBER_2">%d</xliff:g> c."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Ключ розблокування неправильно намальовано стільки разів: <xliff:g id="NUMBER_0">%d</xliff:g>. Ваш телефон потрібно буде розблокувати за допомогою входу в Google після ще стількох неуспішних спроб: <xliff:g id="NUMBER_1">%d</xliff:g>.\n\n Повторіть спробу через <xliff:g id="NUMBER_2">%d</xliff:g> сек."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Кількість невдалих спроб розблокувати пристрій: <xliff:g id="NUMBER_0">%d</xliff:g>. Налаштування пристрою буде змінено на заводські за умовчанням, а всі дані користувача буде втрачено після ще стількох невдалих спроб: <xliff:g id="NUMBER_1">%d</xliff:g>."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Кількість невдалих спроб розблокувати телевізор: <xliff:g id="NUMBER_0">%d</xliff:g>. Залишилося спроб: <xliff:g id="NUMBER_1">%d</xliff:g>. У разі невдачі всі налаштування телевізора буде змінено на заводські за умовчанням, а всі дані користувача – втрачено."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Кількість невдалих спроб розблокувати телефон: <xliff:g id="NUMBER_0">%d</xliff:g>. Налаштування телефону буде змінено на заводські за умовчанням, а всі дані користувача буде втрачено після ще стількох невдалих спроб: <xliff:g id="NUMBER_1">%d</xliff:g>."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Ключ розблокування неправильно намальовано стільки разів: <xliff:g id="NUMBER_0">%1$d</xliff:g>. \n\nПовторіть спробу через <xliff:g id="NUMBER_1">%2$d</xliff:g> сек."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Пароль неправильно введено стільки разів: <xliff:g id="NUMBER_0">%1$d</xliff:g>.\n\nПовторіть спробу через <xliff:g id="NUMBER_1">%2$d</xliff:g> сек."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"PIN-код неправильно введено стільки разів: <xliff:g id="NUMBER_0">%1$d</xliff:g>.\n\nПовторіть спробу через <xliff:g id="NUMBER_1">%2$d</xliff:g> сек."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Ключ розблокування неправильно намальовано стільки разів: <xliff:g id="NUMBER_0">%1$d</xliff:g>. Ваш планшетний ПК потрібно буде розблокувати за допомогою входу в Google після ще стількох неуспішних спроб: <xliff:g id="NUMBER_1">%2$d</xliff:g>.\n\n Повторіть спробу через <xliff:g id="NUMBER_2">%3$d</xliff:g> сек."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Ви неправильно намалювали ключ розблокування стільки разів: <xliff:g id="NUMBER_0">%1$d</xliff:g>. Залишилося спроб: <xliff:g id="NUMBER_1">%2$d</xliff:g>. У разі невдачі з’явиться запит розблокувати телевізор за допомогою входу в Google.\n\n Повторіть спробу через <xliff:g id="NUMBER_2">%3$d</xliff:g> c."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Ключ розблокування неправильно намальовано стільки разів: <xliff:g id="NUMBER_0">%1$d</xliff:g>. Ваш телефон потрібно буде розблокувати за допомогою входу в Google після ще стількох неуспішних спроб: <xliff:g id="NUMBER_1">%2$d</xliff:g>.\n\n Повторіть спробу через <xliff:g id="NUMBER_2">%3$d</xliff:g> сек."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Кількість невдалих спроб розблокувати пристрій: <xliff:g id="NUMBER_0">%1$d</xliff:g>. Налаштування пристрою буде змінено на заводські за умовчанням, а всі дані користувача буде втрачено після ще стількох невдалих спроб: <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Кількість невдалих спроб розблокувати телевізор: <xliff:g id="NUMBER_0">%1$d</xliff:g>. Залишилося спроб: <xliff:g id="NUMBER_1">%2$d</xliff:g>. У разі невдачі всі налаштування телевізора буде змінено на заводські за умовчанням, а всі дані користувача – втрачено."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Кількість невдалих спроб розблокувати телефон: <xliff:g id="NUMBER_0">%1$d</xliff:g>. Налаштування телефону буде змінено на заводські за умовчанням, а всі дані користувача буде втрачено після ще стількох невдалих спроб: <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Кількість невдалих спроб розблокувати пристрій: <xliff:g id="NUMBER">%d</xliff:g>. Налаштування пристрою буде змінено на заводські за умовчанням."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Кількість невдалих спроб розблокувати телевізор: <xliff:g id="NUMBER">%d</xliff:g>. Налаштування телевізора буде змінено на заводські за умовчанням."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Кількість невдалих спроб розблокувати телефон: <xliff:g id="NUMBER">%d</xliff:g>. Налаштування телефону буде змінено на заводські за умовчанням."</string>
@@ -744,8 +744,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"слово"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"посилання"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"рядок"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Помилка завод. тесту"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Дія FACTORY_TEST підтримується лише для пакетів, установлених у /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Не було знайдено жодного пакета, який надає дію FACTORY_TEST."</string>
@@ -1087,11 +1085,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Форматування…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Не вставлено"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Відповідні дії не знайдено."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Скеровувати вивід медіа-даних"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"вибирати маршрути виводу медіа-вмісту"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Дозволяє програмі скеровувати вивід медіа-даних на інші зовнішні пристрої."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Зчитувати дані сеансів встановлення"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"отримувати дані про сеанси встановлення"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Дозволяє додатку читати дані сеансів встановлення. Додаток може бачити деталі про активні встановлення пакетів."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Надсилання запитів на встановлення пакетів"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"запитувати дані про пакети встановлення"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Додаток зможе надсилати запити на встановлення пакетів."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Двічі торкніться, щоб керувати масштабом"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Не вдалося додати віджет."</string>
@@ -1301,18 +1299,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Недійсне ім’я користувача чи пароль."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Не пам’ятаєте ім’я користувача чи пароль?\nВідвідайте сторінку "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Перевірка облікового запису…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"PIN-код неправильно введено стільки разів: <xliff:g id="NUMBER_0">%d</xliff:g>. \n\nПовторіть спробу через <xliff:g id="NUMBER_1">%d</xliff:g> с."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Пароль неправильно введено стільки разів: <xliff:g id="NUMBER_0">%d</xliff:g>. \n\nПовторіть спробу через <xliff:g id="NUMBER_1">%d</xliff:g> с."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Ключ розблокування неправильно намальовано стільки разів: <xliff:g id="NUMBER_0">%d</xliff:g>. \n\nПовторіть спробу через <xliff:g id="NUMBER_1">%d</xliff:g> с."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Кількість невдалих спроб розблокувати планшетний ПК: <xliff:g id="NUMBER_0">%d</xliff:g>. У вас є ще стільки спроб: <xliff:g id="NUMBER_1">%d</xliff:g>. У разі невдачі налаштування планшетного ПК буде змінено на заводські за умовчанням, а всі дані користувача – втрачено."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Кількість невдалих спроб розблокувати телевізор: <xliff:g id="NUMBER_0">%d</xliff:g>. Залишилося спроб: <xliff:g id="NUMBER_1">%d</xliff:g>. У разі невдачі всі налаштування телевізора буде змінено на заводські за умовчанням, а всі дані користувача – втрачено."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Кількість невдалих спроб розблокувати телефон: <xliff:g id="NUMBER_0">%d</xliff:g>. У вас є ще стільки спроб: <xliff:g id="NUMBER_1">%d</xliff:g>. У разі невдачі налаштування телефону буде змінено на заводські за умовчанням, а всі дані користувача – втрачено."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"PIN-код неправильно введено стільки разів: <xliff:g id="NUMBER_0">%1$d</xliff:g>. \n\nПовторіть спробу через <xliff:g id="NUMBER_1">%2$d</xliff:g> с."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Пароль неправильно введено стільки разів: <xliff:g id="NUMBER_0">%1$d</xliff:g>. \n\nПовторіть спробу через <xliff:g id="NUMBER_1">%2$d</xliff:g> с."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Ключ розблокування неправильно намальовано стільки разів: <xliff:g id="NUMBER_0">%1$d</xliff:g>. \n\nПовторіть спробу через <xliff:g id="NUMBER_1">%2$d</xliff:g> с."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Кількість невдалих спроб розблокувати планшетний ПК: <xliff:g id="NUMBER_0">%1$d</xliff:g>. У вас є ще стільки спроб: <xliff:g id="NUMBER_1">%2$d</xliff:g>. У разі невдачі налаштування планшетного ПК буде змінено на заводські за умовчанням, а всі дані користувача – втрачено."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Кількість невдалих спроб розблокувати телевізор: <xliff:g id="NUMBER_0">%1$d</xliff:g>. Залишилося спроб: <xliff:g id="NUMBER_1">%2$d</xliff:g>. У разі невдачі всі налаштування телевізора буде змінено на заводські за умовчанням, а всі дані користувача – втрачено."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Кількість невдалих спроб розблокувати телефон: <xliff:g id="NUMBER_0">%1$d</xliff:g>. У вас є ще стільки спроб: <xliff:g id="NUMBER_1">%2$d</xliff:g>. У разі невдачі налаштування телефону буде змінено на заводські за умовчанням, а всі дані користувача – втрачено."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Кількість невдалих спроб розблокувати планшетний ПК: <xliff:g id="NUMBER">%d</xliff:g>. Налаштування планшетного ПК буде змінено на заводські за умовчанням."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Кількість невдалих спроб розблокувати телевізор: <xliff:g id="NUMBER">%d</xliff:g>. Налаштування телевізора буде змінено на заводські за умовчанням."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Кількість невдалих спроб розблокувати телефон: <xliff:g id="NUMBER">%d</xliff:g>. Налаштування телефону буде змінено на заводські за умовчанням."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Ключ розблокування неправильно намальовано стільки разів: <xliff:g id="NUMBER_0">%d</xliff:g>. У вас є ще стільки спроб: <xliff:g id="NUMBER_1">%d</xliff:g>. У разі невдачі з’явиться запит розблокувати планшетний ПК за допомогою облікового запису електронної пошти.\n\n Повторіть спробу через <xliff:g id="NUMBER_2">%d</xliff:g> сек."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Ви неправильно намалювали ключ розблокування стільки разів: <xliff:g id="NUMBER_0">%d</xliff:g>. Залишилося спроб: <xliff:g id="NUMBER_1">%d</xliff:g>. У разі невдачі з’явиться запит розблокувати телевізор за допомогою облікового запису електронної пошти.\n\n Повторіть спробу через <xliff:g id="NUMBER_2">%d</xliff:g> c."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Ключ розблокування неправильно намальовано стільки разів: <xliff:g id="NUMBER_0">%d</xliff:g>. У вас є ще стільки спроб: <xliff:g id="NUMBER_1">%d</xliff:g>. У разі невдачі з’явиться запит розблокувати телефон за допомогою облікового запису електронної пошти.\n\n Повторіть спробу через <xliff:g id="NUMBER_2">%d</xliff:g> сек."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Ключ розблокування неправильно намальовано стільки разів: <xliff:g id="NUMBER_0">%1$d</xliff:g>. У вас є ще стільки спроб: <xliff:g id="NUMBER_1">%2$d</xliff:g>. У разі невдачі з’явиться запит розблокувати планшетний ПК за допомогою облікового запису електронної пошти.\n\n Повторіть спробу через <xliff:g id="NUMBER_2">%3$d</xliff:g> сек."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Ви неправильно намалювали ключ розблокування стільки разів: <xliff:g id="NUMBER_0">%1$d</xliff:g>. Залишилося спроб: <xliff:g id="NUMBER_1">%2$d</xliff:g>. У разі невдачі з’явиться запит розблокувати телевізор за допомогою облікового запису електронної пошти.\n\n Повторіть спробу через <xliff:g id="NUMBER_2">%3$d</xliff:g> c."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Ключ розблокування неправильно намальовано стільки разів: <xliff:g id="NUMBER_0">%1$d</xliff:g>. У вас є ще стільки спроб: <xliff:g id="NUMBER_1">%2$d</xliff:g>. У разі невдачі з’явиться запит розблокувати телефон за допомогою облікового запису електронної пошти.\n\n Повторіть спробу через <xliff:g id="NUMBER_2">%3$d</xliff:g> сек."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Вилучити"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Збільшити гучність понад рекомендований рівень?\n\nЯкщо слухати надто гучну музику тривалий час, можна пошкодити слух."</string>
@@ -1504,6 +1502,7 @@
<item quantity="other">Протягом %d год</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (наступний будильник)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Доки ви не вимкнете"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Доки ввімкнено режим \"Не турбувати\""</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-ur-rPK-watch/strings.xml b/core/res/res/values-ur-rPK-watch/strings.xml
index 8ba8042..6aea67a 100644
--- a/core/res/res/values-ur-rPK-watch/strings.xml
+++ b/core/res/res/values-ur-rPK-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"ایپ <xliff:g id="NUMBER_0">%1$d</xliff:g> از <xliff:g id="NUMBER_1">%2$d</xliff:g>۔"</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"سینسرز"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"اپنے رابطوں تک رسائی حاصل کریں"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"اس گھڑی کے مقام تک رسائی حاصل کریں"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"اپنے کیلنڈر تک رسائی حاصل کریں"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"SMS پیغامات بھیجیں اور دیکھیں"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"اپنی گھڑی پر تصاویر، میڈیا اور فائلوں تک رسائی حاصل کریں"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"آڈیو ریکارڈ کریں"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"تصاویر لیں اور ویڈیو ریکارڈ کریں"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"فون کالز کریں اور ان کا نظم کریں"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"اپنی علامات حیات کے متعلق سنسر ڈیٹا تک رسائی حاصل کریں"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"بطور اسٹیٹس بار کام لیں"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"باڈی سینسرز تک رسائی حاصل کریں (جیسے دل کی دھڑکن کے مانیٹرز)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"قطعی مقام تک رسائی حاصل کریں (GPS اور نیٹ ورک پر مبنی)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"تخمینی مقام تک رسائی حاصل کریں (نیٹ ورک پر مبنی)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"SIM کو ہدایات بھیجیں"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"پورے نیٹ ورک تک رسائی حاصل کریں"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"پروفائل اور آلہ کے مالکان کا نظم کریں"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"WiMAX کی حیثیت تبدیل کریں"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"Android Beam منتقلی کی صورت حال موصول کریں"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"میڈیا آؤٹ پٹ کی سمت طے کریں"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"انسٹال سیشنز پڑھیں"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"پیکجز انسٹال کرنے کی درخواست کریں"</string>
</resources>
diff --git a/core/res/res/values-ur-rPK/strings.xml b/core/res/res/values-ur-rPK/strings.xml
index e6e20f1..175a840 100644
--- a/core/res/res/values-ur-rPK/strings.xml
+++ b/core/res/res/values-ur-rPK/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"اس میں ذاتی ڈیٹا جیسے کریڈٹ کارڈ نمبرز اور پاس ورڈز شامل ہیں۔"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"اسٹیٹس بار کو غیر فعال یا اس میں ترمیم کریں"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"ایپ کو اسٹیٹس بار غیر فعال کرنے یا سسٹم آئیکنز شامل کرنے اور ہٹانے کی اجازت دیتا ہے۔"</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"حیثیت بار"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"بطور اسٹیٹس بار کام لیں"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"ایپ کو اسٹیٹس بار بننے کی اجازت دیتا ہے۔"</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"حیثیت بار پھیلائیں/سکیڑیں"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"ایپ کو اسٹیٹس بار پھیلانے یا سکیڑنے کی اجازت دیتا ہے۔"</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"ایپ کو WAP پیغامات حاصل اور ان پر کارروائی کرنے کی اجازت دیتا ہے۔ اس اجازت میں آپ کو مرسلہ پیغامات آپ کو دکھائے بغیر ان پر نگاہ رکھنے یا انہیں حذف کرنے کی اہلیت شامل ہے۔"</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"چل رہی ایپس کی بازیافت کریں"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"ایپ کو موجودہ اور حالیہ چل رہے ٹاسکس کے بارے میں معلومات بازیافت کرنے کی اجازت دیتا ہے۔ یہ ایپ کو اس بارے میں معلومات دریافت کرنے کی اجازت دے سکتا ہے کہ آلہ پر کون سی ایپلیکیشنز استعمال کی جاتی ہیں۔"</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"پروفائل اور آلہ کے مالکان کا نظم کریں"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"پروفائل اور آلہ کے مالکان کا نظم کریں"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"ایپس کو پروفائل کے مالکان اور آلہ کے مالک کو سیٹ کرنے کی اجازت دیتا ہے۔"</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"چل رہی ایپس کو دوبارہ ترتیب دیں"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"ایپ کو پیش منظر یا پس منظر میں ٹاسکس کو منتقل کرنے کی اجازت دیتا ہے۔ ایپ آپ کے ان پٹ کے بغیر یہ کام کرسکتی ہے۔"</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"ایپ کو آپ کے ٹیبلٹ کی کال لاگ، بشمول آنے والی اور باہر جانے والی کالوں کے بارے میں ڈیٹا میں ترمیم کرنے کی اجازت دیتا ہے۔ نقصان دہ ایپس آپ کی کال لاگ مٹانے یا اس میں ترمیم کرنے کیلئے اسے استعمال کرسکتی ہیں۔"</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"انکمنگ اور آؤٹ گوئنگ کالز کے بارے میں ڈیٹا سمیت، ایپ کو آپ کے TV کے کال لاگ میں ترمیم کرنے کی اجازت دیتا ہے۔ نقصان دہ ایپس آپ کی کال لاگ مٹانے یا اس میں ترمیم کرنے کیلئے اسے استعمال کرسکتی ہیں۔"</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"ایپ کو آپ کے فون کی کال لاگ، بشمول آنے والی اور باہر جانے والی کالوں کے بارے میں ڈیٹا میں ترمیم کرنے کی اجازت دیتا ہے۔ نقصان دہ ایپس آپ کی کال لاگ مٹانے یا اس میں ترمیم کرنے کیلئے اسے استعمال کرسکتی ہیں۔"</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"باڈی سینسرز (جیسے دل کی دھڑکن کے مانیٹرز)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"باڈی سینسرز تک رسائی حاصل کریں (جیسے حرکت قلب شرح مانیٹرز)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"ان سینسرز سے ڈیٹا تک رسائی حاصل کرنے کی اجازت دیتی ہے جو آپ کی حرکت قلب کی شرح جیسی آپ کی فزیکل صورتحال کو مانیٹر کرتے ہیں۔"</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"کیلنڈر ایونٹس کے ساتھ رازداری کی معلومات پڑھیں"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"ایپ کو آپ کے، بشمول آپ کے دوستوں یا ساتھی کارکنان کے ٹیبلٹ پر اسٹور کردہ سبھی کیلنڈر ایونٹس کو پڑھنے کی اجازت دیتا ہے۔ یہ ایپ کو رازداری یا حساسیت سے قطع نظر آپ کے کیلنڈر ڈیٹا کا اشتراک یا اسے محفوظ کرنے کی اجازت دیتا ہے۔"</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"ایپ کو وہ ایونٹس جن میں آپ اپنے فون پر ترمیم کرسکتے ہیں، بشمول دوسروں یا ساتھی کارکنوں کے ایونٹس شامل کرنے، ہٹانے، تبدیل کرنے کی اجازت دیتا ہے۔ یہ ایپ کو ایسے پیغامات بھیجنے کی جو کیلنڈر مالکان کی جانب سے آئے ہوئے معلوم پڑتے ہیں یا مالکان کی جانکاری کے بغیر ایونٹس میں ترمیم کرنے کی اجازت دے سکتا ہے۔"</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"اضافی مقام فراہم کنندہ کی کمانڈز تک رسائی حاصل کریں"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"ایپ کو اضافی مقام فراہم کنندہ کی کمانڈز تک رسائی حاصل کرنے کی اجازت دیتی ہے۔ یہ ایپ کو GPS یا دوسرے مقام کے مآخذ کے عمل کے ساتھ مداخلت کرنے کی اجازت دے سکتی ہے۔"</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"قطعی مقام (GPS اور نیٹ ورک پر مبنی)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"قطعی مقام تک رسائی حاصل کریں (GPS اور نیٹ ورک پر مبنی)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"ایپ کو گلوبل پوزیشننگ سسٹم (GPS) یا نیٹ ورک کے مقام کے مآخذ جیسے سیل ٹاورز اور Wi-Fi کا استعمال کرکے آپ کا درست مقام حاصل کرنے کی اجازت دیتا ہے۔ ان مقام کی سروسز کا آن ہونا اور ایپ کو انہیں استعمال کرنے کیلئے آپ کے آلے پر دستیاب ہونا ضروری ہے۔ ایپس تخمینی طور پر آپ کے محل وقوع کا تعین کرنے کیلئے اسے استعمال کرسکتی ہیں اور بیٹری کی اضافی قوت صرف کرسکتی ہیں۔"</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"تخمینی مقام (نیٹ ورک پر مبنی)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"تخمینی مقام تک رسائی حاصل کریں (نیٹ ورک پر مبنی)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"ایپ کو آپ کا تخمینی مقام حاصل کرنے کی اجازت دیتا ہے۔ یہ مقام نیٹ ورک کے مقام کے مآخذ جیسے سیل ٹاورز اور Wi-Fi کا استعمال کرکے مقام کی سروسز کے ذریعہ اخذ کیا جاتا ہے۔ ان مقام کی سروسز کا آن ہونا اور آپ کے آلے پر دستیاب ہونا ضروری ہے تاکہ ایپ انہیں استعمال کرسکے۔ ایپس تخمینی طور پر آپ کے محل وقوع کا تعین کرنے کیلئے اسے استعمال کر سکتی ہیں۔"</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"اپنے آڈیو کی ترتیبات کو تبدیل کریں"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"ایپ کو مجموعی آڈیو ترتیبات جیسے والیوم اور آؤٹ پٹ کیلئے جو اسپیکر استعمال ہوتا ہے اس میں ترمیم کرنے کی اجازت دیتا ہے۔"</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"آڈیو ریکارڈ کریں"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"ایپ کو مائکرو فون سے آڈیو ریکارڈ کرنے کی اجازت دیتا ہے۔ یہ اجازت ایپ کو کسی بھی وقت آپ کی تصدیق کے بغیر آڈیو ریکارڈ کرنے کی اجازت دیتی ہے۔"</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"سم مواصلت"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"SIM کو ہدایات بھیجیں"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"ایپ کو SIM کو کمانڈز بھیجنے کی اجازت دیتا ہے۔ یہ بہت خطرناک ہے۔"</string>
<string name="permlab_camera" msgid="3616391919559751192">"تصاویر لیں اور ویڈیوز بنائیں"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"ایپ کو کیمرے سے تصویریں لینے اور ویڈیوز بنانے کی اجازت دیتا ہے۔ یہ اجازت ایپ کو آپ کی تصدیق کے بغیر کسی بھی وقت کیمرا استعمال کرنے کی اجازت دیتی ہے۔"</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"ایپ کو فون کو معلوم اکاؤنٹس کی فہرست حاصل کرنے کی اجازت دیتا ہے۔ اس میں آپ کی انسٹال کردہ ایپلیکیشنز کے ذریعہ بنائے گئے کوئی بھی اکاؤنٹس شامل ہوسکتے ہیں۔"</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"نیٹ ورک کنکشنز دیکھیں"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"ایپ کو نیٹ ورک کنکشنز کے بارے میں معلومات دیکھنے کی اجازت دیتا ہے جیسے کون سے نیٹ ورکس موجود اور مربوط ہیں۔"</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"پورے نیٹ ورک تک رسائی"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"پورے نیٹ ورک تک رسائی حاصل کریں"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"ایپ کو نیٹ ورک ساکیٹس بنانے اور حسب ضرورت نیٹ ورک پروٹوکولز استعمال کرنے کی اجازت دیتا ہے۔ براؤزر اور دوسری ایپلیکیشنز انٹرنیٹ کو ڈیٹا بھیجنے کا ذریعہ فراہم کرتے ہیں، لہذا انٹرنیٹ کو ڈیٹا بھیجنے کیلئے یہ اجازت درکار نہیں ہوتی ہے۔"</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"نیٹ ورک کنیکٹوٹی تبدیل کریں"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"ایپ کو نیٹ ورک کنیکٹوٹی کی حالت تبدیل کرنے کی اجازت دیتا ہے۔"</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"ایپ کو مقامی بلوٹوتھ فون کنفیگر کرنے اور ریموٹ آلات دریافت کرنے اور ان کے ساتھ جوڑا بنانے کی اجازت دیتا ہے۔"</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"WiMAX سے مربوط اور غیر مربوط کریں"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"ایپ کو یہ تعین کرنے کی کہ آیا WiMAX فعال ہے اور کسی مربوط WiMAX نیٹ ورکس کے بارے میں معلومات کا تعین کرنے کی اجازت دیتا ہے۔"</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX کی حیثیت تبدیل کریں"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"WiMAX کی حیثیت تبدیل کریں"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"ایپ کو WiMAX نیٹ ورکس سے ٹیبلٹ کو مربوط اور ٹیبلٹ کو منقطع کرنے کی اجازت دیتا ہے۔"</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"ایپ کو WiMAX نیٹ ورکس سے TV کو منسلک اور TV کو غیر منسلک کرنے کی اجازت دیتا ہے۔"</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"ایپ کو WiMAX نیٹ ورکس سے فون کو مربوط اور فون کو منقطع کرنے کی اجازت دیتا ہے۔"</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"ایپ کو ٹچ اسکرین کے کیلیبریشن پیرامیٹرز میں ترمیم کرنے کی اجازت دیتا ہے۔ عام ایپس کیلئے کبھی بھی اس کی ضرورت نہيں ہونی چاہئے۔"</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"DRM سرٹیفکیٹس تک رسائی حاصل کریں"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"ایک ایپ کو DRM سرٹیفکیٹس فراہم کرنے اور ان کا استعمال کرنے کی اجازت دیتا ہے۔ عام ایپس کیلئے کبھی بھی اس کی ضرورت نہیں ہوتی ہے۔"</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Android Beam منتقلی کی صورت حال موصول کریں"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"Android Beam منتقلی کی صورت حال موصول کریں"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"اس ایپلیکیشن کو Android Beam کی حالیہ منتقلیوں کے بارے میں معلومات موصول کرنے کی اجازت دیتا ہے"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"DRM سرٹیفکیٹس کو ہٹائیں"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"ایک ایپلیکیشن کو DRM سرٹیفکیٹس کو ہٹانے کی اجازت دیتا ہے۔ عام ایپس کیلئے کبھی بھی اس کی ضرورت نہیں ہونی چاہیے۔"</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"غلط PIN کوڈ۔"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"غیر مقفل کرنے کیلئے، مینو پھر 0 دبائیں۔"</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"ہنگامی نمبر"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"کوئی سروس نہیں ہے۔"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"کوئی سروس نہیں ہے"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"اسکرین مقفل ہے۔"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"غیر مقفل کرنے کیلئے مینو دبائیں یا ہنگامی کال کریں۔"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"غیر مقفل کرنے کیلئے مینو دبائیں۔"</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"رہنمائے صارف دیکھیں یا کسٹمر کیئر سے رابطہ کریں۔"</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM کارڈ مقفل ہے۔"</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"SIM کارڈ غیر مقفل ہو رہا ہے…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"آپ نے اپنا غیر مقفل کرنے کا پیٹرن <xliff:g id="NUMBER_0">%d</xliff:g> بار غلط طریقے سے ڈرا کیا ہے۔ \n\n <xliff:g id="NUMBER_1">%d</xliff:g> سیکنڈ میں دوبارہ کوشش کریں۔"</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"آپ نے اپنا پاس ورڈ <xliff:g id="NUMBER_0">%d</xliff:g> بار غلط طریقے سے ٹائپ کیا ہے۔ \n\n <xliff:g id="NUMBER_1">%d</xliff:g> سیکنڈ میں دوبارہ کوشش کریں۔"</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"آپ نے <xliff:g id="NUMBER_0">%d</xliff:g> بار اپنا PIN غلط طریقے سے ٹائپ کیا ہے۔ \n\n <xliff:g id="NUMBER_1">%d</xliff:g> سیکنڈ میں دوبارہ کوشش کریں۔"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"آپ نے اپنے غیر مقفل کرنے کے پیٹرن کو <xliff:g id="NUMBER_0">%d</xliff:g> بار غلط طریقے سے ڈرا کیا ہے۔ <xliff:g id="NUMBER_1">%d</xliff:g> مزید ناکام کوششوں کے بعد، آپ سے اپنے Google سائن ان کا استعمال کرکے اپنے ٹیبلٹ کو غیر مقفل کرنے کیلئے کہا جائے گا۔ \n\n <xliff:g id="NUMBER_2">%d</xliff:g> سیکنڈز بعد دوبارہ کوشش کریں۔"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"آپ نے اپنا غیر مقفل کرنے کا پیٹرن <xliff:g id="NUMBER_0">%d</xliff:g> بار غلط طریقے سے ڈرا کیا ہے۔ <xliff:g id="NUMBER_1">%d</xliff:g> مزید ناکام کوششوں کے بعد، آپ سے اپنا Google سائن ان استعمال کرکے اپنا TV غیر مقفل کرنے کو کہا جائے گا۔\n\n <xliff:g id="NUMBER_2">%d</xliff:g> سیکنڈ میں دوبارہ کوشش کریں۔"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"آپ نے اپنے غیر مقفل کرنے کے پیٹرن کو <xliff:g id="NUMBER_0">%d</xliff:g> بار غلط طریقے سے ڈرا کیا ہے۔ <xliff:g id="NUMBER_1">%d</xliff:g> مزید ناکام کوششوں کے بعد، آپ سے اپنے Google سائن ان کا استعمال کرکے اپنے فون کو غیر مقفل کرنے کیلئے کہا جائے گا۔ \n\n <xliff:g id="NUMBER_2">%d</xliff:g> سیکنڈز بعد دوبارہ کوشش کریں۔"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"آپ نے ٹیبلیٹ کو <xliff:g id="NUMBER_0">%d</xliff:g> بار غلط طریقہ سے غیر مقفل کرنے کی کوشش کی ہے۔ <xliff:g id="NUMBER_1">%d</xliff:g> مزید ناکام کوششوں کے بعد، ٹیبلیٹ کو فیکٹری ڈیفالٹ پر ری سیٹ کر دیا جائے گیا اور صارف کا سبھی ڈیٹا ضائع ہو جائے گا۔"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"آپ نے TV کو غیر مقفل کرنے کیلئے <xliff:g id="NUMBER_0">%d</xliff:g> بار غلط طریقے سے کوششیں کی ہیں۔ <xliff:g id="NUMBER_1">%d</xliff:g> مزید ناکام کوششوں کے بعد، TV فیکٹری ڈیفالٹ پر ری سیٹ ہو جائے گا اور صارف کا سبھی ڈیٹا ضائع ہو جائے گا۔"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"آپ نے فون کو <xliff:g id="NUMBER_0">%d</xliff:g> بار غلط طریقہ سے غیر مقفل کرنے کی کوشش کی ہے۔ <xliff:g id="NUMBER_1">%d</xliff:g> مزید ناکام کوششوں کے بعد، فون کو فیکٹری ڈیفالٹ پر ری سیٹ کر دیا جائے گی اور صارف کا سبھی ڈیٹا ضائع ہو جائے گا۔"</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"آپ نے اپنا غیر مقفل کرنے کا پیٹرن <xliff:g id="NUMBER_0">%1$d</xliff:g> بار غلط طریقے سے ڈرا کیا ہے۔ \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> سیکنڈ میں دوبارہ کوشش کریں۔"</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"آپ نے اپنا پاس ورڈ <xliff:g id="NUMBER_0">%1$d</xliff:g> بار غلط طریقے سے ٹائپ کیا ہے۔ \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> سیکنڈ میں دوبارہ کوشش کریں۔"</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"آپ نے <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اپنا PIN غلط طریقے سے ٹائپ کیا ہے۔ \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> سیکنڈ میں دوبارہ کوشش کریں۔"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"آپ نے اپنے غیر مقفل کرنے کے پیٹرن کو <xliff:g id="NUMBER_0">%1$d</xliff:g> بار غلط طریقے سے ڈرا کیا ہے۔ <xliff:g id="NUMBER_1">%2$d</xliff:g> مزید ناکام کوششوں کے بعد، آپ سے اپنے Google سائن ان کا استعمال کرکے اپنے ٹیبلٹ کو غیر مقفل کرنے کیلئے کہا جائے گا۔ \n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> سیکنڈز بعد دوبارہ کوشش کریں۔"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"آپ نے اپنا غیر مقفل کرنے کا پیٹرن <xliff:g id="NUMBER_0">%1$d</xliff:g> بار غلط طریقے سے ڈرا کیا ہے۔ <xliff:g id="NUMBER_1">%2$d</xliff:g> مزید ناکام کوششوں کے بعد، آپ سے اپنا Google سائن ان استعمال کرکے اپنا TV غیر مقفل کرنے کو کہا جائے گا۔\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> سیکنڈ میں دوبارہ کوشش کریں۔"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"آپ نے اپنے غیر مقفل کرنے کے پیٹرن کو <xliff:g id="NUMBER_0">%1$d</xliff:g> بار غلط طریقے سے ڈرا کیا ہے۔ <xliff:g id="NUMBER_1">%2$d</xliff:g> مزید ناکام کوششوں کے بعد، آپ سے اپنے Google سائن ان کا استعمال کرکے اپنے فون کو غیر مقفل کرنے کیلئے کہا جائے گا۔ \n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> سیکنڈز بعد دوبارہ کوشش کریں۔"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"آپ نے ٹیبلیٹ کو <xliff:g id="NUMBER_0">%1$d</xliff:g> بار غلط طریقہ سے غیر مقفل کرنے کی کوشش کی ہے۔ <xliff:g id="NUMBER_1">%2$d</xliff:g> مزید ناکام کوششوں کے بعد، ٹیبلیٹ کو فیکٹری ڈیفالٹ پر ری سیٹ کر دیا جائے گیا اور صارف کا سبھی ڈیٹا ضائع ہو جائے گا۔"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"آپ نے TV کو غیر مقفل کرنے کیلئے <xliff:g id="NUMBER_0">%1$d</xliff:g> بار غلط طریقے سے کوششیں کی ہیں۔ <xliff:g id="NUMBER_1">%2$d</xliff:g> مزید ناکام کوششوں کے بعد، TV فیکٹری ڈیفالٹ پر ری سیٹ ہو جائے گا اور صارف کا سبھی ڈیٹا ضائع ہو جائے گا۔"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"آپ نے فون کو <xliff:g id="NUMBER_0">%1$d</xliff:g> بار غلط طریقہ سے غیر مقفل کرنے کی کوشش کی ہے۔ <xliff:g id="NUMBER_1">%2$d</xliff:g> مزید ناکام کوششوں کے بعد، فون کو فیکٹری ڈیفالٹ پر ری سیٹ کر دیا جائے گی اور صارف کا سبھی ڈیٹا ضائع ہو جائے گا۔"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"آپ نے ٹیبلیٹ کو <xliff:g id="NUMBER">%d</xliff:g> بار غلط طریقہ سے غیر مقفل کرنے کی کوشش کی ہے۔ ٹیبلیٹ کو اب فیکٹری ڈیفالٹ پر ری سیٹ کر دیا جائے گیا۔"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"آپ نے TV کو غیر مقفل کرنے کیلئے <xliff:g id="NUMBER">%d</xliff:g> بار غلط طریقے سے کوشش کی ہے۔ TV اب فیکٹری ڈیفالٹ پر ری سیٹ ہو جائے گا۔"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"آپ نے فون کو <xliff:g id="NUMBER">%d</xliff:g> بار غلط طریقہ سے غیر مقفل کرنے کی کوشش کی ہے۔ فون کو اب فیکٹری ڈیفالٹ پر ری سیٹ کر دیا جائے گیا۔"</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"لفظ"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"لنک"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"لکیر"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"فیکٹری ٹیسٹ ناکام رہا"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"FACTORY_TEST کارروائی صرف /system/app میں انسٹال کردہ پیکیجز کیلئے تعاون یافتہ ہے۔"</string>
<string name="factorytest_no_action" msgid="872991874799998561">"ایسا کوئی پیکیج نہیں ملا، جو FACTORY_TEST کارروائی فراہم کرتا ہے۔"</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"فارمیٹ کیا جا رہا ہے…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"دلچسپی نہیں ہے"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"کوئی مماثل سرگرمیاں نہیں ملیں۔"</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"میڈیا آؤٹ پٹ کی سمت طے کریں"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"میڈیا آؤٹ پٹ کی سمت طے کریں"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"کسی ایپلیکیشن کو دوسرے خارجی آلات تک میڈیا آؤٹ پٹ کا راستہ بنانے کی اجازت دیتا ہے۔"</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"انسٹال سیشنز پڑھیں"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"انسٹال سیشنز پڑھیں"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"ایک ایپلیکیشن کو انسٹال سیشنز پڑھنے کی اجازت دیتا ہے۔ یہ اسے فعال پیکیج انسٹالیشنز کے بارے میں تفصیلات دیکھنے کی اجازت دیتا ہے۔"</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"پیکجز انسٹال کرنے کی درخواست کریں"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"پیکجز انسٹال کرنے کی درخواست کریں"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"ایک ایپلیکیشن کو پیکجز انسٹال کرنے کی اجازت دیتی ہے۔"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"زوم کے کنٹرول کیلئے دو بار ٹچ کریں"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"ویجٹس کو شامل نہیں کرسکا۔"</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"غلط صارف نام یا پاس ورڈ۔"</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"اپنا صارف نام یا پاس ورڈ بھول گئے؟\n"<b>"google.com/accounts/recovery"</b>" ملاحظہ کریں۔"</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"اکاؤنٹ چیک کیا جا رہا ہے…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"آپ نے <xliff:g id="NUMBER_0">%d</xliff:g> بار اپنا PIN غلط طریقے سے ٹائپ کیا ہے۔ \n\n <xliff:g id="NUMBER_1">%d</xliff:g> سیکنڈ میں دوبارہ کوشش کریں۔"</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"آپ نے اپنا پاس ورڈ <xliff:g id="NUMBER_0">%d</xliff:g> بار غلط طریقے سے ٹائپ کیا ہے۔ \n\n <xliff:g id="NUMBER_1">%d</xliff:g> سیکنڈ میں دوبارہ کوشش کریں۔"</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"آپ نے اپنا غیر مقفل کرنے کا پیٹرن <xliff:g id="NUMBER_0">%d</xliff:g> بار غلط طریقے سے ڈرا کیا ہے۔ \n\n <xliff:g id="NUMBER_1">%d</xliff:g> سیکنڈ میں دوبارہ کوشش کریں۔"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"آپ نے ٹیبلیٹ کو غیر مقفل کرنے کیلئے <xliff:g id="NUMBER_0">%d</xliff:g> بار غلط طریقے سے کوشش کی ہے۔ <xliff:g id="NUMBER_1">%d</xliff:g> مزید ناکام کوششوں کے بعد، ٹیبلیٹ فیکٹری ڈیفالٹ پر دوبارہ ترتیب دے دیا جائے گا اور صارف کا سبھی ڈیٹا ضائع ہو جائے گا۔"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"آپ نے TV کو غیر مقفل کرنے کیلئے <xliff:g id="NUMBER_0">%d</xliff:g> بار غلط طریقے سے کوشش کی ہے۔ <xliff:g id="NUMBER_1">%d</xliff:g> مزید ناکام کوششوں کے بعد، TV فیکٹری ڈیفالٹ پر ری سیٹ ہو جائے گا اور صارف کا سبھی ڈیٹا ضائع ہو جائے گا۔"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"آپ نے فون کو غیر مقفل کرنے کیلئے <xliff:g id="NUMBER_0">%d</xliff:g> بار غلط طریقے سے کوشش کی ہے۔ <xliff:g id="NUMBER_1">%d</xliff:g> مزید ناکام کوششوں کے بعد، فون فیکٹری ڈیفالٹ پر دوبارہ ترتیب دے دیا جائے گا اور صارف کا سبھی ڈیٹا ضائع ہو جائے گا۔"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"آپ نے <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اپنا PIN غلط طریقے سے ٹائپ کیا ہے۔ \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> سیکنڈ میں دوبارہ کوشش کریں۔"</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"آپ نے اپنا پاس ورڈ <xliff:g id="NUMBER_0">%1$d</xliff:g> بار غلط طریقے سے ٹائپ کیا ہے۔ \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> سیکنڈ میں دوبارہ کوشش کریں۔"</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"آپ نے اپنا غیر مقفل کرنے کا پیٹرن <xliff:g id="NUMBER_0">%1$d</xliff:g> بار غلط طریقے سے ڈرا کیا ہے۔ \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> سیکنڈ میں دوبارہ کوشش کریں۔"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"آپ نے ٹیبلیٹ کو غیر مقفل کرنے کیلئے <xliff:g id="NUMBER_0">%1$d</xliff:g> بار غلط طریقے سے کوشش کی ہے۔ <xliff:g id="NUMBER_1">%2$d</xliff:g> مزید ناکام کوششوں کے بعد، ٹیبلیٹ فیکٹری ڈیفالٹ پر دوبارہ ترتیب دے دیا جائے گا اور صارف کا سبھی ڈیٹا ضائع ہو جائے گا۔"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"آپ نے TV کو غیر مقفل کرنے کیلئے <xliff:g id="NUMBER_0">%1$d</xliff:g> بار غلط طریقے سے کوشش کی ہے۔ <xliff:g id="NUMBER_1">%2$d</xliff:g> مزید ناکام کوششوں کے بعد، TV فیکٹری ڈیفالٹ پر ری سیٹ ہو جائے گا اور صارف کا سبھی ڈیٹا ضائع ہو جائے گا۔"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"آپ نے فون کو غیر مقفل کرنے کیلئے <xliff:g id="NUMBER_0">%1$d</xliff:g> بار غلط طریقے سے کوشش کی ہے۔ <xliff:g id="NUMBER_1">%2$d</xliff:g> مزید ناکام کوششوں کے بعد، فون فیکٹری ڈیفالٹ پر دوبارہ ترتیب دے دیا جائے گا اور صارف کا سبھی ڈیٹا ضائع ہو جائے گا۔"</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"آپ نے ٹیبلٹ کو غیر مقفل کرنے کیلئے <xliff:g id="NUMBER">%d</xliff:g> بار غلط طریقے سے کوشش کی ہے۔ اب ٹیبلیٹ فیکٹری ڈیفالٹ پر دوبارہ ترتیب دے دیا جائے گا۔"</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"آپ نے TV کو غیر مقفل کرنے کیلئے <xliff:g id="NUMBER">%d</xliff:g> بار غلط طریقے سے کوششیں کی ہیں۔ TV اب فیکٹری ڈیفالٹ پر ری سیٹ ہو جائے گا۔"</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"آپ نے فون کو غیر مقفل کرنے کیلئے <xliff:g id="NUMBER">%d</xliff:g> بار غلط طریقے سے کوشش کی ہے۔ اب فون فیکٹری ڈیفالٹ پر دوبارہ ترتیب دے دیا جائے گا۔"</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"آپ نے اپنا غیر مقفل کرنے کا پیٹرن <xliff:g id="NUMBER_0">%d</xliff:g> بار غلط طریقے سے ڈرا کیا ہے۔ <xliff:g id="NUMBER_1">%d</xliff:g> مزید ناکام کوششوں کے بعد، آپ سے ایک ای میل اکاؤنٹ استعمال کرکے اپنا ٹیبلیٹ غیر مقفل کرنے کو کہا جائے گا۔\n\n <xliff:g id="NUMBER_2">%d</xliff:g> سیکنڈ میں دوبارہ کوشش کریں۔"</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"آپ نے اپنا غیر مقفل کرنے کا پیٹرن <xliff:g id="NUMBER_0">%d</xliff:g> بار غلط طریقے سے ڈرا کیا ہے۔ <xliff:g id="NUMBER_1">%d</xliff:g> مزید ناکام کوششوں کے بعد، آپ سے ایک ای میل اکاؤنٹ استعمال کرکے اپنا TV غیر مقفل کرنے کو کہا جائے گا۔\n\n <xliff:g id="NUMBER_2">%d</xliff:g> سیکنڈ میں دوبارہ کوشش کریں۔"</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"آپ نے اپنا غیر مقفل کرنے کا پیٹرن <xliff:g id="NUMBER_0">%d</xliff:g> بار غلط طریقے سے ڈرا کیا ہے۔ <xliff:g id="NUMBER_1">%d</xliff:g> مزید ناکام کوششوں کے بعد، آپ سے ایک ای میل اکاؤنٹ استعمال کرکے اپنا فون غیر مقفل کرنے کو کہا جائے گا۔\n\n <xliff:g id="NUMBER_2">%d</xliff:g> سیکنڈ میں دوبارہ کوشش کریں۔"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"آپ نے اپنا غیر مقفل کرنے کا پیٹرن <xliff:g id="NUMBER_0">%1$d</xliff:g> بار غلط طریقے سے ڈرا کیا ہے۔ <xliff:g id="NUMBER_1">%2$d</xliff:g> مزید ناکام کوششوں کے بعد، آپ سے ایک ای میل اکاؤنٹ استعمال کرکے اپنا ٹیبلیٹ غیر مقفل کرنے کو کہا جائے گا۔\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> سیکنڈ میں دوبارہ کوشش کریں۔"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"آپ نے اپنا غیر مقفل کرنے کا پیٹرن <xliff:g id="NUMBER_0">%1$d</xliff:g> بار غلط طریقے سے ڈرا کیا ہے۔ <xliff:g id="NUMBER_1">%2$d</xliff:g> مزید ناکام کوششوں کے بعد، آپ سے ایک ای میل اکاؤنٹ استعمال کرکے اپنا TV غیر مقفل کرنے کو کہا جائے گا۔\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> سیکنڈ میں دوبارہ کوشش کریں۔"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"آپ نے اپنا غیر مقفل کرنے کا پیٹرن <xliff:g id="NUMBER_0">%1$d</xliff:g> بار غلط طریقے سے ڈرا کیا ہے۔ <xliff:g id="NUMBER_1">%2$d</xliff:g> مزید ناکام کوششوں کے بعد، آپ سے ایک ای میل اکاؤنٹ استعمال کرکے اپنا فون غیر مقفل کرنے کو کہا جائے گا۔\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> سیکنڈ میں دوبارہ کوشش کریں۔"</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"ہٹائیں"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"والیوم کو تجویز کردہ سطح سے زیادہ کریں؟\n\nزیادہ وقت تک اونچی آواز میں سننے سے آپ کی سماعت کو نقصان پہنچ سکتا ہے۔"</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">1 گھنٹہ کیلئے</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> تک"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> تک (اگلا الارم)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"جب تک آپ اسے آف نہ کر دیں"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"جب تک آپ ڈسڑب نہ کریں کو آف نہیں کر دیتے"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-uz-rUZ-watch/strings.xml b/core/res/res/values-uz-rUZ-watch/strings.xml
index 44af51d..4b633e8 100644
--- a/core/res/res/values-uz-rUZ-watch/strings.xml
+++ b/core/res/res/values-uz-rUZ-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_1">%2$d</xliff:g>dan <xliff:g id="NUMBER_0">%1$d</xliff:g> ilova."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Sensorlar"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"kontaktlarga kirish"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"mazkur soatning joylashgan joyini ko‘rishga ruxsat berish"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"taqvim ma’lumotlariga kirish"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"SMS xabarlarni yuborish va ko‘rish"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"Soatingizdagi rasmlar, media va fayllarga kirish"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"ovoz yozib olish"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"suratga olish va video yozib olish"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"telefon qo‘ng‘iroqlarini amalga oshirish va boshqarish"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"organizm holati haqidagi sezgich ma’lumotlariga kirish"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"holat qatorida ko‘rinishi"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"tana sezgichlari (m-n, yurak urishi sensori) ma’lumotlaridan foydalanishga ruxsat"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"aniq joylashuv (GPS va tarmoqqa asoslanib) ma’lumotlaridan foydalanishga ruxsat"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"taxminiy joylashuv (tarmoq asosida) ma’lumotlaridan foydalanishga ruxsat"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"SIM kartaga buyruqlar yuborish"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"to‘liq tarmoqdan foydalanish ruxsatiga ega"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"profil va qurilma egalarini boshqarish"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"WiMAX holatini o‘zgartirish"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"Android Beam o‘tkazmasi holati haqidagi ma’lumotlarni olish"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"media chiqishni yo‘naltirish"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"o‘rnatish seansi ma’lumotlarini o‘qish"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"paketlarni o‘rnatish so‘rovini yuborish"</string>
</resources>
diff --git a/core/res/res/values-uz-rUZ/strings.xml b/core/res/res/values-uz-rUZ/strings.xml
index b7ae992..278ef6d 100644
--- a/core/res/res/values-uz-rUZ/strings.xml
+++ b/core/res/res/values-uz-rUZ/strings.xml
@@ -62,8 +62,8 @@
<string name="needPuk2" msgid="4526033371987193070">"SIM kartani blokdan chiqarish uchun PUK2 raqamini kiriting."</string>
<string name="enablePin" msgid="209412020907207950">"Ishlamadi, SIM/RUIM qulfni yoqish."</string>
<plurals name="pinpuk_attempts" formatted="false" msgid="1251012001539225582">
- <item quantity="other">Yana <xliff:g id="NUMBER_1">%d</xliff:g> ta muvaffaqiyatsiz urinishdan so‘ng SIM-karta qulflanadi.</item>
- <item quantity="one">Yana <xliff:g id="NUMBER_0">%d</xliff:g> ta muvaffaqiyatsiz urinishdan so‘ng SIM-karta qulflanadi.</item>
+ <item quantity="other">Yana <xliff:g id="NUMBER_1">%d</xliff:g> ta muvaffaqiyatsiz urinishdan so‘ng SIM karta qulflanadi.</item>
+ <item quantity="one">Yana <xliff:g id="NUMBER_0">%d</xliff:g> ta muvaffaqiyatsiz urinishdan so‘ng SIM karta qulflanadi.</item>
</plurals>
<string name="imei" msgid="2625429890869005782">"IMEI"</string>
<string name="meid" msgid="4841221237681254195">"MEID"</string>
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Bunga kredit karta raqamlari va parollar kabi shaxsiy ma’lumotlar kiradi."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"holat panelini o‘zgartirish yoki o‘chirish"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Ilova holat panelini o‘chirib qo‘yishi hamda tizim ikonkalarini qo‘shishi yoki olib tashlashi mumkin."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"holat paneli"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"holat qatorida ko‘rinishi"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Ilova holat qatorining o‘rnini egallashi mumkin."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"holat panelini yoyish/yig‘ish"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Ilova holat panelini yoyishi va yig‘ishi mumkin."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Ilovaga WAP xabarlarni qabul qilish va ularni qayta ishlash uchun ruxsat beradi. Ushbu huquq sizga ko‘rsatmasdan sizga yuborilgan xabarlarni kuzatish yoki o‘chirish xususiyatiga ham ega."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"ishlab turgan ilovalar to‘g‘risida ma’lumot olish"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Ilovaga hozirda va so‘nggi ishga tushirilgan vazifalar haqida to‘liq ma’lumot olishiga ruxsat beradi. Bu ilovaga qurilmadagi ishlatilayotgan ilovalar haqidagi ma’lumotlarga ega bo‘lishiga ruxsat berishi mumkin."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Profil va qurilma egalarini boshqarish"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"profil va qurilma egalarini boshqarish"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Ilovaga profil egalari va qurilma egalarini aniqlash uchun ruxsat beradi."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"ishlab turgan ilovalarni qayta tartiblash"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Ilovalarga vazifalarni old va orqa fonga o‘tkazish uchun ruxsat beradi. Ilova buni sizning yordamingizsiz bajarishi mumkin."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Ilovaga planshetingizdagi qo‘ng‘iroq jurnallari, kiruvchi va chiquvchi qo‘ng‘rioqlar haqidagi ma’lumotlarni o‘zgartirishga ruxsat beradi. Zararli ilovalar bundan qo‘ng‘iroqlar jurnalini o‘zgartirish yoki o‘chirish uchun foydalanishi mumkin."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Ilovaga televizoringizdagi qo‘ng‘iroqlar jurnali, kirish va chiqish qo‘ng‘rioqlari haqidagi ma’lumotlarni o‘zgartirish huquqini beradi. Zararli ilovalar undan qo‘ng‘iroqlar jurnalini o‘zgartirish yoki o‘chirish uchun foydalanishi mumkin."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Ilovaga telefoningizdagi qo‘ng‘iroq jurnallari, kiruvchi va chiquvchi qo‘ng‘rioqlar haqidagi ma’lumotlarni o‘zgartirishga ruxsat beradi. Zararli ilovalar bundan qo‘ng‘iroqlar jurnalini o‘zgartirish yoki o‘chirish uchun foydalanishi mumkin."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"sezgichlar (m-n, yurak urishi)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"tana sezgichlari (m-n, yurak urishi sensori) ma’lumotlaridan foydalanishga ruxsat"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Ilovaga sezgichlardan olingan jismoniy holatingiz haqidagi ma’lumotlarni, masalan, yurak urishini kuzatish uchun ruxsat beradi."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"taqvimdagi tadbirlarni maxfiy ma’lumotlari bilan birga o‘qish"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Ilovaga planshetingizda joylashgan va do‘stlaringiz yoki hamkasblaringiz tomonidan qo‘shilgan barcha taqvim tadbirlarini o‘qishga ruxsat beradi. Bu ilovaga maxfiyligi va muhimligidan qat’iy nazar taqvim ma’lumotlaringizni ulashish yoki saqlashga ruxsat berishi mumkin."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Ilovaga telefoningizda o‘zgartirishingiz mumkin bo‘lgan, shuningdek, do‘stlaringiz va hamkasblaringizning tadbirlarini qo‘shish, o‘chirish va o‘zgartirish uchun ruxsat beradi. Bu ilovaga go‘yoki taqvim egalari nomidan kelgan xabarlarni jo‘natishga yoki egasiga bildirmasdan tadbirlarni o‘zgartirishga ruxsat berishi mumkin."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"qo‘shimcha manzillarga kirish buyruqlari"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Ilovaga qo‘shimcha joylashuv xizmati buyruqlaridan foydalanishga ruxsat beradi. Uning yordamida ilova GPS yoki boshqa joylashuv ma’lumoti manbalarining ishlashiga xalaqit qilishi mumkin."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"aniq joylashuv (GPS va tarmoqqa asoslanib)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"aniq joylashuv (GPS va tarmoqqa asoslanib) ma’lumotlaridan foydalanishga ruxsat"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Ilovaga global joylashuvni aniqlash tizimi (GPS) yoki Wi-Fi va uyali tarmoq antennalari kabi tarmoq joylashuv manbalaridan foydalanib, aniq joylashuvingizni topishga ruxsat beradi. Ushbu joylashuv xizmatlari yoqib qo‘yilgan bo‘lishi va qurilmangizdagi ilovaga ulardan foydalanish uchun mavjud bo‘lishi kerak. Ilovalar bundan foydalanib, sizning joylashuvingizni aniqlaydi. Bu batareya quvvatini ko‘proq sarflaydi."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"yaqin manzil (tarmoq)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"taxminiy joylashuv (tarmoq asosida) ma’lumotlaridan foydalanishga ruxsat"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Ilovaga sizning taxminiy joylashuvingizni topishga ruxsat beradi. Ushbu joylashuv Wi-Fi va uyali tarmoq antennalari kabi tarmoq joylashuv manbalaridan foydlanuvchi joylashuv xizmatlari orqali aniqlanadi. Ushbu joylashuv xizmatlari yoqib qo‘yilgan bo‘lishi va qurilmangizdagi ilovaga ulardan foydalanish uchun mavjud bo‘lishi kerak. Ilovalar bundan foydalanib, sizning taxminiy joylashuvingizni aniqlaydi."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"audio sozlamalaringizni o‘zgartirish"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Ilovalarga tovush va ovoz chiqarish uchun foydalaniladigan karnay kabi global audio sozlamalarini o‘zgartirish uchun ruxsat beradi."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"ovoz yozib olish"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Ilovaga mikrofon yordamida audio yozish uchun ruxsat beradi. Bu huquq ilovaga ruxsatingizsiz audio fayllarni yozib olishga ruxsat beradi."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"sim orqali ulanish"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"SIM kartaga buyruqlar yuborish"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Dasturga SIM kartaga buyruqlar jo‘natishga ruxsat beradi. Bu juda ham xavfli."</string>
<string name="permlab_camera" msgid="3616391919559751192">"rasmga tushirish va videoga olish"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Ilovaga kameradan foydalanib rasm va videoga olishga ruxsat beradi. Bu ruxsat ilovaga sizdan tasdiqlashni so‘ramasdan kameradan foydalanishga imkon beradi."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Ilovaga telefondagi hisoblar ro‘yxatini olishga ruxsat beradi. Bunga siz o‘rnatgan ilovalar tomonidan yaratilgan har qanday hisoblar kirishi mumkin."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"tarmoq ulanishlarini ko‘rish"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Ilovaga mavjud va ulangan tarmoqlar kabi tarmoqqa ulanishlar haqidagi ma’lumotni ko‘rish imkonini beradi."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"butun tarmoqqa ruxsat"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"to‘liq tarmoqdan foydalanish ruxsatiga ega"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Ilovaga tarmoq ulagichlarini yaratish va odatdagi tarmoq protokollaridan foydalanishga ruxsat beradi. Brauzer va boshqa ilovalar internetga ma’lumot jo‘natish uchun imkoniyat yaratadi. Natijada, internetga ma’lumot jo‘natish uchun bu ruxsat talab qilinmaydi."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"tarmoqqa ulanishni o‘zgartirish"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Ilova tarmoqqa ulanish holatini o‘zgartirishi mumkin."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Ilova Bluetooth funksiyali mahalliy telefon sozlamalarini sozlashi hamda masofadan turib qurilmalarni aniqlash va ular bilan juftlashishni amalga oshirishi mumkin."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"WiMAX’ga ulanish va uzish"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Ilovaga har qanday ulangan WiMAX tarmoqlari haqida va yoqilgan WiMAX tarmoqlarini aniqlashga ruxsat beradi."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX holatini o‘zgartirish"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"WiMAX holatini o‘zgartirish"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Ilovaga planshetni WiMAX tarmoqlariga ulashga va ulardan uzishga ruxsat beradi."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Ilovaga televizorni WiMAX tarmoqlariga ulash va ulardan uzish huquqini beradi."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Ilovaga telefonni WiMAX tarmoqlariga ulashga va ulardan uzishga ruxsat beradi."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Ilova sensorli ekranning muvozanat ko‘rsatkichlarini o‘zgartirishi mumkin. Oddiy ilovalar uchun talab qilinmaydi."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"DRM sertifikatlariga kirish"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Ilova DRM sertifikatlarini sinxronlashi va ulardan foydalanishi mumkin. Ushbu ruxsatnoma faqat maxsus ilovalar uchun talab qilinadi."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Android Beam uzatish holatini olish"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"Android Beam o‘tkazmasi holati haqidagi ma’lumotlarni olish"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Ushbu ilovaga joriy Android Beam uzatishlari haqida ma\'lumotlarni olish ruxsati berilsin."</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"DRM sertifikatlarini o‘chirib tashlash"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Ilovaga DRM sertifikatlarini o‘chirib tashlash uchun ruxsat beradi. Oddiy ilovalar uchun talab qilinmaydi."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Noto‘g‘ri PIN-kod."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Qulfni ochish uchun avval \"Menu\"ni, so‘ngra 0 raqamini bosing."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Favqulodda raqam"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Aloqa yo‘q."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Xizmat mavjud emas"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Ekran qulflangan."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Qulfdan chiqarish yoki favqulodda qo‘ng‘iroqni amalga oshirish uchun \"Menyu\"ni bosing."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Qulfni ochish uchun \"Menyu\"ga bosing."</string>
@@ -661,14 +661,14 @@
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Qaytadan urining"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Qaytadan urining"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Yuzni tanitib qulfni ochishga urinish miqdoridan oshib ketdi"</string>
- <string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"SIM-karta yo‘q"</string>
- <string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Planshetingizga SIM-karta yo‘q."</string>
- <string name="lockscreen_missing_sim_message" product="tv" msgid="1943633865476989599">"Televizorda SIM-karta yo‘q."</string>
- <string name="lockscreen_missing_sim_message" product="default" msgid="2186920585695169078">"Telefoningizga SIM-karta yo‘q."</string>
+ <string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"SIM karta yo‘q"</string>
+ <string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Planshetingizga SIM karta yo‘q."</string>
+ <string name="lockscreen_missing_sim_message" product="tv" msgid="1943633865476989599">"Televizorda SIM karta yo‘q."</string>
+ <string name="lockscreen_missing_sim_message" product="default" msgid="2186920585695169078">"Telefoningizga SIM karta yo‘q."</string>
<string name="lockscreen_missing_sim_instructions" msgid="5372787138023272615">"SIM kartani soling."</string>
- <string name="lockscreen_missing_sim_instructions_long" msgid="3526573099019319472">"SIM-karta solinmagan yoki uni o‘qib bo‘lmaydi. SIM-kartani soling."</string>
- <string name="lockscreen_permanent_disabled_sim_message_short" msgid="5096149665138916184">"Foydalanib bo‘lmaydigan SIM-karta."</string>
- <string name="lockscreen_permanent_disabled_sim_instructions" msgid="910904643433151371">"SIM-kartangiz butunlay bloklab qo‘yilgan.\n Yangi SIM-karta olish uchun aloqa operatoringiz bilan bog‘laning."</string>
+ <string name="lockscreen_missing_sim_instructions_long" msgid="3526573099019319472">"SIM karta solinmagan yoki uni o‘qib bo‘lmaydi. SIM kartani soling."</string>
+ <string name="lockscreen_permanent_disabled_sim_message_short" msgid="5096149665138916184">"Foydalanib bo‘lmaydigan SIM karta."</string>
+ <string name="lockscreen_permanent_disabled_sim_instructions" msgid="910904643433151371">"SIM kartangiz butunlay bloklab qo‘yilgan.\n Yangi SIM karta olish uchun aloqa operatoringiz bilan bog‘laning."</string>
<string name="lockscreen_transport_prev_description" msgid="6300840251218161534">"Avvalgi musiqa"</string>
<string name="lockscreen_transport_next_description" msgid="573285210424377338">"Keyingi musiqa"</string>
<string name="lockscreen_transport_pause_description" msgid="3980308465056173363">"To‘xtatib turish"</string>
@@ -678,19 +678,19 @@
<string name="lockscreen_transport_ffw_description" msgid="42987149870928985">"Oldinga o‘tkazish"</string>
<string name="emergency_calls_only" msgid="6733978304386365407">"Faqat favqulodda qo‘ng‘iroqlar"</string>
<string name="lockscreen_network_locked_message" msgid="143389224986028501">"Tarmoq qulflangan"</string>
- <string name="lockscreen_sim_puk_locked_message" msgid="7441797339976230">"SIM-karta PUK kod bilan qulflangan."</string>
+ <string name="lockscreen_sim_puk_locked_message" msgid="7441797339976230">"SIM karta PUK kod bilan qulflangan."</string>
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Foydalanuvchi qo‘llanmasiga qarang yoki Abonentlarni qo‘llab-quvvatlash markaziga murojaat qiling."</string>
- <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM-karta qulflangan."</string>
- <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"SIM-karta qulfdan chiqarilmoqda…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Siz chizmali kalitni <xliff:g id="NUMBER_0">%d</xliff:g> marta noto‘g‘ri kiritdingiz. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> soniyadan so‘ng qayta urining."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Siz parolni <xliff:g id="NUMBER_0">%d</xliff:g> marta noto‘g‘ri kiritdingiz. \n\n <xliff:g id="NUMBER_1">%d</xliff:g> soniyadan so‘ng qayta urining."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Siz PIN-kodni <xliff:g id="NUMBER_0">%d</xliff:g> marta noto‘g‘ri kiritdingiz. \n\n <xliff:g id="NUMBER_1">%d</xliff:g> soniyadan so‘ng qayta urining."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Siz chizmali kalitni <xliff:g id="NUMBER_0">%d</xliff:g> marta noto‘g‘ri kiritdingiz. Telefon qulfini ochish uchun yana <xliff:g id="NUMBER_1">%d</xliff:g> marta noto‘g‘ri urinish qilsangiz, sizdan Google hisobingizga kirish talab qilinadi. \n\n <xliff:g id="NUMBER_2">%d</xliff:g> soniyadan so‘ng qayta urining."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Siz chizmali kalitni <xliff:g id="NUMBER_0">%d</xliff:g> marta noto‘g‘ri kiritdingiz. Agar uni yana <xliff:g id="NUMBER_1">%d</xliff:g> marta noto‘g‘ri kiritsangiz, televizoringizni qulfdan chiqarish uchun Google hisobingizga kirish talab qilinadi.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> soniyadan so‘ng qaytadan urining."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Siz chizmali kalitni <xliff:g id="NUMBER_0">%d</xliff:g> marta noto‘g‘ri kiritdingiz. Telefon qulfini ochish uchun yana <xliff:g id="NUMBER_1">%d</xliff:g> marta noto‘g‘ri urinish qilsangiz, sizdan Google hisobingizga kirish talab qilinadi.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> soniyadan so‘ng qayta urining."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Planshet qulfini <xliff:g id="NUMBER_0">%d</xliff:g> marta ochishga urinib ko‘rdingiz. <xliff:g id="NUMBER_1">%d</xliff:g> marta muvaffaqiyatsiz urinishlardan so‘ng, planshet ishlab chiqarilgan holatiga tiklanadi va barcha foydalanuvchi ma’lumotlari yo‘qoladi."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Siz televizorni qulfdan chiqarish parolini <xliff:g id="NUMBER_0">%d</xliff:g> marta noto‘g‘ri kiritdingiz. Agar uni yana <xliff:g id="NUMBER_1">%d</xliff:g> marta noto‘g‘ri kiritsangiz, televizoringizda zavod sozlamalari qayta tiklanadi hamda undagi barcha ma’lumotlaringiz o‘chib ketadi."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Telefon qulfini <xliff:g id="NUMBER_0">%d</xliff:g> marta ochishga urinib ko‘rdingiz. <xliff:g id="NUMBER_1">%d</xliff:g> marta muvaffaqiyatsiz urinishlardan so‘ng, telefon ishlab chiqarilgan holatiga tiklanadi va barcha foydalanuvchi ma’lumotlari yo‘qoladi."</string>
+ <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM karta qulflangan."</string>
+ <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"SIM karta qulfdan chiqarilmoqda…"</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Siz chizmali kalitni <xliff:g id="NUMBER_0">%1$d</xliff:g> marta noto‘g‘ri kiritdingiz. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> soniyadan so‘ng qayta urining."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Siz parolni <xliff:g id="NUMBER_0">%1$d</xliff:g> marta noto‘g‘ri kiritdingiz. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> soniyadan so‘ng qayta urining."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Siz PIN-kodni <xliff:g id="NUMBER_0">%1$d</xliff:g> marta noto‘g‘ri kiritdingiz. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> soniyadan so‘ng qayta urining."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Siz chizmali kalitni <xliff:g id="NUMBER_0">%1$d</xliff:g> marta noto‘g‘ri kiritdingiz. Telefon qulfini ochish uchun yana <xliff:g id="NUMBER_1">%2$d</xliff:g> marta noto‘g‘ri urinish qilsangiz, sizdan Google hisobingizga kirish talab qilinadi. \n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> soniyadan so‘ng qayta urining."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Siz chizmali kalitni <xliff:g id="NUMBER_0">%1$d</xliff:g> marta noto‘g‘ri kiritdingiz. Agar uni yana <xliff:g id="NUMBER_1">%2$d</xliff:g> marta noto‘g‘ri kiritsangiz, televizoringizni qulfdan chiqarish uchun Google hisobingizga kirish talab qilinadi.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> soniyadan so‘ng qaytadan urining."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Siz chizmali kalitni <xliff:g id="NUMBER_0">%1$d</xliff:g> marta noto‘g‘ri kiritdingiz. Telefon qulfini ochish uchun yana <xliff:g id="NUMBER_1">%2$d</xliff:g> marta noto‘g‘ri urinish qilsangiz, sizdan Google hisobingizga kirish talab qilinadi.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> soniyadan so‘ng qayta urining."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Planshet qulfini <xliff:g id="NUMBER_0">%1$d</xliff:g> marta ochishga urinib ko‘rdingiz. <xliff:g id="NUMBER_1">%2$d</xliff:g> marta muvaffaqiyatsiz urinishlardan so‘ng, planshet ishlab chiqarilgan holatiga tiklanadi va barcha foydalanuvchi ma’lumotlari yo‘qoladi."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Siz televizorni qulfdan chiqarish parolini <xliff:g id="NUMBER_0">%1$d</xliff:g> marta noto‘g‘ri kiritdingiz. Agar uni yana <xliff:g id="NUMBER_1">%2$d</xliff:g> marta noto‘g‘ri kiritsangiz, televizoringizda zavod sozlamalari qayta tiklanadi hamda undagi barcha ma’lumotlaringiz o‘chib ketadi."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Telefon qulfini <xliff:g id="NUMBER_0">%1$d</xliff:g> marta ochishga urinib ko‘rdingiz. <xliff:g id="NUMBER_1">%2$d</xliff:g> marta muvaffaqiyatsiz urinishlardan so‘ng, telefon ishlab chiqarilgan holatiga tiklanadi va barcha foydalanuvchi ma’lumotlari yo‘qoladi."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Planshet qulfini <xliff:g id="NUMBER">%d</xliff:g> marta ochishga urinib ko‘rdingiz. Planshet hozir ishlab chiqarilgan holatiga tiklanadi."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Siz televizorni qulfdan chiqarish parolini <xliff:g id="NUMBER">%d</xliff:g> marta noto‘g‘ri kiritdingiz. Endi, televizoringizda zavod sozlamalari qayta tiklanadi."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Telefon qulfini <xliff:g id="NUMBER">%d</xliff:g> marta ochishga urinib ko‘rdingiz. Telefon hozir ishlab chiqarilgan holatiga tiklanadi."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"so‘z"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"link"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"qator"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Ishlab chiqarish sinovi amalga oshmadi"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"The FACTORY_TEST amali faqatgina /system/app ichiga o‘rnatilgan paketlar bilan ishlay oladi."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"FACTORY_TEST amalini bajarish uchun birorta ham paket topilmadi."</string>
@@ -1004,10 +1002,10 @@
<string name="sms_short_code_remember_undo_instruction" msgid="4960944133052287484">"Siz buni keyinroq sozlamalar > ilovalar menusidan o‘zgartirishingiz mumkin"</string>
<string name="sms_short_code_confirm_always_allow" msgid="3241181154869493368">"Doimo ruxsat berilsin"</string>
<string name="sms_short_code_confirm_never_allow" msgid="446992765774269673">"Hech qachon ruxsat berilmasin"</string>
- <string name="sim_removed_title" msgid="6227712319223226185">"SIM-karta olib tashlandi"</string>
+ <string name="sim_removed_title" msgid="6227712319223226185">"SIM karta olib tashlandi"</string>
<string name="sim_removed_message" msgid="5450336489923274918">"Mobil tarmoqqa ulanish uchun faol SIM kartani joylang va qurilmani o‘chirib yoqing."</string>
<string name="sim_done_button" msgid="827949989369963775">"Tayyor"</string>
- <string name="sim_added_title" msgid="3719670512889674693">"SIM-karta qo‘shildi"</string>
+ <string name="sim_added_title" msgid="3719670512889674693">"SIM karta qo‘shildi"</string>
<string name="sim_added_message" msgid="7797975656153714319">"Mobil tarmoqqa ulanish uchun qurilmangizni o‘chirib yoqing."</string>
<string name="sim_restart_button" msgid="4722407842815232347">"O‘chirib-yoqish"</string>
<string name="time_picker_dialog_title" msgid="8349362623068819295">"Vaqtni o‘rnatish"</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Formatlanmoqda…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Kiritilmagan"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Hech qanday mos faoliyat topilmadi."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Media chiqishni yo‘naltirish"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"media chiqishni yo‘naltirish"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Ilovaga media chiqish ovozini boshqa tashqi qurilmalarga yo‘naltirish uchun ruxsat beradi."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"O‘rnatilgan seanslarni o‘qish"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"o‘rnatish seansi ma’lumotlarini o‘qish"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Ilovaga o‘rnatilgan seanslarni o‘qish uchun ruxsat beradi. Bu unga faol paket o‘rnatmalari haqidagi ma’lumotlarni ko‘rish imkonini beradi."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Paketlarni o‘rnatish so‘rovini yuborish"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"paketlarni o‘rnatish so‘rovini yuborish"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Ilovaga paketlarni o‘rnatish so‘rovini yuborish imkonini beradi."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Masshtabni o‘zgartirish uchun ikki marta bosing"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Vidjet qo‘shilmadi."</string>
@@ -1191,8 +1189,8 @@
<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>
<string name="storage_internal" msgid="4891916833657929263">"Ichki 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_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>
<string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB xotira qurilmasi"</string>
<string name="storage_usb" msgid="3017954059538517278">"USB xotira"</string>
@@ -1268,10 +1266,10 @@
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"SIM kartaning PIN kodini kiriting"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"PIN kodni tering"</string>
<string name="kg_password_instructions" msgid="5753646556186936819">"Parol kiriting"</string>
- <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"SIM-karta hozir o‘chirilgan. Davom etish uchun PUK kodni kiriting. To‘liqroq ma’lumot olish uchun tarmoq operatori bilan bog‘laning."</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"SIM karta hozir o‘chirilgan. Davom etish uchun PUK kodni kiriting. To‘liqroq ma’lumot olish uchun tarmoq operatori bilan bog‘laning."</string>
<string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"So‘ralgan PIN kodni kiriting"</string>
<string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"So‘ralgan PIN kodni tasdiqlang"</string>
- <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"SIM-karta qulfi ochilmoqda…"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"SIM karta qulfi ochilmoqda…"</string>
<string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Xato PIN kodi."</string>
<string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"4 tadan 8 ta raqamgacha bo‘lgan PIN kodni kiriting."</string>
<string name="kg_invalid_sim_puk_hint" msgid="6025069204539532000">"PUK kod 8 ta raqam bo‘lishi shart."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Foydalanuvchi nomi yoki parol xato."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Foydalanuvchi nomingiz yoki parolingizni unutdingizmi?\n "<b>"google.com/accounts/recovery"</b>"ga tashrif buyuring."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Hisob tekshirilmoqda…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Siz PIN-kodni <xliff:g id="NUMBER_0">%d</xliff:g> marta noto‘g‘ri kiritdingiz. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> soniyadan so‘ng qayta urinib ko‘ring."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Siz parolni <xliff:g id="NUMBER_0">%d</xliff:g> marta noto‘g‘ri kiritdingiz. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> soniyadan so‘ng qayta urinib ko‘ring."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Siz chizmali kalitni <xliff:g id="NUMBER_0">%d</xliff:g> marta noto‘g‘ri kiritdingiz. \n\n<xliff:g id="NUMBER_1">%d</xliff:g> soniyadan so‘ng qayta urinib ko‘ring."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Siz planshet qulfini ochish uchun <xliff:g id="NUMBER_0">%d</xliff:g> marta muvaffaqiyatsiz urindingiz. <xliff:g id="NUMBER_1">%d</xliff:g> marta muvaffaqiyatsiz urinishlardan so‘ng planshetning zavod sozlamalari tiklanadi va barcha foydalanuvchi ma’lumotlari o‘chiriladi."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Siz televizorni qulfdan chiqarish parolini <xliff:g id="NUMBER_0">%d</xliff:g> marta noto‘g‘ri kiritdingiz. Agar uni yana <xliff:g id="NUMBER_1">%d</xliff:g> marta noto‘g‘ri kiritsangiz, televizoringizda zavod sozlamalari qayta tiklanadi hamda undagi barcha ma’lumotlaringiz o‘chib ketadi."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Siz telefon qulfini ochish uchun <xliff:g id="NUMBER_0">%d</xliff:g> marta muvaffaqiyatsiz urindingiz. <xliff:g id="NUMBER_1">%d</xliff:g> marta muvaffaqiyatsiz urinishlardan so‘ng telefonning zavod sozlamalari tiklanadi va barcha foydalanuvchi ma’lumotlari o‘chiriladi."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Siz PIN-kodni <xliff:g id="NUMBER_0">%1$d</xliff:g> marta noto‘g‘ri kiritdingiz. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> soniyadan so‘ng qayta urinib ko‘ring."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Siz parolni <xliff:g id="NUMBER_0">%1$d</xliff:g> marta noto‘g‘ri kiritdingiz. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> soniyadan so‘ng qayta urinib ko‘ring."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Siz chizmali kalitni <xliff:g id="NUMBER_0">%1$d</xliff:g> marta noto‘g‘ri kiritdingiz. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> soniyadan so‘ng qayta urinib ko‘ring."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Siz planshet qulfini ochish uchun <xliff:g id="NUMBER_0">%1$d</xliff:g> marta muvaffaqiyatsiz urindingiz. <xliff:g id="NUMBER_1">%2$d</xliff:g> marta muvaffaqiyatsiz urinishlardan so‘ng planshetning zavod sozlamalari tiklanadi va barcha foydalanuvchi ma’lumotlari o‘chiriladi."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Siz televizorni qulfdan chiqarish parolini <xliff:g id="NUMBER_0">%1$d</xliff:g> marta noto‘g‘ri kiritdingiz. Agar uni yana <xliff:g id="NUMBER_1">%2$d</xliff:g> marta noto‘g‘ri kiritsangiz, televizoringizda zavod sozlamalari qayta tiklanadi hamda undagi barcha ma’lumotlaringiz o‘chib ketadi."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Siz telefon qulfini ochish uchun <xliff:g id="NUMBER_0">%1$d</xliff:g> marta muvaffaqiyatsiz urindingiz. <xliff:g id="NUMBER_1">%2$d</xliff:g> marta muvaffaqiyatsiz urinishlardan so‘ng telefonning zavod sozlamalari tiklanadi va barcha foydalanuvchi ma’lumotlari o‘chiriladi."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Planshet qulfini ochish uchun <xliff:g id="NUMBER">%d</xliff:g> marta muvaffaqiyatsiz urinib ko‘rdingiz. Planshetning hozir zavod sozlamari tiklanadi."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Siz televizorni qulfdan chiqarish parolini <xliff:g id="NUMBER">%d</xliff:g> marta noto‘g‘ri kiritdingiz. Endi, televizoringizda zavod sozlamalari qayta tiklanadi."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Telefon qulfini ochish uchun <xliff:g id="NUMBER">%d</xliff:g> marta muvaffaqiyatsiz urinib ko‘rdingiz. Telefonning hozir zavod sozlamari tiklanadi."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Siz chizmali kalitni <xliff:g id="NUMBER_0">%d</xliff:g> marta noto‘g‘ri kiritdingiz. <xliff:g id="NUMBER_1">%d</xliff:g> marta muvaffaqiyatsiz urinishdan so‘ng, sizdan e-pochtangizdan foydalanib, planshet qulfini ochishingiz so‘raladi.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> soniyadan so‘ng yana urinib ko‘ring."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Siz chizmali kalitni <xliff:g id="NUMBER_0">%d</xliff:g> marta noto‘g‘ri kiritdingiz. Agar uni yana <xliff:g id="NUMBER_1">%d</xliff:g> marta noto‘g‘ri kiritsangiz, televizoringizni qulfdan chiqarish uchun sizda e-pochta hisobingizga kirish talab qilinadi.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> soniyadan so‘ng qaytadan urining."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Siz chizmali kalitni <xliff:g id="NUMBER_0">%d</xliff:g> marta noto‘g‘ri chizdingiz. <xliff:g id="NUMBER_1">%d</xliff:g> marta muvaffaqiyatsiz urinishdan so‘ng, sizdan e-pochtangizdan foydalanib, telefon qulfini ochishingiz so‘raladi.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> soniyadan so‘ng yana urinib ko‘ring."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Siz chizmali kalitni <xliff:g id="NUMBER_0">%1$d</xliff:g> marta noto‘g‘ri kiritdingiz. <xliff:g id="NUMBER_1">%2$d</xliff:g> marta muvaffaqiyatsiz urinishdan so‘ng, sizdan e-pochtangizdan foydalanib, planshet qulfini ochishingiz so‘raladi.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> soniyadan so‘ng yana urinib ko‘ring."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Siz chizmali kalitni <xliff:g id="NUMBER_0">%1$d</xliff:g> marta noto‘g‘ri kiritdingiz. Agar uni yana <xliff:g id="NUMBER_1">%2$d</xliff:g> marta noto‘g‘ri kiritsangiz, televizoringizni qulfdan chiqarish uchun sizda e-pochta hisobingizga kirish talab qilinadi.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> soniyadan so‘ng qaytadan urining."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Siz chizmali kalitni <xliff:g id="NUMBER_0">%1$d</xliff:g> marta noto‘g‘ri chizdingiz. <xliff:g id="NUMBER_1">%2$d</xliff:g> marta muvaffaqiyatsiz urinishdan so‘ng, sizdan e-pochtangizdan foydalanib, telefon qulfini ochishingiz so‘raladi.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> soniyadan so‘ng yana urinib ko‘ring."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"O‘chirish"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Ovoz balandligi tavsiya etilgan darajadan ham yuqori ko‘tarilsinmi?\n\nUzoq vaqt davomida baland ovozda tinglash eshitish qobiliyatingizga salbiy ta’sir ko‘rsatishi mumkin."</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">1 soat</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Ushbu vaqtgacha: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> gacha (keyingi signal)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Men o‘chirmaguncha"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"“Bezovta qilinmasin” rejimi o‘chirilmaguncha"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-vi-watch/strings.xml b/core/res/res/values-vi-watch/strings.xml
index 8bfd3df..f6c970d 100644
--- a/core/res/res/values-vi-watch/strings.xml
+++ b/core/res/res/values-vi-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Ứng dụng <xliff:g id="NUMBER_0">%1$d</xliff:g> / <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Cảm biến"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"truy cập danh bạ của bạn"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"truy cập vị trí của đồng hồ này"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"truy cập lịch của bạn"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"gửi và xem tin nhắn SMS"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"truy cập ảnh, phương tiện và tệp trên đồng hồ của bạn"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"ghi âm"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"chụp ảnh và quay video"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"thực hiện và quản lý cuộc gọi điện thoại"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"truy cập dữ liệu cảm biến về dấu hiệu sinh tồn của bạn"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"trở thành thanh trạng thái"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"truy cập cảm biến cơ thể (như máy đo nhịp tim)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"truy cập vị trí chính xác (dựa vào mạng và GPS)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"truy cập vị trí gần đúng (dựa vào mạng)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"gửi lệnh đến SIM"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"có quyền truy cập mạng đầy đủ"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"quản lý chủ sở hữu thiết bị và hồ sơ"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"thay đổi trạng thái WiMAX"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"nhận trạng thái chuyển của Android Beam"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"định tuyến thiết bị ra phương tiện"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"đọc phiên cài đặt"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"yêu cầu gói cài đặt"</string>
</resources>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 83b0d18..439fe25 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Bao gồm dữ liệu cá nhân chẳng hạn như số thẻ tín dụng và mật khẩu."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"vô hiệu hóa hoặc sửa đổi thanh trạng thái"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Cho phép ứng dụng vô hiệu hóa thanh trạng thái hoặc thêm và xóa biểu tượng hệ thống."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"thanh trạng thái"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"trở thành thanh trạng thái"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Cho phép ứng dụng trở thành thanh trạng thái."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"mở rộng/thu gọn thanh trạng thái"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Cho phép ứng dụng mở rộng hoặc thu gọn thanh trạng thái."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Cho phép ứng dụng nhận và xử lý tin nhắn WAP. Quyền này bao gồm khả năng giám sát hoặc xóa tin nhắn được gửi cho bạn mà không hiển thị chúng cho bạn."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"truy xuất các ứng dụng đang chạy"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Cho phép ứng dụng truy xuất thông tin về các công việc đã và đang chạy gần đây. Việc này có thể cho phép ứng dụng phát hiện thông tin về những ứng dụng nào đã được sử dụng trên thiết bị."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Quản lý chủ sở hữu thiết bị và hồ sơ"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"quản lý chủ sở hữu thiết bị và hồ sơ"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Cho phép ứng dụng đặt chủ sở hữu hồ sơ và chủ sở hữu thiết bị."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"sắp xếp lại những ứng dụng đang chạy"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Cho phép ứng dụng di chuyển công việc sang nền trước và nền sau. Ứng dụng có thể thực hiện việc này mà không cần bạn nhập."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Cho phép ứng dụng sửa đổi nhật ký cuộc gọi trên máy tính bảng của bạn, bao gồm dữ liệu về các cuộc gọi đến và gọi đi. Các ứng dụng độc hại có thể sử dụng quyền này để xóa hoặc sửa đổi nhật ký cuộc gọi của bạn."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Cho phép ứng dụng sửa đổi nhật ký cuộc gọi trên TV của bạn, bao gồm dữ liệu về các cuộc gọi đến và gọi đi. Các ứng dụng độc hại có thể sử dụng quyền này để xóa hoặc sửa đổi nhật ký cuộc gọi của bạn."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Cho phép ứng dụng sửa đổi nhật ký cuộc gọi trên điện thoại của bạn, bao gồm dữ liệu về các cuộc gọi đến và gọi đi. Các ứng dụng độc hại có thể sử dụng quyền này để xóa hoặc sửa đổi nhật ký cuộc gọi của bạn."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"cảm biến cơ thể (như máy đo nhịp tim)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"truy cập cảm biến cơ thể (như máy đo nhịp tim)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Cho phép ứng dụng truy cập dữ liệu từ bộ cảm biến giám sát tình trạng sức khỏe của bạn, ví dụ như nhịp tim."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"đọc các sự kiện lịch và thông tin bí mật"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Cho phép ứng dụng đọc tất cả các sự kiện trên lịch được lưu trữ trên máy tính bảng của bạn, bao gồm các sự kiện trên lịch của bạn bè hoặc đồng nghiệp. Việc này có thể cho phép ứng dụng chia sẻ hoặc lưu dữ liệu lịch của bạn, bất kể tính bí mật hay tính nhạy cảm là gì."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Cho phép ứng dụng thêm, xóa, thay đổi các sự kiện mà bạn có thể sửa đổi trên điện thoại của mình, bao gồm những sự kiện của bạn bè hoặc đồng nghiệp. Việc này có thể cho phép ứng dụng gửi tin nhắn dường như đến từ chủ sở hữu lịch hoặc sửa đổi các sự kiện mà chủ sở hữu không biết."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"truy cập vào các lệnh của nhà cung cấp vị trí bổ sung"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Cho phép ứng dụng truy cập vào các lệnh của nhà cung cấp vị trí bổ sung. Điều này có thể cho phép ứng dụng can thiệp vào hoạt động của Hệ thống định vị toàn cầu (GPS) hoặc các nguồn vị trí khác."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"vị trí chính xác (dựa vào mạng và GPS)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"truy cập vị trí chính xác (dựa vào mạng và GPS)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Cho phép ứng dụng nhận vị trí chính xác của bạn bằng cách sử dụng Hệ thống định vị toàn cầu (GPS) hoặc các nguồn vị trí mạng chẳng hạn như tháp điện thoại di động và Wi-Fi. Các dịch vụ vị trí này phải được bật và có sẵn cho thiết bị của bạn để ứng dụng sử dụng chúng. Ứng dụng có thể sử dụng dịch vụ vị trí này để xác định vị trí của bạn và có thể tiêu hao thêm nguồn pin."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"vị trí gần đúng (dựa vào mạng)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"truy cập vị trí gần đúng (dựa vào mạng)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Cho phép ứng dụng nhận vị trí gần đúng của bạn. Vị trí này có được là do dịch vụ vị trí sử dụng các nguồn vị trí mạng chẳng hạn như tháp điện thoại di động và Wi-Fi. Các dịch vụ vị trí này phải được bật và có sẵn cho thiết bị của bạn để ứng dụng sử dụng chúng. Ứng dụng có thể sử dụng dịch vụ vị trí này để xác định vị trí gần đúng của bạn."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"thay đổi cài đặt âm thanh của bạn"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Cho phép ứng dụng sửa đổi cài đặt âm thanh chung chẳng hạn như âm lượng và loa nào được sử dụng cho thiết bị ra."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"ghi âm"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Cho phép ứng dụng ghi âm bằng micrô. Quyền này cho phép ứng dụng ghi âm bất kỳ lúc nào mà không cần sự xác nhận của bạn."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"liên lạc qua sim"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"gửi lệnh đến SIM"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Cho phép ứng dụng gửi lệnh đến SIM. Việc này rất nguy hiểm."</string>
<string name="permlab_camera" msgid="3616391919559751192">"chụp ảnh và quay video"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Cho phép ứng dụng chụp ảnh và quay video bằng máy ảnh. Quyền này cho phép ứng dụng sử dụng máy ảnh bất kỳ lúc nào mà không cần sự xác nhận của bạn."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Cho phép ứng dụng nhận danh sách các tài khoản mà điện thoại biết. Danh sách này có thể bao gồm bất kỳ tài khoản nào được tạo bởi các ứng dụng mà bạn đã cài đặt."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"xem kết nối mạng"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Cho phép ứng dụng xem thông tin về các kết nối mạng như mạng nào thoát và mạng nào được kết nối."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"quyền truy cập mạng đầy đủ"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"có quyền truy cập mạng đầy đủ"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Cho phép ứng dụng tạo cổng mạng và sử dụng giao thức mạng tùy chỉnh. Trình duyệt và các ứng dụng khác cung cấp các phương tiện để gửi dữ liệu lên internet do đó không yêu cầu quyền này để gửi dữ liệu lên internet."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"thay đổi kết nối mạng"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Cho phép ứng dụng thay đổi trạng thái kết nối mạng."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Cho phép ứng dụng định cấu hình điện thoại Bluetooth cục bộ cũng như phát hiện và ghép nối với các thiết bị từ xa."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"kết nối và ngắt kết nối khỏi WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Cho phép ứng dụng xác định liệu WiMAX đã được bật chưa và thông tin về bất kỳ mạng WiMAX nào được kết nối."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Thay đổi trạng thái WiMAX"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"thay đổi trạng thái WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Cho phép ứng dụng kết nối máy tính bảng và ngắt kết nối máy tính bảng khỏi mạng WiMAX."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Cho phép ứng dụng kết nối TV với và ngắt kết nối TV khỏi mạng WiMAX."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Cho phép ứng dụng kết nối điện thoại và ngắt kết nối điện thoại khỏi mạng WiMAX."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Cho phép ứng dụng sửa đổi các thông số hiệu chỉnh của màn hình cảm ứng. Không cần cho ứng dụng thông thường."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"truy cập chứng chỉ DRM"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Cho phép ứng dụng cung cấp và sử dụng chứng chỉ DRM. Không cần thiết cho các ứng dụng thông thường."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Nhận trạng thái chuyển của Android Beam"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"nhận trạng thái chuyển của Android Beam"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Cho phép ứng dụng này nhận thông tin về các lần chuyển hiện tại của Android Beam"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"xóa chứng chỉ DRM"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Cho phép ứng dụng xóa chứng chỉ DRM. Không cần thiết cho các ứng dụng thông thường."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Mã PIN không chính xác."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Để mở khóa, hãy nhấn vào Menu sau đó nhấn 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Số khẩn cấp"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Không có dịch vụ nào."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Không có dịch vụ"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Màn hình đã khóa."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Nhấn vào Menu để mở khóa hoặc thực hiện cuộc gọi khẩn cấp."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Nhấn vào Menu để mở khóa."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Vui lòng xem Hướng dẫn người dùng hoặc liên hệ với Bộ phận chăm sóc khách hàng."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"Thẻ SIM đã bị khóa."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Đang mở khóa thẻ SIM…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Bạn đã <xliff:g id="NUMBER_0">%d</xliff:g> lần vẽ không chính xác hình mở khóa. \n\nVui lòng thử lại sau <xliff:g id="NUMBER_1">%d</xliff:g> giây."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Bạn đã <xliff:g id="NUMBER_0">%d</xliff:g> lần nhập sai mật khẩu. \n\nHãy thử lại sau <xliff:g id="NUMBER_1">%d</xliff:g> giây."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Bạn đã nhập sai mã PIN <xliff:g id="NUMBER_0">%d</xliff:g> lần. \n\nHãy thử lại sau <xliff:g id="NUMBER_1">%d</xliff:g> giây."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Bạn đã <xliff:g id="NUMBER_0">%d</xliff:g> lần vẽ không chính xác hình mở khóa của mình. Sau <xliff:g id="NUMBER_1">%d</xliff:g> lần thử không thành công nữa, bạn sẽ được yêu cầu mở khóa máy tính bảng bằng thông tin đăng nhập Google của mình.\n\n Vui lòng thử lại sau <xliff:g id="NUMBER_2">%d</xliff:g> giây."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Bạn đã vẽ sai hình mở khóa <xliff:g id="NUMBER_0">%d</xliff:g> lần. Sau <xliff:g id="NUMBER_1">%d</xliff:g> lần mở khóa không thành công nữa, bạn sẽ được yêu cầu mở khóa TV bằng cách đăng nhập vào Google.\n\n Hãy thử lại sau <xliff:g id="NUMBER_2">%d</xliff:g> giây."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Bạn đã <xliff:g id="NUMBER_0">%d</xliff:g> lần vẽ không chính xác hình mở khóa. Sau <xliff:g id="NUMBER_1">%d</xliff:g> lần thử không thành công nữa, bạn sẽ được yêu cầu mở khóa điện thoại bằng thông tin đăng nhập Google của bạn.\n\n Vui lòng thử lại sau <xliff:g id="NUMBER_2">%d</xliff:g> giây."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Bạn đã mở khóa máy tính bảng không đúng cách <xliff:g id="NUMBER_0">%d</xliff:g> lần. Sau <xliff:g id="NUMBER_1">%d</xliff:g> lần mở khóa không thành công nữa, máy tính bảng sẽ được đặt lại về mặc định ban đầu và tất cả dữ liệu người dùng sẽ bị mất."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Bạn đã mở khóa TV sai <xliff:g id="NUMBER_0">%d</xliff:g> lần. Sau <xliff:g id="NUMBER_1">%d</xliff:g> lần mở khóa không thành công nữa, TV sẽ được đặt lại về cài đặt mặc định ban đầu và toàn bộ dữ liệu người dùng sẽ bị mất."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Bạn đã mở khóa điện thoại không đúng cách <xliff:g id="NUMBER_0">%d</xliff:g> lần. Sau <xliff:g id="NUMBER_1">%d</xliff:g> lần mở khóa không thành công nữa, điện thoại sẽ được đặt lại về mặc định ban đầu và tất cả dữ liệu người dùng sẽ bị mất."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Bạn đã <xliff:g id="NUMBER_0">%1$d</xliff:g> lần vẽ không chính xác hình mở khóa. \n\nVui lòng thử lại sau <xliff:g id="NUMBER_1">%2$d</xliff:g> giây."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Bạn đã <xliff:g id="NUMBER_0">%1$d</xliff:g> lần nhập sai mật khẩu. \n\nHãy thử lại sau <xliff:g id="NUMBER_1">%2$d</xliff:g> giây."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Bạn đã nhập sai mã PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> lần. \n\nHãy thử lại sau <xliff:g id="NUMBER_1">%2$d</xliff:g> giây."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Bạn đã <xliff:g id="NUMBER_0">%1$d</xliff:g> lần vẽ không chính xác hình mở khóa của mình. Sau <xliff:g id="NUMBER_1">%2$d</xliff:g> lần thử không thành công nữa, bạn sẽ được yêu cầu mở khóa máy tính bảng bằng thông tin đăng nhập Google của mình.\n\n Vui lòng thử lại sau <xliff:g id="NUMBER_2">%3$d</xliff:g> giây."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Bạn đã vẽ sai hình mở khóa <xliff:g id="NUMBER_0">%1$d</xliff:g> lần. Sau <xliff:g id="NUMBER_1">%2$d</xliff:g> lần mở khóa không thành công nữa, bạn sẽ được yêu cầu mở khóa TV bằng cách đăng nhập vào Google.\n\n Hãy thử lại sau <xliff:g id="NUMBER_2">%3$d</xliff:g> giây."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Bạn đã <xliff:g id="NUMBER_0">%1$d</xliff:g> lần vẽ không chính xác hình mở khóa. Sau <xliff:g id="NUMBER_1">%2$d</xliff:g> lần thử không thành công nữa, bạn sẽ được yêu cầu mở khóa điện thoại bằng thông tin đăng nhập Google của bạn.\n\n Vui lòng thử lại sau <xliff:g id="NUMBER_2">%3$d</xliff:g> giây."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Bạn đã mở khóa máy tính bảng không đúng cách <xliff:g id="NUMBER_0">%1$d</xliff:g> lần. Sau <xliff:g id="NUMBER_1">%2$d</xliff:g> lần mở khóa không thành công nữa, máy tính bảng sẽ được đặt lại về mặc định ban đầu và tất cả dữ liệu người dùng sẽ bị mất."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Bạn đã mở khóa TV sai <xliff:g id="NUMBER_0">%1$d</xliff:g> lần. Sau <xliff:g id="NUMBER_1">%2$d</xliff:g> lần mở khóa không thành công nữa, TV sẽ được đặt lại về cài đặt mặc định ban đầu và toàn bộ dữ liệu người dùng sẽ bị mất."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Bạn đã mở khóa điện thoại không đúng cách <xliff:g id="NUMBER_0">%1$d</xliff:g> lần. Sau <xliff:g id="NUMBER_1">%2$d</xliff:g> lần mở khóa không thành công nữa, điện thoại sẽ được đặt lại về mặc định ban đầu và tất cả dữ liệu người dùng sẽ bị mất."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Bạn đã mở khóa máy tính bảng không đúng cách <xliff:g id="NUMBER">%d</xliff:g> lần. Bây giờ, máy tính bảng sẽ được đặt lại về mặc định ban đầu."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Bạn đã mở khóa TV sai <xliff:g id="NUMBER">%d</xliff:g> lần. Bây giờ, TV sẽ được đặt lại về cài đặt mặc định ban đầu."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Bạn đã mở khóa điện thoại không đúng cách <xliff:g id="NUMBER">%d</xliff:g> lần. Bây giờ, điện thoại sẽ được đặt lại về mặc định ban đầu."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"từ"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"liên kết"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"dòng"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Thử nghiệm ban đầu không thành công"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Tác vụ FACTORY_TEST chỉ được hỗ trợ cho các gói được cài đặt trong /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Không tìm thấy gói cung cấp tác vụ FACTORY_TEST."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Đang định dạng…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Chưa được lắp"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Không tìm thấy hoạt động nào phù hợp."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Định tuyến thiết bị ra phương tiện"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"định tuyến thiết bị ra phương tiện"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Cho phép ứng dụng định tuyến thiết bị ra phương tiện đến các thiết bị bên ngoài khác."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Đọc phiên cài đặt"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"đọc phiên cài đặt"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Cho phép ứng dụng đọc phiên cài đặt. Thao tác này sẽ cho phép ứng dụng xem chi tiết về gói cài đặt đang hoạt động."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Yêu cầu gói cài đặt"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"yêu cầu gói cài đặt"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Cho phép ứng dụng yêu cầu cài đặt gói."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Chạm hai lần để kiểm soát thu phóng"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Không thể thêm tiện ích."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Tên người dùng hoặc mật khẩu không hợp lệ."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Bạn quên tên người dùng hoặc mật khẩu?\nHãy truy cập "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Đang kiểm tra tài khoản…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Bạn đã <xliff:g id="NUMBER_0">%d</xliff:g> lần nhập sai mã PIN. \n\nHãy thử lại sau <xliff:g id="NUMBER_1">%d</xliff:g> giây."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Bạn đã <xliff:g id="NUMBER_0">%d</xliff:g> lần nhập sai mật khẩu. \n\nHãy thử lại sau <xliff:g id="NUMBER_1">%d</xliff:g> giây."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Bạn đã <xliff:g id="NUMBER_0">%d</xliff:g> lần vẽ không chính xác hình mở khóa. \n\nHãy thử lại sau <xliff:g id="NUMBER_1">%d</xliff:g> giây."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Bạn đã <xliff:g id="NUMBER_0">%d</xliff:g> lần mở khóa máy tính bảng không đúng cách. Sau <xliff:g id="NUMBER_1">%d</xliff:g> lần mở khóa không thành công nữa, máy tính bảng sẽ được đặt lại về mặc định ban đầu và tất cả dữ liệu người dùng sẽ bị mất."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Bạn đã mở khóa TV sai <xliff:g id="NUMBER_0">%d</xliff:g> lần. Sau <xliff:g id="NUMBER_1">%d</xliff:g> lần mở khóa không thành công nữa, TV sẽ được đặt lại về cài đặt mặc định ban đầu và toàn bộ dữ liệu người dùng sẽ bị mất."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Bạn đã <xliff:g id="NUMBER_0">%d</xliff:g> lần mở khóa điện thoại không đúng cách. Sau <xliff:g id="NUMBER_1">%d</xliff:g> lần mở khóa không thành công nữa, điện thoại sẽ được đặt lại về mặc định ban đầu và tất cả dữ liệu người dùng sẽ bị mất."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Bạn đã <xliff:g id="NUMBER_0">%1$d</xliff:g> lần nhập sai mã PIN. \n\nHãy thử lại sau <xliff:g id="NUMBER_1">%2$d</xliff:g> giây."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Bạn đã <xliff:g id="NUMBER_0">%1$d</xliff:g> lần nhập sai mật khẩu. \n\nHãy thử lại sau <xliff:g id="NUMBER_1">%2$d</xliff:g> giây."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Bạn đã <xliff:g id="NUMBER_0">%1$d</xliff:g> lần vẽ không chính xác hình mở khóa. \n\nHãy thử lại sau <xliff:g id="NUMBER_1">%2$d</xliff:g> giây."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Bạn đã <xliff:g id="NUMBER_0">%1$d</xliff:g> lần mở khóa máy tính bảng không đúng cách. Sau <xliff:g id="NUMBER_1">%2$d</xliff:g> lần mở khóa không thành công nữa, máy tính bảng sẽ được đặt lại về mặc định ban đầu và tất cả dữ liệu người dùng sẽ bị mất."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Bạn đã mở khóa TV sai <xliff:g id="NUMBER_0">%1$d</xliff:g> lần. Sau <xliff:g id="NUMBER_1">%2$d</xliff:g> lần mở khóa không thành công nữa, TV sẽ được đặt lại về cài đặt mặc định ban đầu và toàn bộ dữ liệu người dùng sẽ bị mất."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Bạn đã <xliff:g id="NUMBER_0">%1$d</xliff:g> lần mở khóa điện thoại không đúng cách. Sau <xliff:g id="NUMBER_1">%2$d</xliff:g> lần mở khóa không thành công nữa, điện thoại sẽ được đặt lại về mặc định ban đầu và tất cả dữ liệu người dùng sẽ bị mất."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Bạn đã <xliff:g id="NUMBER">%d</xliff:g> lần mở khóa máy tính bảng không đúng cách. Bây giờ, máy tính bảng sẽ được đặt lại về mặc định ban đầu."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Bạn đã mở khóa TV sai <xliff:g id="NUMBER">%d</xliff:g> lần. Bây giờ, TV sẽ được đặt lại về cài đặt mặc định ban đầu."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Bạn đã <xliff:g id="NUMBER">%d</xliff:g> lần mở khóa điện thoại không đúng cách. Bây giờ, điện thoại sẽ được đặt lại về mặc định ban đầu."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Bạn đã <xliff:g id="NUMBER_0">%d</xliff:g> lần vẽ không chính xác hình mở khóa của mình. Sau <xliff:g id="NUMBER_1">%d</xliff:g> lần thử không thành công nữa, bạn sẽ được yêu cầu mở khóa máy tính bảng bằng tài khoản email.\n\n Vui lòng thử lại sau <xliff:g id="NUMBER_2">%d</xliff:g> giây."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Bạn đã vẽ sai hình mở khóa <xliff:g id="NUMBER_0">%d</xliff:g> lần. Sau <xliff:g id="NUMBER_1">%d</xliff:g> lần mở khóa không thành công nữa, bạn sẽ được yêu cầu mở khóa TV bằng tài khoản email.\n\n Hãy thử lại sau <xliff:g id="NUMBER_2">%d</xliff:g> giây."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Bạn đã <xliff:g id="NUMBER_0">%d</xliff:g> lần vẽ không chính xác hình mở khóa của mình. Sau <xliff:g id="NUMBER_1">%d</xliff:g> lần thử không thành công nữa, bạn sẽ được yêu cầu mở khóa điện thoại bằng tài khoản email.\n\n Vui lòng thử lại sau <xliff:g id="NUMBER_2">%d</xliff:g> giây."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Bạn đã <xliff:g id="NUMBER_0">%1$d</xliff:g> lần vẽ không chính xác hình mở khóa của mình. Sau <xliff:g id="NUMBER_1">%2$d</xliff:g> lần thử không thành công nữa, bạn sẽ được yêu cầu mở khóa máy tính bảng bằng tài khoản email.\n\n Vui lòng thử lại sau <xliff:g id="NUMBER_2">%3$d</xliff:g> giây."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Bạn đã vẽ sai hình mở khóa <xliff:g id="NUMBER_0">%1$d</xliff:g> lần. Sau <xliff:g id="NUMBER_1">%2$d</xliff:g> lần mở khóa không thành công nữa, bạn sẽ được yêu cầu mở khóa TV bằng tài khoản email.\n\n Hãy thử lại sau <xliff:g id="NUMBER_2">%3$d</xliff:g> giây."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Bạn đã <xliff:g id="NUMBER_0">%1$d</xliff:g> lần vẽ không chính xác hình mở khóa của mình. Sau <xliff:g id="NUMBER_1">%2$d</xliff:g> lần thử không thành công nữa, bạn sẽ được yêu cầu mở khóa điện thoại bằng tài khoản email.\n\n Vui lòng thử lại sau <xliff:g id="NUMBER_2">%3$d</xliff:g> giây."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Xóa"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Bạn tăng âm lượng lên quá mức khuyên dùng?\n\nViệc nghe ở mức âm lượng cao trong thời gian dài có thể gây tổn thương thính giác của bạn."</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">Trong 1 giờ</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Cho đến <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Cho tới <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (cảnh báo tiếp theo)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Cho đến khi bạn tắt tính năng này"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Cho đến khi bạn tắt Đừng làm phiền"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-watch/strings.xml b/core/res/res/values-watch/strings.xml
index e5991fc..dde8b2e 100644
--- a/core/res/res/values-watch/strings.xml
+++ b/core/res/res/values-watch/strings.xml
@@ -26,47 +26,4 @@
<!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. Override from base which says "Body Sensors". [CHAR_LIMIT=25] -->
<string name="permgrouplab_sensors">Sensors</string>
-
- <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
- <string name="permgrouplab_contactswear">access your contacts</string>
- <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
- <string name="permgrouplab_locationwear">access this watch\'s location</string>
- <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
- <string name="permgrouplab_calendarwear">access your calendar</string>
- <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
- <string name="permgrouplab_smswear">send and view SMS messages</string>
- <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
- <string name="permgrouplab_storagewear">access photos, media, and files on your watch</string>
- <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
- <string name="permgrouplab_microphonewear">record audio</string>
- <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
- <string name="permgrouplab_camerawear">take pictures and record video</string>
- <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
- <string name="permgrouplab_phonewear">make and manage phone calls</string>
- <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
- <string name="permgrouplab_sensorswear">access sensor data about your vital signs</string>
- <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
- <string name="permlab_statusBarServicewear">be the status bar</string>
- <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
- <string name="permlab_bodySensorswear">access body sensors (like heart rate monitors)</string>
- <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
- <string name="permlab_accessFineLocationwear">access precise location (GPS and network-based)</string>
- <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
- <string name="permlab_accessCoarseLocationwear">access approximate location (network-based)</string>
- <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
- <string name="permlab_sim_communicationwear">send commands to the SIM</string>
- <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
- <string name="permlab_createNetworkSocketswear">have full network access</string>
- <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
- <string name="permlab_manageProfileAndDeviceOwnerswear">manage profile and device owners</string>
- <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
- <string name="permlab_changeWimaxStatewear">change WiMAX state</string>
- <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
- <string name="permlab_handoverStatuswear">receive Android Beam transfer status</string>
- <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
- <string name="permlab_route_media_outputwear">route media output</string>
- <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
- <string name="permlab_readInstallSessionswear">read install sessions</string>
- <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
- <string name="permlab_requestInstallPackageswear">request install packages</string>
</resources>
diff --git a/core/res/res/values-zh-rCN-watch/strings.xml b/core/res/res/values-zh-rCN-watch/strings.xml
index f336907..9ab0e39 100644
--- a/core/res/res/values-zh-rCN-watch/strings.xml
+++ b/core/res/res/values-zh-rCN-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"应用:<xliff:g id="NUMBER_0">%1$d</xliff:g> / <xliff:g id="NUMBER_1">%2$d</xliff:g>。"</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"传感器"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"使用您的通讯录"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"访问此手表的位置信息"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"访问您的日历"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"发送和查看短信"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"访问您手表中的照片、媒体和文件"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"录制音频"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"拍摄照片和录制视频"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"拨打电话和管理通话"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"访问与您的生命体征相关的传感器数据"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"用作状态栏"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"访问身体传感器(如心率监测器)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"访问确切位置信息(以 GPS 和网络为依据)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"访问大致位置信息(以网络为依据)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"向 SIM 卡发送命令"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"拥有完全的网络访问权限"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"管理个人资料和设备所有者"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"更改 WiMAX 状态"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"接收 Android Beam 的传输状态"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"更改媒体输出线路"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"读取安装会话"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"请求安装文件包"</string>
</resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index c13ef14..b3c70a5 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -223,7 +223,7 @@
<string name="global_action_lockdown" msgid="8751542514724332873">"立即锁定"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"安全模式"</string>
- <string name="android_system_label" msgid="6577375335728551336">"Android系统"</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="permgrouplab_contacts" msgid="3657758145679177612">"通讯录"</string>
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"包含个人数据,例如信用卡号和密码。"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"停用或修改状态栏"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"允许应用停用状态栏或者增删系统图标。"</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"状态栏"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"用作状态栏"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"允许以状态栏形式显示应用。"</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"展开/收拢状态栏"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"允许应用展开或折叠状态栏。"</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"允许该应用接收和处理 WAP 消息。此权限包括监视发送给您的消息或删除发送给您的消息而不向您显示的功能。"</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"检索正在运行的应用"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"允许该应用检索近期运行的和当前正在运行的任务的相关信息。此权限可让该应用了解设备上使用了哪些应用。"</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"管理个人资料和设备所有者"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"管理个人资料和设备所有者"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"允许应用设置个人资料所有者和设备所有者。"</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"对正在运行的应用重新排序"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"允许该应用将任务移动到前台和后台。该应用可能不经您的命令自行执行此操作。"</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"允许该应用修改平板电脑的通话记录,包括有关来电和外拨电话的数据。恶意应用可能会借此清除或修改您的通话记录。"</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"允许应用修改电视的通话记录,包括有关来电和外拨电话的数据。恶意应用可能会借此清除或修改您的通话记录。"</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"允许该应用修改手机的通话记录,包括有关来电和外拨电话的数据。恶意应用可能会借此清除或修改您的通话记录。"</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"人体传感器(如心跳速率检测器)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"访问身体传感器(如心率监测器)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"允许该应用存取监测您身体状况的传感器所收集的数据,例如您的心率。"</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"读取日历活动和机密信息"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"允许该应用读取您平板电脑上存储的所有日历活动,包括朋友或同事的活动。此权限可让该应用分享或保存您的日历数据,而不论这些数据是否属于机密或敏感内容。"</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"允许该应用添加、删除、更改您可在手机上修改的活动,包括朋友或同事的活动。此权限可让该应用冒充日历所有者发送消息,或在所有者不知情的情况下修改活动。"</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"获取额外的位置信息提供程序命令"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"允许该应用使用其他的位置信息提供程序命令。此权限使该应用可以干扰GPS或其他位置信息源的运作。"</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"精确位置(基于GPS和网络)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"访问确切位置信息(以 GPS 和网络为依据)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"允许该应用通过全球定位系统(GPS)或网络位置信息源(例如基站和WLAN)获取您的精确位置信息。您必须在设备上开启这些位置信息服务,应用才能获得位置信息。应用会使用此类服务确定您的位置,这可能会消耗更多电量。"</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"大致位置(基于网络)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"访问大致位置信息(以网络为依据)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"允许该应用获取您的大致位置信息。这类位置信息来自于使用网络位置信息源(例如基站和WLAN)的位置信息服务。您必须在设备上开启这些位置信息服务,应用才能获得位置信息。应用会使用此类服务确定您的大概位置。"</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"更改您的音频设置"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"允许该应用修改全局音频设置,例如音量和用于输出的扬声器。"</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"录音"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"允许该应用使用麦克风录制音频。此权限可让该应用不经您的确认即可随时录制音频。"</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"SIM卡通信"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"向 SIM 卡发送命令"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"允许应用向SIM卡发送命令(此权限具有很高的危险性)。"</string>
<string name="permlab_camera" msgid="3616391919559751192">"拍摄照片和视频"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"允许该应用使用相机拍摄照片和视频。此权限可让该应用随时使用相机,而无需您的确认。"</string>
@@ -376,13 +376,13 @@
<string name="permdesc_setTimeZone" product="tablet" msgid="1676983712315827645">"允许应用更改平板电脑的时区。"</string>
<string name="permdesc_setTimeZone" product="tv" msgid="888864653946175955">"允许应用更改电视的时区。"</string>
<string name="permdesc_setTimeZone" product="default" msgid="4499943488436633398">"允许应用更改手机的时区。"</string>
- <string name="permlab_getAccounts" msgid="1086795467760122114">"查找设备上的帐户"</string>
- <string name="permdesc_getAccounts" product="tablet" msgid="2741496534769660027">"允许该应用获取平板电脑已知的帐户列表,其中可能包括由已安装的应用创建的所有帐户。"</string>
- <string name="permdesc_getAccounts" product="tv" msgid="4190633395633907543">"允许应用获取电视已知的帐户列表,其中可能包括由已安装的应用创建的所有帐户。"</string>
- <string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"允许该应用获取手机已知的帐户列表,其中可能包括由已安装的应用创建的所有帐户。"</string>
+ <string name="permlab_getAccounts" msgid="1086795467760122114">"查找设备上的帐号"</string>
+ <string name="permdesc_getAccounts" product="tablet" msgid="2741496534769660027">"允许该应用获取平板电脑已知的帐号列表,其中可能包括由已安装的应用创建的所有帐号。"</string>
+ <string name="permdesc_getAccounts" product="tv" msgid="4190633395633907543">"允许应用获取电视已知的帐号列表,其中可能包括由已安装的应用创建的所有帐号。"</string>
+ <string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"允许该应用获取手机已知的帐号列表,其中可能包括由已安装的应用创建的所有帐号。"</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"查看网络连接"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"允许该应用查看网络连接的相关信息,例如存在和连接的网络。"</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"完全的网络访问权限"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"拥有完全的网络访问权限"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"允许该应用创建网络套接字和使用自定义网络协议。浏览器和其他某些应用提供了向互联网发送数据的途径,因此应用无需该权限即可向互联网发送数据。"</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"更改网络连接性"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"允许应用更改网络连接的状态。"</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"允许应用配置本地蓝牙手机,并允许其查找远程设备且与之配对。"</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"建立或中断 WiMAX 网络连接"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"允许该应用确定是否启用了 WiMAX 以及连接的任何 WiMAX 网络的相关信息。"</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"更改 WiMAX 状态"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"更改 WiMAX 状态"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"允许该应用建立和断开平板电脑与 WiMAX 网络之间的连接。"</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"允许应用建立和断开电视与 WiMAX 网络之间的连接。"</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"允许该应用建立和断开手机与 WiMAX 网络之间的连接。"</string>
@@ -436,11 +436,11 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"指纹图标"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"读取同步设置"</string>
- <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"允许该应用读取某个帐户的同步设置。例如,此权限可确定“联系人”应用是否与某个帐户同步。"</string>
+ <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"允许该应用读取某个帐号的同步设置。例如,此权限可确定“联系人”应用是否与某个帐号同步。"</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"启用和停用同步"</string>
- <string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"允许该应用修改某个帐户的同步设置。例如,此权限可用于在“联系人”应用与某个帐户之间启用同步。"</string>
+ <string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"允许该应用修改某个帐号的同步设置。例如,此权限可用于在“联系人”应用与某个帐号之间启用同步。"</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"读取同步统计信息"</string>
- <string name="permdesc_readSyncStats" msgid="1510143761757606156">"允许该应用读取某个帐户的同步统计信息,包括同步活动历史记录和同步数据量。"</string>
+ <string name="permdesc_readSyncStats" msgid="1510143761757606156">"允许该应用读取某个帐号的同步统计信息,包括同步活动历史记录和同步数据量。"</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"读取您的USB存储设备中的内容"</string>
<string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"读取您的SD卡中的内容"</string>
<string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"允许应用读取您USB存储设备中的内容。"</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"允许应用修改触摸屏的校准参数。普通应用绝不需要此权限。"</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"访问DRM证书"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"允许应用配置和使用DRM证书。普通应用绝不需要此权限。"</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"接收Android Beam的传输状态"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"接收 Android Beam 的传输状态"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"允许此应用接收Android Beam当前传输内容的相关信息"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"移除DRM证书"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"允许应用移除DRM证书。普通应用绝不需要此权限。"</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PIN码有误。"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"要解锁,请先按 MENU 再按 0。"</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"急救或报警电话"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"无服务。"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"没有服务"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"屏幕已锁定。"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"按 Menu 解锁或进行紧急呼救。"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"按 MENU 解锁。"</string>
@@ -682,23 +682,23 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"请参阅《用户指南》或与客服人员联系。"</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM卡已被锁定。"</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"正在解锁SIM卡..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"您已连续 <xliff:g id="NUMBER_0">%d</xliff:g> 次画错解锁图案。\n\n请在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒后重试。"</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"您已连续 <xliff:g id="NUMBER_0">%d</xliff:g> 次输错密码。\n\n请在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒后重试。"</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"您已经<xliff:g id="NUMBER_0">%d</xliff:g>次输错了PIN码。\n\n请在<xliff:g id="NUMBER_1">%d</xliff:g>秒后重试。"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"您已连续 <xliff:g id="NUMBER_0">%d</xliff:g> 次画错解锁图案。如果再尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次后仍不成功,系统就会要求您使用自己的 Google 登录信息解锁平板电脑。\n\n请在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒后重试。"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"您已连续 <xliff:g id="NUMBER_0">%d</xliff:g> 次画错解锁图案。如果再尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次后仍不成功,系统就会要求您使用自己的 Google 登录信息解锁电视。\n\n请在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒后重试。"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"您已连续 <xliff:g id="NUMBER_0">%d</xliff:g> 次画错解锁图案。如果再尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次后仍不成功,系统就会要求您使用自己的 Google 登录信息解锁手机。\n\n请在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒后重试。"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"您已经 <xliff:g id="NUMBER_0">%d</xliff:g> 次错误地尝试解锁平板电脑。如果再尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次后仍不成功,平板电脑将恢复为出厂默认设置,所有用户数据都会丢失。"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"您已经 <xliff:g id="NUMBER_0">%d</xliff:g> 次错误地尝试解锁电视。如果再尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次后仍不成功,电视将恢复为出厂默认设置,所有用户数据都会丢失。"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"您已经 <xliff:g id="NUMBER_0">%d</xliff:g> 次错误地尝试解锁手机。如果再尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次后仍不成功,手机将恢复为出厂默认设置,所有用户数据都会丢失。"</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"您已连续 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次画错解锁图案。\n\n请在 <xliff:g id="NUMBER_1">%2$d</xliff:g> 秒后重试。"</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"您已连续 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次输错密码。\n\n请在 <xliff:g id="NUMBER_1">%2$d</xliff:g> 秒后重试。"</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"您已经<xliff:g id="NUMBER_0">%1$d</xliff:g>次输错了PIN码。\n\n请在<xliff:g id="NUMBER_1">%2$d</xliff:g>秒后重试。"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"您已连续 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次画错解锁图案。如果再尝试 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次后仍不成功,系统就会要求您使用自己的 Google 登录信息解锁平板电脑。\n\n请在 <xliff:g id="NUMBER_2">%3$d</xliff:g> 秒后重试。"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"您已连续 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次画错解锁图案。如果再尝试 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次后仍不成功,系统就会要求您使用自己的 Google 登录信息解锁电视。\n\n请在 <xliff:g id="NUMBER_2">%3$d</xliff:g> 秒后重试。"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"您已连续 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次画错解锁图案。如果再尝试 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次后仍不成功,系统就会要求您使用自己的 Google 登录信息解锁手机。\n\n请在 <xliff:g id="NUMBER_2">%3$d</xliff:g> 秒后重试。"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"您已经 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次错误地尝试解锁平板电脑。如果再尝试 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次后仍不成功,平板电脑将恢复为出厂默认设置,所有用户数据都会丢失。"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"您已经 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次错误地尝试解锁电视。如果再尝试 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次后仍不成功,电视将恢复为出厂默认设置,所有用户数据都会丢失。"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"您已经 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次错误地尝试解锁手机。如果再尝试 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次后仍不成功,手机将恢复为出厂默认设置,所有用户数据都会丢失。"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"您已经<xliff:g id="NUMBER">%d</xliff:g>次错误地尝试解锁平板电脑。平板电脑现在将恢复为出厂默认设置。"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"您已经 <xliff:g id="NUMBER">%d</xliff:g> 次错误地尝试解锁电视。电视现在将恢复为出厂默认设置。"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"您已经<xliff:g id="NUMBER">%d</xliff:g>次错误地尝试解锁手机。手机现在将恢复为出厂默认设置。"</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"<xliff:g id="NUMBER">%d</xliff:g>秒后重试。"</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"忘记了图案?"</string>
- <string name="lockscreen_glogin_forgot_pattern" msgid="2588521501166032747">"帐户解锁"</string>
+ <string name="lockscreen_glogin_forgot_pattern" msgid="2588521501166032747">"帐号解锁"</string>
<string name="lockscreen_glogin_too_many_attempts" msgid="2751368605287288808">"图案尝试次数过多"</string>
- <string name="lockscreen_glogin_instructions" msgid="3931816256100707784">"要解除锁定,请使用您的Google帐户登录。"</string>
+ <string name="lockscreen_glogin_instructions" msgid="3931816256100707784">"要解除锁定,请使用您的Google帐号登录。"</string>
<string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"用户名(电子邮件)"</string>
<string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"密码"</string>
<string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"登录"</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"字"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"链接"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"行"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="AMPM">%P</xliff:g> <xliff:g id="HOUR">%-l</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="AMPM">%p</xliff:g> <xliff:g id="HOUR">%-l</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"出厂测试失败"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"只有安装在/system/app中的软件包支持FACTORY_TEST操作。"</string>
<string name="factorytest_no_action" msgid="872991874799998561">"找不到提供FACTORY_TEST操作的软件包。"</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"正在格式化…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"未插入"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"未找到匹配的活动。"</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"更改媒体输出线路"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"更改媒体输出线路"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"允许该应用将媒体输出线路更改到其他外部设备。"</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"读取安装会话"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"读取安装会话"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"允许应用读取安装会话。这样,应用将可以查看有关当前软件包安装的详情。"</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"请求安装文件包"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"请求安装文件包"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"允许应用请求安装文件包。"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"触摸两次可进行缩放控制"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"无法添加小部件。"</string>
@@ -1090,13 +1088,13 @@
<string name="ime_action_default" msgid="2840921885558045721">"执行"</string>
<string name="dial_number_using" msgid="5789176425167573586">"拨打电话\n<xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"创建电话号码为\n<xliff:g id="NUMBER">%s</xliff:g> 的联系人"</string>
- <string name="grant_credentials_permission_message_header" msgid="2106103817937859662">"以下一个或多个应用请求获得相应权限,以便在当前和以后访问您的帐户。"</string>
+ <string name="grant_credentials_permission_message_header" msgid="2106103817937859662">"以下一个或多个应用请求获得相应权限,以便在当前和以后访问您的帐号。"</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"您是否同意此请求?"</string>
<string name="grant_permissions_header_text" msgid="6874497408201826708">"访问权限请求"</string>
<string name="allow" msgid="7225948811296386551">"允许"</string>
<string name="deny" msgid="2081879885755434506">"拒绝"</string>
<string name="permission_request_notification_title" msgid="6486759795926237907">"权限请求"</string>
- <string name="permission_request_notification_with_subtitle" msgid="8530393139639560189">"应用对帐户 <xliff:g id="ACCOUNT">%s</xliff:g>\n 提出权限请求。"</string>
+ <string name="permission_request_notification_with_subtitle" msgid="8530393139639560189">"应用对帐号 <xliff:g id="ACCOUNT">%s</xliff:g>\n 提出权限请求。"</string>
<string name="forward_intent_to_owner" msgid="1207197447013960896">"您目前是在工作资料之外使用此应用"</string>
<string name="forward_intent_to_work" msgid="621480743856004612">"您目前是在工作资料内使用此应用"</string>
<string name="input_method_binding_label" msgid="1283557179944992649">"输入法"</string>
@@ -1145,13 +1143,13 @@
<string name="gpsVerifYes" msgid="2346566072867213563">"是"</string>
<string name="gpsVerifNo" msgid="1146564937346454865">"否"</string>
<string name="sync_too_many_deletes" msgid="5296321850662746890">"超出删除限制"</string>
- <string name="sync_too_many_deletes_desc" msgid="496551671008694245">"帐户 <xliff:g id="ACCOUNT_NAME">%3$s</xliff:g> 在进行“<xliff:g id="TYPE_OF_SYNC">%2$s</xliff:g>”同步时删除了 <xliff:g id="NUMBER_OF_DELETED_ITEMS">%1$d</xliff:g> 项内容。您要如何处理这些删除的内容?"</string>
+ <string name="sync_too_many_deletes_desc" msgid="496551671008694245">"帐号 <xliff:g id="ACCOUNT_NAME">%3$s</xliff:g> 在进行“<xliff:g id="TYPE_OF_SYNC">%2$s</xliff:g>”同步时删除了 <xliff:g id="NUMBER_OF_DELETED_ITEMS">%1$d</xliff:g> 项内容。您要如何处理这些删除的内容?"</string>
<string name="sync_really_delete" msgid="2572600103122596243">"删除这些内容"</string>
<string name="sync_undo_deletes" msgid="2941317360600338602">"撤消删除"</string>
<string name="sync_do_nothing" msgid="3743764740430821845">"目前不进行任何操作"</string>
- <string name="choose_account_label" msgid="5655203089746423927">"选择帐户"</string>
- <string name="add_account_label" msgid="2935267344849993553">"添加帐户"</string>
- <string name="add_account_button_label" msgid="3611982894853435874">"添加帐户"</string>
+ <string name="choose_account_label" msgid="5655203089746423927">"选择帐号"</string>
+ <string name="add_account_label" msgid="2935267344849993553">"添加帐号"</string>
+ <string name="add_account_button_label" msgid="3611982894853435874">"添加帐号"</string>
<string name="number_picker_increment_button" msgid="2412072272832284313">"增大"</string>
<string name="number_picker_decrement_button" msgid="476050778386779067">"减小"</string>
<string name="number_picker_increment_scroll_mode" msgid="3073101067441638428">"触摸 <xliff:g id="VALUE">%s</xliff:g> 次并按住。"</string>
@@ -1278,25 +1276,25 @@
<string name="kg_invalid_puk" msgid="3638289409676051243">"请重新输入正确的PUK码。如果尝试错误次数过多,SIM卡将永久停用。"</string>
<string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"PIN码不匹配"</string>
<string name="kg_login_too_many_attempts" msgid="6486842094005698475">"图案尝试次数过多"</string>
- <string name="kg_login_instructions" msgid="1100551261265506448">"要解锁,请登录您的Google帐户。"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"要解锁,请登录您的Google帐号。"</string>
<string name="kg_login_username_hint" msgid="5718534272070920364">"用户名(电子邮件地址)"</string>
<string name="kg_login_password_hint" msgid="9057289103827298549">"密码"</string>
<string name="kg_login_submit_button" msgid="5355904582674054702">"登录"</string>
<string name="kg_login_invalid_input" msgid="5754664119319872197">"用户名或密码无效。"</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"忘记了用户名或密码?\n请访问 "<b>"google.com/accounts/recovery"</b>"。"</string>
- <string name="kg_login_checking_password" msgid="1052685197710252395">"正在检查帐户…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"您已经<xliff:g id="NUMBER_0">%d</xliff:g>次输错了PIN码。\n\n请在<xliff:g id="NUMBER_1">%d</xliff:g>秒后重试。"</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"您已连续 <xliff:g id="NUMBER_0">%d</xliff:g> 次输错密码。\n\n请在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒后重试。"</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"您已连续 <xliff:g id="NUMBER_0">%d</xliff:g> 次画错解锁图案。\n\n请在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒后重试。"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"您已经 <xliff:g id="NUMBER_0">%d</xliff:g> 次错误地尝试解锁平板电脑。如果再尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次后仍不成功,平板电脑将恢复为出厂默认设置,所有用户数据都会丢失。"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"您已经 <xliff:g id="NUMBER_0">%d</xliff:g> 次错误地尝试解锁电视。如果再尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次后仍不成功,电视将恢复为出厂默认设置,所有用户数据都会丢失。"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"您已经 <xliff:g id="NUMBER_0">%d</xliff:g> 次错误地尝试解锁手机。如果再尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次后仍不成功,手机将恢复为出厂默认设置,所有用户数据都会丢失。"</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"正在检查帐号…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"您已经<xliff:g id="NUMBER_0">%1$d</xliff:g>次输错了PIN码。\n\n请在<xliff:g id="NUMBER_1">%2$d</xliff:g>秒后重试。"</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"您已连续 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次输错密码。\n\n请在 <xliff:g id="NUMBER_1">%2$d</xliff:g> 秒后重试。"</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"您已连续 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次画错解锁图案。\n\n请在 <xliff:g id="NUMBER_1">%2$d</xliff:g> 秒后重试。"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"您已经 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次错误地尝试解锁平板电脑。如果再尝试 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次后仍不成功,平板电脑将恢复为出厂默认设置,所有用户数据都会丢失。"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"您已经 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次错误地尝试解锁电视。如果再尝试 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次后仍不成功,电视将恢复为出厂默认设置,所有用户数据都会丢失。"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"您已经 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次错误地尝试解锁手机。如果再尝试 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次后仍不成功,手机将恢复为出厂默认设置,所有用户数据都会丢失。"</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"您已经<xliff:g id="NUMBER">%d</xliff:g>次错误地尝试解锁平板电脑。平板电脑现在将恢复为出厂默认设置。"</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"您已经 <xliff:g id="NUMBER">%d</xliff:g> 次错误地尝试解锁电视。电视现在将恢复为出厂默认设置。"</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"您已经<xliff:g id="NUMBER">%d</xliff:g>次错误地尝试解锁手机。手机现在将恢复为出厂默认设置。"</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"您已连续 <xliff:g id="NUMBER_0">%d</xliff:g> 次画错解锁图案。如果再尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次后仍不成功,系统就会要求您使用自己的电子邮件帐户解锁平板电脑。\n\n请在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒后重试。"</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"您已连续 <xliff:g id="NUMBER_0">%d</xliff:g> 次画错解锁图案。如果再尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次后仍不成功,系统就会要求您使用电子邮件帐户解锁电视。\n\n请在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒后重试。"</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"您已连续 <xliff:g id="NUMBER_0">%d</xliff:g> 次画错解锁图案。如果再尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次后仍不成功,系统就会要求您使用自己的电子邮件帐户解锁手机。\n\n请在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒后重试。"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"您已连续 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次画错解锁图案。如果再尝试 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次后仍不成功,系统就会要求您使用自己的电子邮件帐号解锁平板电脑。\n\n请在 <xliff:g id="NUMBER_2">%3$d</xliff:g> 秒后重试。"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"您已连续 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次画错解锁图案。如果再尝试 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次后仍不成功,系统就会要求您使用电子邮件帐号解锁电视。\n\n请在 <xliff:g id="NUMBER_2">%3$d</xliff:g> 秒后重试。"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"您已连续 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次画错解锁图案。如果再尝试 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次后仍不成功,系统就会要求您使用自己的电子邮件帐号解锁手机。\n\n请在 <xliff:g id="NUMBER_2">%3$d</xliff:g> 秒后重试。"</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"删除"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"要将音量调高到推荐水平以上吗?\n\n长时间保持高音量可能会损伤听力。"</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">1 小时</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"到<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"直到<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>(闹钟下次响铃时)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"直到您将其关闭"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"直到您关闭“勿扰”模式"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-zh-rHK-watch/strings.xml b/core/res/res/values-zh-rHK-watch/strings.xml
index b5ecb08..72311ff 100644
--- a/core/res/res/values-zh-rHK-watch/strings.xml
+++ b/core/res/res/values-zh-rHK-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"應用程式 (<xliff:g id="NUMBER_0">%1$d</xliff:g>/<xliff:g id="NUMBER_1">%2$d</xliff:g>)"</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"感應器"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"存取您的通訊錄"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"存取此手錶的位置"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"存取您的日曆"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"傳送和查看短訊"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"存取手錶上的相片、媒體和檔案"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"錄製語音"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"拍攝和錄製影片"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"撥打電話及管理通話"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"存取與您生命體徵相關的感應器資料"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"成為狀態列"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"存取身體感應器 (例如心跳監測器)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"存取精確位置 (根據 GPS 和網絡)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"存取約略位置 (根據網絡)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"發送指令至 SIM 卡"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"擁有全面網絡存取權"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"管理個人檔案和裝置擁有者"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"變更 WiMAX 狀態"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"接收 Android Beam 的傳送狀態"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"轉送媒體輸出"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"讀取安裝工作階段"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"要求安裝套件"</string>
</resources>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 4c713c9..ddafe6d 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"包括個人資料,如信用卡號碼和密碼。"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"停用或修改狀態列"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"允許應用程式停用狀態列,並可新增或移除系統圖示。"</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"狀態列"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"成為狀態列"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"允許應用程式以狀態列顯示。"</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"展開/收合狀態列"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"允許應用程式展開或收合狀態列。"</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"允許應用程式接收和處理 WAP 訊息。這項權限也能讓應用程式監控訊息,或在您閱讀訊息前擅自刪除訊息。"</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"擷取執行中的應用程式"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"允許應用程式擷取有關目前和最近執行的工作的資訊。如此一來,應用程式或可找出裝置上所使用應用程式的相關資訊。"</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"管理您的設定檔和裝置擁有者"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"管理個人檔案和裝置擁有者"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"允許應用程式設定檔案擁有者和裝置擁有者。"</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"為執行中的應用程式重新排序"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"允許應用程式將工作移至前景或背景。應用程式可以自行處理,您無須操作。"</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"允許應用程式修改平板電腦的通話記錄,包括來電和已撥電話相關資料。惡意應用程式可能會藉此刪除或修改您的通話記錄。"</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"允許應用程式修改電視的通話記錄,包括來電和撥出電話的相關資料。惡意應用程式可能會藉此清除或修改您的通話記錄。"</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"允許應用程式修改手機的通話記錄,包括來電和已撥電話相關資料。惡意應用程式可能會藉此刪除或修改您的通話記錄。"</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"身體感應器 (例如心跳監視器)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"存取身體感應器 (例如心跳監測器)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"允許應用程式存取感應器所收集的資料 (這類感應器可監測您的體能狀態,例如您的心跳速率)。"</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"讀取日曆活動與機密資訊"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"允許應用程式讀取平板電腦上儲存的所有日曆活動,包括好友或同事的活動。如此一來,應用程式或可不論資料是否機密或敏感,自行共用或儲存您的日曆資料。"</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"允許應用程式新增、移除及更改您可以在手機上修改的活動,包括好友或同事的活動。如此一來,應用程式或可偽裝日曆擁有者傳送訊息,或在擁有者不知情下擅自修改活動。"</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"接收額外的位置提供者指令"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"允許應用程式存取額外的位置提供者指令。這項設定可能會使應用程式干擾 GPS 或其他位置來源的運作。"</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"精確位置 (以 GPS 和網絡為基準)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"存取精確位置 (根據 GPS 和網絡)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"允許應用程式使用全球衛星定位系統 (GPS) 或網絡位置來源 (例如手機發射塔和 Wi-Fi) 取得您的精確位置。您必須在裝置上開啟這些位置服務供應用程式使用。應用程式可能藉此確定您所在的位置,也可能會耗用更多電量。"</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"約略位置 (以網絡為基準)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"存取約略位置 (根據網絡)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"允許應用程式取得您的約略位置。這些位置資訊由位置服務使用網絡位置來源 (例如手機發射塔和 Wi-Fi) 取得。您必須在裝置上開啟這些位置服務供應用程式使用。應用程式可能藉此確定您的約略位置。"</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"更改音效設定"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"允許應用程式修改全域音頻設定,例如音量和用於輸出的喇叭。"</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"錄製音效"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"允許應用程式使用麥克風錄音。這項權限允許應用程式隨時錄音,而不需經您確認。"</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"SIM 卡通訊"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"發送指令至 SIM 卡"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"允許應用程式傳送指令到 SIM 卡。這項操作具有高危險性。"</string>
<string name="permlab_camera" msgid="3616391919559751192">"拍照和拍攝影片"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"允許應用程式使用相機拍照和錄影。這項權限允許應用程式隨時使用相機,而不需經您確認。"</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"允許應用程式取得手機已知的帳戶清單,其中可能包括您安裝的應用程式所建立的任何帳戶。"</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"查看網絡連線"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"允許應用程式查看網絡連線相關資訊,例如有哪些網絡和已連接哪些網絡。"</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"全面網絡存取權"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"擁有全面網絡存取權"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"允許應用程式建立網絡通訊端及使用自訂的網絡通訊協定。瀏覽器和其他應用程式提供傳送資料至互聯網的途徑,因此不需要這項權限來傳送資料至互聯網。"</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"更改網絡連線"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"允許應用程式更改網絡連線狀態。"</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"允許應用程式設定本機藍牙手機,以及與偵測到的遠端裝置配對。"</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"建立或中斷與 WiMAX 網絡的連線"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"允許應用程式確定是否已啟用 WiMAX,以及判斷任何已連接 WiMAX 網絡的相關資訊。"</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"更改 WiMAX 狀態"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"變更 WiMAX 狀態"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"允許應用程式建立或中斷平板電腦與 WiMAX 網絡的連線。"</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"允許應用程式連接至電視,並中斷電視與 WiMAX 網絡的連線。"</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"允許應用程式建立或中斷手機與 WiMAX 網絡的連線。"</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"允許應用程式修改觸控式螢幕的校正參數,而一般應用程式並不需要作出類似修改。"</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"存取 DRM 憑證"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"允許應用程式準備和使用 DRM 憑證,但一般應用程式並不需要使用。"</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"接收 Android Beam 的傳送狀態"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"接收 Android Beam 的傳送狀態"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"允許應用程式接收 Android Beam 目前傳送的資料"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"移除 DRM 憑證"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"允許應用程式移除 DRM 憑證 (一般應用程式並不需要)。"</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PIN 碼不正確。"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"如要解鎖,請按選單鍵,然後按 0。"</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"緊急電話號碼"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"沒有服務。"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"沒有服務"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"螢幕已鎖定。"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"按選單鍵解鎖或撥打緊急電話。"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"按選單鍵解鎖。"</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"請參閱使用者指南或與客戶服務中心聯絡。"</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM 卡處於鎖定狀態。"</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"正在解除 SIM 卡鎖定..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"您已畫錯解鎖圖案 <xliff:g id="NUMBER_0">%d</xliff:g> 次。\n\n請在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再試一次。"</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"您已輸入錯誤的密碼 <xliff:g id="NUMBER_0">%d</xliff:g> 次。\n\n請在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再試一次。"</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"您已輸入錯誤的 PIN 碼 <xliff:g id="NUMBER_0">%d</xliff:g> 次。\n\n請在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再試一次。"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"您已畫錯解鎖圖案 <xliff:g id="NUMBER_0">%d</xliff:g> 次,如果再嘗試 <xliff:g id="NUMBER_1">%d</xliff:g> 次仍未成功,系統會要求您使用您的 Google 登入資料解開上鎖的平板電腦。\n\n請在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒後再試一次。"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"您已 <xliff:g id="NUMBER_0">%d</xliff:g> 次錯誤繪製解鎖圖案。如再解鎖失敗 <xliff:g id="NUMBER_1">%d</xliff:g> 次,您將需要使用 Google 登入帳戶解鎖電視。\n\n請在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒後再試一次。"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"您已畫錯解鎖圖案 <xliff:g id="NUMBER_0">%d</xliff:g> 次,如果再嘗試 <xliff:g id="NUMBER_1">%d</xliff:g> 次仍未成功,系統會要求您使用您的 Google 登入資料解開上鎖的手機。\n\n請在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒後再試一次。"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"您嘗試解除這部平板電腦的鎖定已失敗 <xliff:g id="NUMBER_0">%d</xliff:g> 次,剩餘 <xliff:g id="NUMBER_1">%d</xliff:g> 次嘗試機會。如果失敗次數超過嘗試次數限制,平板電腦將恢復原廠設定,所有使用者資料均會遺失。"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"您已 <xliff:g id="NUMBER_0">%d</xliff:g> 次錯誤解鎖電視。如再解鎖失敗 <xliff:g id="NUMBER_1">%d</xliff:g> 次,電視將重設至原廠設定,所有使用者資料亦會遺失。"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"您嘗試解除這部手機的鎖定已失敗 <xliff:g id="NUMBER_0">%d</xliff:g> 次,剩餘 <xliff:g id="NUMBER_1">%d</xliff:g> 次嘗試機會。如果失敗次數超過嘗試次數限制,手機將恢復原廠設定,所有使用者資料均會遺失。"</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"您已畫錯解鎖圖案 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次。\n\n請在 <xliff:g id="NUMBER_1">%2$d</xliff:g> 秒後再試一次。"</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"您已輸入錯誤的密碼 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次。\n\n請在 <xliff:g id="NUMBER_1">%2$d</xliff:g> 秒後再試一次。"</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"您已輸入錯誤的 PIN 碼 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次。\n\n請在 <xliff:g id="NUMBER_1">%2$d</xliff:g> 秒後再試一次。"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"您已畫錯解鎖圖案 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次,如果再嘗試 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次仍未成功,系統會要求您使用您的 Google 登入資料解開上鎖的平板電腦。\n\n請在 <xliff:g id="NUMBER_2">%3$d</xliff:g> 秒後再試一次。"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"您已 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次錯誤繪製解鎖圖案。如再解鎖失敗 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次,您將需要使用 Google 登入帳戶解鎖電視。\n\n請在 <xliff:g id="NUMBER_2">%3$d</xliff:g> 秒後再試一次。"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"您已畫錯解鎖圖案 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次,如果再嘗試 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次仍未成功,系統會要求您使用您的 Google 登入資料解開上鎖的手機。\n\n請在 <xliff:g id="NUMBER_2">%3$d</xliff:g> 秒後再試一次。"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"您嘗試解除這部平板電腦的鎖定已失敗 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次,剩餘 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次嘗試機會。如果失敗次數超過嘗試次數限制,平板電腦將恢復原廠設定,所有使用者資料均會遺失。"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"您已 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次錯誤解鎖電視。如再解鎖失敗 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次,電視將重設至原廠設定,所有使用者資料亦會遺失。"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"您嘗試解除這部手機的鎖定已失敗 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次,剩餘 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次嘗試機會。如果失敗次數超過嘗試次數限制,手機將恢復原廠設定,所有使用者資料均會遺失。"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"您嘗試解除這部平板電腦的鎖定已失敗 <xliff:g id="NUMBER">%d</xliff:g> 次。平板電腦現在會重設為原廠預設值。"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"您已 <xliff:g id="NUMBER">%d</xliff:g> 次錯誤解鎖電視。電視將重設至原廠設定。"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"您嘗試解除這部手機的鎖定已失敗 <xliff:g id="NUMBER">%d</xliff:g> 次。手機現在會重設為原廠預設值。"</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"字詞"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"連結"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"行"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"原廠測試失敗"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"只有安裝在 /system/app 裡的程式套件才能支援 FACTORY_TEST 操作。"</string>
<string name="factorytest_no_action" msgid="872991874799998561">"沒有可提供 FACTORY_TEST 操作的套件。"</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"正在格式化…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"未插入"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"找不到相符的活動。"</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"轉送媒體輸出"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"轉送媒體輸出"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"允許應用程式將媒體輸出轉送至其他外部裝置。"</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"讀取安裝工作階段"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"讀取安裝工作階段"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"允許應用程式讀取安裝工作階段。應用程式將可查看目前安裝套裝的詳細資料。"</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"要求安裝套件"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"要求安裝套件"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"允許應用程式要求安裝套件"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"輕觸兩下即可控制縮放"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"無法新增小工具。"</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"無效的使用者名稱或密碼。"</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"忘記使用者名稱或密碼?\n請瀏覽 "<b>"google.com/accounts/recovery"</b>"。"</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"正在檢查帳戶…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"您已輸入錯誤的 PIN 碼 <xliff:g id="NUMBER_0">%d</xliff:g> 次。\n\n請在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再試一次。"</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"您已輸入錯誤的密碼 <xliff:g id="NUMBER_0">%d</xliff:g> 次。\n\n請在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再試一次。"</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"您已畫錯解鎖圖案 <xliff:g id="NUMBER_0">%d</xliff:g> 次。\n\n請在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再試一次。"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"您嘗試了 <xliff:g id="NUMBER_0">%d</xliff:g> 次仍未能成功解開這部上鎖的平板電腦。如果再嘗試 <xliff:g id="NUMBER_1">%d</xliff:g> 次仍未成功,平板電腦將回復原廠設定,所有使用者資料均會失去。"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"您已 <xliff:g id="NUMBER_0">%d</xliff:g> 次錯誤解鎖電視。如再解鎖失敗 <xliff:g id="NUMBER_1">%d</xliff:g> 次,電視將重設至原廠設定,所有使用者資料亦會遺失。"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"您嘗試了 <xliff:g id="NUMBER_0">%d</xliff:g> 次仍未能成功解開這部上鎖的手機。如果再嘗試 <xliff:g id="NUMBER_1">%d</xliff:g> 次仍未成功,手機將回復原廠設定,所有使用者資料均會失去。"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"您已輸入錯誤的 PIN 碼 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次。\n\n請在 <xliff:g id="NUMBER_1">%2$d</xliff:g> 秒後再試一次。"</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"您已輸入錯誤的密碼 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次。\n\n請在 <xliff:g id="NUMBER_1">%2$d</xliff:g> 秒後再試一次。"</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"您已畫錯解鎖圖案 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次。\n\n請在 <xliff:g id="NUMBER_1">%2$d</xliff:g> 秒後再試一次。"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"您嘗試了 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次仍未能成功解開這部上鎖的平板電腦。如果再嘗試 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次仍未成功,平板電腦將回復原廠設定,所有使用者資料均會失去。"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"您已 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次錯誤解鎖電視。如再解鎖失敗 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次,電視將重設至原廠設定,所有使用者資料亦會遺失。"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"您嘗試了 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次仍未能成功解開這部上鎖的手機。如果再嘗試 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次仍未成功,手機將回復原廠設定,所有使用者資料均會失去。"</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"您嘗試了 <xliff:g id="NUMBER">%d</xliff:g> 次仍未能成功解開這部上鎖的平板電腦。平板電腦現在將回復原廠設定。"</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"您已 <xliff:g id="NUMBER">%d</xliff:g> 次錯誤解鎖電視。電視將重設至原廠設定。"</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"您嘗試了 <xliff:g id="NUMBER">%d</xliff:g> 次仍未能成功解開這部上鎖的手機。手機現在將回復原廠設定。"</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"您已畫錯解鎖圖案 <xliff:g id="NUMBER_0">%d</xliff:g> 次,如果再嘗試 <xliff:g id="NUMBER_1">%d</xliff:g> 次仍未成功,系統會要求您透過電郵帳戶解開上鎖的平板電腦。\n\n請在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒後再試一次。"</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"您已 <xliff:g id="NUMBER_0">%d</xliff:g> 次錯誤繪製解鎖圖案。如再解鎖失敗 <xliff:g id="NUMBER_1">%d</xliff:g> 次,您將需要使用電郵帳戶解鎖電視。\n\n請在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒後再試一次。"</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"您已畫錯解鎖圖案 <xliff:g id="NUMBER_0">%d</xliff:g> 次,如果再嘗試 <xliff:g id="NUMBER_1">%d</xliff:g> 次仍未成功,系統會要求您透過電郵帳戶解開上鎖的手機。\n\n請在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒後再試一次。"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"您已畫錯解鎖圖案 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次,如果再嘗試 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次仍未成功,系統會要求您透過電郵帳戶解開上鎖的平板電腦。\n\n請在 <xliff:g id="NUMBER_2">%3$d</xliff:g> 秒後再試一次。"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"您已 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次錯誤繪製解鎖圖案。如再解鎖失敗 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次,您將需要使用電郵帳戶解鎖電視。\n\n請在 <xliff:g id="NUMBER_2">%3$d</xliff:g> 秒後再試一次。"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"您已畫錯解鎖圖案 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次,如果再嘗試 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次仍未成功,系統會要求您透過電郵帳戶解開上鎖的手機。\n\n請在 <xliff:g id="NUMBER_2">%3$d</xliff:g> 秒後再試一次。"</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"移除"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"要調高音量 (比建議的音量更大聲) 嗎?\n\n長時間聆聽高分貝音量可能會導致您的聽力受損。"</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">需時 1 小時</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"完成時間:<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"直至<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (下一次響鬧)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"直至您關閉這項設定"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"直至您關閉「請勿騷擾」功能"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-zh-rTW-watch/strings.xml b/core/res/res/values-zh-rTW-watch/strings.xml
index 79fb99d..1c11f0e 100644
--- a/core/res/res/values-zh-rTW-watch/strings.xml
+++ b/core/res/res/values-zh-rTW-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"應用程式 <xliff:g id="NUMBER_0">%1$d</xliff:g>/<xliff:g id="NUMBER_1">%2$d</xliff:g>。"</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"感應器"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"存取您的聯絡人"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"存取這個手錶的位置資訊"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"存取您的日曆"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"傳送及查看簡訊"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"存取手錶上的相片、媒體和檔案"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"錄音"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"拍照及錄製影片"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"撥打電話及管理通話"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"存取生命徵象相關感應器資料"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"用作狀態列"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"存取人體感測器 (例如心跳速率監測器)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"存取精確位置 (以 GPS 和網路為依據)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"存取概略位置 (以網路為依據)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"傳送指令到 SIM 卡"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"擁有完整的網路存取權"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"管理個人資料和裝置擁有者"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"變更 WiMAX 狀態"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"接收 Android Beam 的傳輸狀態"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"轉送媒體輸出"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"讀取安裝工作階段"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"索取安裝套件"</string>
</resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 46f0d2c..1163137 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"包括個人資料,如信用卡號碼和密碼。"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"停用或變更狀態列"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"允許應用程式停用狀態列,並可新增或移除系統圖示。"</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"狀態列"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"以狀態列顯示"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"允許應用程式以狀態列顯示。"</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"展開/收攏狀態列"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"允許應用程式展開或收合狀態列。"</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"允許應用程式接收和處理 WAP 訊息。這項權限也能讓應用程式監控訊息,或在您閱讀訊息前擅自刪除訊息。"</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"擷取執行中的應用程式"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"允許應用程式擷取最近執行工作的資訊。這項設定可讓應用程式找出裝置所用程式的相關資訊。"</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"管理個人資料和裝置擁有者"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"管理個人資料和裝置擁有者"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"允許應用程式設定個人資料擁有者和裝置擁有者。"</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"重新排序正在執行的應用程式"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"允許應用程式將工作移至前景或背景。應用程式可以自行處理,不待您操作。"</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"允許應用程式修改平板電腦的通話紀錄,包括來電和已撥電話相關資料。請注意,惡意應用程式可能濫用此功能刪除或修改您的通話紀錄。"</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"允許應用程式修改電視的通話紀錄,包括來電和已撥電話相關資料。惡意應用程式可能會藉此清除或修改您的通話紀錄。"</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"允許應用程式修改手機的通話紀錄,包括來電和已撥電話相關資料。請注意,惡意應用程式可能濫用此功能刪除或修改您的通話紀錄。"</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"身體感應器 (例如心律監測器)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"存取身體感應器 (例如心跳速率監測器)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"允許應用程式存取感測器所收集的資料 (這類感測器可監測您的體能狀態,例如您的心跳速率)。"</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"讀取日曆活動與機密資訊"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"允許應用程式讀取平板電腦上儲存的所有日曆活動,包括好友或同事的活動。這項設定會讓應用程式共用或儲存您的日曆資料,甚至包括機密或敏感的資料。"</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"允許應用程式新增、移除、變更您可以在手機上修改的活動,包括好友或同事的活動。這項設定可能會讓應用程式偽裝日曆擁有者傳送訊息,或私自修改活動。"</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"接收額外的位置提供者指令"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"允許應用程式存取額外位置資訊提供者指令。這項設定可能會造成應用程式干擾 GPS 或其他位置資訊來源的運作。"</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"精確位置 (以 GPS 和網路為基準)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"存取精確位置 (以 GPS 和網路為依據)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"允許應用程式使用全球衛星定位系統 (GPS) 或網路位置來源 (例如無線通信基地台和 Wi-Fi) 取得您的精確位置。您必須在裝置上開啟這些定位服務,才能供應用程式使用。應用程式可能藉此判別您的位置,也可能增加額外耗電。"</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"概略位置 (以網路為基準)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"存取概略位置 (以網路為依據)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"允許應用程式取得您的概略位置。這類位置資訊取自使用網路位置來源 (例如無線通信基地台和 Wi-Fi) 的定位服務。您必須在裝置上開啟這些定位服務,才能供應用程式使用。應用程式可能藉此判別您的概略位置。"</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"變更音訊設定"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"允許應用程式修改全域音訊設定,例如音量和用來輸出的喇叭。"</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"錄製音訊"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"允許應用程式使用麥克風錄音。這項權限可讓應用程式隨時錄音,不需經過您的確認。"</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"SIM 卡通訊"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"傳送指令到 SIM 卡"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"允許應用程式傳送指令到 SIM 卡。這麼做非常危險。"</string>
<string name="permlab_camera" msgid="3616391919559751192">"拍攝相片和影片"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"允許應用程式使用相機拍照和錄影。這項權限可讓應用程式隨時使用相機,而不需請求您進行確認。"</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"允許應用程式取得手機上所記憶的帳戶清單,其中可能包括您安裝的應用程式所建立的任何帳戶。"</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"查看網路連線"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"允許應用程式查看網路連線相關資訊,例如有哪些網路,以及已連上哪些網路。"</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"完整網路存取權"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"擁有完整的網路存取權"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"允許應用程式建立網路通訊端及使用自訂網路通訊協定。瀏覽器和其他應用程式會提供將資料傳輸到網際網路的方法,因此不需要這項權限也能將資料傳輸到網際網路。"</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"變更網路連線"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"允許應用程式變更網路連線狀態。"</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"允許應用程式設定本機藍牙手機,以及搜尋遠端裝置並配對連線。"</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"建立或中斷與 WiMAX 網路的連線"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"允許應用程式判斷是否已啟用 WiMAX,以及判讀任何已連上 WiMAX 網路的相關資訊。"</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"變更 WiMAX 狀態"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"變更 WiMAX 狀態"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"允許應用程式建立或中斷平板電腦與 WiMAX 網路的連線。"</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"允許應用程式建立及中斷電視的 WiMAX 網路連線。"</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"允許應用程式建立或中斷手機與 WiMAX 網路的連線。"</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"允許應用程式修改觸控螢幕的校正參數 (一般應用程式並不需要)。"</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"存取 DRM 憑證"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"允許應用程式佈建及使用 DRM 憑證 (一般應用程式並不需要)。"</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"接收 Android Beam 的傳輸狀態"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"接收 Android Beam 的傳輸狀態"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"允許應用程式接收 Android Beam 目前傳輸的資訊"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"移除 DRM 憑證"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"允許應用程式移除 DRM 憑證 (一般應用程式並不需要)。"</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PIN 碼不正確。"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"如要解鎖,請按 Menu 鍵,然後按 0。"</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"緊急電話號碼"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"沒有服務。"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"沒有服務"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"螢幕已鎖定。"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"按下 [Menu] 解鎖或撥打緊急電話。"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"按下 Menu 鍵解鎖。"</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"參閱《使用者指南》或與客戶服務中心聯絡。"</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM 卡已鎖定。"</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"解鎖 SIM 卡中…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"您的解鎖圖形已畫錯 <xliff:g id="NUMBER_0">%d</xliff:g> 次。\n\n請在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再試一次。"</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"您的密碼已輸錯 <xliff:g id="NUMBER_0">%d</xliff:g> 次。\n\n請在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再試一次。"</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"您的 PIN 已輸錯 <xliff:g id="NUMBER_0">%d</xliff:g> 次。\n\n請在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再試一次。"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"您的解鎖圖形已畫錯 <xliff:g id="NUMBER_0">%d</xliff:g> 次,如果再嘗試 <xliff:g id="NUMBER_1">%d</xliff:g> 次仍未成功,系統就會要求您使用您的 Google 登入資訊解除平板電腦的鎖定狀態。\n\n請在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒後再試一次。"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"您已畫錯解鎖圖形 <xliff:g id="NUMBER_0">%d</xliff:g> 次,目前還剩 <xliff:g id="NUMBER_1">%d</xliff:g> 次嘗試機會。如果失敗次數超過限制,您就必須登入 Google 帳戶才能解鎖電視。\n\n請過 <xliff:g id="NUMBER_2">%d</xliff:g> 秒後再試一次。"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"您的解鎖圖形已畫錯 <xliff:g id="NUMBER_0">%d</xliff:g> 次,如果再試 <xliff:g id="NUMBER_1">%d</xliff:g> 次仍未成功,系統就會要求您使用您的 Google 登入資訊解除手機的鎖定狀態。\n\n請在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒後再試一次。"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"您嘗試解除這個平板電腦的鎖定已失敗 <xliff:g id="NUMBER_0">%d</xliff:g> 次,目前還剩 <xliff:g id="NUMBER_1">%d</xliff:g> 次機會。如果失敗次數超過限制,平板電腦將恢復原廠設定,所有使用者資料都會遺失。"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"您嘗試解鎖電視已失敗 <xliff:g id="NUMBER_0">%d</xliff:g> 次,目前還剩 <xliff:g id="NUMBER_1">%d</xliff:g> 次機會。如果失敗次數超過限制,電視將恢復原廠設定,所有使用者資料都會遺失。"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"您嘗試解除這支手機的鎖定已失敗 <xliff:g id="NUMBER_0">%d</xliff:g> 次,目前還剩 <xliff:g id="NUMBER_1">%d</xliff:g> 次機會。如果失敗次數超過限制,手機將恢復原廠設定,所有使用者資料都會遺失。"</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"您的解鎖圖形已畫錯 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次。\n\n請在 <xliff:g id="NUMBER_1">%2$d</xliff:g> 秒後再試一次。"</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"您的密碼已輸錯 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次。\n\n請在 <xliff:g id="NUMBER_1">%2$d</xliff:g> 秒後再試一次。"</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"您的 PIN 已輸錯 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次。\n\n請在 <xliff:g id="NUMBER_1">%2$d</xliff:g> 秒後再試一次。"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"您的解鎖圖形已畫錯 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次,如果再嘗試 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次仍未成功,系統就會要求您使用您的 Google 登入資訊解除平板電腦的鎖定狀態。\n\n請在 <xliff:g id="NUMBER_2">%3$d</xliff:g> 秒後再試一次。"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"您已畫錯解鎖圖形 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次,目前還剩 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次嘗試機會。如果失敗次數超過限制,您就必須登入 Google 帳戶才能解鎖電視。\n\n請過 <xliff:g id="NUMBER_2">%3$d</xliff:g> 秒後再試一次。"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"您的解鎖圖形已畫錯 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次,如果再試 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次仍未成功,系統就會要求您使用您的 Google 登入資訊解除手機的鎖定狀態。\n\n請在 <xliff:g id="NUMBER_2">%3$d</xliff:g> 秒後再試一次。"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"您嘗試解除這個平板電腦的鎖定已失敗 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次,目前還剩 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次機會。如果失敗次數超過限制,平板電腦將恢復原廠設定,所有使用者資料都會遺失。"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"您嘗試解鎖電視已失敗 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次,目前還剩 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次機會。如果失敗次數超過限制,電視將恢復原廠設定,所有使用者資料都會遺失。"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"您嘗試解除這支手機的鎖定已失敗 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次,目前還剩 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次機會。如果失敗次數超過限制,手機將恢復原廠設定,所有使用者資料都會遺失。"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"您嘗試解除這個平板電腦的鎖定已失敗 <xliff:g id="NUMBER">%d</xliff:g> 次,平板電腦現在將恢復原廠設定。"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"您嘗試解鎖電視已失敗 <xliff:g id="NUMBER">%d</xliff:g> 次,電視現在將恢復原廠設定。"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"您嘗試解除這支手機的鎖定已失敗 <xliff:g id="NUMBER">%d</xliff:g> 次,手機現在將恢復原廠設定。"</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"字詞"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"連結"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"行"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"出廠測試失敗"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"只有安裝在 /system/app 裡的程式才能支援 FACTORY_TEST 操作。"</string>
<string name="factorytest_no_action" msgid="872991874799998561">"找不到提供 FACTORY_TEST 的程式。"</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"正在格式化…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"未插入"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"找不到相符的活動。"</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"轉送媒體輸出"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"轉送媒體輸出"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"允許應用程式將媒體輸出轉送至其他外部裝置。"</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"讀取安裝工作階段"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"讀取安裝工作階段"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"允許應用程式讀取安裝工作階段。應用程式將可查看目前的套件安裝詳細資料。"</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"要求安裝套件"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"要求安裝套件"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"允許應用程式要求安裝套件。"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"輕觸兩下即可控制縮放"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"無法新增小工具。"</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"使用者名稱或密碼無效。"</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"忘了使用者名稱或密碼?\n請前往 "<b>"google.com/accounts/recovery"</b>"。"</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"正在檢查帳戶…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"您的 PIN 已輸錯 <xliff:g id="NUMBER_0">%d</xliff:g> 次。\n\n請在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再試一次。"</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"您的密碼已輸錯 <xliff:g id="NUMBER_0">%d</xliff:g> 次。\n\n請在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再試一次。"</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"您的解鎖圖形已畫錯 <xliff:g id="NUMBER_0">%d</xliff:g> 次。\n\n請在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再試一次。"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"您嘗試解除這個平板電腦的鎖定已失敗 <xliff:g id="NUMBER_0">%d</xliff:g> 次,目前還剩 <xliff:g id="NUMBER_1">%d</xliff:g> 次機會。如果失敗次數超過限制,平板電腦將恢復原廠設定,所有使用者資料都會遺失。"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"您嘗試解鎖電視已失敗 <xliff:g id="NUMBER_0">%d</xliff:g> 次,目前還剩 <xliff:g id="NUMBER_1">%d</xliff:g> 次機會。如果失敗次數超過限制,電視將恢復原廠設定,所有使用者資料都會遺失。"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"您嘗試解除這支手機的鎖定已失敗 <xliff:g id="NUMBER_0">%d</xliff:g> 次,目前還剩 <xliff:g id="NUMBER_1">%d</xliff:g> 次機會。如果失敗次數超過限制,手機將恢復原廠設定,所有使用者資料都會遺失。"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"您的 PIN 已輸錯 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次。\n\n請在 <xliff:g id="NUMBER_1">%2$d</xliff:g> 秒後再試一次。"</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"您的密碼已輸錯 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次。\n\n請在 <xliff:g id="NUMBER_1">%2$d</xliff:g> 秒後再試一次。"</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"您的解鎖圖形已畫錯 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次。\n\n請在 <xliff:g id="NUMBER_1">%2$d</xliff:g> 秒後再試一次。"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"您嘗試解除這個平板電腦的鎖定已失敗 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次,目前還剩 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次機會。如果失敗次數超過限制,平板電腦將恢復原廠設定,所有使用者資料都會遺失。"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"您嘗試解鎖電視已失敗 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次,目前還剩 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次機會。如果失敗次數超過限制,電視將恢復原廠設定,所有使用者資料都會遺失。"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"您嘗試解除這支手機的鎖定已失敗 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次,目前還剩 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次機會。如果失敗次數超過限制,手機將恢復原廠設定,所有使用者資料都會遺失。"</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"您嘗試解除這個平板電腦的鎖定已失敗 <xliff:g id="NUMBER">%d</xliff:g> 次,平板電腦現在將恢復原廠設定。"</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"您嘗試解鎖電視已失敗 <xliff:g id="NUMBER">%d</xliff:g> 次,電視現在將恢復原廠設定。"</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"您嘗試解除這支手機的鎖定已失敗 <xliff:g id="NUMBER">%d</xliff:g> 次,手機現在將恢復原廠設定。"</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"您的解鎖圖形已畫錯 <xliff:g id="NUMBER_0">%d</xliff:g> 次,如果再嘗試 <xliff:g id="NUMBER_1">%d</xliff:g> 次仍未成功,系統就會要求您透過電子郵件帳戶解除平板電腦的鎖定狀態。\n\n請在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒後再試一次。"</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"您已畫錯解鎖圖形 <xliff:g id="NUMBER_0">%d</xliff:g> 次,目前還剩 <xliff:g id="NUMBER_1">%d</xliff:g> 次嘗試機會。如果失敗次數超過限制,您就必須使用電子郵件帳戶才能解鎖電視。\n\n請過 <xliff:g id="NUMBER_2">%d</xliff:g> 秒後再試一次。"</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"您的解鎖圖形已畫錯 <xliff:g id="NUMBER_0">%d</xliff:g> 次,如果再嘗試 <xliff:g id="NUMBER_1">%d</xliff:g> 次仍未成功,系統就會要求您透過電子郵件帳戶解除手機的鎖定狀態。\n\n請在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒後再試一次。"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"您的解鎖圖形已畫錯 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次,如果再嘗試 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次仍未成功,系統就會要求您透過電子郵件帳戶解除平板電腦的鎖定狀態。\n\n請在 <xliff:g id="NUMBER_2">%3$d</xliff:g> 秒後再試一次。"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"您已畫錯解鎖圖形 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次,目前還剩 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次嘗試機會。如果失敗次數超過限制,您就必須使用電子郵件帳戶才能解鎖電視。\n\n請過 <xliff:g id="NUMBER_2">%3$d</xliff:g> 秒後再試一次。"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"您的解鎖圖形已畫錯 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次,如果再嘗試 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次仍未成功,系統就會要求您透過電子郵件帳戶解除手機的鎖定狀態。\n\n請在 <xliff:g id="NUMBER_2">%3$d</xliff:g> 秒後再試一次。"</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"移除"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"要調高音量,比建議的音量更大聲嗎?\n\n長時間聆聽高分貝音量可能會使您的聽力受損。"</string>
@@ -1470,6 +1468,7 @@
<item quantity="one">1 小時</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"結束時間:<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"到<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> 為止 (下一個鬧鐘)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"手動關閉這項設定前一律啟用"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"直到您關閉「零打擾」模式"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-zu-watch/strings.xml b/core/res/res/values-zu-watch/strings.xml
index e9121b9..26f8071 100644
--- a/core/res/res/values-zu-watch/strings.xml
+++ b/core/res/res/values-zu-watch/strings.xml
@@ -22,25 +22,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Uhlelo lokusebenza olungu-<xliff:g id="NUMBER_0">%1$d</xliff:g> kokungu-<xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Izinzwa"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"finyelela koxhumana nabo"</string>
- <string name="permgrouplab_locationwear" msgid="6275317222482780209">"finyelela indawo yaleli washi"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"finyelela kukhalenda yakho"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"thumela uphinde ubuke imilayezo ye-SMS"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"finyelela izithombe, imidiya, namafayela kuwashi lakho"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"rekhoda ividiyo"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"thatha izithombe uphinde urekhode ividiyo"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"yenza uphinde uphathe amakholi wefoni"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"finyelela idatha yenzwa mayelana nezimpawu zakho ezibalulekile"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"yiba yibha yesimo"</string>
- <string name="permlab_bodySensorswear" msgid="7857941041202791873">"finyelela kuzinzwa zomzimba (ezifana neziqaphi zokulinganisela inhliziyo)"</string>
- <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"finyelela indawo enembile (i-GPS nesuselwa kunethiwekhi)"</string>
- <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"finyelela kundawo elinganiselwe (esuselwa kunethiwekhi)"</string>
- <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"thumela imilayezo ku-SIM"</string>
- <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"iba nokufinyelela okugcwele kwenethiwekhi"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"phatha iphrofayela nabanikazi bedivayisi"</string>
- <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"shintsha isimo se-WiMAX"</string>
- <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"thola isimo sokudlulisa se-Android Beam"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"yenza umzila ukukhipha kwemidiya"</string>
- <string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"funda izikhathi zokufaka"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"cela amaphakheji wokufaka"</string>
</resources>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 0ecd571..9557f58 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -254,7 +254,7 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Kufaka phakathi idatha yomuntu siqu efana nezinombolo zekhadi lesikweletu namaphasiwedi."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"khubaza noma guqula ibha yomumo"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Ivumela uhlelo lokusebenza ukuthi yenze umudwa ochaza ngesimo ukuthi ungasebenzi noma ukufaka noma ukukhipha izithonjana zohlelo."</string>
- <string name="permlab_statusBarService" msgid="7247281911387931485">"umudwa ochaza ngesimo"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"yiba yibha yesimo"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Ivumela uhlelo lokusebenza ukuthi lube umudwa ochaza ngesimo."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"khulisa/nciphisa ibha yomumo"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Ivumela uhlelo lokusebenza ukuthi ikhulise noma inciphise umudwa ochza ngesimo."</string>
@@ -282,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Ivumela uhlelo lokusebenza ukuthola nokucubungula imilayezo ye-WAP. Le mvume ifaka phakathi amandla okungamela noma okwesusa imilayezo ethunyelwe kuwe ngaphandle kokukubonisa."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"thola izinhlelo zokusebenza ezisebenzayo"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Ivumela uhlelo lokusebenza ukubuyisa ulwazi mayelana nemisebenzi yamanje neyakamuva. Lokhu kungavumela uhlelo lokusebenza ukuthola ulwazi mayelana nokuthi iziphi izinhlelo zokusebenza ezisetshenziswa kudivayisi."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Phatha iphrofayela nabanikazi bedivayisi"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"phatha iphrofayela nabanikazi bedivayisi"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Ivumela izinhlelo zokusebenza ukuthi zisethe abanikazi bephrofayela nomnikazi wedivayisi."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"misa kabusha izinhlelo zokusebenza ezisebenzayo"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Ivumela uhlelo lokusebenza ukugudluza imisebenzi ngaphambili nangasemuva. Uhlelo lokusebenza lungenza lokhu ngaphandle kwakho."</string>
@@ -324,7 +324,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Ivumela uhlelo lokusebenza ukushintsha ilogi yekholi yethebulethi yakho, kufaka phakathi idatha mayelana namakholi angenayo naphumayo. Izinhlelo zikusebenza ezingalungile zingasebenzisa lokhu ukusula noma ukushintsha irekhodi lwamakholi wakho."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Ivumela uhlelo lokusebenza ukuthi liguqule ilogi yekholi yakho ye-TV, okufaka idatha emayelana namakholi angenayo naphumayo. Izinhlelo zokusebenza ezinobungozi zingasebenzisa lokhu ukususa noma ukuguqula ilogi yakho yekholi."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Ivumela uhlelo lokusebenza ukushintsha irekhodi lamakholi efoni yakho, kufaka phakathi idatha emayelana namakholi angenayo naphumayo. Izinhlelo zikusebenza ezingalungile zingasebenzisa lokhu ukusula noma ukushintsha irekhodi lwamakholi wakho."</string>
- <string name="permlab_bodySensors" msgid="4871091374767171066">"izinzwa zomzimba (njengeziqaphi zokulinganisela inhliziyo)"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"finyelela kuzinzwa zomzimba (ezifana neziqaphi zokulinganisela inhliziyo)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Ivumela uhlelo lokusebenza ukuthi lufinyelele kudatha kusukela kuzinzwa eziqapha isimo sakho somzimba, esifana nesilinganiso senhliziyo yakho."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"funda imicimbi yekhalenda kanye nokwaziswa okuyimfihlo"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Ivumela uhlelo lokusebenza ukufunda zonke izehlakalo zekhalenda ezilondolozwe kuthebhulethi yakho, kufaka phakathi lezo zabangani noma osebenza nabo. Lokhu kungavumela uhlelo lokusebenza ukwabelana noma ukulondoloza idatha yakho yekhalenda, ngaphandle kokugcinwa kuyimfihlo noma ukuzwela."</string>
@@ -336,15 +336,15 @@
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Ivumela uhlelo lokusebenza ukungeza, ukususa, ukushintsha izehlakalo ongazishintsha efonini yakho, kufaka phakathi nalezo zabangani noma labo osebenza nabo. Lokhu kungavumela uhlelo lokusebenza ukuthumela imilayezo ebonakala ngathi ivela kubanikazi bekhalenda, noma lishintshe izehlakalo ngaphandle kolwazi labanikazi."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"finyelela kweminye imiyalo yokunikeza indawo"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Ivumela uhlelo lokusebenza ukufinyelela imiyalo eyengeziwe yabahlinzeki bendawo. Lokhu kungase kuvumele uhlelo lokusebenza ukuthi liphazamisane nomsebenzi we-GPS noma eminye imithombo yendawo."</string>
- <string name="permlab_accessFineLocation" msgid="1191898061965273372">"indawo eqondile (kususelwe ku-GPS nakunethiwekhi)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"finyelela indawo enembile (i-GPS nesuselwa kunethiwekhi)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Ivumela uhlelo lokusebenza ukuthola indawo yakho uqobo isebenzisa i-Global Positioning System (GPS) noma imithombo yendawo yenethiwekhi njengama-cell tower ne-Wi-Fi. Lawa masevisi endawo kufanele akhanyiswe futhi atholakale kudivayisi yakho ukuze asetshenziswe uhlelo lokusebenza. Izinhlelo zokusebenza zingasebenzias lokhu ukucacisa lapho ukhona, futhi angasebenzisa ibhethri elingeziwe."</string>
- <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"indawo eseduze (kususelwe kunethiwekhi)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"finyelela kundawo elinganiselwe (esuselwa kunethiwekhi)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Ivumela uhlelo lokusebenza ukuthola cishe indawo yakho. Le ndawo isuselwe kumasevisi endawo kusetshenziswa imithombo yendawo yenethiwekhi njengama-cell tower ne-Wi-Fi. Lawo masevisi endawo kufanele akhanyiswe futhi atholakale kudivayisi yakho ukuze asetshenziswe uhlelo lakho lokusebenza. Izinhlelo zokusebenza zingasebenzisa lokhu ukucacisa cishe lapho ukhona."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"shintsha izilungiselelo zakho zomsindo"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Ivumela uhlelo lokusebenza ukushintsha izilungiselelo zomsindo we-global njengevolomu nokuthi isiphi isipika esisetshenziselwa okukhiphayo."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"qopha umsindo"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Ivumela uhlelo lokusebenza ukurekhoda umsindo nge-microphone. Le mvume ivumela uhlelo lokusebenza ukuqopha umsindo noma kunini ngaphandle kokuqinisekisa kwakho."</string>
- <string name="permlab_sim_communication" msgid="1180265879464893029">"uxhumano le-sim"</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"thumela imilayezo ku-SIM"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Ivumela uhlelo lokusebenza ukuthumela imiyalo ku-SIM. Lokhu kuyingozi kakhulu."</string>
<string name="permlab_camera" msgid="3616391919559751192">"thatha izithombe namavidiyo"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Ivumela uhlelo lokusebenza ukuthatha izithombe namavidiyo ngekhamera. Le mvume ivumela uhlelo lokusebenza ukusebenzisa ikhamera nganoma isiphi isikhathi ngaphandle kwemvume yakho."</string>
@@ -382,7 +382,7 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Ivumela uhlelo lokusebenza ukuthola uhlu lwama-akhawunti aziwa ifoni. Lokhu kufaka phakathi noma yimaphi ama-akhawunti adalwe izinhlelo zokusebenza ozifakile."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"buka ukuxhumeka kunethiwekhi"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Ivumela uhlelo lokusebenza ukubuka ulwazi mayelana noxhumo lenethiwekhi njengokuthi imaphi amanethiwekhi akhona futhi axhunyiwe."</string>
- <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"ukufinyelela kwenethiwekhi okugcwele"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"iba nokufinyelela okugcwele kwenethiwekhi"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Ivumela uhlelo lokusebenza ukudala amasokethi enethiwekhi nokusebenzisa iziphakamiso eziyisisekelo zenethiwekhi yezifiso. Iziphequluli nezinye izinhlelo zokusebenza zinikela ngezindlela zokuthumela idatha ku-intanethi, ngakho-le le mvume ayidingekile ukuthumela idatha ku-intanethi."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"shintsha uxhumano lwenethiwekhi"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Ivumela uhlelo lokusebenza ukuthi iguqule isimo sokuxhuaniseka kwenethiwekhi."</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Ivumela uhlelo lokusebenza ukumisa ifoni ye-Bluetooth yasendawni, nokuthola nokubhanqanisa namadivaysi okulawula okukude."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"xhuma futhi unqamule kusuka ku-WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Ivumela uhlelo lokusebenza ukucacisa ukuthi ingabe i-WiMAX inikwe amandla futhi ulwazi mayelana namanethiwekhi e-WiMAX axhunyiwe."</string>
- <string name="permlab_changeWimaxState" msgid="2405042267131496579">"Shintsha isimo se-WiMAX"</string>
+ <string name="permlab_changeWimaxState" msgid="340465839241528618">"shintsha isimo se-WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Ivumela uhlelo lokusebenza ukuxhuma ithebhulethi nokunqamula ithebhulethi kumanethiwekhi e-WiMAX."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Ivumela uhlelo lokusebenza ukuthi lixhume i-TV liphinde liyinqamule kusukela kumanethiwekhi we-WiMAX."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Ivumela uhlelo lokusebenza ukuxhuma ifoni nokuyinqamula kumanethiwekhi e-WiMAX."</string>
@@ -485,7 +485,7 @@
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Ivumela uhlelo lokusebenza ukuthi lushintshe imingcele yokulinganisa yesikrini esithintwayo. Akumele idingelwe izinhlelo zokusebenza ezijwayelekile."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"finyelela izitifiketi ze-DRM"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Ivumela uhlelo lokusebenza ekunikezweni nokusetshenziswa kwezitifiketi ze-DRM. Akufanele kudingeke kuzinhlelo zokusebenza ezivamile."</string>
- <string name="permlab_handoverStatus" msgid="1159132046126626731">"Thola isimo sokundlulisa se-Android Beam"</string>
+ <string name="permlab_handoverStatus" msgid="7820353257219300883">"thola isimo sokudlulisa se-Android Beam"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Ivumela lolu hlelo lokusebenza ukuthi luthole ulwazi mayelana nokundluliswa kwamanje kwe-Android Beam"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"susa izitifiketi ze-DRM"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Ivumela uhlelo lokusebenza ukususa izitifiketi ze-DRM. Akufanele idingeke ngezinhlelo zokusebenza ezivamile."</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Ikhodi ye-PIN engalungile!"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Ukuvula, chofoza Menyu bese 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Inombolo ephuthumayo"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Ayikho isevisi"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Ayikho isevisi"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Isikrini sivaliwe."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Chofoza Menyu ukuvula noma ukwenza ikholi ephuthumayo."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Chofoza Menyu ukuvula."</string>
@@ -682,15 +682,15 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Sicela ubone Isiqondisi Somsebenzisi noma xhumana Nokunakekela Ikhasimende"</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"Ikhadi le-SIM livaliwe."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Ivula ikhadi le-SIM..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Udwebe iphathini yakho yokuvula ngendlela engafanele izinkathi ezingu-<xliff:g id="NUMBER_0">%d</xliff:g>. \n\n Zama futhi emuva kwamasekhondi angu-<xliff:g id="NUMBER_1">%d</xliff:g>"</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Ubhale iphasiwedi yakho ngendlela engafanele <xliff:g id="NUMBER_0">%d</xliff:g> izikhathi. \n\nZama futhi <xliff:g id="NUMBER_1">%d</xliff:g> imizuzwna."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Ubhale i-PIN ykho ngendlela engafanele <xliff:g id="NUMBER_0">%d</xliff:g> izikhathi. \n\nZama futhi emuva kwamasekhondi angu-<xliff:g id="NUMBER_1">%d</xliff:g>."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Udwebe ngokungalungile iphathini yakho yokuvula izikhathi ezingu-<xliff:g id="NUMBER_0">%d</xliff:g>. Emva <xliff:g id="NUMBER_1">%d</xliff:g> kweminye imizamo engaphumelelanga, uzocelwa ukuvula ithebhulethi yakho usebenzisa ukungena ngemvume kwi-Google.\n\n Sicela uzame futhi emuva kwamasekhondi angu-<xliff:g id="NUMBER_2">%d</xliff:g>"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Udwebele ngokungalungile iphethini yakho yokuvula izikhathi ezingu-<xliff:g id="NUMBER_0">%d</xliff:g>. Ngemuva kwemizamo engaphumelelanga engu-<xliff:g id="NUMBER_1">%d</xliff:g> ngaphezulu, uzocelwa ukuthi uvule i-TV yakho usebenzisa ukungena kwakho ngemvume kwe-Google.\n\n Zama futhi kumasekhondi angu-<xliff:g id="NUMBER_2">%d</xliff:g>."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Ukulayisha ungenisa iphathini yakho yokuvula ngendlela engalungile izikhathi ezingu-<xliff:g id="NUMBER_0">%d</xliff:g> Emumva kweminye imizamo engu-<xliff:g id="NUMBER_1">%d</xliff:g>, uzocelwa ukuvula ifoni yakho usebenzisa ukungena ngemvume ku-Google\n\n Zame futhi emuva kwamasekhondi angu- <xliff:g id="NUMBER_2">%d</xliff:g>"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Uzame ngokusebenzisa indlela engafanele ukuvula izikhathi <xliff:g id="NUMBER_0">%d</xliff:g> ze-tablet. Ngemuva <xliff:g id="NUMBER_1">%d</xliff:g> kokuzama kaningana okuyimpumelelo i-tablet izobuyela kwizimo zasembonini futhi yonke imininingo yomsebenzisi izolahleka."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Uzame ukuvula ngokungalungile i-TV ngezikhathi ezingu-<xliff:g id="NUMBER_0">%d</xliff:g>. Ngemuva kwemizamo engaphumelelanga engu-<xliff:g id="NUMBER_1">%d</xliff:g> ngaphezulu, i-TV izosethelwa kabusha kokuzenzakalelayo kwasekuqaleni futhi yonke idatha yomsebenzisi izolahleka."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Uzame ngokusebenzisa indlela engafanele ukuvula izikhathi <xliff:g id="NUMBER_0">%d</xliff:g> zocingo. Ngemuva <xliff:g id="NUMBER_1">%d</xliff:g> kokuzama kaningana ngaphandle kwempumelelo, ucingo luzobiyiselwa kwizimiso zasembonini futhi yonke imininingo yomsebenzisi izolahleka."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Udwebe iphathini yakho yokuvula ngendlela engafanele izinkathi ezingu-<xliff:g id="NUMBER_0">%1$d</xliff:g>. \n\n Zama futhi emuva kwamasekhondi angu-<xliff:g id="NUMBER_1">%2$d</xliff:g>"</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Ubhale iphasiwedi yakho ngendlela engafanele <xliff:g id="NUMBER_0">%1$d</xliff:g> izikhathi. \n\nZama futhi <xliff:g id="NUMBER_1">%2$d</xliff:g> imizuzwna."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Ubhale i-PIN ykho ngendlela engafanele <xliff:g id="NUMBER_0">%1$d</xliff:g> izikhathi. \n\nZama futhi emuva kwamasekhondi angu-<xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Udwebe ngokungalungile iphathini yakho yokuvula izikhathi ezingu-<xliff:g id="NUMBER_0">%1$d</xliff:g>. Emva <xliff:g id="NUMBER_1">%2$d</xliff:g> kweminye imizamo engaphumelelanga, uzocelwa ukuvula ithebhulethi yakho usebenzisa ukungena ngemvume kwi-Google.\n\n Sicela uzame futhi emuva kwamasekhondi angu-<xliff:g id="NUMBER_2">%3$d</xliff:g>"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Udwebele ngokungalungile iphethini yakho yokuvula izikhathi ezingu-<xliff:g id="NUMBER_0">%1$d</xliff:g>. Ngemuva kwemizamo engaphumelelanga engu-<xliff:g id="NUMBER_1">%2$d</xliff:g> ngaphezulu, uzocelwa ukuthi uvule i-TV yakho usebenzisa ukungena kwakho ngemvume kwe-Google.\n\n Zama futhi kumasekhondi angu-<xliff:g id="NUMBER_2">%3$d</xliff:g>."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Ukulayisha ungenisa iphathini yakho yokuvula ngendlela engalungile izikhathi ezingu-<xliff:g id="NUMBER_0">%1$d</xliff:g> Emumva kweminye imizamo engu-<xliff:g id="NUMBER_1">%2$d</xliff:g>, uzocelwa ukuvula ifoni yakho usebenzisa ukungena ngemvume ku-Google\n\n Zame futhi emuva kwamasekhondi angu- <xliff:g id="NUMBER_2">%3$d</xliff:g>"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Uzame ngokusebenzisa indlela engafanele ukuvula izikhathi <xliff:g id="NUMBER_0">%1$d</xliff:g> ze-tablet. Ngemuva <xliff:g id="NUMBER_1">%2$d</xliff:g> kokuzama kaningana okuyimpumelelo i-tablet izobuyela kwizimo zasembonini futhi yonke imininingo yomsebenzisi izolahleka."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Uzame ukuvula ngokungalungile i-TV ngezikhathi ezingu-<xliff:g id="NUMBER_0">%1$d</xliff:g>. Ngemuva kwemizamo engaphumelelanga engu-<xliff:g id="NUMBER_1">%2$d</xliff:g> ngaphezulu, i-TV izosethelwa kabusha kokuzenzakalelayo kwasekuqaleni futhi yonke idatha yomsebenzisi izolahleka."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Uzame ngokusebenzisa indlela engafanele ukuvula izikhathi <xliff:g id="NUMBER_0">%1$d</xliff:g> zocingo. Ngemuva <xliff:g id="NUMBER_1">%2$d</xliff:g> kokuzama kaningana ngaphandle kwempumelelo, ucingo luzobiyiselwa kwizimiso zasembonini futhi yonke imininingo yomsebenzisi izolahleka."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Uzame ukuvula ngendlela engafanele izikhathi <xliff:g id="NUMBER">%d</xliff:g> ze-tablet. I-tablet manje seyizosethwa kabusha ibe yizimiso zasembonini."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Uzame ngokungalungile ukuvula i-TV izikhathi ezingu-<xliff:g id="NUMBER">%d</xliff:g>. I-TV manje izosethelwa kabusha kokuzenzakalelayo kwasekuqaleni."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Uzame ukuvula ngendlela engafanele izikhathi <xliff:g id="NUMBER">%d</xliff:g> zocingo. Ucingo manje seyizosethwa kabusha ibe yizimiso zasembonini."</string>
@@ -742,8 +742,6 @@
<string name="granularity_label_word" msgid="7075570328374918660">"igama"</string>
<string name="granularity_label_link" msgid="5815508880782488267">"isixhumanisi"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"ulayini"</string>
- <string name="hour_ampm" msgid="4584338083529355982">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
- <string name="hour_cap_ampm" msgid="2083465992940444366">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Ukuhlola kwemboni kwehlulekile"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Isenzo SOKUHLOLA_KWASEMBONINI sisekelwa kuphela amaphakheji afakwe kwisistimu/uhlelokusebenza."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Ayikho iphakheji etholakele enikeze isenzo SOKUHLOLA KWASEMBONINI."</string>
@@ -1073,11 +1071,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Iyafometha..."</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Akufakiwe"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Ayikho imisebenzi efanayo etholakele"</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Yenza umzila wemidiya wokukhiphayo"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"yenza umzila ukukhipha kwemidiya"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Ivumela uhlelo lokusebenza ukwenza umzila wokukhiphayo wemidiya kuya kumadivayisi angaphandle."</string>
- <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Funda izikhathi zokufaka"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"funda izikhathi zokufaka"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Ivumela uhlelo lokusebenza ukufunda izikhathi. Lokhu kuzolivumela ukubona imininingwane mayelana nokufaka kwephakethi esebenzayo."</string>
- <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Cela amaphakheji wokufaka"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"cela amaphakheji wokufaka"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Ivumela uhlelo lokusebenza ukucela ukufakwa kwamaphakheji."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Thinta kabili ukulawula ukusondeza"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Yehlulekile ukwengeza i-widget."</string>
@@ -1285,18 +1283,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Igama lomsebezisi elingalungile noma iphasiwedi."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Ukhohlwe igama lomsebenzisi noma iphasiwedi?\nVakashela"<b>"google.com/accounts/recovery"</b></string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Ukuhlola i-akhawunti…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Ubhale iphinikhodi ykho ngendlela engafanele izikhathi ezingu-<xliff:g id="NUMBER_0">%d</xliff:g>. \n\nZama futhi emasekhondini angu-<xliff:g id="NUMBER_1">%d</xliff:g>."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Ubhale iphasiwedi yakho ngendlela engafanele <xliff:g id="NUMBER_0">%d</xliff:g> izikhathi. \n\nZama futhi emasekhondini angu-<xliff:g id="NUMBER_1">%d</xliff:g>."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Udwebe iphathini yakho yokuvula ngendlela engafanele-<xliff:g id="NUMBER_0">%d</xliff:g>. \n\n Zama futhi emasekhondini angu-<xliff:g id="NUMBER_1">%d</xliff:g>"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Uzame ngokusebenzisa indlela engafanele ukuvula ithebhulethi izikhathi ezingu-<xliff:g id="NUMBER_0">%d</xliff:g>. Ngemuva kokuzama ngaphandle kwempumelelo okungu-<xliff:g id="NUMBER_1">%d</xliff:g>, ithebhulethi izobuyiselwa kwizimiso zasembonini futhi yonke imininingwane yomsebenzisi izolahleka."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Uzame ukuvula ngokungalungile i-TV ngezikhathi ezingu-<xliff:g id="NUMBER_0">%d</xliff:g>. Ngemuva kwemizamo engaphumelelanga engu-<xliff:g id="NUMBER_1">%d</xliff:g> ngaphezulu, i-TV izosethelwa kabusha kokuzenzakalelayo kwasekuqaleni futhi yonke idatha yomsebenzisi izolahleka."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Uzame ngokusebenzisa indlela engafanele ukuvula ifoni izikhathi ezingu-<xliff:g id="NUMBER_0">%d</xliff:g>. Ngemuva kokuzama ngaphandle kwempumelelo okungu-<xliff:g id="NUMBER_1">%d</xliff:g>, ifoni izobuyiselwa kwizimiso zasembonini futhi yonke imininingwane yomsebenzisi izolahleka."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Ubhale iphinikhodi ykho ngendlela engafanele izikhathi ezingu-<xliff:g id="NUMBER_0">%1$d</xliff:g>. \n\nZama futhi emasekhondini angu-<xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Ubhale iphasiwedi yakho ngendlela engafanele <xliff:g id="NUMBER_0">%1$d</xliff:g> izikhathi. \n\nZama futhi emasekhondini angu-<xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Udwebe iphathini yakho yokuvula ngendlela engafanele-<xliff:g id="NUMBER_0">%1$d</xliff:g>. \n\n Zama futhi emasekhondini angu-<xliff:g id="NUMBER_1">%2$d</xliff:g>"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Uzame ngokusebenzisa indlela engafanele ukuvula ithebhulethi izikhathi ezingu-<xliff:g id="NUMBER_0">%1$d</xliff:g>. Ngemuva kokuzama ngaphandle kwempumelelo okungu-<xliff:g id="NUMBER_1">%2$d</xliff:g>, ithebhulethi izobuyiselwa kwizimiso zasembonini futhi yonke imininingwane yomsebenzisi izolahleka."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Uzame ukuvula ngokungalungile i-TV ngezikhathi ezingu-<xliff:g id="NUMBER_0">%1$d</xliff:g>. Ngemuva kwemizamo engaphumelelanga engu-<xliff:g id="NUMBER_1">%2$d</xliff:g> ngaphezulu, i-TV izosethelwa kabusha kokuzenzakalelayo kwasekuqaleni futhi yonke idatha yomsebenzisi izolahleka."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Uzame ngokusebenzisa indlela engafanele ukuvula ifoni izikhathi ezingu-<xliff:g id="NUMBER_0">%1$d</xliff:g>. Ngemuva kokuzama ngaphandle kwempumelelo okungu-<xliff:g id="NUMBER_1">%2$d</xliff:g>, ifoni izobuyiselwa kwizimiso zasembonini futhi yonke imininingwane yomsebenzisi izolahleka."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Uzame ukuvula ngendlela engafanele ifoni izikhathi ezingu-<xliff:g id="NUMBER">%d</xliff:g>. Ithebhulethi manje isizosethwa kabusha ibe yizimiso ezizenzakalelayo."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Uzame ngokungalungile ukuvula i-TV izikhathi ezingu-<xliff:g id="NUMBER">%d</xliff:g>. I-TV manje izosethelwa kabusha kokuzenzakalelayo kwasekuqaleni."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Uzame ukuvula ngendlela engafanele ifoni izikhathi ezingu-<xliff:g id="NUMBER">%d</xliff:g>. Ifoni manje isizosethwa kabusha ibe yizimiso ezizenzakalelayo."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Udwebe ngokungalungile iphathini yakho yokuvula izikhathi ezingu-<xliff:g id="NUMBER_0">%d</xliff:g>. Emva <xliff:g id="NUMBER_1">%d</xliff:g> kweminye imizamo engaphumelelanga, uzocelwa ukuvula ithebhulethi yakho usebenzisa ukungena ngemvume kwi-Google.\n\n Sicela uzame futhi emuva kwamasekhondi angu-<xliff:g id="NUMBER_2">%d</xliff:g>"</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Udwebele ngokungalungile iphethini yakho yokuvula izikhathi ezingu-<xliff:g id="NUMBER_0">%d</xliff:g>. Ngemuva kwemizamo engaphezulu kwengu-<xliff:g id="NUMBER_1">%d</xliff:g> engaphumelelanga, uzocelwa ukuthi uvule i-TV yakho usebenzisa i-akhawunti ye-imeyili.\n\n Zama futhi kumasekhondi angu-<xliff:g id="NUMBER_2">%d</xliff:g>."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Ukulayisha ungenisa iphathini yakho yokuvula ngendlela engalungile izikhathi ezi-<xliff:g id="NUMBER_0">%d</xliff:g> Emva kweminye imizamo engu-<xliff:g id="NUMBER_1">%d</xliff:g>, uzocelwa ukuvula ifoni yakho usebenzisa ukungena ngemvume ku-Google\n\n Zame futhi emumva kwengu- <xliff:g id="NUMBER_2">%d</xliff:g> amasekhondi."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Udwebe ngokungalungile iphathini yakho yokuvula izikhathi ezingu-<xliff:g id="NUMBER_0">%1$d</xliff:g>. Emva <xliff:g id="NUMBER_1">%2$d</xliff:g> kweminye imizamo engaphumelelanga, uzocelwa ukuvula ithebhulethi yakho usebenzisa ukungena ngemvume kwi-Google.\n\n Sicela uzame futhi emuva kwamasekhondi angu-<xliff:g id="NUMBER_2">%3$d</xliff:g>"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Udwebele ngokungalungile iphethini yakho yokuvula izikhathi ezingu-<xliff:g id="NUMBER_0">%1$d</xliff:g>. Ngemuva kwemizamo engaphezulu kwengu-<xliff:g id="NUMBER_1">%2$d</xliff:g> engaphumelelanga, uzocelwa ukuthi uvule i-TV yakho usebenzisa i-akhawunti ye-imeyili.\n\n Zama futhi kumasekhondi angu-<xliff:g id="NUMBER_2">%3$d</xliff:g>."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Ukulayisha ungenisa iphathini yakho yokuvula ngendlela engalungile izikhathi ezi-<xliff:g id="NUMBER_0">%1$d</xliff:g> Emva kweminye imizamo engu-<xliff:g id="NUMBER_1">%2$d</xliff:g>, uzocelwa ukuvula ifoni yakho usebenzisa ukungena ngemvume ku-Google\n\n Zame futhi emumva kwengu- <xliff:g id="NUMBER_2">%3$d</xliff:g> amasekhondi."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Susa"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Khuphukisa ivolumu ngaphezu kweleveli enconyiwe?\n\nUkulalela ngevolumu ephezulu izikhathi ezide kungahle kulimaze ukuzwa kwakho."</string>
@@ -1470,6 +1468,7 @@
<item quantity="other">Ngamahora angu-%d</item>
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Kuze kube ngu-<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="9128205721301330797">"Kuze kube ngu-<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (i-alamu elandelayo)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Uze uvale lokhu"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Uze uvale ungaphazamisi"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index a6eb68b..d7dd3ec 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -323,7 +323,9 @@
<attr name="windowBackground" format="reference" />
<!-- Drawable to draw selectively within the inset areas when the windowBackground
has been set to null. This protects against seeing visual garbage in the
- surface when the app has not drawn any content into this area. -->
+ surface when the app has not drawn any content into this area. One example is
+ when the user is resizing a window of an activity that has
+ {@link android.R.attr#resizeableActivity} set for multi-window mode. -->
<attr name="windowBackgroundFallback" format="reference" />
<!-- Drawable to use as a frame around the window. -->
<attr name="windowFrame" format="reference" />
@@ -1826,6 +1828,9 @@
<enum name="KEYCODE_MEDIA_STEP_FORWARD" value="274" />
<enum name="KEYCODE_MEDIA_STEP_BACKWARD" value="275" />
<enum name="KEYCODE_SOFT_SLEEP" value="276" />
+ <enum name="KEYCODE_CUT" value="277" />
+ <enum name="KEYCODE_COPY" value="278" />
+ <enum name="KEYCODE_PASTE" value="279" />
</attr>
<!-- ***************************************************************** -->
@@ -5013,6 +5018,12 @@
<attr name="autoMirrored" format="boolean" />
</declare-styleable>
+ <!-- Drawable class used to wrap other drawables. -->
+ <declare-styleable name="DrawableWrapper">
+ <!-- The wrapped drawable. -->
+ <attr name="drawable" />
+ </declare-styleable>
+
<!-- Drawable used to render several states. Each state is represented by
a child drawable. -->
<declare-styleable name="StateListDrawable">
@@ -5380,6 +5391,7 @@
<attr name="color" />
</declare-styleable>
+ <!-- Drawable used to wrap and inset another drawable. -->
<declare-styleable name="InsetDrawable">
<attr name="visible" />
<attr name="drawable" />
@@ -7605,6 +7617,44 @@
<attr name="pointerIconSpotTouch" format="reference" />
<!-- Reference to a pointer icon drawable with STYLE_SPOT_ANCHOR -->
<attr name="pointerIconSpotAnchor" format="reference" />
+ <!-- Reference to a pointer drawable with STYLE_CONTEXT_MENU -->
+ <attr name="pointerIconContextMenu" format="reference"/>
+ <!-- Reference to a pointer drawable with STYLE_HAND -->
+ <attr name="pointerIconHand" format="reference"/>
+ <!-- Reference to a pointer drawable with STYLE_HELP -->
+ <attr name="pointerIconHelp" format="reference"/>
+ <!-- Reference to a pointer drawable with STYLE_CELL -->
+ <attr name="pointerIconCell" format="reference"/>
+ <!-- Reference to a pointer drawable with STYLE_CROSSHAIR -->
+ <attr name="pointerIconCrosshair" format="reference"/>
+ <!-- Reference to a pointer drawable with STYLE_TEXT -->
+ <attr name="pointerIconText" format="reference"/>
+ <!-- Reference to a pointer drawable with STYLE_VERTICAL_TEXT -->
+ <attr name="pointerIconVerticalText" format="reference"/>
+ <!-- Reference to a pointer drawable with STYLE_ALIAS -->
+ <attr name="pointerIconAlias" format="reference"/>
+ <!-- Reference to a pointer drawable with STYLE_COPY -->
+ <attr name="pointerIconCopy" format="reference"/>
+ <!-- Reference to a pointer drawable with STYLE_NODROP -->
+ <attr name="pointerIconNodrop" format="reference"/>
+ <!-- Reference to a pointer drawable with STYLE_ALL_SCROLL -->
+ <attr name="pointerIconAllScroll" format="reference"/>
+ <!-- Reference to a pointer drawable with STYLE_HORIZONTAL_DOUBLE_ARROW -->
+ <attr name="pointerIconHorizontalDoubleArrow" format="reference"/>
+ <!-- Reference to a pointer drawable with STYLE_VERTICAL_DOUBLE_ARROW -->
+ <attr name="pointerIconVerticalDoubleArrow" format="reference"/>
+ <!-- Reference to a pointer drawable with STYLE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW -->
+ <attr name="pointerIconTopRightDiagonalDoubleArrow" format="reference"/>
+ <!-- Reference to a pointer drawable with STYLE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW -->
+ <attr name="pointerIconTopLeftDiagonalDoubleArrow" format="reference"/>
+ <!-- Reference to a pointer drawable with STYLE_ZOOM_IN -->
+ <attr name="pointerIconZoomIn" format="reference"/>
+ <!-- Reference to a pointer drawable with STYLE_ZOOM_OUT -->
+ <attr name="pointerIconZoomOut" format="reference"/>
+ <!-- Reference to a pointer drawable with STYLE_GRAB -->
+ <attr name="pointerIconGrab" format="reference"/>
+ <!-- Reference to a pointer drawable with STYLE_GRABBING -->
+ <attr name="pointerIconGrabbing" format="reference"/>
</declare-styleable>
<declare-styleable name="PointerIcon">
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 9d844a8..e376903 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -571,6 +571,11 @@
single integer, with higher numbers considered to be better. -->
<attr name="priority" format="integer" />
+ <!-- Indicate if this component is aware of encryption lifecycle, and can be
+ safely run before the user has entered their credentials (such as a lock
+ pattern or PIN). -->
+ <attr name="encryptionAware" format="boolean" />
+
<!-- Specify how an activity should be launched. See the
<a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
Stack</a> document for important information on how these options impact
@@ -1059,7 +1064,7 @@
at the same time.
<p>The default value is <code>false</code> for applications with
- <code>targetSdkVersion</code> lesser than {@link android.os.Build.VERSION_CODES#M} and
+ <code>targetSdkVersion</code> lesser than {@link android.os.Build.VERSION_CODES#N} and
<code>true</code> otherwise.
<p>NOTE: A task's root activity value is applied to all additional activities launched in
@@ -1067,11 +1072,24 @@
all other activities in the task as resizeable and will not if the root activity isn't
resizeable.
- <p>NOTE: The value of {@link android.R.attr#screenOrientation} will be ignored for
- resizeable activities as the system doesn't support fixed orientation on a resizeable
- activity. -->
+ <p>NOTE: The value of {@link android.R.attr#screenOrientation} is ignored for
+ resizeable activities when in multi-window mode. -->
<attr name="resizeableActivity" format="boolean" />
+ <!-- Indicates that the activity supports the picture-in-picture (PiP) form of multi-window.
+ While it makes sense to be able to resize most activities types in multi-window mode when
+ {@link android.R.attr#resizeableActivity} is set. It only makes sense to put specific types
+ of activities in PiP mode of multi-window. For example, activities that play video. When
+ set the activity will be allowed to enter PiP mode when the system deems it appropriate on
+ devices that support PiP.
+
+ <p>The default value is <code>false</code> for applications with
+ <code>targetSdkVersion</code> lesser than {@link android.os.Build.VERSION_CODES#N} and
+ <code>true</code> otherwise.
+
+ <p>NOTE: Attribute is only used if {@link android.R.attr#resizeableActivity} is true. -->
+ <attr name="supportsPictureInPicture" format="boolean" />
+
<!-- This value indicates how tasks rooted at this activity will behave in lockTask mode.
While in lockTask mode the system will not launch non-permitted tasks until
lockTask mode is disabled.
@@ -1264,6 +1282,8 @@
<attr name="usesCleartextTraffic" />
<attr name="multiArch" />
<attr name="extractNativeLibs" />
+ <attr name="forceDeviceEncrypted" format="boolean" />
+ <attr name="encryptionAware" />
</declare-styleable>
<!-- The <code>permission</code> tag declares a security permission that can be
used to control access from other packages to specific components or
@@ -1639,6 +1659,7 @@
<attr name="enabled" />
<attr name="exported" />
<attr name="singleUser" />
+ <attr name="encryptionAware" />
</declare-styleable>
<!-- Attributes that can be supplied in an AndroidManifest.xml
@@ -1722,6 +1743,7 @@
with it is through the Service API (binding and starting). -->
<attr name="isolatedProcess" format="boolean" />
<attr name="singleUser" />
+ <attr name="encryptionAware" />
</declare-styleable>
<!-- The <code>receiver</code> tag declares an
@@ -1757,6 +1779,7 @@
<attr name="enabled" />
<attr name="exported" />
<attr name="singleUser" />
+ <attr name="encryptionAware" />
</declare-styleable>
<!-- The <code>activity</code> tag declares an
@@ -1826,8 +1849,10 @@
<attr name="relinquishTaskIdentity" />
<attr name="resumeWhilePausing" />
<attr name="resizeableActivity" />
+ <attr name="supportsPictureInPicture" />
<attr name="lockTaskMode" />
<attr name="showForAllUsers" />
+ <attr name="encryptionAware" />
</declare-styleable>
<!-- The <code>activity-alias</code> tag declares a new
@@ -2197,18 +2222,25 @@
<attr name="name" />
</declare-styleable>
- <!-- <code>initial-layout</code> tag allows configuring the initial layout for the activity
- within multi-window environment. -->
- <declare-styleable name="AndroidManifestInitialLayout" parent="AndroidManifestActivity">
+ <!-- <code>layout</code> tag allows configuring the layout for the activity within multi-window
+ environment. -->
+ <declare-styleable name="AndroidManifestLayout" parent="AndroidManifestActivity">
<!-- Initial width of the activity. Can be either a fixed value or fraction, in which case
the width will be constructed as a fraction of the total available width. -->
- <attr name="activityWidth" format="dimension|fraction" />
+ <attr name="initialWidth" format="dimension|fraction" />
<!-- Initial height of the activity. Can be either a fixed value or fraction, in which case
the height will be constructed as a fraction of the total available height. -->
- <attr name="activityHeight" format="dimension|fraction" />
+ <attr name="initialHeight" format="dimension|fraction" />
<!-- Where to initially position the activity inside the available space. Uses constants
defined in {@link android.view.Gravity}. -->
<attr name="gravity" />
+ <!-- Minimal height of the activity.
+
+ <p>NOTE: 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" />
</declare-styleable>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 24d760f..9c75b7a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -938,6 +938,9 @@
<!-- Is the device capable of hot swapping an UICC Card -->
<bool name="config_hotswapCapable">false</bool>
+ <!-- Component name of the ICC hotswap prompt for restart dialog -->
+ <string name="config_iccHotswapPromptForRestartDialogComponent" translateable="false">@null</string>
+
<!-- Enable puk unlockscreen by default.
If unlock screen is disabled, the puk should be unlocked through Emergency Dialer -->
<bool name="config_enable_puk_unlock_screen">true</bool>
@@ -1246,6 +1249,9 @@
<!-- Operating volatage for bluetooth controller. 0 by default-->
<integer translatable="false" name="config_bluetooth_operating_voltage_mv">4</integer>
+ <!-- Whether supported profiles should be reloaded upon enabling bluetooth -->
+ <bool name="config_bluetooth_reload_supported_profiles_when_enabled">false</bool>
+
<!-- The default data-use polling period. -->
<integer name="config_datause_polling_period_sec">600</integer>
@@ -1282,6 +1288,10 @@
device is data-only. -->
<bool name="config_voice_capable">true</bool>
+ <!-- Flag indicating that an outbound call must have a call capable phone account
+ that has declared it can process the call's handle. -->
+ <bool name="config_requireCallCapableAccountForHandle">false</bool>
+
<!-- Flag indicating if the user is notified when the mobile network access is restricted -->
<bool name="config_user_notification_of_restrictied_mobile_access">true</bool>
@@ -1520,8 +1530,8 @@
<!-- If the time difference is greater than this threshold in milliseconds,
then update the time. -->
<integer name="config_ntpThreshold">5000</integer>
- <!-- Timeout to wait for NTP server response. -->
- <integer name="config_ntpTimeout">20000</integer>
+ <!-- Timeout to wait for NTP server response in milliseconds. -->
+ <integer name="config_ntpTimeout">5000</integer>
<!-- Default network policy warning threshold, in megabytes. -->
<integer name="config_networkPolicyDefaultWarning">2048</integer>
@@ -2120,8 +2130,10 @@
string that's stored in 8-bit unpacked format) characters.-->
<bool translatable="false" name="config_sms_decode_gsm_8bit_data">false</bool>
- <!-- Package name providing WebView implementation. -->
- <string name="config_webViewPackageName" translatable="false">com.android.webview</string>
+ <!-- List of package names (ordered by preference) providing WebView implementations. -->
+ <string-array name="config_webViewPackageNames" translatable="false">
+ <item>com.android.webview</item>
+ </string-array>
<!-- If EMS is not supported, framework breaks down EMS into single segment SMS
and adds page info " x/y". This config is used to set which carrier doesn't
@@ -2318,7 +2330,7 @@
<!-- Flag indicating device support for EAP SIM, AKA, AKA' -->
<bool name="config_eap_sim_based_auth_supported">true</bool>
-
+
<!-- How long history of previous vibrations should be kept for the dumpsys. -->
<integer name="config_previousVibrationsDumpLimit">20</integer>
@@ -2349,4 +2361,12 @@
<!-- Name of the component to handle network policy notifications. If present,
disables NetworkPolicyManagerService's presentation of data-usage notifications. -->
<string translatable="false" name="config_networkPolicyNotificationComponent"></string>
+
+ <!-- The fraction of display size (lower of height and width) that will be used to determine
+ the default minimal size for resizeable tasks. -->
+ <fraction name="config_displayFractionForDefaultMinimalSizeOfResizeableTask">25%</fraction>
+
+ <!-- The BT name of the keyboard packaged with the device. If this is defined, SystemUI will
+ automatically try to pair with it when the device exits tablet mode. -->
+ <string translatable="false" name="config_packagedKeyboardName"></string>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 2621bc9c..28756f5 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -45,8 +45,12 @@
<!-- Margin at the edge of the screen to ignore touch events for in the windowshade. -->
<dimen name="status_bar_edge_ignore">5dp</dimen>
- <!-- Width of a divider bar used to resize docked stacks. -->
- <dimen name="docked_stack_divider_thickness">24dp</dimen>
+ <!-- Width of the window of the divider bar used to resize docked stacks. -->
+ <dimen name="docked_stack_divider_thickness">48dp</dimen>
+
+ <!-- How much the content in the divider is inset from the window bounds when resting. Used to
+ calculate the bounds of the stacks-->
+ <dimen name="docked_stack_divider_insets">18dp</dimen>
<!-- Min width for a tablet device -->
<dimen name="min_xlarge_screen_width">800dp</dimen>
@@ -367,10 +371,6 @@
<dimen name="resolver_max_width">480dp</dimen>
- <!-- Size of the offset applied to the position of the circular mask. This
- is only used on circular displays. In the case where there is no
- "chin", this will default to 0 -->
- <dimen name="circular_display_mask_offset">0px</dimen>
<!-- Amount to reduce the size of the circular mask by (to compensate for
aliasing effects). This is only used on circular displays. -->
<dimen name="circular_display_mask_thickness">1px</dimen>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index f4d0b39..43a6acd 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2662,9 +2662,11 @@
<public type="attr" name="listMenuViewStyle" />
<public type="attr" name="subMenuArrow" />
- <public type="attr" name="activityWidth" />
- <public type="attr" name="activityHeight" />
+ <public type="attr" name="initialWidth" />
+ <public type="attr" name="initialHeight" />
+ <public type="attr" name="minimalSize" />
<public type="attr" name="resizeableActivity" />
+ <public type="attr" name="supportsPictureInPicture" />
<public type="attr" name="titleMargin" />
<public type="attr" name="titleMarginStart" />
<public type="attr" name="titleMarginEnd" />
@@ -2676,6 +2678,9 @@
<public type="attr" name="level" />
<public type="attr" name="contextPopupMenuStyle" />
<public type="attr" name="textAppearancePopupMenuHeader" />
+ <public type="attr" name="windowBackgroundFallback" />
+ <public type="attr" name="forceDeviceEncrypted" />
+ <public type="attr" name="encryptionAware" />
<public type="style" name="Theme.Material.DayNight" />
<public type="style" name="Theme.Material.DayNight.DarkActionBar" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index af5c74d..faa76f2 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -621,7 +621,7 @@
<string name="permdesc_statusBar">Allows the app to disable the status bar or add and remove system icons.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permlab_statusBarService">status bar</string>
+ <string name="permlab_statusBarService">be the status bar</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_statusBarService">Allows the app to be the status bar.</string>
@@ -718,7 +718,7 @@
discover information about which applications are used on the device.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permlab_manageProfileAndDeviceOwners">Manage profile and device owners</string>
+ <string name="permlab_manageProfileAndDeviceOwners">manage profile and device owners</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to set the profile/device owners.
[CHAR LIMIT=NONE] -->
<string name="permdesc_manageProfileAndDeviceOwners">Allows apps to set the profile owners and the device owner.</string>
@@ -882,7 +882,7 @@
Malicious apps may use this to erase or modify your call log.</string>
<!-- Title of the body sensors permission, listed so the user can decide whether to allow the application to access body sensor data. [CHAR LIMIT=30] -->
- <string name="permlab_bodySensors">body sensors (like heart rate monitors)
+ <string name="permlab_bodySensors">access body sensors (like heart rate monitors)
</string>
<!-- Description of the body sensors permission, listed so the user can decide whether to allow the application to access data from body sensors. [CHAR LIMIT=NONE] -->
<string name="permdesc_bodySensors" product="default">Allows the app to access data from sensors
@@ -936,7 +936,7 @@
with the operation of the GPS or other location sources.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permlab_accessFineLocation">precise location (GPS and
+ <string name="permlab_accessFineLocation">access precise location (GPS and
network-based)</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_accessFineLocation">Allows the app to get your
@@ -947,7 +947,7 @@
battery power.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permlab_accessCoarseLocation">approximate location
+ <string name="permlab_accessCoarseLocation">access approximate location
(network-based)</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_accessCoarseLocation">Allows the app to get your
@@ -970,7 +970,7 @@
without your confirmation.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permlab_sim_communication">sim communication</string>
+ <string name="permlab_sim_communication">send commands to the SIM</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_sim_communication">Allows the app to send commands to the SIM. This is very dangerous.</string>
@@ -1077,7 +1077,7 @@
connected.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permlab_createNetworkSockets">full network access</string>
+ <string name="permlab_createNetworkSockets">have full network access</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_createNetworkSockets">Allows the app to create
network sockets and use custom network protocols. The browser and other
@@ -1141,7 +1141,7 @@
WiMAX is enabled and information about any WiMAX networks that are
connected. </string>
- <string name="permlab_changeWimaxState">Change WiMAX state</string>
+ <string name="permlab_changeWimaxState">change WiMAX state</string>
<string name="permdesc_changeWimaxState" product="tablet">Allows the app to
connect the tablet to and disconnect the tablet from WiMAX networks.</string>
<string name="permdesc_changeWimaxState" product="tv">Allows the app to
@@ -1346,7 +1346,7 @@
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_accessDrmCertificates">Allows an application to provision and use DRM certficates. Should never be needed for normal apps.</string>
- <string name="permlab_handoverStatus">Receive Android Beam transfer status</string>
+ <string name="permlab_handoverStatus">receive Android Beam transfer status</string>
<string name="permdesc_handoverStatus">Allows this application to receive information about current Android Beam transfers</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -1697,7 +1697,7 @@
--> <skip />
<!-- On the keyguard screen, it shows the carrier the phone is connected to. This is displayed if the phone is not connected to a carrier.-->
- <string name="lockscreen_carrier_default">No service.</string>
+ <string name="lockscreen_carrier_default">No service</string>
<!-- Shown in the lock screen to tell the user that the screen is locked. -->
<string name="lockscreen_screen_locked">Screen locked.</string>
@@ -1781,72 +1781,72 @@
<!-- For the unlock screen, Information message shown in dialog when user has too many failed attempts at
drawing the unlock pattern -->
<string name="lockscreen_too_many_failed_attempts_dialog_message">
- You have incorrectly drawn your unlock pattern <xliff:g id="number">%d</xliff:g> times.
- \n\nTry again in <xliff:g id="number">%d</xliff:g> seconds.
+ You have incorrectly drawn your unlock pattern <xliff:g id="number">%1$d</xliff:g> times.
+ \n\nTry again in <xliff:g id="number">%2$d</xliff:g> seconds.
</string>
<!-- For the unlock screen, Information message shown in dialog when user has too many failed attempts at
entering the password -->
<string name="lockscreen_too_many_failed_password_attempts_dialog_message">
- You have incorrectly typed your password <xliff:g id="number">%d</xliff:g> times.
- \n\nTry again in <xliff:g id="number">%d</xliff:g> seconds.
+ You have incorrectly typed your password <xliff:g id="number">%1$d</xliff:g> times.
+ \n\nTry again in <xliff:g id="number">%2$d</xliff:g> seconds.
</string>
<!-- For the unlock screen, Information message shown in dialog when user has too many failed attempts at
entering the PIN -->
<string name="lockscreen_too_many_failed_pin_attempts_dialog_message">
- You have incorrectly typed your PIN <xliff:g id="number">%d</xliff:g> times.
- \n\nTry again in <xliff:g id="number">%d</xliff:g> seconds.
+ You have incorrectly typed your PIN <xliff:g id="number">%1$d</xliff:g> times.
+ \n\nTry again in <xliff:g id="number">%2$d</xliff:g> seconds.
</string>
<!-- For the unlock screen, informational message shown in dialog when user is almost at the limit
where they will be locked out and may have to enter an alternate username/password to unlock the phone -->
<string name="lockscreen_failed_attempts_almost_glogin" product="tablet">
- You have incorrectly drawn your unlock pattern <xliff:g id="number">%d</xliff:g> times.
- After <xliff:g id="number">%d</xliff:g> more unsuccessful attempts,
+ You have incorrectly drawn your unlock pattern <xliff:g id="number">%1$d</xliff:g> times.
+ After <xliff:g id="number">%2$d</xliff:g> more unsuccessful attempts,
you will be asked to unlock your tablet using your Google signin.\n\n
- Try again in <xliff:g id="number">%d</xliff:g> seconds.
+ Try again in <xliff:g id="number">%3$d</xliff:g> seconds.
</string>
<!-- For the unlock screen, informational message shown in dialog when user is almost at the limit
where they will be locked out and may have to enter an alternate username/password to unlock the phone -->
<string name="lockscreen_failed_attempts_almost_glogin" product="tv">
- You have incorrectly drawn your unlock pattern <xliff:g id="number">%d</xliff:g> times.
- After <xliff:g id="number">%d</xliff:g> more unsuccessful attempts,
+ You have incorrectly drawn your unlock pattern <xliff:g id="number">%1$d</xliff:g> times.
+ After <xliff:g id="number">%2$d</xliff:g> more unsuccessful attempts,
you will be asked to unlock your TV using your Google signin.\n\n
- Try again in <xliff:g id="number">%d</xliff:g> seconds.
+ Try again in <xliff:g id="number">%3$d</xliff:g> seconds.
</string>
<!-- For the unlock screen, informational message shown in dialog when user is almost at the limit
where they will be locked out and may have to enter an alternate username/password to unlock the phone -->
<string name="lockscreen_failed_attempts_almost_glogin" product="default">
- You have incorrectly drawn your unlock pattern <xliff:g id="number">%d</xliff:g> times.
- After <xliff:g id="number">%d</xliff:g> more unsuccessful attempts,
+ You have incorrectly drawn your unlock pattern <xliff:g id="number">%1$d</xliff:g> times.
+ After <xliff:g id="number">%2$d</xliff:g> more unsuccessful attempts,
you will be asked to unlock your phone using your Google signin.\n\n
- Try again in <xliff:g id="number">%d</xliff:g> seconds.
+ Try again in <xliff:g id="number">%3$d</xliff:g> seconds.
</string>
<!-- For the unlock screen, informational message shown in dialog when user is almost at the limit
where the device will be wiped. -->
<string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet">
- You have incorrectly attempted to unlock the tablet <xliff:g id="number">%d</xliff:g> times.
- After <xliff:g id="number">%d</xliff:g> more unsuccessful attempts,
+ You have incorrectly attempted to unlock the tablet <xliff:g id="number">%1$d</xliff:g> times.
+ After <xliff:g id="number">%2$d</xliff:g> more unsuccessful attempts,
the tablet will be reset to factory default and all user data will be lost.
</string>
<!-- For the unlock screen, informational message shown in dialog when user is almost at the limit
where the device will be wiped. -->
<string name="lockscreen_failed_attempts_almost_at_wipe" product="tv">
- You have incorrectly attempted to unlock the TV <xliff:g id="number">%d</xliff:g> times.
- After <xliff:g id="number">%d</xliff:g> more unsuccessful attempts,
+ You have incorrectly attempted to unlock the TV <xliff:g id="number">%1$d</xliff:g> times.
+ After <xliff:g id="number">%2$d</xliff:g> more unsuccessful attempts,
the TV will be reset to factory default and all user data will be lost.
</string>
<!-- For the unlock screen, informational message shown in dialog when user is almost at the limit
where the device will be wiped. -->
<string name="lockscreen_failed_attempts_almost_at_wipe" product="default">
- You have incorrectly attempted to unlock the phone <xliff:g id="number">%d</xliff:g> times.
- After <xliff:g id="number">%d</xliff:g> more unsuccessful attempts,
+ You have incorrectly attempted to unlock the phone <xliff:g id="number">%1$d</xliff:g> times.
+ After <xliff:g id="number">%2$d</xliff:g> more unsuccessful attempts,
the phone will be reset to factory default and all user data will be lost.
</string>
@@ -1982,12 +1982,6 @@
<!-- Label for granularity to traverse the content on an AccessibilityNodeInfo by line. Only spoken to the user. [CHAR LIMIT=NONE] -->
<string name="granularity_label_line">line</string>
- <!-- A format string for 12-hour time of day, just the hour, not the minute, with lower-case "am" or "pm" (example: "3pm"). -->
- <string name="hour_ampm">"<xliff:g id="hour" example="3">%-l</xliff:g><xliff:g id="ampm" example="pm">%P</xliff:g>"</string>
-
- <!-- A format string for 12-hour time of day, just the hour, not the minute, with capital "AM" or "PM" (example: "3PM"). -->
- <string name="hour_cap_ampm">"<xliff:g id="hour" example="3">%-l</xliff:g><xliff:g id="ampm" example="PM">%p</xliff:g>"</string>
-
<!-- Title of the alert when something went wrong in the factory test. -->
<string name="factorytest_failed">Factory test failed</string>
<!-- Error message displayed when a non-system application tries to start a factory test. -->
@@ -2975,17 +2969,17 @@
<string name="activity_list_empty">No matching activities found.</string>
<!-- Title of an application permission that lets an application route media output. -->
- <string name="permlab_route_media_output">Route media output</string>
+ <string name="permlab_route_media_output">route media output</string>
<!-- Description of an application permission that lets an application route media output. -->
<string name="permdesc_route_media_output">Allows an application to route media output to other external devices.</string>
<!-- Title of an application permission that lets it read install sessions. -->
- <string name="permlab_readInstallSessions">Read install sessions</string>
+ <string name="permlab_readInstallSessions">read install sessions</string>
<!-- Description of an application permission that lets it read install sessions. -->
<string name="permdesc_readInstallSessions">Allows an application to read install sessions. This allows it to see details about active package installations.</string>
<!-- Title of an application permission that lets it read install sessions. -->
- <string name="permlab_requestInstallPackages">Request install packages</string>
+ <string name="permlab_requestInstallPackages">request install packages</string>
<!-- Description of an application permission that lets it read install sessions. -->
<string name="permdesc_requestInstallPackages">Allows an application to request installation of packages.</string>
@@ -3532,34 +3526,34 @@
<string name="kg_login_checking_password">Checking account\u2026</string>
<!-- Message shown in dialog when max number of attempts are reached for PIN screen of keyguard -->
<string name="kg_too_many_failed_pin_attempts_dialog_message">
- You have incorrectly typed your PIN <xliff:g id="number">%d</xliff:g> times.
- \n\nTry again in <xliff:g id="number">%d</xliff:g> seconds.
+ You have incorrectly typed your PIN <xliff:g id="number">%1$d</xliff:g> times.
+ \n\nTry again in <xliff:g id="number">%2$d</xliff:g> seconds.
</string>
<!-- Message shown in dialog when max number of attempts are reached for password screen of keyguard -->
<string name="kg_too_many_failed_password_attempts_dialog_message">
- You have incorrectly typed your password <xliff:g id="number">%d</xliff:g> times.
- \n\nTry again in <xliff:g id="number">%d</xliff:g> seconds.
+ You have incorrectly typed your password <xliff:g id="number">%1$d</xliff:g> times.
+ \n\nTry again in <xliff:g id="number">%2$d</xliff:g> seconds.
</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message">
- You have incorrectly drawn your unlock pattern <xliff:g id="number">%d</xliff:g> times.
- \n\nTry again in <xliff:g id="number">%d</xliff:g> seconds.
+ You have incorrectly drawn your unlock pattern <xliff:g id="number">%1$d</xliff:g> times.
+ \n\nTry again in <xliff:g id="number">%2$d</xliff:g> seconds.
</string>
<!-- Message shown when user is almost at the limit of password attempts where the device will be wiped. -->
<string name="kg_failed_attempts_almost_at_wipe" product="tablet">
- You have incorrectly attempted to unlock the tablet <xliff:g id="number">%d</xliff:g> times.
- After <xliff:g id="number">%d</xliff:g> more unsuccessful attempts,
+ You have incorrectly attempted to unlock the tablet <xliff:g id="number">%1$d</xliff:g> times.
+ After <xliff:g id="number">%2$d</xliff:g> more unsuccessful attempts,
the tablet will be reset to factory default and all user data will be lost.
</string>
<!-- Message shown when user is almost at the limit of password attempts where the device will be wiped. -->
<string name="kg_failed_attempts_almost_at_wipe" product="tv">
- You have incorrectly attempted to unlock the TV <xliff:g id="number">%d</xliff:g> times.
- After <xliff:g id="number">%d</xliff:g> more unsuccessful attempts,
+ You have incorrectly attempted to unlock the TV <xliff:g id="number">%1$d</xliff:g> times.
+ After <xliff:g id="number">%2$d</xliff:g> more unsuccessful attempts,
the TV will be reset to factory default and all user data will be lost.
</string>
<!-- Message shown when user is almost at the limit of password attempts where the device will be wiped. -->
<string name="kg_failed_attempts_almost_at_wipe" product="default">
- You have incorrectly attempted to unlock the phone <xliff:g id="number">%d</xliff:g> times.
- After <xliff:g id="number">%d</xliff:g> more unsuccessful attempts,
+ You have incorrectly attempted to unlock the phone <xliff:g id="number">%1$d</xliff:g> times.
+ After <xliff:g id="number">%2$d</xliff:g> more unsuccessful attempts,
the phone will be reset to factory default and all user data will be lost.
</string>
<!-- Message shown in dialog when user has exceeded the maximum attempts and the device will now be wiped -->
@@ -3580,26 +3574,26 @@
<!-- Message shown in dialog when user is almost at the limit where they will be
locked out and may have to enter an alternate username/password to unlock the phone -->
<string name="kg_failed_attempts_almost_at_login" product="tablet">
- You have incorrectly drawn your unlock pattern <xliff:g id="number">%d</xliff:g> times.
- After <xliff:g id="number">%d</xliff:g> more unsuccessful attempts,
+ You have incorrectly drawn your unlock pattern <xliff:g id="number">%1$d</xliff:g> times.
+ After <xliff:g id="number">%2$d</xliff:g> more unsuccessful attempts,
you will be asked to unlock your tablet using an email account.\n\n
- Try again in <xliff:g id="number">%d</xliff:g> seconds.
+ Try again in <xliff:g id="number">%3$d</xliff:g> seconds.
</string>
<!-- Message shown in dialog when user is almost at the limit where they will be
locked out and may have to enter an alternate username/password to unlock the phone -->
<string name="kg_failed_attempts_almost_at_login" product="tv">
- You have incorrectly drawn your unlock pattern <xliff:g id="number">%d</xliff:g> times.
- After <xliff:g id="number">%d</xliff:g> more unsuccessful attempts,
+ You have incorrectly drawn your unlock pattern <xliff:g id="number">%1$d</xliff:g> times.
+ After <xliff:g id="number">%2$d</xliff:g> more unsuccessful attempts,
you will be asked to unlock your TV using an email account.\n\n
- Try again in <xliff:g id="number">%d</xliff:g> seconds.
+ Try again in <xliff:g id="number">%3$d</xliff:g> seconds.
</string>
<!-- Message shown in dialog when user is almost at the limit where they will be
locked out and may have to enter an alternate username/password to unlock the phone -->
<string name="kg_failed_attempts_almost_at_login" product="default">
- You have incorrectly drawn your unlock pattern <xliff:g id="number">%d</xliff:g> times.
- After <xliff:g id="number">%d</xliff:g> more unsuccessful attempts,
+ You have incorrectly drawn your unlock pattern <xliff:g id="number">%1$d</xliff:g> times.
+ After <xliff:g id="number">%2$d</xliff:g> more unsuccessful attempts,
you will be asked to unlock your phone using an email account.\n\n
- Try again in <xliff:g id="number">%d</xliff:g> seconds.
+ Try again in <xliff:g id="number">%3$d</xliff:g> seconds.
</string>
<!-- Sequence of characters used to separate message strings in keyguard. Typically just em-dash
with spaces on either side. [CHAR LIMIT=3] -->
@@ -3993,6 +3987,9 @@
<!-- Zen mode condition - line two: ending time. [CHAR LIMIT=NONE] -->
<string name="zen_mode_until">Until <xliff:g id="formattedTime" example="10:00 PM">%1$s</xliff:g></string>
+ <!-- Zen mode condition - line one: Until next alarm. [CHAR LIMIT=NONE] -->
+ <string name="zen_mode_alarm">Until <xliff:g id="formattedTime" example="10:00 PM">%1$s</xliff:g> (next alarm)</string>
+
<!-- Zen mode condition: no exit criteria. [CHAR LIMIT=NONE] -->
<string name="zen_mode_forever">Until you turn this off</string>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 11c4cc0..b831df8 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1336,6 +1336,33 @@
<item name="pointerIconSpotHover">@drawable/pointer_spot_hover_icon</item>
<item name="pointerIconSpotTouch">@drawable/pointer_spot_touch_icon</item>
<item name="pointerIconSpotAnchor">@drawable/pointer_spot_anchor_icon</item>
+ <item name="pointerIconHand">@drawable/pointer_hand_icon</item>
+ <item name="pointerIconContextMenu">@drawable/pointer_context_menu_icon</item>
+ <item name="pointerIconHelp">@drawable/pointer_help_icon</item>
+ <item name="pointerIconCell">@drawable/pointer_cell_icon</item>
+ <item name="pointerIconCrosshair">@drawable/pointer_crosshair_icon</item>
+ <item name="pointerIconText">@drawable/pointer_text_icon</item>
+ <item name="pointerIconVerticalText">@drawable/pointer_vertical_text_icon</item>
+ <item name="pointerIconAlias">@drawable/pointer_alias_icon</item>
+ <item name="pointerIconCopy">@drawable/pointer_copy_icon</item>
+ <item name="pointerIconAllScroll">@drawable/pointer_all_scroll_icon</item>
+ <item name="pointerIconNodrop">@drawable/pointer_nodrop_icon</item>
+ <item name="pointerIconHorizontalDoubleArrow">
+ @drawable/pointer_horizontal_double_arrow_icon
+ </item>
+ <item name="pointerIconVerticalDoubleArrow">
+ @drawable/pointer_vertical_double_arrow_icon
+ </item>
+ <item name="pointerIconTopRightDiagonalDoubleArrow">
+ @drawable/pointer_top_right_diagonal_double_arrow_icon
+ </item>
+ <item name="pointerIconTopLeftDiagonalDoubleArrow">
+ @drawable/pointer_top_left_diagonal_double_arrow_icon
+ </item>
+ <item name="pointerIconZoomIn">@drawable/pointer_zoom_in_icon</item>
+ <item name="pointerIconZoomOut">@drawable/pointer_zoom_out_icon</item>
+ <item name="pointerIconGrab">@drawable/pointer_grab_icon</item>
+ <item name="pointerIconGrabbing">@drawable/pointer_grabbing_icon</item>
</style>
<!-- Wifi dialog styles -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 06de81d..cda7faa 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -275,6 +275,7 @@
<java-symbol type="bool" name="config_ui_enableFadingMarquee" />
<java-symbol type="bool" name="config_use_strict_phone_number_comparation" />
<java-symbol type="bool" name="config_voice_capable" />
+ <java-symbol type="bool" name="config_requireCallCapableAccountForHandle" />
<java-symbol type="bool" name="config_user_notification_of_restrictied_mobile_access" />
<java-symbol type="bool" name="config_wifiDisplaySupportsProtectedBuffers" />
<java-symbol type="bool" name="preferences_prefer_dual_pane" />
@@ -370,6 +371,7 @@
<java-symbol type="integer" name="config_bluetooth_rx_cur_ma" />
<java-symbol type="integer" name="config_bluetooth_tx_cur_ma" />
<java-symbol type="integer" name="config_bluetooth_operating_voltage_mv" />
+ <java-symbol type="bool" name="config_bluetooth_reload_supported_profiles_when_enabled" />
<java-symbol type="integer" name="config_cursorWindowSize" />
<java-symbol type="integer" name="config_drawLockTimeoutMillis" />
<java-symbol type="integer" name="config_doublePressOnPowerBehavior" />
@@ -443,7 +445,6 @@
<java-symbol type="dimen" name="notification_large_icon_circle_padding" />
<java-symbol type="dimen" name="notification_badge_size" />
<java-symbol type="dimen" name="immersive_mode_cling_width" />
- <java-symbol type="dimen" name="circular_display_mask_offset" />
<java-symbol type="dimen" name="accessibility_magnification_indicator_width" />
<java-symbol type="dimen" name="circular_display_mask_thickness" />
@@ -641,8 +642,6 @@
<java-symbol type="string" name="gpsVerifNo" />
<java-symbol type="string" name="gpsVerifYes" />
<java-symbol type="string" name="gsm_alphabet_default_charset" />
- <java-symbol type="string" name="hour_ampm" />
- <java-symbol type="string" name="hour_cap_ampm" />
<java-symbol type="string" name="httpError" />
<java-symbol type="string" name="httpErrorAuth" />
<java-symbol type="string" name="httpErrorConnect" />
@@ -1449,6 +1448,8 @@
<java-symbol type="anim" name="dock_left_exit" />
<java-symbol type="anim" name="dock_right_enter" />
<java-symbol type="anim" name="dock_right_exit" />
+ <java-symbol type="anim" name="fade_in" />
+ <java-symbol type="anim" name="fade_out" />
<java-symbol type="anim" name="voice_activity_close_exit" />
<java-symbol type="anim" name="voice_activity_close_enter" />
<java-symbol type="anim" name="voice_activity_open_exit" />
@@ -1480,6 +1481,7 @@
<java-symbol type="bool" name="config_supportAutoRotation" />
<java-symbol type="bool" name="target_honeycomb_needs_options_menu" />
<java-symbol type="dimen" name="docked_stack_divider_thickness" />
+ <java-symbol type="dimen" name="docked_stack_divider_insets" />
<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" />
@@ -1686,6 +1688,7 @@
<java-symbol type="id" name="replace_app_icon" />
<java-symbol type="id" name="replace_message" />
<java-symbol type="fraction" name="config_dimBehindFadeDuration" />
+ <java-symbol type="fraction" name="config_displayFractionForDefaultMinimalSizeOfResizeableTask" />
<java-symbol type="fraction" name="config_screenAutoBrightnessDozeScaleFactor" />
<java-symbol type="integer" name="config_autoBrightnessBrighteningLightDebounce"/>
<java-symbol type="integer" name="config_autoBrightnessDarkeningLightDebounce"/>
@@ -1722,7 +1725,6 @@
<java-symbol type="integer" name="config_undockedHdmiRotation" />
<java-symbol type="integer" name="config_virtualKeyQuietTimeMillis" />
<java-symbol type="layout" name="am_compat_mode_dialog" />
- <java-symbol type="layout" name="docked_stack_divider" />
<java-symbol type="layout" name="launch_warning" />
<java-symbol type="layout" name="safe_mode" />
<java-symbol type="layout" name="simple_list_item_2_single_choice" />
@@ -1953,8 +1955,10 @@
<java-symbol type="bool" name="config_built_in_sip_phone" />
<java-symbol type="id" name="maximize_window" />
<java-symbol type="id" name="close_window" />
+ <java-symbol type="id" name="client_decor_placeholder" />
<java-symbol type="layout" name="non_client_decor_light" />
<java-symbol type="layout" name="non_client_decor_dark" />
+ <java-symbol type="drawable" name="non_client_decor_title_focused" />
<!-- From TelephonyProvider -->
<java-symbol type="xml" name="apns" />
@@ -2014,7 +2018,7 @@
<java-symbol type="attr" name="actionModeWebSearchDrawable" />
<java-symbol type="string" name="websearch" />
<java-symbol type="drawable" name="ic_media_video_poster" />
- <java-symbol type="string" name="config_webViewPackageName" />
+ <java-symbol type="array" name="config_webViewPackageNames" />
<!-- From SubtitleView -->
<java-symbol type="dimen" name="subtitle_corner_radius" />
@@ -2078,6 +2082,7 @@
<java-symbol type="string" name="zen_mode_default_events_name" />
<java-symbol type="array" name="config_system_condition_providers" />
<java-symbol type="string" name="muted_by" />
+ <java-symbol type="string" name="zen_mode_alarm" />
<java-symbol type="string" name="select_day" />
<java-symbol type="string" name="select_year" />
@@ -2326,4 +2331,8 @@
<java-symbol type="bool" name="config_cameraDoubleTapPowerGestureEnabled" />
<java-symbol type="drawable" name="platlogo_m" />
+
+ <java-symbol type="string" name="config_iccHotswapPromptForRestartDialogComponent" />
+
+ <java-symbol type="string" name="config_packagedKeyboardName" />
</resources>
diff --git a/core/tests/benchmarks/src/android/content/res/ResourcesBenchmark.java b/core/tests/benchmarks/src/android/content/res/ResourcesBenchmark.java
new file mode 100644
index 0000000..3638473
--- /dev/null
+++ b/core/tests/benchmarks/src/android/content/res/ResourcesBenchmark.java
@@ -0,0 +1,84 @@
+/*
+ * 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 android.content.res;
+
+import android.util.AttributeSet;
+import android.util.Xml;
+
+import com.android.internal.R;
+import com.google.caliper.SimpleBenchmark;
+
+import org.xmlpull.v1.XmlPullParser;
+
+public class ResourcesBenchmark extends SimpleBenchmark {
+
+ private AssetManager mAsset;
+ private Resources mRes;
+
+ private int mTextId;
+ private int mColorId;
+ private int mIntegerId;
+ private int mLayoutId;
+
+ @Override
+ protected void setUp() {
+ mAsset = new AssetManager();
+ mAsset.addAssetPath("/system/framework/framework-res.apk");
+ mRes = new Resources(mAsset, null, null);
+
+ mTextId = mRes.getIdentifier("cancel", "string", "android");
+ mColorId = mRes.getIdentifier("transparent", "color", "android");
+ mIntegerId = mRes.getIdentifier("config_shortAnimTime", "integer", "android");
+ mLayoutId = mRes.getIdentifier("two_line_list_item", "layout", "android");
+ }
+
+ @Override
+ protected void tearDown() {
+ mAsset.close();
+ }
+
+ public void timeGetString(int reps) {
+ for (int i = 0; i < reps; i++) {
+ mRes.getText(mTextId);
+ }
+ }
+
+ public void timeGetColor(int reps) {
+ for (int i = 0; i < reps; i++) {
+ mRes.getColor(mColorId, null);
+ }
+ }
+
+ public void timeGetInteger(int reps) {
+ for (int i = 0; i < reps; i++) {
+ mRes.getInteger(mIntegerId);
+ }
+ }
+
+ public void timeGetLayoutAndTraverse(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ final XmlResourceParser parser = mRes.getLayout(mLayoutId);
+ try {
+ while (parser.next() != XmlPullParser.END_DOCUMENT) {
+ // Walk the entire tree
+ }
+ } finally {
+ parser.close();
+ }
+ }
+ }
+}
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 3c10368..6903b7b 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -136,7 +136,9 @@
</intent-filter>
</activity>
- <activity android:name="android.widget.TextViewActivity" android:label="TextViewActivity">
+ <activity android:name="android.widget.TextViewActivity"
+ android:label="TextViewActivity"
+ android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
diff --git a/core/tests/coretests/src/android/animation/ValueAnimatorTests.java b/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
index 049da2d..998c72a 100644
--- a/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
+++ b/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
@@ -811,6 +811,65 @@
}
@SmallTest
+ public void testEndBeforeStart() throws Throwable {
+ // This test calls two animators that are not yet started. One animator has completed a
+ // previous run but hasn't started since then, the other one has never run. When end() is
+ // called on these two animators, we expected their animation listeners to receive both
+ // onAnimationStarted(Animator) and onAnimationEnded(Animator) callbacks, in that sequence.
+
+ a1.setStartDelay(20);
+
+ // First start a1's first run.
+ final MyListener normalEndingListener = new MyListener();
+ a1.addListener(normalEndingListener);
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ assertFalse(a1.isStarted());
+ assertFalse(normalEndingListener.startCalled);
+ assertFalse(normalEndingListener.endCalled);
+ // Start normally
+ a1.start();
+ }
+ });
+
+ Thread.sleep(a1.getTotalDuration() + POLL_INTERVAL);
+
+ // a1 should have finished by now.
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ // Call end() on both a1 and a2 without calling start()
+ final MyListener l1 = new MyListener();
+ a1.addListener(l1);
+ final MyListener l2 = new MyListener();
+ a2.addListener(l2);
+
+ assertFalse(a1.isStarted());
+ assertFalse(l1.startCalled);
+ assertFalse(l1.endCalled);
+ assertFalse(a2.isStarted());
+ assertFalse(l2.startCalled);
+ assertFalse(l1.endCalled);
+
+ a1.end();
+ a2.end();
+
+ // Check that both animators' listeners have received the animation callbacks.
+ assertTrue(l1.startCalled);
+ assertTrue(l1.endCalled);
+ assertFalse(a1.isStarted());
+ assertTrue(l1.endTime >= l1.startTime);
+
+ assertTrue(l2.startCalled);
+ assertTrue(l2.endCalled);
+ assertFalse(a2.isStarted());
+ assertTrue(l2.endTime >= l1.startTime);
+ }
+ });
+ }
+
+ @SmallTest
public void testZeroDuration() throws Throwable {
// Run two animators with zero duration, with one running forward and the other one
// backward. Check that the animations start and finish with the correct end fractions.
@@ -865,6 +924,47 @@
}
@SmallTest
+ public void testZeroScale() throws Throwable {
+ // Test whether animations would end properly when the scale is forced to be zero
+ float scale = ValueAnimator.getDurationScale();
+ ValueAnimator.setDurationScale(0f);
+
+ // Run two animators, one of which has a start delay, after setting the duration scale to 0
+ a1.setStartDelay(200);
+ final MyListener l1 = new MyListener();
+ final MyListener l2 = new MyListener();
+ a1.addListener(l1);
+ a2.addListener(l2);
+
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ assertFalse(l1.startCalled);
+ assertFalse(l2.startCalled);
+ assertFalse(l1.endCalled);
+ assertFalse(l2.endCalled);
+
+ a1.start();
+ a2.start();
+ }
+ });
+ Thread.sleep(POLL_INTERVAL);
+
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ assertTrue(l1.startCalled);
+ assertTrue(l2.startCalled);
+ assertTrue(l1.endCalled);
+ assertTrue(l2.endCalled);
+ }
+ });
+
+ // Restore duration scale
+ ValueAnimator.setDurationScale(scale);
+ }
+
+ @SmallTest
public void testReverse() throws Throwable {
// Prolong animators duration so that we can do multiple checks during their run
final ValueAnimator a3 = ValueAnimator.ofInt(0, 100);
diff --git a/core/tests/coretests/src/android/content/pm/AppsQueryHelperTests.java b/core/tests/coretests/src/android/content/pm/AppsQueryHelperTests.java
index 80181cf..4c9b00a 100644
--- a/core/tests/coretests/src/android/content/pm/AppsQueryHelperTests.java
+++ b/core/tests/coretests/src/android/content/pm/AppsQueryHelperTests.java
@@ -20,6 +20,7 @@
import android.content.Intent;
import android.os.UserHandle;
import android.test.AndroidTestCase;
+import android.view.inputmethod.InputMethodInfo;
import java.util.Arrays;
import java.util.HashSet;
@@ -77,6 +78,27 @@
assertEqualsIgnoreOrder(Arrays.asList("sys_app1", "sys_app3"), apps);
}
+ public void testQueryAppsDefaultIme() {
+ // Test query default system IMEs
+ List<String> apps = mAppsQueryHelper.queryApps(AppsQueryHelper.GET_DEFAULT_IMES,
+ true, UserHandle.of(UserHandle.myUserId()));
+ assertEqualsIgnoreOrder(Arrays.asList("sys_app1"), apps);
+
+ // Test query default IMEs
+ apps = mAppsQueryHelper.queryApps(AppsQueryHelper.GET_DEFAULT_IMES, false,
+ UserHandle.of(UserHandle.myUserId()));
+ assertEqualsIgnoreOrder(Arrays.asList("sys_app1", "app4"), apps);
+
+ // Test that GET_DEFAULT_IMES cannot be used with a user id different from current process
+ try {
+ mAppsQueryHelper.queryApps(AppsQueryHelper.GET_DEFAULT_IMES, false,
+ UserHandle.of(UserHandle.USER_NULL));
+ fail("queryApps must fail if wrong user was passed");
+ } catch (IllegalArgumentException e) {
+ // OK
+ }
+ }
+
private class AppsQueryHelperTestable extends AppsQueryHelper {
public AppsQueryHelperTestable(Context context) {
@@ -121,6 +143,21 @@
p1.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
return Arrays.asList(p1);
}
+
+ @Override
+ protected List<InputMethodInfo> getInputMethodList() {
+ final ResolveInfo sysApp1 = new ResolveInfo();
+ sysApp1.serviceInfo = new ServiceInfo();
+ sysApp1.serviceInfo.packageName = "sys_app1";
+ sysApp1.serviceInfo.name = "name";
+ InputMethodInfo imi1 = new InputMethodInfo(sysApp1, false, null, null, 0, true);
+ final ResolveInfo app4 = new ResolveInfo();
+ app4.serviceInfo = new ServiceInfo();
+ app4.serviceInfo.packageName = "app4";
+ app4.serviceInfo.name = "name";
+ InputMethodInfo imi2 = new InputMethodInfo(app4, false, null, null, 0, true);
+ return Arrays.asList(imi1, imi2);
+ }
}
private static void assertEqualsIgnoreOrder(List<String> expected, List<String> actual) {
diff --git a/core/tests/coretests/src/android/net/LinkPropertiesTest.java b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
index b6b4f4f..d5f6321 100644
--- a/core/tests/coretests/src/android/net/LinkPropertiesTest.java
+++ b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
@@ -561,9 +561,13 @@
assertTrue(v46lp.isProvisioned());
assertEquals(ProvisioningChange.STILL_PROVISIONED,
+ LinkProperties.compareProvisioning(v4lp, v46lp));
+ assertEquals(ProvisioningChange.STILL_PROVISIONED,
LinkProperties.compareProvisioning(v6lp, v46lp));
assertEquals(ProvisioningChange.LOST_PROVISIONING,
LinkProperties.compareProvisioning(v46lp, v6lp));
+ assertEquals(ProvisioningChange.LOST_PROVISIONING,
+ LinkProperties.compareProvisioning(v46lp, v4lp));
// Check that losing and gaining a secondary router does not change
// the provisioning status.
diff --git a/core/tests/coretests/src/android/net/SntpClientTest.java b/core/tests/coretests/src/android/net/SntpClientTest.java
new file mode 100644
index 0000000..8b8cf67
--- /dev/null
+++ b/core/tests/coretests/src/android/net/SntpClientTest.java
@@ -0,0 +1,222 @@
+/*
+ * 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 android.net;
+
+import android.net.SntpClient;
+import android.util.Log;
+import libcore.util.HexEncoding;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.SocketException;
+import java.util.Arrays;
+import junit.framework.TestCase;
+
+
+public class SntpClientTest extends TestCase {
+ private static final String TAG = "SntpClientTest";
+
+ private static final int ORIGINATE_TIME_OFFSET = 24;
+ private static final int TRANSMIT_TIME_OFFSET = 40;
+
+ private static final int NTP_MODE_SERVER = 4;
+ private static final int NTP_MODE_BROADCAST = 5;
+
+ // From tcpdump (admittedly, an NTPv4 packet):
+ //
+ // Server, Leap indicator: (0), Stratum 2 (secondary reference), poll 6 (64s), precision -20
+ // Root Delay: 0.005447, Root dispersion: 0.002716, Reference-ID: 221.253.71.41
+ // Reference Timestamp: 3653932102.507969856 (2015/10/15 14:08:22)
+ // Originator Timestamp: 3653932113.576327741 (2015/10/15 14:08:33)
+ // Receive Timestamp: 3653932113.581012725 (2015/10/15 14:08:33)
+ // Transmit Timestamp: 3653932113.581012725 (2015/10/15 14:08:33)
+ // Originator - Receive Timestamp: +0.004684958
+ // Originator - Transmit Timestamp: +0.004684958
+ private static final String WORKING_VERSION4 =
+ "240206ec" +
+ "00000165" +
+ "000000b2" +
+ "ddfd4729" +
+ "d9ca9446820a5000" +
+ "d9ca9451938a3771" +
+ "d9ca945194bd3fff" +
+ "d9ca945194bd4001";
+
+ private final SntpTestServer mServer = new SntpTestServer();
+ private final SntpClient mClient = new SntpClient();
+
+ public void testBasicWorkingSntpClientQuery() throws Exception {
+ mServer.setServerReply(HexEncoding.decode(WORKING_VERSION4.toCharArray(), false));
+ assertTrue(mClient.requestTime(mServer.getAddress(), mServer.getPort(), 500));
+ assertEquals(1, mServer.numRequestsReceived());
+ assertEquals(1, mServer.numRepliesSent());
+ }
+
+ public void testDnsResolutionFailure() throws Exception {
+ assertFalse(mClient.requestTime("ntp.server.doesnotexist.example", 5000));
+ }
+
+ public void testTimeoutFailure() throws Exception {
+ mServer.clearServerReply();
+ assertFalse(mClient.requestTime(mServer.getAddress(), mServer.getPort(), 500));
+ assertEquals(1, mServer.numRequestsReceived());
+ assertEquals(0, mServer.numRepliesSent());
+ }
+
+ public void testIgnoreLeapNoSync() throws Exception {
+ final byte[] reply = HexEncoding.decode(WORKING_VERSION4.toCharArray(), false);
+ reply[0] |= (byte) 0xc0;
+ mServer.setServerReply(reply);
+ assertFalse(mClient.requestTime(mServer.getAddress(), mServer.getPort(), 500));
+ assertEquals(1, mServer.numRequestsReceived());
+ assertEquals(1, mServer.numRepliesSent());
+ }
+
+ public void testAcceptOnlyServerAndBroadcastModes() throws Exception {
+ final byte[] reply = HexEncoding.decode(WORKING_VERSION4.toCharArray(), false);
+ for (int i = 0; i <= 7; i++) {
+ final String logMsg = "mode: " + i;
+ reply[0] &= (byte) 0xf8;
+ reply[0] |= (byte) i;
+ mServer.setServerReply(reply);
+ final boolean rval = mClient.requestTime(mServer.getAddress(), mServer.getPort(), 500);
+ switch (i) {
+ case NTP_MODE_SERVER:
+ case NTP_MODE_BROADCAST:
+ assertTrue(logMsg, rval);
+ break;
+ default:
+ assertFalse(logMsg, rval);
+ break;
+ }
+ assertEquals(logMsg, 1, mServer.numRequestsReceived());
+ assertEquals(logMsg, 1, mServer.numRepliesSent());
+ }
+ }
+
+ public void testAcceptableStrataOnly() throws Exception {
+ final int STRATUM_MIN = 1;
+ final int STRATUM_MAX = 15;
+
+ final byte[] reply = HexEncoding.decode(WORKING_VERSION4.toCharArray(), false);
+ for (int i = 0; i < 256; i++) {
+ final String logMsg = "stratum: " + i;
+ reply[1] = (byte) i;
+ mServer.setServerReply(reply);
+ final boolean rval = mClient.requestTime(mServer.getAddress(), mServer.getPort(), 500);
+ if (STRATUM_MIN <= i && i <= STRATUM_MAX) {
+ assertTrue(logMsg, rval);
+ } else {
+ assertFalse(logMsg, rval);
+ }
+ assertEquals(logMsg, 1, mServer.numRequestsReceived());
+ assertEquals(logMsg, 1, mServer.numRepliesSent());
+ }
+ }
+
+ public void testZeroTransmitTime() throws Exception {
+ final byte[] reply = HexEncoding.decode(WORKING_VERSION4.toCharArray(), false);
+ Arrays.fill(reply, TRANSMIT_TIME_OFFSET, TRANSMIT_TIME_OFFSET + 8, (byte) 0x00);
+ mServer.setServerReply(reply);
+ assertFalse(mClient.requestTime(mServer.getAddress(), mServer.getPort(), 500));
+ assertEquals(1, mServer.numRequestsReceived());
+ assertEquals(1, mServer.numRepliesSent());
+ }
+
+
+ private static class SntpTestServer {
+ private final Object mLock = new Object();
+ private final DatagramSocket mSocket;
+ private final InetAddress mAddress;
+ private final int mPort;
+ private byte[] mReply;
+ private int mRcvd;
+ private int mSent;
+ private Thread mListeningThread;
+
+ public SntpTestServer() {
+ mSocket = makeSocket();
+ mAddress = mSocket.getLocalAddress();
+ mPort = mSocket.getLocalPort();
+ Log.d(TAG, "testing server listening on (" + mAddress + ", " + mPort + ")");
+
+ mListeningThread = new Thread() {
+ public void run() {
+ while (true) {
+ byte[] buffer = new byte[512];
+ DatagramPacket ntpMsg = new DatagramPacket(buffer, buffer.length);
+ try {
+ mSocket.receive(ntpMsg);
+ } catch (IOException e) {
+ Log.e(TAG, "datagram receive error: " + e);
+ break;
+ }
+ synchronized (mLock) {
+ mRcvd++;
+ if (mReply == null) { continue; }
+ // Copy transmit timestamp into originate timestamp.
+ // TODO: bounds checking.
+ System.arraycopy(ntpMsg.getData(), TRANSMIT_TIME_OFFSET,
+ mReply, ORIGINATE_TIME_OFFSET, 8);
+ ntpMsg.setData(mReply);
+ ntpMsg.setLength(mReply.length);
+ try {
+ mSocket.send(ntpMsg);
+ } catch (IOException e) {
+ Log.e(TAG, "datagram send error: " + e);
+ break;
+ }
+ mSent++;
+ }
+ }
+ mSocket.close();
+ }
+ };
+ mListeningThread.start();
+ }
+
+ private DatagramSocket makeSocket() {
+ DatagramSocket socket;
+ try {
+ socket = new DatagramSocket(0, InetAddress.getLoopbackAddress());
+ } catch (SocketException e) {
+ Log.e(TAG, "Failed to create test server socket: " + e);
+ return null;
+ }
+ return socket;
+ }
+
+ public void clearServerReply() {
+ setServerReply(null);
+ }
+
+ public void setServerReply(byte[] reply) {
+ synchronized (mLock) {
+ mReply = reply;
+ mRcvd = 0;
+ mSent = 0;
+ }
+ }
+
+ public InetAddress getAddress() { return mAddress; }
+ public int getPort() { return mPort; }
+ public int numRequestsReceived() { synchronized (mLock) { return mRcvd; } }
+ public int numRepliesSent() { synchronized (mLock) { return mSent; } }
+ }
+}
diff --git a/core/tests/coretests/src/android/util/TimeUtilsTest.java b/core/tests/coretests/src/android/util/TimeUtilsTest.java
index 74c8e04..2370627 100644
--- a/core/tests/coretests/src/android/util/TimeUtilsTest.java
+++ b/core/tests/coretests/src/android/util/TimeUtilsTest.java
@@ -436,15 +436,17 @@
assertFormatDuration("+100ms", 100);
assertFormatDuration("+101ms", 101);
assertFormatDuration("+330ms", 330);
+ assertFormatDuration("+1s0ms", 1000);
assertFormatDuration("+1s330ms", 1330);
assertFormatDuration("+10s24ms", 10024);
+ assertFormatDuration("+1m0s30ms", 60030);
+ assertFormatDuration("+1h0m0s30ms", 3600030);
+ assertFormatDuration("+1d0h0m0s30ms", 86400030);
}
public void testFormatHugeDuration() {
- //assertFormatDuration("+15542d1h11m11s555ms", 1342833071555L);
- // TODO: improve formatDuration() API
- assertFormatDuration("+999d23h59m59s999ms", 1342833071555L);
- assertFormatDuration("-999d23h59m59s999ms", -1342833071555L);
+ assertFormatDuration("+15542d1h11m11s555ms", 1342833071555L);
+ assertFormatDuration("-15542d1h11m11s555ms", -1342833071555L);
}
private void assertFormatDuration(String expected, long duration) {
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
new file mode 100644
index 0000000..c5e2ae6
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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 android.widget;
+
+import static android.widget.espresso.TextViewActions.mouseDragOnText;
+import static android.widget.espresso.TextViewAssertions.hasSelection;
+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.typeTextIntoFocusedView;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+
+import com.android.frameworks.coretests.R;
+
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.SmallTest;
+
+/**
+ * Tests mouse interaction of the TextView widget from an Activity
+ */
+public class TextViewActivityMouseTest extends ActivityInstrumentationTestCase2<TextViewActivity>{
+
+ public TextViewActivityMouseTest() {
+ super(TextViewActivity.class);
+ }
+
+ @SmallTest
+ public void testSelectTextByDrag() throws Exception {
+ getActivity();
+
+ final String helloWorld = "Hello world!";
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
+ onView(withId(R.id.textview)).perform(
+ mouseDragOnText(helloWorld.indexOf("llo"), helloWorld.indexOf("ld!")));
+
+ onView(withId(R.id.textview)).check(hasSelection("llo wor"));
+ }
+
+ @SmallTest
+ public void testSelectTextByDrag_reverse() throws Exception {
+ getActivity();
+
+ final String helloWorld = "Hello world!";
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
+ onView(withId(R.id.textview)).perform(
+ mouseDragOnText( helloWorld.indexOf("ld!"), helloWorld.indexOf("llo")));
+
+ onView(withId(R.id.textview)).check(hasSelection("llo wor"));
+ }
+}
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 6a76a27..bb51570 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -36,7 +36,6 @@
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.SmallTest;
-import android.util.OrientationUtil;
import android.view.KeyEvent;
/**
@@ -44,16 +43,13 @@
*/
public class TextViewActivityTest extends ActivityInstrumentationTestCase2<TextViewActivity>{
- private OrientationUtil mOrientationUtil;
-
public TextViewActivityTest() {
super(TextViewActivity.class);
}
@Override
public void setUp() {
- mOrientationUtil = OrientationUtil.initializeAndStartActivityIfNotStarted(this);
- mOrientationUtil.setPortraitOrientation();
+ getActivity();
}
@SmallTest
diff --git a/core/tests/coretests/src/android/widget/espresso/DragOnTextViewActions.java b/core/tests/coretests/src/android/widget/espresso/DragOnTextViewActions.java
index a0cd848..9ff8e82 100644
--- a/core/tests/coretests/src/android/widget/espresso/DragOnTextViewActions.java
+++ b/core/tests/coretests/src/android/widget/espresso/DragOnTextViewActions.java
@@ -20,25 +20,22 @@
import static android.support.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
import static com.android.internal.util.Preconditions.checkNotNull;
import static org.hamcrest.Matchers.allOf;
-
import android.annotation.Nullable;
import android.os.SystemClock;
import android.support.test.espresso.UiController;
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.MotionEvents;
import android.support.test.espresso.action.PrecisionDescriber;
-import android.support.test.espresso.action.Press;
import android.support.test.espresso.action.Swiper;
-import android.support.test.espresso.action.Tap;
import android.support.test.espresso.util.HumanReadables;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.TextView;
+
import org.hamcrest.Matcher;
@@ -51,11 +48,48 @@
* <ul>
*/
public final class DragOnTextViewActions implements ViewAction {
+ public interface Dragger extends Swiper {
+ UiController wrapUiController(UiController uiController);
+ }
/**
* Executes different "drag on text" types to given positions.
*/
- public enum Drag implements Swiper {
+ public enum Drag implements Dragger {
+
+ /**
+ * Starts a drag with a mouse down.
+ */
+ MOUSE_DOWN {
+ private DownMotionPerformer downMotion = new DownMotionPerformer() {
+ @Override
+ public MotionEvent perform(
+ UiController uiController, float[] coordinates, float[] precision) {
+ MotionEvent downEvent = MotionEvents.sendDown(
+ uiController, coordinates, precision)
+ .down;
+ return downEvent;
+ }
+ };
+
+ @Override
+ public Status sendSwipe(
+ UiController uiController,
+ float[] startCoordinates, float[] endCoordinates, float[] precision) {
+ return sendLinearDrag(
+ uiController, downMotion, startCoordinates, endCoordinates, precision);
+ }
+
+ @Override
+ public String toString() {
+ return "mouse down and drag to select";
+ }
+
+ @Override
+ public UiController wrapUiController(UiController uiController) {
+ return new MouseUiController(uiController);
+ }
+ },
/**
* Starts a drag with a long-press.
@@ -197,6 +231,11 @@
return res;
}
+
+ @Override
+ public UiController wrapUiController(UiController uiController) {
+ return uiController;
+ }
}
/**
@@ -215,13 +254,13 @@
MotionEvent perform(UiController uiController, float[] coordinates, float[] precision);
}
- private final Swiper mDragger;
+ private final Dragger mDragger;
private final CoordinatesProvider mStartCoordinatesProvider;
private final CoordinatesProvider mEndCoordinatesProvider;
private final PrecisionDescriber mPrecisionDescriber;
public DragOnTextViewActions(
- Swiper dragger,
+ Dragger dragger,
CoordinatesProvider startCoordinatesProvider,
CoordinatesProvider endCoordinatesProvider,
PrecisionDescriber precisionDescriber) {
@@ -242,6 +281,8 @@
checkNotNull(uiController);
checkNotNull(view);
+ uiController = mDragger.wrapUiController(uiController);
+
float[] startCoordinates = mStartCoordinatesProvider.calculateCoordinates(view);
float[] endCoordinates = mEndCoordinatesProvider.calculateCoordinates(view);
float[] precision = mPrecisionDescriber.describePrecision();
diff --git a/core/tests/coretests/src/android/widget/espresso/MouseUiController.java b/core/tests/coretests/src/android/widget/espresso/MouseUiController.java
new file mode 100644
index 0000000..f1387f8
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/espresso/MouseUiController.java
@@ -0,0 +1,63 @@
+/*
+ * 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 android.widget.espresso;
+
+import android.support.test.espresso.InjectEventSecurityException;
+import android.support.test.espresso.UiController;
+import android.view.InputDevice;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+/**
+ * Class to wrap an UiController to overwrite source of motion events to SOURCE_MOUSE.
+ * Note that this doesn't change the tool type.
+ */
+public class MouseUiController implements UiController {
+ private final UiController mUiController;
+
+ public MouseUiController(UiController uiController) {
+ mUiController = uiController;
+ }
+
+ @Override
+ public boolean injectKeyEvent(KeyEvent event) throws InjectEventSecurityException {
+ return mUiController.injectKeyEvent(event);
+ }
+
+ @Override
+ public boolean injectMotionEvent(MotionEvent event) throws InjectEventSecurityException {
+ // Modify the event to mimic mouse primary button event.
+ event.setSource(InputDevice.SOURCE_MOUSE);
+ event.setButtonState(MotionEvent.BUTTON_PRIMARY);
+ return mUiController.injectMotionEvent(event);
+ }
+
+ @Override
+ public boolean injectString(String str) throws InjectEventSecurityException {
+ return mUiController.injectString(str);
+ }
+
+ @Override
+ public void loopMainThreadForAtLeast(long millisDelay) {
+ mUiController.loopMainThreadForAtLeast(millisDelay);
+ }
+
+ @Override
+ public void loopMainThreadUntilIdle() {
+ mUiController.loopMainThreadUntilIdle();
+ }
+}
diff --git a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
index 835b1b9..4f5a72b 100644
--- a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
+++ b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
@@ -124,6 +124,27 @@
}
/**
+ * Returns an action that click then drags by mouse on text from startIndex to endIndex on the
+ * TextView.<br>
+ * <br>
+ * View constraints:
+ * <ul>
+ * <li>must be a TextView displayed on screen
+ * <ul>
+ *
+ * @param startIndex The index of the TextView's text to start a drag from
+ * @param endIndex The index of the TextView's text to end the drag at
+ */
+ public static ViewAction mouseDragOnText(int startIndex, int endIndex) {
+ return actionWithAssertions(
+ new DragOnTextViewActions(
+ DragOnTextViewActions.Drag.MOUSE_DOWN,
+ new TextCoordinates(startIndex),
+ new TextCoordinates(endIndex),
+ Press.PINPOINT));
+ }
+
+ /**
* A provider of the x, y coordinates of the text at the specified index in a text view.
*/
private static final class TextCoordinates implements CoordinatesProvider {
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index f10ba96..2a10bdd 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -152,11 +152,11 @@
# key 130 "KEY_PROPS"
# key 131 "KEY_UNDO"
# key 132 "KEY_FRONT"
-# key 133 "KEY_COPY"
+key 133 COPY
# key 134 "KEY_OPEN"
-# key 135 "KEY_PASTE"
+key 135 PASTE
# key 136 "KEY_FIND"
-# key 137 "KEY_CUT"
+key 137 CUT
# key 138 "KEY_HELP"
key 139 MENU
key 140 CALCULATOR
diff --git a/data/keyboards/Vendor_0079_Product_0011.kl b/data/keyboards/Vendor_0079_Product_0011.kl
index 2ae2a01..32f8c82 100644
--- a/data/keyboards/Vendor_0079_Product_0011.kl
+++ b/data/keyboards/Vendor_0079_Product_0011.kl
@@ -12,10 +12,14 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# Classic NES Controller
+# Classic [S]NES Controller
+key 288 BUTTON_X
key 289 BUTTON_A
key 290 BUTTON_B
+key 291 BUTTON_Y
+key 292 BUTTON_L1
+key 293 BUTTON_R1
key 297 BUTTON_START
key 296 BUTTON_SELECT
diff --git a/docs/html/guide/topics/manifest/uses-feature-element.jd b/docs/html/guide/topics/manifest/uses-feature-element.jd
index c1ccef0..e22dc4a 100644
--- a/docs/html/guide/topics/manifest/uses-feature-element.jd
+++ b/docs/html/guide/topics/manifest/uses-feature-element.jd
@@ -562,10 +562,11 @@
<tr>
<td rowspan="6">Camera</td>
<td><code>android.hardware.camera</code></td>
- <td>The application uses the device's camera. If the device supports
- multiple cameras, the application uses the camera that facing
- away from the screen.</td>
- <td></td>
+ <td>The application uses the device's back-facing (main) camera.</td>
+ <td>Importantly, devices with only a front-facing camera will not list this
+ feature, so the <code>android.hardware.camera.any</code> feature should be
+ used instead if a camera facing any direction is acceptable for the
+ application.</td>
</tr>
<tr>
<td><code>android.hardware.camera.autofocus</code></td>
diff --git a/docs/html/sdk/installing/index.jd b/docs/html/sdk/installing/index.jd
index dc258db..0375a2f 100644
--- a/docs/html/sdk/installing/index.jd
+++ b/docs/html/sdk/installing/index.jd
@@ -42,9 +42,9 @@
JDK 6 or higher (the JRE alone is not sufficient)—JDK 7 is required when
developing for Android 5.0 and higher. To check if you
have JDK installed (and which version), open a terminal and type <code>javac -version</code>.
-If the JDK is not available or the version is lower than 6,
-<a href="http://www.oracle.com/technetwork/java/javase/downloads/index.html" class="external-link"
->go download JDK</a>.</p>
+If the JDK is not available or the version is lower than version 6, download the
+<a href="http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html" class="external-link"
+>Java SE Development Kit 7</a>.</p>
<div class="procedure-box">
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 11b4a9e..35182f9 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -1335,7 +1335,7 @@
return;
}
mLocales = new LocaleList(locale);
- nSetTextLocale(mNativePaint, locale.toString());
+ nSetTextLocales(mNativePaint, locale.toString());
}
/**
@@ -1372,8 +1372,7 @@
}
if (locales.equals(mLocales)) return;
mLocales = locales;
- // TODO: Pass the whole LocaleList to native code
- nSetTextLocale(mNativePaint, locales.getPrimary().toString());
+ nSetTextLocales(mNativePaint, locales.toLanguageTags());
}
/**
@@ -2715,8 +2714,8 @@
private static native void nSetTextAlign(long paintPtr,
int align);
- private static native void nSetTextLocale(long paintPtr,
- String locale);
+ private static native void nSetTextLocales(long paintPtr,
+ String locales);
private static native float nGetTextAdvances(long paintPtr, long typefacePtr,
char[] text, int index, int count, int contextIndex, int contextCount,
diff --git a/graphics/java/android/graphics/Point.java b/graphics/java/android/graphics/Point.java
index e0d8ccc..abcccbd 100644
--- a/graphics/java/android/graphics/Point.java
+++ b/graphics/java/android/graphics/Point.java
@@ -19,6 +19,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.io.PrintWriter;
+
/**
* Point holds two integer coordinates
@@ -95,6 +97,11 @@
return "Point(" + x + ", " + y + ")";
}
+ /** @hide */
+ public void printShortString(PrintWriter pw) {
+ pw.print("["); pw.print(x); pw.print(","); pw.print(y); pw.print("]");
+ }
+
/**
* Parcelable interface methods
*/
diff --git a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
index 4fc5ede..971a3a2 100644
--- a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
@@ -50,7 +50,7 @@
* Creates a new animated rotating drawable with no wrapped drawable.
*/
public AnimatedRotateDrawable() {
- this(new AnimatedRotateState(null), null);
+ this(new AnimatedRotateState(null, null), null);
}
@Override
@@ -126,58 +126,22 @@
@NonNull AttributeSet attrs, @Nullable Theme theme)
throws XmlPullParserException, IOException {
final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.AnimatedRotateDrawable);
- super.inflateWithAttributes(r, parser, a, R.styleable.AnimatedRotateDrawable_visible);
+
+ // Inflation will advance the XmlPullParser and AttributeSet.
+ super.inflate(r, parser, attrs, theme);
updateStateFromTypedArray(a);
- inflateChildDrawable(r, parser, attrs, theme);
verifyRequiredAttributes(a);
a.recycle();
updateLocalState();
}
- private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException {
- // If we're not waiting on a theme, verify required attributes.
- if (getDrawable() == null && (mState.mThemeAttrs == null
- || mState.mThemeAttrs[R.styleable.AnimatedRotateDrawable_drawable] == 0)) {
- throw new XmlPullParserException(a.getPositionDescription()
- + ": <animated-rotate> tag requires a 'drawable' attribute or "
- + "child tag defining a drawable");
- }
- }
-
@Override
- void updateStateFromTypedArray(TypedArray a) {
- super.updateStateFromTypedArray(a);
+ public void applyTheme(@NonNull Theme t) {
+ super.applyTheme(t);
final AnimatedRotateState state = mState;
-
- if (a.hasValue(R.styleable.AnimatedRotateDrawable_pivotX)) {
- final TypedValue tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotX);
- state.mPivotXRel = tv.type == TypedValue.TYPE_FRACTION;
- state.mPivotX = state.mPivotXRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat();
- }
-
- if (a.hasValue(R.styleable.AnimatedRotateDrawable_pivotY)) {
- final TypedValue tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotY);
- state.mPivotYRel = tv.type == TypedValue.TYPE_FRACTION;
- state.mPivotY = state.mPivotYRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat();
- }
-
- setFramesCount(a.getInt(
- R.styleable.AnimatedRotateDrawable_framesCount, state.mFramesCount));
- setFramesDuration(a.getInt(
- R.styleable.AnimatedRotateDrawable_frameDuration, state.mFrameDuration));
-
- final Drawable dr = a.getDrawable(R.styleable.AnimatedRotateDrawable_drawable);
- if (dr != null) {
- setDrawable(dr);
- }
- }
-
- @Override
- public void applyTheme(@Nullable Theme t) {
- final AnimatedRotateState state = mState;
if (state == null) {
return;
}
@@ -195,13 +159,49 @@
}
}
- // The drawable may have changed as a result of applying the theme, so
- // apply the theme to the wrapped drawable last.
- super.applyTheme(t);
-
updateLocalState();
}
+ private void verifyRequiredAttributes(@NonNull TypedArray a) throws XmlPullParserException {
+ // If we're not waiting on a theme, verify required attributes.
+ if (getDrawable() == null && (mState.mThemeAttrs == null
+ || mState.mThemeAttrs[R.styleable.AnimatedRotateDrawable_drawable] == 0)) {
+ throw new XmlPullParserException(a.getPositionDescription()
+ + ": <animated-rotate> tag requires a 'drawable' attribute or "
+ + "child tag defining a drawable");
+ }
+ }
+
+ private void updateStateFromTypedArray(@NonNull TypedArray a) {
+ final AnimatedRotateState state = mState;
+ if (state == null) {
+ return;
+ }
+
+ // Account for any configuration changes.
+ state.mChangingConfigurations |= a.getChangingConfigurations();
+
+ // Extract the theme attributes, if any.
+ state.mThemeAttrs = a.extractThemeAttrs();
+
+ if (a.hasValue(R.styleable.AnimatedRotateDrawable_pivotX)) {
+ final TypedValue tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotX);
+ state.mPivotXRel = tv.type == TypedValue.TYPE_FRACTION;
+ state.mPivotX = state.mPivotXRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat();
+ }
+
+ if (a.hasValue(R.styleable.AnimatedRotateDrawable_pivotY)) {
+ final TypedValue tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotY);
+ state.mPivotYRel = tv.type == TypedValue.TYPE_FRACTION;
+ state.mPivotY = state.mPivotYRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat();
+ }
+
+ setFramesCount(a.getInt(
+ R.styleable.AnimatedRotateDrawable_framesCount, state.mFramesCount));
+ setFramesDuration(a.getInt(
+ R.styleable.AnimatedRotateDrawable_frameDuration, state.mFrameDuration));
+ }
+
public void setFramesCount(int framesCount) {
mState.mFramesCount = framesCount;
mIncrement = 360.0f / mState.mFramesCount;
@@ -211,7 +211,15 @@
mState.mFrameDuration = framesDuration;
}
+ @Override
+ DrawableWrapperState mutateConstantState() {
+ mState = new AnimatedRotateState(mState, null);
+ return mState;
+ }
+
static final class AnimatedRotateState extends DrawableWrapper.DrawableWrapperState {
+ private int[] mThemeAttrs;
+
boolean mPivotXRel = false;
float mPivotX = 0;
boolean mPivotYRel = false;
@@ -219,8 +227,8 @@
int mFrameDuration = 150;
int mFramesCount = 12;
- public AnimatedRotateState(AnimatedRotateState orig) {
- super(orig);
+ public AnimatedRotateState(AnimatedRotateState orig, Resources res) {
+ super(orig, res);
if (orig != null) {
mPivotXRel = orig.mPivotXRel;
diff --git a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
index e235a99..3ed6a78 100644
--- a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
@@ -379,6 +379,7 @@
r, theme, attrs, R.styleable.AnimatedStateListDrawable);
super.inflateWithAttributes(r, parser, a, R.styleable.AnimatedStateListDrawable_visible);
updateStateFromTypedArray(a);
+ updateDensity(r);
a.recycle();
inflateChildElements(r, parser, attrs, theme);
diff --git a/graphics/java/android/graphics/drawable/AnimationDrawable.java b/graphics/java/android/graphics/drawable/AnimationDrawable.java
index 521c74b..6bf3afd 100644
--- a/graphics/java/android/graphics/drawable/AnimationDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimationDrawable.java
@@ -291,6 +291,7 @@
final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.AnimationDrawable);
super.inflateWithAttributes(r, parser, a, R.styleable.AnimationDrawable_visible);
updateStateFromTypedArray(a);
+ updateDensity(r);
a.recycle();
inflateChildElements(r, parser, attrs, theme);
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index 4f56965..4d2037b 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -812,8 +812,7 @@
setTileModeY(parseTileMode(tileModeY));
}
- final int densityDpi = r.getDisplayMetrics().densityDpi;
- state.mTargetDensity = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
+ state.mTargetDensity = Drawable.resolveDensity(r, 0);
}
@Override
@@ -975,13 +974,7 @@
* after inflating or applying a theme.
*/
private void updateLocalState(Resources res) {
- if (res != null) {
- final int densityDpi = res.getDisplayMetrics().densityDpi;
- mTargetDensity = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
- } else {
- mTargetDensity = mBitmapState.mTargetDensity;
- }
-
+ mTargetDensity = resolveDensity(res, mBitmapState.mTargetDensity);
mTintFilter = updateTintFilter(mTintFilter, mBitmapState.mTint, mBitmapState.mTintMode);
computeBitmapSize();
}
diff --git a/graphics/java/android/graphics/drawable/ClipDrawable.java b/graphics/java/android/graphics/drawable/ClipDrawable.java
index 31fccd0..cdd336d 100644
--- a/graphics/java/android/graphics/drawable/ClipDrawable.java
+++ b/graphics/java/android/graphics/drawable/ClipDrawable.java
@@ -21,6 +21,8 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.Resources.Theme;
@@ -59,7 +61,7 @@
private ClipState mState;
ClipDrawable() {
- this(new ClipState(null), null);
+ this(new ClipState(null, null), null);
}
/**
@@ -72,7 +74,7 @@
* {@link #VERTICAL}
*/
public ClipDrawable(Drawable drawable, int gravity, int orientation) {
- this(new ClipState(null), null);
+ this(new ClipState(null, null), null);
mState.mGravity = gravity;
mState.mOrientation = orientation;
@@ -81,46 +83,24 @@
}
@Override
- public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
+ public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs, @Nullable Theme theme)
throws XmlPullParserException, IOException {
+ final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.ClipDrawable);
+
+ // Inflation will advance the XmlPullParser and AttributeSet.
super.inflate(r, parser, attrs, theme);
- final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.ClipDrawable);
updateStateFromTypedArray(a);
- inflateChildDrawable(r, parser, attrs, theme);
verifyRequiredAttributes(a);
a.recycle();
}
- private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException {
- // If we're not waiting on a theme, verify required attributes.
- if (getDrawable() == null && (mState.mThemeAttrs == null
- || mState.mThemeAttrs[R.styleable.ClipDrawable_drawable] == 0)) {
- throw new XmlPullParserException(a.getPositionDescription()
- + ": <clip> tag requires a 'drawable' attribute or "
- + "child tag defining a drawable");
- }
- }
-
@Override
- void updateStateFromTypedArray(TypedArray a) {
- super.updateStateFromTypedArray(a);
+ public void applyTheme(@NonNull Theme t) {
+ super.applyTheme(t);
final ClipState state = mState;
- state.mOrientation = a.getInt(
- R.styleable.ClipDrawable_clipOrientation, state.mOrientation);
- state.mGravity = a.getInt(
- R.styleable.ClipDrawable_gravity, state.mGravity);
-
- final Drawable dr = a.getDrawable(R.styleable.ClipDrawable_drawable);
- if (dr != null) {
- setDrawable(dr);
- }
- }
-
- @Override
- public void applyTheme(Theme t) {
- final ClipState state = mState;
if (state == null) {
return;
}
@@ -136,10 +116,34 @@
a.recycle();
}
}
+ }
- // The drawable may have changed as a result of applying the theme, so
- // apply the theme to the wrapped drawable last.
- super.applyTheme(t);
+ private void verifyRequiredAttributes(@NonNull TypedArray a) throws XmlPullParserException {
+ // If we're not waiting on a theme, verify required attributes.
+ if (getDrawable() == null && (mState.mThemeAttrs == null
+ || mState.mThemeAttrs[R.styleable.ClipDrawable_drawable] == 0)) {
+ throw new XmlPullParserException(a.getPositionDescription()
+ + ": <clip> tag requires a 'drawable' attribute or "
+ + "child tag defining a drawable");
+ }
+ }
+
+ private void updateStateFromTypedArray(@NonNull TypedArray a) {
+ final ClipState state = mState;
+ if (state == null) {
+ return;
+ }
+
+ // Account for any configuration changes.
+ state.mChangingConfigurations |= a.getChangingConfigurations();
+
+ // Extract the theme attributes, if any.
+ state.mThemeAttrs = a.extractThemeAttrs();
+
+ state.mOrientation = a.getInt(
+ R.styleable.ClipDrawable_clipOrientation, state.mOrientation);
+ state.mGravity = a.getInt(
+ R.styleable.ClipDrawable_gravity, state.mGravity);
}
@Override
@@ -200,12 +204,20 @@
}
}
+ @Override
+ DrawableWrapperState mutateConstantState() {
+ mState = new ClipState(mState, null);
+ return mState;
+ }
+
static final class ClipState extends DrawableWrapper.DrawableWrapperState {
+ private int[] mThemeAttrs;
+
int mOrientation = HORIZONTAL;
int mGravity = Gravity.LEFT;
- ClipState(ClipState orig) {
- super(orig);
+ ClipState(ClipState orig, Resources res) {
+ super(orig, res);
if (orig != null) {
mOrientation = orig.mOrientation;
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index b95c183..39d13df 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -55,6 +55,8 @@
import java.util.Arrays;
import java.util.Collection;
+import com.android.internal.R;
+
/**
* A Drawable is a general abstraction for "something that can be drawn." Most
* often you will deal with Drawable as the type of resource retrieved for
@@ -791,8 +793,10 @@
/**
* Applies the specified theme to this Drawable and its children.
+ *
+ * @param t the theme to apply
*/
- public void applyTheme(@SuppressWarnings("unused") Theme t) {
+ public void applyTheme(@NonNull @SuppressWarnings("unused") Theme t) {
}
public boolean canApplyTheme() {
@@ -1075,8 +1079,7 @@
// to the compatibility density only to have them scaled back up when
// drawn to the screen.
if (opts == null) opts = new BitmapFactory.Options();
- opts.inScreenDensity = res != null
- ? res.getDisplayMetrics().noncompatDensityDpi : DisplayMetrics.DENSITY_DEVICE;
+ opts.inScreenDensity = Drawable.resolveDensity(res, 0);
Bitmap bm = BitmapFactory.decodeResourceStream(res, value, is, pad, opts);
if (bm != null) {
byte[] np = bm.getNinePatchChunk();
@@ -1177,8 +1180,8 @@
*
* @see #inflate(Resources, XmlPullParser, AttributeSet, Theme)
*/
- public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
- throws XmlPullParserException, IOException {
+ public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs) throws XmlPullParserException, IOException {
inflate(r, parser, attrs, null);
}
@@ -1192,17 +1195,11 @@
* @throws XmlPullParserException
* @throws IOException
*/
- public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
+ public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs, @Nullable Theme theme)
throws XmlPullParserException, IOException {
- final TypedArray a;
- if (theme != null) {
- a = theme.obtainStyledAttributes(
- attrs, com.android.internal.R.styleable.Drawable, 0, 0);
- } else {
- a = r.obtainAttributes(attrs, com.android.internal.R.styleable.Drawable);
- }
-
- inflateWithAttributes(r, parser, a, com.android.internal.R.styleable.Drawable_visible);
+ final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.Drawable);
+ mVisible = a.getBoolean(R.styleable.Drawable_visible, mVisible);
a.recycle();
}
@@ -1212,8 +1209,8 @@
* @throws XmlPullParserException
* @throws IOException
*/
- void inflateWithAttributes(Resources r, XmlPullParser parser, TypedArray attrs, int visibleAttr)
- throws XmlPullParserException, IOException {
+ void inflateWithAttributes(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull TypedArray attrs, int visibleAttr) throws XmlPullParserException, IOException {
mVisible = attrs.getBoolean(visibleAttr, mVisible);
}
@@ -1340,6 +1337,65 @@
}
/**
+ * Scales a floating-point pixel value from the source density to the
+ * target density.
+ *
+ * @param pixels the pixel value for use in source density
+ * @param sourceDensity the source density
+ * @param targetDensity the target density
+ * @return the scaled pixel value for use in target density
+ */
+ static float scaleFromDensity(float pixels, int sourceDensity, int targetDensity) {
+ return pixels * targetDensity / sourceDensity;
+ }
+
+ /**
+ * Scales a pixel value from the source density to the target density,
+ * optionally handling the resulting pixel value as a size rather than an
+ * offset.
+ * <p>
+ * A size conversion involves rounding the base value and ensuring that
+ * a non-zero base value is at least one pixel in size.
+ * <p>
+ * An offset conversion involves simply truncating the base value to an
+ * integer.
+ *
+ * @param pixels the pixel value for use in source density
+ * @param sourceDensity the source density
+ * @param targetDensity the target density
+ * @param isSize {@code true} to handle the resulting scaled value as a
+ * size, or {@code false} to handle it as an offset
+ * @return the scaled pixel value for use in target density
+ */
+ static int scaleFromDensity(
+ int pixels, int sourceDensity, int targetDensity, boolean isSize) {
+ if (pixels == 0 || sourceDensity == targetDensity) {
+ return pixels;
+ }
+
+ final float result = pixels * targetDensity / (float) sourceDensity;
+ if (!isSize) {
+ return (int) result;
+ }
+
+ final int rounded = Math.round(result);
+ if (rounded != 0) {
+ return rounded;
+ } else if (pixels == 0) {
+ return 0;
+ } else if (pixels > 0) {
+ return 1;
+ } else {
+ return -1;
+ }
+ }
+
+ static int resolveDensity(@NonNull Resources r, int parentDensity) {
+ final int densityDpi = r == null ? parentDensity : r.getDisplayMetrics().densityDpi;
+ return densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
+ }
+
+ /**
* Parses a {@link android.graphics.PorterDuff.Mode} from a tintMode
* attribute's enum value.
*
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index 1f7d996..d05c66a 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -29,6 +29,7 @@
import android.graphics.Rect;
import android.graphics.PorterDuff.Mode;
import android.os.SystemClock;
+import android.util.DisplayMetrics;
import android.util.LayoutDirection;
import android.util.SparseArray;
import android.view.View;
@@ -143,7 +144,7 @@
mAlpha = alpha;
if (mCurrDrawable != null) {
if (mEnterAnimationEnd == 0) {
- mCurrDrawable.mutate().setAlpha(alpha);
+ mCurrDrawable.setAlpha(alpha);
} else {
animate(false);
}
@@ -161,7 +162,7 @@
if (mDrawableContainerState.mDither != dither) {
mDrawableContainerState.mDither = dither;
if (mCurrDrawable != null) {
- mCurrDrawable.mutate().setDither(mDrawableContainerState.mDither);
+ mCurrDrawable.setDither(mDrawableContainerState.mDither);
}
}
}
@@ -174,7 +175,7 @@
mDrawableContainerState.mColorFilter = colorFilter;
if (mCurrDrawable != null) {
- mCurrDrawable.mutate().setColorFilter(colorFilter);
+ mCurrDrawable.setColorFilter(colorFilter);
}
}
}
@@ -187,7 +188,7 @@
mDrawableContainerState.mTintList = tint;
if (mCurrDrawable != null) {
- mCurrDrawable.mutate().setTintList(tint);
+ mCurrDrawable.setTintList(tint);
}
}
}
@@ -200,7 +201,7 @@
mDrawableContainerState.mTintMode = tintMode;
if (mCurrDrawable != null) {
- mCurrDrawable.mutate().setTintMode(tintMode);
+ mCurrDrawable.setTintMode(tintMode);
}
}
}
@@ -243,7 +244,7 @@
if (mDrawableContainerState.mAutoMirrored != mirrored) {
mDrawableContainerState.mAutoMirrored = mirrored;
if (mCurrDrawable != null) {
- mCurrDrawable.mutate().setAutoMirrored(mDrawableContainerState.mAutoMirrored);
+ mCurrDrawable.setAutoMirrored(mDrawableContainerState.mAutoMirrored);
}
}
}
@@ -265,7 +266,7 @@
if (mCurrDrawable != null) {
mCurrDrawable.jumpToCurrentState();
if (mHasAlpha) {
- mCurrDrawable.mutate().setAlpha(mAlpha);
+ mCurrDrawable.setAlpha(mAlpha);
}
}
if (mExitAnimationEnd != 0) {
@@ -498,8 +499,6 @@
* @param d The drawable to initialize.
*/
private void initializeDrawableForDisplay(Drawable d) {
- d.mutate();
-
if (mDrawableContainerState.mEnterFadeDuration <= 0 && mHasAlpha) {
d.setAlpha(mAlpha);
}
@@ -539,13 +538,12 @@
if (mCurrDrawable != null) {
if (mEnterAnimationEnd != 0) {
if (mEnterAnimationEnd <= now) {
- mCurrDrawable.mutate().setAlpha(mAlpha);
+ mCurrDrawable.setAlpha(mAlpha);
mEnterAnimationEnd = 0;
} else {
int animAlpha = (int)((mEnterAnimationEnd-now)*255)
/ mDrawableContainerState.mEnterFadeDuration;
- if (DEBUG) android.util.Log.i(TAG, toString() + " cur alpha " + animAlpha);
- mCurrDrawable.mutate().setAlpha(((255-animAlpha)*mAlpha)/255);
+ mCurrDrawable.setAlpha(((255-animAlpha)*mAlpha)/255);
animating = true;
}
}
@@ -562,8 +560,7 @@
} else {
int animAlpha = (int)((mExitAnimationEnd-now)*255)
/ mDrawableContainerState.mExitFadeDuration;
- if (DEBUG) android.util.Log.i(TAG, toString() + " last alpha " + animAlpha);
- mLastDrawable.mutate().setAlpha((animAlpha*mAlpha)/255);
+ mLastDrawable.setAlpha((animAlpha*mAlpha)/255);
animating = true;
}
}
@@ -581,6 +578,17 @@
return mCurrDrawable;
}
+ /**
+ * Updates the source density based on the resources used to inflate
+ * density-dependent values. Implementing classes should call this method
+ * during inflation.
+ *
+ * @param res the resources used to inflate density-dependent values
+ */
+ final void updateDensity(Resources res) {
+ mDrawableContainerState.updateDensity(res);
+ }
+
@Override
public void applyTheme(Theme theme) {
mDrawableContainerState.applyTheme(theme);
@@ -638,22 +646,22 @@
*/
public abstract static class DrawableContainerState extends ConstantState {
final DrawableContainer mOwner;
- final Resources mRes;
- SparseArray<ConstantStateFuture> mDrawableFutures;
-
+ Resources mSourceRes;
+ int mDensity = DisplayMetrics.DENSITY_DEFAULT;
int mChangingConfigurations;
int mChildrenChangingConfigurations;
+ SparseArray<ConstantState> mDrawableFutures;
Drawable[] mDrawables;
int mNumChildren;
boolean mVariablePadding = false;
- boolean mPaddingChecked;
+ boolean mCheckedPadding;
Rect mConstantPadding;
boolean mConstantSize = false;
- boolean mComputedConstantSize;
+ boolean mCheckedConstantSize;
int mConstantWidth;
int mConstantHeight;
int mConstantMinimumWidth;
@@ -689,7 +697,8 @@
DrawableContainerState(DrawableContainerState orig, DrawableContainer owner,
Resources res) {
mOwner = owner;
- mRes = res != null ? res : orig != null ? orig.mRes : null;
+ mSourceRes = res != null ? res : (orig != null ? orig.mSourceRes : null);
+ mDensity = Drawable.resolveDensity(res, orig != null ? orig.mDensity : 0);
if (orig != null) {
mChangingConfigurations = orig.mChangingConfigurations;
@@ -713,21 +722,30 @@
mHasTintList = orig.mHasTintList;
mHasTintMode = orig.mHasTintMode;
- // Cloning the following values may require creating futures.
- mConstantPadding = orig.getConstantPadding();
- mPaddingChecked = true;
+ if (orig.mDensity == mDensity) {
+ if (orig.mCheckedPadding) {
+ mConstantPadding = new Rect(orig.mConstantPadding);
+ mCheckedPadding = true;
+ }
- mConstantWidth = orig.getConstantWidth();
- mConstantHeight = orig.getConstantHeight();
- mConstantMinimumWidth = orig.getConstantMinimumWidth();
- mConstantMinimumHeight = orig.getConstantMinimumHeight();
- mComputedConstantSize = true;
+ if (orig.mCheckedConstantSize) {
+ mConstantWidth = orig.mConstantWidth;
+ mConstantHeight = orig.mConstantHeight;
+ mConstantMinimumWidth = orig.mConstantMinimumWidth;
+ mConstantMinimumHeight = orig.mConstantMinimumHeight;
+ mCheckedConstantSize = true;
+ }
+ }
- mOpacity = orig.getOpacity();
- mCheckedOpacity = true;
+ if (orig.mCheckedOpacity) {
+ mOpacity = orig.mOpacity;
+ mCheckedOpacity = true;
+ }
- mStateful = orig.isStateful();
- mCheckedStateful = true;
+ if (orig.mCheckedStateful) {
+ mStateful = orig.mStateful;
+ mCheckedStateful = true;
+ }
// Postpone cloning children and futures until we're absolutely
// sure that we're done computing values for the original state.
@@ -735,7 +753,7 @@
mDrawables = new Drawable[origDr.length];
mNumChildren = orig.mNumChildren;
- final SparseArray<ConstantStateFuture> origDf = orig.mDrawableFutures;
+ final SparseArray<ConstantState> origDf = orig.mDrawableFutures;
if (origDf != null) {
mDrawableFutures = origDf.clone();
} else {
@@ -748,8 +766,9 @@
final int N = mNumChildren;
for (int i = 0; i < N; i++) {
if (origDr[i] != null) {
- if (origDr[i].getConstantState() != null) {
- mDrawableFutures.put(i, new ConstantStateFuture(origDr[i]));
+ final ConstantState cs = origDr[i].getConstantState();
+ if (cs != null) {
+ mDrawableFutures.put(i, cs);
} else {
mDrawables[i] = origDr[i];
}
@@ -783,8 +802,8 @@
mCheckedOpacity = false;
mConstantPadding = null;
- mPaddingChecked = false;
- mComputedConstantSize = false;
+ mCheckedPadding = false;
+ mCheckedConstantSize = false;
return pos;
}
@@ -793,18 +812,26 @@
return mDrawables.length;
}
- private final void createAllFutures() {
+ private void createAllFutures() {
if (mDrawableFutures != null) {
final int futureCount = mDrawableFutures.size();
for (int keyIndex = 0; keyIndex < futureCount; keyIndex++) {
final int index = mDrawableFutures.keyAt(keyIndex);
- mDrawables[index] = mDrawableFutures.valueAt(keyIndex).get(this);
+ final ConstantState cs = mDrawableFutures.valueAt(keyIndex);
+ mDrawables[index] = prepareDrawable(cs.newDrawable(mSourceRes));
}
mDrawableFutures = null;
}
}
+ private Drawable prepareDrawable(Drawable child) {
+ child.setLayoutDirection(mLayoutDirection);
+ child.setCallback(mOwner);
+ child = child.mutate();
+ return child;
+ }
+
public final int getChildCount() {
return mNumChildren;
}
@@ -829,7 +856,8 @@
if (mDrawableFutures != null) {
final int keyIndex = mDrawableFutures.indexOfKey(index);
if (keyIndex >= 0) {
- final Drawable prepared = mDrawableFutures.valueAt(keyIndex).get(this);
+ final ConstantState cs = mDrawableFutures.valueAt(keyIndex);
+ final Drawable prepared = prepareDrawable(cs.newDrawable(mSourceRes));
mDrawables[index] = prepared;
mDrawableFutures.removeAt(keyIndex);
if (mDrawableFutures.size() == 0) {
@@ -863,6 +891,29 @@
return changed;
}
+ /**
+ * Updates the source density based on the resources used to inflate
+ * density-dependent values.
+ *
+ * @param res the resources used to inflate density-dependent values
+ */
+ final void updateDensity(Resources res) {
+ if (mSourceRes != null) {
+ mSourceRes = res;
+ }
+
+ // The density may have changed since the last update (if any). Any
+ // dimension-type attributes will need their default values scaled.
+ final int targetDensity = Drawable.resolveDensity(res, mDensity);
+ final int sourceDensity = mDensity;
+ mDensity = targetDensity;
+
+ if (sourceDensity != targetDensity) {
+ mCheckedConstantSize = false;
+ mCheckedPadding = false;
+ }
+ }
+
final void applyTheme(Theme theme) {
if (theme != null) {
createAllFutures();
@@ -877,6 +928,8 @@
mChildrenChangingConfigurations |= drawables[i].getChangingConfigurations();
}
}
+
+ updateDensity(theme.getResources());
}
}
@@ -891,7 +944,7 @@
return true;
}
} else {
- final ConstantStateFuture future = mDrawableFutures.get(i);
+ final ConstantState future = mDrawableFutures.get(i);
if (future != null && future.canApplyTheme()) {
return true;
}
@@ -941,7 +994,7 @@
return null;
}
- if ((mConstantPadding != null) || mPaddingChecked) {
+ if ((mConstantPadding != null) || mCheckedPadding) {
return mConstantPadding;
}
@@ -961,7 +1014,7 @@
}
}
- mPaddingChecked = true;
+ mCheckedPadding = true;
return (mConstantPadding = r);
}
@@ -974,7 +1027,7 @@
}
public final int getConstantWidth() {
- if (!mComputedConstantSize) {
+ if (!mCheckedConstantSize) {
computeConstantSize();
}
@@ -982,7 +1035,7 @@
}
public final int getConstantHeight() {
- if (!mComputedConstantSize) {
+ if (!mCheckedConstantSize) {
computeConstantSize();
}
@@ -990,7 +1043,7 @@
}
public final int getConstantMinimumWidth() {
- if (!mComputedConstantSize) {
+ if (!mCheckedConstantSize) {
computeConstantSize();
}
@@ -998,7 +1051,7 @@
}
public final int getConstantMinimumHeight() {
- if (!mComputedConstantSize) {
+ if (!mCheckedConstantSize) {
computeConstantSize();
}
@@ -1006,7 +1059,7 @@
}
protected void computeConstantSize() {
- mComputedConstantSize = true;
+ mCheckedConstantSize = true;
createAllFutures();
@@ -1126,49 +1179,6 @@
}
return pixelCount;
}
-
- /**
- * Class capable of cloning a Drawable from another Drawable's
- * ConstantState.
- */
- private static class ConstantStateFuture {
- private final ConstantState mConstantState;
-
- private ConstantStateFuture(Drawable source) {
- mConstantState = source.getConstantState();
- }
-
- /**
- * Obtains and prepares the Drawable represented by this future.
- *
- * @param state the container into which this future will be placed
- * @return a prepared Drawable
- */
- public Drawable get(DrawableContainerState state) {
- final Drawable result;
- if (state.mRes == null) {
- result = mConstantState.newDrawable();
- } else {
- result = mConstantState.newDrawable(state.mRes);
- }
- result.setLayoutDirection(state.mLayoutDirection);
- result.setCallback(state.mOwner);
-
- if (state.mMutated) {
- result.mutate();
- }
-
- return result;
- }
-
- /**
- * Whether the constant state wrapped by this future can apply a
- * theme.
- */
- public boolean canApplyTheme() {
- return mConstantState.canApplyTheme();
- }
- }
}
protected void setConstantState(DrawableContainerState state) {
diff --git a/graphics/java/android/graphics/drawable/DrawableWrapper.java b/graphics/java/android/graphics/drawable/DrawableWrapper.java
index 9185e1a..c427870 100644
--- a/graphics/java/android/graphics/drawable/DrawableWrapper.java
+++ b/graphics/java/android/graphics/drawable/DrawableWrapper.java
@@ -16,6 +16,8 @@
package android.graphics.drawable;
+import com.android.internal.R;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -23,6 +25,7 @@
import android.annotation.Nullable;
import android.content.res.ColorStateList;
import android.content.res.Resources;
+import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -33,6 +36,7 @@
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.util.AttributeSet;
+import android.util.DisplayMetrics;
import android.view.View;
import java.io.IOException;
@@ -112,7 +116,66 @@
return mDrawable;
}
- void updateStateFromTypedArray(TypedArray a) {
+ @Override
+ public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs, @Nullable Theme theme)
+ throws XmlPullParserException, IOException {
+ super.inflate(r, parser, attrs, theme);
+
+ final DrawableWrapperState state = mState;
+ if (state == null) {
+ return;
+ }
+
+ // The density may have changed since the last update. This will
+ // apply scaling to any existing constant state properties.
+ final int densityDpi = r.getDisplayMetrics().densityDpi;
+ final int targetDensity = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
+ state.setDensity(targetDensity);
+
+ final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.DrawableWrapper);
+ updateStateFromTypedArray(a);
+ a.recycle();
+
+ inflateChildDrawable(r, parser, attrs, theme);
+ }
+
+ @Override
+ public void applyTheme(@NonNull Theme t) {
+ super.applyTheme(t);
+
+ // If we load the drawable later as part of updating from the typed
+ // array, it will already be themed correctly. So, we can theme the
+ // local drawable first.
+ if (mDrawable != null && mDrawable.canApplyTheme()) {
+ mDrawable.applyTheme(t);
+ }
+
+ final DrawableWrapperState state = mState;
+ if (state == null) {
+ return;
+ }
+
+ final int densityDpi = t.getResources().getDisplayMetrics().densityDpi;
+ final int density = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
+ state.setDensity(density);
+
+ if (state.mThemeAttrs != null) {
+ final TypedArray a = t.resolveAttributes(
+ state.mThemeAttrs, R.styleable.DrawableWrapper);
+ updateStateFromTypedArray(a);
+ a.recycle();
+ }
+ }
+
+ /**
+ * Updates constant state properties from the provided typed array.
+ * <p>
+ * Implementing subclasses should call through to the super method first.
+ *
+ * @param a the typed array rom which properties should be read
+ */
+ private void updateStateFromTypedArray(@NonNull TypedArray a) {
final DrawableWrapperState state = mState;
if (state == null) {
return;
@@ -124,20 +187,8 @@
// Extract the theme attributes, if any.
state.mThemeAttrs = a.extractThemeAttrs();
- // TODO: Consider using R.styleable.DrawableWrapper_drawable
- }
-
- @Override
- public void applyTheme(Resources.Theme t) {
- super.applyTheme(t);
-
- final DrawableWrapperState state = mState;
- if (state == null) {
- return;
- }
-
- if (mDrawable != null && mDrawable.canApplyTheme()) {
- mDrawable.applyTheme(t);
+ if (a.hasValueOrEmpty(R.styleable.DrawableWrapper_drawable)) {
+ setDrawable(a.getDrawable(R.styleable.DrawableWrapper_drawable));
}
}
@@ -371,8 +422,9 @@
* child element will take precedence over any other child elements or
* explicit drawable attribute.
*/
- void inflateChildDrawable(Resources r, XmlPullParser parser, AttributeSet attrs,
- Resources.Theme theme) throws XmlPullParserException, IOException {
+ private void inflateChildDrawable(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs, @Nullable Theme theme)
+ throws XmlPullParserException, IOException {
// Seek to the first child element.
Drawable dr = null;
int type;
@@ -390,17 +442,61 @@
}
abstract static class DrawableWrapperState extends Drawable.ConstantState {
- int[] mThemeAttrs;
+ private int[] mThemeAttrs;
+
int mChangingConfigurations;
+ int mDensity = DisplayMetrics.DENSITY_DEFAULT;
Drawable.ConstantState mDrawableState;
- DrawableWrapperState(DrawableWrapperState orig) {
+ DrawableWrapperState(@Nullable DrawableWrapperState orig, @Nullable Resources res) {
if (orig != null) {
mThemeAttrs = orig.mThemeAttrs;
mChangingConfigurations = orig.mChangingConfigurations;
mDrawableState = orig.mDrawableState;
}
+
+ final int density;
+ if (res != null) {
+ density = res.getDisplayMetrics().densityDpi;
+ } else if (orig != null) {
+ density = orig.mDensity;
+ } else {
+ density = 0;
+ }
+
+ mDensity = density == 0 ? DisplayMetrics.DENSITY_DEFAULT : density;
+ }
+
+ /**
+ * Sets the constant state density.
+ * <p>
+ * If the density has been previously set, dispatches the change to
+ * subclasses so that density-dependent properties may be scaled as
+ * necessary.
+ *
+ * @param targetDensity the new constant state density
+ */
+ public final void setDensity(int targetDensity) {
+ if (mDensity != targetDensity) {
+ final int sourceDensity = mDensity;
+ mDensity = targetDensity;
+
+ onDensityChanged(sourceDensity, targetDensity);
+ }
+ }
+
+ /**
+ * Called when the constant state density changes.
+ * <p>
+ * Subclasses with density-dependent constant state properties should
+ * override this method and scale their properties as necessary.
+ *
+ * @param sourceDensity the previous constant state density
+ * @param targetDensity the new constant state density
+ */
+ void onDensityChanged(int sourceDensity, int targetDensity) {
+ // Stub method.
}
@Override
@@ -425,7 +521,7 @@
}
@Override
- public abstract Drawable newDrawable(Resources res);
+ public abstract Drawable newDrawable(@Nullable Resources res);
@Override
public int getChangingConfigurations() {
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index d7fd8a5..4be86ef 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -17,11 +17,13 @@
package android.graphics.drawable;
import android.annotation.ColorInt;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
+import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
@@ -40,6 +42,7 @@
import android.graphics.Shader;
import android.graphics.SweepGradient;
import android.util.AttributeSet;
+import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
@@ -1136,10 +1139,14 @@
}
@Override
- public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
+ public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs, @Nullable Theme theme)
throws XmlPullParserException, IOException {
+ super.inflate(r, parser, attrs, theme);
+
+ mGradientState.setDensity(Drawable.resolveDensity(r, 0));
+
final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.GradientDrawable);
- super.inflateWithAttributes(r, parser, a, R.styleable.GradientDrawable_visible);
updateStateFromTypedArray(a);
a.recycle();
@@ -1149,7 +1156,7 @@
}
@Override
- public void applyTheme(Theme t) {
+ public void applyTheme(@NonNull Theme t) {
super.applyTheme(t);
final GradientState state = mGradientState;
@@ -1157,6 +1164,8 @@
return;
}
+ state.setDensity(Drawable.resolveDensity(t.getResources(), 0));
+
if (state.mThemeAttrs != null) {
final TypedArray a = t.resolveAttributes(
state.mThemeAttrs, R.styleable.GradientDrawable);
@@ -1668,7 +1677,7 @@
@Override
public Drawable mutate() {
if (!mMutated && super.mutate() == this) {
- mGradientState = new GradientState(mGradientState);
+ mGradientState = new GradientState(mGradientState, null);
updateLocalState(null);
mMutated = true;
}
@@ -1723,6 +1732,8 @@
ColorStateList mTint = null;
PorterDuff.Mode mTintMode = DEFAULT_TINT_MODE;
+ int mDensity = DisplayMetrics.DENSITY_DEFAULT;
+
int[] mThemeAttrs;
int[] mAttrSize;
int[] mAttrGradient;
@@ -1736,55 +1747,145 @@
setGradientColors(gradientColors);
}
- public GradientState(GradientState state) {
- mChangingConfigurations = state.mChangingConfigurations;
- mShape = state.mShape;
- mGradient = state.mGradient;
- mAngle = state.mAngle;
- mOrientation = state.mOrientation;
- mSolidColors = state.mSolidColors;
- if (state.mGradientColors != null) {
- mGradientColors = state.mGradientColors.clone();
+ public GradientState(@NonNull GradientState orig, @Nullable Resources res) {
+ mChangingConfigurations = orig.mChangingConfigurations;
+ mShape = orig.mShape;
+ mGradient = orig.mGradient;
+ mAngle = orig.mAngle;
+ mOrientation = orig.mOrientation;
+ mSolidColors = orig.mSolidColors;
+ if (orig.mGradientColors != null) {
+ mGradientColors = orig.mGradientColors.clone();
}
- if (state.mPositions != null) {
- mPositions = state.mPositions.clone();
+ if (orig.mPositions != null) {
+ mPositions = orig.mPositions.clone();
}
- mStrokeColors = state.mStrokeColors;
- mStrokeWidth = state.mStrokeWidth;
- mStrokeDashWidth = state.mStrokeDashWidth;
- mStrokeDashGap = state.mStrokeDashGap;
- mRadius = state.mRadius;
- if (state.mRadiusArray != null) {
- mRadiusArray = state.mRadiusArray.clone();
+ mStrokeColors = orig.mStrokeColors;
+ mStrokeWidth = orig.mStrokeWidth;
+ mStrokeDashWidth = orig.mStrokeDashWidth;
+ mStrokeDashGap = orig.mStrokeDashGap;
+ mRadius = orig.mRadius;
+ if (orig.mRadiusArray != null) {
+ mRadiusArray = orig.mRadiusArray.clone();
}
- if (state.mPadding != null) {
- mPadding = new Rect(state.mPadding);
+ if (orig.mPadding != null) {
+ mPadding = new Rect(orig.mPadding);
}
- mWidth = state.mWidth;
- mHeight = state.mHeight;
- mInnerRadiusRatio = state.mInnerRadiusRatio;
- mThicknessRatio = state.mThicknessRatio;
- mInnerRadius = state.mInnerRadius;
- mThickness = state.mThickness;
- mDither = state.mDither;
- mOpticalInsets = state.mOpticalInsets;
- mCenterX = state.mCenterX;
- mCenterY = state.mCenterY;
- mGradientRadius = state.mGradientRadius;
- mGradientRadiusType = state.mGradientRadiusType;
- mUseLevel = state.mUseLevel;
- mUseLevelForShape = state.mUseLevelForShape;
- mOpaqueOverBounds = state.mOpaqueOverBounds;
- mOpaqueOverShape = state.mOpaqueOverShape;
- mTint = state.mTint;
- mTintMode = state.mTintMode;
- mThemeAttrs = state.mThemeAttrs;
- mAttrSize = state.mAttrSize;
- mAttrGradient = state.mAttrGradient;
- mAttrSolid = state.mAttrSolid;
- mAttrStroke = state.mAttrStroke;
- mAttrCorners = state.mAttrCorners;
- mAttrPadding = state.mAttrPadding;
+ mWidth = orig.mWidth;
+ mHeight = orig.mHeight;
+ mInnerRadiusRatio = orig.mInnerRadiusRatio;
+ mThicknessRatio = orig.mThicknessRatio;
+ mInnerRadius = orig.mInnerRadius;
+ mThickness = orig.mThickness;
+ mDither = orig.mDither;
+ mOpticalInsets = orig.mOpticalInsets;
+ mCenterX = orig.mCenterX;
+ mCenterY = orig.mCenterY;
+ mGradientRadius = orig.mGradientRadius;
+ mGradientRadiusType = orig.mGradientRadiusType;
+ mUseLevel = orig.mUseLevel;
+ mUseLevelForShape = orig.mUseLevelForShape;
+ mOpaqueOverBounds = orig.mOpaqueOverBounds;
+ mOpaqueOverShape = orig.mOpaqueOverShape;
+ mTint = orig.mTint;
+ mTintMode = orig.mTintMode;
+ mThemeAttrs = orig.mThemeAttrs;
+ mAttrSize = orig.mAttrSize;
+ mAttrGradient = orig.mAttrGradient;
+ mAttrSolid = orig.mAttrSolid;
+ mAttrStroke = orig.mAttrStroke;
+ mAttrCorners = orig.mAttrCorners;
+ mAttrPadding = orig.mAttrPadding;
+
+ mDensity = Drawable.resolveDensity(res, orig.mDensity);
+ if (orig.mDensity != mDensity) {
+ applyDensityScaling(orig.mDensity, mDensity);
+ }
+ }
+
+ /**
+ * Sets the constant state density.
+ * <p>
+ * If the density has been previously set, dispatches the change to
+ * subclasses so that density-dependent properties may be scaled as
+ * necessary.
+ *
+ * @param targetDensity the new constant state density
+ */
+ public final void setDensity(int targetDensity) {
+ if (mDensity != targetDensity) {
+ final int sourceDensity = mDensity;
+ mDensity = targetDensity;
+
+ applyDensityScaling(sourceDensity, targetDensity);
+ }
+ }
+
+ private void applyDensityScaling(int sourceDensity, int targetDensity) {
+ if (mInnerRadius > 0) {
+ mInnerRadius = Drawable.scaleFromDensity(
+ mInnerRadius, sourceDensity, targetDensity, true);
+ }
+ if (mThickness > 0) {
+ mThickness = Drawable.scaleFromDensity(
+ mThickness, sourceDensity, targetDensity, true);
+ }
+ if (mOpticalInsets != Insets.NONE) {
+ final int left = Drawable.scaleFromDensity(
+ mOpticalInsets.left, sourceDensity, targetDensity, true);
+ final int top = Drawable.scaleFromDensity(
+ mOpticalInsets.top, sourceDensity, targetDensity, true);
+ final int right = Drawable.scaleFromDensity(
+ mOpticalInsets.right, sourceDensity, targetDensity, true);
+ final int bottom = Drawable.scaleFromDensity(
+ mOpticalInsets.bottom, sourceDensity, targetDensity, true);
+ mOpticalInsets = Insets.of(left, top, right, bottom);
+ }
+ if (mPadding != null) {
+ mPadding.left = Drawable.scaleFromDensity(
+ mPadding.left, sourceDensity, targetDensity, false);
+ mPadding.top = Drawable.scaleFromDensity(
+ mPadding.top, sourceDensity, targetDensity, false);
+ mPadding.right = Drawable.scaleFromDensity(
+ mPadding.right, sourceDensity, targetDensity, false);
+ mPadding.bottom = Drawable.scaleFromDensity(
+ mPadding.bottom, sourceDensity, targetDensity, false);
+ }
+ if (mRadius > 0) {
+ mRadius = Drawable.scaleFromDensity(mRadius, sourceDensity, targetDensity);
+ }
+ if (mRadiusArray != null) {
+ mRadiusArray[0] = Drawable.scaleFromDensity(
+ (int) mRadiusArray[0], sourceDensity, targetDensity, true);
+ mRadiusArray[1] = Drawable.scaleFromDensity(
+ (int) mRadiusArray[1], sourceDensity, targetDensity, true);
+ mRadiusArray[2] = Drawable.scaleFromDensity(
+ (int) mRadiusArray[2], sourceDensity, targetDensity, true);
+ mRadiusArray[3] = Drawable.scaleFromDensity(
+ (int) mRadiusArray[3], sourceDensity, targetDensity, true);
+ }
+ if (mStrokeWidth > 0) {
+ mStrokeWidth = Drawable.scaleFromDensity(
+ mStrokeWidth, sourceDensity, targetDensity, true);
+ }
+ if (mStrokeDashWidth > 0) {
+ mStrokeDashWidth = Drawable.scaleFromDensity(
+ mStrokeDashGap, sourceDensity, targetDensity);
+ }
+ if (mStrokeDashGap > 0) {
+ mStrokeDashGap = Drawable.scaleFromDensity(
+ mStrokeDashGap, sourceDensity, targetDensity);
+ }
+ if (mGradientRadiusType == RADIUS_TYPE_PIXELS) {
+ mGradientRadius = Drawable.scaleFromDensity(
+ mGradientRadius, sourceDensity, targetDensity);
+ }
+ if (mWidth > 0) {
+ mWidth = Drawable.scaleFromDensity(mWidth, sourceDensity, targetDensity, true);
+ }
+ if (mHeight > 0) {
+ mHeight = Drawable.scaleFromDensity(mHeight, sourceDensity, targetDensity, true);
+ }
}
@Override
@@ -1805,8 +1906,18 @@
}
@Override
- public Drawable newDrawable(Resources res) {
- return new GradientDrawable(this, res);
+ public Drawable newDrawable(@Nullable Resources res) {
+ // If this drawable is being created for a different density,
+ // just create a new constant state and call it a day.
+ final GradientState state;
+ final int density = Drawable.resolveDensity(res, mDensity);
+ if (density != mDensity) {
+ state = new GradientState(this, res);
+ } else {
+ state = this;
+ }
+
+ return new GradientDrawable(state, res);
}
@Override
@@ -1913,7 +2024,7 @@
*
* @param state Constant state from which the drawable inherits
*/
- private GradientDrawable(GradientState state, Resources res) {
+ private GradientDrawable(@NonNull GradientState state, @Nullable Resources res) {
mGradientState = state;
updateLocalState(res);
diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
index 26232a9..d800acb 100644
--- a/graphics/java/android/graphics/drawable/Icon.java
+++ b/graphics/java/android/graphics/drawable/Icon.java
@@ -377,12 +377,17 @@
return loadDrawable(context);
}
+ /** @hide */
+ public static final int MIN_ASHMEM_ICON_SIZE = 128 * (1 << 10);
+
/**
* Puts the memory used by this instance into Ashmem memory, if possible.
* @hide
*/
public void convertToAshmem() {
- if (mType == TYPE_BITMAP && getBitmap().isMutable()) {
+ if (mType == TYPE_BITMAP &&
+ getBitmap().isMutable() &&
+ getBitmap().getAllocationByteCount() >= MIN_ASHMEM_ICON_SIZE) {
setBitmap(getBitmap().createAshmemBitmap());
}
}
diff --git a/graphics/java/android/graphics/drawable/InsetDrawable.java b/graphics/java/android/graphics/drawable/InsetDrawable.java
index e1ebdbb..927b9c9 100644
--- a/graphics/java/android/graphics/drawable/InsetDrawable.java
+++ b/graphics/java/android/graphics/drawable/InsetDrawable.java
@@ -22,14 +22,17 @@
import org.xmlpull.v1.XmlPullParserException;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
+import android.graphics.Bitmap;
import android.graphics.Insets;
import android.graphics.Outline;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.util.AttributeSet;
+import android.util.DisplayMetrics;
import java.io.IOException;
@@ -58,7 +61,7 @@
* No-arg constructor used by drawable inflation.
*/
InsetDrawable() {
- this(new InsetState(null), null);
+ this(new InsetState(null, null), null);
}
/**
@@ -67,7 +70,7 @@
* @param drawable The drawable to inset.
* @param inset Inset in pixels around the drawable.
*/
- public InsetDrawable(Drawable drawable, int inset) {
+ public InsetDrawable(@Nullable Drawable drawable, int inset) {
this(drawable, inset, inset, inset, inset);
}
@@ -80,9 +83,9 @@
* @param insetRight Right inset in pixels.
* @param insetBottom Bottom inset in pixels.
*/
- public InsetDrawable(Drawable drawable, int insetLeft, int insetTop,int insetRight,
- int insetBottom) {
- this(new InsetState(null), null);
+ public InsetDrawable(@Nullable Drawable drawable, int insetLeft, int insetTop,
+ int insetRight, int insetBottom) {
+ this(new InsetState(null, null), null);
mState.mInsetLeft = insetLeft;
mState.mInsetTop = insetTop;
@@ -93,70 +96,24 @@
}
@Override
- public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
+ public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs, @Nullable Theme theme)
throws XmlPullParserException, IOException {
+ final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.InsetDrawable);
+
+ // Inflation will advance the XmlPullParser and AttributeSet.
super.inflate(r, parser, attrs, theme);
- final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.InsetDrawable);
updateStateFromTypedArray(a);
- inflateChildDrawable(r, parser, attrs, theme);
verifyRequiredAttributes(a);
a.recycle();
}
- private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException {
- // If we're not waiting on a theme, verify required attributes.
- if (getDrawable() == null && (mState.mThemeAttrs == null
- || mState.mThemeAttrs[R.styleable.InsetDrawable_drawable] == 0)) {
- throw new XmlPullParserException(a.getPositionDescription()
- + ": <inset> tag requires a 'drawable' attribute or "
- + "child tag defining a drawable");
- }
- }
-
@Override
- void updateStateFromTypedArray(TypedArray a) {
- super.updateStateFromTypedArray(a);
+ public void applyTheme(@NonNull Theme t) {
+ super.applyTheme(t);
final InsetState state = mState;
- final int N = a.getIndexCount();
- for (int i = 0; i < N; i++) {
- final int attr = a.getIndex(i);
- switch (attr) {
- case R.styleable.InsetDrawable_drawable:
- final Drawable dr = a.getDrawable(attr);
- if (dr != null) {
- setDrawable(dr);
- }
- break;
- case R.styleable.InsetDrawable_inset:
- final int inset = a.getDimensionPixelOffset(attr, Integer.MIN_VALUE);
- if (inset != Integer.MIN_VALUE) {
- state.mInsetLeft = inset;
- state.mInsetTop = inset;
- state.mInsetRight = inset;
- state.mInsetBottom = inset;
- }
- break;
- case R.styleable.InsetDrawable_insetLeft:
- state.mInsetLeft = a.getDimensionPixelOffset(attr, state.mInsetLeft);
- break;
- case R.styleable.InsetDrawable_insetTop:
- state.mInsetTop = a.getDimensionPixelOffset(attr, state.mInsetTop);
- break;
- case R.styleable.InsetDrawable_insetRight:
- state.mInsetRight = a.getDimensionPixelOffset(attr, state.mInsetRight);
- break;
- case R.styleable.InsetDrawable_insetBottom:
- state.mInsetBottom = a.getDimensionPixelOffset(attr, state.mInsetBottom);
- break;
- }
- }
- }
-
- @Override
- public void applyTheme(Theme t) {
- final InsetState state = mState;
if (state == null) {
return;
}
@@ -172,10 +129,47 @@
a.recycle();
}
}
+ }
- // The drawable may have changed as a result of applying the theme, so
- // apply the theme to the wrapped drawable last.
- super.applyTheme(t);
+ private void verifyRequiredAttributes(@NonNull TypedArray a) throws XmlPullParserException {
+ // If we're not waiting on a theme, verify required attributes.
+ if (getDrawable() == null && (mState.mThemeAttrs == null
+ || mState.mThemeAttrs[R.styleable.InsetDrawable_drawable] == 0)) {
+ throw new XmlPullParserException(a.getPositionDescription()
+ + ": <inset> tag requires a 'drawable' attribute or "
+ + "child tag defining a drawable");
+ }
+ }
+
+ private void updateStateFromTypedArray(@NonNull TypedArray a) {
+ final InsetState state = mState;
+ if (state == null) {
+ return;
+ }
+
+ // Account for any configuration changes.
+ state.mChangingConfigurations |= a.getChangingConfigurations();
+
+ // Extract the theme attributes, if any.
+ state.mThemeAttrs = a.extractThemeAttrs();
+
+ // Inset attribute may be overridden by more specific attributes.
+ if (a.hasValue(R.styleable.InsetDrawable_inset)) {
+ final int inset = a.getDimensionPixelOffset(R.styleable.InsetDrawable_inset, 0);
+ state.mInsetLeft = inset;
+ state.mInsetTop = inset;
+ state.mInsetRight = inset;
+ state.mInsetBottom = inset;
+ }
+
+ state.mInsetLeft = a.getDimensionPixelOffset(
+ R.styleable.InsetDrawable_insetLeft, state.mInsetLeft);
+ state.mInsetRight = a.getDimensionPixelOffset(
+ R.styleable.InsetDrawable_insetRight, state.mInsetRight);
+ state.mInsetTop = a.getDimensionPixelOffset(
+ R.styleable.InsetDrawable_insetTop, state.mInsetTop);
+ state.mInsetBottom = a.getDimensionPixelOffset(
+ R.styleable.InsetDrawable_insetBottom, state.mInsetBottom);
}
@Override
@@ -243,30 +237,72 @@
@Override
DrawableWrapperState mutateConstantState() {
- mState = new InsetState(mState);
+ mState = new InsetState(mState, null);
return mState;
}
static final class InsetState extends DrawableWrapper.DrawableWrapperState {
+ private int[] mThemeAttrs;
+
int mInsetLeft = 0;
int mInsetTop = 0;
int mInsetRight = 0;
int mInsetBottom = 0;
- InsetState(InsetState orig) {
- super(orig);
+ InsetState(@Nullable InsetState orig, @Nullable Resources res) {
+ super(orig, res);
if (orig != null) {
mInsetLeft = orig.mInsetLeft;
mInsetTop = orig.mInsetTop;
mInsetRight = orig.mInsetRight;
mInsetBottom = orig.mInsetBottom;
+
+ if (orig.mDensity != mDensity) {
+ applyDensityScaling(orig.mDensity, mDensity);
+ }
}
}
@Override
- public Drawable newDrawable(Resources res) {
- return new InsetDrawable(this, res);
+ void onDensityChanged(int sourceDensity, int targetDensity) {
+ super.onDensityChanged(sourceDensity, targetDensity);
+
+ applyDensityScaling(sourceDensity, targetDensity);
+ }
+
+ /**
+ * Called when the constant state density changes to scale
+ * density-dependent properties specific to insets.
+ *
+ * @param sourceDensity the previous constant state density
+ * @param targetDensity the new constant state density
+ */
+ private void applyDensityScaling(int sourceDensity, int targetDensity) {
+ mInsetLeft = Bitmap.scaleFromDensity(mInsetLeft, sourceDensity, targetDensity);
+ mInsetTop = Bitmap.scaleFromDensity(mInsetTop, sourceDensity, targetDensity);
+ mInsetRight = Bitmap.scaleFromDensity(mInsetRight, sourceDensity, targetDensity);
+ mInsetBottom = Bitmap.scaleFromDensity(mInsetBottom, sourceDensity, targetDensity);
+ }
+
+ @Override
+ public Drawable newDrawable(@Nullable Resources res) {
+ // If this drawable is being created for a different density,
+ // just create a new constant state and call it a day.
+ final InsetState state;
+ if (res != null) {
+ final int densityDpi = res.getDisplayMetrics().densityDpi;
+ final int density = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
+ if (density != mDensity) {
+ state = new InsetState(this, res);
+ } else {
+ state = this;
+ }
+ } else {
+ state = this;
+ }
+
+ return new InsetDrawable(state, res);
}
}
@@ -274,7 +310,7 @@
* The one constructor to rule them all. This is called by all public
* constructors to set the state and initialize local properties.
*/
- private InsetDrawable(InsetState state, Resources res) {
+ private InsetDrawable(@NonNull InsetState state, @Nullable Resources res) {
super(state, res);
mState = state;
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index 1a0ba6f..651b453 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -30,6 +30,7 @@
import android.graphics.PorterDuff.Mode;
import android.graphics.Rect;
import android.util.AttributeSet;
+import android.util.DisplayMetrics;
import android.util.LayoutDirection;
import android.view.Gravity;
import android.view.View;
@@ -124,7 +125,7 @@
final int length = layers.length;
final ChildDrawable[] r = new ChildDrawable[length];
for (int i = 0; i < length; i++) {
- r[i] = new ChildDrawable();
+ r[i] = new ChildDrawable(mLayerState.mDensity);
r[i].mDrawable = layers[i];
layers[i].setCallback(this);
mLayerState.mChildrenChangingConfigurations |= layers[i].getChangingConfigurations();
@@ -140,6 +141,10 @@
this((LayerState) null, null);
}
+ /**
+ * The one constructor to rule them all. This is called by all public
+ * constructors to set the state and initialize local properties.
+ */
LayerDrawable(@Nullable LayerState state, @Nullable Resources res) {
mLayerState = createConstantState(state, res);
if (mLayerState.mNum > 0) {
@@ -153,24 +158,135 @@
}
@Override
- public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
+ public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs, @Nullable Theme theme)
throws XmlPullParserException, IOException {
super.inflate(r, parser, attrs, theme);
+ final LayerState state = mLayerState;
+ if (state == null) {
+ return;
+ }
+
+ // The density may have changed since the last update. This will
+ // apply scaling to any existing constant state properties.
+ final int density = Drawable.resolveDensity(r, 0);
+ state.setDensity(density);
+
final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.LayerDrawable);
updateStateFromTypedArray(a);
a.recycle();
+ final ChildDrawable[] array = state.mChildren;
+ final int N = state.mNum;
+ for (int i = 0; i < N; i++) {
+ final ChildDrawable layer = array[i];
+ layer.setDensity(density);
+ }
+
inflateLayers(r, parser, attrs, theme);
ensurePadding();
refreshPadding();
}
+ @Override
+ public void applyTheme(@NonNull Theme t) {
+ super.applyTheme(t);
+
+ final LayerState state = mLayerState;
+ if (state == null) {
+ return;
+ }
+
+ final int density = Drawable.resolveDensity(t.getResources(), 0);
+ state.setDensity(density);
+
+ if (state.mThemeAttrs != null) {
+ final TypedArray a = t.resolveAttributes(
+ state.mThemeAttrs, R.styleable.LayerDrawable);
+ updateStateFromTypedArray(a);
+ a.recycle();
+ }
+
+ final ChildDrawable[] array = state.mChildren;
+ final int N = state.mNum;
+ for (int i = 0; i < N; i++) {
+ final ChildDrawable layer = array[i];
+ layer.setDensity(density);
+
+ if (layer.mThemeAttrs != null) {
+ final TypedArray a = t.resolveAttributes(
+ layer.mThemeAttrs, R.styleable.LayerDrawableItem);
+ updateLayerFromTypedArray(layer, a);
+ a.recycle();
+ }
+
+ final Drawable d = layer.mDrawable;
+ if (d != null && d.canApplyTheme()) {
+ d.applyTheme(t);
+
+ // Update cached mask of child changing configurations.
+ state.mChildrenChangingConfigurations |= d.getChangingConfigurations();
+ }
+ }
+ }
+
+ /**
+ * Inflates child layers using the specified parser.
+ */
+ private void inflateLayers(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs, @Nullable Theme theme)
+ throws XmlPullParserException, IOException {
+ final LayerState state = mLayerState;
+
+ final int innerDepth = parser.getDepth() + 1;
+ int type;
+ int depth;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ if (depth > innerDepth || !parser.getName().equals("item")) {
+ continue;
+ }
+
+ final ChildDrawable layer = new ChildDrawable(state.mDensity);
+ final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.LayerDrawableItem);
+ updateLayerFromTypedArray(layer, a);
+ a.recycle();
+
+ // If the layer doesn't have a drawable or unresolved theme
+ // attribute for a drawable, attempt to parse one from the child
+ // element.
+ if (layer.mDrawable == null && (layer.mThemeAttrs == null ||
+ layer.mThemeAttrs[R.styleable.LayerDrawableItem_drawable] == 0)) {
+ while ((type = parser.next()) == XmlPullParser.TEXT) {
+ }
+ if (type != XmlPullParser.START_TAG) {
+ throw new XmlPullParserException(parser.getPositionDescription()
+ + ": <item> tag requires a 'drawable' attribute or "
+ + "child tag defining a drawable");
+ }
+ layer.mDrawable = Drawable.createFromXmlInner(r, parser, attrs, theme);
+ }
+
+ if (layer.mDrawable != null) {
+ state.mChildrenChangingConfigurations |=
+ layer.mDrawable.getChangingConfigurations();
+ layer.mDrawable.setCallback(this);
+ }
+
+ addLayer(layer);
+ }
+ }
+
/**
* Initializes the constant state from the values in the typed array.
*/
- private void updateStateFromTypedArray(TypedArray a) {
+ private void updateStateFromTypedArray(@NonNull TypedArray a) {
final LayerState state = mLayerState;
// Account for any configuration changes.
@@ -181,7 +297,7 @@
final int N = a.getIndexCount();
for (int i = 0; i < N; i++) {
- int attr = a.getIndex(i);
+ final int attr = a.getIndex(i);
switch (attr) {
case R.styleable.LayerDrawable_opacity:
state.mOpacityOverride = a.getInt(attr, state.mOpacityOverride);
@@ -214,57 +330,7 @@
}
}
- /**
- * Inflates child layers using the specified parser.
- */
- private void inflateLayers(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
- throws XmlPullParserException, IOException {
- final LayerState state = mLayerState;
-
- final int innerDepth = parser.getDepth() + 1;
- int type;
- int depth;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
- if (type != XmlPullParser.START_TAG) {
- continue;
- }
-
- if (depth > innerDepth || !parser.getName().equals("item")) {
- continue;
- }
-
- final ChildDrawable layer = new ChildDrawable();
- final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.LayerDrawableItem);
- updateLayerFromTypedArray(layer, a);
- a.recycle();
-
- // If the layer doesn't have a drawable or unresolved theme
- // attribute for a drawable, attempt to parse one from the child
- // element.
- if (layer.mDrawable == null && (layer.mThemeAttrs == null ||
- layer.mThemeAttrs[R.styleable.LayerDrawableItem_drawable] == 0)) {
- while ((type = parser.next()) == XmlPullParser.TEXT) {
- }
- if (type != XmlPullParser.START_TAG) {
- throw new XmlPullParserException(parser.getPositionDescription()
- + ": <item> tag requires a 'drawable' attribute or "
- + "child tag defining a drawable");
- }
- layer.mDrawable = Drawable.createFromXmlInner(r, parser, attrs, theme);
- }
-
- if (layer.mDrawable != null) {
- state.mChildrenChangingConfigurations |=
- layer.mDrawable.getChangingConfigurations();
- layer.mDrawable.setCallback(this);
- }
-
- addLayer(layer);
- }
- }
-
- private void updateLayerFromTypedArray(ChildDrawable layer, TypedArray a) {
+ private void updateLayerFromTypedArray(@NonNull ChildDrawable layer, @NonNull TypedArray a) {
final LayerState state = mLayerState;
// Account for any configuration changes.
@@ -273,25 +339,42 @@
// Extract the theme attributes, if any.
layer.mThemeAttrs = a.extractThemeAttrs();
- layer.mInsetL = a.getDimensionPixelOffset(
- R.styleable.LayerDrawableItem_left, layer.mInsetL);
- layer.mInsetT = a.getDimensionPixelOffset(
- R.styleable.LayerDrawableItem_top, layer.mInsetT);
- layer.mInsetR = a.getDimensionPixelOffset(
- R.styleable.LayerDrawableItem_right, layer.mInsetR);
- layer.mInsetB = a.getDimensionPixelOffset(
- R.styleable.LayerDrawableItem_bottom, layer.mInsetB);
- layer.mInsetS = a.getDimensionPixelOffset(
- R.styleable.LayerDrawableItem_start, layer.mInsetS);
- layer.mInsetE = a.getDimensionPixelOffset(
- R.styleable.LayerDrawableItem_end, layer.mInsetE);
- layer.mWidth = a.getDimensionPixelSize(
- R.styleable.LayerDrawableItem_width, layer.mWidth);
- layer.mHeight = a.getDimensionPixelSize(
- R.styleable.LayerDrawableItem_height, layer.mHeight);
- layer.mGravity = a.getInteger(
- R.styleable.LayerDrawableItem_gravity, layer.mGravity);
- layer.mId = a.getResourceId(R.styleable.LayerDrawableItem_id, layer.mId);
+ final int N = a.getIndexCount();
+ for (int i = 0; i < N; i++) {
+ final int attr = a.getIndex(i);
+ switch (attr) {
+ case R.styleable.LayerDrawableItem_left:
+ layer.mInsetL = a.getDimensionPixelOffset(attr, layer.mInsetL);
+ break;
+ case R.styleable.LayerDrawableItem_top:
+ layer.mInsetT = a.getDimensionPixelOffset(attr, layer.mInsetT);
+ break;
+ case R.styleable.LayerDrawableItem_right:
+ layer.mInsetR = a.getDimensionPixelOffset(attr, layer.mInsetR);
+ break;
+ case R.styleable.LayerDrawableItem_bottom:
+ layer.mInsetB = a.getDimensionPixelOffset(attr, layer.mInsetB);
+ break;
+ case R.styleable.LayerDrawableItem_start:
+ layer.mInsetS = a.getDimensionPixelOffset(attr, layer.mInsetS);
+ break;
+ case R.styleable.LayerDrawableItem_end:
+ layer.mInsetE = a.getDimensionPixelOffset(attr, layer.mInsetE);
+ break;
+ case R.styleable.LayerDrawableItem_width:
+ layer.mWidth = a.getDimensionPixelSize(attr, layer.mWidth);
+ break;
+ case R.styleable.LayerDrawableItem_height:
+ layer.mHeight = a.getDimensionPixelSize(attr, layer.mHeight);
+ break;
+ case R.styleable.LayerDrawableItem_gravity:
+ layer.mGravity = a.getInteger(attr, layer.mGravity);
+ break;
+ case R.styleable.LayerDrawableItem_id:
+ layer.mId = a.getResourceId(attr, layer.mId);
+ break;
+ }
+ }
final Drawable dr = a.getDrawable(R.styleable.LayerDrawableItem_drawable);
if (dr != null) {
@@ -300,44 +383,6 @@
}
@Override
- public void applyTheme(Theme t) {
- super.applyTheme(t);
-
- final LayerState state = mLayerState;
- if (state == null) {
- return;
- }
-
- if (state.mThemeAttrs != null) {
- final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.LayerDrawable);
- updateStateFromTypedArray(a);
- a.recycle();
- }
-
- final ChildDrawable[] array = state.mChildren;
- final int N = state.mNum;
- for (int i = 0; i < N; i++) {
- final ChildDrawable layer = array[i];
- if (layer.mThemeAttrs != null) {
- final TypedArray a = t.resolveAttributes(layer.mThemeAttrs,
- R.styleable.LayerDrawableItem);
- updateLayerFromTypedArray(layer, a);
- a.recycle();
- }
-
- final Drawable d = layer.mDrawable;
- if (d != null && d.canApplyTheme()) {
- d.applyTheme(t);
-
- // Update cached mask of child changing configurations.
- state.mChildrenChangingConfigurations |= d.getChangingConfigurations();
- }
- }
-
- ensurePadding();
- }
-
- @Override
public boolean canApplyTheme() {
return (mLayerState != null && mLayerState.canApplyTheme()) || super.canApplyTheme();
}
@@ -368,7 +413,7 @@
* @param layer The layer to add.
* @return The index of the layer.
*/
- int addLayer(ChildDrawable layer) {
+ int addLayer(@NonNull ChildDrawable layer) {
final LayerState st = mLayerState;
final int N = st.mChildren != null ? st.mChildren.length : 0;
final int i = st.mNum;
@@ -418,7 +463,7 @@
}
private ChildDrawable createLayer(Drawable dr) {
- final ChildDrawable layer = new ChildDrawable();
+ final ChildDrawable layer = new ChildDrawable(mLayerState.mDensity);
layer.mDrawable = dr;
return layer;
}
@@ -1556,8 +1601,10 @@
insetR = r.mInsetE == UNDEFINED_INSET ? r.mInsetR : r.mInsetE;
}
+ // Don't apply padding and insets for children that don't have
+ // an intrinsic dimension.
final int minWidth = r.mWidth < 0 ? r.mDrawable.getIntrinsicWidth() : r.mWidth;
- final int w = minWidth + insetL + insetR + padL + padR;
+ final int w = minWidth < 0 ? -1 : minWidth + insetL + insetR + padL + padR;
if (w > width) {
width = w;
}
@@ -1586,8 +1633,10 @@
continue;
}
+ // Don't apply padding and insets for children that don't have
+ // an intrinsic dimension.
final int minHeight = r.mHeight < 0 ? r.mDrawable.getIntrinsicHeight() : r.mHeight;
- final int h = minHeight + r.mInsetT + r.mInsetB + padT + padB;
+ final int h = minHeight < 0 ? -1 : minHeight + r.mInsetT + r.mInsetB + padT + padB;
if (h > height) {
height = h;
}
@@ -1708,6 +1757,7 @@
static class ChildDrawable {
public Drawable mDrawable;
public int[] mThemeAttrs;
+ public int mDensity = DisplayMetrics.DENSITY_DEFAULT;
public int mInsetL, mInsetT, mInsetR, mInsetB;
public int mInsetS = UNDEFINED_INSET;
public int mInsetE = UNDEFINED_INSET;
@@ -1716,16 +1766,19 @@
public int mGravity = Gravity.NO_GRAVITY;
public int mId = View.NO_ID;
- ChildDrawable() {
- // Default empty constructor.
+ ChildDrawable(int density) {
+ mDensity = density;
}
- ChildDrawable(ChildDrawable orig, LayerDrawable owner, Resources res) {
+ ChildDrawable(@NonNull ChildDrawable orig, @NonNull LayerDrawable owner,
+ @Nullable Resources res) {
final Drawable dr = orig.mDrawable;
final Drawable clone;
if (dr != null) {
final ConstantState cs = dr.getConstantState();
- if (res != null) {
+ if (cs == null) {
+ clone = dr;
+ } else if (res != null) {
clone = cs.newDrawable(res);
} else {
clone = cs.newDrawable();
@@ -1750,19 +1803,56 @@
mHeight = orig.mHeight;
mGravity = orig.mGravity;
mId = orig.mId;
+
+ mDensity = Drawable.resolveDensity(res, orig.mDensity);
+ if (orig.mDensity != mDensity) {
+ applyDensityScaling(orig.mDensity, mDensity);
+ }
}
public boolean canApplyTheme() {
return mThemeAttrs != null
|| (mDrawable != null && mDrawable.canApplyTheme());
}
+
+ public final void setDensity(int targetDensity) {
+ if (mDensity != targetDensity) {
+ final int sourceDensity = mDensity;
+ mDensity = targetDensity;
+
+ applyDensityScaling(sourceDensity, targetDensity);
+ }
+ }
+
+ private void applyDensityScaling(int sourceDensity, int targetDensity) {
+ mInsetL = Drawable.scaleFromDensity(mInsetL, sourceDensity, targetDensity, false);
+ mInsetT = Drawable.scaleFromDensity(mInsetT, sourceDensity, targetDensity, false);
+ mInsetR = Drawable.scaleFromDensity(mInsetR, sourceDensity, targetDensity, false);
+ mInsetB = Drawable.scaleFromDensity(mInsetB, sourceDensity, targetDensity, false);
+ if (mInsetS != UNDEFINED_INSET) {
+ mInsetS = Drawable.scaleFromDensity(mInsetS, sourceDensity, targetDensity, false);
+ }
+ if (mInsetE != UNDEFINED_INSET) {
+ mInsetE = Drawable.scaleFromDensity(mInsetE, sourceDensity, targetDensity, false);
+ }
+ if (mWidth > 0) {
+ mWidth = Drawable.scaleFromDensity(mWidth, sourceDensity, targetDensity, true);
+ }
+ if (mHeight > 0) {
+ mHeight = Drawable.scaleFromDensity(mHeight, sourceDensity, targetDensity, true);
+ }
+ }
}
static class LayerState extends ConstantState {
+ private int[] mThemeAttrs;
+
int mNum;
ChildDrawable[] mChildren;
- int[] mThemeAttrs;
+ int mDensity;
+
+ // These values all correspond to mDensity.
int mPaddingTop = -1;
int mPaddingBottom = -1;
int mPaddingLeft = -1;
@@ -1784,7 +1874,10 @@
private int mPaddingMode = PADDING_MODE_NEST;
- LayerState(LayerState orig, LayerDrawable owner, Resources res) {
+ LayerState(@Nullable LayerState orig, @NonNull LayerDrawable owner,
+ @Nullable Resources res) {
+ mDensity = Drawable.resolveDensity(res, orig != null ? orig.mDensity : 0);
+
if (orig != null) {
final ChildDrawable[] origChildDrawable = orig.mChildren;
final int N = orig.mNum;
@@ -1814,12 +1907,56 @@
mPaddingStart = orig.mPaddingStart;
mPaddingEnd = orig.mPaddingEnd;
mOpacityOverride = orig.mOpacityOverride;
+
+ if (orig.mDensity != mDensity) {
+ applyDensityScaling(orig.mDensity, mDensity);
+ }
} else {
mNum = 0;
mChildren = null;
}
}
+ public final void setDensity(int targetDensity) {
+ if (mDensity != targetDensity) {
+ final int sourceDensity = mDensity;
+ mDensity = targetDensity;
+
+ onDensityChanged(sourceDensity, targetDensity);
+ }
+ }
+
+ protected void onDensityChanged(int sourceDensity, int targetDensity) {
+ applyDensityScaling(sourceDensity, targetDensity);
+ }
+
+ private void applyDensityScaling(int sourceDensity, int targetDensity) {
+ if (mPaddingLeft > 0) {
+ mPaddingLeft = Drawable.scaleFromDensity(
+ mPaddingLeft, sourceDensity, targetDensity, false);
+ }
+ if (mPaddingTop > 0) {
+ mPaddingTop = Drawable.scaleFromDensity(
+ mPaddingTop, sourceDensity, targetDensity, false);
+ }
+ if (mPaddingRight > 0) {
+ mPaddingRight = Drawable.scaleFromDensity(
+ mPaddingRight, sourceDensity, targetDensity, false);
+ }
+ if (mPaddingBottom > 0) {
+ mPaddingBottom = Drawable.scaleFromDensity(
+ mPaddingBottom, sourceDensity, targetDensity, false);
+ }
+ if (mPaddingStart > 0) {
+ mPaddingStart = Drawable.scaleFromDensity(
+ mPaddingStart, sourceDensity, targetDensity, false);
+ }
+ if (mPaddingEnd > 0) {
+ mPaddingEnd = Drawable.scaleFromDensity(
+ mPaddingEnd, sourceDensity, targetDensity, false);
+ }
+ }
+
@Override
public boolean canApplyTheme() {
if (mThemeAttrs != null || super.canApplyTheme()) {
@@ -1844,7 +1981,7 @@
}
@Override
- public Drawable newDrawable(Resources res) {
+ public Drawable newDrawable(@Nullable Resources res) {
return new LayerDrawable(this, res);
}
diff --git a/graphics/java/android/graphics/drawable/LevelListDrawable.java b/graphics/java/android/graphics/drawable/LevelListDrawable.java
index b01c643..4ce52d1 100644
--- a/graphics/java/android/graphics/drawable/LevelListDrawable.java
+++ b/graphics/java/android/graphics/drawable/LevelListDrawable.java
@@ -88,7 +88,13 @@
public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
throws XmlPullParserException, IOException {
super.inflate(r, parser, attrs, theme);
+ updateDensity(r);
+ inflateChildElements(r, parser, attrs, theme);
+ }
+
+ private void inflateChildElements(Resources r, XmlPullParser parser, AttributeSet attrs,
+ Theme theme) throws XmlPullParserException, IOException {
int type;
int low = 0;
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index 231405d..4d51d63 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -181,10 +181,10 @@
}
private static Insets scaleFromDensity(Insets insets, int sdensity, int tdensity) {
- int left = Bitmap.scaleFromDensity(insets.left, sdensity, tdensity);
- int top = Bitmap.scaleFromDensity(insets.top, sdensity, tdensity);
- int right = Bitmap.scaleFromDensity(insets.right, sdensity, tdensity);
- int bottom = Bitmap.scaleFromDensity(insets.bottom, sdensity, tdensity);
+ int left = Drawable.scaleFromDensity(insets.left, sdensity, tdensity, true);
+ int top = Drawable.scaleFromDensity(insets.top, sdensity, tdensity, true);
+ int right = Drawable.scaleFromDensity(insets.right, sdensity, tdensity, true);
+ int bottom = Drawable.scaleFromDensity(insets.bottom, sdensity, tdensity, true);
return Insets.of(left, top, right, bottom);
}
@@ -196,18 +196,20 @@
mBitmapHeight = mNinePatch.getHeight();
mOpticalInsets = mNinePatchState.mOpticalInsets;
} else {
- mBitmapWidth = Bitmap.scaleFromDensity(mNinePatch.getWidth(), sdensity, tdensity);
- mBitmapHeight = Bitmap.scaleFromDensity(mNinePatch.getHeight(), sdensity, tdensity);
+ mBitmapWidth = Drawable.scaleFromDensity(
+ mNinePatch.getWidth(), sdensity, tdensity, true);
+ mBitmapHeight = Drawable.scaleFromDensity(
+ mNinePatch.getHeight(), sdensity, tdensity, true);
if (mNinePatchState.mPadding != null && mPadding != null) {
Rect dest = mPadding;
Rect src = mNinePatchState.mPadding;
if (dest == src) {
mPadding = dest = new Rect(src);
}
- dest.left = Bitmap.scaleFromDensity(src.left, sdensity, tdensity);
- dest.top = Bitmap.scaleFromDensity(src.top, sdensity, tdensity);
- dest.right = Bitmap.scaleFromDensity(src.right, sdensity, tdensity);
- dest.bottom = Bitmap.scaleFromDensity(src.bottom, sdensity, tdensity);
+ dest.left = Drawable.scaleFromDensity(src.left, sdensity, tdensity, true);
+ dest.top = Drawable.scaleFromDensity(src.top, sdensity, tdensity, true);
+ dest.right = Drawable.scaleFromDensity(src.right, sdensity, tdensity, true);
+ dest.bottom = Drawable.scaleFromDensity(src.bottom, sdensity, tdensity, true);
}
mOpticalInsets = scaleFromDensity(mNinePatchState.mOpticalInsets, sdensity, tdensity);
}
@@ -490,8 +492,7 @@
state.mTint = tint;
}
- final int densityDpi = r.getDisplayMetrics().densityDpi;
- state.mTargetDensity = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
+ state.mTargetDensity = Drawable.resolveDensity(r, state.mTargetDensity);
}
@Override
diff --git a/graphics/java/android/graphics/drawable/RippleComponent.java b/graphics/java/android/graphics/drawable/RippleComponent.java
index 2d378c6..e83513c 100644
--- a/graphics/java/android/graphics/drawable/RippleComponent.java
+++ b/graphics/java/android/graphics/drawable/RippleComponent.java
@@ -20,6 +20,7 @@
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
+import android.util.DisplayMetrics;
import android.view.DisplayListCanvas;
import android.view.RenderNodeAnimator;
@@ -50,7 +51,7 @@
protected float mTargetRadius;
/** Screen density used to adjust pixel-based constants. */
- protected float mDensity;
+ protected float mDensityScale;
/**
* If set, force all ripple animations to not run on RenderThread, even if it would be
@@ -71,7 +72,7 @@
}
}
- public final void setup(float maxRadius, float density) {
+ public final void setup(float maxRadius, int densityDpi) {
if (maxRadius >= 0) {
mHasMaxRadius = true;
mTargetRadius = maxRadius;
@@ -79,7 +80,7 @@
mTargetRadius = getTargetRadius(mBounds);
}
- mDensity = density;
+ mDensityScale = densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
onTargetRadiusChanged(mTargetRadius);
}
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 2690223..aaab529 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -160,7 +160,7 @@
private Paint mRipplePaint;
/** Target density of the display into which ripples are drawn. */
- private float mDensity = 1.0f;
+ private int mDensity;
/** Whether bounds are being overridden. */
private boolean mOverrideBounds;
@@ -409,18 +409,20 @@
}
@Override
- public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
+ public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs, @Nullable Theme theme)
throws XmlPullParserException, IOException {
final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.RippleDrawable);
- updateStateFromTypedArray(a);
- a.recycle();
// Force padding default to STACK before inflating.
setPaddingMode(PADDING_MODE_STACK);
+ // Inflation will advance the XmlPullParser and AttributeSet.
super.inflate(r, parser, attrs, theme);
- setTargetDensity(r.getDisplayMetrics());
+ updateStateFromTypedArray(a);
+ verifyRequiredAttributes(a);
+ a.recycle();
updateLocalState();
}
@@ -461,7 +463,7 @@
/**
* Initializes the constant state from the values in the typed array.
*/
- private void updateStateFromTypedArray(TypedArray a) throws XmlPullParserException {
+ private void updateStateFromTypedArray(@NonNull TypedArray a) throws XmlPullParserException {
final RippleState state = mState;
// Account for any configuration changes.
@@ -477,11 +479,9 @@
mState.mMaxRadius = a.getDimensionPixelSize(
R.styleable.RippleDrawable_radius, mState.mMaxRadius);
-
- verifyRequiredAttributes(a);
}
- private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException {
+ private void verifyRequiredAttributes(@NonNull TypedArray a) throws XmlPullParserException {
if (mState.mColor == null && (mState.mTouchThemeAttrs == null
|| mState.mTouchThemeAttrs[R.styleable.RippleDrawable_color] == 0)) {
throw new XmlPullParserException(a.getPositionDescription() +
@@ -489,20 +489,8 @@
}
}
- /**
- * Set the density at which this drawable will be rendered.
- *
- * @param metrics The display metrics for this drawable.
- */
- private void setTargetDensity(DisplayMetrics metrics) {
- if (mDensity != metrics.density) {
- mDensity = metrics.density;
- invalidateSelf(false);
- }
- }
-
@Override
- public void applyTheme(Theme t) {
+ public void applyTheme(@NonNull Theme t) {
super.applyTheme(t);
final RippleState state = mState;
@@ -515,6 +503,7 @@
R.styleable.RippleDrawable);
try {
updateStateFromTypedArray(a);
+ verifyRequiredAttributes(a);
} catch (XmlPullParserException e) {
throw new RuntimeException(e);
} finally {
@@ -1002,6 +991,24 @@
mTouchThemeAttrs = origs.mTouchThemeAttrs;
mColor = origs.mColor;
mMaxRadius = origs.mMaxRadius;
+
+ if (origs.mDensity != mDensity) {
+ applyDensityScaling(orig.mDensity, mDensity);
+ }
+ }
+ }
+
+ @Override
+ protected void onDensityChanged(int sourceDensity, int targetDensity) {
+ super.onDensityChanged(sourceDensity, targetDensity);
+
+ applyDensityScaling(sourceDensity, targetDensity);
+ }
+
+ private void applyDensityScaling(int sourceDensity, int targetDensity) {
+ if (mMaxRadius != RADIUS_AUTO) {
+ mMaxRadius = Drawable.scaleFromDensity(
+ mMaxRadius, sourceDensity, targetDensity, true);
}
}
@@ -1032,16 +1039,13 @@
private RippleDrawable(RippleState state, Resources res) {
mState = new RippleState(state, this, res);
mLayerState = mState;
+ mDensity = Drawable.resolveDensity(res, mState.mDensity);
if (mState.mNum > 0) {
ensurePadding();
refreshPadding();
}
- if (res != null) {
- mDensity = res.getDisplayMetrics().density;
- }
-
updateLocalState();
}
diff --git a/graphics/java/android/graphics/drawable/RippleForeground.java b/graphics/java/android/graphics/drawable/RippleForeground.java
index c660846..829733e 100644
--- a/graphics/java/android/graphics/drawable/RippleForeground.java
+++ b/graphics/java/android/graphics/drawable/RippleForeground.java
@@ -168,7 +168,7 @@
}
final int duration = (int)
- (1000 * Math.sqrt(mTargetRadius / WAVE_TOUCH_DOWN_ACCELERATION * mDensity) + 0.5);
+ (1000 * Math.sqrt(mTargetRadius / WAVE_TOUCH_DOWN_ACCELERATION * mDensityScale) + 0.5);
final ObjectAnimator tweenRadius = ObjectAnimator.ofFloat(this, TWEEN_RADIUS, 1);
tweenRadius.setAutoCancel(true);
@@ -204,7 +204,7 @@
private int getRadiusExitDuration() {
final float remainingRadius = mTargetRadius - getCurrentRadius();
return (int) (1000 * Math.sqrt(remainingRadius / (WAVE_TOUCH_UP_ACCELERATION
- + WAVE_TOUCH_DOWN_ACCELERATION) * mDensity) + 0.5);
+ + WAVE_TOUCH_DOWN_ACCELERATION) * mDensityScale) + 0.5);
}
private float getCurrentRadius() {
diff --git a/graphics/java/android/graphics/drawable/RotateDrawable.java b/graphics/java/android/graphics/drawable/RotateDrawable.java
index 036a078..1531ba2 100644
--- a/graphics/java/android/graphics/drawable/RotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/RotateDrawable.java
@@ -21,6 +21,8 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.content.res.Resources;
@@ -58,22 +60,46 @@
* Creates a new rotating drawable with no wrapped drawable.
*/
public RotateDrawable() {
- this(new RotateState(null), null);
+ this(new RotateState(null, null), null);
}
@Override
- public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
+ public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs, @Nullable Theme theme)
throws XmlPullParserException, IOException {
final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.RotateDrawable);
- super.inflateWithAttributes(r, parser, a, R.styleable.RotateDrawable_visible);
+
+ // Inflation will advance the XmlPullParser and AttributeSet.
+ super.inflate(r, parser, attrs, theme);
updateStateFromTypedArray(a);
- inflateChildDrawable(r, parser, attrs, theme);
verifyRequiredAttributes(a);
a.recycle();
}
- private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException {
+ @Override
+ public void applyTheme(@NonNull Theme t) {
+ super.applyTheme(t);
+
+ final RotateState state = mState;
+ if (state == null) {
+ return;
+ }
+
+ if (state.mThemeAttrs != null) {
+ final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.RotateDrawable);
+ try {
+ updateStateFromTypedArray(a);
+ verifyRequiredAttributes(a);
+ } catch (XmlPullParserException e) {
+ throw new RuntimeException(e);
+ } finally {
+ a.recycle();
+ }
+ }
+ }
+
+ private void verifyRequiredAttributes(@NonNull TypedArray a) throws XmlPullParserException {
// If we're not waiting on a theme, verify required attributes.
if (getDrawable() == null && (mState.mThemeAttrs == null
|| mState.mThemeAttrs[R.styleable.RotateDrawable_drawable] == 0)) {
@@ -83,11 +109,14 @@
}
}
- @Override
- void updateStateFromTypedArray(TypedArray a) {
- super.updateStateFromTypedArray(a);
-
+ private void updateStateFromTypedArray(@NonNull TypedArray a) {
final RotateState state = mState;
+ if (state == null) {
+ return;
+ }
+
+ // Account for any configuration changes.
+ state.mChangingConfigurations |= a.getChangingConfigurations();
// Extract the theme attributes, if any.
state.mThemeAttrs = a.extractThemeAttrs();
@@ -109,35 +138,6 @@
state.mToDegrees = a.getFloat(
R.styleable.RotateDrawable_toDegrees, state.mToDegrees);
state.mCurrentDegrees = state.mFromDegrees;
-
- final Drawable dr = a.getDrawable(R.styleable.RotateDrawable_drawable);
- if (dr != null) {
- setDrawable(dr);
- }
- }
-
- @Override
- public void applyTheme(Theme t) {
- final RotateState state = mState;
- if (state == null) {
- return;
- }
-
- if (state.mThemeAttrs != null) {
- final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.RotateDrawable);
- try {
- updateStateFromTypedArray(a);
- verifyRequiredAttributes(a);
- } catch (XmlPullParserException e) {
- throw new RuntimeException(e);
- } finally {
- a.recycle();
- }
- }
-
- // The drawable may have changed as a result of applying the theme, so
- // apply the theme to the wrapped drawable last.
- super.applyTheme(t);
}
@Override
@@ -316,11 +316,13 @@
@Override
DrawableWrapperState mutateConstantState() {
- mState = new RotateState(mState);
+ mState = new RotateState(mState, null);
return mState;
}
static final class RotateState extends DrawableWrapper.DrawableWrapperState {
+ private int[] mThemeAttrs;
+
boolean mPivotXRel = true;
float mPivotX = 0.5f;
boolean mPivotYRel = true;
@@ -329,8 +331,8 @@
float mToDegrees = 360.0f;
float mCurrentDegrees = 0.0f;
- RotateState(RotateState orig) {
- super(orig);
+ RotateState(RotateState orig, Resources res) {
+ super(orig, res);
if (orig != null) {
mPivotXRel = orig.mPivotXRel;
diff --git a/graphics/java/android/graphics/drawable/ScaleDrawable.java b/graphics/java/android/graphics/drawable/ScaleDrawable.java
index f9206b7..f87c19a 100644
--- a/graphics/java/android/graphics/drawable/ScaleDrawable.java
+++ b/graphics/java/android/graphics/drawable/ScaleDrawable.java
@@ -21,6 +21,8 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
@@ -67,7 +69,7 @@
private ScaleState mState;
ScaleDrawable() {
- this(new ScaleState(null), null);
+ this(new ScaleState(null, null), null);
}
/**
@@ -83,7 +85,7 @@
* is at the maximum value, or -1 to not scale height
*/
public ScaleDrawable(Drawable drawable, int gravity, float scaleWidth, float scaleHeight) {
- this(new ScaleState(null), null);
+ this(new ScaleState(null, null), null);
mState.mGravity = gravity;
mState.mScaleWidth = scaleWidth;
@@ -93,20 +95,46 @@
}
@Override
- public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
+ public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs, @Nullable Theme theme)
throws XmlPullParserException, IOException {
+ final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.ScaleDrawable);
+
+ // Inflation will advance the XmlPullParser and AttributeSet.
super.inflate(r, parser, attrs, theme);
- final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.ScaleDrawable);
updateStateFromTypedArray(a);
- inflateChildDrawable(r, parser, attrs, theme);
verifyRequiredAttributes(a);
a.recycle();
updateLocalState();
}
- private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException {
+ @Override
+ public void applyTheme(@NonNull Theme t) {
+ super.applyTheme(t);
+
+ final ScaleState state = mState;
+ if (state == null) {
+ return;
+ }
+
+ if (state.mThemeAttrs != null) {
+ final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.ScaleDrawable);
+ try {
+ updateStateFromTypedArray(a);
+ verifyRequiredAttributes(a);
+ } catch (XmlPullParserException e) {
+ throw new RuntimeException(e);
+ } finally {
+ a.recycle();
+ }
+ }
+
+ updateLocalState();
+ }
+
+ private void verifyRequiredAttributes(@NonNull TypedArray a) throws XmlPullParserException {
// If we're not waiting on a theme, verify required attributes.
if (getDrawable() == null && (mState.mThemeAttrs == null
|| mState.mThemeAttrs[R.styleable.ScaleDrawable_drawable] == 0)) {
@@ -116,11 +144,18 @@
}
}
- @Override
- void updateStateFromTypedArray(TypedArray a) {
- super.updateStateFromTypedArray(a);
-
+ private void updateStateFromTypedArray(@NonNull TypedArray a) {
final ScaleState state = mState;
+ if (state == null) {
+ return;
+ }
+
+ // Account for any configuration changes.
+ state.mChangingConfigurations |= a.getChangingConfigurations();
+
+ // Extract the theme attributes, if any.
+ state.mThemeAttrs = a.extractThemeAttrs();
+
state.mScaleWidth = getPercent(a,
R.styleable.ScaleDrawable_scaleWidth, state.mScaleWidth);
state.mScaleHeight = getPercent(a,
@@ -131,11 +166,6 @@
R.styleable.ScaleDrawable_useIntrinsicSizeAsMinimum, state.mUseIntrinsicSizeAsMin);
state.mInitialLevel = a.getInt(
R.styleable.ScaleDrawable_level, state.mInitialLevel);
-
- final Drawable dr = a.getDrawable(R.styleable.ScaleDrawable_drawable);
- if (dr != null) {
- setDrawable(dr);
- }
}
private static float getPercent(TypedArray a, int index, float defaultValue) {
@@ -157,33 +187,6 @@
}
@Override
- public void applyTheme(Theme t) {
- final ScaleState state = mState;
- if (state == null) {
- return;
- }
-
- if (state.mThemeAttrs != null) {
- final TypedArray a = t.resolveAttributes(
- state.mThemeAttrs, R.styleable.ScaleDrawable);
- try {
- updateStateFromTypedArray(a);
- verifyRequiredAttributes(a);
- } catch (XmlPullParserException e) {
- throw new RuntimeException(e);
- } finally {
- a.recycle();
- }
- }
-
- // The drawable may have changed as a result of applying the theme, so
- // apply the theme to the wrapped drawable last.
- super.applyTheme(t);
-
- updateLocalState();
- }
-
- @Override
public void draw(Canvas canvas) {
final Drawable d = getDrawable();
if (d != null && d.getLevel() != 0) {
@@ -243,7 +246,7 @@
@Override
DrawableWrapperState mutateConstantState() {
- mState = new ScaleState(mState);
+ mState = new ScaleState(mState, null);
return mState;
}
@@ -251,14 +254,16 @@
/** Constant used to disable scaling for a particular dimension. */
private static final float DO_NOT_SCALE = -1.0f;
+ private int[] mThemeAttrs;
+
float mScaleWidth = DO_NOT_SCALE;
float mScaleHeight = DO_NOT_SCALE;
int mGravity = Gravity.LEFT;
boolean mUseIntrinsicSizeAsMin = false;
int mInitialLevel = 0;
- ScaleState(ScaleState orig) {
- super(orig);
+ ScaleState(ScaleState orig, Resources res) {
+ super(orig, res);
if (orig != null) {
mScaleWidth = orig.mScaleWidth;
diff --git a/graphics/java/android/graphics/drawable/StateListDrawable.java b/graphics/java/android/graphics/drawable/StateListDrawable.java
index 758410a..64a9eb5 100644
--- a/graphics/java/android/graphics/drawable/StateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/StateListDrawable.java
@@ -110,6 +110,7 @@
final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.StateListDrawable);
super.inflateWithAttributes(r, parser, a, R.styleable.StateListDrawable_visible);
updateStateFromTypedArray(a);
+ updateDensity(r);
a.recycle();
inflateChildElements(r, parser, attrs, theme);
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index 1105ca4..eee9b24 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -222,11 +222,14 @@
// caching the bitmap by default is allowed.
private boolean mAllowCaching = true;
+ /** The density of the display on which this drawable will be rendered. */
+ private int mTargetDensity;
+
// Given the virtual display setup, the dpi can be different than the inflation's dpi.
// Therefore, we need to scale the values we got from the getDimension*().
private int mDpiScaledWidth = 0;
private int mDpiScaledHeight = 0;
- private Insets mDpiScaleInsets = Insets.NONE;
+ private Insets mDpiScaledInsets = Insets.NONE;
// Temp variable, only for saving "new" operation at the draw() time.
private final float[] mTmpFloats = new float[9];
@@ -234,17 +237,31 @@
private final Rect mTmpBounds = new Rect();
public VectorDrawable() {
- this(null, null);
+ this(new VectorDrawableState(), null);
}
+ /**
+ * The one constructor to rule them all. This is called by all public
+ * constructors to set the state and initialize local properties.
+ */
private VectorDrawable(@NonNull VectorDrawableState state, @Nullable Resources res) {
- if (state == null) {
- mVectorState = new VectorDrawableState();
- } else {
- mVectorState = state;
- mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
- }
- updateDimensionInfo(res, false);
+ mVectorState = state;
+
+ updateLocalState(res);
+ }
+
+ /**
+ * Initializes local dynamic properties from state. This should be called
+ * after significant state changes, e.g. from the One True Constructor and
+ * after inflating or applying a theme.
+ *
+ * @param res resources of the context in which the drawable will be
+ * displayed, or {@code null} to use the constant state defaults
+ */
+ private void updateLocalState(Resources res) {
+ mTargetDensity = Drawable.resolveDensity(res, mVectorState.mVPathRenderer.mSourceDensity);
+ mTintFilter = updateTintFilter(mTintFilter, mVectorState.mTint, mVectorState.mTintMode);
+ computeVectorSize();
}
@Override
@@ -416,55 +433,38 @@
/** @hide */
@Override
public Insets getOpticalInsets() {
- return mDpiScaleInsets;
+ return mDpiScaledInsets;
}
/*
- * Update the VectorDrawable dimension since the res can be in different Dpi now.
- * Basically, when a new instance is created or getDimension() is called, we should update
- * the current VectorDrawable's dimension information.
- * Only after updateStateFromTypedArray() is called, we should called this and update the
- * constant state's dpi info, i.e. updateConstantStateDensity == true.
+ * Update local dimensions to adjust for a target density that may differ
+ * from the source density against which the constant state was loaded.
*/
- void updateDimensionInfo(@Nullable Resources res, boolean updateConstantStateDensity) {
- if (res != null) {
- final int densityDpi = res.getDisplayMetrics().densityDpi;
- final int targetDensity = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
+ void computeVectorSize() {
+ final VPathRenderer pathRenderer = mVectorState.mVPathRenderer;
+ final Insets opticalInsets = pathRenderer.mOpticalInsets;
- if (updateConstantStateDensity) {
- mVectorState.mVPathRenderer.mTargetDensity = targetDensity;
- } else {
- final int constantStateDensity = mVectorState.mVPathRenderer.mTargetDensity;
- if (targetDensity != constantStateDensity && constantStateDensity != 0) {
- mDpiScaledWidth = Bitmap.scaleFromDensity(
- (int) mVectorState.mVPathRenderer.mBaseWidth, constantStateDensity,
- targetDensity);
- mDpiScaledHeight = Bitmap.scaleFromDensity(
- (int) mVectorState.mVPathRenderer.mBaseHeight,constantStateDensity,
- targetDensity);
- final int left = Bitmap.scaleFromDensity(
- mVectorState.mVPathRenderer.mOpticalInsets.left, constantStateDensity,
- targetDensity);
- final int right = Bitmap.scaleFromDensity(
- mVectorState.mVPathRenderer.mOpticalInsets.right, constantStateDensity,
- targetDensity);
- final int top = Bitmap.scaleFromDensity(
- mVectorState.mVPathRenderer.mOpticalInsets.top, constantStateDensity,
- targetDensity);
- final int bottom = Bitmap.scaleFromDensity(
- mVectorState.mVPathRenderer.mOpticalInsets.bottom, constantStateDensity,
- targetDensity);
- mDpiScaleInsets = Insets.of(left, top, right, bottom);
- return;
- }
- }
+ final int sourceDensity = pathRenderer.mSourceDensity;
+ final int targetDensity = mTargetDensity;
+ if (targetDensity != sourceDensity) {
+ mDpiScaledWidth = Drawable.scaleFromDensity(
+ (int) pathRenderer.mBaseWidth, sourceDensity, targetDensity, true);
+ mDpiScaledHeight = Drawable.scaleFromDensity(
+ (int) pathRenderer.mBaseHeight,sourceDensity, targetDensity, true);
+ final int left = Drawable.scaleFromDensity(
+ opticalInsets.left, sourceDensity, targetDensity, false);
+ final int right = Drawable.scaleFromDensity(
+ opticalInsets.right, sourceDensity, targetDensity, false);
+ final int top = Drawable.scaleFromDensity(
+ opticalInsets.top, sourceDensity, targetDensity, false);
+ final int bottom = Drawable.scaleFromDensity(
+ opticalInsets.bottom, sourceDensity, targetDensity, false);
+ mDpiScaledInsets = Insets.of(left, top, right, bottom);
+ } else {
+ mDpiScaledWidth = (int) pathRenderer.mBaseWidth;
+ mDpiScaledHeight = (int) pathRenderer.mBaseHeight;
+ mDpiScaledInsets = opticalInsets;
}
- // For all the other cases, like either res is null, constant state is not initialized or
- // target density is the same as the constant state, we will just use the constant state
- // dimensions.
- mDpiScaledWidth = (int) mVectorState.mVPathRenderer.mBaseWidth;
- mDpiScaledHeight = (int) mVectorState.mVPathRenderer.mBaseHeight;
- mDpiScaleInsets = mVectorState.mVPathRenderer.mOpticalInsets;
}
@Override
@@ -487,7 +487,6 @@
try {
state.mCacheDirty = true;
updateStateFromTypedArray(a);
- updateDimensionInfo(t.getResources(), true /* update constant state */);
} catch (XmlPullParserException e) {
throw new RuntimeException(e);
} finally {
@@ -505,8 +504,8 @@
path.applyTheme(t);
}
- // Update local state.
- mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
+ // Update local properties.
+ updateLocalState(t.getResources());
}
/**
@@ -579,8 +578,8 @@
state.mCacheDirty = true;
inflateInternal(res, parser, attrs, theme);
- mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
- updateDimensionInfo(res, true /* update constant state */);
+ // Update local properties.
+ updateLocalState(res);
}
private void updateStateFromTypedArray(TypedArray a) throws XmlPullParserException {
@@ -593,6 +592,13 @@
// Extract the theme attributes, if any.
state.mThemeAttrs = a.extractThemeAttrs();
+ // The density may have changed since the last update (if any). Any
+ // dimension-type attributes will need their default values scaled.
+ final int targetDensity = Drawable.resolveDensity(a.getResources(), 0);
+ final int sourceDensity = pathRenderer.mSourceDensity;
+ final float densityScale = targetDensity / (float) sourceDensity;
+ pathRenderer.mSourceDensity = targetDensity;
+
final int tintMode = a.getInt(R.styleable.VectorDrawable_tintMode, -1);
if (tintMode != -1) {
state.mTintMode = Drawable.parseTintMode(tintMode, Mode.SRC_IN);
@@ -620,9 +626,11 @@
}
pathRenderer.mBaseWidth = a.getDimension(
- R.styleable.VectorDrawable_width, pathRenderer.mBaseWidth);
+ R.styleable.VectorDrawable_width,
+ pathRenderer.mBaseWidth * densityScale);
pathRenderer.mBaseHeight = a.getDimension(
- R.styleable.VectorDrawable_height, pathRenderer.mBaseHeight);
+ R.styleable.VectorDrawable_height,
+ pathRenderer.mBaseHeight * densityScale);
if (pathRenderer.mBaseWidth <= 0) {
throw new XmlPullParserException(a.getPositionDescription() +
@@ -632,14 +640,18 @@
"<vector> tag requires height > 0");
}
- final int insetLeft = a.getDimensionPixelSize(
- R.styleable.VectorDrawable_opticalInsetLeft, pathRenderer.mOpticalInsets.left);
- final int insetTop = a.getDimensionPixelSize(
- R.styleable.VectorDrawable_opticalInsetTop, pathRenderer.mOpticalInsets.top);
- final int insetRight = a.getDimensionPixelSize(
- R.styleable.VectorDrawable_opticalInsetRight, pathRenderer.mOpticalInsets.right);
- final int insetBottom = a.getDimensionPixelSize(
- R.styleable.VectorDrawable_opticalInsetBottom, pathRenderer.mOpticalInsets.bottom);
+ final int insetLeft = a.getDimensionPixelOffset(
+ R.styleable.VectorDrawable_opticalInsetLeft,
+ (int) (pathRenderer.mOpticalInsets.left * densityScale));
+ final int insetTop = a.getDimensionPixelOffset(
+ R.styleable.VectorDrawable_opticalInsetTop,
+ (int) (pathRenderer.mOpticalInsets.top * densityScale));
+ final int insetRight = a.getDimensionPixelOffset(
+ R.styleable.VectorDrawable_opticalInsetRight,
+ (int) (pathRenderer.mOpticalInsets.right * densityScale));
+ final int insetBottom = a.getDimensionPixelOffset(
+ R.styleable.VectorDrawable_opticalInsetBottom,
+ (int) (pathRenderer.mOpticalInsets.bottom * densityScale));
pathRenderer.mOpticalInsets = Insets.of(insetLeft, insetTop, insetRight, insetBottom);
final float alphaInFloat = a.getFloat(R.styleable.VectorDrawable_alpha,
@@ -917,7 +929,7 @@
int mRootAlpha = 0xFF;
String mRootName = null;
- int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT;
+ int mSourceDensity = DisplayMetrics.DENSITY_DEFAULT;
final ArrayMap<String, Object> mVGTargetsMap = new ArrayMap<>();
@@ -954,7 +966,7 @@
mChangingConfigurations = copy.mChangingConfigurations;
mRootAlpha = copy.mRootAlpha;
mRootName = copy.mRootName;
- mTargetDensity = copy.mTargetDensity;
+ mSourceDensity = copy.mSourceDensity;
if (copy.mRootName != null) {
mVGTargetsMap.put(copy.mRootName, this);
}
@@ -1569,16 +1581,16 @@
public boolean onStateChange(int[] stateSet) {
boolean changed = false;
- if (mStrokeColors != null && mStrokeColors.isStateful()) {
- final int strokeColor = mStrokeColor;
- mStrokeColor = mStrokeColors.getColorForState(stateSet, strokeColor);
- changed |= strokeColor != mStrokeColor;
+ if (mStrokeColors != null) {
+ final int oldStrokeColor = mStrokeColor;
+ mStrokeColor = mStrokeColors.getColorForState(stateSet, oldStrokeColor);
+ changed |= oldStrokeColor != mStrokeColor;
}
- if (mFillColors != null && mFillColors.isStateful()) {
- final int fillColor = mFillColor;
- mFillColor = mFillColors.getColorForState(stateSet, fillColor);
- changed |= fillColor != mFillColor;
+ if (mFillColors != null) {
+ final int oldFillColor = mFillColor;
+ mFillColor = mFillColors.getColorForState(stateSet, oldFillColor);
+ changed |= oldFillColor != mFillColor;
}
return changed;
@@ -1586,8 +1598,7 @@
@Override
public boolean isStateful() {
- return mStrokeColors != null && mStrokeColors.isStateful()
- || mFillColors != null && mFillColors.isStateful();
+ return mStrokeColors != null || mFillColors != null;
}
@Override
@@ -1716,31 +1727,31 @@
final ColorStateList fillColors = a.getColorStateList(
R.styleable.VectorDrawablePath_fillColor);
if (fillColors != null) {
- mFillColors = fillColors;
+ // If the color state list isn't stateful, discard the state
+ // list and keep the default (e.g. the only) color.
+ mFillColors = fillColors.isStateful() ? fillColors : null;
mFillColor = fillColors.getDefaultColor();
}
final ColorStateList strokeColors = a.getColorStateList(
R.styleable.VectorDrawablePath_strokeColor);
if (strokeColors != null) {
- mStrokeColors = strokeColors;
+ // If the color state list isn't stateful, discard the state
+ // list and keep the default (e.g. the only) color.
+ mStrokeColors = strokeColors.isStateful() ? strokeColors : null;
mStrokeColor = strokeColors.getDefaultColor();
}
- mFillAlpha = a.getFloat(R.styleable.VectorDrawablePath_fillAlpha,
- mFillAlpha);
+ mFillAlpha = a.getFloat(R.styleable.VectorDrawablePath_fillAlpha, mFillAlpha);
mStrokeLineCap = getStrokeLineCap(a.getInt(
R.styleable.VectorDrawablePath_strokeLineCap, -1), mStrokeLineCap);
mStrokeLineJoin = getStrokeLineJoin(a.getInt(
R.styleable.VectorDrawablePath_strokeLineJoin, -1), mStrokeLineJoin);
mStrokeMiterlimit = a.getFloat(
R.styleable.VectorDrawablePath_strokeMiterLimit, mStrokeMiterlimit);
- mStrokeAlpha = a.getFloat(R.styleable.VectorDrawablePath_strokeAlpha,
- mStrokeAlpha);
- mStrokeWidth = a.getFloat(R.styleable.VectorDrawablePath_strokeWidth,
- mStrokeWidth);
- mTrimPathEnd = a.getFloat(R.styleable.VectorDrawablePath_trimPathEnd,
- mTrimPathEnd);
+ mStrokeAlpha = a.getFloat(R.styleable.VectorDrawablePath_strokeAlpha, mStrokeAlpha);
+ mStrokeWidth = a.getFloat(R.styleable.VectorDrawablePath_strokeWidth, mStrokeWidth);
+ mTrimPathEnd = a.getFloat(R.styleable.VectorDrawablePath_trimPathEnd, mTrimPathEnd);
mTrimPathOffset = a.getFloat(
R.styleable.VectorDrawablePath_trimPathOffset, mTrimPathOffset);
mTrimPathStart = a.getFloat(
diff --git a/libs/androidfw/tests/AppAsLib_test.cpp b/libs/androidfw/tests/AppAsLib_test.cpp
index bdb0c3d..8489acf 100644
--- a/libs/androidfw/tests/AppAsLib_test.cpp
+++ b/libs/androidfw/tests/AppAsLib_test.cpp
@@ -16,7 +16,6 @@
#include <androidfw/ResourceTypes.h>
-#include "data/basic/R.h"
#include "data/appaslib/R.h"
#include <gtest/gtest.h>
@@ -25,29 +24,45 @@
namespace {
-#include "data/basic/basic_arsc.h"
+#include "data/appaslib/appaslib_arsc.h"
+#include "data/appaslib/appaslib_lib_arsc.h"
+// This tests the app resources loaded as app.
TEST(AppAsLibTest, loadedAsApp) {
ResTable table;
- ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+ ASSERT_EQ(NO_ERROR, table.add(appaslib_arsc, appaslib_arsc_len));
Res_value val;
- ssize_t block = table.getResource(base::R::integer::number2, &val);
+ ssize_t block = table.getResource(appaslib::R::app::integer::number1, &val);
ASSERT_GE(block, 0);
ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
- ASSERT_EQ(base::R::array::integerArray1, val.data);
+ ASSERT_EQ(appaslib::R::app::array::integerArray1, val.data);
}
+// This tests the app resources loaded as shared-lib.
TEST(AppAsLibTest, loadedAsSharedLib) {
ResTable table;
// Load as shared library.
- ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len, NULL, 0, -1, false, true));
+ ASSERT_EQ(NO_ERROR, table.add(appaslib_arsc, appaslib_arsc_len, NULL, 0, -1, false, true));
Res_value val;
- ssize_t block = table.getResource(appaslib::R::integer::number2, &val);
+ ssize_t block = table.getResource(appaslib::R::lib::integer::number1, &val);
ASSERT_GE(block, 0);
ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
- ASSERT_EQ(appaslib::R::array::integerArray1, val.data);
+ ASSERT_EQ(appaslib::R::lib::array::integerArray1, val.data);
+}
+
+// This tests the shared-lib loaded with appAsLib as true.
+TEST(AppAsLibTest, loadedSharedLib) {
+ ResTable table;
+ // Load shared library with appAsLib as true.
+ ASSERT_EQ(NO_ERROR, table.add(appaslib_lib_arsc, appaslib_lib_arsc_len, NULL, 0, -1, false, true));
+
+ Res_value val;
+ ssize_t block = table.getResource(appaslib::R::lib::integer::number1, &val);
+ ASSERT_GE(block, 0);
+ ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
+ ASSERT_EQ(appaslib::R::lib::array::integerArray1, val.data);
}
}
diff --git a/libs/androidfw/tests/data/appaslib/AndroidManifest.xml b/libs/androidfw/tests/data/appaslib/AndroidManifest.xml
new file mode 100644
index 0000000..e00045b
--- /dev/null
+++ b/libs/androidfw/tests/data/appaslib/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.test.basic">
+ <application>
+ </application>
+</manifest>
diff --git a/libs/androidfw/tests/data/appaslib/R.h b/libs/androidfw/tests/data/appaslib/R.h
index f89d4bf..3af921a 100644
--- a/libs/androidfw/tests/data/appaslib/R.h
+++ b/libs/androidfw/tests/data/appaslib/R.h
@@ -19,19 +19,33 @@
namespace appaslib {
namespace R {
-
+namespace lib {
namespace integer {
enum {
- number2 = 0x02040001, // default
+ number1 = 0x02020000, // default
};
}
namespace array {
enum {
- integerArray1 = 0x02060000, // default
+ integerArray1 = 0x02030000, // default
+ };
+}
+} // namespace lib
+
+namespace app {
+namespace integer {
+ enum {
+ number1 = 0x7f020000, // default
};
}
+namespace array {
+ enum {
+ integerArray1 = 0x7f030000, // default
+ };
+}
+} // namespace app
} // namespace R
} // namespace appaslib
diff --git a/libs/androidfw/tests/data/appaslib/appaslib_arsc.h b/libs/androidfw/tests/data/appaslib/appaslib_arsc.h
new file mode 100644
index 0000000..be176ab
--- /dev/null
+++ b/libs/androidfw/tests/data/appaslib/appaslib_arsc.h
@@ -0,0 +1,68 @@
+unsigned char appaslib_arsc[] = {
+ 0x02, 0x00, 0x0c, 0x00, 0x04, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xdc, 0x02, 0x00, 0x00,
+ 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00,
+ 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00,
+ 0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
+ 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, 0x69, 0x00, 0x63, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x74, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x54, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00,
+ 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00, 0x07, 0x00, 0x69, 0x00,
+ 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, 0x72, 0x00, 0x72, 0x00, 0x61, 0x00,
+ 0x79, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x54, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x12, 0x00, 0x00, 0x00, 0x07, 0x00, 0x6e, 0x00, 0x75, 0x00, 0x6d, 0x00,
+ 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x31, 0x00, 0x00, 0x00, 0x0d, 0x00,
+ 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00,
+ 0x72, 0x00, 0x41, 0x00, 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00,
+ 0x31, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x48, 0x00, 0x5c, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
+ 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x7f,
+ 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x48, 0x00,
+ 0x80, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x4c, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10,
+ 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10,
+ 0x03, 0x00, 0x00, 0x00
+};
+unsigned int appaslib_arsc_len = 772;
diff --git a/libs/androidfw/tests/data/appaslib/appaslib_lib_arsc.h b/libs/androidfw/tests/data/appaslib/appaslib_lib_arsc.h
new file mode 100644
index 0000000..099285a
--- /dev/null
+++ b/libs/androidfw/tests/data/appaslib/appaslib_lib_arsc.h
@@ -0,0 +1,68 @@
+unsigned char appaslib_lib_arsc[] = {
+ 0x02, 0x00, 0x0c, 0x00, 0x04, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xdc, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00,
+ 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00,
+ 0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
+ 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, 0x69, 0x00, 0x63, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x74, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x54, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00,
+ 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00, 0x07, 0x00, 0x69, 0x00,
+ 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, 0x72, 0x00, 0x72, 0x00, 0x61, 0x00,
+ 0x79, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x54, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x12, 0x00, 0x00, 0x00, 0x07, 0x00, 0x6e, 0x00, 0x75, 0x00, 0x6d, 0x00,
+ 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x31, 0x00, 0x00, 0x00, 0x0d, 0x00,
+ 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00,
+ 0x72, 0x00, 0x41, 0x00, 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00,
+ 0x31, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x48, 0x00, 0x5c, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
+ 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x03, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x48, 0x00,
+ 0x80, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x4c, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10,
+ 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10,
+ 0x03, 0x00, 0x00, 0x00
+};
+unsigned int appaslib_lib_arsc_len = 772;
diff --git a/libs/androidfw/tests/data/appaslib/build b/libs/androidfw/tests/data/appaslib/build
new file mode 100755
index 0000000..e4bd88b
--- /dev/null
+++ b/libs/androidfw/tests/data/appaslib/build
@@ -0,0 +1,28 @@
+#!/bin/bash
+#
+# 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.
+#
+
+PATH_TO_FRAMEWORK_RES=$(gettop)/prebuilts/sdk/current/android.jar
+
+aapt package -M AndroidManifest.xml -S res -I $PATH_TO_FRAMEWORK_RES -F bundle.apk -f && \
+unzip bundle.apk resources.arsc && \
+mv resources.arsc appaslib.arsc && \
+xxd -i appaslib.arsc > appaslib_arsc.h && \
+aapt package -M AndroidManifest.xml -S res -I $PATH_TO_FRAMEWORK_RES -F bundle.apk -f --shared-lib && \
+unzip bundle.apk resources.arsc && \
+mv resources.arsc appaslib_lib.arsc && \
+xxd -i appaslib_lib.arsc > appaslib_lib_arsc.h \
+
diff --git a/libs/androidfw/tests/data/appaslib/res/values/values.xml b/libs/androidfw/tests/data/appaslib/res/values/values.xml
new file mode 100644
index 0000000..39b99a6
--- /dev/null
+++ b/libs/androidfw/tests/data/appaslib/res/values/values.xml
@@ -0,0 +1,24 @@
+<?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.
+-->
+
+<resources>
+ <integer name="number1">@array/integerArray1</integer>
+ <integer-array name="integerArray1">
+ <item>1</item>
+ <item>2</item>
+ <item>3</item>
+ </integer-array>
+</resources>
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 8011b59..5cdd723 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -2,6 +2,8 @@
include $(CLEAR_VARS)
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+HWUI_NEW_OPS := false
+
hwui_src_files := \
font/CacheTexture.cpp \
font/Font.cpp \
@@ -25,6 +27,7 @@
utils/LinearAllocator.cpp \
utils/NinePatchImpl.cpp \
utils/StringUtils.cpp \
+ utils/TestWindowContext.cpp \
AmbientShadow.cpp \
AnimationContext.cpp \
Animator.cpp \
@@ -36,6 +39,7 @@
DamageAccumulator.cpp \
DeferredDisplayList.cpp \
DeferredLayerUpdater.cpp \
+ DeviceInfo.cpp \
DisplayList.cpp \
DisplayListCanvas.cpp \
Dither.cpp \
@@ -53,12 +57,14 @@
Layer.cpp \
LayerCache.cpp \
LayerRenderer.cpp \
+ LayerUpdateQueue.cpp \
Matrix.cpp \
OpenGLRenderer.cpp \
Patch.cpp \
PatchCache.cpp \
PathCache.cpp \
PathTessellator.cpp \
+ PathParser.cpp \
PixelBuffer.cpp \
Program.cpp \
ProgramCache.cpp \
@@ -77,13 +83,27 @@
TextDropShadowCache.cpp \
Texture.cpp \
TextureCache.cpp \
+ VectorDrawablePath.cpp \
protos/hwui.proto
hwui_cflags := \
-DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES \
-DATRACE_TAG=ATRACE_TAG_VIEW -DLOG_TAG=\"OpenGLRenderer\" \
- -Wall -Wno-unused-parameter -Wunreachable-code \
- -ffast-math -O3 -Werror
+ -Wall -Wno-unused-parameter -Wunreachable-code -Werror
+
+# GCC false-positives on this warning, and since we -Werror that's
+# a problem
+hwui_cflags += -Wno-free-nonheap-object
+
+ifeq (true, $(HWUI_NEW_OPS))
+ hwui_src_files += \
+ BakedOpRenderer.cpp \
+ OpReorderer.cpp \
+ RecordingCanvas.cpp
+
+ hwui_cflags += -DHWUI_NEW_OPS
+
+endif
ifndef HWUI_COMPILE_SYMBOLS
hwui_cflags += -fvisibility=hidden
@@ -101,6 +121,7 @@
endef
hwui_c_includes += \
+ external/skia/include/private \
external/skia/src/core
hwui_shared_libraries := \
@@ -141,6 +162,27 @@
include $(BUILD_STATIC_LIBRARY)
# ------------------------
+# static library null gpu
+# ------------------------
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+LOCAL_MODULE := libhwui_static_null_gpu
+LOCAL_SHARED_LIBRARIES := $(hwui_shared_libraries)
+LOCAL_CFLAGS := \
+ $(hwui_cflags) \
+ -DHWUI_NULL_GPU
+LOCAL_SRC_FILES := \
+ $(hwui_src_files) \
+ tests/nullegl.cpp \
+ tests/nullgles.cpp
+LOCAL_C_INCLUDES := $(hwui_c_includes) $(call hwui_proto_include)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(hwui_c_includes) $(call hwui_proto_include)
+
+include $(BUILD_STATIC_LIBRARY)
+
+# ------------------------
# shared library
# ------------------------
@@ -162,16 +204,27 @@
LOCAL_MODULE := hwui_unit_tests
LOCAL_MODULE_TAGS := tests
LOCAL_SHARED_LIBRARIES := $(hwui_shared_libraries)
-LOCAL_STATIC_LIBRARIES := libhwui_static
+LOCAL_STATIC_LIBRARIES := libhwui_static_null_gpu
LOCAL_CFLAGS := $(hwui_cflags)
LOCAL_SRC_FILES += \
unit_tests/CanvasStateTests.cpp \
unit_tests/ClipAreaTests.cpp \
unit_tests/DamageAccumulatorTests.cpp \
+ unit_tests/DeviceInfoTests.cpp \
+ unit_tests/FatVectorTests.cpp \
+ unit_tests/LayerUpdateQueueTests.cpp \
unit_tests/LinearAllocatorTests.cpp \
+ unit_tests/PathParserTests.cpp \
unit_tests/StringUtilsTests.cpp
+ifeq (true, $(HWUI_NEW_OPS))
+ LOCAL_SRC_FILES += \
+ unit_tests/BakedOpStateTests.cpp \
+ unit_tests/RecordingCanvasTests.cpp \
+ unit_tests/OpReordererTests.cpp
+endif
+
include $(BUILD_NATIVE_TEST)
# ------------------------
@@ -190,22 +243,8 @@
LOCAL_SHARED_LIBRARIES := $(hwui_shared_libraries)
LOCAL_CFLAGS := $(hwui_cflags)
-HWUI_NULL_GPU := false
-
-ifeq (true, $(HWUI_NULL_GPU))
- # Only need to specify the includes if we are not linking against
- # libhwui_static as libhwui_static exports the appropriate includes
- LOCAL_C_INCLUDES := $(hwui_c_includes) $(call hwui_proto_include)
-
- LOCAL_SRC_FILES := \
- $(hwui_src_files) \
- tests/nullegl.cpp \
- tests/nullgles.cpp
-
- LOCAL_CFLAGS += -DHWUI_NULL_GPU
-else
- LOCAL_WHOLE_STATIC_LIBRARIES := libhwui_static
-endif
+# set to libhwui_static_null_gpu to skip actual GL commands
+LOCAL_WHOLE_STATIC_LIBRARIES := libhwui_static
LOCAL_SRC_FILES += \
tests/TestContext.cpp \
@@ -230,10 +269,18 @@
LOCAL_CFLAGS := $(hwui_cflags)
LOCAL_C_INCLUDES += bionic/benchmarks/
-LOCAL_WHOLE_STATIC_LIBRARIES := libhwui_static
+LOCAL_WHOLE_STATIC_LIBRARIES := libhwui_static_null_gpu
LOCAL_STATIC_LIBRARIES := libbenchmark libbase
LOCAL_SRC_FILES += \
- microbench/DisplayListCanvasBench.cpp
+ microbench/DisplayListCanvasBench.cpp \
+ microbench/LinearAllocatorBench.cpp \
+ microbench/PathParserBench.cpp \
+ microbench/ShadowBench.cpp
+
+ifeq (true, $(HWUI_NEW_OPS))
+ LOCAL_SRC_FILES += \
+ microbench/OpReordererBench.cpp
+endif
include $(BUILD_EXECUTABLE)
diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp
new file mode 100644
index 0000000..2fca5ea
--- /dev/null
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -0,0 +1,299 @@
+/*
+ * 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.
+ */
+
+#include "BakedOpRenderer.h"
+
+#include "Caches.h"
+#include "Glop.h"
+#include "GlopBuilder.h"
+#include "renderstate/RenderState.h"
+#include "utils/FatVector.h"
+#include "utils/GLUtils.h"
+
+namespace android {
+namespace uirenderer {
+
+////////////////////////////////////////////////////////////////////////////////
+// OffscreenBuffer
+////////////////////////////////////////////////////////////////////////////////
+
+OffscreenBuffer::OffscreenBuffer(RenderState& renderState, Caches& caches,
+ uint32_t textureWidth, uint32_t textureHeight,
+ uint32_t viewportWidth, uint32_t viewportHeight)
+ : renderState(renderState)
+ , viewportWidth(viewportWidth)
+ , viewportHeight(viewportHeight)
+ , texture(caches) {
+ texture.width = textureWidth;
+ texture.height = textureHeight;
+
+ caches.textureState().activateTexture(0);
+ glGenTextures(1, &texture.id);
+ caches.textureState().bindTexture(GL_TEXTURE_2D, texture.id);
+
+ texture.setWrap(GL_CLAMP_TO_EDGE, false, false, GL_TEXTURE_2D);
+ // not setting filter on texture, since it's set when drawing, based on transform
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture.width, texture.height, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+}
+
+void OffscreenBuffer::updateMeshFromRegion() {
+ // avoid T-junctions as they cause artifacts in between the resultant
+ // geometry when complex transforms occur.
+ // TODO: generate the safeRegion only if necessary based on drawing transform
+ Region safeRegion = Region::createTJunctionFreeRegion(region);
+
+ size_t count;
+ const android::Rect* rects = safeRegion.getArray(&count);
+
+ const float texX = 1.0f / float(viewportWidth);
+ const float texY = 1.0f / float(viewportHeight);
+
+ FatVector<TextureVertex, 64> meshVector(count * 4); // uses heap if more than 64 vertices needed
+ TextureVertex* mesh = &meshVector[0];
+ for (size_t i = 0; i < count; i++) {
+ const android::Rect* r = &rects[i];
+
+ const float u1 = r->left * texX;
+ const float v1 = (viewportHeight - r->top) * texY;
+ const float u2 = r->right * texX;
+ const float v2 = (viewportHeight - r->bottom) * texY;
+
+ TextureVertex::set(mesh++, r->left, r->top, u1, v1);
+ TextureVertex::set(mesh++, r->right, r->top, u2, v1);
+ TextureVertex::set(mesh++, r->left, r->bottom, u1, v2);
+ TextureVertex::set(mesh++, r->right, r->bottom, u2, v2);
+ }
+ elementCount = count * 6;
+ renderState.meshState().genOrUpdateMeshBuffer(&vbo,
+ sizeof(TextureVertex) * count * 4,
+ &meshVector[0],
+ GL_DYNAMIC_DRAW); // TODO: GL_STATIC_DRAW if savelayer
+}
+
+OffscreenBuffer::~OffscreenBuffer() {
+ texture.deleteTexture();
+ renderState.meshState().deleteMeshBuffer(vbo);
+ elementCount = 0;
+ vbo = 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// BakedOpRenderer
+////////////////////////////////////////////////////////////////////////////////
+
+OffscreenBuffer* BakedOpRenderer::createOffscreenBuffer(RenderState& renderState,
+ uint32_t width, uint32_t height) {
+ // TODO: get from cache!
+ return new OffscreenBuffer(renderState, Caches::getInstance(), width, height, width, height);
+}
+
+void BakedOpRenderer::destroyOffscreenBuffer(OffscreenBuffer* offscreenBuffer) {
+ // TODO: return texture/offscreenbuffer to cache!
+ delete offscreenBuffer;
+}
+
+OffscreenBuffer* BakedOpRenderer::createLayer(uint32_t width, uint32_t height) {
+ LOG_ALWAYS_FATAL_IF(mRenderTarget.offscreenBuffer, "already has layer...");
+
+ OffscreenBuffer* buffer = createOffscreenBuffer(mRenderState, width, height);
+ startLayer(buffer);
+ return buffer;
+}
+
+void BakedOpRenderer::startLayer(OffscreenBuffer* offscreenBuffer) {
+ mRenderTarget.offscreenBuffer = offscreenBuffer;
+
+ // create and bind framebuffer
+ mRenderTarget.frameBufferId = mRenderState.genFramebuffer();
+ mRenderState.bindFramebuffer(mRenderTarget.frameBufferId);
+
+ // attach the texture to the FBO
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+ offscreenBuffer->texture.id, 0);
+ LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(), "startLayer FAILED");
+ LOG_ALWAYS_FATAL_IF(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE,
+ "framebuffer incomplete!");
+
+ // Clear the FBO
+ mRenderState.scissor().setEnabled(false);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ // Change the viewport & ortho projection
+ setViewport(offscreenBuffer->viewportWidth, offscreenBuffer->viewportHeight);
+}
+
+void BakedOpRenderer::endLayer() {
+ mRenderTarget.offscreenBuffer->updateMeshFromRegion();
+ mRenderTarget.offscreenBuffer = nullptr;
+
+ // Detach the texture from the FBO
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
+ LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(), "endLayer FAILED");
+ mRenderState.deleteFramebuffer(mRenderTarget.frameBufferId);
+ mRenderTarget.frameBufferId = -1;
+}
+
+void BakedOpRenderer::startFrame(uint32_t width, uint32_t height) {
+ mRenderState.bindFramebuffer(0);
+ setViewport(width, height);
+ mCaches.clearGarbage();
+
+ if (!mOpaque) {
+ // TODO: partial invalidate!
+ mRenderState.scissor().setEnabled(false);
+ glClear(GL_COLOR_BUFFER_BIT);
+ mHasDrawn = true;
+ }
+}
+
+void BakedOpRenderer::endFrame() {
+ mCaches.pathCache.trim();
+ mCaches.tessellationCache.trim();
+
+#if DEBUG_OPENGL
+ GLUtils::dumpGLErrors();
+#endif
+
+#if DEBUG_MEMORY_USAGE
+ mCaches.dumpMemoryUsage();
+#else
+ if (Properties::debugLevel & kDebugMemory) {
+ mCaches.dumpMemoryUsage();
+ }
+#endif
+}
+
+void BakedOpRenderer::setViewport(uint32_t width, uint32_t height) {
+ mRenderTarget.viewportWidth = width;
+ mRenderTarget.viewportHeight = height;
+ mRenderTarget.orthoMatrix.loadOrtho(width, height);
+
+ mRenderState.setViewport(width, height);
+ mRenderState.blend().syncEnabled();
+}
+
+Texture* BakedOpRenderer::getTexture(const SkBitmap* bitmap) {
+ Texture* texture = mRenderState.assetAtlas().getEntryTexture(bitmap);
+ if (!texture) {
+ return mCaches.textureCache.get(bitmap);
+ }
+ return texture;
+}
+
+void BakedOpRenderer::renderGlop(const BakedOpState& state, const Glop& glop) {
+ bool useScissor = state.computedState.clipSideFlags != OpClipSideFlags::None;
+ mRenderState.scissor().setEnabled(useScissor);
+ if (useScissor) {
+ const Rect& clip = state.computedState.clipRect;
+ mRenderState.scissor().set(clip.left, mRenderTarget.viewportHeight - clip.bottom,
+ clip.getWidth(), clip.getHeight());
+ }
+ if (mRenderTarget.offscreenBuffer) { // TODO: not with multi-draw
+ // register layer damage to draw-back region
+ const Rect& uiDirty = state.computedState.clippedBounds;
+ android::Rect dirty(uiDirty.left, uiDirty.top, uiDirty.right, uiDirty.bottom);
+ mRenderTarget.offscreenBuffer->region.orSelf(dirty);
+ }
+ mRenderState.render(glop, mRenderTarget.orthoMatrix);
+ mHasDrawn = true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// static BakedOpDispatcher methods
+////////////////////////////////////////////////////////////////////////////////
+
+void BakedOpDispatcher::onRenderNodeOp(BakedOpRenderer&, const RenderNodeOp&, const BakedOpState&) {
+ LOG_ALWAYS_FATAL("unsupported operation");
+}
+
+void BakedOpDispatcher::onBeginLayerOp(BakedOpRenderer& renderer, const BeginLayerOp& op, const BakedOpState& state) {
+ LOG_ALWAYS_FATAL("unsupported operation");
+}
+
+void BakedOpDispatcher::onEndLayerOp(BakedOpRenderer& renderer, const EndLayerOp& op, const BakedOpState& state) {
+ LOG_ALWAYS_FATAL("unsupported operation");
+}
+
+void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op, const BakedOpState& state) {
+ renderer.caches().textureState().activateTexture(0); // TODO: should this be automatic, and/or elsewhere?
+ Texture* texture = renderer.getTexture(op.bitmap);
+ if (!texture) return;
+ const AutoTexture autoCleanup(texture);
+
+ const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType)
+ ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
+ Glop glop;
+ GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
+ .setRoundRectClipState(state.roundRectClipState)
+ .setMeshTexturedUnitQuad(texture->uvMapper)
+ .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
+ .setTransform(state.computedState.transform, TransformFlags::None)
+ .setModelViewMapUnitToRectSnap(Rect(0, 0, texture->width, texture->height))
+ .build();
+ renderer.renderGlop(state, glop);
+}
+
+void BakedOpDispatcher::onRectOp(BakedOpRenderer& renderer, const RectOp& op, const BakedOpState& state) {
+ Glop glop;
+ GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
+ .setRoundRectClipState(state.roundRectClipState)
+ .setMeshUnitQuad()
+ .setFillPaint(*op.paint, state.alpha)
+ .setTransform(state.computedState.transform, TransformFlags::None)
+ .setModelViewMapUnitToRect(op.unmappedBounds)
+ .build();
+ renderer.renderGlop(state, glop);
+}
+
+void BakedOpDispatcher::onSimpleRectsOp(BakedOpRenderer& renderer, const SimpleRectsOp& op, const BakedOpState& state) {
+ Glop glop;
+ GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
+ .setRoundRectClipState(state.roundRectClipState)
+ .setMeshIndexedQuads(&op.vertices[0], op.vertexCount / 4)
+ .setFillPaint(*op.paint, state.alpha)
+ .setTransform(state.computedState.transform, TransformFlags::None)
+ .setModelViewOffsetRect(0, 0, op.unmappedBounds)
+ .build();
+ renderer.renderGlop(state, glop);
+}
+
+void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state) {
+ OffscreenBuffer* buffer = *op.layerHandle;
+
+ // TODO: extend this to handle HW layers & paint properties which
+ // reside in node.properties().layerProperties()
+ float layerAlpha = op.alpha * state.alpha;
+ Glop glop;
+ GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
+ .setRoundRectClipState(state.roundRectClipState)
+ .setMeshTexturedIndexedVbo(buffer->vbo, buffer->elementCount)
+ .setFillLayer(buffer->texture, op.colorFilter, layerAlpha, op.mode, Blend::ModeOrderSwap::NoSwap)
+ .setTransform(state.computedState.transform, TransformFlags::None)
+ .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top,
+ Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
+ .build();
+ renderer.renderGlop(state, glop);
+
+ if (op.destroy) {
+ BakedOpRenderer::destroyOffscreenBuffer(buffer);
+ }
+}
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/BakedOpRenderer.h b/libs/hwui/BakedOpRenderer.h
new file mode 100644
index 0000000..aa1e67d
--- /dev/null
+++ b/libs/hwui/BakedOpRenderer.h
@@ -0,0 +1,129 @@
+/*
+ * 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 ANDROID_HWUI_BAKED_OP_RENDERER_H
+#define ANDROID_HWUI_BAKED_OP_RENDERER_H
+
+#include "BakedOpState.h"
+#include "Matrix.h"
+
+namespace android {
+namespace uirenderer {
+
+class Caches;
+struct Glop;
+class Layer;
+class RenderState;
+
+/**
+ * Lightweight alternative to Layer. Owns the persistent state of an offscreen render target, and
+ * encompasses enough information to draw it back on screen (minus paint properties, which are held
+ * by LayerOp).
+ */
+class OffscreenBuffer {
+public:
+ OffscreenBuffer(RenderState& renderState, Caches& caches,
+ uint32_t textureWidth, uint32_t textureHeight,
+ uint32_t viewportWidth, uint32_t viewportHeight);
+ ~OffscreenBuffer();
+
+ // must be called prior to rendering, to construct/update vertex buffer
+ void updateMeshFromRegion();
+
+ RenderState& renderState;
+ uint32_t viewportWidth;
+ uint32_t viewportHeight;
+ Texture texture;
+
+ // Portion of offscreen buffer that has been drawn to. Used to minimize drawing area when
+ // drawing back to screen / parent FBO.
+ Region region;
+ GLsizei elementCount = 0;
+ GLuint vbo = 0;
+};
+
+/**
+ * Main rendering manager for a collection of work - one frame + any contained FBOs.
+ *
+ * Manages frame and FBO lifecycle, binding the GL framebuffer as appropriate. This is the only
+ * place where FBOs are bound, created, and destroyed.
+ *
+ * All rendering operations will be sent by the Dispatcher, a collection of static methods,
+ * which has intentionally limited access to the renderer functionality.
+ */
+class BakedOpRenderer {
+public:
+ BakedOpRenderer(Caches& caches, RenderState& renderState, bool opaque)
+ : mRenderState(renderState)
+ , mCaches(caches)
+ , mOpaque(opaque) {
+ }
+
+ static OffscreenBuffer* createOffscreenBuffer(RenderState& renderState,
+ uint32_t width, uint32_t height);
+ static void destroyOffscreenBuffer(OffscreenBuffer*);
+
+ RenderState& renderState() { return mRenderState; }
+ Caches& caches() { return mCaches; }
+
+ void startFrame(uint32_t width, uint32_t height);
+ void endFrame();
+ OffscreenBuffer* createLayer(uint32_t width, uint32_t height);
+ void startLayer(OffscreenBuffer* offscreenBuffer);
+ void endLayer();
+
+ Texture* getTexture(const SkBitmap* bitmap);
+
+ void renderGlop(const BakedOpState& state, const Glop& glop);
+ bool didDraw() { return mHasDrawn; }
+private:
+ void setViewport(uint32_t width, uint32_t height);
+
+ RenderState& mRenderState;
+ Caches& mCaches;
+ bool mOpaque;
+ bool mHasDrawn = false;
+
+ // render target state - setup by start/end layer/frame
+ // only valid to use in between start/end pairs.
+ struct {
+ GLuint frameBufferId = 0;
+ OffscreenBuffer* offscreenBuffer = nullptr;
+ uint32_t viewportWidth = 0;
+ uint32_t viewportHeight = 0;
+ Matrix4 orthoMatrix;
+ } mRenderTarget;
+};
+
+/**
+ * Provides all "onBitmapOp(...)" style static methods for every op type, which convert the
+ * RecordedOps and their state to Glops, and renders them with the provided BakedOpRenderer.
+ *
+ * This dispatcher is separate from the renderer so that the dispatcher / renderer interaction is
+ * minimal through public BakedOpRenderer APIs.
+ */
+class BakedOpDispatcher {
+public:
+ // Declares all "onBitmapOp(...)" style methods for every op type
+#define DISPATCH_METHOD(Type) \
+ static void on##Type(BakedOpRenderer& renderer, const Type& op, const BakedOpState& state);
+ MAP_OPS(DISPATCH_METHOD);
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_BAKED_OP_RENDERER_H
diff --git a/libs/hwui/BakedOpState.h b/libs/hwui/BakedOpState.h
new file mode 100644
index 0000000..ddb8c84
--- /dev/null
+++ b/libs/hwui/BakedOpState.h
@@ -0,0 +1,142 @@
+/*
+ * 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 ANDROID_HWUI_BAKED_OP_STATE_H
+#define ANDROID_HWUI_BAKED_OP_STATE_H
+
+#include "Matrix.h"
+#include "RecordedOp.h"
+#include "Rect.h"
+#include "Snapshot.h"
+
+namespace android {
+namespace uirenderer {
+
+namespace OpClipSideFlags {
+ enum {
+ None = 0x0,
+ Left = 0x1,
+ Top = 0x2,
+ Right = 0x4,
+ Bottom = 0x8,
+ Full = 0xF,
+ // ConservativeFull = 0x1F needed?
+ };
+}
+
+/**
+ * Holds the resolved clip, transform, and bounds of a recordedOp, when replayed with a snapshot
+ */
+class ResolvedRenderState {
+public:
+ // TODO: remove the mapRects/matrix multiply when snapshot & recorded transforms are translates
+ ResolvedRenderState(const Snapshot& snapshot, const RecordedOp& recordedOp) {
+ /* TODO: benchmark a fast path for translate-only matrices, such as:
+ if (CC_LIKELY(snapshot.transform->getType() == Matrix4::kTypeTranslate
+ && recordedOp.localMatrix.getType() == Matrix4::kTypeTranslate)) {
+ float translateX = snapshot.transform->getTranslateX() + recordedOp.localMatrix.getTranslateX();
+ float translateY = snapshot.transform->getTranslateY() + recordedOp.localMatrix.getTranslateY();
+ transform.loadTranslate(translateX, translateY, 0);
+
+ // resolvedClipRect = intersect(parentMatrix * localClip, parentClip)
+ clipRect = recordedOp.localClipRect;
+ clipRect.translate(translateX, translateY);
+ clipRect.doIntersect(snapshot.getClipRect());
+ clipRect.snapToPixelBoundaries();
+
+ // resolvedClippedBounds = intersect(resolvedMatrix * opBounds, resolvedClipRect)
+ clippedBounds = recordedOp.unmappedBounds;
+ clippedBounds.translate(translateX, translateY);
+ } ... */
+
+ // resolvedMatrix = parentMatrix * localMatrix
+ transform.loadMultiply(*snapshot.transform, recordedOp.localMatrix);
+
+ // resolvedClipRect = intersect(parentMatrix * localClip, parentClip)
+ clipRect = recordedOp.localClipRect;
+ snapshot.transform->mapRect(clipRect);
+ clipRect.doIntersect(snapshot.getRenderTargetClip());
+ clipRect.snapToPixelBoundaries();
+
+ // resolvedClippedBounds = intersect(resolvedMatrix * opBounds, resolvedClipRect)
+ clippedBounds = recordedOp.unmappedBounds;
+ transform.mapRect(clippedBounds);
+
+ if (clipRect.left > clippedBounds.left) clipSideFlags |= OpClipSideFlags::Left;
+ if (clipRect.top > clippedBounds.top) clipSideFlags |= OpClipSideFlags::Top;
+ if (clipRect.right < clippedBounds.right) clipSideFlags |= OpClipSideFlags::Right;
+ if (clipRect.bottom < clippedBounds.bottom) clipSideFlags |= OpClipSideFlags::Bottom;
+ clippedBounds.doIntersect(clipRect);
+
+ /**
+ * TODO: once we support complex clips, we may want to reject to avoid that work where
+ * possible. Should we:
+ * 1 - quickreject based on clippedBounds, quick early (duplicating logic in resolvedOp)
+ * 2 - merge stuff into tryConstruct factory method, so it can handle quickRejection
+ * and early return null in one place.
+ */
+ }
+ Matrix4 transform;
+ Rect clipRect;
+ int clipSideFlags = 0;
+ Rect clippedBounds;
+};
+
+/**
+ * Self-contained op wrapper, containing all resolved state required to draw the op.
+ *
+ * Stashed pointers within all point to longer lived objects, with no ownership implied.
+ */
+class BakedOpState {
+public:
+ static BakedOpState* tryConstruct(LinearAllocator& allocator,
+ const Snapshot& snapshot, const RecordedOp& recordedOp) {
+ BakedOpState* bakedOp = new (allocator) BakedOpState(
+ snapshot, recordedOp);
+ if (bakedOp->computedState.clippedBounds.isEmpty()) {
+ // bounds are empty, so op is rejected
+ allocator.rewindIfLastAlloc(bakedOp);
+ return nullptr;
+ }
+ return bakedOp;
+ }
+
+ static void* operator new(size_t size, LinearAllocator& allocator) {
+ return allocator.alloc(size);
+ }
+
+ // computed state:
+ const ResolvedRenderState computedState;
+
+ // simple state (straight pointer/value storage):
+ const float alpha;
+ const RoundRectClipState* roundRectClipState;
+ const ProjectionPathMask* projectionPathMask;
+ const RecordedOp* op;
+
+private:
+ BakedOpState(const Snapshot& snapshot, const RecordedOp& recordedOp)
+ : computedState(snapshot, recordedOp)
+ , alpha(snapshot.alpha)
+ , roundRectClipState(snapshot.roundRectClipState)
+ , projectionPathMask(snapshot.projectionPathMask)
+ , op(&recordedOp) {}
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_BAKED_OP_STATE_H
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index 7c63e31..94a11f1 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -23,6 +23,7 @@
#include "ShadowTessellator.h"
#include "utils/GLUtils.h"
+#include <cutils/properties.h>
#include <utils/Log.h>
#include <utils/String8.h>
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 61e958d..330dc29 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -43,7 +43,6 @@
#include <GLES3/gl3.h>
#include <utils/KeyedVector.h>
-#include <utils/Singleton.h>
#include <cutils/compiler.h>
diff --git a/libs/hwui/CanvasState.cpp b/libs/hwui/CanvasState.cpp
index eca71c6..6a6cc42 100644
--- a/libs/hwui/CanvasState.cpp
+++ b/libs/hwui/CanvasState.cpp
@@ -259,7 +259,7 @@
currentTransform()->mapRect(r);
r.snapGeometryToPixelBoundaries(snapOut);
- Rect clipRect(currentClipRect());
+ Rect clipRect(currentRenderTargetClip());
clipRect.snapToPixelBoundaries();
if (!clipRect.intersects(r)) return true;
@@ -287,7 +287,7 @@
currentTransform()->mapRect(r);
r.roundOut(); // rounded out to be conservative
- Rect clipRect(currentClipRect());
+ Rect clipRect(currentRenderTargetClip());
clipRect.snapToPixelBoundaries();
if (!clipRect.intersects(r)) return true;
diff --git a/libs/hwui/CanvasState.h b/libs/hwui/CanvasState.h
index be57f44..4709ef4 100644
--- a/libs/hwui/CanvasState.h
+++ b/libs/hwui/CanvasState.h
@@ -147,7 +147,7 @@
void setInvisible(bool value) { mSnapshot->invisible = value; }
inline const mat4* currentTransform() const { return currentSnapshot()->transform; }
- inline const Rect& currentClipRect() const { return currentSnapshot()->getClipRect(); }
+ inline const Rect& currentRenderTargetClip() const { return currentSnapshot()->getRenderTargetClip(); }
inline Region* currentRegion() const { return currentSnapshot()->region; }
inline int currentFlags() const { return currentSnapshot()->flags; }
const Vector3& currentLightCenter() const { return currentSnapshot()->getRelativeLightCenter(); }
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index 0c29a9e..a1825c5 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -528,7 +528,7 @@
int insertBatchIndex = mBatches.size();
if (!mBatches.empty()) {
if (state->mBounds.isEmpty()) {
- // don't know the bounds for op, so add to last batch and start from scratch on next op
+ // don't know the bounds for op, so create new batch and start from scratch on next op
DrawBatch* b = new DrawBatch(deferInfo);
b->add(op, state, deferInfo.opaqueOverBounds);
mBatches.push_back(b);
diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h
index 7873fbd..2d5979f 100644
--- a/libs/hwui/DeferredDisplayList.h
+++ b/libs/hwui/DeferredDisplayList.h
@@ -49,7 +49,7 @@
class DeferredDisplayState {
public:
- /** static void* operator new(size_t size); PURPOSELY OMITTED **/
+ static void* operator new(size_t size) = delete;
static void* operator new(size_t size, LinearAllocator& allocator) {
return allocator.alloc(size);
}
@@ -61,7 +61,6 @@
bool mClipValid;
Rect mClip;
int mClipSideFlags; // specifies which sides of the bounds are clipped, unclipped if cleared
- bool mClipped;
mat4 mMatrix;
float mAlpha;
const RoundRectClipState* mRoundRectClipState;
diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp
new file mode 100644
index 0000000..03b1706
--- /dev/null
+++ b/libs/hwui/DeviceInfo.cpp
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+#include <DeviceInfo.h>
+
+#include "Extensions.h"
+
+#include <GLES2/gl2.h>
+
+#include <thread>
+#include <mutex>
+
+namespace android {
+namespace uirenderer {
+
+static DeviceInfo* sDeviceInfo = nullptr;
+static std::once_flag sInitializedFlag;
+
+const DeviceInfo* DeviceInfo::get() {
+ return sDeviceInfo;
+}
+
+void DeviceInfo::initialize() {
+ std::call_once(sInitializedFlag, []() {
+ sDeviceInfo = new DeviceInfo();
+ sDeviceInfo->load();
+ });
+}
+
+void DeviceInfo::load() {
+ mExtensions.load();
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h
new file mode 100644
index 0000000..f576a4f
--- /dev/null
+++ b/libs/hwui/DeviceInfo.h
@@ -0,0 +1,54 @@
+/*
+ * 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 DEVICEINFO_H
+#define DEVICEINFO_H
+
+#include "Extensions.h"
+#include "utils/Macros.h"
+
+namespace android {
+namespace uirenderer {
+
+class DeviceInfo {
+ PREVENT_COPY_AND_ASSIGN(DeviceInfo);
+public:
+ // returns nullptr if DeviceInfo is not initialized yet
+ // Note this does not have a memory fence so it's up to the caller
+ // to use one if required. Normally this should not be necessary
+ static const DeviceInfo* get();
+
+ // only call this after GL has been initialized, or at any point if compiled
+ // with HWUI_NULL_GPU
+ static void initialize();
+
+ const Extensions& extensions() const { return mExtensions; }
+
+ int maxTextureSize() const { return mMaxTextureSize; }
+
+private:
+ DeviceInfo() {}
+ ~DeviceInfo() {}
+
+ void load();
+
+ Extensions mExtensions;
+ int mMaxTextureSize;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* DEVICEINFO_H */
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index ee51da2..59f0d7c 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -21,21 +21,38 @@
#include "Debug.h"
#include "DisplayList.h"
+#include "RenderNode.h"
+
+#if HWUI_NEW_OPS
+#include "RecordedOp.h"
+#else
#include "DisplayListOp.h"
+#endif
namespace android {
namespace uirenderer {
-DisplayListData::DisplayListData()
+DisplayList::DisplayList()
: projectionReceiveIndex(-1)
+ , stdAllocator(allocator)
+ , chunks(stdAllocator)
+ , ops(stdAllocator)
+ , children(stdAllocator)
+ , bitmapResources(stdAllocator)
+ , pathResources(stdAllocator)
+ , patchResources(stdAllocator)
+ , paints(stdAllocator)
+ , regions(stdAllocator)
+ , referenceHolders(stdAllocator)
+ , functors(stdAllocator)
, hasDrawOps(false) {
}
-DisplayListData::~DisplayListData() {
+DisplayList::~DisplayList() {
cleanupResources();
}
-void DisplayListData::cleanupResources() {
+void DisplayList::cleanupResources() {
if (CC_UNLIKELY(patchResources.size())) {
ResourceCache& resourceCache = ResourceCache::getInstance();
resourceCache.lock();
@@ -61,10 +78,10 @@
regions.clear();
}
-size_t DisplayListData::addChild(DrawRenderNodeOp* op) {
- mReferenceHolders.push_back(op->renderNode());
- size_t index = mChildren.size();
- mChildren.push_back(op);
+size_t DisplayList::addChild(NodeOpType* op) {
+ referenceHolders.push_back(op->renderNode);
+ size_t index = children.size();
+ children.push_back(op);
return index;
}
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index 0bdb816..00c4e2d 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -55,12 +55,19 @@
class Rect;
class Layer;
-class ClipRectOp;
-class SaveLayerOp;
-class SaveOp;
-class RestoreToCountOp;
+#if HWUI_NEW_OPS
+struct RecordedOp;
+struct RenderNodeOp;
+
+typedef RecordedOp BaseOpType;
+typedef RenderNodeOp NodeOpType;
+#else
class DrawRenderNodeOp;
+typedef DisplayListOp BaseOpType;
+typedef DrawRenderNodeOp NodeOpType;
+#endif
+
/**
* Holds data used in the playback a tree of DisplayLists.
*/
@@ -105,15 +112,16 @@
/**
* Data structure that holds the list of commands used in display list stream
*/
-class DisplayListData {
+class DisplayList {
friend class DisplayListCanvas;
+ friend class RecordingCanvas;
public:
struct Chunk {
- // range of included ops in DLD::displayListOps
+ // range of included ops in DisplayList::ops()
size_t beginOpIndex;
size_t endOpIndex;
- // range of included children in DLD::mChildren
+ // range of included children in DisplayList::children()
size_t beginChildIndex;
size_t endChildIndex;
@@ -121,52 +129,61 @@
bool reorderChildren;
};
- DisplayListData();
- ~DisplayListData();
-
- // pointers to all ops within display list, pointing into allocator data
- std::vector<DisplayListOp*> displayListOps;
+ DisplayList();
+ ~DisplayList();
// index of DisplayListOp restore, after which projected descendents should be drawn
int projectionReceiveIndex;
- std::vector<const SkBitmap*> bitmapResources;
- std::vector<const SkPath*> pathResources;
- std::vector<const Res_png_9patch*> patchResources;
+ const LsaVector<Chunk>& getChunks() const { return chunks; }
+ const LsaVector<BaseOpType*>& getOps() const { return ops; }
- std::vector<std::unique_ptr<const SkPaint>> paints;
- std::vector<std::unique_ptr<const SkRegion>> regions;
- Vector<Functor*> functors;
+ const LsaVector<NodeOpType*>& getChildren() const { return children; }
- const std::vector<Chunk>& getChunks() const {
- return chunks;
- }
+ const LsaVector<const SkBitmap*>& getBitmapResources() const { return bitmapResources; }
+ const LsaVector<Functor*>& getFunctors() const { return functors; }
- size_t addChild(DrawRenderNodeOp* childOp);
- const std::vector<DrawRenderNodeOp*>& children() { return mChildren; }
+ size_t addChild(NodeOpType* childOp);
+
void ref(VirtualLightRefBase* prop) {
- mReferenceHolders.push_back(prop);
+ referenceHolders.push_back(prop);
}
size_t getUsedSize() {
return allocator.usedSize();
}
bool isEmpty() {
+#if HWUI_NEW_OPS
+ return ops.empty();
+#else
return !hasDrawOps;
+#endif
}
private:
- std::vector< sp<VirtualLightRefBase> > mReferenceHolders;
-
- // list of children display lists for quick, non-drawing traversal
- std::vector<DrawRenderNodeOp*> mChildren;
-
- std::vector<Chunk> chunks;
-
- // allocator into which all ops were allocated
+ // allocator into which all ops and LsaVector arrays allocated
LinearAllocator allocator;
- bool hasDrawOps;
+ LinearStdAllocator<void*> stdAllocator;
+
+ LsaVector<Chunk> chunks;
+ LsaVector<BaseOpType*> ops;
+
+ // list of Ops referring to RenderNode children for quick, non-drawing traversal
+ LsaVector<NodeOpType*> children;
+
+ // Resources - Skia objects + 9 patches referred to by this DisplayList
+ LsaVector<const SkBitmap*> bitmapResources;
+ LsaVector<const SkPath*> pathResources;
+ LsaVector<const Res_png_9patch*> patchResources;
+ LsaVector<std::unique_ptr<const SkPaint>> paints;
+ LsaVector<std::unique_ptr<const SkRegion>> regions;
+ LsaVector< sp<VirtualLightRefBase> > referenceHolders;
+
+ // List of functors
+ LsaVector<Functor*> functors;
+
+ bool hasDrawOps; // only used if !HWUI_NEW_OPS
void cleanupResources();
};
diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp
index 77bde86..f5e5735 100644
--- a/libs/hwui/DisplayListCanvas.cpp
+++ b/libs/hwui/DisplayListCanvas.cpp
@@ -34,7 +34,7 @@
DisplayListCanvas::DisplayListCanvas(int width, int height)
: mState(*this)
, mResourceCache(ResourceCache::getInstance())
- , mDisplayListData(nullptr)
+ , mDisplayList(nullptr)
, mTranslateX(0.0f)
, mTranslateY(0.0f)
, mHasDeferredTranslate(false)
@@ -45,14 +45,14 @@
}
DisplayListCanvas::~DisplayListCanvas() {
- LOG_ALWAYS_FATAL_IF(mDisplayListData,
+ LOG_ALWAYS_FATAL_IF(mDisplayList,
"Destroyed a DisplayListCanvas during a record!");
}
void DisplayListCanvas::reset(int width, int height) {
- LOG_ALWAYS_FATAL_IF(mDisplayListData,
+ LOG_ALWAYS_FATAL_IF(mDisplayList,
"prepareDirty called a second time during a recording!");
- mDisplayListData = new DisplayListData();
+ mDisplayList = new DisplayList();
mState.initializeSaveStack(width, height,
0, 0, width, height, Vector3());
@@ -67,26 +67,26 @@
// Operations
///////////////////////////////////////////////////////////////////////////////
-DisplayListData* DisplayListCanvas::finishRecording() {
+DisplayList* DisplayListCanvas::finishRecording() {
flushRestoreToCount();
flushTranslate();
mPaintMap.clear();
mRegionMap.clear();
mPathMap.clear();
- DisplayListData* data = mDisplayListData;
- mDisplayListData = nullptr;
+ DisplayList* displayList = mDisplayList;
+ mDisplayList = nullptr;
mSkiaCanvasProxy.reset(nullptr);
- return data;
+ return displayList;
}
void DisplayListCanvas::callDrawGLFunction(Functor *functor) {
addDrawOp(new (alloc()) DrawFunctorOp(functor));
- mDisplayListData->functors.add(functor);
+ mDisplayList->functors.push_back(functor);
}
SkCanvas* DisplayListCanvas::asSkCanvas() {
- LOG_ALWAYS_FATAL_IF(!mDisplayListData,
+ LOG_ALWAYS_FATAL_IF(!mDisplayList,
"attempting to get an SkCanvas when we are not recording!");
if (!mSkiaCanvasProxy) {
mSkiaCanvasProxy.reset(new SkiaCanvasProxy(this));
@@ -219,7 +219,7 @@
void DisplayListCanvas::drawLayer(DeferredLayerUpdater* layerHandle) {
// We ref the DeferredLayerUpdater due to its thread-safe ref-counting
// semantics.
- mDisplayListData->ref(layerHandle);
+ mDisplayList->ref(layerHandle);
addDrawOp(new (alloc()) DrawLayerOp(layerHandle->backingLayer()));
}
@@ -354,13 +354,13 @@
CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom,
CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry,
CanvasPropertyPaint* paint) {
- mDisplayListData->ref(left);
- mDisplayListData->ref(top);
- mDisplayListData->ref(right);
- mDisplayListData->ref(bottom);
- mDisplayListData->ref(rx);
- mDisplayListData->ref(ry);
- mDisplayListData->ref(paint);
+ mDisplayList->ref(left);
+ mDisplayList->ref(top);
+ mDisplayList->ref(right);
+ mDisplayList->ref(bottom);
+ mDisplayList->ref(rx);
+ mDisplayList->ref(ry);
+ mDisplayList->ref(paint);
refBitmapsInShader(paint->value.getShader());
addDrawOp(new (alloc()) DrawRoundRectPropsOp(&left->value, &top->value,
&right->value, &bottom->value, &rx->value, &ry->value, &paint->value));
@@ -372,10 +372,10 @@
void DisplayListCanvas::drawCircle(CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y,
CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint) {
- mDisplayListData->ref(x);
- mDisplayListData->ref(y);
- mDisplayListData->ref(radius);
- mDisplayListData->ref(paint);
+ mDisplayList->ref(x);
+ mDisplayList->ref(y);
+ mDisplayList->ref(radius);
+ mDisplayList->ref(paint);
refBitmapsInShader(paint->value.getShader());
addDrawOp(new (alloc()) DrawCirclePropsOp(&x->value, &y->value,
&radius->value, &paint->value));
@@ -514,22 +514,26 @@
}
size_t DisplayListCanvas::addOpAndUpdateChunk(DisplayListOp* op) {
- int insertIndex = mDisplayListData->displayListOps.size();
- mDisplayListData->displayListOps.push_back(op);
+ int insertIndex = mDisplayList->ops.size();
+#if HWUI_NEW_OPS
+ LOG_ALWAYS_FATAL("unsupported");
+#else
+ mDisplayList->ops.push_back(op);
+#endif
if (mDeferredBarrierType != kBarrier_None) {
// op is first in new chunk
- mDisplayListData->chunks.emplace_back();
- DisplayListData::Chunk& newChunk = mDisplayListData->chunks.back();
+ mDisplayList->chunks.emplace_back();
+ DisplayList::Chunk& newChunk = mDisplayList->chunks.back();
newChunk.beginOpIndex = insertIndex;
newChunk.endOpIndex = insertIndex + 1;
newChunk.reorderChildren = (mDeferredBarrierType == kBarrier_OutOfOrder);
- int nextChildIndex = mDisplayListData->children().size();
+ int nextChildIndex = mDisplayList->children.size();
newChunk.beginChildIndex = newChunk.endChildIndex = nextChildIndex;
mDeferredBarrierType = kBarrier_None;
} else {
// standard case - append to existing chunk
- mDisplayListData->chunks.back().endOpIndex = insertIndex + 1;
+ mDisplayList->chunks.back().endOpIndex = insertIndex + 1;
}
return insertIndex;
}
@@ -552,22 +556,24 @@
op->setQuickRejected(rejected);
}
- mDisplayListData->hasDrawOps = true;
+ mDisplayList->hasDrawOps = true;
return flushAndAddOp(op);
}
size_t DisplayListCanvas::addRenderNodeOp(DrawRenderNodeOp* op) {
int opIndex = addDrawOp(op);
- int childIndex = mDisplayListData->addChild(op);
+#if !HWUI_NEW_OPS
+ int childIndex = mDisplayList->addChild(op);
// update the chunk's child indices
- DisplayListData::Chunk& chunk = mDisplayListData->chunks.back();
+ DisplayList::Chunk& chunk = mDisplayList->chunks.back();
chunk.endChildIndex = childIndex + 1;
- if (op->renderNode()->stagingProperties().isProjectionReceiver()) {
+ if (op->renderNode->stagingProperties().isProjectionReceiver()) {
// use staging property, since recording on UI thread
- mDisplayListData->projectionReceiveIndex = opIndex;
+ mDisplayList->projectionReceiveIndex = opIndex;
}
+#endif
return opIndex;
}
@@ -578,7 +584,7 @@
// it to the bitmap pile
SkBitmap bitmap;
SkShader::TileMode xy[2];
- if (shader->asABitmap(&bitmap, nullptr, xy) == SkShader::kDefault_BitmapType) {
+ if (shader->isABitmap(&bitmap, nullptr, xy)) {
refBitmap(bitmap);
return;
}
diff --git a/libs/hwui/DisplayListCanvas.h b/libs/hwui/DisplayListCanvas.h
index 392bb3e..fc08504 100644
--- a/libs/hwui/DisplayListCanvas.h
+++ b/libs/hwui/DisplayListCanvas.h
@@ -66,8 +66,7 @@
virtual ~DisplayListCanvas();
void reset(int width, int height);
-
- DisplayListData* finishRecording();
+ __attribute__((warn_unused_result)) DisplayList* finishRecording();
// ----------------------------------------------------------------------------
// HWUI Canvas state operations
@@ -237,7 +236,7 @@
void flushTranslate();
void flushReorderBarrier();
- LinearAllocator& alloc() { return mDisplayListData->allocator; }
+ LinearAllocator& alloc() { return mDisplayList->allocator; }
// Each method returns final index of op
size_t addOpAndUpdateChunk(DisplayListOp* op);
@@ -254,7 +253,7 @@
inline const T* refBuffer(const T* srcBuffer, int32_t count) {
if (!srcBuffer) return nullptr;
- T* dstBuffer = (T*) mDisplayListData->allocator.alloc(count * sizeof(T));
+ T* dstBuffer = (T*) mDisplayList->allocator.alloc(count * sizeof(T));
memcpy(dstBuffer, srcBuffer, count * sizeof(T));
return dstBuffer;
}
@@ -269,7 +268,7 @@
// The points/verbs within the path are refcounted so this copy operation
// is inexpensive and maintains the generationID of the original path.
const SkPath* cachedPath = new SkPath(*path);
- mDisplayListData->pathResources.push_back(cachedPath);
+ mDisplayList->pathResources.push_back(cachedPath);
return cachedPath;
}
@@ -293,7 +292,7 @@
if (cachedPaint == nullptr || *cachedPaint != *paint) {
cachedPaint = new SkPaint(*paint);
std::unique_ptr<const SkPaint> copy(cachedPaint);
- mDisplayListData->paints.push_back(std::move(copy));
+ mDisplayList->paints.push_back(std::move(copy));
// replaceValueFor() performs an add if the entry doesn't exist
mPaintMap.replaceValueFor(key, cachedPaint);
@@ -313,7 +312,7 @@
if (cachedRegion == nullptr) {
std::unique_ptr<const SkRegion> copy(new SkRegion(*region));
cachedRegion = copy.get();
- mDisplayListData->regions.push_back(std::move(copy));
+ mDisplayList->regions.push_back(std::move(copy));
// replaceValueFor() performs an add if the entry doesn't exist
mRegionMap.replaceValueFor(region, cachedRegion);
@@ -329,12 +328,12 @@
// which doesn't seem worth the extra cycles for this unlikely case.
SkBitmap* localBitmap = new (alloc()) SkBitmap(bitmap);
alloc().autoDestroy(localBitmap);
- mDisplayListData->bitmapResources.push_back(localBitmap);
+ mDisplayList->bitmapResources.push_back(localBitmap);
return localBitmap;
}
inline const Res_png_9patch* refPatch(const Res_png_9patch* patch) {
- mDisplayListData->patchResources.push_back(patch);
+ mDisplayList->patchResources.push_back(patch);
mResourceCache.incrementRefcount(patch);
return patch;
}
@@ -344,7 +343,7 @@
DefaultKeyedVector<const SkRegion*, const SkRegion*> mRegionMap;
ResourceCache& mResourceCache;
- DisplayListData* mDisplayListData;
+ DisplayList* mDisplayList;
float mTranslateX;
float mTranslateY;
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index ddfc533..772aa72 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -1396,26 +1396,28 @@
class DrawRenderNodeOp : public DrawBoundedOp {
friend class RenderNode; // grant RenderNode access to info of child
- friend class DisplayListData; // grant DisplayListData access to info of child
+ friend class DisplayList; // grant DisplayList access to info of child
+ friend class DisplayListCanvas;
+ friend class TestUtils;
public:
DrawRenderNodeOp(RenderNode* renderNode, const mat4& transformFromParent, bool clipIsSimple)
: DrawBoundedOp(0, 0, renderNode->getWidth(), renderNode->getHeight(), nullptr)
- , mRenderNode(renderNode)
+ , renderNode(renderNode)
, mRecordedWithPotentialStencilClip(!clipIsSimple || !transformFromParent.isSimple())
, mTransformFromParent(transformFromParent)
, mSkipInOrderDraw(false) {}
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
bool useQuickReject) override {
- if (mRenderNode->isRenderable() && !mSkipInOrderDraw) {
- mRenderNode->defer(deferStruct, level + 1);
+ if (renderNode->isRenderable() && !mSkipInOrderDraw) {
+ renderNode->defer(deferStruct, level + 1);
}
}
virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level,
bool useQuickReject) override {
- if (mRenderNode->isRenderable() && !mSkipInOrderDraw) {
- mRenderNode->replay(replayStruct, level + 1);
+ if (renderNode->isRenderable() && !mSkipInOrderDraw) {
+ renderNode->replay(replayStruct, level + 1);
}
}
@@ -1424,18 +1426,16 @@
}
virtual void output(int level, uint32_t logFlags) const override {
- OP_LOG("Draw RenderNode %p %s", mRenderNode, mRenderNode->getName());
- if (mRenderNode && (logFlags & kOpLogFlag_Recurse)) {
- mRenderNode->output(level + 1);
+ OP_LOG("Draw RenderNode %p %s", renderNode, renderNode->getName());
+ if (renderNode && (logFlags & kOpLogFlag_Recurse)) {
+ renderNode->output(level + 1);
}
}
virtual const char* name() override { return "DrawRenderNode"; }
- RenderNode* renderNode() { return mRenderNode; }
-
private:
- RenderNode* mRenderNode;
+ RenderNode* renderNode;
/**
* This RenderNode was drawn into a DisplayList with the canvas in a state that will likely
diff --git a/libs/hwui/Extensions.cpp b/libs/hwui/Extensions.cpp
index 06c8a21..e257715 100644
--- a/libs/hwui/Extensions.cpp
+++ b/libs/hwui/Extensions.cpp
@@ -20,6 +20,7 @@
#include "Properties.h"
#include "utils/StringUtils.h"
+#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <utils/Log.h>
@@ -34,8 +35,8 @@
#endif
-Extensions::Extensions() {
- StringCollection extensions((const char*) glGetString(GL_EXTENSIONS));
+void Extensions::load() {
+ auto extensions = StringUtils::split((const char*) glGetString(GL_EXTENSIONS));
mHasNPot = extensions.has("GL_OES_texture_npot");
mHasFramebufferFetch = extensions.has("GL_NV_shader_framebuffer_fetch");
mHasDiscardFramebuffer = extensions.has("GL_EXT_discard_framebuffer");
diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h
index 0a30d16..8ccfabd 100644
--- a/libs/hwui/Extensions.h
+++ b/libs/hwui/Extensions.h
@@ -19,11 +19,8 @@
#include <cutils/compiler.h>
-#include <utils/Singleton.h>
-#include <utils/SortedVector.h>
-#include <utils/String8.h>
-
-#include <GLES2/gl2.h>
+#include <string>
+#include <unordered_set>
namespace android {
namespace uirenderer {
@@ -32,9 +29,9 @@
// Classes
///////////////////////////////////////////////////////////////////////////////
-class ANDROID_API Extensions {
+class Extensions {
public:
- Extensions();
+ void load();
inline bool hasNPot() const { return mHasNPot; }
inline bool hasFramebufferFetch() const { return mHasFramebufferFetch; }
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
index fa166ae..f3ac93b 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -70,6 +70,20 @@
// Mesh
////////////////////////////////////////////////////////////////////////////////
+GlopBuilder& GlopBuilder::setMeshTexturedIndexedVbo(GLuint vbo, GLsizei elementCount) {
+ TRIGGER_STAGE(kMeshStage);
+
+ mOutGlop->mesh.primitiveMode = GL_TRIANGLES;
+ mOutGlop->mesh.indices = { mRenderState.meshState().getQuadListIBO(), nullptr };
+ mOutGlop->mesh.vertices = {
+ vbo,
+ VertexAttribFlags::TextureCoord,
+ nullptr, nullptr, nullptr,
+ kTextureVertexStride };
+ mOutGlop->mesh.elementCount = elementCount;
+ return *this;
+}
+
GlopBuilder& GlopBuilder::setMeshUnitQuad() {
TRIGGER_STAGE(kMeshStage);
@@ -461,12 +475,12 @@
// Transform
////////////////////////////////////////////////////////////////////////////////
-void GlopBuilder::setTransform(const Matrix4& canvas,
- const int transformFlags) {
+GlopBuilder& GlopBuilder::setTransform(const Matrix4& canvas, const int transformFlags) {
TRIGGER_STAGE(kTransformStage);
mOutGlop->transform.canvas = canvas;
mOutGlop->transform.transformFlags = transformFlags;
+ return *this;
}
////////////////////////////////////////////////////////////////////////////////
@@ -632,5 +646,42 @@
mOutGlop->transform.meshTransform().mapRect(mOutGlop->bounds);
}
+void GlopBuilder::dump(const Glop& glop) {
+ ALOGD("Glop Mesh");
+ const Glop::Mesh& mesh = glop.mesh;
+ ALOGD(" primitive mode: %d", mesh.primitiveMode);
+ ALOGD(" indices: buffer obj %x, indices %p", mesh.indices.bufferObject, mesh.indices.indices);
+
+ const Glop::Mesh::Vertices& vertices = glop.mesh.vertices;
+ ALOGD(" vertices: buffer obj %x, flags %x, pos %p, tex %p, clr %p, stride %d",
+ vertices.bufferObject, vertices.attribFlags,
+ vertices.position, vertices.texCoord, vertices.color, vertices.stride);
+ ALOGD(" element count: %d", mesh.elementCount);
+
+ ALOGD("Glop Fill");
+ const Glop::Fill& fill = glop.fill;
+ ALOGD(" program %p", fill.program);
+ if (fill.texture.texture) {
+ ALOGD(" texture %p, target %d, filter %d, clamp %d",
+ fill.texture.texture, fill.texture.target, fill.texture.filter, fill.texture.clamp);
+ if (fill.texture.textureTransform) {
+ fill.texture.textureTransform->dump("texture transform");
+ }
+ }
+ ALOGD_IF(fill.colorEnabled, " color (argb) %.2f %.2f %.2f %.2f",
+ fill.color.a, fill.color.r, fill.color.g, fill.color.b);
+ ALOGD_IF(fill.filterMode != ProgramDescription::ColorFilterMode::None,
+ " filterMode %d", (int)fill.filterMode);
+ ALOGD_IF(fill.skiaShaderData.skiaShaderType, " shader type %d",
+ fill.skiaShaderData.skiaShaderType);
+
+ ALOGD("Glop transform");
+ glop.transform.modelView.dump("model view");
+ glop.transform.canvas.dump("canvas");
+
+ ALOGD("Glop blend %d %d", glop.blend.src, glop.blend.dst);
+ ALOGD("Glop bounds " RECT_STRING, RECT_ARGS(glop.bounds));
+}
+
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/GlopBuilder.h b/libs/hwui/GlopBuilder.h
index 8d05570..6270dcb 100644
--- a/libs/hwui/GlopBuilder.h
+++ b/libs/hwui/GlopBuilder.h
@@ -47,6 +47,7 @@
public:
GlopBuilder(RenderState& renderState, Caches& caches, Glop* outGlop);
+ GlopBuilder& setMeshTexturedIndexedVbo(GLuint vbo, GLsizei elementCount);
GlopBuilder& setMeshUnitQuad();
GlopBuilder& setMeshTexturedUnitQuad(const UvMapper* uvMapper);
GlopBuilder& setMeshTexturedUvQuad(const UvMapper* uvMapper, const Rect uvs);
@@ -71,9 +72,9 @@
GlopBuilder& setFillTextureLayer(Layer& layer, float alpha);
GlopBuilder& setTransform(const Snapshot& snapshot, const int transformFlags) {
- setTransform(*snapshot.transform, transformFlags);
- return *this;
+ return setTransform(*snapshot.transform, transformFlags);
}
+ GlopBuilder& setTransform(const Matrix4& canvas, const int transformFlags);
GlopBuilder& setModelViewMapUnitToRect(const Rect destination);
GlopBuilder& setModelViewMapUnitToRectSnap(const Rect destination);
@@ -98,11 +99,12 @@
GlopBuilder& setRoundRectClipState(const RoundRectClipState* roundRectClipState);
void build();
+
+ static void dump(const Glop& glop);
private:
void setFill(int color, float alphaScale,
SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage,
const SkShader* shader, const SkColorFilter* colorFilter);
- void setTransform(const Matrix4& canvas, const int transformFlags);
enum StageFlags {
kInitialStage = 0,
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index aa105f9..8c46450 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -21,6 +21,8 @@
#include "GradientCache.h"
#include "Properties.h"
+#include <cutils/properties.h>
+
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index f99d92b..489ebc1 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -155,8 +155,7 @@
if (fbo) {
if (flush) LayerRenderer::flushLayer(renderState, this);
- // If put fails the cache will delete the FBO
- caches.fboCache.put(fbo);
+ renderState.deleteFramebuffer(fbo);
fbo = 0;
}
}
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 227271d..e9e5d81 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -189,7 +189,7 @@
LAYER_RENDERER_LOGD("Requesting new render layer %dx%d", width, height);
Caches& caches = Caches::getInstance();
- GLuint fbo = caches.fboCache.get();
+ GLuint fbo = renderState.genFramebuffer();
if (!fbo) {
ALOGW("Could not obtain an FBO");
return nullptr;
@@ -204,7 +204,7 @@
// We first obtain a layer before comparing against the max texture size
// because layers are not allocated at the exact desired size. They are
- // always created slighly larger to improve recycling
+ // always created slightly larger to improve recycling
const uint32_t maxTextureSize = caches.maxTextureSize;
if (layer->getWidth() > maxTextureSize || layer->getHeight() > maxTextureSize) {
ALOGW("Layer exceeds max. dimensions supported by the GPU (%dx%d, max=%dx%d)",
@@ -357,7 +357,7 @@
&& bitmap->width() <= caches.maxTextureSize
&& bitmap->height() <= caches.maxTextureSize) {
- GLuint fbo = caches.fboCache.get();
+ GLuint fbo = renderState.getFramebuffer();
if (!fbo) {
ALOGW("Could not obtain an FBO");
return false;
@@ -465,7 +465,7 @@
layer->setAlpha(alpha, mode);
layer->setFbo(previousLayerFbo);
caches.textureState().deleteTexture(texture);
- caches.fboCache.put(fbo);
+ renderState.deleteFramebuffer(fbo);
renderState.setViewport(previousViewportWidth, previousViewportHeight);
return status;
diff --git a/libs/hwui/LayerUpdateQueue.cpp b/libs/hwui/LayerUpdateQueue.cpp
new file mode 100644
index 0000000..db5f676
--- /dev/null
+++ b/libs/hwui/LayerUpdateQueue.cpp
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#include "LayerUpdateQueue.h"
+
+#include "RenderNode.h"
+
+namespace android {
+namespace uirenderer {
+
+void LayerUpdateQueue::clear() {
+ mEntries.clear();
+}
+
+void LayerUpdateQueue::enqueueLayerWithDamage(RenderNode* renderNode, Rect damage) {
+ damage.doIntersect(0, 0, renderNode->getWidth(), renderNode->getHeight());
+ if (!damage.isEmpty()) {
+ for (Entry& entry : mEntries) {
+ if (CC_UNLIKELY(entry.renderNode == renderNode)) {
+ entry.damage.unionWith(damage);
+ return;
+ }
+ }
+ mEntries.emplace_back(renderNode, damage);
+ }
+}
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/LayerUpdateQueue.h b/libs/hwui/LayerUpdateQueue.h
new file mode 100644
index 0000000..be612d2
--- /dev/null
+++ b/libs/hwui/LayerUpdateQueue.h
@@ -0,0 +1,53 @@
+/*
+ * 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 ANDROID_HWUI_LAYER_UPDATE_QUEUE_H
+#define ANDROID_HWUI_LAYER_UPDATE_QUEUE_H
+
+#include "Rect.h"
+#include "utils/Macros.h"
+
+#include <vector>
+#include <unordered_map>
+
+namespace android {
+namespace uirenderer {
+
+class RenderNode;
+
+class LayerUpdateQueue {
+ PREVENT_COPY_AND_ASSIGN(LayerUpdateQueue);
+public:
+ struct Entry {
+ Entry(RenderNode* renderNode, const Rect& damage)
+ : renderNode(renderNode)
+ , damage(damage) {}
+ RenderNode* renderNode;
+ Rect damage;
+ };
+
+ LayerUpdateQueue() {}
+ void enqueueLayerWithDamage(RenderNode* renderNode, Rect dirty);
+ void clear();
+ const std::vector<Entry> entries() const { return mEntries; }
+private:
+ std::vector<Entry> mEntries;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_LAYER_UPDATE_QUEUE_H
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index ed517ac..c017638 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -126,6 +126,9 @@
void loadMultiply(const Matrix4& u, const Matrix4& v);
void loadOrtho(float left, float right, float bottom, float top, float near, float far);
+ void loadOrtho(int width, int height) {
+ loadOrtho(0, width, height, 0, -1, 1);
+ }
uint8_t getType() const;
diff --git a/libs/hwui/OpReorderer.cpp b/libs/hwui/OpReorderer.cpp
new file mode 100644
index 0000000..68f80ea
--- /dev/null
+++ b/libs/hwui/OpReorderer.cpp
@@ -0,0 +1,597 @@
+/*
+ * 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.
+ */
+
+#include "OpReorderer.h"
+
+#include "LayerUpdateQueue.h"
+#include "RenderNode.h"
+#include "utils/FatVector.h"
+#include "utils/PaintUtils.h"
+
+#include <SkCanvas.h>
+#include <utils/Trace.h>
+#include <utils/TypeHelpers.h>
+
+namespace android {
+namespace uirenderer {
+
+class BatchBase {
+
+public:
+ BatchBase(batchid_t batchId, BakedOpState* op, bool merging)
+ : mBatchId(batchId)
+ , mMerging(merging) {
+ mBounds = op->computedState.clippedBounds;
+ mOps.push_back(op);
+ }
+
+ bool intersects(const Rect& rect) const {
+ if (!rect.intersects(mBounds)) return false;
+
+ for (const BakedOpState* op : mOps) {
+ if (rect.intersects(op->computedState.clippedBounds)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ batchid_t getBatchId() const { return mBatchId; }
+ bool isMerging() const { return mMerging; }
+
+ const std::vector<BakedOpState*>& getOps() const { return mOps; }
+
+ void dump() const {
+ ALOGD(" Batch %p, id %d, merging %d, count %d, bounds " RECT_STRING,
+ this, mBatchId, mMerging, mOps.size(), RECT_ARGS(mBounds));
+ }
+protected:
+ batchid_t mBatchId;
+ Rect mBounds;
+ std::vector<BakedOpState*> mOps;
+ bool mMerging;
+};
+
+class OpBatch : public BatchBase {
+public:
+ static void* operator new(size_t size, LinearAllocator& allocator) {
+ return allocator.alloc(size);
+ }
+
+ OpBatch(batchid_t batchId, BakedOpState* op)
+ : BatchBase(batchId, op, false) {
+ }
+
+ void batchOp(BakedOpState* op) {
+ mBounds.unionWith(op->computedState.clippedBounds);
+ mOps.push_back(op);
+ }
+};
+
+class MergingOpBatch : public BatchBase {
+public:
+ static void* operator new(size_t size, LinearAllocator& allocator) {
+ return allocator.alloc(size);
+ }
+
+ MergingOpBatch(batchid_t batchId, BakedOpState* op)
+ : BatchBase(batchId, op, true) {
+ }
+
+ /*
+ * Helper for determining if a new op can merge with a MergingDrawBatch based on their bounds
+ * and clip side flags. Positive bounds delta means new bounds fit in old.
+ */
+ static inline bool checkSide(const int currentFlags, const int newFlags, const int side,
+ float boundsDelta) {
+ bool currentClipExists = currentFlags & side;
+ bool newClipExists = newFlags & side;
+
+ // if current is clipped, we must be able to fit new bounds in current
+ if (boundsDelta > 0 && currentClipExists) return false;
+
+ // if new is clipped, we must be able to fit current bounds in new
+ if (boundsDelta < 0 && newClipExists) return false;
+
+ return true;
+ }
+
+ static bool paintIsDefault(const SkPaint& paint) {
+ return paint.getAlpha() == 255
+ && paint.getColorFilter() == nullptr
+ && paint.getShader() == nullptr;
+ }
+
+ static bool paintsAreEquivalent(const SkPaint& a, const SkPaint& b) {
+ return a.getAlpha() == b.getAlpha()
+ && a.getColorFilter() == b.getColorFilter()
+ && a.getShader() == b.getShader();
+ }
+
+ /*
+ * Checks if a (mergeable) op can be merged into this batch
+ *
+ * If true, the op's multiDraw must be guaranteed to handle both ops simultaneously, so it is
+ * important to consider all paint attributes used in the draw calls in deciding both a) if an
+ * op tries to merge at all, and b) if the op can merge with another set of ops
+ *
+ * False positives can lead to information from the paints of subsequent merged operations being
+ * dropped, so we make simplifying qualifications on the ops that can merge, per op type.
+ */
+ bool canMergeWith(BakedOpState* op) const {
+ bool isTextBatch = getBatchId() == OpBatchType::Text
+ || getBatchId() == OpBatchType::ColorText;
+
+ // Overlapping other operations is only allowed for text without shadow. For other ops,
+ // multiDraw isn't guaranteed to overdraw correctly
+ if (!isTextBatch || PaintUtils::hasTextShadow(op->op->paint)) {
+ if (intersects(op->computedState.clippedBounds)) return false;
+ }
+
+ const BakedOpState* lhs = op;
+ const BakedOpState* rhs = mOps[0];
+
+ if (!MathUtils::areEqual(lhs->alpha, rhs->alpha)) return false;
+
+ // Identical round rect clip state means both ops will clip in the same way, or not at all.
+ // As the state objects are const, we can compare their pointers to determine mergeability
+ if (lhs->roundRectClipState != rhs->roundRectClipState) return false;
+ if (lhs->projectionPathMask != rhs->projectionPathMask) return false;
+
+ /* Clipping compatibility check
+ *
+ * Exploits the fact that if a op or batch is clipped on a side, its bounds will equal its
+ * clip for that side.
+ */
+ const int currentFlags = mClipSideFlags;
+ const int newFlags = op->computedState.clipSideFlags;
+ if (currentFlags != OpClipSideFlags::None || newFlags != OpClipSideFlags::None) {
+ const Rect& opBounds = op->computedState.clippedBounds;
+ float boundsDelta = mBounds.left - opBounds.left;
+ if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Left, boundsDelta)) return false;
+ boundsDelta = mBounds.top - opBounds.top;
+ if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Top, boundsDelta)) return false;
+
+ // right and bottom delta calculation reversed to account for direction
+ boundsDelta = opBounds.right - mBounds.right;
+ if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Right, boundsDelta)) return false;
+ boundsDelta = opBounds.bottom - mBounds.bottom;
+ if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Bottom, boundsDelta)) return false;
+ }
+
+ const SkPaint* newPaint = op->op->paint;
+ const SkPaint* oldPaint = mOps[0]->op->paint;
+
+ if (newPaint == oldPaint) {
+ // if paints are equal, then modifiers + paint attribs don't need to be compared
+ return true;
+ } else if (newPaint && !oldPaint) {
+ return paintIsDefault(*newPaint);
+ } else if (!newPaint && oldPaint) {
+ return paintIsDefault(*oldPaint);
+ }
+ return paintsAreEquivalent(*newPaint, *oldPaint);
+ }
+
+ void mergeOp(BakedOpState* op) {
+ mBounds.unionWith(op->computedState.clippedBounds);
+ mOps.push_back(op);
+
+ const int newClipSideFlags = op->computedState.clipSideFlags;
+ mClipSideFlags |= newClipSideFlags;
+
+ const Rect& opClip = op->computedState.clipRect;
+ if (newClipSideFlags & OpClipSideFlags::Left) mClipRect.left = opClip.left;
+ if (newClipSideFlags & OpClipSideFlags::Top) mClipRect.top = opClip.top;
+ if (newClipSideFlags & OpClipSideFlags::Right) mClipRect.right = opClip.right;
+ if (newClipSideFlags & OpClipSideFlags::Bottom) mClipRect.bottom = opClip.bottom;
+ }
+
+private:
+ int mClipSideFlags = 0;
+ Rect mClipRect;
+};
+
+OpReorderer::LayerReorderer::LayerReorderer(uint32_t width, uint32_t height,
+ const BeginLayerOp* beginLayerOp, RenderNode* renderNode)
+ : width(width)
+ , height(height)
+ , offscreenBuffer(renderNode ? renderNode->getLayer() : nullptr)
+ , beginLayerOp(beginLayerOp)
+ , renderNode(renderNode) {}
+
+// iterate back toward target to see if anything drawn since should overlap the new op
+// if no target, merging ops still iterate to find similar batch to insert after
+void OpReorderer::LayerReorderer::locateInsertIndex(int batchId, const Rect& clippedBounds,
+ BatchBase** targetBatch, size_t* insertBatchIndex) const {
+ for (int i = mBatches.size() - 1; i >= 0; i--) {
+ BatchBase* overBatch = mBatches[i];
+
+ if (overBatch == *targetBatch) break;
+
+ // TODO: also consider shader shared between batch types
+ if (batchId == overBatch->getBatchId()) {
+ *insertBatchIndex = i + 1;
+ if (!*targetBatch) break; // found insert position, quit
+ }
+
+ if (overBatch->intersects(clippedBounds)) {
+ // NOTE: it may be possible to optimize for special cases where two operations
+ // of the same batch/paint could swap order, such as with a non-mergeable
+ // (clipped) and a mergeable text operation
+ *targetBatch = nullptr;
+ break;
+ }
+ }
+}
+
+void OpReorderer::LayerReorderer::deferUnmergeableOp(LinearAllocator& allocator,
+ BakedOpState* op, batchid_t batchId) {
+ OpBatch* targetBatch = mBatchLookup[batchId];
+
+ size_t insertBatchIndex = mBatches.size();
+ if (targetBatch) {
+ locateInsertIndex(batchId, op->computedState.clippedBounds,
+ (BatchBase**)(&targetBatch), &insertBatchIndex);
+ }
+
+ if (targetBatch) {
+ targetBatch->batchOp(op);
+ } else {
+ // new non-merging batch
+ targetBatch = new (allocator) OpBatch(batchId, op);
+ mBatchLookup[batchId] = targetBatch;
+ mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
+ }
+}
+
+// insertion point of a new batch, will hopefully be immediately after similar batch
+// (generally, should be similar shader)
+void OpReorderer::LayerReorderer::deferMergeableOp(LinearAllocator& allocator,
+ BakedOpState* op, batchid_t batchId, mergeid_t mergeId) {
+ MergingOpBatch* targetBatch = nullptr;
+
+ // Try to merge with any existing batch with same mergeId
+ auto getResult = mMergingBatchLookup[batchId].find(mergeId);
+ if (getResult != mMergingBatchLookup[batchId].end()) {
+ targetBatch = getResult->second;
+ if (!targetBatch->canMergeWith(op)) {
+ targetBatch = nullptr;
+ }
+ }
+
+ size_t insertBatchIndex = mBatches.size();
+ locateInsertIndex(batchId, op->computedState.clippedBounds,
+ (BatchBase**)(&targetBatch), &insertBatchIndex);
+
+ if (targetBatch) {
+ targetBatch->mergeOp(op);
+ } else {
+ // new merging batch
+ targetBatch = new (allocator) MergingOpBatch(batchId, op);
+ mMergingBatchLookup[batchId].insert(std::make_pair(mergeId, targetBatch));
+
+ mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
+ }
+}
+
+void OpReorderer::LayerReorderer::replayBakedOpsImpl(void* arg, BakedOpDispatcher* receivers) const {
+ ATRACE_NAME("flush drawing commands");
+ for (const BatchBase* batch : mBatches) {
+ // TODO: different behavior based on batch->isMerging()
+ for (const BakedOpState* op : batch->getOps()) {
+ receivers[op->op->opId](arg, *op->op, *op);
+ }
+ }
+}
+
+void OpReorderer::LayerReorderer::dump() const {
+ ALOGD("LayerReorderer %p, %ux%u buffer %p, blo %p, rn %p",
+ this, width, height, offscreenBuffer, beginLayerOp, renderNode);
+ for (const BatchBase* batch : mBatches) {
+ batch->dump();
+ }
+}
+
+OpReorderer::OpReorderer(const LayerUpdateQueue& layers, const SkRect& clip,
+ uint32_t viewportWidth, uint32_t viewportHeight,
+ const std::vector< sp<RenderNode> >& nodes)
+ : mCanvasState(*this) {
+ ATRACE_NAME("prepare drawing commands");
+ mLayerReorderers.emplace_back(viewportWidth, viewportHeight);
+ mLayerStack.push_back(0);
+
+ mCanvasState.initializeSaveStack(viewportWidth, viewportHeight,
+ clip.fLeft, clip.fTop, clip.fRight, clip.fBottom,
+ Vector3());
+
+ // Render all layers to be updated, in order. Defer in reverse order, so that they'll be
+ // updated in the order they're passed in (mLayerReorderers are issued to Renderer in reverse)
+ for (int i = layers.entries().size() - 1; i >= 0; i--) {
+ RenderNode* layerNode = layers.entries()[i].renderNode;
+ const Rect& layerDamage = layers.entries()[i].damage;
+
+ saveForLayer(layerNode->getWidth(), layerNode->getHeight(), nullptr, layerNode);
+ mCanvasState.writableSnapshot()->setClip(
+ layerDamage.left, layerDamage.top, layerDamage.right, layerDamage.bottom);
+
+ if (layerNode->getDisplayList()) {
+ deferImpl(*(layerNode->getDisplayList()));
+ }
+ restoreForLayer();
+ }
+
+ // Defer Fbo0
+ for (const sp<RenderNode>& node : nodes) {
+ if (node->nothingToDraw()) continue;
+
+ int count = mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+ deferNodePropsAndOps(*node);
+ mCanvasState.restoreToCount(count);
+ }
+}
+
+OpReorderer::OpReorderer(int viewportWidth, int viewportHeight, const DisplayList& displayList)
+ : mCanvasState(*this) {
+ ATRACE_NAME("prepare drawing commands");
+
+ mLayerReorderers.emplace_back(viewportWidth, viewportHeight);
+ mLayerStack.push_back(0);
+
+ mCanvasState.initializeSaveStack(viewportWidth, viewportHeight,
+ 0, 0, viewportWidth, viewportHeight, Vector3());
+ deferImpl(displayList);
+}
+
+void OpReorderer::onViewportInitialized() {}
+
+void OpReorderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
+
+void OpReorderer::deferNodePropsAndOps(RenderNode& node) {
+ if (node.applyViewProperties(mCanvasState, mAllocator)) {
+ // not rejected so render
+ if (node.getLayer()) {
+ // HW layer
+ LayerOp* drawLayerOp = new (mAllocator) LayerOp(node);
+ BakedOpState* bakedOpState = tryBakeOpState(*drawLayerOp);
+ if (bakedOpState) {
+ // Layer will be drawn into parent layer (which is now current, since we popped mLayerStack)
+ currentLayer().deferUnmergeableOp(mAllocator, bakedOpState, OpBatchType::Bitmap);
+ }
+ } else {
+ deferImpl(*(node.getDisplayList()));
+ }
+ }
+}
+
+typedef key_value_pair_t<float, const RenderNodeOp*> ZRenderNodeOpPair;
+
+template <typename V>
+static void buildZSortedChildList(V* zTranslatedNodes,
+ const DisplayList& displayList, const DisplayList::Chunk& chunk) {
+ if (chunk.beginChildIndex == chunk.endChildIndex) return;
+
+ for (size_t i = chunk.beginChildIndex; i < chunk.endChildIndex; i++) {
+ RenderNodeOp* childOp = displayList.getChildren()[i];
+ RenderNode* child = childOp->renderNode;
+ float childZ = child->properties().getZ();
+
+ if (!MathUtils::isZero(childZ) && chunk.reorderChildren) {
+ zTranslatedNodes->push_back(ZRenderNodeOpPair(childZ, childOp));
+ childOp->skipInOrderDraw = true;
+ } else if (!child->properties().getProjectBackwards()) {
+ // regular, in order drawing DisplayList
+ childOp->skipInOrderDraw = false;
+ }
+ }
+
+ // Z sort any 3d children (stable-ness makes z compare fall back to standard drawing order)
+ std::stable_sort(zTranslatedNodes->begin(), zTranslatedNodes->end());
+}
+
+template <typename V>
+static size_t findNonNegativeIndex(const V& zTranslatedNodes) {
+ for (size_t i = 0; i < zTranslatedNodes.size(); i++) {
+ if (zTranslatedNodes[i].key >= 0.0f) return i;
+ }
+ return zTranslatedNodes.size();
+}
+
+template <typename V>
+void OpReorderer::defer3dChildren(ChildrenSelectMode mode, const V& zTranslatedNodes) {
+ const int size = zTranslatedNodes.size();
+ if (size == 0
+ || (mode == ChildrenSelectMode::Negative&& zTranslatedNodes[0].key > 0.0f)
+ || (mode == ChildrenSelectMode::Positive && zTranslatedNodes[size - 1].key < 0.0f)) {
+ // no 3d children to draw
+ return;
+ }
+
+ /**
+ * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters
+ * with very similar Z heights to draw together.
+ *
+ * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are
+ * underneath both, and neither's shadow is drawn on top of the other.
+ */
+ const size_t nonNegativeIndex = findNonNegativeIndex(zTranslatedNodes);
+ size_t drawIndex, shadowIndex, endIndex;
+ if (mode == ChildrenSelectMode::Negative) {
+ drawIndex = 0;
+ endIndex = nonNegativeIndex;
+ shadowIndex = endIndex; // draw no shadows
+ } else {
+ drawIndex = nonNegativeIndex;
+ endIndex = size;
+ shadowIndex = drawIndex; // potentially draw shadow for each pos Z child
+ }
+
+ float lastCasterZ = 0.0f;
+ while (shadowIndex < endIndex || drawIndex < endIndex) {
+ if (shadowIndex < endIndex) {
+ const RenderNodeOp* casterNodeOp = zTranslatedNodes[shadowIndex].value;
+ const float casterZ = zTranslatedNodes[shadowIndex].key;
+ // attempt to render the shadow if the caster about to be drawn is its caster,
+ // OR if its caster's Z value is similar to the previous potential caster
+ if (shadowIndex == drawIndex || casterZ - lastCasterZ < 0.1f) {
+ deferShadow(*casterNodeOp);
+
+ lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
+ shadowIndex++;
+ continue;
+ }
+ }
+
+ const RenderNodeOp* childOp = zTranslatedNodes[drawIndex].value;
+ deferRenderNodeOp(*childOp);
+ drawIndex++;
+ }
+}
+
+void OpReorderer::deferShadow(const RenderNodeOp& casterNodeOp) {
+ // TODO
+}
+/**
+ * Used to define a list of lambdas referencing private OpReorderer::onXXXXOp() methods.
+ *
+ * This allows opIds embedded in the RecordedOps to be used for dispatching to these lambdas. E.g. a
+ * BitmapOp op then would be dispatched to OpReorderer::onBitmapOp(const BitmapOp&)
+ */
+#define OP_RECEIVER(Type) \
+ [](OpReorderer& reorderer, const RecordedOp& op) { reorderer.on##Type(static_cast<const Type&>(op)); },
+void OpReorderer::deferImpl(const DisplayList& displayList) {
+ static std::function<void(OpReorderer& reorderer, const RecordedOp&)> receivers[] = {
+ MAP_OPS(OP_RECEIVER)
+ };
+ for (const DisplayList::Chunk& chunk : displayList.getChunks()) {
+ FatVector<ZRenderNodeOpPair, 16> zTranslatedNodes;
+ buildZSortedChildList(&zTranslatedNodes, displayList, chunk);
+
+ defer3dChildren(ChildrenSelectMode::Negative, zTranslatedNodes);
+ for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
+ const RecordedOp* op = displayList.getOps()[opIndex];
+ receivers[op->opId](*this, *op);
+ }
+ defer3dChildren(ChildrenSelectMode::Positive, zTranslatedNodes);
+ }
+}
+
+void OpReorderer::deferRenderNodeOp(const RenderNodeOp& op) {
+ if (op.renderNode->nothingToDraw()) return;
+ int count = mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+
+ // apply state from RecordedOp
+ mCanvasState.concatMatrix(op.localMatrix);
+ mCanvasState.clipRect(op.localClipRect.left, op.localClipRect.top,
+ op.localClipRect.right, op.localClipRect.bottom, SkRegion::kIntersect_Op);
+
+ // then apply state from node properties, and defer ops
+ deferNodePropsAndOps(*op.renderNode);
+
+ mCanvasState.restoreToCount(count);
+}
+
+void OpReorderer::onRenderNodeOp(const RenderNodeOp& op) {
+ if (!op.skipInOrderDraw) {
+ deferRenderNodeOp(op);
+ }
+}
+
+static batchid_t tessellatedBatchId(const SkPaint& paint) {
+ return paint.getPathEffect()
+ ? OpBatchType::AlphaMaskTexture
+ : (paint.isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices);
+}
+
+void OpReorderer::onBitmapOp(const BitmapOp& op) {
+ BakedOpState* bakedStateOp = tryBakeOpState(op);
+ if (!bakedStateOp) return; // quick rejected
+
+ mergeid_t mergeId = (mergeid_t) op.bitmap->getGenerationID();
+ // TODO: AssetAtlas
+ currentLayer().deferMergeableOp(mAllocator, bakedStateOp, OpBatchType::Bitmap, mergeId);
+}
+
+void OpReorderer::onRectOp(const RectOp& op) {
+ BakedOpState* bakedStateOp = tryBakeOpState(op);
+ if (!bakedStateOp) return; // quick rejected
+ currentLayer().deferUnmergeableOp(mAllocator, bakedStateOp, tessellatedBatchId(*op.paint));
+}
+
+void OpReorderer::onSimpleRectsOp(const SimpleRectsOp& op) {
+ BakedOpState* bakedStateOp = tryBakeOpState(op);
+ if (!bakedStateOp) return; // quick rejected
+ currentLayer().deferUnmergeableOp(mAllocator, bakedStateOp, OpBatchType::Vertices);
+}
+
+void OpReorderer::saveForLayer(uint32_t layerWidth, uint32_t layerHeight,
+ const BeginLayerOp* beginLayerOp, RenderNode* renderNode) {
+
+ mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+ mCanvasState.writableSnapshot()->transform->loadIdentity();
+ mCanvasState.writableSnapshot()->initializeViewport(layerWidth, layerHeight);
+ mCanvasState.writableSnapshot()->roundRectClipState = nullptr;
+
+ // create a new layer, and push its index on the stack
+ mLayerStack.push_back(mLayerReorderers.size());
+ mLayerReorderers.emplace_back(layerWidth, layerHeight, beginLayerOp, renderNode);
+}
+
+void OpReorderer::restoreForLayer() {
+ // restore canvas, and pop finished layer off of the stack
+ mCanvasState.restore();
+ mLayerStack.pop_back();
+}
+
+// TODO: test rejection at defer time, where the bounds become empty
+void OpReorderer::onBeginLayerOp(const BeginLayerOp& op) {
+ const uint32_t layerWidth = (uint32_t) op.unmappedBounds.getWidth();
+ const uint32_t layerHeight = (uint32_t) op.unmappedBounds.getHeight();
+ saveForLayer(layerWidth, layerHeight, &op, nullptr);
+}
+
+void OpReorderer::onEndLayerOp(const EndLayerOp& /* ignored */) {
+ const BeginLayerOp& beginLayerOp = *currentLayer().beginLayerOp;
+ int finishedLayerIndex = mLayerStack.back();
+
+ restoreForLayer();
+
+ // record the draw operation into the previous layer's list of draw commands
+ // uses state from the associated beginLayerOp, since it has all the state needed for drawing
+ LayerOp* drawLayerOp = new (mAllocator) LayerOp(
+ beginLayerOp.unmappedBounds,
+ beginLayerOp.localMatrix,
+ beginLayerOp.localClipRect,
+ beginLayerOp.paint,
+ &mLayerReorderers[finishedLayerIndex].offscreenBuffer);
+ BakedOpState* bakedOpState = tryBakeOpState(*drawLayerOp);
+
+ if (bakedOpState) {
+ // Layer will be drawn into parent layer (which is now current, since we popped mLayerStack)
+ currentLayer().deferUnmergeableOp(mAllocator, bakedOpState, OpBatchType::Bitmap);
+ } else {
+ // Layer won't be drawn - delete its drawing batches to prevent it from doing any work
+ mLayerReorderers[finishedLayerIndex].clear();
+ return;
+ }
+}
+
+void OpReorderer::onLayerOp(const LayerOp& op) {
+ LOG_ALWAYS_FATAL("unsupported");
+}
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/OpReorderer.h b/libs/hwui/OpReorderer.h
new file mode 100644
index 0000000..936b6ed
--- /dev/null
+++ b/libs/hwui/OpReorderer.h
@@ -0,0 +1,249 @@
+/*
+ * 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 ANDROID_HWUI_OP_REORDERER_H
+#define ANDROID_HWUI_OP_REORDERER_H
+
+#include "BakedOpState.h"
+#include "CanvasState.h"
+#include "DisplayList.h"
+#include "RecordedOp.h"
+
+#include <vector>
+#include <unordered_map>
+
+struct SkRect;
+
+namespace android {
+namespace uirenderer {
+
+class BakedOpState;
+class BatchBase;
+class LayerUpdateQueue;
+class MergingOpBatch;
+class OffscreenBuffer;
+class OpBatch;
+class Rect;
+
+typedef int batchid_t;
+typedef const void* mergeid_t;
+
+namespace OpBatchType {
+ enum {
+ None = 0, // Don't batch
+ Bitmap,
+ Patch,
+ AlphaVertices,
+ Vertices,
+ AlphaMaskTexture,
+ Text,
+ ColorText,
+
+ Count // must be last
+ };
+}
+
+class OpReorderer : public CanvasStateClient {
+ typedef std::function<void(void*, const RecordedOp&, const BakedOpState&)> BakedOpDispatcher;
+
+ /**
+ * Stores the deferred render operations and state used to compute ordering
+ * for a single FBO/layer.
+ */
+ class LayerReorderer {
+ public:
+ // Create LayerReorderer for Fbo0
+ LayerReorderer(uint32_t width, uint32_t height)
+ : LayerReorderer(width, height, nullptr, nullptr) {};
+
+ // Create LayerReorderer for an offscreen layer, where beginLayerOp is present for a
+ // saveLayer, renderNode is present for a HW layer.
+ LayerReorderer(uint32_t width, uint32_t height,
+ const BeginLayerOp* beginLayerOp, RenderNode* renderNode);
+
+ // iterate back toward target to see if anything drawn since should overlap the new op
+ // if no target, merging ops still iterate to find similar batch to insert after
+ void locateInsertIndex(int batchId, const Rect& clippedBounds,
+ BatchBase** targetBatch, size_t* insertBatchIndex) const;
+
+ void deferUnmergeableOp(LinearAllocator& allocator, BakedOpState* op, batchid_t batchId);
+
+ // insertion point of a new batch, will hopefully be immediately after similar batch
+ // (generally, should be similar shader)
+ void deferMergeableOp(LinearAllocator& allocator,
+ BakedOpState* op, batchid_t batchId, mergeid_t mergeId);
+
+ void replayBakedOpsImpl(void* arg, BakedOpDispatcher* receivers) const;
+
+ bool empty() const {
+ return mBatches.empty();
+ }
+
+ void clear() {
+ mBatches.clear();
+ }
+
+ void dump() const;
+
+ const uint32_t width;
+ const uint32_t height;
+ OffscreenBuffer* offscreenBuffer;
+ const BeginLayerOp* beginLayerOp;
+ const RenderNode* renderNode;
+ private:
+ std::vector<BatchBase*> mBatches;
+
+ /**
+ * Maps the mergeid_t returned by an op's getMergeId() to the most recently seen
+ * MergingDrawBatch of that id. These ids are unique per draw type and guaranteed to not
+ * collide, which avoids the need to resolve mergeid collisions.
+ */
+ std::unordered_map<mergeid_t, MergingOpBatch*> mMergingBatchLookup[OpBatchType::Count];
+
+ // Maps batch ids to the most recent *non-merging* batch of that id
+ OpBatch* mBatchLookup[OpBatchType::Count] = { nullptr };
+
+ };
+public:
+ OpReorderer(const LayerUpdateQueue& layers, const SkRect& clip,
+ uint32_t viewportWidth, uint32_t viewportHeight,
+ const std::vector< sp<RenderNode> >& nodes);
+
+ OpReorderer(int viewportWidth, int viewportHeight, const DisplayList& displayList);
+
+ virtual ~OpReorderer() {}
+
+ /**
+ * replayBakedOps() is templated based on what class will receive ops being replayed.
+ *
+ * It constructs a lookup array of lambdas, which allows a recorded BakeOpState to use
+ * state->op->opId to lookup a receiver that will be called when the op is replayed.
+ *
+ * For example a BitmapOp would resolve, via the lambda lookup, to calling:
+ *
+ * StaticDispatcher::onBitmapOp(Renderer& renderer, const BitmapOp& op, const BakedOpState& state);
+ */
+#define BAKED_OP_RECEIVER(Type) \
+ [](void* internalRenderer, const RecordedOp& op, const BakedOpState& state) { \
+ StaticDispatcher::on##Type(*(static_cast<Renderer*>(internalRenderer)), static_cast<const Type&>(op), state); \
+ },
+ template <typename StaticDispatcher, typename Renderer>
+ void replayBakedOps(Renderer& renderer) {
+ static BakedOpDispatcher receivers[] = {
+ MAP_OPS(BAKED_OP_RECEIVER)
+ };
+
+ // Relay through layers in reverse order, since layers
+ // later in the list will be drawn by earlier ones
+ for (int i = mLayerReorderers.size() - 1; i >= 1; i--) {
+ LayerReorderer& layer = mLayerReorderers[i];
+ if (layer.renderNode) {
+ // cached HW layer - can't skip layer if empty
+ renderer.startLayer(layer.offscreenBuffer);
+ layer.replayBakedOpsImpl((void*)&renderer, receivers);
+ renderer.endLayer();
+ } else if (!layer.empty()) { // save layer - skip entire layer if empty
+ layer.offscreenBuffer = renderer.createLayer(layer.width, layer.height);
+ layer.replayBakedOpsImpl((void*)&renderer, receivers);
+ renderer.endLayer();
+ }
+ }
+
+ const LayerReorderer& fbo0 = mLayerReorderers[0];
+ renderer.startFrame(fbo0.width, fbo0.height);
+ fbo0.replayBakedOpsImpl((void*)&renderer, receivers);
+ renderer.endFrame();
+ }
+
+ void dump() const {
+ for (auto&& layer : mLayerReorderers) {
+ layer.dump();
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ /// CanvasStateClient interface
+ ///////////////////////////////////////////////////////////////////
+ virtual void onViewportInitialized() override;
+ virtual void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) override;
+ virtual GLuint getTargetFbo() const override { return 0; }
+
+private:
+ enum class ChildrenSelectMode {
+ Negative,
+ Positive
+ };
+ void saveForLayer(uint32_t layerWidth, uint32_t layerHeight,
+ const BeginLayerOp* beginLayerOp, RenderNode* renderNode);
+ void restoreForLayer();
+
+ LayerReorderer& currentLayer() { return mLayerReorderers[mLayerStack.back()]; }
+
+ BakedOpState* tryBakeOpState(const RecordedOp& recordedOp) {
+ return BakedOpState::tryConstruct(mAllocator, *mCanvasState.currentSnapshot(), recordedOp);
+ }
+
+ // should always be surrounded by a save/restore pair
+ void deferNodePropsAndOps(RenderNode& node);
+
+ void deferShadow(const RenderNodeOp& casterOp);
+
+ void deferImpl(const DisplayList& displayList);
+
+ template <typename V>
+ void defer3dChildren(ChildrenSelectMode mode, const V& zTranslatedNodes);
+
+ void deferRenderNodeOp(const RenderNodeOp& op);
+
+ void replayBakedOpsImpl(void* arg, BakedOpDispatcher* receivers);
+
+ /**
+ * Declares all OpReorderer::onXXXXOp() methods for every RecordedOp type.
+ *
+ * These private methods are called from within deferImpl to defer each individual op
+ * type differently.
+ */
+#define INTERNAL_OP_HANDLER(Type) \
+ void on##Type(const Type& op);
+ MAP_OPS(INTERNAL_OP_HANDLER)
+
+ // List of every deferred layer's render state. Replayed in reverse order to render a frame.
+ std::vector<LayerReorderer> mLayerReorderers;
+
+ /*
+ * Stack of indices within mLayerReorderers representing currently active layers. If drawing
+ * layerA within a layerB, will contain, in order:
+ * - 0 (representing FBO 0, always present)
+ * - layerB's index
+ * - layerA's index
+ *
+ * Note that this doesn't vector doesn't always map onto all values of mLayerReorderers. When a
+ * layer is finished deferring, it will still be represented in mLayerReorderers, but it's index
+ * won't be in mLayerStack. This is because it can be replayed, but can't have any more drawing
+ * ops added to it.
+ */
+ std::vector<size_t> mLayerStack;
+
+ CanvasState mCanvasState;
+
+ // contains ResolvedOps and Batches
+ LinearAllocator mAllocator;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_OP_REORDERER_H
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index cd03ac4..12c4607 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -223,7 +223,7 @@
void OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) {
if (mState.currentlyIgnored()) return;
- Rect clip(mState.currentClipRect());
+ Rect clip(mState.currentRenderTargetClip());
clip.snapToPixelBoundaries();
// Since we don't know what the functor will draw, let's dirty
@@ -488,7 +488,7 @@
currentTransform()->mapRect(bounds);
// Layers only make sense if they are in the framebuffer's bounds
- bounds.doIntersect(mState.currentClipRect());
+ bounds.doIntersect(mState.currentRenderTargetClip());
if (!bounds.isEmpty()) {
// We cannot work with sub-pixels in this case
bounds.snapToPixelBoundaries();
@@ -673,7 +673,7 @@
bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) {
layer->clipRect.set(clip);
- layer->setFbo(mCaches.fboCache.get());
+ layer->setFbo(mRenderState.genFramebuffer());
writableSnapshot()->region = &writableSnapshot()->layer->region;
writableSnapshot()->flags |= Snapshot::kFlagFboTarget | Snapshot::kFlagIsFboLayer;
@@ -1036,7 +1036,7 @@
}
void OpenGLRenderer::dirtyLayerUnchecked(Rect& bounds, Region* region) {
- bounds.doIntersect(mState.currentClipRect());
+ bounds.doIntersect(mState.currentRenderTargetClip());
if (!bounds.isEmpty()) {
bounds.snapToPixelBoundaries();
android::Rect dirty(bounds.left, bounds.top, bounds.right, bounds.bottom);
@@ -1084,7 +1084,7 @@
.setMeshIndexedQuads(&mesh[0], quadCount)
.setFillClear()
.setTransform(*currentSnapshot(), transformFlags)
- .setModelViewOffsetRect(0, 0, Rect(currentSnapshot()->getClipRect()))
+ .setModelViewOffsetRect(0, 0, Rect(currentSnapshot()->getRenderTargetClip()))
.build();
renderGlop(glop, GlopRenderType::LayerClear);
@@ -1099,7 +1099,7 @@
///////////////////////////////////////////////////////////////////////////////
bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDeferFlags) {
- const Rect& currentClip = mState.currentClipRect();
+ const Rect& currentClip = mState.currentRenderTargetClip();
const mat4* currentMatrix = currentTransform();
if (stateDeferFlags & kStateDeferFlag_Draw) {
@@ -1187,7 +1187,7 @@
///////////////////////////////////////////////////////////////////////////////
void OpenGLRenderer::setScissorFromClip() {
- Rect clip(mState.currentClipRect());
+ Rect clip(mState.currentRenderTargetClip());
clip.snapToPixelBoundaries();
if (mRenderState.scissor().set(clip.left, getViewportHeight() - clip.bottom,
@@ -1430,7 +1430,7 @@
return;
}
- DeferredDisplayList deferredList(mState.currentClipRect());
+ DeferredDisplayList deferredList(mState.currentRenderTargetClip());
DeferStateStruct deferStruct(deferredList, *this, replayFlags);
renderNode->defer(deferStruct, 0);
@@ -1765,7 +1765,7 @@
// No need to check against the clip, we fill the clip region
if (mState.currentlyIgnored()) return;
- Rect clip(mState.currentClipRect());
+ Rect clip(mState.currentRenderTargetClip());
clip.snapToPixelBoundaries();
SkPaint paint;
@@ -2030,7 +2030,7 @@
}
fontRenderer.setTextureFiltering(linearFilter);
- const Rect& clip(pureTranslate ? writableSnapshot()->getClipRect() : writableSnapshot()->getLocalClip());
+ const Rect& clip(pureTranslate ? writableSnapshot()->getRenderTargetClip() : writableSnapshot()->getLocalClip());
Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
TextDrawFunctor functor(this, x, y, pureTranslate, alpha, mode, paint);
@@ -2191,7 +2191,7 @@
fontRenderer.setTextureFiltering(linearFilter);
// TODO: Implement better clipping for scaled/rotated text
- const Rect* clip = !pureTranslate ? nullptr : &mState.currentClipRect();
+ const Rect* clip = !pureTranslate ? nullptr : &mState.currentRenderTargetClip();
Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
bool status;
@@ -2255,12 +2255,15 @@
PathTexture* texture = mCaches.pathCache.get(path, paint);
if (!texture) return;
- const AutoTexture autoCleanup(texture);
const float x = texture->left - texture->offset;
const float y = texture->top - texture->offset;
drawPathTexture(texture, x, y, paint);
+
+ if (texture->cleanup) {
+ mCaches.pathCache.remove(path, paint);
+ }
mDirty = true;
}
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index 4031f2e1..06ea55a 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -30,6 +30,8 @@
#include "thread/Signal.h"
#include "thread/TaskProcessor.h"
+#include <cutils/properties.h>
+
namespace android {
namespace uirenderer {
@@ -394,6 +396,13 @@
return texture;
}
+void PathCache::remove(const SkPath* path, const SkPaint* paint)
+{
+ PathDescription entry(kShapePath, paint);
+ entry.shape.path.mGenerationID = path->getGenerationID();
+ mCache.remove(entry);
+}
+
void PathCache::precache(const SkPath* path, const SkPaint* paint) {
if (!Caches::getInstance().tasks.canRunTasks()) {
return;
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index 31f8d35..302e9f8 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -202,6 +202,7 @@
PathTexture* getArc(float width, float height, float startAngle, float sweepAngle,
bool useCenter, const SkPaint* paint);
PathTexture* get(const SkPath* path, const SkPaint* paint);
+ void remove(const SkPath* path, const SkPaint* paint);
/**
* Removes the specified path. This is meant to be called from threads
diff --git a/libs/hwui/PathParser.cpp b/libs/hwui/PathParser.cpp
new file mode 100644
index 0000000..61b9d21
--- /dev/null
+++ b/libs/hwui/PathParser.cpp
@@ -0,0 +1,199 @@
+/*
+ * 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.
+ */
+
+#include "PathParser.h"
+
+#include "jni.h"
+
+#include <utils/Log.h>
+#include <sstream>
+#include <stdlib.h>
+#include <string>
+#include <vector>
+
+namespace android {
+namespace uirenderer {
+
+static size_t nextStart(const char* s, size_t length, size_t startIndex) {
+ size_t index = startIndex;
+ while (index < length) {
+ char c = s[index];
+ // Note that 'e' or 'E' are not valid path commands, but could be
+ // used for floating point numbers' scientific notation.
+ // Therefore, when searching for next command, we should ignore 'e'
+ // and 'E'.
+ if ((((c - 'A') * (c - 'Z') <= 0) || ((c - 'a') * (c - 'z') <= 0))
+ && c != 'e' && c != 'E') {
+ return index;
+ }
+ index++;
+ }
+ return index;
+}
+
+/**
+ * Calculate the position of the next comma or space or negative sign
+ * @param s the string to search
+ * @param start the position to start searching
+ * @param result the result of the extraction, including the position of the
+ * the starting position of next number, whether it is ending with a '-'.
+ */
+static void extract(int* outEndPosition, bool* outEndWithNegOrDot, const char* s, int start, int end) {
+ // Now looking for ' ', ',', '.' or '-' from the start.
+ int currentIndex = start;
+ bool foundSeparator = false;
+ *outEndWithNegOrDot = false;
+ bool secondDot = false;
+ bool isExponential = false;
+ for (; currentIndex < end; currentIndex++) {
+ bool isPrevExponential = isExponential;
+ isExponential = false;
+ char currentChar = s[currentIndex];
+ switch (currentChar) {
+ case ' ':
+ case ',':
+ foundSeparator = true;
+ break;
+ case '-':
+ // The negative sign following a 'e' or 'E' is not a separator.
+ if (currentIndex != start && !isPrevExponential) {
+ foundSeparator = true;
+ *outEndWithNegOrDot = true;
+ }
+ break;
+ case '.':
+ if (!secondDot) {
+ secondDot = true;
+ } else {
+ // This is the second dot, and it is considered as a separator.
+ foundSeparator = true;
+ *outEndWithNegOrDot = true;
+ }
+ break;
+ case 'e':
+ case 'E':
+ isExponential = true;
+ break;
+ }
+ if (foundSeparator) {
+ break;
+ }
+ }
+ // In the case where nothing is found, we put the end position to the end of
+ // our extract range. Otherwise, end position will be where separator is found.
+ *outEndPosition = currentIndex;
+}
+
+/**
+* Parse the floats in the string.
+* This is an optimized version of parseFloat(s.split(",|\\s"));
+*
+* @param s the string containing a command and list of floats
+* @return array of floats
+*/
+static void getFloats(std::vector<float>* outPoints, const char* pathStr, int start, int end) {
+
+ if (pathStr[start] == 'z' || pathStr[start] == 'Z') {
+ return;
+ }
+ int startPosition = start + 1;
+ int endPosition = start;
+
+ // The startPosition should always be the first character of the
+ // current number, and endPosition is the character after the current
+ // number.
+ while (startPosition < end) {
+ bool endWithNegOrDot;
+ extract(&endPosition, &endWithNegOrDot, pathStr, startPosition, end);
+
+ if (startPosition < endPosition) {
+ outPoints->push_back(strtof(&pathStr[startPosition], NULL));
+ }
+
+ if (endWithNegOrDot) {
+ // Keep the '-' or '.' sign with next number.
+ startPosition = endPosition;
+ } else {
+ startPosition = endPosition + 1;
+ }
+ }
+}
+
+void PathParser::getPathDataFromString(PathData* data, const char* pathStr, size_t strLen) {
+ if (pathStr == NULL) {
+ return;
+ }
+
+ size_t start = 0;
+ size_t end = 1;
+
+ while (end < strLen) {
+ end = nextStart(pathStr, strLen, end);
+ std::vector<float> points;
+ getFloats(&points, pathStr, start, end);
+ data->verbs.push_back(pathStr[start]);
+ data->verbSizes.push_back(points.size());
+ data->points.insert(data->points.end(), points.begin(), points.end());
+ start = end;
+ end++;
+ }
+
+ if ((end - start) == 1 && pathStr[start] != '\0') {
+ data->verbs.push_back(pathStr[start]);
+ data->verbSizes.push_back(0);
+ }
+
+ int i = 0;
+ while(pathStr[i] != '\0') {
+ i++;
+ }
+
+}
+
+void PathParser::dump(const PathData& data) {
+ // Print out the path data.
+ size_t start = 0;
+ for (size_t i = 0; i < data.verbs.size(); i++) {
+ std::ostringstream os;
+ os << data.verbs[i];
+ for (size_t j = 0; j < data.verbSizes[i]; j++) {
+ os << " " << data.points[start + j];
+ }
+ start += data.verbSizes[i];
+ ALOGD("%s", os.str().c_str());
+ }
+
+ std::ostringstream os;
+ for (size_t i = 0; i < data.points.size(); i++) {
+ os << data.points[i] << ", ";
+ }
+ ALOGD("points are : %s", os.str().c_str());
+}
+
+bool PathParser::parseStringForSkPath(SkPath* skPath, const char* pathStr, size_t strLen) {
+ PathData pathData;
+ getPathDataFromString(&pathData, pathStr, strLen);
+
+ // Check if there is valid data coming out of parsing the string.
+ if (pathData.verbs.size() == 0) {
+ return false;
+ }
+ VectorDrawablePath::verbsToPath(skPath, &pathData);
+ return true;
+}
+
+}; // namespace uirenderer
+}; //namespace android
diff --git a/libs/hwui/PathParser.h b/libs/hwui/PathParser.h
new file mode 100644
index 0000000..d30bb0f
--- /dev/null
+++ b/libs/hwui/PathParser.h
@@ -0,0 +1,42 @@
+/*
+ * 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 ANDROID_HWUI_PATHPARSER_H
+#define ANDROID_HWUI_PATHPARSER_H
+
+#include "VectorDrawablePath.h"
+
+#include <jni.h>
+#include <android/log.h>
+#include <cutils/compiler.h>
+
+namespace android {
+namespace uirenderer {
+
+class PathParser {
+public:
+ /**
+ * Parse the string literal and create a Skia Path. Return true on success.
+ */
+ ANDROID_API static bool parseStringForSkPath(SkPath* outPath, const char* pathStr,
+ size_t strLength);
+ static void getPathDataFromString(PathData* outData, const char* pathStr, size_t strLength);
+ static void dump(const PathData& data);
+};
+
+}; // namespace uirenderer
+}; // namespace android
+#endif //ANDROID_HWUI_PATHPARSER_H
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 36a8dac..e818186 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -17,8 +17,12 @@
#include "Debug.h"
-#include <algorithm>
+#include <cutils/compiler.h>
#include <cutils/log.h>
+#include <cutils/properties.h>
+
+#include <algorithm>
+#include <cstdlib>
namespace android {
namespace uirenderer {
@@ -29,7 +33,6 @@
bool Properties::debugOverdraw = false;
bool Properties::showDirtyRegions = false;
bool Properties::skipEmptyFrames = true;
-bool Properties::swapBuffersWithDamage = true;
bool Properties::useBufferAge = true;
bool Properties::enablePartialUpdates = true;
@@ -117,7 +120,6 @@
}
skipEmptyFrames = property_get_bool(PROPERTY_SKIP_EMPTY_DAMAGE, true);
- swapBuffersWithDamage = property_get_bool(PROPERTY_SWAP_WITH_DAMAGE, true);
useBufferAge = property_get_bool(PROPERTY_USE_BUFFER_AGE, true);
enablePartialUpdates = property_get_bool(PROPERTY_ENABLE_PARTIAL_UPDATES, true);
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 3512c36..1293c78 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -18,8 +18,6 @@
#define ANDROID_HWUI_PROPERTIES_H
#include <cutils/properties.h>
-#include <stdlib.h>
-#include <utils/Singleton.h>
/**
* This file contains the list of system properties used to configure
@@ -142,13 +140,6 @@
#define PROPERTY_SKIP_EMPTY_DAMAGE "debug.hwui.skip_empty_damage"
/**
- * Setting this property will enable or disable usage of EGL_KHR_swap_buffers_with_damage
- * See: https://www.khronos.org/registry/egl/extensions/KHR/EGL_KHR_swap_buffers_with_damage.txt
- * Default is "true"
- */
-#define PROPERTY_SWAP_WITH_DAMAGE "debug.hwui.swap_with_damage"
-
-/**
* Controls whether or not HWUI will use the EGL_EXT_buffer_age extension
* to do partial invalidates. Setting this to "false" will fall back to
* using BUFFER_PRESERVED instead
@@ -271,8 +262,6 @@
static bool showDirtyRegions;
// TODO: Remove after stabilization period
static bool skipEmptyFrames;
- // TODO: Remove after stabilization period
- static bool swapBuffersWithDamage;
static bool useBufferAge;
static bool enablePartialUpdates;
diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h
new file mode 100644
index 0000000..04af8e3
--- /dev/null
+++ b/libs/hwui/RecordedOp.h
@@ -0,0 +1,182 @@
+/*
+ * 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 ANDROID_HWUI_RECORDED_OP_H
+#define ANDROID_HWUI_RECORDED_OP_H
+
+#include "utils/LinearAllocator.h"
+#include "Rect.h"
+#include "Matrix.h"
+#include "RenderNode.h"
+
+#include "SkXfermode.h"
+
+class SkBitmap;
+class SkPaint;
+
+namespace android {
+namespace uirenderer {
+
+class OffscreenBuffer;
+class RenderNode;
+struct Vertex;
+
+/**
+ * The provided macro is executed for each op type in order, with the results separated by commas.
+ *
+ * This serves as the authoritative list of ops, used for generating ID enum, and ID based LUTs.
+ */
+#define MAP_OPS(OP_FN) \
+ OP_FN(BitmapOp) \
+ OP_FN(RectOp) \
+ OP_FN(RenderNodeOp) \
+ OP_FN(SimpleRectsOp) \
+ OP_FN(BeginLayerOp) \
+ OP_FN(EndLayerOp) \
+ OP_FN(LayerOp)
+
+// Generate OpId enum
+#define IDENTITY_FN(Type) Type,
+namespace RecordedOpId {
+ enum {
+ MAP_OPS(IDENTITY_FN)
+ Count,
+ };
+}
+static_assert(RecordedOpId::BitmapOp == 0,
+ "First index must be zero for LUTs to work");
+
+#define BASE_PARAMS const Rect& unmappedBounds, const Matrix4& localMatrix, const Rect& localClipRect, const SkPaint* paint
+#define BASE_PARAMS_PAINTLESS const Rect& unmappedBounds, const Matrix4& localMatrix, const Rect& localClipRect
+#define SUPER(Type) RecordedOp(RecordedOpId::Type, unmappedBounds, localMatrix, localClipRect, paint)
+#define SUPER_PAINTLESS(Type) RecordedOp(RecordedOpId::Type, unmappedBounds, localMatrix, localClipRect, nullptr)
+
+struct RecordedOp {
+ /* ID from RecordedOpId - generally used for jumping into function tables */
+ const int opId;
+
+ /* bounds in *local* space, without accounting for DisplayList transformation */
+ const Rect unmappedBounds;
+
+ /* transform in recording space (vs DisplayList origin) */
+ const Matrix4 localMatrix;
+
+ /* clip in recording space */
+ const Rect localClipRect;
+
+ /* optional paint, stored in base object to simplify merging logic */
+ const SkPaint* paint;
+protected:
+ RecordedOp(unsigned int opId, BASE_PARAMS)
+ : opId(opId)
+ , unmappedBounds(unmappedBounds)
+ , localMatrix(localMatrix)
+ , localClipRect(localClipRect)
+ , paint(paint) {}
+};
+
+struct RenderNodeOp : RecordedOp {
+ RenderNodeOp(BASE_PARAMS_PAINTLESS, RenderNode* renderNode)
+ : SUPER_PAINTLESS(RenderNodeOp)
+ , renderNode(renderNode) {}
+ RenderNode * renderNode; // not const, since drawing modifies it (somehow...)
+ bool skipInOrderDraw = false;
+};
+
+struct BitmapOp : RecordedOp {
+ BitmapOp(BASE_PARAMS, const SkBitmap* bitmap)
+ : SUPER(BitmapOp)
+ , bitmap(bitmap) {}
+ const SkBitmap* bitmap;
+ // TODO: asset atlas/texture id lookup?
+};
+
+struct RectOp : RecordedOp {
+ RectOp(BASE_PARAMS)
+ : SUPER(RectOp) {}
+};
+
+struct SimpleRectsOp : RecordedOp { // Filled, no AA (TODO: better name?)
+ SimpleRectsOp(BASE_PARAMS, Vertex* vertices, size_t vertexCount)
+ : SUPER(SimpleRectsOp)
+ , vertices(vertices)
+ , vertexCount(vertexCount) {}
+ Vertex* vertices;
+ const size_t vertexCount;
+};
+
+/**
+ * Stateful operation! denotes the creation of an off-screen layer,
+ * and that commands following will render into it.
+ */
+struct BeginLayerOp : RecordedOp {
+ BeginLayerOp(BASE_PARAMS)
+ : SUPER(BeginLayerOp) {}
+};
+
+/**
+ * Stateful operation! Denotes end of off-screen layer, and that
+ * commands since last BeginLayerOp should be drawn into parent FBO.
+ *
+ * State in this op is empty, it just serves to signal that a layer has been finished.
+ */
+struct EndLayerOp : RecordedOp {
+ EndLayerOp()
+ : RecordedOp(RecordedOpId::EndLayerOp, Rect(0, 0), Matrix4::identity(), Rect(0, 0), nullptr) {}
+};
+
+/**
+ * Draws an OffscreenBuffer.
+ *
+ * Alpha, mode, and colorfilter are embedded, since LayerOps are always dynamically generated,
+ * 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.
+ LayerOp(BASE_PARAMS, OffscreenBuffer** layerHandle)
+ : SUPER_PAINTLESS(LayerOp)
+ , layerHandle(layerHandle)
+ , alpha(paint->getAlpha() / 255.0f)
+ , mode(PaintUtils::getXfermodeDirect(paint))
+ , colorFilter(paint->getColorFilter())
+ , destroy(true) {}
+
+ LayerOp(RenderNode& node)
+ : RecordedOp(RecordedOpId::LayerOp, Rect(node.getWidth(), node.getHeight()), Matrix4::identity(), Rect(node.getWidth(), node.getHeight()), nullptr)
+ , layerHandle(node.getLayerHandle())
+ , alpha(node.properties().layerProperties().alpha() / 255.0f)
+ , mode(node.properties().layerProperties().xferMode())
+ , colorFilter(node.properties().layerProperties().colorFilter())
+ , destroy(false) {}
+
+ // Records a handle to the Layer object, since the Layer itself won't be
+ // constructed until after this operation is constructed.
+ OffscreenBuffer** layerHandle;
+ const float alpha;
+ const SkXfermode::Mode mode;
+
+ // 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
+}; // namespace android
+
+#endif // ANDROID_HWUI_RECORDED_OP_H
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
new file mode 100644
index 0000000..e988555
--- /dev/null
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -0,0 +1,473 @@
+/*
+ * 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.
+ */
+
+#include "RecordingCanvas.h"
+
+#include "RecordedOp.h"
+#include "RenderNode.h"
+
+namespace android {
+namespace uirenderer {
+
+RecordingCanvas::RecordingCanvas(size_t width, size_t height)
+ : mState(*this)
+ , mResourceCache(ResourceCache::getInstance()) {
+ reset(width, height);
+}
+
+RecordingCanvas::~RecordingCanvas() {
+ LOG_ALWAYS_FATAL_IF(mDisplayList,
+ "Destroyed a RecordingCanvas during a record!");
+}
+
+void RecordingCanvas::reset(int width, int height) {
+ LOG_ALWAYS_FATAL_IF(mDisplayList,
+ "prepareDirty called a second time during a recording!");
+ mDisplayList = new DisplayList();
+
+ mState.initializeSaveStack(width, height, 0, 0, width, height, Vector3());
+
+ mDeferredBarrierType = DeferredBarrierType::InOrder;
+ mState.setDirtyClip(false);
+ mRestoreSaveCount = -1;
+}
+
+DisplayList* RecordingCanvas::finishRecording() {
+ mPaintMap.clear();
+ mRegionMap.clear();
+ mPathMap.clear();
+ DisplayList* displayList = mDisplayList;
+ mDisplayList = nullptr;
+ mSkiaCanvasProxy.reset(nullptr);
+ return displayList;
+}
+
+SkCanvas* RecordingCanvas::asSkCanvas() {
+ LOG_ALWAYS_FATAL_IF(!mDisplayList,
+ "attempting to get an SkCanvas when we are not recording!");
+ if (!mSkiaCanvasProxy) {
+ mSkiaCanvasProxy.reset(new SkiaCanvasProxy(this));
+ }
+
+ // SkCanvas instances default to identity transform, but should inherit
+ // the state of this Canvas; if this code was in the SkiaCanvasProxy
+ // constructor, we couldn't cache mSkiaCanvasProxy.
+ SkMatrix parentTransform;
+ getMatrix(&parentTransform);
+ mSkiaCanvasProxy.get()->setMatrix(parentTransform);
+
+ return mSkiaCanvasProxy.get();
+}
+
+// ----------------------------------------------------------------------------
+// CanvasStateClient implementation
+// ----------------------------------------------------------------------------
+
+void RecordingCanvas::onViewportInitialized() {
+}
+
+void RecordingCanvas::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {
+ if (removed.flags & Snapshot::kFlagIsFboLayer) {
+ addOp(new (alloc()) EndLayerOp());
+ }
+}
+
+// ----------------------------------------------------------------------------
+// android/graphics/Canvas state operations
+// ----------------------------------------------------------------------------
+// Save (layer)
+int RecordingCanvas::save(SkCanvas::SaveFlags flags) {
+ return mState.save((int) flags);
+}
+
+void RecordingCanvas::RecordingCanvas::restore() {
+ if (mRestoreSaveCount < 0) {
+ restoreToCount(getSaveCount() - 1);
+ return;
+ }
+
+ mRestoreSaveCount--;
+ mState.restore();
+}
+
+void RecordingCanvas::restoreToCount(int saveCount) {
+ mRestoreSaveCount = saveCount;
+ mState.restoreToCount(saveCount);
+}
+
+int RecordingCanvas::saveLayer(float left, float top, float right, float bottom, const SkPaint* paint,
+ SkCanvas::SaveFlags flags) {
+ if (!(flags & SkCanvas::kClipToLayer_SaveFlag)) {
+ LOG_ALWAYS_FATAL("unclipped layers not supported");
+ }
+ // force matrix/clip isolation for layer
+ flags |= SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag;
+
+
+ const Snapshot& previous = *mState.currentSnapshot();
+
+ // initialize the snapshot as though it almost represents an FBO layer so deferred draw
+ // operations will be able to store and restore the current clip and transform info, and
+ // quick rejection will be correct (for display lists)
+
+ const Rect untransformedBounds(left, top, right, bottom);
+
+ // determine clipped bounds relative to previous viewport.
+ Rect visibleBounds = untransformedBounds;
+ previous.transform->mapRect(visibleBounds);
+
+
+ visibleBounds.doIntersect(previous.getRenderTargetClip());
+ visibleBounds.snapToPixelBoundaries();
+
+ Rect previousViewport(0, 0, previous.getViewportWidth(), previous.getViewportHeight());
+ visibleBounds.doIntersect(previousViewport);
+
+ // Map visible bounds back to layer space, and intersect with parameter bounds
+ Rect layerBounds = visibleBounds;
+ Matrix4 inverse;
+ inverse.loadInverse(*previous.transform);
+ inverse.mapRect(layerBounds);
+ layerBounds.doIntersect(untransformedBounds);
+
+ int saveValue = mState.save((int) flags);
+ Snapshot& snapshot = *mState.writableSnapshot();
+
+ // layerBounds is now original bounds, but with clipped to clip
+ // and viewport to ensure it's minimal size.
+ if (layerBounds.isEmpty() || untransformedBounds.isEmpty()) {
+ // Don't bother recording layer, since it's been rejected
+ snapshot.resetClip(0, 0, 0, 0);
+ return saveValue;
+ }
+
+ snapshot.flags |= Snapshot::kFlagFboTarget | Snapshot::kFlagIsFboLayer;
+ snapshot.initializeViewport(untransformedBounds.getWidth(), untransformedBounds.getHeight());
+ snapshot.resetTransform(-untransformedBounds.left, -untransformedBounds.top, 0.0f);
+
+ Rect clip = layerBounds;
+ clip.translate(-untransformedBounds.left, -untransformedBounds.top);
+ snapshot.resetClip(clip.left, clip.top, clip.right, clip.bottom);
+ snapshot.roundRectClipState = nullptr;
+
+ addOp(new (alloc()) BeginLayerOp(
+ Rect(left, top, right, bottom),
+ *previous.transform, // transform to *draw* with
+ previous.getRenderTargetClip(), // clip to *draw* with
+ refPaint(paint)));
+
+ return saveValue;
+}
+
+// Matrix
+void RecordingCanvas::rotate(float degrees) {
+ if (degrees == 0) return;
+
+ mState.rotate(degrees);
+}
+
+void RecordingCanvas::scale(float sx, float sy) {
+ if (sx == 1 && sy == 1) return;
+
+ mState.scale(sx, sy);
+}
+
+void RecordingCanvas::skew(float sx, float sy) {
+ mState.skew(sx, sy);
+}
+
+void RecordingCanvas::translate(float dx, float dy) {
+ if (dx == 0 && dy == 0) return;
+
+ mState.translate(dx, dy, 0);
+}
+
+// Clip
+bool RecordingCanvas::getClipBounds(SkRect* outRect) const {
+ Rect bounds = mState.getLocalClipBounds();
+ *outRect = SkRect::MakeLTRB(bounds.left, bounds.top, bounds.right, bounds.bottom);
+ return !(outRect->isEmpty());
+}
+bool RecordingCanvas::quickRejectRect(float left, float top, float right, float bottom) const {
+ return mState.quickRejectConservative(left, top, right, bottom);
+}
+bool RecordingCanvas::quickRejectPath(const SkPath& path) const {
+ SkRect bounds = path.getBounds();
+ return mState.quickRejectConservative(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom);
+}
+bool RecordingCanvas::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
+ return mState.clipRect(left, top, right, bottom, op);
+}
+bool RecordingCanvas::clipPath(const SkPath* path, SkRegion::Op op) {
+ return mState.clipPath(path, op);
+}
+bool RecordingCanvas::clipRegion(const SkRegion* region, SkRegion::Op op) {
+ return mState.clipRegion(region, op);
+}
+
+// ----------------------------------------------------------------------------
+// android/graphics/Canvas draw operations
+// ----------------------------------------------------------------------------
+void RecordingCanvas::drawColor(int color, SkXfermode::Mode mode) {
+ SkPaint paint;
+ paint.setColor(color);
+ paint.setXfermodeMode(mode);
+ drawPaint(paint);
+}
+
+void RecordingCanvas::drawPaint(const SkPaint& paint) {
+ // TODO: more efficient recording?
+ Matrix4 identity;
+ identity.loadIdentity();
+
+ addOp(new (alloc()) RectOp(
+ mState.getRenderTargetClipBounds(),
+ identity,
+ mState.getRenderTargetClipBounds(),
+ refPaint(&paint)));
+}
+
+// Geometry
+void RecordingCanvas::drawPoints(const float* points, int count, const SkPaint& paint) {
+ LOG_ALWAYS_FATAL("TODO!");
+}
+void RecordingCanvas::drawLines(const float* points, int count, const SkPaint& paint) {
+ LOG_ALWAYS_FATAL("TODO!");
+}
+void RecordingCanvas::drawRect(float left, float top, float right, float bottom, const SkPaint& paint) {
+ addOp(new (alloc()) RectOp(
+ Rect(left, top, right, bottom),
+ *(mState.currentSnapshot()->transform),
+ mState.getRenderTargetClipBounds(),
+ refPaint(&paint)));
+}
+
+void RecordingCanvas::drawSimpleRects(const float* rects, int vertexCount, const SkPaint* paint) {
+ if (rects == nullptr) return;
+
+ Vertex* rectData = (Vertex*) mDisplayList->allocator.alloc(vertexCount * sizeof(Vertex));
+ Vertex* vertex = rectData;
+
+ float left = FLT_MAX;
+ float top = FLT_MAX;
+ float right = FLT_MIN;
+ float bottom = FLT_MIN;
+ for (int index = 0; index < vertexCount; index += 4) {
+ float l = rects[index + 0];
+ float t = rects[index + 1];
+ float r = rects[index + 2];
+ float b = rects[index + 3];
+
+ Vertex::set(vertex++, l, t);
+ Vertex::set(vertex++, r, t);
+ Vertex::set(vertex++, l, b);
+ Vertex::set(vertex++, r, b);
+
+ left = std::min(left, l);
+ top = std::min(top, t);
+ right = std::max(right, r);
+ bottom = std::max(bottom, b);
+ }
+ addOp(new (alloc()) SimpleRectsOp(
+ Rect(left, top, right, bottom),
+ *(mState.currentSnapshot()->transform),
+ mState.getRenderTargetClipBounds(),
+ refPaint(paint), rectData, vertexCount));
+}
+
+void RecordingCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
+ if (paint.getStyle() == SkPaint::kFill_Style
+ && (!paint.isAntiAlias() || mState.currentTransform()->isSimple())) {
+ int count = 0;
+ Vector<float> rects;
+ SkRegion::Iterator it(region);
+ while (!it.done()) {
+ const SkIRect& r = it.rect();
+ rects.push(r.fLeft);
+ rects.push(r.fTop);
+ rects.push(r.fRight);
+ rects.push(r.fBottom);
+ count += 4;
+ it.next();
+ }
+ drawSimpleRects(rects.array(), count, &paint);
+ } else {
+ SkRegion::Iterator it(region);
+ while (!it.done()) {
+ const SkIRect& r = it.rect();
+ drawRect(r.fLeft, r.fTop, r.fRight, r.fBottom, paint);
+ it.next();
+ }
+ }
+}
+void RecordingCanvas::drawRoundRect(float left, float top, float right, float bottom,
+ float rx, float ry, const SkPaint& paint) {
+ LOG_ALWAYS_FATAL("TODO!");
+}
+void RecordingCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) {
+ LOG_ALWAYS_FATAL("TODO!");
+}
+void RecordingCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) {
+ LOG_ALWAYS_FATAL("TODO!");
+}
+void RecordingCanvas::drawArc(float left, float top, float right, float bottom,
+ float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) {
+ LOG_ALWAYS_FATAL("TODO!");
+}
+void RecordingCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
+ LOG_ALWAYS_FATAL("TODO!");
+}
+
+// Bitmap-based
+void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint) {
+ save(SkCanvas::kMatrix_SaveFlag);
+ translate(left, top);
+ drawBitmap(&bitmap, paint);
+ restore();
+}
+
+void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
+ const SkPaint* paint) {
+ if (matrix.isIdentity()) {
+ drawBitmap(&bitmap, paint);
+ } else if (!(matrix.getType() & ~(SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask))
+ && MathUtils::isPositive(matrix.getScaleX())
+ && MathUtils::isPositive(matrix.getScaleY())) {
+ // SkMatrix::isScaleTranslate() not available in L
+ SkRect src;
+ SkRect dst;
+ bitmap.getBounds(&src);
+ matrix.mapRect(&dst, src);
+ drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom,
+ dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint);
+ } else {
+ save(SkCanvas::kMatrix_SaveFlag);
+ concat(matrix);
+ drawBitmap(&bitmap, paint);
+ restore();
+ }
+}
+void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
+ float srcRight, float srcBottom, float dstLeft, float dstTop,
+ float dstRight, float dstBottom, const SkPaint* paint) {
+ if (srcLeft == 0 && srcTop == 0
+ && srcRight == bitmap.width()
+ && srcBottom == bitmap.height()
+ && (srcBottom - srcTop == dstBottom - dstTop)
+ && (srcRight - srcLeft == dstRight - dstLeft)) {
+ // transform simple rect to rect drawing case into position bitmap ops, since they merge
+ save(SkCanvas::kMatrix_SaveFlag);
+ translate(dstLeft, dstTop);
+ drawBitmap(&bitmap, paint);
+ restore();
+ } else {
+ LOG_ALWAYS_FATAL("TODO!");
+ }
+}
+void RecordingCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
+ const float* vertices, const int* colors, const SkPaint* paint) {
+ LOG_ALWAYS_FATAL("TODO!");
+}
+void RecordingCanvas::drawNinePatch(const SkBitmap& bitmap, const android::Res_png_9patch& chunk,
+ float dstLeft, float dstTop, float dstRight, float dstBottom,
+ const SkPaint* paint) {
+ LOG_ALWAYS_FATAL("TODO!");
+}
+
+// Text
+void RecordingCanvas::drawText(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) {
+ LOG_ALWAYS_FATAL("TODO!");
+}
+void RecordingCanvas::drawPosText(const uint16_t* text, const float* positions, int count,
+ int posCount, const SkPaint& paint) {
+ LOG_ALWAYS_FATAL("TODO!");
+}
+void RecordingCanvas::drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path,
+ float hOffset, float vOffset, const SkPaint& paint) {
+ LOG_ALWAYS_FATAL("TODO!");
+}
+
+void RecordingCanvas::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) {
+ addOp(new (alloc()) BitmapOp(
+ Rect(0, 0, bitmap->width(), bitmap->height()),
+ *(mState.currentSnapshot()->transform),
+ mState.getRenderTargetClipBounds(),
+ refPaint(paint), refBitmap(*bitmap)));
+}
+void RecordingCanvas::drawRenderNode(RenderNode* renderNode) {
+ RenderNodeOp* op = new (alloc()) RenderNodeOp(
+ Rect(0, 0, renderNode->getWidth(), renderNode->getHeight()), // are these safe? they're theoretically dynamic
+ *(mState.currentSnapshot()->transform),
+ mState.getRenderTargetClipBounds(),
+ renderNode);
+ int opIndex = addOp(op);
+ int childIndex = mDisplayList->addChild(op);
+
+ // update the chunk's child indices
+ DisplayList::Chunk& chunk = mDisplayList->chunks.back();
+ chunk.endChildIndex = childIndex + 1;
+
+ if (renderNode->stagingProperties().isProjectionReceiver()) {
+ // use staging property, since recording on UI thread
+ mDisplayList->projectionReceiveIndex = opIndex;
+ }
+}
+
+size_t RecordingCanvas::addOp(RecordedOp* op) {
+ // TODO: validate if "addDrawOp" quickrejection logic is useful before adding
+ int insertIndex = mDisplayList->ops.size();
+ mDisplayList->ops.push_back(op);
+ if (mDeferredBarrierType != DeferredBarrierType::None) {
+ // op is first in new chunk
+ mDisplayList->chunks.emplace_back();
+ DisplayList::Chunk& newChunk = mDisplayList->chunks.back();
+ newChunk.beginOpIndex = insertIndex;
+ newChunk.endOpIndex = insertIndex + 1;
+ newChunk.reorderChildren = (mDeferredBarrierType == DeferredBarrierType::OutOfOrder);
+
+ int nextChildIndex = mDisplayList->children.size();
+ newChunk.beginChildIndex = newChunk.endChildIndex = nextChildIndex;
+ mDeferredBarrierType = DeferredBarrierType::None;
+ } else {
+ // standard case - append to existing chunk
+ mDisplayList->chunks.back().endOpIndex = insertIndex + 1;
+ }
+ return insertIndex;
+}
+
+void RecordingCanvas::refBitmapsInShader(const SkShader* shader) {
+ if (!shader) return;
+
+ // If this paint has an SkShader that has an SkBitmap add
+ // it to the bitmap pile
+ SkBitmap bitmap;
+ SkShader::TileMode xy[2];
+ if (shader->isABitmap(&bitmap, nullptr, xy)) {
+ refBitmap(bitmap);
+ return;
+ }
+ SkShader::ComposeRec rec;
+ if (shader->asACompose(&rec)) {
+ refBitmapsInShader(rec.fShaderA);
+ refBitmapsInShader(rec.fShaderB);
+ return;
+ }
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
new file mode 100644
index 0000000..fc84c98
--- /dev/null
+++ b/libs/hwui/RecordingCanvas.h
@@ -0,0 +1,306 @@
+/*
+ * 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 ANDROID_HWUI_RECORDING_CANVAS_H
+#define ANDROID_HWUI_RECORDING_CANVAS_H
+
+#include "Canvas.h"
+#include "CanvasState.h"
+#include "DisplayList.h"
+#include "utils/LinearAllocator.h"
+#include "utils/NinePatch.h"
+#include "ResourceCache.h"
+#include "SkiaCanvasProxy.h"
+#include "Snapshot.h"
+
+#include <SkDrawFilter.h>
+#include <SkPaint.h>
+#include <SkTLazy.h>
+
+#include <vector>
+
+namespace android {
+namespace uirenderer {
+
+class OpReceiver;
+struct RecordedOp;
+
+class RecordingCanvas: public Canvas, public CanvasStateClient {
+ enum class DeferredBarrierType {
+ None,
+ InOrder,
+ OutOfOrder,
+ };
+public:
+ RecordingCanvas(size_t width, size_t height);
+ virtual ~RecordingCanvas();
+
+ void reset(int width, int height);
+ __attribute__((warn_unused_result)) DisplayList* finishRecording();
+
+// ----------------------------------------------------------------------------
+// MISC HWUI OPERATIONS - TODO: CATEGORIZE
+// ----------------------------------------------------------------------------
+ void insertReorderBarrier(bool enableReorder) {
+ mDeferredBarrierType = enableReorder
+ ? DeferredBarrierType::OutOfOrder : DeferredBarrierType::InOrder;
+ }
+ void drawRenderNode(RenderNode* renderNode);
+
+// ----------------------------------------------------------------------------
+// CanvasStateClient interface
+// ----------------------------------------------------------------------------
+ virtual void onViewportInitialized() override;
+ virtual void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) override;
+ virtual GLuint getTargetFbo() const override { return -1; }
+
+// ----------------------------------------------------------------------------
+// android/graphics/Canvas interface
+// ----------------------------------------------------------------------------
+ virtual SkCanvas* asSkCanvas() override;
+
+ virtual void setBitmap(const SkBitmap& bitmap) override {
+ LOG_ALWAYS_FATAL("RecordingCanvas is not backed by a bitmap.");
+ }
+
+ virtual bool isOpaque() override { return false; }
+ virtual int width() override { return mState.getWidth(); }
+ virtual int height() override { return mState.getHeight(); }
+
+ virtual void setHighContrastText(bool highContrastText) override {
+ mHighContrastText = highContrastText;
+ }
+ virtual bool isHighContrastText() override { return mHighContrastText; }
+
+// ----------------------------------------------------------------------------
+// android/graphics/Canvas state operations
+// ----------------------------------------------------------------------------
+ // Save (layer)
+ virtual int getSaveCount() const override { return mState.getSaveCount(); }
+ virtual int save(SkCanvas::SaveFlags flags) override;
+ virtual void restore() override;
+ virtual void restoreToCount(int saveCount) override;
+
+ virtual int saveLayer(float left, float top, float right, float bottom, const SkPaint* paint,
+ SkCanvas::SaveFlags flags) override;
+ virtual int saveLayerAlpha(float left, float top, float right, float bottom,
+ int alpha, SkCanvas::SaveFlags flags) override {
+ SkPaint paint;
+ paint.setAlpha(alpha);
+ return saveLayer(left, top, right, bottom, &paint, flags);
+ }
+
+ // Matrix
+ virtual void getMatrix(SkMatrix* outMatrix) const override { mState.getMatrix(outMatrix); }
+ virtual void setMatrix(const SkMatrix& matrix) override { mState.setMatrix(matrix); }
+
+ virtual void concat(const SkMatrix& matrix) override { mState.concatMatrix(matrix); }
+ virtual void rotate(float degrees) override;
+ virtual void scale(float sx, float sy) override;
+ virtual void skew(float sx, float sy) override;
+ virtual void translate(float dx, float dy) override;
+
+ // Clip
+ virtual bool getClipBounds(SkRect* outRect) const override;
+ virtual bool quickRejectRect(float left, float top, float right, float bottom) const override;
+ virtual bool quickRejectPath(const SkPath& path) const override;
+
+ virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op) override;
+ virtual bool clipPath(const SkPath* path, SkRegion::Op op) override;
+ virtual bool clipRegion(const SkRegion* region, SkRegion::Op op) override;
+
+ // Misc
+ virtual SkDrawFilter* getDrawFilter() override { return mDrawFilter.get(); }
+ virtual void setDrawFilter(SkDrawFilter* filter) override {
+ mDrawFilter.reset(SkSafeRef(filter));
+ }
+
+// ----------------------------------------------------------------------------
+// android/graphics/Canvas draw operations
+// ----------------------------------------------------------------------------
+ virtual void drawColor(int color, SkXfermode::Mode mode) override;
+ virtual void drawPaint(const SkPaint& paint) override;
+
+ // Geometry
+ virtual void drawPoint(float x, float y, const SkPaint& paint) override {
+ float points[2] = { x, y };
+ drawPoints(points, 2, paint);
+ }
+ virtual void drawPoints(const float* points, int count, const SkPaint& paint) override;
+ virtual void drawLine(float startX, float startY, float stopX, float stopY,
+ const SkPaint& paint) override {
+ float points[4] = { startX, startY, stopX, stopY };
+ drawLines(points, 4, paint);
+ }
+ virtual void drawLines(const float* points, int count, const SkPaint& paint) override;
+ virtual void drawRect(float left, float top, float right, float bottom, const SkPaint& paint) override;
+ virtual void drawRegion(const SkRegion& region, const SkPaint& paint) override;
+ virtual void drawRoundRect(float left, float top, float right, float bottom,
+ float rx, float ry, const SkPaint& paint) override;
+ virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) override;
+ virtual void drawOval(float left, float top, float right, float bottom, const SkPaint& paint) override;
+ virtual void drawArc(float left, float top, float right, float bottom,
+ float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) override;
+ virtual void drawPath(const SkPath& path, const SkPaint& paint) override;
+ virtual void drawVertices(SkCanvas::VertexMode vertexMode, int vertexCount,
+ const float* verts, const float* tex, const int* colors,
+ const uint16_t* indices, int indexCount, const SkPaint& paint) override
+ { /* RecordingCanvas does not support drawVertices(); ignore */ }
+
+ // Bitmap-based
+ virtual void drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint) override;
+ virtual void drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
+ const SkPaint* paint) override;
+ virtual void drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
+ float srcRight, float srcBottom, float dstLeft, float dstTop,
+ float dstRight, float dstBottom, const SkPaint* paint) override;
+ virtual void drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
+ const float* vertices, const int* colors, const SkPaint* paint) override;
+ virtual void drawNinePatch(const SkBitmap& bitmap, const android::Res_png_9patch& chunk,
+ float dstLeft, float dstTop, float dstRight, float dstBottom,
+ const SkPaint* paint) override;
+
+ // Text
+ virtual void drawText(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) override;
+ virtual void drawPosText(const uint16_t* text, const float* positions, int count,
+ int posCount, const SkPaint& paint) override;
+ virtual void drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path,
+ float hOffset, float vOffset, const SkPaint& paint) override;
+ virtual bool drawTextAbsolutePos() const override { return false; }
+
+private:
+
+ void drawBitmap(const SkBitmap* bitmap, const SkPaint* paint);
+ void drawSimpleRects(const float* rects, int vertexCount, const SkPaint* paint);
+
+
+ size_t addOp(RecordedOp* op);
+// ----------------------------------------------------------------------------
+// lazy object copy
+// ----------------------------------------------------------------------------
+ LinearAllocator& alloc() { return mDisplayList->allocator; }
+
+ void refBitmapsInShader(const SkShader* shader);
+
+ template<class T>
+ inline const T* refBuffer(const T* srcBuffer, int32_t count) {
+ if (!srcBuffer) return nullptr;
+
+ T* dstBuffer = (T*) mDisplayList->allocator.alloc(count * sizeof(T));
+ memcpy(dstBuffer, srcBuffer, count * sizeof(T));
+ return dstBuffer;
+ }
+
+ inline char* refText(const char* text, size_t byteLength) {
+ return (char*) refBuffer<uint8_t>((uint8_t*)text, byteLength);
+ }
+
+ inline const SkPath* refPath(const SkPath* path) {
+ if (!path) return nullptr;
+
+ // The points/verbs within the path are refcounted so this copy operation
+ // is inexpensive and maintains the generationID of the original path.
+ const SkPath* cachedPath = new SkPath(*path);
+ mDisplayList->pathResources.push_back(cachedPath);
+ return cachedPath;
+ }
+
+ inline const SkPaint* refPaint(const SkPaint* paint) {
+ if (!paint) return nullptr;
+
+ // If there is a draw filter apply it here and store the modified paint
+ // so that we don't need to modify the paint every time we access it.
+ SkTLazy<SkPaint> filteredPaint;
+ if (mDrawFilter.get()) {
+ filteredPaint.set(*paint);
+ mDrawFilter->filter(filteredPaint.get(), SkDrawFilter::kPaint_Type);
+ paint = filteredPaint.get();
+ }
+
+ // compute the hash key for the paint and check the cache.
+ const uint32_t key = paint->getHash();
+ const SkPaint* cachedPaint = mPaintMap.valueFor(key);
+ // In the unlikely event that 2 unique paints have the same hash we do a
+ // object equality check to ensure we don't erroneously dedup them.
+ if (cachedPaint == nullptr || *cachedPaint != *paint) {
+ cachedPaint = new SkPaint(*paint);
+ std::unique_ptr<const SkPaint> copy(cachedPaint);
+ mDisplayList->paints.push_back(std::move(copy));
+
+ // replaceValueFor() performs an add if the entry doesn't exist
+ mPaintMap.replaceValueFor(key, cachedPaint);
+ refBitmapsInShader(cachedPaint->getShader());
+ }
+
+ return cachedPaint;
+ }
+
+ inline const SkRegion* refRegion(const SkRegion* region) {
+ if (!region) {
+ return region;
+ }
+
+ const SkRegion* cachedRegion = mRegionMap.valueFor(region);
+ // TODO: Add generation ID to SkRegion
+ if (cachedRegion == nullptr) {
+ std::unique_ptr<const SkRegion> copy(new SkRegion(*region));
+ cachedRegion = copy.get();
+ mDisplayList->regions.push_back(std::move(copy));
+
+ // replaceValueFor() performs an add if the entry doesn't exist
+ mRegionMap.replaceValueFor(region, cachedRegion);
+ }
+
+ return cachedRegion;
+ }
+
+ inline const SkBitmap* refBitmap(const SkBitmap& bitmap) {
+ // Note that this assumes the bitmap is immutable. There are cases this won't handle
+ // correctly, such as creating the bitmap from scratch, drawing with it, changing its
+ // contents, and drawing again. The only fix would be to always copy it the first time,
+ // which doesn't seem worth the extra cycles for this unlikely case.
+ SkBitmap* localBitmap = new (alloc()) SkBitmap(bitmap);
+ alloc().autoDestroy(localBitmap);
+ mDisplayList->bitmapResources.push_back(localBitmap);
+ return localBitmap;
+ }
+
+ inline const Res_png_9patch* refPatch(const Res_png_9patch* patch) {
+ mDisplayList->patchResources.push_back(patch);
+ mResourceCache.incrementRefcount(patch);
+ return patch;
+ }
+
+ DefaultKeyedVector<uint32_t, const SkPaint*> mPaintMap;
+ DefaultKeyedVector<const SkPath*, const SkPath*> mPathMap;
+ DefaultKeyedVector<const SkRegion*, const SkRegion*> mRegionMap;
+
+ CanvasState mState;
+ std::unique_ptr<SkiaCanvasProxy> mSkiaCanvasProxy;
+ ResourceCache& mResourceCache;
+ DeferredBarrierType mDeferredBarrierType = DeferredBarrierType::None;
+ DisplayList* mDisplayList = nullptr;
+ bool mHighContrastText = false;
+ SkAutoTUnref<SkDrawFilter> mDrawFilter;
+ int mRestoreSaveCount = -1;
+}; // class RecordingCanvas
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_RECORDING_CANVAS_H
diff --git a/libs/hwui/RenderBufferCache.cpp b/libs/hwui/RenderBufferCache.cpp
index 8beed25..11d7a6a 100644
--- a/libs/hwui/RenderBufferCache.cpp
+++ b/libs/hwui/RenderBufferCache.cpp
@@ -14,12 +14,14 @@
* limitations under the License.
*/
-#include <utils/Log.h>
-
#include "Debug.h"
#include "Properties.h"
#include "RenderBufferCache.h"
+#include <utils/Log.h>
+
+#include <cstdlib>
+
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index bf1b4d0..15ca718 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -16,15 +16,12 @@
#include "RenderNode.h"
-#include <algorithm>
-#include <string>
-
-#include <SkCanvas.h>
-#include <algorithm>
-
-
#include "DamageAccumulator.h"
#include "Debug.h"
+#if HWUI_NEW_OPS
+#include "RecordedOp.h"
+#include "BakedOpRenderer.h"
+#endif
#include "DisplayListOp.h"
#include "LayerRenderer.h"
#include "OpenGLRenderer.h"
@@ -36,46 +33,64 @@
#include "protos/hwui.pb.h"
#include "protos/ProtoHelpers.h"
+#include <SkCanvas.h>
+
+#include <algorithm>
+#include <sstream>
+#include <string>
+
namespace android {
namespace uirenderer {
void RenderNode::debugDumpLayers(const char* prefix) {
+#if HWUI_NEW_OPS
+ LOG_ALWAYS_FATAL("TODO: dump layer");
+#else
if (mLayer) {
ALOGD("%sNode %p (%s) has layer %p (fbo = %u, wasBuildLayered = %s)",
prefix, this, getName(), mLayer, mLayer->getFbo(),
mLayer->wasBuildLayered ? "true" : "false");
}
- if (mDisplayListData) {
- for (size_t i = 0; i < mDisplayListData->children().size(); i++) {
- mDisplayListData->children()[i]->mRenderNode->debugDumpLayers(prefix);
+#endif
+ if (mDisplayList) {
+ for (auto&& child : mDisplayList->getChildren()) {
+ child->renderNode->debugDumpLayers(prefix);
}
}
}
RenderNode::RenderNode()
: mDirtyPropertyFields(0)
- , mNeedsDisplayListDataSync(false)
- , mDisplayListData(nullptr)
- , mStagingDisplayListData(nullptr)
+ , mNeedsDisplayListSync(false)
+ , mDisplayList(nullptr)
+ , mStagingDisplayList(nullptr)
, mAnimatorManager(*this)
- , mLayer(nullptr)
, mParentCount(0) {
}
RenderNode::~RenderNode() {
- deleteDisplayListData();
- delete mStagingDisplayListData;
+ deleteDisplayList();
+ delete mStagingDisplayList;
+#if HWUI_NEW_OPS
+ LOG_ALWAYS_FATAL_IF(mLayer, "layer missed detachment!");
+#else
if (mLayer) {
ALOGW("Memory Warning: Layer %p missed its detachment, held on to for far too long!", mLayer);
mLayer->postDecStrong();
mLayer = nullptr;
}
+#endif
}
-void RenderNode::setStagingDisplayList(DisplayListData* data) {
- mNeedsDisplayListDataSync = true;
- delete mStagingDisplayListData;
- mStagingDisplayListData = data;
+void RenderNode::setStagingDisplayList(DisplayList* displayList) {
+ mNeedsDisplayListSync = true;
+ delete mStagingDisplayList;
+ mStagingDisplayList = displayList;
+ // If mParentCount == 0 we are the sole reference to this RenderNode,
+ // so immediately free the old display list
+ if (!mParentCount && !mStagingDisplayList) {
+ deleteDisplayList();
+ }
}
/**
@@ -94,12 +109,16 @@
SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
properties().debugOutputProperties(level);
- int flags = DisplayListOp::kOpLogFlag_Recurse;
- if (mDisplayListData) {
+
+ if (mDisplayList) {
+#if HWUI_NEW_OPS
+ LOG_ALWAYS_FATAL("op dumping unsupported");
+#else
// TODO: consider printing the chunk boundaries here
- for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
- mDisplayListData->displayListOps[i]->output(level, flags);
+ for (auto&& op : mDisplayList->getOps()) {
+ op->output(level, DisplayListOp::kOpLogFlag_Recurse);
}
+#endif
}
ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, getName());
@@ -170,20 +189,20 @@
}
pnode->clear_children();
- if (mDisplayListData) {
- for (auto&& child : mDisplayListData->children()) {
- child->mRenderNode->copyTo(pnode->add_children());
+ if (mDisplayList) {
+ for (auto&& child : mDisplayList->getChildren()) {
+ child->renderNode->copyTo(pnode->add_children());
}
}
}
int RenderNode::getDebugSize() {
int size = sizeof(RenderNode);
- if (mStagingDisplayListData) {
- size += mStagingDisplayListData->getUsedSize();
+ if (mStagingDisplayList) {
+ size += mStagingDisplayList->getUsedSize();
}
- if (mDisplayListData && mDisplayListData != mStagingDisplayListData) {
- size += mDisplayListData->getUsedSize();
+ if (mDisplayList && mDisplayList != mStagingDisplayList) {
+ size += mDisplayList->getUsedSize();
}
return size;
}
@@ -229,13 +248,29 @@
}
}
+layer_t* createLayer(RenderState& renderState, uint32_t width, uint32_t height) {
+#if HWUI_NEW_OPS
+ return BakedOpRenderer::createOffscreenBuffer(renderState, width, height);
+#else
+ return LayerRenderer::createRenderLayer(renderState, width, height);
+#endif
+}
+
+void destroyLayer(layer_t* layer) {
+#if HWUI_NEW_OPS
+ BakedOpRenderer::destroyOffscreenBuffer(layer);
+#else
+ LayerRenderer::destroyLayer(layer);
+#endif
+}
+
void RenderNode::pushLayerUpdate(TreeInfo& info) {
LayerType layerType = properties().effectiveLayerType();
// If we are not a layer OR we cannot be rendered (eg, view was detached)
// we need to destroy any Layers we may have had previously
if (CC_LIKELY(layerType != LayerType::RenderLayer) || CC_UNLIKELY(!isRenderable())) {
if (CC_UNLIKELY(mLayer)) {
- LayerRenderer::destroyLayer(mLayer);
+ destroyLayer(mLayer);
mLayer = nullptr;
}
return;
@@ -243,13 +278,20 @@
bool transformUpdateNeeded = false;
if (!mLayer) {
- mLayer = LayerRenderer::createRenderLayer(info.renderState, getWidth(), getHeight());
- applyLayerPropertiesToLayer(info);
- damageSelf(info);
- transformUpdateNeeded = true;
+ mLayer = createLayer(info.canvasContext.getRenderState(), getWidth(), getHeight());
+ damageSelf(info);
+ transformUpdateNeeded = true;
+#if HWUI_NEW_OPS
+ } else if (mLayer->viewportWidth != (uint32_t) getWidth()
+ || mLayer->viewportHeight != (uint32_t)getHeight()) {
+ // TODO: allow node's layer to grow larger
+ if ((uint32_t)getWidth() > mLayer->texture.width
+ || (uint32_t)getHeight() > mLayer->texture.height) {
+#else
} else if (mLayer->layer.getWidth() != getWidth() || mLayer->layer.getHeight() != getHeight()) {
if (!LayerRenderer::resizeLayer(mLayer, getWidth(), getHeight())) {
- LayerRenderer::destroyLayer(mLayer);
+#endif
+ destroyLayer(mLayer);
mLayer = nullptr;
}
damageSelf(info);
@@ -262,9 +304,16 @@
if (!mLayer) {
Caches::getInstance().dumpMemoryUsage();
if (info.errorHandler) {
- std::string msg = "Unable to create layer for ";
- msg += getName();
- info.errorHandler->onError(msg);
+ std::ostringstream err;
+ err << "Unable to create layer for " << getName();
+ const int maxTextureSize = Caches::getInstance().maxTextureSize;
+ if (getWidth() > maxTextureSize || getHeight() > maxTextureSize) {
+ err << ", size " << getWidth() << "x" << getHeight()
+ << " exceeds max size " << maxTextureSize;
+ } else {
+ err << ", see logcat for more info";
+ }
+ info.errorHandler->onError(err.str());
}
return;
}
@@ -273,9 +322,16 @@
// update the transform in window of the layer to reset its origin wrt light source position
Matrix4 windowTransform;
info.damageAccumulator->computeCurrentTransform(&windowTransform);
+#if HWUI_NEW_OPS
+ // TODO: update layer transform (perhaps as part of enqueueLayerWithDamage)
+#else
mLayer->setWindowTransform(windowTransform);
+#endif
}
+#if HWUI_NEW_OPS
+ info.layerUpdateQueue->enqueueLayerWithDamage(this, dirty);
+#else
if (dirty.intersect(0, 0, getWidth(), getHeight())) {
dirty.roundOut(&dirty);
mLayer->updateDeferred(this, dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom);
@@ -285,13 +341,12 @@
if (info.renderer && mLayer->deferredUpdateScheduled) {
info.renderer->pushLayerUpdate(mLayer);
}
+#endif
- if (info.canvasContext) {
- // There might be prefetched layers that need to be accounted for.
- // That might be us, so tell CanvasContext that this layer is in the
- // tree and should not be destroyed.
- info.canvasContext->markLayerInUse(this);
- }
+ // There might be prefetched layers that need to be accounted for.
+ // That might be us, so tell CanvasContext that this layer is in the
+ // tree and should not be destroyed.
+ info.canvasContext.markLayerInUse(this);
}
/**
@@ -314,10 +369,10 @@
}
bool willHaveFunctor = false;
- if (info.mode == TreeInfo::MODE_FULL && mStagingDisplayListData) {
- willHaveFunctor = !mStagingDisplayListData->functors.isEmpty();
- } else if (mDisplayListData) {
- willHaveFunctor = !mDisplayListData->functors.isEmpty();
+ if (info.mode == TreeInfo::MODE_FULL && mStagingDisplayList) {
+ willHaveFunctor = !mStagingDisplayList->getFunctors().empty();
+ } else if (mDisplayList) {
+ willHaveFunctor = !mDisplayList->getFunctors().empty();
}
bool childFunctorsNeedLayer = mProperties.prepareForFunctorPresence(
willHaveFunctor, functorsNeedLayer);
@@ -326,12 +381,16 @@
if (info.mode == TreeInfo::MODE_FULL) {
pushStagingDisplayListChanges(info);
}
- prepareSubTree(info, childFunctorsNeedLayer, mDisplayListData);
+ prepareSubTree(info, childFunctorsNeedLayer, mDisplayList);
pushLayerUpdate(info);
info.damageAccumulator->popTransform();
}
+void RenderNode::syncProperties() {
+ mProperties = mStagingProperties;
+}
+
void RenderNode::pushStagingPropertiesChanges(TreeInfo& info) {
// Push the animators first so that setupStartValueIfNecessary() is called
// before properties() is trampled by stagingProperties(), as they are
@@ -343,8 +402,10 @@
mDirtyPropertyFields = 0;
damageSelf(info);
info.damageAccumulator->popTransform();
- mProperties = mStagingProperties;
+ syncProperties();
+#if !HWUI_NEW_OPS
applyLayerPropertiesToLayer(info);
+#endif
// We could try to be clever and only re-damage if the matrix changed.
// However, we don't need to worry about that. The cost of over-damaging
// here is only going to be a single additional map rect of this node
@@ -355,6 +416,7 @@
}
}
+#if !HWUI_NEW_OPS
void RenderNode::applyLayerPropertiesToLayer(TreeInfo& info) {
if (CC_LIKELY(!mLayer)) return;
@@ -363,57 +425,66 @@
mLayer->setColorFilter(props.colorFilter());
mLayer->setBlend(props.needsBlending());
}
+#endif
+
+void RenderNode::syncDisplayList() {
+ // Make sure we inc first so that we don't fluctuate between 0 and 1,
+ // which would thrash the layer cache
+ if (mStagingDisplayList) {
+ for (auto&& child : mStagingDisplayList->getChildren()) {
+ child->renderNode->incParentRefCount();
+ }
+ }
+ deleteDisplayList();
+ mDisplayList = mStagingDisplayList;
+ mStagingDisplayList = nullptr;
+ if (mDisplayList) {
+ for (size_t i = 0; i < mDisplayList->getFunctors().size(); i++) {
+ (*mDisplayList->getFunctors()[i])(DrawGlInfo::kModeSync, nullptr);
+ }
+ }
+}
void RenderNode::pushStagingDisplayListChanges(TreeInfo& info) {
- if (mNeedsDisplayListDataSync) {
- mNeedsDisplayListDataSync = false;
- // Make sure we inc first so that we don't fluctuate between 0 and 1,
- // which would thrash the layer cache
- if (mStagingDisplayListData) {
- for (size_t i = 0; i < mStagingDisplayListData->children().size(); i++) {
- mStagingDisplayListData->children()[i]->mRenderNode->incParentRefCount();
- }
- }
+ if (mNeedsDisplayListSync) {
+ mNeedsDisplayListSync = false;
// Damage with the old display list first then the new one to catch any
// changes in isRenderable or, in the future, bounds
damageSelf(info);
- deleteDisplayListData();
- mDisplayListData = mStagingDisplayListData;
- mStagingDisplayListData = nullptr;
- if (mDisplayListData) {
- for (size_t i = 0; i < mDisplayListData->functors.size(); i++) {
- (*mDisplayListData->functors[i])(DrawGlInfo::kModeSync, nullptr);
- }
- }
+ syncDisplayList();
damageSelf(info);
}
}
-void RenderNode::deleteDisplayListData() {
- if (mDisplayListData) {
- for (size_t i = 0; i < mDisplayListData->children().size(); i++) {
- mDisplayListData->children()[i]->mRenderNode->decParentRefCount();
+void RenderNode::deleteDisplayList() {
+ if (mDisplayList) {
+ for (auto&& child : mDisplayList->getChildren()) {
+ child->renderNode->decParentRefCount();
}
}
- delete mDisplayListData;
- mDisplayListData = nullptr;
+ delete mDisplayList;
+ mDisplayList = nullptr;
}
-void RenderNode::prepareSubTree(TreeInfo& info, bool functorsNeedLayer, DisplayListData* subtree) {
+void RenderNode::prepareSubTree(TreeInfo& info, bool functorsNeedLayer, DisplayList* subtree) {
if (subtree) {
TextureCache& cache = Caches::getInstance().textureCache;
- info.out.hasFunctors |= subtree->functors.size();
- for (size_t i = 0; info.prepareTextures && i < subtree->bitmapResources.size(); i++) {
- info.prepareTextures = cache.prefetchAndMarkInUse(
- info.canvasContext, subtree->bitmapResources[i]);
+ info.out.hasFunctors |= subtree->getFunctors().size();
+ for (auto&& bitmapResource : subtree->getBitmapResources()) {
+ void* ownerToken = &info.canvasContext;
+ info.prepareTextures = cache.prefetchAndMarkInUse(ownerToken, bitmapResource);
}
- for (size_t i = 0; i < subtree->children().size(); i++) {
- DrawRenderNodeOp* op = subtree->children()[i];
- RenderNode* childNode = op->mRenderNode;
+ for (auto&& op : subtree->getChildren()) {
+ RenderNode* childNode = op->renderNode;
+#if HWUI_NEW_OPS
+ info.damageAccumulator->pushTransform(&op->localMatrix);
+ bool childFunctorsNeedLayer = functorsNeedLayer; // TODO! || op->mRecordedWithPotentialStencilClip;
+#else
info.damageAccumulator->pushTransform(&op->mTransformFromParent);
bool childFunctorsNeedLayer = functorsNeedLayer
// Recorded with non-rect clip, or canvas-rotated by parent
|| op->mRecordedWithPotentialStencilClip;
+#endif
childNode->prepareTreeImpl(info, childFunctorsNeedLayer);
info.damageAccumulator->popTransform();
}
@@ -422,17 +493,17 @@
void RenderNode::destroyHardwareResources() {
if (mLayer) {
- LayerRenderer::destroyLayer(mLayer);
+ destroyLayer(mLayer);
mLayer = nullptr;
}
- if (mDisplayListData) {
- for (size_t i = 0; i < mDisplayListData->children().size(); i++) {
- mDisplayListData->children()[i]->mRenderNode->destroyHardwareResources();
+ if (mDisplayList) {
+ for (auto&& child : mDisplayList->getChildren()) {
+ child->renderNode->destroyHardwareResources();
}
- if (mNeedsDisplayListDataSync) {
+ if (mNeedsDisplayListSync) {
// Next prepare tree we are going to push a new display list, so we can
// drop our current one now
- deleteDisplayListData();
+ deleteDisplayList();
}
}
}
@@ -449,6 +520,76 @@
}
}
+bool RenderNode::applyViewProperties(CanvasState& canvasState, LinearAllocator& allocator) const {
+ const Outline& outline = properties().getOutline();
+ if (properties().getAlpha() <= 0
+ || (outline.getShouldClip() && outline.isEmpty())
+ || properties().getScaleX() == 0
+ || properties().getScaleY() == 0) {
+ return false; // rejected
+ }
+
+ if (properties().getLeft() != 0 || properties().getTop() != 0) {
+ canvasState.translate(properties().getLeft(), properties().getTop());
+ }
+ if (properties().getStaticMatrix()) {
+ canvasState.concatMatrix(*properties().getStaticMatrix());
+ } else if (properties().getAnimationMatrix()) {
+ canvasState.concatMatrix(*properties().getAnimationMatrix());
+ }
+ if (properties().hasTransformMatrix()) {
+ if (properties().isTransformTranslateOnly()) {
+ canvasState.translate(properties().getTranslationX(), properties().getTranslationY());
+ } else {
+ canvasState.concatMatrix(*properties().getTransformMatrix());
+ }
+ }
+
+ const bool isLayer = properties().effectiveLayerType() != LayerType::None;
+ int clipFlags = properties().getClippingFlags();
+ if (properties().getAlpha() < 1) {
+ if (isLayer) {
+ clipFlags &= ~CLIP_TO_BOUNDS; // bounds clipping done by layer
+ }
+ if (CC_LIKELY(isLayer || !properties().getHasOverlappingRendering())) {
+ // simply scale rendering content's alpha
+ canvasState.scaleAlpha(properties().getAlpha());
+ } else {
+ // savelayer needed to create an offscreen buffer
+ Rect layerBounds(0, 0, getWidth(), getHeight());
+ if (clipFlags) {
+ properties().getClippingRectForFlags(clipFlags, &layerBounds);
+ clipFlags = 0; // all clipping done by savelayer
+ }
+ LOG_ALWAYS_FATAL("TODO: savelayer");
+ }
+
+ if (CC_UNLIKELY(ATRACE_ENABLED() && properties().promotedToLayer())) {
+ // pretend alpha always causes savelayer to warn about
+ // performance problem affecting old versions
+ ATRACE_FORMAT("%s alpha caused saveLayer %dx%d", getName(), getWidth(), getHeight());
+ }
+ }
+ if (clipFlags) {
+ Rect clipRect;
+ properties().getClippingRectForFlags(clipFlags, &clipRect);
+ canvasState.clipRect(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom,
+ SkRegion::kIntersect_Op);
+ }
+
+ // TODO: support nesting round rect clips
+ if (mProperties.getRevealClip().willClip()) {
+ Rect bounds;
+ mProperties.getRevealClip().getBounds(&bounds);
+ canvasState.setClippingRoundRect(allocator,
+ bounds, mProperties.getRevealClip().getRadius());
+ } else if (mProperties.getOutline().willClip()) {
+ canvasState.setClippingOutline(allocator, &(mProperties.getOutline()));
+ }
+ return !canvasState.quickRejectConservative(
+ 0, 0, properties().getWidth(), properties().getHeight());
+}
+
/*
* For property operations, we pass a savecount of 0, since the operations aren't part of the
* displaylist, and thus don't have to compensate for the record-time/playback-time discrepancy in
@@ -580,24 +721,27 @@
* which are flagged to not draw in the standard draw loop.
*/
void RenderNode::computeOrdering() {
+#if !HWUI_NEW_OPS
ATRACE_CALL();
mProjectedNodes.clear();
// TODO: create temporary DDLOp and call computeOrderingImpl on top DisplayList so that
// transform properties are applied correctly to top level children
- if (mDisplayListData == nullptr) return;
- for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) {
- DrawRenderNodeOp* childOp = mDisplayListData->children()[i];
- childOp->mRenderNode->computeOrderingImpl(childOp, &mProjectedNodes, &mat4::identity());
+ if (mDisplayList == nullptr) return;
+ for (unsigned int i = 0; i < mDisplayList->getChildren().size(); i++) {
+ DrawRenderNodeOp* childOp = mDisplayList->getChildren()[i];
+ childOp->renderNode->computeOrderingImpl(childOp, &mProjectedNodes, &mat4::identity());
}
+#endif
}
void RenderNode::computeOrderingImpl(
DrawRenderNodeOp* opState,
std::vector<DrawRenderNodeOp*>* compositedChildrenOfProjectionSurface,
const mat4* transformFromProjectionSurface) {
+#if !HWUI_NEW_OPS
mProjectedNodes.clear();
- if (mDisplayListData == nullptr || mDisplayListData->isEmpty()) return;
+ if (mDisplayList == nullptr || mDisplayList->isEmpty()) return;
// TODO: should avoid this calculation in most cases
// TODO: just calculate single matrix, down to all leaf composited elements
@@ -614,12 +758,12 @@
opState->mSkipInOrderDraw = false;
}
- if (mDisplayListData->children().size() > 0) {
- const bool isProjectionReceiver = mDisplayListData->projectionReceiveIndex >= 0;
+ if (mDisplayList->getChildren().size() > 0) {
+ const bool isProjectionReceiver = mDisplayList->projectionReceiveIndex >= 0;
bool haveAppliedPropertiesToProjection = false;
- for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) {
- DrawRenderNodeOp* childOp = mDisplayListData->children()[i];
- RenderNode* child = childOp->mRenderNode;
+ for (unsigned int i = 0; i < mDisplayList->getChildren().size(); i++) {
+ DrawRenderNodeOp* childOp = mDisplayList->getChildren()[i];
+ RenderNode* child = childOp->renderNode;
std::vector<DrawRenderNodeOp*>* projectionChildren = nullptr;
const mat4* projectionTransform = nullptr;
@@ -642,6 +786,7 @@
child->computeOrderingImpl(childOp, projectionChildren, projectionTransform);
}
}
+#endif
}
class DeferOperationHandler {
@@ -699,13 +844,14 @@
issueOperations<ReplayOperationHandler>(replayStruct.mRenderer, handler);
}
-void RenderNode::buildZSortedChildList(const DisplayListData::Chunk& chunk,
+void RenderNode::buildZSortedChildList(const DisplayList::Chunk& chunk,
std::vector<ZDrawRenderNodeOpPair>& zTranslatedNodes) {
+#if !HWUI_NEW_OPS
if (chunk.beginChildIndex == chunk.endChildIndex) return;
for (unsigned int i = chunk.beginChildIndex; i < chunk.endChildIndex; i++) {
- DrawRenderNodeOp* childOp = mDisplayListData->children()[i];
- RenderNode* child = childOp->mRenderNode;
+ DrawRenderNodeOp* childOp = mDisplayList->getChildren()[i];
+ RenderNode* child = childOp->renderNode;
float childZ = child->properties().getZ();
if (!MathUtils::isZero(childZ) && chunk.reorderChildren) {
@@ -719,6 +865,7 @@
// Z sort any 3d children (stable-ness makes z compare fall back to standard drawing order)
std::stable_sort(zTranslatedNodes.begin(), zTranslatedNodes.end());
+#endif
}
template <class T>
@@ -824,7 +971,7 @@
while (shadowIndex < endIndex || drawIndex < endIndex) {
if (shadowIndex < endIndex) {
DrawRenderNodeOp* casterOp = zTranslatedNodes[shadowIndex].value;
- RenderNode* caster = casterOp->mRenderNode;
+ RenderNode* caster = casterOp->renderNode;
const float casterZ = zTranslatedNodes[shadowIndex].key;
// attempt to render the shadow if the caster about to be drawn is its caster,
// OR if its caster's Z value is similar to the previous potential caster
@@ -867,12 +1014,17 @@
// Transform renderer to match background we're projecting onto
// (by offsetting canvas by translationX/Y of background rendernode, since only those are set)
const DisplayListOp* op =
- (mDisplayListData->displayListOps[mDisplayListData->projectionReceiveIndex]);
+#if HWUI_NEW_OPS
+ nullptr;
+ LOG_ALWAYS_FATAL("unsupported");
+#else
+ (mDisplayList->getOps()[mDisplayList->projectionReceiveIndex]);
+#endif
const DrawRenderNodeOp* backgroundOp = reinterpret_cast<const DrawRenderNodeOp*>(op);
- const RenderProperties& backgroundProps = backgroundOp->mRenderNode->properties();
+ const RenderProperties& backgroundProps = backgroundOp->renderNode->properties();
renderer.translate(backgroundProps.getTranslationX(), backgroundProps.getTranslationY());
- // If the projection reciever has an outline, we mask projected content to it
+ // If the projection receiver has an outline, we mask projected content to it
// (which we know, apriori, are all tessellated paths)
renderer.setProjectionPathMask(alloc, projectionReceiverOutline);
@@ -904,13 +1056,17 @@
*/
template <class T>
void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
- if (mDisplayListData->isEmpty()) {
+ if (mDisplayList->isEmpty()) {
DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", handler.level() * 2, "",
this, getName());
return;
}
+#if HWUI_NEW_OPS
+ const bool drawLayer = false;
+#else
const bool drawLayer = (mLayer && (&renderer != mLayer->renderer.get()));
+#endif
// If we are updating the contents of mLayer, we don't want to apply any of
// the RenderNode's properties to this issueOperations pass. Those will all
// be applied when the layer is drawn, aka when this is true.
@@ -948,6 +1104,9 @@
setViewProperties<T>(renderer, handler);
}
+#if HWUI_NEW_OPS
+ LOG_ALWAYS_FATAL("legacy op traversal not supported");
+#else
bool quickRejected = properties().getClipToBounds()
&& renderer.quickRejectConservative(0, 0, properties().getWidth(), properties().getHeight());
if (!quickRejected) {
@@ -959,9 +1118,9 @@
renderer.getSaveCount() - 1, properties().getClipToBounds());
} else {
const int saveCountOffset = renderer.getSaveCount() - 1;
- const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex;
- for (size_t chunkIndex = 0; chunkIndex < mDisplayListData->getChunks().size(); chunkIndex++) {
- const DisplayListData::Chunk& chunk = mDisplayListData->getChunks()[chunkIndex];
+ const int projectionReceiveIndex = mDisplayList->projectionReceiveIndex;
+ for (size_t chunkIndex = 0; chunkIndex < mDisplayList->getChunks().size(); chunkIndex++) {
+ const DisplayList::Chunk& chunk = mDisplayList->getChunks()[chunkIndex];
std::vector<ZDrawRenderNodeOpPair> zTranslatedNodes;
buildZSortedChildList(chunk, zTranslatedNodes);
@@ -969,9 +1128,8 @@
issueOperationsOf3dChildren(ChildrenSelectMode::NegativeZChildren,
initialTransform, zTranslatedNodes, renderer, handler);
-
for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
- DisplayListOp *op = mDisplayListData->displayListOps[opIndex];
+ DisplayListOp *op = mDisplayList->getOps()[opIndex];
#if DEBUG_DISPLAY_LIST
op->output(handler.level() + 1);
#endif
@@ -988,6 +1146,7 @@
}
}
}
+#endif
DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (handler.level() + 1) * 2, "", restoreTo);
handler(new (alloc) RestoreToCountOp(restoreTo),
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 88fc608..bae5ebe 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -29,8 +29,8 @@
#include "AnimatorManager.h"
#include "Debug.h"
-#include "Matrix.h"
#include "DisplayList.h"
+#include "Matrix.h"
#include "RenderProperties.h"
#include <vector>
@@ -43,13 +43,23 @@
namespace android {
namespace uirenderer {
-class DisplayListOp;
+class CanvasState;
class DisplayListCanvas;
+class DisplayListOp;
class OpenGLRenderer;
+class OpReorderer;
class Rect;
-class Layer;
class SkiaShader;
+
+#if HWUI_NEW_OPS
+class OffscreenBuffer;
+typedef OffscreenBuffer layer_t;
+#else
+class Layer;
+typedef Layer layer_t;
+#endif
+
class ClipRectOp;
class SaveLayerOp;
class SaveOp;
@@ -65,15 +75,16 @@
* Primary class for storing recorded canvas commands, as well as per-View/ViewGroup display properties.
*
* Recording of canvas commands is somewhat similar to SkPicture, except the canvas-recording
- * functionality is split between DisplayListCanvas (which manages the recording), DisplayListData
+ * functionality is split between DisplayListCanvas (which manages the recording), DisplayList
* (which holds the actual data), and DisplayList (which holds properties and performs playback onto
* a renderer).
*
- * Note that DisplayListData is swapped out from beneath an individual DisplayList when a view's
- * recorded stream of canvas operations is refreshed. The DisplayList (and its properties) stay
+ * Note that DisplayList is swapped out from beneath an individual RenderNode when a view's
+ * recorded stream of canvas operations is refreshed. The RenderNode (and its properties) stay
* attached.
*/
class RenderNode : public VirtualLightRefBase {
+friend class TestUtils; // allow TestUtils to access syncDisplayList / syncProperties
public:
enum DirtyPropertyMask {
GENERIC = 1 << 1,
@@ -102,7 +113,7 @@
void debugDumpLayers(const char* prefix);
- ANDROID_API void setStagingDisplayList(DisplayListData* newData);
+ ANDROID_API void setStagingDisplayList(DisplayList* newData);
void computeOrdering();
@@ -114,11 +125,11 @@
void copyTo(proto::RenderNode* node);
bool isRenderable() const {
- return mDisplayListData && !mDisplayListData->isEmpty();
+ return mDisplayList && !mDisplayList->isEmpty();
}
bool hasProjectionReceiver() const {
- return mDisplayListData && mDisplayListData->projectionReceiveIndex >= 0;
+ return mDisplayList && mDisplayList->projectionReceiveIndex >= 0;
}
const char* getName() const {
@@ -160,11 +171,11 @@
return mStagingProperties;
}
- int getWidth() {
+ int getWidth() const {
return properties().getWidth();
}
- int getHeight() {
+ int getHeight() const {
return properties().getHeight();
}
@@ -176,8 +187,28 @@
AnimatorManager& animators() { return mAnimatorManager; }
+ // Returns false if the properties dictate the subtree contained in this RenderNode won't render
+ bool applyViewProperties(CanvasState& canvasState, LinearAllocator& allocator) const;
+
void applyViewPropertyTransforms(mat4& matrix, bool true3dTransform = false) const;
+ bool nothingToDraw() const {
+ const Outline& outline = properties().getOutline();
+ return mDisplayList == nullptr
+ || properties().getAlpha() <= 0
+ || (outline.getShouldClip() && outline.isEmpty())
+ || properties().getScaleX() == 0
+ || properties().getScaleY() == 0;
+ }
+
+ const DisplayList* getDisplayList() const {
+ return mDisplayList;
+ }
+#if HWUI_NEW_OPS
+ OffscreenBuffer* getLayer() const { return mLayer; }
+ OffscreenBuffer** getLayerHandle() { return &mLayer; } // ugh...
+#endif
+
private:
typedef key_value_pair_t<float, DrawRenderNodeOp*> ZDrawRenderNodeOpPair;
@@ -200,7 +231,7 @@
template <class T>
inline void setViewProperties(OpenGLRenderer& renderer, T& handler);
- void buildZSortedChildList(const DisplayListData::Chunk& chunk,
+ void buildZSortedChildList(const DisplayList::Chunk& chunk,
std::vector<ZDrawRenderNodeOpPair>& zTranslatedNodes);
template<class T>
@@ -235,14 +266,20 @@
const char* mText;
};
+
+ void syncProperties();
+ void syncDisplayList();
+
void prepareTreeImpl(TreeInfo& info, bool functorsNeedLayer);
void pushStagingPropertiesChanges(TreeInfo& info);
void pushStagingDisplayListChanges(TreeInfo& info);
- void prepareSubTree(TreeInfo& info, bool functorsNeedLayer, DisplayListData* subtree);
+ void prepareSubTree(TreeInfo& info, bool functorsNeedLayer, DisplayList* subtree);
+#if !HWUI_NEW_OPS
void applyLayerPropertiesToLayer(TreeInfo& info);
+#endif
void prepareLayer(TreeInfo& info, uint32_t dirtyMask);
void pushLayerUpdate(TreeInfo& info);
- void deleteDisplayListData();
+ void deleteDisplayList();
void damageSelf(TreeInfo& info);
void incParentRefCount() { mParentCount++; }
@@ -254,17 +291,17 @@
RenderProperties mProperties;
RenderProperties mStagingProperties;
- bool mNeedsDisplayListDataSync;
- // WARNING: Do not delete this directly, you must go through deleteDisplayListData()!
- DisplayListData* mDisplayListData;
- DisplayListData* mStagingDisplayListData;
+ bool mNeedsDisplayListSync;
+ // WARNING: Do not delete this directly, you must go through deleteDisplayList()!
+ DisplayList* mDisplayList;
+ DisplayList* mStagingDisplayList;
friend class AnimatorManager;
AnimatorManager mAnimatorManager;
// Owned by RT. Lifecycle is managed by prepareTree(), with the exception
// being in ~RenderNode() which may happen on any thread.
- Layer* mLayer;
+ layer_t* mLayer = nullptr;
/**
* Draw time state - these properties are only set and used during rendering
@@ -278,7 +315,7 @@
// When this hits 0 we are no longer in the tree, so any hardware resources
// (specifically Layers) should be released.
// This is *NOT* thread-safe, and should therefore only be tracking
- // mDisplayListData, not mStagingDisplayListData.
+ // mDisplayList, not mStagingDisplayList.
uint32_t mParentCount;
}; // class RenderNode
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index f824cc0..ca7789e 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -16,23 +16,24 @@
#ifndef RENDERNODEPROPERTIES_H
#define RENDERNODEPROPERTIES_H
-#include <algorithm>
-#include <stddef.h>
-#include <vector>
-#include <cutils/compiler.h>
-#include <androidfw/ResourceTypes.h>
-#include <utils/Log.h>
+#include "Caches.h"
+#include "DeviceInfo.h"
+#include "Rect.h"
+#include "RevealClip.h"
+#include "Outline.h"
+#include "utils/MathUtils.h"
#include <SkCamera.h>
#include <SkMatrix.h>
#include <SkRegion.h>
#include <SkXfermode.h>
-#include "Caches.h"
-#include "Rect.h"
-#include "RevealClip.h"
-#include "Outline.h"
-#include "utils/MathUtils.h"
+#include <algorithm>
+#include <stddef.h>
+#include <vector>
+#include <cutils/compiler.h>
+#include <androidfw/ResourceTypes.h>
+#include <utils/Log.h>
class SkBitmap;
class SkColorFilter;
@@ -203,8 +204,8 @@
return RP_SET(mPrimitiveFields.mProjectBackwards, shouldProject);
}
- bool setProjectionReceiver(bool shouldRecieve) {
- return RP_SET(mPrimitiveFields.mProjectionReceiver, shouldRecieve);
+ bool setProjectionReceiver(bool shouldReceive) {
+ return RP_SET(mPrimitiveFields.mProjectionReceiver, shouldReceive);
}
bool isProjectionReceiver() const {
@@ -608,10 +609,11 @@
}
bool promotedToLayer() const {
- const int maxTextureSize = Caches::getInstance().maxTextureSize;
+ const DeviceInfo* deviceInfo = DeviceInfo::get();
+ LOG_ALWAYS_FATAL_IF(!deviceInfo, "DeviceInfo uninitialized");
return mLayerProperties.mType == LayerType::None
- && mPrimitiveFields.mWidth <= maxTextureSize
- && mPrimitiveFields.mHeight <= maxTextureSize
+ && mPrimitiveFields.mWidth <= deviceInfo->maxTextureSize()
+ && mPrimitiveFields.mHeight <= deviceInfo->maxTextureSize()
&& (mComputedFields.mNeedLayerForFunctors
|| (!MathUtils::isZero(mPrimitiveFields.mAlpha)
&& mPrimitiveFields.mAlpha < 1
diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp
index eb0fa74..e94a70a 100644
--- a/libs/hwui/ShadowTessellator.cpp
+++ b/libs/hwui/ShadowTessellator.cpp
@@ -76,6 +76,11 @@
ALOGD("light center %f %f %f %d",
adjustedLightCenter.x, adjustedLightCenter.y, adjustedLightCenter.z, lightRadius);
#endif
+ if (isnan(adjustedLightCenter.x)
+ || isnan(adjustedLightCenter.y)
+ || isnan(adjustedLightCenter.z)) {
+ return;
+ }
// light position (because it's in local space) needs to compensate for receiver transform
// TODO: should apply to light orientation, not just position
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 36633b5..6d3dfac 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -194,15 +194,13 @@
void SkiaCanvas::setBitmap(const SkBitmap& bitmap) {
SkCanvas* newCanvas = new SkCanvas(bitmap);
- SkASSERT(newCanvas);
if (!bitmap.isNull()) {
// Copy the canvas matrix & clip state.
newCanvas->setMatrix(mCanvas->getTotalMatrix());
- if (NULL != mCanvas->getDevice() && NULL != newCanvas->getDevice()) {
- ClipCopier copier(newCanvas);
- mCanvas->replayClips(&copier);
- }
+
+ ClipCopier copier(newCanvas);
+ mCanvas->replayClips(&copier);
}
// unrefs the existing canvas
@@ -217,15 +215,15 @@
// ----------------------------------------------------------------------------
bool SkiaCanvas::isOpaque() {
- return mCanvas->getDevice()->accessBitmap(false).isOpaque();
+ return mCanvas->imageInfo().isOpaque();
}
int SkiaCanvas::width() {
- return mCanvas->getBaseLayerSize().width();
+ return mCanvas->imageInfo().width();
}
int SkiaCanvas::height() {
- return mCanvas->getBaseLayerSize().height();
+ return mCanvas->imageInfo().height();
}
// ----------------------------------------------------------------------------
@@ -242,11 +240,15 @@
return count;
}
+// The SkiaCanvas::restore operation layers on the capability to preserve
+// either (or both) the matrix and/or clip state after a SkCanvas::restore
+// operation. It does this by explicitly saving off the clip & matrix state
+// when requested and playing it back after the SkCanvas::restore.
void SkiaCanvas::restore() {
const SaveRec* rec = (NULL == mSaveStack.get())
? NULL
: static_cast<SaveRec*>(mSaveStack->back());
- int currentSaveCount = mCanvas->getSaveCount() - 1;
+ int currentSaveCount = mCanvas->getSaveCount();
SkASSERT(NULL == rec || currentSaveCount >= rec->saveCount);
if (NULL == rec || rec->saveCount != currentSaveCount) {
@@ -264,8 +266,9 @@
}
SkTArray<SkClipStack::Element> savedClips;
+ int topClipStackFrame = mCanvas->getClipStack()->getSaveCount();
if (preserveClip) {
- saveClipsForFrame(savedClips, currentSaveCount);
+ saveClipsForFrame(savedClips, topClipStackFrame);
}
mCanvas->restore();
@@ -274,7 +277,11 @@
mCanvas->setMatrix(savedMatrix);
}
- if (preserveClip && !savedClips.empty()) {
+ if (preserveClip && !savedClips.empty() &&
+ topClipStackFrame != mCanvas->getClipStack()->getSaveCount()) {
+ // Only reapply the saved clips if the top clip stack frame was actually
+ // popped by restore(). If it wasn't, it means it doesn't belong to the
+ // restored canvas frame (SkCanvas lazy save/restore kicked in).
applyClips(savedClips);
}
@@ -324,21 +331,23 @@
}
SaveRec* rec = static_cast<SaveRec*>(mSaveStack->push_back());
- // Store the save counter in the SkClipStack domain.
- // (0-based, equal to the number of save ops on the stack).
- rec->saveCount = mCanvas->getSaveCount() - 1;
+ rec->saveCount = mCanvas->getSaveCount();
rec->saveFlags = flags;
}
-void SkiaCanvas::saveClipsForFrame(SkTArray<SkClipStack::Element>& clips, int frameSaveCount) {
+void SkiaCanvas::saveClipsForFrame(SkTArray<SkClipStack::Element>& clips,
+ int saveCountToBackup) {
+ // Each SkClipStack::Element stores the index of the canvas save
+ // with which it is associated. Backup only those Elements that
+ // are associated with 'saveCountToBackup'
SkClipStack::Iter clipIterator(*mCanvas->getClipStack(),
SkClipStack::Iter::kTop_IterStart);
- while (const SkClipStack::Element* elem = clipIterator.next()) {
- if (elem->getSaveCount() < frameSaveCount) {
- // done with the current frame.
+ while (const SkClipStack::Element* elem = clipIterator.prev()) {
+ if (elem->getSaveCount() < saveCountToBackup) {
+ // done with the target save count.
break;
}
- SkASSERT(elem->getSaveCount() == frameSaveCount);
+ SkASSERT(elem->getSaveCount() == saveCountToBackup);
clips.push_back(*elem);
}
}
@@ -581,7 +590,7 @@
float dstRight, float dstBottom, const SkPaint* paint) {
SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
- mCanvas->drawBitmapRectToRect(bitmap, &srcRect, dstRect, paint);
+ mCanvas->drawBitmapRect(bitmap, srcRect, dstRect, paint);
}
void SkiaCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp
index c3f5eb2..2d5f70f 100644
--- a/libs/hwui/SkiaCanvasProxy.cpp
+++ b/libs/hwui/SkiaCanvasProxy.cpp
@@ -125,7 +125,7 @@
}
void SkiaCanvasProxy::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* srcPtr,
- const SkRect& dst, const SkPaint* paint, DrawBitmapRectFlags) {
+ const SkRect& dst, const SkPaint* paint, SrcRectConstraint) {
SkRect src = (srcPtr) ? *srcPtr : SkRect::MakeWH(bitmap.width(), bitmap.height());
// TODO: if bitmap is a subset, do we need to add pixelRefOrigin to src?
mCanvas->drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom,
diff --git a/libs/hwui/SkiaCanvasProxy.h b/libs/hwui/SkiaCanvasProxy.h
index 0de9650..2fe4327 100644
--- a/libs/hwui/SkiaCanvasProxy.h
+++ b/libs/hwui/SkiaCanvasProxy.h
@@ -63,7 +63,7 @@
virtual void onDrawBitmap(const SkBitmap&, SkScalar left, SkScalar top,
const SkPaint*) override;
virtual void onDrawBitmapRect(const SkBitmap&, const SkRect* src, const SkRect& dst,
- const SkPaint* paint, DrawBitmapRectFlags flags) override;
+ const SkPaint* paint, SrcRectConstraint) override;
virtual void onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
const SkRect& dst, const SkPaint*) override;
virtual void onDrawSprite(const SkBitmap&, int left, int top,
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index 6c105cf..83652c6 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -204,7 +204,7 @@
SkiaShaderData::BitmapShaderData* outData) {
SkBitmap bitmap;
SkShader::TileMode xy[2];
- if (shader.asABitmap(&bitmap, nullptr, xy) != SkShader::kDefault_BitmapType) {
+ if (!shader.isABitmap(&bitmap, nullptr, xy)) {
return false;
}
@@ -272,7 +272,7 @@
}
// The shader is not a gradient. Check for a bitmap shader.
- if (shader.asABitmap(nullptr, nullptr, nullptr) == SkShader::kDefault_BitmapType) {
+ if (shader.isABitmap()) {
return kBitmap_SkiaShaderType;
}
return kNone_SkiaShaderType;
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index aeeda96..4789b33 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -158,13 +158,12 @@
/**
* Returns the current clip in render target coordinates.
*/
- const Rect& getRenderTargetClip() { return mClipArea->getClipRect(); }
+ const Rect& getRenderTargetClip() const { return mClipArea->getClipRect(); }
/*
* Accessor functions so that the clip area can stay private
*/
bool clipIsEmpty() const { return mClipArea->isEmpty(); }
- const Rect& getClipRect() const { return mClipArea->getClipRect(); }
const SkRegion& getClipRegion() const { return mClipArea->getClipRegion(); }
bool clipIsSimple() const { return mClipArea->isSimple(); }
const ClipArea& getClipArea() const { return *mClipArea; }
diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp
index bdce73c..759e39b 100644
--- a/libs/hwui/SpotShadow.cpp
+++ b/libs/hwui/SpotShadow.cpp
@@ -741,7 +741,7 @@
// vertex's location.
int newPenumbraNumber = indexDelta - 1;
- float accumulatedDeltaLength[newPenumbraNumber];
+ float accumulatedDeltaLength[indexDelta];
float totalDeltaLength = 0;
// To save time, cache the previous umbra vertex info outside the loop
diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp
index 12a3e76..0835c29 100644
--- a/libs/hwui/TessellationCache.cpp
+++ b/libs/hwui/TessellationCache.cpp
@@ -217,7 +217,7 @@
}
}
-static void tessellateShadows(
+void tessellateShadows(
const Matrix4* drawTransform, const Rect* localClip,
bool isCasterOpaque, const SkPath* casterPerimeter,
const Matrix4* casterTransformXY, const Matrix4* casterTransformZ,
diff --git a/libs/hwui/TessellationCache.h b/libs/hwui/TessellationCache.h
index b54666b..06e567e 100644
--- a/libs/hwui/TessellationCache.h
+++ b/libs/hwui/TessellationCache.h
@@ -17,16 +17,22 @@
#ifndef ANDROID_HWUI_TESSELLATION_CACHE_H
#define ANDROID_HWUI_TESSELLATION_CACHE_H
-#include <utils/LruCache.h>
-#include <utils/Mutex.h>
-
#include "Debug.h"
+#include "Matrix.h"
+#include "Rect.h"
+#include "Vector.h"
+#include "thread/TaskProcessor.h"
#include "utils/Macros.h"
#include "utils/Pair.h"
+#include <SkPaint.h>
+
+#include <utils/LruCache.h>
+#include <utils/Mutex.h>
+#include <utils/StrongPointer.h>
+
class SkBitmap;
class SkCanvas;
-class SkPaint;
class SkPath;
struct SkRect;
@@ -185,6 +191,13 @@
}; // class TessellationCache
+void tessellateShadows(
+ const Matrix4* drawTransform, const Rect* localClip,
+ bool isCasterOpaque, const SkPath* casterPerimeter,
+ const Matrix4* casterTransformXY, const Matrix4* casterTransformZ,
+ const Vector3& lightCenter, float lightRadius,
+ VertexBuffer& ambientBuffer, VertexBuffer& spotBuffer);
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index 98e6146..be25516 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -16,11 +16,11 @@
#ifndef TREEINFO_H
#define TREEINFO_H
-#include <string>
+#include "utils/Macros.h"
#include <utils/Timers.h>
-#include "utils/Macros.h"
+#include <string>
namespace android {
namespace uirenderer {
@@ -30,6 +30,7 @@
}
class DamageAccumulator;
+class LayerUpdateQueue;
class OpenGLRenderer;
class RenderState;
@@ -55,70 +56,51 @@
MODE_RT_ONLY,
};
- explicit TreeInfo(TraversalMode mode, RenderState& renderState)
- : mode(mode)
- , prepareTextures(mode == MODE_FULL)
- , runAnimations(true)
- , damageAccumulator(nullptr)
- , renderState(renderState)
- , renderer(nullptr)
- , errorHandler(nullptr)
- , canvasContext(nullptr)
- {}
-
- explicit TreeInfo(TraversalMode mode, const TreeInfo& clone)
- : mode(mode)
- , prepareTextures(mode == MODE_FULL)
- , runAnimations(clone.runAnimations)
- , damageAccumulator(clone.damageAccumulator)
- , renderState(clone.renderState)
- , renderer(clone.renderer)
- , errorHandler(clone.errorHandler)
- , canvasContext(clone.canvasContext)
+ TreeInfo(TraversalMode mode, renderthread::CanvasContext& canvasContext)
+ : mode(mode)
+ , prepareTextures(mode == MODE_FULL)
+ , canvasContext(canvasContext)
{}
TraversalMode mode;
// TODO: Remove this? Currently this is used to signal to stop preparing
// textures if we run out of cache space.
bool prepareTextures;
+ renderthread::CanvasContext& canvasContext;
// TODO: buildLayer uses this to suppress running any animations, but this
// should probably be refactored somehow. The reason this is done is
// because buildLayer is not setup for injecting the animationHook, as well
// as this being otherwise wasted work as all the animators will be
// re-evaluated when the frame is actually drawn
- bool runAnimations;
+ bool runAnimations = true;
// Must not be null during actual usage
- DamageAccumulator* damageAccumulator;
- RenderState& renderState;
+ DamageAccumulator* damageAccumulator = nullptr;
+
+#if HWUI_NEW_OPS
+ LayerUpdateQueue* layerUpdateQueue = nullptr;
+#else
// The renderer that will be drawing the next frame. Use this to push any
// layer updates or similar. May be NULL.
- OpenGLRenderer* renderer;
- ErrorHandler* errorHandler;
- // May be NULL (TODO: can it really?)
- renderthread::CanvasContext* canvasContext;
+ OpenGLRenderer* renderer = nullptr;
+#endif
+ ErrorHandler* errorHandler = nullptr;
struct Out {
- Out()
- : hasFunctors(false)
- , hasAnimations(false)
- , requiresUiRedraw(false)
- , canDrawThisFrame(true)
- {}
- bool hasFunctors;
+ bool hasFunctors = false;
// This is only updated if evaluateAnimations is true
- bool hasAnimations;
+ bool hasAnimations = false;
// This is set to true if there is an animation that RenderThread cannot
// animate itself, such as if hasFunctors is true
// This is only set if hasAnimations is true
- bool requiresUiRedraw;
+ bool requiresUiRedraw = false;
// This is set to true if draw() can be called this frame
// false means that we must delay until the next vsync pulse as frame
// production is outrunning consumption
// NOTE that if this is false CanvasContext will set either requiresUiRedraw
// *OR* will post itself for the next vsync automatically, use this
// only to avoid calling draw()
- bool canDrawThisFrame;
+ bool canDrawThisFrame = true;
} out;
// TODO: Damage calculations
diff --git a/libs/hwui/Vector.h b/libs/hwui/Vector.h
index 7c3f2fd..6367dbd 100644
--- a/libs/hwui/Vector.h
+++ b/libs/hwui/Vector.h
@@ -135,8 +135,8 @@
}
- void dump() {
- ALOGD("Vector3[%.2f, %.2f, %.2f]", x, y, z);
+ void dump(const char* label = "Vector3") const {
+ ALOGD("%s[%.2f, %.2f, %.2f]", label, x, y, z);
}
};
diff --git a/libs/hwui/VectorDrawablePath.cpp b/libs/hwui/VectorDrawablePath.cpp
new file mode 100644
index 0000000..115435c
--- /dev/null
+++ b/libs/hwui/VectorDrawablePath.cpp
@@ -0,0 +1,496 @@
+/*
+ * 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.
+ */
+
+#include "VectorDrawablePath.h"
+
+#include "PathParser.h"
+
+#include <math.h>
+#include <utils/Log.h>
+
+namespace android {
+namespace uirenderer {
+
+class PathResolver {
+public:
+ float currentX = 0;
+ float currentY = 0;
+ float ctrlPointX = 0;
+ float ctrlPointY = 0;
+ float currentSegmentStartX = 0;
+ float currentSegmentStartY = 0;
+ void addCommand(SkPath* outPath, char previousCmd,
+ char cmd, const std::vector<float>* points, size_t start, size_t end);
+};
+
+VectorDrawablePath::VectorDrawablePath(const char* pathStr, size_t strLength) {
+ PathParser::getPathDataFromString(&mData, pathStr, strLength);
+ verbsToPath(&mSkPath, &mData);
+}
+
+VectorDrawablePath::VectorDrawablePath(const PathData& data) {
+ mData = data;
+ // Now we need to construct a path
+ verbsToPath(&mSkPath, &data);
+}
+
+VectorDrawablePath::VectorDrawablePath(const VectorDrawablePath& path) {
+ mData = path.mData;
+ verbsToPath(&mSkPath, &mData);
+}
+
+bool VectorDrawablePath::canMorph(const PathData& morphTo) {
+ if (mData.verbs.size() != morphTo.verbs.size()) {
+ return false;
+ }
+
+ for (unsigned int i = 0; i < mData.verbs.size(); i++) {
+ if (mData.verbs[i] != morphTo.verbs[i]
+ || mData.verbSizes[i] != morphTo.verbSizes[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool VectorDrawablePath::canMorph(const VectorDrawablePath& path) {
+ return canMorph(path.mData);
+}
+ /**
+ * Convert an array of PathVerb to Path.
+ */
+void VectorDrawablePath::verbsToPath(SkPath* outPath, const PathData* data) {
+ PathResolver resolver;
+ char previousCommand = 'm';
+ size_t start = 0;
+ outPath->reset();
+ for (unsigned int i = 0; i < data->verbs.size(); i++) {
+ size_t verbSize = data->verbSizes[i];
+ resolver.addCommand(outPath, previousCommand, data->verbs[i], &data->points, start,
+ start + verbSize - 1u);
+ previousCommand = data->verbs[i];
+ start += verbSize;
+ }
+}
+
+/**
+ * The current PathVerb will be interpolated between the
+ * <code>nodeFrom</code> and <code>nodeTo</code> according to the
+ * <code>fraction</code>.
+ *
+ * @param nodeFrom The start value as a PathVerb.
+ * @param nodeTo The end value as a PathVerb
+ * @param fraction The fraction to interpolate.
+ */
+void VectorDrawablePath::interpolatePaths(PathData* outData,
+ const PathData* from, const PathData* to, float fraction) {
+ outData->points.resize(from->points.size());
+ outData->verbSizes = from->verbSizes;
+ outData->verbs = from->verbs;
+
+ for (size_t i = 0; i < from->points.size(); i++) {
+ outData->points[i] = from->points[i] * (1 - fraction) + to->points[i] * fraction;
+ }
+}
+
+/**
+ * Converts an arc to cubic Bezier segments and records them in p.
+ *
+ * @param p The target for the cubic Bezier segments
+ * @param cx The x coordinate center of the ellipse
+ * @param cy The y coordinate center of the ellipse
+ * @param a The radius of the ellipse in the horizontal direction
+ * @param b The radius of the ellipse in the vertical direction
+ * @param e1x E(eta1) x coordinate of the starting point of the arc
+ * @param e1y E(eta2) y coordinate of the starting point of the arc
+ * @param theta The angle that the ellipse bounding rectangle makes with horizontal plane
+ * @param start The start angle of the arc on the ellipse
+ * @param sweep The angle (positive or negative) of the sweep of the arc on the ellipse
+ */
+static void arcToBezier(SkPath* p,
+ double cx,
+ double cy,
+ double a,
+ double b,
+ double e1x,
+ double e1y,
+ double theta,
+ double start,
+ double sweep) {
+ // Taken from equations at: http://spaceroots.org/documents/ellipse/node8.html
+ // and http://www.spaceroots.org/documents/ellipse/node22.html
+
+ // Maximum of 45 degrees per cubic Bezier segment
+ int numSegments = ceil(fabs(sweep * 4 / M_PI));
+
+ double eta1 = start;
+ double cosTheta = cos(theta);
+ double sinTheta = sin(theta);
+ double cosEta1 = cos(eta1);
+ double sinEta1 = sin(eta1);
+ double ep1x = (-a * cosTheta * sinEta1) - (b * sinTheta * cosEta1);
+ double ep1y = (-a * sinTheta * sinEta1) + (b * cosTheta * cosEta1);
+
+ double anglePerSegment = sweep / numSegments;
+ for (int i = 0; i < numSegments; i++) {
+ double eta2 = eta1 + anglePerSegment;
+ double sinEta2 = sin(eta2);
+ double cosEta2 = cos(eta2);
+ double e2x = cx + (a * cosTheta * cosEta2) - (b * sinTheta * sinEta2);
+ double e2y = cy + (a * sinTheta * cosEta2) + (b * cosTheta * sinEta2);
+ double ep2x = -a * cosTheta * sinEta2 - b * sinTheta * cosEta2;
+ double ep2y = -a * sinTheta * sinEta2 + b * cosTheta * cosEta2;
+ double tanDiff2 = tan((eta2 - eta1) / 2);
+ double alpha =
+ sin(eta2 - eta1) * (sqrt(4 + (3 * tanDiff2 * tanDiff2)) - 1) / 3;
+ double q1x = e1x + alpha * ep1x;
+ double q1y = e1y + alpha * ep1y;
+ double q2x = e2x - alpha * ep2x;
+ double q2y = e2y - alpha * ep2y;
+
+ p->cubicTo((float) q1x,
+ (float) q1y,
+ (float) q2x,
+ (float) q2y,
+ (float) e2x,
+ (float) e2y);
+ eta1 = eta2;
+ e1x = e2x;
+ e1y = e2y;
+ ep1x = ep2x;
+ ep1y = ep2y;
+ }
+}
+
+inline double toRadians(float theta) { return theta * M_PI / 180;}
+
+static void drawArc(SkPath* p,
+ float x0,
+ float y0,
+ float x1,
+ float y1,
+ float a,
+ float b,
+ float theta,
+ bool isMoreThanHalf,
+ bool isPositiveArc) {
+
+ /* Convert rotation angle from degrees to radians */
+ double thetaD = toRadians(theta);
+ /* Pre-compute rotation matrix entries */
+ double cosTheta = cos(thetaD);
+ double sinTheta = sin(thetaD);
+ /* Transform (x0, y0) and (x1, y1) into unit space */
+ /* using (inverse) rotation, followed by (inverse) scale */
+ double x0p = (x0 * cosTheta + y0 * sinTheta) / a;
+ double y0p = (-x0 * sinTheta + y0 * cosTheta) / b;
+ double x1p = (x1 * cosTheta + y1 * sinTheta) / a;
+ double y1p = (-x1 * sinTheta + y1 * cosTheta) / b;
+
+ /* Compute differences and averages */
+ double dx = x0p - x1p;
+ double dy = y0p - y1p;
+ double xm = (x0p + x1p) / 2;
+ double ym = (y0p + y1p) / 2;
+ /* Solve for intersecting unit circles */
+ double dsq = dx * dx + dy * dy;
+ if (dsq == 0.0) {
+ ALOGW("Points are coincident");
+ return; /* Points are coincident */
+ }
+ double disc = 1.0 / dsq - 1.0 / 4.0;
+ if (disc < 0.0) {
+ ALOGW("Points are too far apart %f", dsq);
+ float adjust = (float) (sqrt(dsq) / 1.99999);
+ drawArc(p, x0, y0, x1, y1, a * adjust,
+ b * adjust, theta, isMoreThanHalf, isPositiveArc);
+ return; /* Points are too far apart */
+ }
+ double s = sqrt(disc);
+ double sdx = s * dx;
+ double sdy = s * dy;
+ double cx;
+ double cy;
+ if (isMoreThanHalf == isPositiveArc) {
+ cx = xm - sdy;
+ cy = ym + sdx;
+ } else {
+ cx = xm + sdy;
+ cy = ym - sdx;
+ }
+
+ double eta0 = atan2((y0p - cy), (x0p - cx));
+
+ double eta1 = atan2((y1p - cy), (x1p - cx));
+
+ double sweep = (eta1 - eta0);
+ if (isPositiveArc != (sweep >= 0)) {
+ if (sweep > 0) {
+ sweep -= 2 * M_PI;
+ } else {
+ sweep += 2 * M_PI;
+ }
+ }
+
+ cx *= a;
+ cy *= b;
+ double tcx = cx;
+ cx = cx * cosTheta - cy * sinTheta;
+ cy = tcx * sinTheta + cy * cosTheta;
+
+ arcToBezier(p, cx, cy, a, b, x0, y0, thetaD, eta0, sweep);
+}
+
+
+void PathResolver::addCommand(SkPath* outPath, char previousCmd,
+ char cmd, const std::vector<float>* points, size_t start, size_t end) {
+
+ int incr = 2;
+ float reflectiveCtrlPointX;
+ float reflectiveCtrlPointY;
+
+ switch (cmd) {
+ case 'z':
+ case 'Z':
+ outPath->close();
+ // Path is closed here, but we need to move the pen to the
+ // closed position. So we cache the segment's starting position,
+ // and restore it here.
+ currentX = currentSegmentStartX;
+ currentY = currentSegmentStartY;
+ ctrlPointX = currentSegmentStartX;
+ ctrlPointY = currentSegmentStartY;
+ outPath->moveTo(currentX, currentY);
+ break;
+ case 'm':
+ case 'M':
+ case 'l':
+ case 'L':
+ case 't':
+ case 'T':
+ incr = 2;
+ break;
+ case 'h':
+ case 'H':
+ case 'v':
+ case 'V':
+ incr = 1;
+ break;
+ case 'c':
+ case 'C':
+ incr = 6;
+ break;
+ case 's':
+ case 'S':
+ case 'q':
+ case 'Q':
+ incr = 4;
+ break;
+ case 'a':
+ case 'A':
+ incr = 7;
+ break;
+ }
+
+ for (unsigned int k = start; k <= end; k += incr) {
+ switch (cmd) {
+ case 'm': // moveto - Start a new sub-path (relative)
+ currentX += points->at(k + 0);
+ currentY += points->at(k + 1);
+ if (k > start) {
+ // According to the spec, if a moveto is followed by multiple
+ // pairs of coordinates, the subsequent pairs are treated as
+ // implicit lineto commands.
+ outPath->rLineTo(points->at(k + 0), points->at(k + 1));
+ } else {
+ outPath->rMoveTo(points->at(k + 0), points->at(k + 1));
+ currentSegmentStartX = currentX;
+ currentSegmentStartY = currentY;
+ }
+ break;
+ case 'M': // moveto - Start a new sub-path
+ currentX = points->at(k + 0);
+ currentY = points->at(k + 1);
+ if (k > start) {
+ // According to the spec, if a moveto is followed by multiple
+ // pairs of coordinates, the subsequent pairs are treated as
+ // implicit lineto commands.
+ outPath->lineTo(points->at(k + 0), points->at(k + 1));
+ } else {
+ outPath->moveTo(points->at(k + 0), points->at(k + 1));
+ currentSegmentStartX = currentX;
+ currentSegmentStartY = currentY;
+ }
+ break;
+ case 'l': // lineto - Draw a line from the current point (relative)
+ outPath->rLineTo(points->at(k + 0), points->at(k + 1));
+ currentX += points->at(k + 0);
+ currentY += points->at(k + 1);
+ break;
+ case 'L': // lineto - Draw a line from the current point
+ outPath->lineTo(points->at(k + 0), points->at(k + 1));
+ currentX = points->at(k + 0);
+ currentY = points->at(k + 1);
+ break;
+ case 'h': // horizontal lineto - Draws a horizontal line (relative)
+ outPath->rLineTo(points->at(k + 0), 0);
+ currentX += points->at(k + 0);
+ break;
+ case 'H': // horizontal lineto - Draws a horizontal line
+ outPath->lineTo(points->at(k + 0), currentY);
+ currentX = points->at(k + 0);
+ break;
+ case 'v': // vertical lineto - Draws a vertical line from the current point (r)
+ outPath->rLineTo(0, points->at(k + 0));
+ currentY += points->at(k + 0);
+ break;
+ case 'V': // vertical lineto - Draws a vertical line from the current point
+ outPath->lineTo(currentX, points->at(k + 0));
+ currentY = points->at(k + 0);
+ break;
+ case 'c': // curveto - Draws a cubic Bézier curve (relative)
+ outPath->rCubicTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), points->at(k + 3),
+ points->at(k + 4), points->at(k + 5));
+
+ ctrlPointX = currentX + points->at(k + 2);
+ ctrlPointY = currentY + points->at(k + 3);
+ currentX += points->at(k + 4);
+ currentY += points->at(k + 5);
+
+ break;
+ case 'C': // curveto - Draws a cubic Bézier curve
+ outPath->cubicTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), points->at(k + 3),
+ points->at(k + 4), points->at(k + 5));
+ currentX = points->at(k + 4);
+ currentY = points->at(k + 5);
+ ctrlPointX = points->at(k + 2);
+ ctrlPointY = points->at(k + 3);
+ break;
+ case 's': // smooth curveto - Draws a cubic Bézier curve (reflective cp)
+ reflectiveCtrlPointX = 0;
+ reflectiveCtrlPointY = 0;
+ if (previousCmd == 'c' || previousCmd == 's'
+ || previousCmd == 'C' || previousCmd == 'S') {
+ reflectiveCtrlPointX = currentX - ctrlPointX;
+ reflectiveCtrlPointY = currentY - ctrlPointY;
+ }
+ outPath->rCubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
+ points->at(k + 0), points->at(k + 1),
+ points->at(k + 2), points->at(k + 3));
+ ctrlPointX = currentX + points->at(k + 0);
+ ctrlPointY = currentY + points->at(k + 1);
+ currentX += points->at(k + 2);
+ currentY += points->at(k + 3);
+ break;
+ case 'S': // shorthand/smooth curveto Draws a cubic Bézier curve(reflective cp)
+ reflectiveCtrlPointX = currentX;
+ reflectiveCtrlPointY = currentY;
+ if (previousCmd == 'c' || previousCmd == 's'
+ || previousCmd == 'C' || previousCmd == 'S') {
+ reflectiveCtrlPointX = 2 * currentX - ctrlPointX;
+ reflectiveCtrlPointY = 2 * currentY - ctrlPointY;
+ }
+ outPath->cubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
+ points->at(k + 0), points->at(k + 1), points->at(k + 2), points->at(k + 3));
+ ctrlPointX = points->at(k + 0);
+ ctrlPointY = points->at(k + 1);
+ currentX = points->at(k + 2);
+ currentY = points->at(k + 3);
+ break;
+ case 'q': // Draws a quadratic Bézier (relative)
+ outPath->rQuadTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), points->at(k + 3));
+ ctrlPointX = currentX + points->at(k + 0);
+ ctrlPointY = currentY + points->at(k + 1);
+ currentX += points->at(k + 2);
+ currentY += points->at(k + 3);
+ break;
+ case 'Q': // Draws a quadratic Bézier
+ outPath->quadTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), points->at(k + 3));
+ ctrlPointX = points->at(k + 0);
+ ctrlPointY = points->at(k + 1);
+ currentX = points->at(k + 2);
+ currentY = points->at(k + 3);
+ break;
+ case 't': // Draws a quadratic Bézier curve(reflective control point)(relative)
+ reflectiveCtrlPointX = 0;
+ reflectiveCtrlPointY = 0;
+ if (previousCmd == 'q' || previousCmd == 't'
+ || previousCmd == 'Q' || previousCmd == 'T') {
+ reflectiveCtrlPointX = currentX - ctrlPointX;
+ reflectiveCtrlPointY = currentY - ctrlPointY;
+ }
+ outPath->rQuadTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
+ points->at(k + 0), points->at(k + 1));
+ ctrlPointX = currentX + reflectiveCtrlPointX;
+ ctrlPointY = currentY + reflectiveCtrlPointY;
+ currentX += points->at(k + 0);
+ currentY += points->at(k + 1);
+ break;
+ case 'T': // Draws a quadratic Bézier curve (reflective control point)
+ reflectiveCtrlPointX = currentX;
+ reflectiveCtrlPointY = currentY;
+ if (previousCmd == 'q' || previousCmd == 't'
+ || previousCmd == 'Q' || previousCmd == 'T') {
+ reflectiveCtrlPointX = 2 * currentX - ctrlPointX;
+ reflectiveCtrlPointY = 2 * currentY - ctrlPointY;
+ }
+ outPath->quadTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
+ points->at(k + 0), points->at(k + 1));
+ ctrlPointX = reflectiveCtrlPointX;
+ ctrlPointY = reflectiveCtrlPointY;
+ currentX = points->at(k + 0);
+ currentY = points->at(k + 1);
+ break;
+ case 'a': // Draws an elliptical arc
+ // (rx ry x-axis-rotation large-arc-flag sweep-flag x y)
+ drawArc(outPath,
+ currentX,
+ currentY,
+ points->at(k + 5) + currentX,
+ points->at(k + 6) + currentY,
+ points->at(k + 0),
+ points->at(k + 1),
+ points->at(k + 2),
+ points->at(k + 3) != 0,
+ points->at(k + 4) != 0);
+ currentX += points->at(k + 5);
+ currentY += points->at(k + 6);
+ ctrlPointX = currentX;
+ ctrlPointY = currentY;
+ break;
+ case 'A': // Draws an elliptical arc
+ drawArc(outPath,
+ currentX,
+ currentY,
+ points->at(k + 5),
+ points->at(k + 6),
+ points->at(k + 0),
+ points->at(k + 1),
+ points->at(k + 2),
+ points->at(k + 3) != 0,
+ points->at(k + 4) != 0);
+ currentX = points->at(k + 5);
+ currentY = points->at(k + 6);
+ ctrlPointX = currentX;
+ ctrlPointY = currentY;
+ break;
+ }
+ previousCmd = cmd;
+ }
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/VectorDrawablePath.h b/libs/hwui/VectorDrawablePath.h
new file mode 100644
index 0000000..40ce986
--- /dev/null
+++ b/libs/hwui/VectorDrawablePath.h
@@ -0,0 +1,58 @@
+/*
+ * 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 ANDROID_HWUI_VPATH_H
+#define ANDROID_HWUI_VPATH_H
+
+#include "SkPath.h"
+#include <vector>
+
+namespace android {
+namespace uirenderer {
+
+
+
+struct PathData {
+ // TODO: Try using FatVector instead of std::vector and do a micro benchmark on the performance
+ // difference.
+ std::vector<char> verbs;
+ std::vector<size_t> verbSizes;
+ std::vector<float> points;
+ bool operator== (const PathData& data) const {
+ return verbs == data.verbs && verbSizes == data.verbSizes && points == data.points;
+ }
+
+};
+
+class VectorDrawablePath {
+public:
+ VectorDrawablePath(const PathData& nodes);
+ VectorDrawablePath(const VectorDrawablePath& path);
+ VectorDrawablePath(const char* path, size_t strLength);
+ bool canMorph(const PathData& path);
+ bool canMorph(const VectorDrawablePath& path);
+ static void verbsToPath(SkPath* outPath, const PathData* data);
+ static void interpolatePaths(PathData* outPathData, const PathData* from, const PathData* to,
+ float fraction);
+private:
+ PathData mData;
+ SkPath mSkPath;
+};
+
+} // namespace uirenderer
+} // namespace android
+
+#endif // ANDROID_HWUI_VPATH_H
diff --git a/libs/hwui/microbench/DisplayListCanvasBench.cpp b/libs/hwui/microbench/DisplayListCanvasBench.cpp
index 7b6be7a..7a62037 100644
--- a/libs/hwui/microbench/DisplayListCanvasBench.cpp
+++ b/libs/hwui/microbench/DisplayListCanvasBench.cpp
@@ -15,55 +15,65 @@
*/
#include <benchmark/Benchmark.h>
-#include <utils/Singleton.h>
#include "DisplayList.h"
+#if HWUI_NEW_OPS
+#include "RecordingCanvas.h"
+#else
#include "DisplayListCanvas.h"
+#endif
#include "microbench/MicroBench.h"
+#include "unit_tests/TestUtils.h"
using namespace android;
using namespace android::uirenderer;
-BENCHMARK_NO_ARG(BM_DisplayListData_alloc);
-void BM_DisplayListData_alloc::Run(int iters) {
+#if HWUI_NEW_OPS
+typedef RecordingCanvas TestCanvas;
+#else
+typedef DisplayListCanvas TestCanvas;
+#endif
+
+BENCHMARK_NO_ARG(BM_DisplayList_alloc);
+void BM_DisplayList_alloc::Run(int iters) {
StartBenchmarkTiming();
for (int i = 0; i < iters; ++i) {
- auto data = new DisplayListData();
- MicroBench::DoNotOptimize(data);
- delete data;
+ auto displayList = new DisplayList();
+ MicroBench::DoNotOptimize(displayList);
+ delete displayList;
}
StopBenchmarkTiming();
}
-BENCHMARK_NO_ARG(BM_DisplayListData_alloc_theoretical);
-void BM_DisplayListData_alloc_theoretical::Run(int iters) {
+BENCHMARK_NO_ARG(BM_DisplayList_alloc_theoretical);
+void BM_DisplayList_alloc_theoretical::Run(int iters) {
StartBenchmarkTiming();
for (int i = 0; i < iters; ++i) {
- auto data = new char[sizeof(DisplayListData)];
- MicroBench::DoNotOptimize(data);
- delete[] data;
+ auto displayList = new char[sizeof(DisplayList)];
+ MicroBench::DoNotOptimize(displayList);
+ delete[] displayList;
}
StopBenchmarkTiming();
}
BENCHMARK_NO_ARG(BM_DisplayListCanvas_record_empty);
void BM_DisplayListCanvas_record_empty::Run(int iters) {
- DisplayListCanvas canvas(100, 100);
- canvas.finishRecording();
+ TestCanvas canvas(100, 100);
+ delete canvas.finishRecording();
StartBenchmarkTiming();
for (int i = 0; i < iters; ++i) {
canvas.reset(100, 100);
MicroBench::DoNotOptimize(&canvas);
- canvas.finishRecording();
+ delete canvas.finishRecording();
}
StopBenchmarkTiming();
}
BENCHMARK_NO_ARG(BM_DisplayListCanvas_record_saverestore);
void BM_DisplayListCanvas_record_saverestore::Run(int iters) {
- DisplayListCanvas canvas(100, 100);
- canvas.finishRecording();
+ TestCanvas canvas(100, 100);
+ delete canvas.finishRecording();
StartBenchmarkTiming();
for (int i = 0; i < iters; ++i) {
@@ -73,22 +83,56 @@
MicroBench::DoNotOptimize(&canvas);
canvas.restore();
canvas.restore();
- canvas.finishRecording();
+ delete canvas.finishRecording();
}
StopBenchmarkTiming();
}
BENCHMARK_NO_ARG(BM_DisplayListCanvas_record_translate);
void BM_DisplayListCanvas_record_translate::Run(int iters) {
- DisplayListCanvas canvas(100, 100);
- canvas.finishRecording();
+ TestCanvas canvas(100, 100);
+ delete canvas.finishRecording();
StartBenchmarkTiming();
for (int i = 0; i < iters; ++i) {
canvas.reset(100, 100);
canvas.scale(10, 10);
MicroBench::DoNotOptimize(&canvas);
- canvas.finishRecording();
+ delete canvas.finishRecording();
+ }
+ StopBenchmarkTiming();
+}
+
+/**
+ * Simulate a simple view drawing a background, overlapped by an image.
+ *
+ * Note that the recording commands are intentionally not perfectly efficient, as the
+ * View system frequently produces unneeded save/restores.
+ */
+BENCHMARK_NO_ARG(BM_DisplayListCanvas_record_simpleBitmapView);
+void BM_DisplayListCanvas_record_simpleBitmapView::Run(int iters) {
+ TestCanvas canvas(100, 100);
+ delete canvas.finishRecording();
+
+ SkPaint rectPaint;
+ SkBitmap iconBitmap = TestUtils::createSkBitmap(80, 80);
+
+ StartBenchmarkTiming();
+ for (int i = 0; i < iters; ++i) {
+ canvas.reset(100, 100);
+ {
+ canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.drawRect(0, 0, 100, 100, rectPaint);
+ canvas.restore();
+ }
+ {
+ canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.translate(10, 10);
+ canvas.drawBitmap(iconBitmap, 0, 0, nullptr);
+ canvas.restore();
+ }
+ MicroBench::DoNotOptimize(&canvas);
+ delete canvas.finishRecording();
}
StopBenchmarkTiming();
}
diff --git a/libs/hwui/microbench/LinearAllocatorBench.cpp b/libs/hwui/microbench/LinearAllocatorBench.cpp
new file mode 100644
index 0000000..75f57cb
--- /dev/null
+++ b/libs/hwui/microbench/LinearAllocatorBench.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#include <benchmark/Benchmark.h>
+
+#include "utils/LinearAllocator.h"
+#include "microbench/MicroBench.h"
+
+#include <vector>
+
+using namespace android;
+using namespace android::uirenderer;
+
+BENCHMARK_NO_ARG(BM_LinearStdAllocator_vectorBaseline);
+void BM_LinearStdAllocator_vectorBaseline::Run(int iters) {
+ StartBenchmarkTiming();
+ for (int i = 0; i < iters; i++) {
+ std::vector<char> v;
+ for (int j = 0; j < 200; j++) {
+ v.push_back(j);
+ }
+ MicroBench::DoNotOptimize(&v);
+ }
+ StopBenchmarkTiming();
+}
+
+BENCHMARK_NO_ARG(BM_LinearStdAllocator_vector);
+void BM_LinearStdAllocator_vector::Run(int iters) {
+ StartBenchmarkTiming();
+ for (int i = 0; i < iters; i++) {
+ LinearAllocator la;
+ LinearStdAllocator<void*> stdAllocator(la);
+ std::vector<char, LinearStdAllocator<char> > v(stdAllocator);
+ for (int j = 0; j < 200; j++) {
+ v.push_back(j);
+ }
+ MicroBench::DoNotOptimize(&v);
+ }
+ StopBenchmarkTiming();
+}
diff --git a/libs/hwui/microbench/OpReordererBench.cpp b/libs/hwui/microbench/OpReordererBench.cpp
new file mode 100644
index 0000000..7b8d0e5
--- /dev/null
+++ b/libs/hwui/microbench/OpReordererBench.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+#include <benchmark/Benchmark.h>
+
+#include "BakedOpState.h"
+#include "BakedOpRenderer.h"
+#include "OpReorderer.h"
+#include "RecordedOp.h"
+#include "RecordingCanvas.h"
+#include "unit_tests/TestUtils.h"
+#include "microbench/MicroBench.h"
+
+#include <vector>
+
+using namespace android;
+using namespace android::uirenderer;
+
+auto sReorderingDisplayList = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+ SkBitmap bitmap = TestUtils::createSkBitmap(10, 10);
+ SkPaint paint;
+
+ // Alternate between drawing rects and bitmaps, with bitmaps overlapping rects.
+ // Rects don't overlap bitmaps, so bitmaps should be brought to front as a group.
+ canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ for (int i = 0; i < 30; i++) {
+ canvas.translate(0, 10);
+ canvas.drawRect(0, 0, 10, 10, paint);
+ canvas.drawBitmap(bitmap, 5, 0, nullptr);
+ }
+ canvas.restore();
+});
+
+BENCHMARK_NO_ARG(BM_OpReorderer_defer);
+void BM_OpReorderer_defer::Run(int iters) {
+ StartBenchmarkTiming();
+ for (int i = 0; i < iters; i++) {
+ OpReorderer reorderer(200, 200, *sReorderingDisplayList);
+ MicroBench::DoNotOptimize(&reorderer);
+ }
+ StopBenchmarkTiming();
+}
+
+BENCHMARK_NO_ARG(BM_OpReorderer_deferAndRender);
+void BM_OpReorderer_deferAndRender::Run(int iters) {
+ TestUtils::runOnRenderThread([this, iters](renderthread::RenderThread& thread) {
+ RenderState& renderState = thread.renderState();
+ Caches& caches = Caches::getInstance();
+ StartBenchmarkTiming();
+ for (int i = 0; i < iters; i++) {
+ OpReorderer reorderer(200, 200, *sReorderingDisplayList);
+
+ BakedOpRenderer renderer(caches, renderState, true);
+ reorderer.replayBakedOps<BakedOpDispatcher>(renderer);
+ MicroBench::DoNotOptimize(&renderer);
+ }
+ StopBenchmarkTiming();
+ });
+}
diff --git a/libs/hwui/microbench/PathParserBench.cpp b/libs/hwui/microbench/PathParserBench.cpp
new file mode 100644
index 0000000..198035e
--- /dev/null
+++ b/libs/hwui/microbench/PathParserBench.cpp
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+#include <benchmark/Benchmark.h>
+
+#include "PathParser.h"
+
+#include <SkPath.h>
+
+using namespace android;
+using namespace android::uirenderer;
+
+BENCHMARK_NO_ARG(BM_PathParser_parseStringPath);
+void BM_PathParser_parseStringPath::Run(int iter) {
+ const char* pathString = "M 1 1 m 2 2, l 3 3 L 3 3 H 4 h4 V5 v5, Q6 6 6 6 q 6 6 6 6t 7 7 T 7 7 C 8 8 8 8 8 8 c 8 8 8 8 8 8 S 9 9 9 9 s 9 9 9 9 A 10 10 0 1 1 10 10 a 10 10 0 1 1 10 10";
+ SkPath skPath;
+ size_t length = strlen(pathString);
+ StartBenchmarkTiming();
+ for (int i = 0; i < iter; i++) {
+ PathParser::parseStringForSkPath(&skPath, pathString, length);
+ }
+ StopBenchmarkTiming();
+}
diff --git a/libs/hwui/microbench/ShadowBench.cpp b/libs/hwui/microbench/ShadowBench.cpp
new file mode 100644
index 0000000..1b0f5ea
--- /dev/null
+++ b/libs/hwui/microbench/ShadowBench.cpp
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ */
+
+#include <benchmark/Benchmark.h>
+
+#include "Matrix.h"
+#include "Rect.h"
+#include "Vector.h"
+#include "VertexBuffer.h"
+#include "TessellationCache.h"
+#include "microbench/MicroBench.h"
+
+#include <SkPath.h>
+
+#include <memory>
+
+using namespace android;
+using namespace android::uirenderer;
+
+struct ShadowTestData {
+ Matrix4 drawTransform;
+ Rect localClip;
+ Matrix4 casterTransformXY;
+ Matrix4 casterTransformZ;
+ Vector3 lightCenter;
+ float lightRadius;
+};
+
+void createShadowTestData(ShadowTestData* out) {
+ static float SAMPLE_DRAW_TRANSFORM[] = {
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1,
+ };
+ static float SAMPLE_CASTERXY[] = {
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 32, 32, 0, 1,
+ };
+ static float SAMPLE_CASTERZ[] = {
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 32, 32, 32, 1,
+ };
+ static Rect SAMPLE_CLIP(0, 0, 1536, 2048);
+ static Vector3 SAMPLE_LIGHT_CENTER{768, -400, 1600};
+ static float SAMPLE_LIGHT_RADIUS = 1600;
+
+ out->drawTransform.load(SAMPLE_DRAW_TRANSFORM);
+ out->localClip = SAMPLE_CLIP;
+ out->casterTransformXY.load(SAMPLE_CASTERXY);
+ out->casterTransformZ.load(SAMPLE_CASTERZ);
+ out->lightCenter = SAMPLE_LIGHT_CENTER;
+ out->lightRadius = SAMPLE_LIGHT_RADIUS;
+}
+
+static inline void tessellateShadows(ShadowTestData& testData, bool opaque,
+ const SkPath& shape, VertexBuffer* ambient, VertexBuffer* spot) {
+ tessellateShadows(&testData.drawTransform, &testData.localClip,
+ opaque, &shape, &testData.casterTransformXY,
+ &testData.casterTransformZ, testData.lightCenter,
+ testData.lightRadius, *ambient, *spot);
+}
+
+BENCHMARK_NO_ARG(BM_TessellateShadows_roundrect_opaque);
+void BM_TessellateShadows_roundrect_opaque::Run(int iters) {
+ ShadowTestData shadowData;
+ createShadowTestData(&shadowData);
+ SkPath path;
+ path.addRoundRect(SkRect::MakeWH(100, 100), 5, 5);
+
+ StartBenchmarkTiming();
+ for (int i = 0; i < iters; i++) {
+ VertexBuffer ambient;
+ VertexBuffer spot;
+ tessellateShadows(shadowData, true, path, &ambient, &spot);
+ MicroBench::DoNotOptimize(&ambient);
+ MicroBench::DoNotOptimize(&spot);
+ }
+ StopBenchmarkTiming();
+}
+
+BENCHMARK_NO_ARG(BM_TessellateShadows_roundrect_translucent);
+void BM_TessellateShadows_roundrect_translucent::Run(int iters) {
+ ShadowTestData shadowData;
+ createShadowTestData(&shadowData);
+ SkPath path;
+ path.reset();
+ path.addRoundRect(SkRect::MakeLTRB(0, 0, 100, 100), 5, 5);
+
+ StartBenchmarkTiming();
+ for (int i = 0; i < iters; i++) {
+ std::unique_ptr<VertexBuffer> ambient(new VertexBuffer);
+ std::unique_ptr<VertexBuffer> spot(new VertexBuffer);
+ tessellateShadows(shadowData, false, path, ambient.get(), spot.get());
+ MicroBench::DoNotOptimize(ambient.get());
+ MicroBench::DoNotOptimize(spot.get());
+ }
+ StopBenchmarkTiming();
+}
diff --git a/libs/hwui/microbench/how_to_run.txt b/libs/hwui/microbench/how_to_run.txt
new file mode 100755
index 0000000..e6f80b2
--- /dev/null
+++ b/libs/hwui/microbench/how_to_run.txt
@@ -0,0 +1,4 @@
+mmm -j8 frameworks/base/libs/hwui &&
+adb push $ANDROID_PRODUCT_OUT/data/local/tmp/hwuimicro \
+ /data/local/tmp/hwuimicro &&
+ adb shell /data/local/tmp/hwuimicro
diff --git a/libs/hwui/renderstate/MeshState.cpp b/libs/hwui/renderstate/MeshState.cpp
index 0521f65..03cb5ce 100644
--- a/libs/hwui/renderstate/MeshState.cpp
+++ b/libs/hwui/renderstate/MeshState.cpp
@@ -100,6 +100,24 @@
return false;
}
+void MeshState::genOrUpdateMeshBuffer(GLuint* buffer, GLsizeiptr size,
+ const void* data, GLenum usage) {
+ if (!*buffer) {
+ glGenBuffers(1, buffer);
+ }
+ bindMeshBuffer(*buffer);
+ glBufferData(GL_ARRAY_BUFFER, size, data, usage);
+}
+
+void MeshState::deleteMeshBuffer(GLuint buffer) {
+ if (buffer == mCurrentBuffer) {
+ // GL defines that deleting the currently bound VBO rebinds to 0 (no VBO).
+ // Reflect this in our cached value.
+ mCurrentBuffer = 0;
+ }
+ glDeleteBuffers(1, &buffer);
+}
+
///////////////////////////////////////////////////////////////////////////////
// Vertices
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/renderstate/MeshState.h b/libs/hwui/renderstate/MeshState.h
index e80f4d0..6c0fb78 100644
--- a/libs/hwui/renderstate/MeshState.h
+++ b/libs/hwui/renderstate/MeshState.h
@@ -75,6 +75,9 @@
*/
bool unbindMeshBuffer();
+ void genOrUpdateMeshBuffer(GLuint* buffer, GLsizeiptr size, const void* data, GLenum usage);
+ void deleteMeshBuffer(GLuint);
+
///////////////////////////////////////////////////////////////////////////////
// Vertices
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
index dfa70ac..9637117 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -125,6 +125,21 @@
}
}
+GLuint RenderState::genFramebuffer() {
+ GLuint ret;
+ glGenFramebuffers(1, &ret);
+ return ret;
+}
+
+void RenderState::deleteFramebuffer(GLuint fbo) {
+ if (mFramebuffer == fbo) {
+ // GL defines that deleting the currently bound FBO rebinds FBO 0.
+ // Reflect this in our cached value.
+ mFramebuffer = 0;
+ }
+ glDeleteFramebuffers(1, &fbo);
+}
+
void RenderState::invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info) {
if (mode == DrawGlInfo::kModeProcessNoContext) {
// If there's no context we don't need to interrupt as there's
diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h
index 9ae0845..3cda170 100644
--- a/libs/hwui/renderstate/RenderState.h
+++ b/libs/hwui/renderstate/RenderState.h
@@ -16,24 +16,25 @@
#ifndef RENDERSTATE_H
#define RENDERSTATE_H
-#include <set>
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-#include <utils/Mutex.h>
-#include <utils/Functor.h>
-#include <utils/RefBase.h>
-#include <private/hwui/DrawGlInfo.h>
-#include <renderstate/Blend.h>
-
#include "AssetAtlas.h"
#include "Caches.h"
#include "Glop.h"
+#include "renderstate/Blend.h"
#include "renderstate/MeshState.h"
#include "renderstate/PixelBufferState.h"
#include "renderstate/Scissor.h"
#include "renderstate/Stencil.h"
#include "utils/Macros.h"
+#include <set>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <ui/Region.h>
+#include <utils/Mutex.h>
+#include <utils/Functor.h>
+#include <utils/RefBase.h>
+#include <private/hwui/DrawGlInfo.h>
+
namespace android {
namespace uirenderer {
@@ -49,6 +50,8 @@
// wrapper of Caches for users to migrate to.
class RenderState {
PREVENT_COPY_AND_ASSIGN(RenderState);
+ friend class renderthread::RenderThread;
+ friend class Caches;
public:
void onGLContextCreated();
void onGLContextDestroyed();
@@ -57,7 +60,9 @@
void getViewport(GLsizei* outWidth, GLsizei* outHeight);
void bindFramebuffer(GLuint fbo);
- GLint getFramebuffer() { return mFramebuffer; }
+ GLuint getFramebuffer() { return mFramebuffer; }
+ GLuint genFramebuffer();
+ void deleteFramebuffer(GLuint fbo);
void invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info);
@@ -93,10 +98,8 @@
Stencil& stencil() { return *mStencil; }
void dump();
-private:
- friend class renderthread::RenderThread;
- friend class Caches;
+private:
void interruptForFunctorInvoke();
void resumeFromFunctorInvoke();
void assertOnGLThread();
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 38f6e53..1b89960 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -20,6 +20,7 @@
#include "Caches.h"
#include "DeferredLayerUpdater.h"
#include "EglManager.h"
+#include "LayerUpdateQueue.h"
#include "LayerRenderer.h"
#include "OpenGLRenderer.h"
#include "Properties.h"
@@ -27,6 +28,12 @@
#include "renderstate/RenderState.h"
#include "renderstate/Stencil.h"
#include "protos/hwui.pb.h"
+#include "utils/TimeUtils.h"
+
+#if HWUI_NEW_OPS
+#include "BakedOpRenderer.h"
+#include "OpReorderer.h"
+#endif
#include <cutils/properties.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
@@ -37,6 +44,8 @@
#include <fcntl.h>
#include <sys/stat.h>
+#include <cstdlib>
+
#define TRIM_MEMORY_COMPLETE 80
#define TRIM_MEMORY_UI_HIDDEN 20
@@ -103,6 +112,7 @@
const bool preserveBuffer = (mSwapBehavior != kSwap_discardBuffer);
mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer);
mHaveNewSurface = true;
+ mSwapHistory.clear();
makeCurrent();
} else {
mRenderThread.removeFrameCallback(this);
@@ -121,9 +131,11 @@
bool CanvasContext::initialize(ANativeWindow* window) {
setSurface(window);
+#if !HWUI_NEW_OPS
if (mCanvas) return false;
mCanvas = new OpenGLRenderer(mRenderThread.renderState());
mCanvas->initProperties();
+#endif
return true;
}
@@ -162,11 +174,13 @@
}
void CanvasContext::processLayerUpdate(DeferredLayerUpdater* layerUpdater) {
+#if !HWUI_NEW_OPS
bool success = layerUpdater->apply();
LOG_ALWAYS_FATAL_IF(!success, "Failed to update layer!");
if (layerUpdater->backingLayer()->deferredUpdateScheduled) {
mCanvas->pushLayerUpdate(layerUpdater->backingLayer());
}
+#endif
}
static bool wasSkipped(FrameInfo* info) {
@@ -187,8 +201,11 @@
mCurrentFrameInfo->markSyncStart();
info.damageAccumulator = &mDamageAccumulator;
+#if HWUI_NEW_OPS
+ info.layerUpdateQueue = &mLayerUpdateQueue;
+#else
info.renderer = mCanvas;
- info.canvasContext = this;
+#endif
mAnimationContext->startFrame(info.mode);
for (const sp<RenderNode>& node : mRenderNodes) {
@@ -208,13 +225,30 @@
return;
}
- int runningBehind = 0;
- // TODO: This query is moderately expensive, investigate adding some sort
- // of fast-path based off when we last called eglSwapBuffers() as well as
- // last vsync time. Or something.
- mNativeWindow->query(mNativeWindow.get(),
- NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &runningBehind);
- info.out.canDrawThisFrame = !runningBehind;
+ if (CC_LIKELY(mSwapHistory.size())) {
+ nsecs_t latestVsync = mRenderThread.timeLord().latestVsync();
+ const SwapHistory& lastSwap = mSwapHistory.back();
+ int vsyncDelta = std::abs(lastSwap.vsyncTime - latestVsync);
+ // The slight fudge-factor is to deal with cases where
+ // the vsync was estimated due to being slow handling the signal.
+ // See the logic in TimeLord#computeFrameTimeNanos or in
+ // Choreographer.java for details on when this happens
+ if (vsyncDelta < 2_ms) {
+ // Already drew for this vsync pulse, UI draw request missed
+ // the deadline for RT animations
+ info.out.canDrawThisFrame = false;
+ } else if (lastSwap.swapTime < latestVsync) {
+ info.out.canDrawThisFrame = true;
+ } else {
+ // We're maybe behind? Find out for sure
+ int runningBehind = 0;
+ mNativeWindow->query(mNativeWindow.get(),
+ NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &runningBehind);
+ info.out.canDrawThisFrame = !runningBehind;
+ }
+ } else {
+ info.out.canDrawThisFrame = true;
+ }
if (!info.out.canDrawThisFrame) {
mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
@@ -239,8 +273,10 @@
}
void CanvasContext::draw() {
+#if !HWUI_NEW_OPS
LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE,
"drawRenderNode called on a context with no canvas or surface!");
+#endif
SkRect dirty;
mDamageAccumulator.finish(&dirty);
@@ -254,10 +290,12 @@
mCurrentFrameInfo->markIssueDrawCommandsStart();
Frame frame = mEglManager.beginFrame(mEglSurface);
- if (frame.width() != mCanvas->getViewportWidth()
- || frame.height() != mCanvas->getViewportHeight()) {
+
+ if (frame.width() != mLastFrameWidth || frame.height() != mLastFrameHeight) {
// can't rely on prior content of window if viewport size changes
dirty.setEmpty();
+ mLastFrameWidth = frame.width();
+ mLastFrameHeight = frame.height();
} else if (mHaveNewSurface || frame.bufferAge() == 0) {
// New surface needs a full draw
dirty.setEmpty();
@@ -284,7 +322,7 @@
// last frame so there's nothing to union() against
// Therefore we only care about the > 1 case.
if (frame.bufferAge() > 1) {
- if (frame.bufferAge() > (int) mDamageHistory.size()) {
+ if (frame.bufferAge() > (int) mSwapHistory.size()) {
// We don't have enough history to handle this old of a buffer
// Just do a full-draw
dirty.set(0, 0, frame.width(), frame.height());
@@ -292,37 +330,47 @@
// At this point we haven't yet added the latest frame
// to the damage history (happens below)
// So we need to damage
- for (int i = mDamageHistory.size() - 1;
- i > ((int) mDamageHistory.size()) - frame.bufferAge(); i--) {
- dirty.join(mDamageHistory[i]);
+ for (int i = mSwapHistory.size() - 1;
+ i > ((int) mSwapHistory.size()) - frame.bufferAge(); i--) {
+ dirty.join(mSwapHistory[i].damage);
}
}
}
- // Add the screen damage to the ring buffer.
- mDamageHistory.next() = screenDirty;
-
mEglManager.damageFrame(frame, dirty);
+
+#if HWUI_NEW_OPS
+ OpReorderer reorderer(mLayerUpdateQueue, dirty, frame.width(), frame.height(), mRenderNodes);
+ mLayerUpdateQueue.clear();
+ BakedOpRenderer renderer(Caches::getInstance(), mRenderThread.renderState(), mOpaque);
+ // TODO: profiler().draw(mCanvas);
+ reorderer.replayBakedOps<BakedOpDispatcher>(renderer);
+
+ bool drew = renderer.didDraw();
+
+#else
mCanvas->prepareDirty(frame.width(), frame.height(),
dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom, mOpaque);
Rect outBounds;
- // It there are multiple render nodes, they are as follows:
- // #0 - backdrop
+ // It there are multiple render nodes, they are laid out as follows:
+ // #0 - backdrop (content + caption)
// #1 - content (positioned at (0,0) and clipped to - its bounds mContentDrawBounds)
- // #2 - frame
+ // #2 - additional overlay nodes
// Usually the backdrop cannot be seen since it will be entirely covered by the content. While
// resizing however it might become partially visible. The following render loop will crop the
- // backdrop against the content and draw the remaining part of it. It will then crop the content
- // against the backdrop (since that indicates a shrinking of the window) and then the frame
- // around everything.
+ // backdrop against the content and draw the remaining part of it. It will then draw the content
+ // cropped to the backdrop (since that indicates a shrinking of the window).
+ //
+ // Additional nodes will be drawn on top with no particular clipping semantics.
+
// The bounds of the backdrop against which the content should be clipped.
Rect backdropBounds = mContentDrawBounds;
// Usually the contents bounds should be mContentDrawBounds - however - we will
// move it towards the fixed edge to give it a more stable appearance (for the moment).
Rect contentBounds;
// If there is no content bounds we ignore the layering as stated above and start with 2.
- int layer = (mContentDrawBounds.isEmpty() || mRenderNodes.size() <= 2) ? 2 : 0;
+ int layer = (mContentDrawBounds.isEmpty() || mRenderNodes.size() == 1) ? 2 : 0;
// Draw all render nodes. Note that
for (const sp<RenderNode>& node : mRenderNodes) {
if (layer == 0) { // Backdrop.
@@ -399,6 +447,7 @@
const float dy = backdropBounds.top - top;
const float width = backdropBounds.getWidth();
const float height = backdropBounds.getHeight();
+
mCanvas->translate(dx, dy);
if (mCanvas->clipRect(left, top, left + width, top + height, SkRegion::kIntersect_Op)) {
mCanvas->drawRenderNode(node.get(), outBounds);
@@ -413,7 +462,7 @@
profiler().draw(mCanvas);
bool drew = mCanvas->finish();
-
+#endif
// Even if we decided to cancel the frame, from the perspective of jank
// metrics the frame was swapped at this point
mCurrentFrameInfo->markSwapBuffers();
@@ -422,6 +471,10 @@
if (CC_UNLIKELY(!mEglManager.swapBuffers(frame, screenDirty))) {
setSurface(nullptr);
}
+ SwapHistory& swap = mSwapHistory.next();
+ swap.damage = screenDirty;
+ swap.swapTime = systemTime(CLOCK_MONOTONIC);
+ swap.vsyncTime = mRenderThread.timeLord().latestVsync();
mHaveNewSurface = false;
}
@@ -464,7 +517,7 @@
.setVsync(mRenderThread.timeLord().computeFrameTimeNanos(),
mRenderThread.timeLord().latestVsync());
- TreeInfo info(TreeInfo::MODE_RT_ONLY, mRenderThread.renderState());
+ TreeInfo info(TreeInfo::MODE_RT_ONLY, *this);
prepareTree(info, frameInfo, systemTime(CLOCK_MONOTONIC), node);
if (info.out.canDrawThisFrame) {
draw();
@@ -508,9 +561,13 @@
// buildLayer() will leave the tree in an unknown state, so we must stop drawing
stopDrawing();
- TreeInfo info(TreeInfo::MODE_FULL, mRenderThread.renderState());
+ TreeInfo info(TreeInfo::MODE_FULL, *this);
info.damageAccumulator = &mDamageAccumulator;
+#if HWUI_NEW_OPS
+ info.layerUpdateQueue = &mLayerUpdateQueue;
+#else
info.renderer = mCanvas;
+#endif
info.runAnimations = false;
node->prepareTree(info);
SkRect ignore;
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index e0cbabd..d656014 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -18,9 +18,10 @@
#define CANVASCONTEXT_H_
#include "DamageAccumulator.h"
-#include "IContextFactory.h"
#include "FrameInfo.h"
#include "FrameInfoVisualizer.h"
+#include "IContextFactory.h"
+#include "LayerUpdateQueue.h"
#include "RenderNode.h"
#include "utils/RingBuffer.h"
#include "renderthread/RenderTask.h"
@@ -83,7 +84,7 @@
void draw();
void destroy();
- // IFrameCallback, Chroreographer-driven frame callback entry point
+ // IFrameCallback, Choreographer-driven frame callback entry point
virtual void doFrame() override;
void prepareAndDraw(RenderNode* node);
@@ -118,7 +119,7 @@
void addRenderNode(RenderNode* node, bool placeFront) {
int pos = placeFront ? 0 : static_cast<int>(mRenderNodes.size());
- mRenderNodes.emplace( mRenderNodes.begin() + pos, node);
+ mRenderNodes.emplace(mRenderNodes.begin() + pos, node);
}
void removeRenderNode(RenderNode* node) {
@@ -130,6 +131,10 @@
mContentDrawBounds.set(left, top, right, bottom);
}
+ RenderState& getRenderState() {
+ return mRenderThread.renderState();
+ }
+
private:
friend class RegisterFrameCallbackTask;
// TODO: Replace with something better for layer & other GL object
@@ -141,18 +146,28 @@
void freePrefetechedLayers();
+ EGLint mLastFrameWidth = 0;
+ EGLint mLastFrameHeight = 0;
+
RenderThread& mRenderThread;
EglManager& mEglManager;
sp<ANativeWindow> mNativeWindow;
EGLSurface mEglSurface = EGL_NO_SURFACE;
bool mBufferPreserved = false;
SwapBehavior mSwapBehavior = kSwap_default;
- RingBuffer<SkRect, 3> mDamageHistory;
+ struct SwapHistory {
+ SkRect damage;
+ nsecs_t vsyncTime;
+ nsecs_t swapTime;
+ };
+
+ RingBuffer<SwapHistory, 3> mSwapHistory;
bool mOpaque;
OpenGLRenderer* mCanvas = nullptr;
bool mHaveNewSurface = false;
DamageAccumulator mDamageAccumulator;
+ LayerUpdateQueue mLayerUpdateQueue;
std::unique_ptr<AnimationContext> mAnimationContext;
std::vector< sp<RenderNode> > mRenderNodes;
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index a47c9ec..ab860c7 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -87,7 +87,7 @@
bool canUnblockUiThread;
bool canDrawThisFrame;
{
- TreeInfo info(TreeInfo::MODE_FULL, mRenderThread->renderState());
+ TreeInfo info(TreeInfo::MODE_FULL, *mContext);
canUnblockUiThread = syncFrameState(info);
canDrawThisFrame = info.out.canDrawThisFrame;
}
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index 68ee897..cae251a9 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -32,7 +32,7 @@
namespace uirenderer {
class DeferredLayerUpdater;
-class DisplayListData;
+class DisplayList;
class RenderNode;
namespace renderthread {
@@ -48,7 +48,7 @@
/*
* This is a special Super Task. It is re-used multiple times by RenderProxy,
- * and contains state (such as layer updaters & new DisplayListDatas) that is
+ * and contains state (such as layer updaters & new DisplayLists) that is
* tracked across many frames not just a single frame.
* It is the sync-state task, and will kick off the post-sync draw
*/
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index c9b9637..78df297 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -17,15 +17,14 @@
#include "EglManager.h"
#include "Caches.h"
+#include "DeviceInfo.h"
#include "Properties.h"
#include "RenderThread.h"
#include "renderstate/RenderState.h"
#include "utils/StringUtils.h"
-
#include <cutils/log.h>
#include <cutils/properties.h>
#include <EGL/eglext.h>
-
#include <string>
#define GLES_VERSION 2
@@ -129,14 +128,18 @@
createContext();
createPBufferSurface();
makeCurrent(mPBufferSurface);
+ DeviceInfo::initialize();
mRenderThread.renderState().onGLContextCreated();
initAtlas();
}
void EglManager::initExtensions() {
- StringCollection extensions(eglQueryString(mEglDisplay, EGL_EXTENSIONS));
+ auto extensions = StringUtils::split(
+ eglQueryString(mEglDisplay, EGL_EXTENSIONS));
EglExtensions.bufferAge = extensions.has("EGL_EXT_buffer_age");
EglExtensions.setDamage = extensions.has("EGL_KHR_partial_update");
+ LOG_ALWAYS_FATAL_IF(!extensions.has("EGL_KHR_swap_buffers_with_damage"),
+ "Missing required extension EGL_KHR_swap_buffers_with_damage");
}
bool EglManager::hasEglContext() {
@@ -322,24 +325,16 @@
}
#endif
-#ifdef EGL_KHR_swap_buffers_with_damage
- if (CC_LIKELY(Properties::swapBuffersWithDamage)) {
- EGLint rects[4];
- frame.map(screenDirty, rects);
- eglSwapBuffersWithDamageKHR(mEglDisplay, frame.mSurface, rects,
- screenDirty.isEmpty() ? 0 : 1);
- } else {
- eglSwapBuffers(mEglDisplay, frame.mSurface);
- }
-#else
- eglSwapBuffers(mEglDisplay, frame.mSurface);
-#endif
+ EGLint rects[4];
+ frame.map(screenDirty, rects);
+ eglSwapBuffersWithDamageKHR(mEglDisplay, frame.mSurface, rects,
+ screenDirty.isEmpty() ? 0 : 1);
EGLint err = eglGetError();
if (CC_LIKELY(err == EGL_SUCCESS)) {
return true;
}
- if (err == EGL_BAD_SURFACE) {
+ if (err == EGL_BAD_SURFACE || err == EGL_BAD_NATIVE_WINDOW) {
// For some reason our surface was destroyed out from under us
// This really shouldn't happen, but if it does we can recover easily
// by just not trying to use the surface anymore
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 26aae90..15ccd6a 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -574,12 +574,7 @@
RenderThread& thread = RenderThread::getInstance();
void* retval;
task->setReturnPtr(&retval);
- Mutex mutex;
- Condition condition;
- SignalingRenderTask syncTask(task, &mutex, &condition);
- AutoMutex _lock(mutex);
- thread.queue(&syncTask);
- condition.wait(mutex);
+ thread.queueAndWait(task);
return retval;
}
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index d1b62f1..338fab6 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -38,7 +38,7 @@
class DeferredLayerUpdater;
class RenderNode;
-class DisplayListData;
+class DisplayList;
class Layer;
class Rect;
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 64075f1..9fb30c9 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -25,12 +25,11 @@
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
#include <sys/resource.h>
+#include <utils/Condition.h>
#include <utils/Log.h>
+#include <utils/Mutex.h>
namespace android {
-using namespace uirenderer::renderthread;
-ANDROID_SINGLETON_STATIC_INSTANCE(RenderThread);
-
namespace uirenderer {
namespace renderthread {
@@ -136,7 +135,22 @@
}
};
-RenderThread::RenderThread() : Thread(true), Singleton<RenderThread>()
+static bool gHasRenderThreadInstance = false;
+
+bool RenderThread::hasInstance() {
+ return gHasRenderThreadInstance;
+}
+
+RenderThread& RenderThread::getInstance() {
+ // This is a pointer because otherwise __cxa_finalize
+ // will try to delete it like a Good Citizen but that causes us to crash
+ // because we don't want to delete the RenderThread normally.
+ static RenderThread* sInstance = new RenderThread();
+ gHasRenderThreadInstance = true;
+ return *sInstance;
+}
+
+RenderThread::RenderThread() : Thread(true)
, mNextWakeup(LLONG_MAX)
, mDisplayEventReceiver(nullptr)
, mVsyncRequested(false)
@@ -312,6 +326,19 @@
}
}
+void RenderThread::queueAndWait(RenderTask* task) {
+ // These need to be local to the thread to avoid the Condition
+ // signaling the wrong thread. The easiest way to achieve that is to just
+ // make this on the stack, although that has a slight cost to it
+ Mutex mutex;
+ Condition condition;
+ SignalingRenderTask syncTask(task, &mutex, &condition);
+
+ AutoMutex _lock(mutex);
+ queue(&syncTask);
+ condition.wait(mutex);
+}
+
void RenderThread::queueAtFront(RenderTask* task) {
AutoMutex _lock(mLock);
mQueue.queueAtFront(task);
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 8096099..076e3d4 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -25,8 +25,6 @@
#include <cutils/compiler.h>
#include <ui/DisplayInfo.h>
#include <utils/Looper.h>
-#include <utils/Mutex.h>
-#include <utils/Singleton.h>
#include <utils/Thread.h>
#include <memory>
@@ -39,6 +37,7 @@
namespace uirenderer {
class RenderState;
+class TestUtils;
namespace renderthread {
@@ -71,11 +70,12 @@
~IFrameCallback() {}
};
-class ANDROID_API RenderThread : public Thread, protected Singleton<RenderThread> {
+class ANDROID_API RenderThread : public Thread {
public:
// RenderThread takes complete ownership of tasks that are queued
// and will delete them after they are run
ANDROID_API void queue(RenderTask* task);
+ ANDROID_API void queueAndWait(RenderTask* task);
ANDROID_API void queueAtFront(RenderTask* task);
void queueAt(RenderTask* task, nsecs_t runAtNs);
void remove(RenderTask* task);
@@ -98,13 +98,16 @@
virtual bool threadLoop() override;
private:
- friend class Singleton<RenderThread>;
friend class DispatchFrameCallbacks;
friend class RenderProxy;
+ friend class android::uirenderer::TestUtils;
RenderThread();
virtual ~RenderThread();
+ static bool hasInstance();
+ static RenderThread& getInstance();
+
void initThreadLocals();
void initializeDisplayEventReceiver();
static int displayEventReceiverCallback(int fd, int events, void* data);
diff --git a/libs/hwui/tests/TreeContentAnimation.cpp b/libs/hwui/tests/TreeContentAnimation.cpp
index a59261c..29d9803 100644
--- a/libs/hwui/tests/TreeContentAnimation.cpp
+++ b/libs/hwui/tests/TreeContentAnimation.cpp
@@ -20,9 +20,11 @@
#include <AnimationContext.h>
#include <DisplayListCanvas.h>
+#include <RecordingCanvas.h>
#include <RenderNode.h>
#include <renderthread/RenderProxy.h>
#include <renderthread/RenderTask.h>
+#include <unit_tests/TestUtils.h>
#include "Benchmark.h"
#include "TestContext.h"
@@ -39,6 +41,13 @@
using namespace android::uirenderer::renderthread;
using namespace android::uirenderer::test;
+#if HWUI_NEW_OPS
+typedef RecordingCanvas TestCanvas;
+#else
+typedef DisplayListCanvas TestCanvas;
+#endif
+
+
class ContextFactory : public IContextFactory {
public:
virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
@@ -46,15 +55,10 @@
}
};
-static DisplayListCanvas* startRecording(RenderNode* node) {
- DisplayListCanvas* renderer = new DisplayListCanvas(
- node->stagingProperties().getWidth(), node->stagingProperties().getHeight());
- return renderer;
-}
-
-static void endRecording(DisplayListCanvas* renderer, RenderNode* node) {
- node->setStagingDisplayList(renderer->finishRecording());
- delete renderer;
+static void recordNode(RenderNode& node, std::function<void(TestCanvas&)> contentCallback) {
+ TestCanvas canvas(node.stagingProperties().getWidth(), node.stagingProperties().getHeight());
+ contentCallback(canvas);
+ node.setStagingDisplayList(canvas.finishRecording());
}
class TreeContentAnimation {
@@ -67,7 +71,7 @@
frameCount = fc;
}
}
- virtual void createContent(int width, int height, DisplayListCanvas* renderer) = 0;
+ virtual void createContent(int width, int height, TestCanvas* canvas) = 0;
virtual void doFrame(int frameNr) = 0;
template <class T>
@@ -100,11 +104,9 @@
proxy->setup(width, height, dp(800.0f), 255 * 0.075, 255 * 0.15);
proxy->setLightCenter((Vector3){lightX, dp(-200.0f), dp(800.0f)});
- android::uirenderer::Rect DUMMY;
-
- DisplayListCanvas* renderer = startRecording(rootNode);
- animation.createContent(width, height, renderer);
- endRecording(renderer, rootNode);
+ recordNode(*rootNode, [&animation, width, height](TestCanvas& canvas) {
+ animation.createContent(width, height, &canvas); //TODO: no&
+ });
// Do a few cold runs then reset the stats so that the caches are all hot
for (int i = 0; i < 3; i++) {
@@ -132,19 +134,19 @@
class ShadowGridAnimation : public TreeContentAnimation {
public:
std::vector< sp<RenderNode> > cards;
- void createContent(int width, int height, DisplayListCanvas* renderer) override {
- renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
- renderer->insertReorderBarrier(true);
+ void createContent(int width, int height, TestCanvas* canvas) override {
+ canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ canvas->insertReorderBarrier(true);
for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
sp<RenderNode> card = createCard(x, y, dp(100), dp(100));
- renderer->drawRenderNode(card.get());
+ canvas->drawRenderNode(card.get());
cards.push_back(card);
}
}
- renderer->insertReorderBarrier(false);
+ canvas->insertReorderBarrier(false);
}
void doFrame(int frameNr) override {
int curFrame = frameNr % 150;
@@ -163,9 +165,9 @@
node->mutateStagingProperties().mutableOutline().setShouldClip(true);
node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
- DisplayListCanvas* renderer = startRecording(node.get());
- renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
- endRecording(renderer, node.get());
+ recordNode(*node, [](TestCanvas& canvas) {
+ canvas.drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
+ });
return node;
}
};
@@ -179,19 +181,19 @@
class ShadowGrid2Animation : public TreeContentAnimation {
public:
std::vector< sp<RenderNode> > cards;
- void createContent(int width, int height, DisplayListCanvas* renderer) override {
- renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
- renderer->insertReorderBarrier(true);
+ void createContent(int width, int height, TestCanvas* canvas) override {
+ canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ canvas->insertReorderBarrier(true);
for (int x = dp(8); x < (width - dp(58)); x += dp(58)) {
for (int y = dp(8); y < (height - dp(58)); y += dp(58)) {
sp<RenderNode> card = createCard(x, y, dp(50), dp(50));
- renderer->drawRenderNode(card.get());
+ canvas->drawRenderNode(card.get());
cards.push_back(card);
}
}
- renderer->insertReorderBarrier(false);
+ canvas->insertReorderBarrier(false);
}
void doFrame(int frameNr) override {
int curFrame = frameNr % 150;
@@ -210,9 +212,9 @@
node->mutateStagingProperties().mutableOutline().setShouldClip(true);
node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
- DisplayListCanvas* renderer = startRecording(node.get());
- renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
- endRecording(renderer, node.get());
+ recordNode(*node, [](TestCanvas& canvas) {
+ canvas.drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
+ });
return node;
}
};
@@ -225,15 +227,30 @@
class RectGridAnimation : public TreeContentAnimation {
public:
- sp<RenderNode> card;
- void createContent(int width, int height, DisplayListCanvas* renderer) override {
- renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
- renderer->insertReorderBarrier(true);
+ sp<RenderNode> card = new RenderNode();
+ void createContent(int width, int height, TestCanvas* canvas) override {
+ canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ canvas->insertReorderBarrier(true);
- card = createCard(40, 40, 200, 200);
- renderer->drawRenderNode(card.get());
+ card->mutateStagingProperties().setLeftTopRightBottom(50, 50, 250, 250);
+ card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ recordNode(*card, [](TestCanvas& canvas) {
+ canvas.drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode);
- renderer->insertReorderBarrier(false);
+ SkRegion region;
+ for (int xOffset = 0; xOffset < 200; xOffset+=2) {
+ for (int yOffset = 0; yOffset < 200; yOffset+=2) {
+ region.op(xOffset, yOffset, xOffset + 1, yOffset + 1, SkRegion::kUnion_Op);
+ }
+ }
+
+ SkPaint paint;
+ paint.setColor(0xff00ffff);
+ canvas.drawRegion(region, paint);
+ });
+ canvas->drawRenderNode(card.get());
+
+ canvas->insertReorderBarrier(false);
}
void doFrame(int frameNr) override {
int curFrame = frameNr % 150;
@@ -241,29 +258,6 @@
card->mutateStagingProperties().setTranslationY(curFrame);
card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
}
-private:
- sp<RenderNode> createCard(int x, int y, int width, int height) {
- sp<RenderNode> node = new RenderNode();
- node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
- node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
-
- DisplayListCanvas* renderer = startRecording(node.get());
- renderer->drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode);
-
- SkRegion region;
- for (int xOffset = 0; xOffset < width; xOffset+=2) {
- for (int yOffset = 0; yOffset < height; yOffset+=2) {
- region.op(xOffset, yOffset, xOffset + 1, yOffset + 1, SkRegion::kUnion_Op);
- }
- }
-
- SkPaint paint;
- paint.setColor(0xff00ffff);
- renderer->drawRegion(region, paint);
-
- endRecording(renderer, node.get());
- return node;
- }
};
static Benchmark _RectGrid(BenchmarkInfo{
"rectgrid",
@@ -274,15 +268,22 @@
class OvalAnimation : public TreeContentAnimation {
public:
- sp<RenderNode> card;
- void createContent(int width, int height, DisplayListCanvas* renderer) override {
- renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
- renderer->insertReorderBarrier(true);
+ sp<RenderNode> card = new RenderNode();
+ void createContent(int width, int height, TestCanvas* canvas) override {
+ canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ canvas->insertReorderBarrier(true);
- card = createCard(40, 40, 400, 400);
- renderer->drawRenderNode(card.get());
+ card->mutateStagingProperties().setLeftTopRightBottom(0, 0, 200, 200);
+ card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ recordNode(*card, [](TestCanvas& canvas) {
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setColor(0xFF000000);
+ canvas.drawOval(0, 0, 200, 200, paint);
+ });
+ canvas->drawRenderNode(card.get());
- renderer->insertReorderBarrier(false);
+ canvas->insertReorderBarrier(false);
}
void doFrame(int frameNr) override {
@@ -291,22 +292,6 @@
card->mutateStagingProperties().setTranslationY(curFrame);
card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
}
-private:
- sp<RenderNode> createCard(int x, int y, int width, int height) {
- sp<RenderNode> node = new RenderNode();
- node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
- node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
-
- DisplayListCanvas* renderer = startRecording(node.get());
-
- SkPaint paint;
- paint.setAntiAlias(true);
- paint.setColor(0xFF000000);
- renderer->drawOval(0, 0, width, height, paint);
-
- endRecording(renderer, node.get());
- return node;
- }
};
static Benchmark _Oval(BenchmarkInfo{
"oval",
@@ -317,7 +302,7 @@
class PartialDamageTest : public TreeContentAnimation {
public:
std::vector< sp<RenderNode> > cards;
- void createContent(int width, int height, DisplayListCanvas* renderer) override {
+ void createContent(int width, int height, TestCanvas* canvas) override {
static SkColor COLORS[] = {
0xFFF44336,
0xFF9C27B0,
@@ -325,13 +310,13 @@
0xFF4CAF50,
};
- renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
sp<RenderNode> card = createCard(x, y, dp(100), dp(100),
COLORS[static_cast<int>((y / dp(116))) % 4]);
- renderer->drawRenderNode(card.get());
+ canvas->drawRenderNode(card.get());
cards.push_back(card);
}
}
@@ -342,10 +327,10 @@
cards[0]->mutateStagingProperties().setTranslationY(curFrame);
cards[0]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- DisplayListCanvas* renderer = startRecording(cards[0].get());
- renderer->drawColor(interpolateColor(curFrame / 150.0f, 0xFFF44336, 0xFFF8BBD0),
- SkXfermode::kSrcOver_Mode);
- endRecording(renderer, cards[0].get());
+ recordNode(*cards[0], [curFrame](TestCanvas& canvas) {
+ canvas.drawColor(interpolateColor(curFrame / 150.0f, 0xFFF44336, 0xFFF8BBD0),
+ SkXfermode::kSrcOver_Mode);
+ });
}
static SkColor interpolateColor(float fraction, SkColor start, SkColor end) {
@@ -370,9 +355,9 @@
node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- DisplayListCanvas* renderer = startRecording(node.get());
- renderer->drawColor(color, SkXfermode::kSrcOver_Mode);
- endRecording(renderer, node.get());
+ recordNode(*node, [color](TestCanvas& canvas) {
+ canvas.drawColor(color, SkXfermode::kSrcOver_Mode);
+ });
return node;
}
};
@@ -383,3 +368,61 @@
"EGL_KHR_partial_update is supported by the device & are enabled in hwui.",
TreeContentAnimation::run<PartialDamageTest>
});
+
+
+class SaveLayerAnimation : public TreeContentAnimation {
+public:
+ sp<RenderNode> card = new RenderNode();
+ void createContent(int width, int height, TestCanvas* canvas) override {
+ canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); // background
+
+ card->mutateStagingProperties().setLeftTopRightBottom(0, 0, 200, 200);
+ card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ recordNode(*card, [](TestCanvas& canvas) {
+ canvas.saveLayerAlpha(0, 0, 200, 200, 128, SkCanvas::kClipToLayer_SaveFlag);
+ canvas.drawColor(0xFF00FF00, SkXfermode::kSrcOver_Mode); // outer, unclipped
+ canvas.saveLayerAlpha(50, 50, 150, 150, 128, SkCanvas::kClipToLayer_SaveFlag);
+ canvas.drawColor(0xFF0000FF, SkXfermode::kSrcOver_Mode); // inner, clipped
+ canvas.restore();
+ canvas.restore();
+ });
+
+ canvas->drawRenderNode(card.get());
+ }
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ card->mutateStagingProperties().setTranslationX(curFrame);
+ card->mutateStagingProperties().setTranslationY(curFrame);
+ card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+};
+static Benchmark _SaveLayer(BenchmarkInfo{
+ "savelayer",
+ "A nested pair of clipped saveLayer operations. "
+ "Tests the clipped saveLayer codepath. Draws content into offscreen buffers and back again.",
+ TreeContentAnimation::run<SaveLayerAnimation>
+});
+
+
+class HwLayerAnimation : public TreeContentAnimation {
+public:
+ sp<RenderNode> card = TestUtils::createNode<TestCanvas>(0, 0, 200, 200, [] (TestCanvas& canvas) {
+ canvas.drawColor(0xFF0000FF, SkXfermode::kSrcOver_Mode);
+ }, true);
+ void createContent(int width, int height, TestCanvas* canvas) override {
+ canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); // background
+ canvas->drawRenderNode(card.get());
+ }
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ card->mutateStagingProperties().setTranslationX(curFrame);
+ card->mutateStagingProperties().setTranslationY(curFrame);
+ card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+};
+static Benchmark _HwLayer(BenchmarkInfo{
+ "hwlayer",
+ "A nested pair of nodes with LAYER_TYPE_HARDWARE set on each. "
+ "Tests the hardware layer codepath.",
+ TreeContentAnimation::run<HwLayerAnimation>
+});
diff --git a/libs/hwui/unit_tests/BakedOpStateTests.cpp b/libs/hwui/unit_tests/BakedOpStateTests.cpp
new file mode 100644
index 0000000..bc1b69f
--- /dev/null
+++ b/libs/hwui/unit_tests/BakedOpStateTests.cpp
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include <BakedOpState.h>
+#include <RecordedOp.h>
+#include <unit_tests/TestUtils.h>
+
+namespace android {
+namespace uirenderer {
+
+TEST(ResolvedRenderState, resolution) {
+ Matrix4 identity;
+ identity.loadIdentity();
+
+ Matrix4 translate10x20;
+ translate10x20.loadTranslate(10, 20, 0);
+
+ SkPaint paint;
+ RectOp recordedOp(Rect(30, 40, 100, 200), translate10x20, Rect(0, 0, 100, 200), &paint);
+ {
+ // recorded with transform, no parent transform
+ auto parentSnapshot = TestUtils::makeSnapshot(identity, Rect(0, 0, 100, 200));
+ ResolvedRenderState state(*parentSnapshot, recordedOp);
+ EXPECT_MATRIX_APPROX_EQ(state.transform, translate10x20);
+ EXPECT_EQ(state.clipRect, Rect(0, 0, 100, 200));
+ EXPECT_EQ(state.clippedBounds, Rect(40, 60, 100, 200)); // translated and also clipped
+ }
+ {
+ // recorded with transform and parent transform
+ auto parentSnapshot = TestUtils::makeSnapshot(translate10x20, Rect(0, 0, 100, 200));
+ ResolvedRenderState state(*parentSnapshot, recordedOp);
+
+ Matrix4 expectedTranslate;
+ expectedTranslate.loadTranslate(20, 40, 0);
+ EXPECT_MATRIX_APPROX_EQ(state.transform, expectedTranslate);
+
+ // intersection of parent & transformed child clip
+ EXPECT_EQ(state.clipRect, Rect(10, 20, 100, 200));
+
+ // translated and also clipped
+ EXPECT_EQ(state.clippedBounds, Rect(50, 80, 100, 200));
+ }
+}
+
+TEST(BakedOpState, constructAndReject) {
+ LinearAllocator allocator;
+
+ Matrix4 identity;
+ identity.loadIdentity();
+
+ Matrix4 translate100x0;
+ translate100x0.loadTranslate(100, 0, 0);
+
+ SkPaint paint;
+ {
+ RectOp rejectOp(Rect(30, 40, 100, 200), translate100x0, Rect(0, 0, 100, 200), &paint);
+ auto snapshot = TestUtils::makeSnapshot(identity, Rect(0, 0, 100, 200));
+ BakedOpState* bakedOp = BakedOpState::tryConstruct(allocator, *snapshot, rejectOp);
+
+ EXPECT_EQ(bakedOp, nullptr); // rejected by clip, so not constructed
+ EXPECT_LE(allocator.usedSize(), 8u); // no significant allocation space used for rejected op
+ }
+ {
+ RectOp successOp(Rect(30, 40, 100, 200), identity, Rect(0, 0, 100, 200), &paint);
+ auto snapshot = TestUtils::makeSnapshot(identity, Rect(0, 0, 100, 200));
+ BakedOpState* bakedOp = BakedOpState::tryConstruct(allocator, *snapshot, successOp);
+
+ EXPECT_NE(bakedOp, nullptr); // NOT rejected by clip, so will be constructed
+ EXPECT_GT(allocator.usedSize(), 64u); // relatively large alloc for non-rejected op
+ }
+}
+
+}
+}
diff --git a/libs/hwui/unit_tests/ClipAreaTests.cpp b/libs/hwui/unit_tests/ClipAreaTests.cpp
index 0c5e5e7..d6192df 100644
--- a/libs/hwui/unit_tests/ClipAreaTests.cpp
+++ b/libs/hwui/unit_tests/ClipAreaTests.cpp
@@ -101,10 +101,9 @@
EXPECT_FALSE(area.isEmpty());
EXPECT_FALSE(area.isSimple());
EXPECT_FALSE(area.isRectangleList());
+
Rect clipRect(area.getClipRect());
- clipRect.dump("clipRect");
Rect expected(0, 0, r * 2, r * 2);
- expected.dump("expected");
EXPECT_EQ(expected, clipRect);
SkRegion clipRegion(area.getClipRegion());
auto skRect(clipRegion.getBounds());
diff --git a/libs/hwui/unit_tests/DeviceInfoTests.cpp b/libs/hwui/unit_tests/DeviceInfoTests.cpp
new file mode 100644
index 0000000..17236bd
--- /dev/null
+++ b/libs/hwui/unit_tests/DeviceInfoTests.cpp
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+#include <DeviceInfo.h>
+
+#include <gtest/gtest.h>
+
+using namespace android;
+using namespace android::uirenderer;
+
+TEST(DeviceInfo, basic) {
+ // can't assert state before init - another test may have initialized the singleton
+ DeviceInfo::initialize();
+ const DeviceInfo* di = DeviceInfo::get();
+ ASSERT_NE(nullptr, di) << "DeviceInfo initialization failed";
+ EXPECT_EQ(2048, di->maxTextureSize()) << "Max texture size didn't match";
+}
diff --git a/libs/hwui/unit_tests/FatVectorTests.cpp b/libs/hwui/unit_tests/FatVectorTests.cpp
new file mode 100644
index 0000000..3ef329a
--- /dev/null
+++ b/libs/hwui/unit_tests/FatVectorTests.cpp
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+#include <utils/FatVector.h>
+
+#include <unit_tests/TestUtils.h>
+
+using namespace android;
+using namespace android::uirenderer;
+
+template<class VectorType>
+static bool allocationIsInternal(VectorType& v) {
+ // allocation array (from &v[0] to &v[0] + v.capacity) is
+ // located within the vector object itself
+ return (char*)(&v) <= (char*)(&v[0])
+ && (char*)(&v + 1) >= (char*)(&v[0] + v.capacity());
+}
+
+TEST(FatVector, baseline) {
+ // Verify allocation behavior FatVector contrasts against - allocations are always external
+ std::vector<int> v;
+ for (int i = 0; i < 50; i++) {
+ v.push_back(i);
+ EXPECT_FALSE(allocationIsInternal(v));
+ }
+}
+
+TEST(FatVector, simpleAllocate) {
+ FatVector<int, 4> v;
+ EXPECT_EQ(4u, v.capacity());
+
+ // can insert 4 items into internal buffer
+ for (int i = 0; i < 4; i++) {
+ v.push_back(i);
+ EXPECT_TRUE(allocationIsInternal(v));
+ }
+
+ // then will fall back to external allocation
+ for (int i = 5; i < 50; i++) {
+ v.push_back(i);
+ EXPECT_FALSE(allocationIsInternal(v));
+ }
+}
+
+TEST(FatVector, preSizeConstructor) {
+ {
+ FatVector<int, 4> v(32);
+ EXPECT_EQ(32u, v.capacity());
+ EXPECT_EQ(32u, v.size());
+ EXPECT_FALSE(allocationIsInternal(v));
+ }
+ {
+ FatVector<int, 4> v(4);
+ EXPECT_EQ(4u, v.capacity());
+ EXPECT_EQ(4u, v.size());
+ EXPECT_TRUE(allocationIsInternal(v));
+ }
+ {
+ FatVector<int, 4> v(2);
+ EXPECT_EQ(4u, v.capacity());
+ EXPECT_EQ(2u, v.size());
+ EXPECT_TRUE(allocationIsInternal(v));
+ }
+}
+
+TEST(FatVector, shrink) {
+ FatVector<int, 10> v;
+ EXPECT_TRUE(allocationIsInternal(v));
+
+ // push into external alloc
+ v.resize(11);
+ EXPECT_FALSE(allocationIsInternal(v));
+
+ // shrinking back to internal alloc succeeds
+ // note that shrinking further will succeed, but is a waste
+ v.resize(10);
+ v.shrink_to_fit();
+ EXPECT_TRUE(allocationIsInternal(v));
+}
+
+TEST(FatVector, destructorInternal) {
+ int count = 0;
+ {
+ // push 1 into external allocation, verify destruction happens once
+ FatVector<TestUtils::SignalingDtor, 0> v;
+ v.emplace_back(&count);
+ EXPECT_FALSE(allocationIsInternal(v));
+ EXPECT_EQ(0, count) << "Destruction shouldn't have happened yet";
+ }
+ EXPECT_EQ(1, count) << "Destruction should happen exactly once";
+}
+
+TEST(FatVector, destructorExternal) {
+ int count = 0;
+ {
+ // push 10 into internal allocation, verify 10 destructors called
+ FatVector<TestUtils::SignalingDtor, 10> v;
+ for (int i = 0; i < 10; i++) {
+ v.emplace_back(&count);
+ EXPECT_TRUE(allocationIsInternal(v));
+ }
+ EXPECT_EQ(0, count) << "Destruction shouldn't have happened yet";
+ }
+ EXPECT_EQ(10, count) << "Destruction should happen exactly once";
+}
diff --git a/libs/hwui/unit_tests/LayerUpdateQueueTests.cpp b/libs/hwui/unit_tests/LayerUpdateQueueTests.cpp
new file mode 100644
index 0000000..ef205ec
--- /dev/null
+++ b/libs/hwui/unit_tests/LayerUpdateQueueTests.cpp
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include <LayerUpdateQueue.h>
+#include <RenderNode.h>
+
+#include <unit_tests/TestUtils.h>
+
+namespace android {
+namespace uirenderer {
+
+TEST(LayerUpdateQueue, construct) {
+ LayerUpdateQueue queue;
+ EXPECT_TRUE(queue.entries().empty());
+}
+
+// sync node properties, so properties() reflects correct width and height
+static sp<RenderNode> createSyncedNode(uint32_t width, uint32_t height) {
+ sp<RenderNode> node = TestUtils::createNode(0, 0, width, height);
+ TestUtils::syncHierarchyPropertiesAndDisplayList(node);
+ return node;
+}
+
+TEST(LayerUpdateQueue, enqueueSimple) {
+ sp<RenderNode> a = createSyncedNode(100, 100);
+ sp<RenderNode> b = createSyncedNode(200, 200);
+
+ LayerUpdateQueue queue;
+ queue.enqueueLayerWithDamage(a.get(), Rect(25, 25, 75, 75));
+ queue.enqueueLayerWithDamage(b.get(), Rect(100, 100, 300, 300));
+
+ EXPECT_EQ(2u, queue.entries().size());
+
+ EXPECT_EQ(a.get(), queue.entries()[0].renderNode);
+ EXPECT_EQ(Rect(25, 25, 75, 75), queue.entries()[0].damage);
+ EXPECT_EQ(b.get(), queue.entries()[1].renderNode);
+ EXPECT_EQ(Rect(100, 100, 200, 200), queue.entries()[1].damage); // clipped to bounds
+}
+
+TEST(LayerUpdateQueue, enqueueUnion) {
+ sp<RenderNode> a = createSyncedNode(100, 100);
+
+ LayerUpdateQueue queue;
+ queue.enqueueLayerWithDamage(a.get(), Rect(10, 10, 20, 20));
+ queue.enqueueLayerWithDamage(a.get(), Rect(30, 30, 40, 40));
+
+ EXPECT_EQ(1u, queue.entries().size());
+
+ EXPECT_EQ(a.get(), queue.entries()[0].renderNode);
+ EXPECT_EQ(Rect(10, 10, 40, 40), queue.entries()[0].damage);
+}
+
+TEST(LayerUpdateQueue, clear) {
+ sp<RenderNode> a = createSyncedNode(100, 100);
+
+ LayerUpdateQueue queue;
+ queue.enqueueLayerWithDamage(a.get(), Rect(100, 100));
+
+ EXPECT_FALSE(queue.entries().empty());
+
+ queue.clear();
+
+ EXPECT_TRUE(queue.entries().empty());
+}
+
+};
+};
diff --git a/libs/hwui/unit_tests/LinearAllocatorTests.cpp b/libs/hwui/unit_tests/LinearAllocatorTests.cpp
index b3959d1..0f6b249 100644
--- a/libs/hwui/unit_tests/LinearAllocatorTests.cpp
+++ b/libs/hwui/unit_tests/LinearAllocatorTests.cpp
@@ -17,6 +17,8 @@
#include <gtest/gtest.h>
#include <utils/LinearAllocator.h>
+#include <unit_tests/TestUtils.h>
+
using namespace android;
using namespace android::uirenderer;
@@ -25,27 +27,6 @@
int two = 2;
};
-class SignalingDtor {
-public:
- SignalingDtor() {
- mDestroyed = nullptr;
- }
- SignalingDtor(bool* destroyedSignal) {
- mDestroyed = destroyedSignal;
- *mDestroyed = false;
- }
- virtual ~SignalingDtor() {
- if (mDestroyed) {
- *mDestroyed = true;
- }
- }
- void setSignal(bool* destroyedSignal) {
- mDestroyed = destroyedSignal;
- }
-private:
- bool* mDestroyed;
-};
-
TEST(LinearAllocator, alloc) {
LinearAllocator la;
EXPECT_EQ(0u, la.usedSize());
@@ -62,31 +43,31 @@
}
TEST(LinearAllocator, dtor) {
- bool destroyed[10];
+ int destroyed[10] = { 0 };
{
LinearAllocator la;
for (int i = 0; i < 5; i++) {
- la.alloc<SignalingDtor>()->setSignal(destroyed + i);
+ la.alloc<TestUtils::SignalingDtor>()->setSignal(destroyed + i);
la.alloc<SimplePair>();
}
la.alloc(100);
for (int i = 0; i < 5; i++) {
- auto sd = new (la) SignalingDtor(destroyed + 5 + i);
+ auto sd = new (la) TestUtils::SignalingDtor(destroyed + 5 + i);
la.autoDestroy(sd);
new (la) SimplePair();
}
la.alloc(100);
for (int i = 0; i < 10; i++) {
- EXPECT_FALSE(destroyed[i]);
+ EXPECT_EQ(0, destroyed[i]);
}
}
for (int i = 0; i < 10; i++) {
- EXPECT_TRUE(destroyed[i]);
+ EXPECT_EQ(1, destroyed[i]);
}
}
TEST(LinearAllocator, rewind) {
- bool destroyed;
+ int destroyed = 0;
{
LinearAllocator la;
auto addr = la.alloc(100);
@@ -94,15 +75,42 @@
la.rewindIfLastAlloc(addr, 100);
EXPECT_GT(16u, la.usedSize());
size_t emptySize = la.usedSize();
- auto sigdtor = la.alloc<SignalingDtor>();
+ auto sigdtor = la.alloc<TestUtils::SignalingDtor>();
sigdtor->setSignal(&destroyed);
- EXPECT_FALSE(destroyed);
+ EXPECT_EQ(0, destroyed);
EXPECT_LE(emptySize, la.usedSize());
la.rewindIfLastAlloc(sigdtor);
- EXPECT_TRUE(destroyed);
+ EXPECT_EQ(1, destroyed);
EXPECT_EQ(emptySize, la.usedSize());
- destroyed = false;
}
// Checking for a double-destroy case
- EXPECT_EQ(destroyed, false);
+ EXPECT_EQ(1, destroyed);
+}
+
+TEST(LinearStdAllocator, simpleAllocate) {
+ LinearAllocator la;
+ LinearStdAllocator<void*> stdAllocator(la);
+
+ std::vector<char, LinearStdAllocator<char> > v(stdAllocator);
+ v.push_back(0);
+ char* initialLocation = &v[0];
+ v.push_back(10);
+ v.push_back(20);
+ v.push_back(30);
+
+ // expect to have allocated (since no space reserved), so [0] will have moved to
+ // slightly further down in the same LinearAllocator page
+ EXPECT_LT(initialLocation, &v[0]);
+ EXPECT_GT(initialLocation + 20, &v[0]);
+
+ // expect to have allocated again inserting 4 more entries
+ char* lastLocation = &v[0];
+ v.push_back(40);
+ v.push_back(50);
+ v.push_back(60);
+ v.push_back(70);
+
+ EXPECT_LT(lastLocation, &v[0]);
+ EXPECT_GT(lastLocation + 20, &v[0]);
+
}
diff --git a/libs/hwui/unit_tests/OpReordererTests.cpp b/libs/hwui/unit_tests/OpReordererTests.cpp
new file mode 100644
index 0000000..f67c24a
--- /dev/null
+++ b/libs/hwui/unit_tests/OpReordererTests.cpp
@@ -0,0 +1,696 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include <BakedOpState.h>
+#include <OpReorderer.h>
+#include <RecordedOp.h>
+#include <RecordingCanvas.h>
+#include <renderthread/CanvasContext.h> // todo: remove
+#include <unit_tests/TestUtils.h>
+
+#include <unordered_map>
+
+namespace android {
+namespace uirenderer {
+
+LayerUpdateQueue sEmptyLayerUpdateQueue;
+
+/**
+ * Virtual class implemented by each test to redirect static operation / state transitions to
+ * virtual methods.
+ *
+ * Virtual dispatch allows for default behaviors to be specified (very common case in below tests),
+ * and allows Renderer vs Dispatching behavior to be merged.
+ *
+ * onXXXOp methods fail by default - tests should override ops they expect
+ * startLayer fails by default - tests should override if expected
+ * startFrame/endFrame do nothing by default - tests should override to intercept
+ */
+class TestRendererBase {
+public:
+ virtual ~TestRendererBase() {}
+ virtual OffscreenBuffer* createLayer(uint32_t, uint32_t) {
+ ADD_FAILURE() << "Layer creation not expected in this test";
+ return nullptr;
+ }
+ virtual void startLayer(OffscreenBuffer*) {
+ ADD_FAILURE() << "Layer repaint not expected in this test";
+ }
+ virtual void endLayer() {
+ ADD_FAILURE() << "Layer updates not expected in this test";
+ }
+ virtual void startFrame(uint32_t width, uint32_t height) {}
+ virtual void endFrame() {}
+
+ // define virtual defaults for direct
+#define BASE_OP_METHOD(Type) \
+ virtual void on##Type(const Type&, const BakedOpState&) { \
+ ADD_FAILURE() << #Type " not expected in this test"; \
+ }
+ MAP_OPS(BASE_OP_METHOD)
+ int getIndex() { return mIndex; }
+
+protected:
+ int mIndex = 0;
+};
+
+/**
+ * Dispatches all static methods to similar formed methods on renderer, which fail by default but
+ * are overriden by subclasses per test.
+ */
+class TestDispatcher {
+public:
+#define DISPATCHER_METHOD(Type) \
+ static void on##Type(TestRendererBase& renderer, const Type& op, const BakedOpState& state) { \
+ renderer.on##Type(op, state); \
+ }
+ MAP_OPS(DISPATCHER_METHOD);
+};
+
+
+class FailRenderer : public TestRendererBase {};
+
+class SimpleTestRenderer : public TestRendererBase {
+public:
+ void startFrame(uint32_t width, uint32_t height) override {
+ EXPECT_EQ(0, mIndex++);
+ EXPECT_EQ(100u, width);
+ EXPECT_EQ(200u, height);
+ }
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(1, mIndex++);
+ }
+ void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(2, mIndex++);
+ }
+ void endFrame() override {
+ EXPECT_EQ(3, mIndex++);
+ }
+};
+TEST(OpReorderer, simple) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
+ SkBitmap bitmap = TestUtils::createSkBitmap(25, 25);
+ canvas.drawRect(0, 0, 100, 200, SkPaint());
+ canvas.drawBitmap(bitmap, 10, 10, nullptr);
+ });
+ OpReorderer reorderer(100, 200, *dl);
+
+ SimpleTestRenderer renderer;
+ reorderer.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end
+}
+
+
+TEST(OpReorderer, simpleRejection) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+ canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op); // intersection should be empty
+ canvas.drawRect(0, 0, 400, 400, SkPaint());
+ canvas.restore();
+ });
+ OpReorderer reorderer(200, 200, *dl);
+
+ FailRenderer renderer;
+ reorderer.replayBakedOps<TestDispatcher>(renderer);
+}
+
+
+static int SIMPLE_BATCHING_LOOPS = 5;
+class SimpleBatchingTestRenderer : public TestRendererBase {
+public:
+ void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
+ EXPECT_TRUE(mIndex++ >= SIMPLE_BATCHING_LOOPS);
+ }
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ EXPECT_TRUE(mIndex++ < SIMPLE_BATCHING_LOOPS);
+ }
+};
+TEST(OpReorderer, simpleBatching) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+ SkBitmap bitmap = TestUtils::createSkBitmap(10, 10);
+
+ // Alternate between drawing rects and bitmaps, with bitmaps overlapping rects.
+ // Rects don't overlap bitmaps, so bitmaps should be brought to front as a group.
+ canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ for (int i = 0; i < SIMPLE_BATCHING_LOOPS; i++) {
+ canvas.translate(0, 10);
+ canvas.drawRect(0, 0, 10, 10, SkPaint());
+ canvas.drawBitmap(bitmap, 5, 0, nullptr);
+ }
+ canvas.restore();
+ });
+
+ OpReorderer reorderer(200, 200, *dl);
+
+ SimpleBatchingTestRenderer renderer;
+ reorderer.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(2 * SIMPLE_BATCHING_LOOPS, renderer.getIndex()); // 2 x loops ops, because no merging (TODO: force no merging)
+}
+
+class RenderNodeTestRenderer : public TestRendererBase {
+public:
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ switch(mIndex++) {
+ case 0:
+ EXPECT_EQ(Rect(0, 0, 200, 200), state.computedState.clippedBounds);
+ EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
+ break;
+ case 1:
+ EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds);
+ EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
+ break;
+ default:
+ ADD_FAILURE();
+ }
+ }
+};
+TEST(OpReorderer, renderNode) {
+ sp<RenderNode> child = TestUtils::createNode<RecordingCanvas>(10, 10, 110, 110, [](RecordingCanvas& canvas) {
+ SkPaint paint;
+ paint.setColor(SK_ColorWHITE);
+ canvas.drawRect(0, 0, 100, 100, paint);
+ });
+
+ RenderNode* childPtr = child.get();
+ sp<RenderNode> parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, [childPtr](RecordingCanvas& canvas) {
+ SkPaint paint;
+ paint.setColor(SK_ColorDKGRAY);
+ canvas.drawRect(0, 0, 200, 200, paint);
+
+ canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.translate(40, 40);
+ canvas.drawRenderNode(childPtr);
+ canvas.restore();
+ });
+
+ TestUtils::syncHierarchyPropertiesAndDisplayList(parent);
+
+ std::vector< sp<RenderNode> > nodes;
+ nodes.push_back(parent.get());
+
+ OpReorderer reorderer(sEmptyLayerUpdateQueue,
+ SkRect::MakeWH(200, 200), 200, 200, nodes);
+
+ RenderNodeTestRenderer renderer;
+ reorderer.replayBakedOps<TestDispatcher>(renderer);
+}
+
+class ClippedTestRenderer : public TestRendererBase {
+public:
+ void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(0, mIndex++);
+ EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clippedBounds);
+ EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clipRect);
+ EXPECT_TRUE(state.computedState.transform.isIdentity());
+ }
+};
+TEST(OpReorderer, clipped) {
+ sp<RenderNode> node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, [](RecordingCanvas& canvas) {
+ SkBitmap bitmap = TestUtils::createSkBitmap(200, 200);
+ canvas.drawBitmap(bitmap, 0, 0, nullptr);
+ });
+ TestUtils::syncHierarchyPropertiesAndDisplayList(node);
+ std::vector< sp<RenderNode> > nodes;
+ nodes.push_back(node.get());
+
+ OpReorderer reorderer(sEmptyLayerUpdateQueue,
+ SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver
+ 200, 200, nodes);
+
+ ClippedTestRenderer renderer;
+ reorderer.replayBakedOps<TestDispatcher>(renderer);
+}
+
+
+class SaveLayerSimpleTestRenderer : public TestRendererBase {
+public:
+ OffscreenBuffer* createLayer(uint32_t width, uint32_t height) override {
+ EXPECT_EQ(0, mIndex++);
+ EXPECT_EQ(180u, width);
+ EXPECT_EQ(180u, height);
+ return nullptr;
+ }
+ void endLayer() override {
+ EXPECT_EQ(2, mIndex++);
+ }
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(1, mIndex++);
+ EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds);
+ EXPECT_EQ(Rect(0, 0, 180, 180), state.computedState.clippedBounds);
+ EXPECT_EQ(Rect(0, 0, 180, 180), state.computedState.clipRect);
+
+ Matrix4 expectedTransform;
+ expectedTransform.loadTranslate(-10, -10, 0);
+ EXPECT_MATRIX_APPROX_EQ(expectedTransform, state.computedState.transform);
+ }
+ void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(3, mIndex++);
+ EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
+ EXPECT_EQ(Rect(0, 0, 200, 200), state.computedState.clipRect);
+ EXPECT_TRUE(state.computedState.transform.isIdentity());
+ }
+};
+TEST(OpReorderer, saveLayerSimple) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+ canvas.saveLayerAlpha(10, 10, 190, 190, 128, SkCanvas::kClipToLayer_SaveFlag);
+ canvas.drawRect(10, 10, 190, 190, SkPaint());
+ canvas.restore();
+ });
+
+ OpReorderer reorderer(200, 200, *dl);
+
+ SaveLayerSimpleTestRenderer renderer;
+ reorderer.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(4, renderer.getIndex());
+}
+
+
+/* saveLayer1 {rect1, saveLayer2 { rect2 } } will play back as:
+ * - createLayer2, rect2 endLayer2
+ * - createLayer1, rect1, drawLayer2, endLayer1
+ * - startFrame, layerOp1, endFrame
+ */
+class SaveLayerNestedTestRenderer : public TestRendererBase {
+public:
+ OffscreenBuffer* createLayer(uint32_t width, uint32_t height) override {
+ const int index = mIndex++;
+ if (index == 0) {
+ EXPECT_EQ(400u, width);
+ EXPECT_EQ(400u, height);
+ return (OffscreenBuffer*) 0x400;
+ } else if (index == 3) {
+ EXPECT_EQ(800u, width);
+ EXPECT_EQ(800u, height);
+ return (OffscreenBuffer*) 0x800;
+ } else { ADD_FAILURE(); }
+ return (OffscreenBuffer*) nullptr;
+ }
+ void endLayer() override {
+ int index = mIndex++;
+ EXPECT_TRUE(index == 2 || index == 6);
+ }
+ void startFrame(uint32_t width, uint32_t height) override {
+ EXPECT_EQ(7, mIndex++);
+ }
+ void endFrame() override {
+ EXPECT_EQ(9, mIndex++);
+ }
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ const int index = mIndex++;
+ if (index == 1) {
+ EXPECT_EQ(Rect(0, 0, 400, 400), op.unmappedBounds); // inner rect
+ } else if (index == 4) {
+ EXPECT_EQ(Rect(0, 0, 800, 800), op.unmappedBounds); // outer rect
+ } else { ADD_FAILURE(); }
+ }
+ void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
+ const int index = mIndex++;
+ if (index == 5) {
+ EXPECT_EQ((OffscreenBuffer*)0x400, *op.layerHandle);
+ EXPECT_EQ(Rect(0, 0, 400, 400), op.unmappedBounds); // inner layer
+ } else if (index == 8) {
+ EXPECT_EQ((OffscreenBuffer*)0x800, *op.layerHandle);
+ EXPECT_EQ(Rect(0, 0, 800, 800), op.unmappedBounds); // outer layer
+ } else { ADD_FAILURE(); }
+ }
+};
+TEST(OpReorderer, saveLayerNested) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(800, 800, [](RecordingCanvas& canvas) {
+ canvas.saveLayerAlpha(0, 0, 800, 800, 128, SkCanvas::kClipToLayer_SaveFlag);
+ {
+ canvas.drawRect(0, 0, 800, 800, SkPaint());
+ canvas.saveLayerAlpha(0, 0, 400, 400, 128, SkCanvas::kClipToLayer_SaveFlag);
+ {
+ canvas.drawRect(0, 0, 400, 400, SkPaint());
+ }
+ canvas.restore();
+ }
+ canvas.restore();
+ });
+
+ OpReorderer reorderer(800, 800, *dl);
+
+ SaveLayerNestedTestRenderer renderer;
+ reorderer.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(10, renderer.getIndex());
+}
+
+TEST(OpReorderer, saveLayerContentRejection) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+ canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op);
+ canvas.saveLayerAlpha(200, 200, 400, 400, 128, SkCanvas::kClipToLayer_SaveFlag);
+
+ // draw within save layer may still be recorded, but shouldn't be drawn
+ canvas.drawRect(200, 200, 400, 400, SkPaint());
+
+ canvas.restore();
+ canvas.restore();
+ });
+ OpReorderer reorderer(200, 200, *dl);
+
+ FailRenderer renderer;
+ // should see no ops, even within the layer, since the layer should be rejected
+ reorderer.replayBakedOps<TestDispatcher>(renderer);
+}
+
+class HwLayerSimpleTestRenderer : public TestRendererBase {
+public:
+ void startLayer(OffscreenBuffer* offscreenBuffer) override {
+ EXPECT_EQ(0, mIndex++);
+ EXPECT_EQ(offscreenBuffer, (OffscreenBuffer*) 0x0124);
+ }
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(1, mIndex++);
+
+ EXPECT_TRUE(state.computedState.transform.isIdentity())
+ << "Transform should be reset within layer";
+
+ EXPECT_EQ(state.computedState.clipRect, Rect(25, 25, 75, 75))
+ << "Damage rect should be used to clip layer content";
+ }
+ void endLayer() override {
+ EXPECT_EQ(2, mIndex++);
+ }
+ void startFrame(uint32_t width, uint32_t height) override {
+ EXPECT_EQ(3, mIndex++);
+ }
+ void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(4, mIndex++);
+ }
+ void endFrame() override {
+ EXPECT_EQ(5, mIndex++);
+ }
+};
+TEST(OpReorderer, hwLayerSimple) {
+ sp<RenderNode> node = TestUtils::createNode<RecordingCanvas>(10, 10, 110, 110, [](RecordingCanvas& canvas) {
+ SkPaint paint;
+ paint.setColor(SK_ColorWHITE);
+ canvas.drawRect(0, 0, 100, 100, paint);
+ });
+ node->mutateStagingProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
+ node->setPropertyFieldsDirty(RenderNode::GENERIC);
+ OffscreenBuffer** bufferHandle = node->getLayerHandle();
+ *bufferHandle = (OffscreenBuffer*) 0x0124;
+
+ TestUtils::syncHierarchyPropertiesAndDisplayList(node);
+
+ std::vector< sp<RenderNode> > nodes;
+ nodes.push_back(node.get());
+
+ // only enqueue partial damage
+ LayerUpdateQueue layerUpdateQueue;
+ layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
+
+ OpReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, nodes);
+
+ HwLayerSimpleTestRenderer renderer;
+ reorderer.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(6, renderer.getIndex());
+
+ // clean up layer pointer, so we can safely destruct RenderNode
+ *bufferHandle = nullptr;
+}
+
+
+/* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as:
+ * - startLayer(child), rect(grey), endLayer
+ * - createLayer, drawLayer(child), endLayer
+ * - startLayer(parent), rect(white), drawLayer(saveLayer), endLayer
+ * - startFrame, drawLayer(parent), endLayerb
+ */
+class HwLayerComplexTestRenderer : public TestRendererBase {
+public:
+ OffscreenBuffer* createLayer(uint32_t width, uint32_t height) {
+ EXPECT_EQ(3, mIndex++); // savelayer first
+ return (OffscreenBuffer*)0xabcd;
+ }
+ void startLayer(OffscreenBuffer* offscreenBuffer) override {
+ int index = mIndex++;
+ if (index == 0) {
+ // starting inner layer
+ EXPECT_EQ((OffscreenBuffer*)0x4567, offscreenBuffer);
+ } else if (index == 6) {
+ // starting outer layer
+ EXPECT_EQ((OffscreenBuffer*)0x0123, offscreenBuffer);
+ } else { ADD_FAILURE(); }
+ }
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ int index = mIndex++;
+ if (index == 1) {
+ // inner layer's rect (white)
+ EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
+ } else if (index == 7) {
+ // outer layer's rect (grey)
+ EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
+ } else { ADD_FAILURE(); }
+ }
+ void endLayer() override {
+ int index = mIndex++;
+ EXPECT_TRUE(index == 2 || index == 5 || index == 9);
+ }
+ void startFrame(uint32_t width, uint32_t height) override {
+ EXPECT_EQ(10, mIndex++);
+ }
+ void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
+ int index = mIndex++;
+ if (index == 4) {
+ EXPECT_EQ((OffscreenBuffer*)0x4567, *op.layerHandle);
+ } else if (index == 8) {
+ EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle);
+ } else if (index == 11) {
+ EXPECT_EQ((OffscreenBuffer*)0x0123, *op.layerHandle);
+ } else { ADD_FAILURE(); }
+ }
+ void endFrame() override {
+ EXPECT_EQ(12, mIndex++);
+ }
+};
+TEST(OpReorderer, hwLayerComplex) {
+ auto child = TestUtils::createNode<RecordingCanvas>(50, 50, 150, 150,
+ [](RecordingCanvas& canvas) {
+ SkPaint paint;
+ paint.setColor(SK_ColorWHITE);
+ canvas.drawRect(0, 0, 100, 100, paint);
+ });
+ child->mutateStagingProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
+ child->setPropertyFieldsDirty(RenderNode::GENERIC);
+ *(child->getLayerHandle()) = (OffscreenBuffer*) 0x4567;
+
+ RenderNode* childPtr = child.get();
+ auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
+ [childPtr](RecordingCanvas& canvas) {
+ SkPaint paint;
+ paint.setColor(SK_ColorDKGRAY);
+ canvas.drawRect(0, 0, 200, 200, paint);
+
+ canvas.saveLayerAlpha(50, 50, 150, 150, 128, SkCanvas::kClipToLayer_SaveFlag);
+ canvas.drawRenderNode(childPtr);
+ canvas.restore();
+ });
+ parent->mutateStagingProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
+ parent->setPropertyFieldsDirty(RenderNode::GENERIC);
+ *(parent->getLayerHandle()) = (OffscreenBuffer*) 0x0123;
+
+ TestUtils::syncHierarchyPropertiesAndDisplayList(parent);
+
+ std::vector< sp<RenderNode> > nodes;
+ nodes.push_back(parent.get());
+
+ LayerUpdateQueue layerUpdateQueue;
+ layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100));
+ layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200));
+
+ OpReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, nodes);
+
+ HwLayerComplexTestRenderer renderer;
+ reorderer.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(13, renderer.getIndex());
+
+ // clean up layer pointers, so we can safely destruct RenderNodes
+ *(child->getLayerHandle()) = nullptr;
+ *(parent->getLayerHandle()) = nullptr;
+}
+
+
+class ZReorderTestRenderer : public TestRendererBase {
+public:
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ int expectedOrder = SkColorGetB(op.paint->getColor()); // extract order from blue channel
+ EXPECT_EQ(expectedOrder, mIndex++) << "An op was drawn out of order";
+ }
+};
+static void drawOrderedRect(RecordingCanvas* canvas, uint8_t expectedDrawOrder) {
+ SkPaint paint;
+ paint.setColor(SkColorSetARGB(256, 0, 0, expectedDrawOrder)); // order put in blue channel
+ canvas->drawRect(0, 0, 100, 100, paint);
+}
+static void drawOrderedNode(RecordingCanvas* canvas, uint8_t expectedDrawOrder, float z) {
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
+ [expectedDrawOrder](RecordingCanvas& canvas) {
+ drawOrderedRect(&canvas, expectedDrawOrder);
+ });
+ node->mutateStagingProperties().setTranslationZ(z);
+ node->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z);
+ canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
+}
+TEST(OpReorderer, zReorder) {
+ auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
+ [](RecordingCanvas& canvas) {
+ drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder
+ drawOrderedRect(&canvas, 1);
+ canvas.insertReorderBarrier(true);
+ drawOrderedNode(&canvas, 6, 2.0f);
+ drawOrderedRect(&canvas, 3);
+ drawOrderedNode(&canvas, 4, 0.0f);
+ drawOrderedRect(&canvas, 5);
+ drawOrderedNode(&canvas, 2, -2.0f);
+ drawOrderedNode(&canvas, 7, 2.0f);
+ canvas.insertReorderBarrier(false);
+ drawOrderedRect(&canvas, 8);
+ drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
+ });
+ TestUtils::syncHierarchyPropertiesAndDisplayList(parent);
+
+ std::vector< sp<RenderNode> > nodes;
+ nodes.push_back(parent.get());
+ OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100, nodes);
+
+ ZReorderTestRenderer renderer;
+ reorderer.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(10, renderer.getIndex());
+};
+
+
+class PropertyTestRenderer : public TestRendererBase {
+public:
+ PropertyTestRenderer(std::function<void(const RectOp&, const BakedOpState&)> callback)
+ : mCallback(callback) {}
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(mIndex++, 0);
+ mCallback(op, state);
+ }
+ std::function<void(const RectOp&, const BakedOpState&)> mCallback;
+};
+
+static void testProperty(
+ std::function<int(RenderProperties&)> propSetupCallback,
+ std::function<void(const RectOp&, const BakedOpState&)> opValidateCallback) {
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, [](RecordingCanvas& canvas) {
+ SkPaint paint;
+ paint.setColor(SK_ColorWHITE);
+ canvas.drawRect(0, 0, 100, 100, paint);
+ });
+ node->setPropertyFieldsDirty(propSetupCallback(node->mutateStagingProperties()));
+ TestUtils::syncHierarchyPropertiesAndDisplayList(node);
+
+ std::vector< sp<RenderNode> > nodes;
+ nodes.push_back(node.get());
+
+ OpReorderer reorderer(sEmptyLayerUpdateQueue,
+ SkRect::MakeWH(100, 100), 200, 200, nodes);
+
+ PropertyTestRenderer renderer(opValidateCallback);
+ reorderer.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op";
+}
+
+TEST(OpReorderer, renderPropOverlappingRenderingAlpha) {
+ testProperty([](RenderProperties& properties) {
+ properties.setAlpha(0.5f);
+ properties.setHasOverlappingRendering(false);
+ return RenderNode::ALPHA | RenderNode::GENERIC;
+ }, [](const RectOp& op, const BakedOpState& state) {
+ EXPECT_EQ(0.5f, state.alpha) << "Alpha should be applied directly to op";
+ });
+}
+
+TEST(OpReorderer, renderPropClipping) {
+ testProperty([](RenderProperties& properties) {
+ properties.setClipToBounds(true);
+ properties.setClipBounds(Rect(10, 20, 300, 400));
+ return RenderNode::GENERIC;
+ }, [](const RectOp& op, const BakedOpState& state) {
+ EXPECT_EQ(Rect(10, 20, 100, 100), state.computedState.clippedBounds)
+ << "Clip rect should be intersection of node bounds and clip bounds";
+ });
+}
+
+TEST(OpReorderer, renderPropRevealClip) {
+ testProperty([](RenderProperties& properties) {
+ properties.mutableRevealClip().set(true, 50, 50, 25);
+ return RenderNode::GENERIC;
+ }, [](const RectOp& op, const BakedOpState& state) {
+ ASSERT_NE(nullptr, state.roundRectClipState);
+ EXPECT_TRUE(state.roundRectClipState->highPriority);
+ EXPECT_EQ(25, state.roundRectClipState->radius);
+ EXPECT_EQ(Rect(50, 50, 50, 50), state.roundRectClipState->innerRect);
+ });
+}
+
+TEST(OpReorderer, renderPropOutlineClip) {
+ testProperty([](RenderProperties& properties) {
+ properties.mutableOutline().setShouldClip(true);
+ properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f);
+ return RenderNode::GENERIC;
+ }, [](const RectOp& op, const BakedOpState& state) {
+ ASSERT_NE(nullptr, state.roundRectClipState);
+ EXPECT_FALSE(state.roundRectClipState->highPriority);
+ EXPECT_EQ(5, state.roundRectClipState->radius);
+ EXPECT_EQ(Rect(15, 25, 25, 35), state.roundRectClipState->innerRect);
+ });
+}
+
+TEST(OpReorderer, renderPropTransform) {
+ testProperty([](RenderProperties& properties) {
+ properties.setLeftTopRightBottom(10, 10, 110, 110);
+
+ SkMatrix staticMatrix = SkMatrix::MakeScale(1.2f, 1.2f);
+ properties.setStaticMatrix(&staticMatrix);
+
+ // ignored, since static overrides animation
+ SkMatrix animationMatrix = SkMatrix::MakeTrans(15, 15);
+ properties.setAnimationMatrix(&animationMatrix);
+
+ properties.setTranslationX(10);
+ properties.setTranslationY(20);
+ properties.setScaleX(0.5f);
+ properties.setScaleY(0.7f);
+ return RenderNode::GENERIC
+ | RenderNode::TRANSLATION_X | RenderNode::TRANSLATION_Y
+ | RenderNode::SCALE_X | RenderNode::SCALE_Y;
+ }, [](const RectOp& op, const BakedOpState& state) {
+ Matrix4 matrix;
+ matrix.loadTranslate(10, 10, 0); // left, top
+ matrix.scale(1.2f, 1.2f, 1); // static matrix
+ // ignore animation matrix, since static overrides it
+
+ // translation xy
+ matrix.translate(10, 20);
+
+ // scale xy (from default pivot - center)
+ matrix.translate(50, 50);
+ matrix.scale(0.5f, 0.7f, 1);
+ matrix.translate(-50, -50);
+ EXPECT_MATRIX_APPROX_EQ(matrix, state.computedState.transform)
+ << "Op draw matrix must match expected combination of transformation properties";
+ });
+}
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/unit_tests/PathParserTests.cpp b/libs/hwui/unit_tests/PathParserTests.cpp
new file mode 100644
index 0000000..ef263a1
--- /dev/null
+++ b/libs/hwui/unit_tests/PathParserTests.cpp
@@ -0,0 +1,209 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include "PathParser.h"
+#include "VectorDrawablePath.h"
+
+#include <functional>
+
+namespace android {
+namespace uirenderer {
+
+struct TestData {
+ const char* pathString;
+ const PathData pathData;
+ const std::function<void(SkPath*)> skPathLamda;
+};
+
+const static std::vector<TestData> testDataSet = {
+ // TestData with scientific notation -2e3 etc.
+ {
+ // Path
+ "M2.000000,22.000000l20.000000,0.000000 1e0-2e3z",
+ {
+ // Verbs
+ {'M', 'l', 'z'},
+ // Verb sizes
+ {2, 4, 0},
+ // Points
+ {2, 22, 20, 0, 1, -2000},
+ },
+ [](SkPath* outPath) {
+ outPath->moveTo(2, 22);
+ outPath->rLineTo(20, 0);
+ outPath->rLineTo(1, -2000);
+ outPath->close();
+ outPath->moveTo(2, 22);
+ }
+ },
+
+ // Comprehensive data, containing all the verbs possible.
+ {
+ // Path
+ "M 1 1 m 2 2, l 3 3 L 3 3 H 4 h4 V5 v5, Q6 6 6 6 q 6 6 6 6t 7 7 T 7 7 C 8 8 8 8 8 8 c 8 8 8 8 8 8 S 9 9 9 9 s 9 9 9 9 A 10 10 0 1 1 10 10 a 10 10 0 1 1 10 10",
+ {
+ // Verbs
+ {'M', 'm', 'l', 'L', 'H', 'h', 'V', 'v', 'Q', 'q', 't', 'T', 'C', 'c', 'S', 's', 'A', 'a'},
+ // VerbSizes
+ {2, 2, 2, 2, 1, 1, 1, 1, 4, 4, 2, 2, 6, 6, 4, 4, 7, 7},
+ // Points
+ {1.0, 1.0, 2.0, 2.0, 3.0, 3.0, 3.0, 3.0, 4.0, 4.0, 5.0, 5.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 7.0, 7.0, 7.0, 7.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 9.0, 9.0, 9.0, 9.0, 9.0, 9.0, 9.0, 9.0, 10.0, 10.0, 0.0, 1.0, 1.0, 10.0, 10.0, 10.0, 10.0, 0.0, 1.0, 1.0, 10.0, 10.0, }
+ },
+ [](SkPath* outPath) {
+ outPath->moveTo(1.0, 1.0);
+ outPath->rMoveTo(2.0, 2.0);
+ outPath->rLineTo(3.0, 3.0);
+ outPath->lineTo(3.0, 3.0);
+ outPath->lineTo(4.0, 3.0);
+ outPath->rLineTo(4.0, 0);
+ outPath->lineTo(8.0, 5.0);
+ outPath->rLineTo(0, 5.0);
+ outPath->quadTo(6.0, 6.0, 6.0, 6.0);
+ outPath->rQuadTo(6.0, 6.0, 6.0, 6.0);
+ outPath->rQuadTo(0.0, 0.0, 7.0, 7.0);
+ outPath->quadTo(26.0, 26.0, 7.0, 7.0);
+ outPath->cubicTo(8.0, 8.0, 8.0, 8.0, 8.0, 8.0);
+ outPath->rCubicTo(8.0, 8.0, 8.0, 8.0, 8.0, 8.0);
+ outPath->cubicTo(16.0, 16.0, 9.0, 9.0, 9.0, 9.0);
+ outPath->rCubicTo(0.0, 0.0, 9.0, 9.0, 9.0, 9.0);
+ outPath->cubicTo(18.447775037328352, 20.404243860300607, 17.998389141249767, 22.8911717921705, 16.737515350332117, 24.986664170401575);
+ outPath->cubicTo(15.476641559414468, 27.08215654863265, 13.489843598291483, 28.644011882390082, 11.155893964798905, 29.37447073281729);
+ outPath->cubicTo(8.821944331306327, 30.1049295832445, 6.299226382436471, 29.954422532383525, 4.0686829203897235, 28.951642951534332);
+ outPath->cubicTo(1.838139458342976, 27.94886337068514, 0.05113662931485696, 26.161860541657013, -0.9516429515343354, 23.931317079610267);
+ outPath->cubicTo(-1.9544225323835278, 21.70077361756352, -2.1049295832444987, 19.178055668693663, -1.37447073281729, 16.844106035201087);
+ outPath->cubicTo(-0.6440118823900814, 14.51015640170851, 0.9178434513673546, 12.523358440585524, 3.0133358295984305, 11.262484649667876);
+ outPath->cubicTo(5.108828207829506, 10.001610858750228, 7.5957561396993984, 9.552224962671648, 10.000000000000005, 10.0);
+ outPath->cubicTo(10.0, 7.348852265086975, 11.054287646850167, 4.803576729418881, 12.928932188134523, 2.9289321881345254);
+ outPath->cubicTo(14.803576729418879, 1.0542876468501696, 17.348852265086972, 4.870079381441987E-16, 19.999999999999996, 0.0);
+ outPath->cubicTo(22.65114773491302, -4.870079381441987E-16, 25.19642327058112, 1.0542876468501678, 27.071067811865476, 2.9289321881345227);
+ outPath->cubicTo(28.94571235314983, 4.803576729418878, 30.0, 7.348852265086974, 30.0, 9.999999999999998);
+ outPath->cubicTo(30.0, 12.651147734913023, 28.94571235314983, 15.19642327058112, 27.071067811865476, 17.071067811865476);
+ outPath->cubicTo(25.19642327058112, 18.94571235314983, 22.651147734913028, 20.0, 20.000000000000004, 20.0);
+ }
+ },
+
+ // Random long data
+ {
+ // Path
+ "M5.3,13.2c-0.1,0.0 -0.3,0.0 -0.4,-0.1c-0.3,-0.2 -0.4,-0.7 -0.2,-1.0c1.3,-1.9 2.9,-3.4 4.9,-4.5c4.1,-2.2 9.3,-2.2 13.4,0.0c1.9,1.1 3.6,2.5 4.9,4.4c0.2,0.3 0.1,0.8 -0.2,1.0c-0.3,0.2 -0.8,0.1 -1.0,-0.2c-1.2,-1.7 -2.6,-3.0 -4.3,-4.0c-3.7,-2.0 -8.3,-2.0 -12.0,0.0c-1.7,0.9 -3.2,2.3 -4.3,4.0C5.7,13.1 5.5,13.2 5.3,13.2z",
+ {
+ // Verbs
+ {'M', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'C', 'z'},
+ // Verb sizes
+ {2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0},
+ // Points
+ {5.3, 13.2, -0.1, 0, -0.3, 0, -0.4, -0.1, -0.3, -0.2, -0.4, -0.7, -0.2, -1, 1.3, -1.9, 2.9, -3.4, 4.9, -4.5, 4.1, -2.2, 9.3, -2.2, 13.4, 0, 1.9, 1.1, 3.6, 2.5, 4.9, 4.4, 0.2, 0.3, 0.1, 0.8, -0.2, 1, -0.3, 0.2, -0.8, 0.1, -1, -0.2, -1.2, -1.7, -2.6, -3, -4.3, -4, -3.7, -2, -8.3, -2, -12, 0, -1.7, 0.9, -3.2, 2.3, -4.3, 4, 5.7, 13.1, 5.5, 13.2, 5.3, 13.2},
+ },
+ [](SkPath* outPath) {
+ outPath->moveTo(5.3, 13.2);
+ outPath->rCubicTo(-0.1, 0.0, -0.3, 0.0, -0.4, -0.1);
+ outPath->rCubicTo(-0.3, -0.2, -0.4, -0.7, -0.2, -1.0);
+ outPath->rCubicTo(1.3, -1.9, 2.9, -3.4, 4.9, -4.5);
+ outPath->rCubicTo(4.1, -2.2, 9.3, -2.2, 13.4, 0.0);
+ outPath->rCubicTo(1.9, 1.1, 3.6, 2.5, 4.9, 4.4);
+ outPath->rCubicTo(0.2, 0.3, 0.1, 0.8, -0.2, 1.0);
+ outPath->rCubicTo(-0.3, 0.2, -0.8, 0.1, -1.0, -0.2);
+ outPath->rCubicTo(-1.2, -1.7, -2.6, -3.0, -4.3, -4.0);
+ outPath->rCubicTo(-3.7, -2.0, -8.3, -2.0, -12.0, 0.0);
+ outPath->rCubicTo(-1.7, 0.9, -3.2, 2.3, -4.3, 4.0);
+ outPath->cubicTo(5.7, 13.1, 5.5, 13.2, 5.3, 13.2);
+ outPath->close();
+ outPath->moveTo(5.3, 13.2);
+ }
+ },
+
+ // Extreme case with numbers and decimal points crunched together
+ {
+ // Path
+ "l0.0.0.5.0.0.5-0.5.0.0-.5z",
+ {
+ // Verbs
+ {'l', 'z'},
+ // Verb sizes
+ {10, 0},
+ // Points
+ {0, 0, 0.5, 0, 0, 0.5, -0.5, 0, 0, -0.5},
+ },
+ [](SkPath* outPath) {
+ outPath->rLineTo(0.0, 0.0);
+ outPath->rLineTo(0.5, 0.0);
+ outPath->rLineTo(0.0, 0.5);
+ outPath->rLineTo(-0.5, 0.0);
+ outPath->rLineTo(0.0, -0.5);
+ outPath->close();
+ outPath->moveTo(0.0, 0.0);
+ }
+ },
+
+ // Empty test data
+ {
+ "",
+ {
+ // Verbs
+ {},
+ {},
+ {},
+ },
+ [](SkPath* outPath) {}
+ }
+
+};
+
+TEST(PathParser, parseStringForData) {
+ for (TestData testData: testDataSet) {
+ // Test generated path data against the given data.
+ PathData pathData;
+ size_t length = strlen(testData.pathString);
+ PathParser::getPathDataFromString(&pathData, testData.pathString, length);
+ PathParser::dump(pathData);
+ EXPECT_EQ(testData.pathData, pathData);
+ }
+
+}
+
+TEST(PathParser, createSkPathFromPathData) {
+ for (TestData testData: testDataSet) {
+ SkPath expectedPath;
+ testData.skPathLamda(&expectedPath);
+ SkPath actualPath;
+ VectorDrawablePath::verbsToPath(&actualPath, &testData.pathData);
+ EXPECT_EQ(expectedPath, actualPath);
+ }
+}
+
+TEST(PathParser, parseStringForSkPath) {
+ for (TestData testData: testDataSet) {
+ size_t length = strlen(testData.pathString);
+ // Check the return value as well as the SkPath generated.
+ SkPath actualPath;
+ bool hasValidData = PathParser::parseStringForSkPath(&actualPath, testData.pathString,
+ length);
+ EXPECT_EQ(hasValidData, testData.pathData.verbs.size() > 0);
+ SkPath expectedPath;
+ testData.skPathLamda(&expectedPath);
+ EXPECT_EQ(expectedPath, actualPath);
+ }
+ SkPath path;
+ EXPECT_FALSE(PathParser::parseStringForSkPath(&path, "l", 1));
+ EXPECT_FALSE(PathParser::parseStringForSkPath(&path, "1 1", 3));
+ EXPECT_FALSE(PathParser::parseStringForSkPath(&path, "LMFAO", 5));
+ EXPECT_TRUE(PathParser::parseStringForSkPath(&path, "m1 1", 4));
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/unit_tests/RecordingCanvasTests.cpp b/libs/hwui/unit_tests/RecordingCanvasTests.cpp
new file mode 100644
index 0000000..83b37ab
--- /dev/null
+++ b/libs/hwui/unit_tests/RecordingCanvasTests.cpp
@@ -0,0 +1,249 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include <RecordedOp.h>
+#include <RecordingCanvas.h>
+#include <unit_tests/TestUtils.h>
+
+namespace android {
+namespace uirenderer {
+
+static void playbackOps(const DisplayList& displayList,
+ std::function<void(const RecordedOp&)> opReceiver) {
+ for (auto& chunk : displayList.getChunks()) {
+ for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
+ RecordedOp* op = displayList.getOps()[opIndex];
+ opReceiver(*op);
+ }
+ }
+}
+
+TEST(RecordingCanvas, emptyPlayback) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
+ canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.restore();
+ });
+ playbackOps(*dl, [](const RecordedOp& op) { ADD_FAILURE(); });
+}
+
+TEST(RecordingCanvas, testSimpleRectRecord) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
+ canvas.drawRect(10, 20, 90, 180, SkPaint());
+ });
+
+ int count = 0;
+ playbackOps(*dl, [&count](const RecordedOp& op) {
+ count++;
+ ASSERT_EQ(RecordedOpId::RectOp, op.opId);
+ ASSERT_EQ(Rect(0, 0, 100, 200), op.localClipRect);
+ ASSERT_EQ(Rect(10, 20, 90, 180), op.unmappedBounds);
+ });
+ ASSERT_EQ(1, count);
+}
+
+TEST(RecordingCanvas, backgroundAndImage) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
+ SkBitmap bitmap;
+ bitmap.setInfo(SkImageInfo::MakeUnknown(25, 25));
+ SkPaint paint;
+ paint.setColor(SK_ColorBLUE);
+
+ canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ {
+ // a background!
+ canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.drawRect(0, 0, 100, 200, paint);
+ canvas.restore();
+ }
+ {
+ // an image!
+ canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.translate(25, 25);
+ canvas.scale(2, 2);
+ canvas.drawBitmap(bitmap, 0, 0, nullptr);
+ canvas.restore();
+ }
+ canvas.restore();
+ });
+
+ int count = 0;
+ playbackOps(*dl, [&count](const RecordedOp& op) {
+ if (count == 0) {
+ ASSERT_EQ(RecordedOpId::RectOp, op.opId);
+ ASSERT_NE(nullptr, op.paint);
+ EXPECT_EQ(SK_ColorBLUE, op.paint->getColor());
+ EXPECT_EQ(Rect(0, 0, 100, 200), op.unmappedBounds);
+ EXPECT_EQ(Rect(0, 0, 100, 200), op.localClipRect);
+
+ Matrix4 expectedMatrix;
+ expectedMatrix.loadIdentity();
+ EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
+ } else {
+ ASSERT_EQ(RecordedOpId::BitmapOp, op.opId);
+ EXPECT_EQ(nullptr, op.paint);
+ EXPECT_EQ(Rect(0, 0, 25, 25), op.unmappedBounds);
+ EXPECT_EQ(Rect(0, 0, 100, 200), op.localClipRect);
+
+ Matrix4 expectedMatrix;
+ expectedMatrix.loadTranslate(25, 25, 0);
+ expectedMatrix.scale(2, 2, 1);
+ EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
+ }
+ count++;
+ });
+ ASSERT_EQ(2, count);
+}
+
+TEST(RecordingCanvas, saveLayerSimple) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+ canvas.saveLayerAlpha(10, 20, 190, 180, 128, SkCanvas::kARGB_ClipLayer_SaveFlag);
+ canvas.drawRect(10, 20, 190, 180, SkPaint());
+ canvas.restore();
+ });
+ int count = 0;
+ playbackOps(*dl, [&count](const RecordedOp& op) {
+ Matrix4 expectedMatrix;
+ switch(count++) {
+ case 0:
+ EXPECT_EQ(RecordedOpId::BeginLayerOp, op.opId);
+ EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds);
+ EXPECT_EQ(Rect(0, 0, 200, 200), op.localClipRect);
+ EXPECT_TRUE(op.localMatrix.isIdentity());
+ break;
+ case 1:
+ EXPECT_EQ(RecordedOpId::RectOp, op.opId);
+ EXPECT_EQ(Rect(0, 0, 180, 160), op.localClipRect);
+ EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds);
+ expectedMatrix.loadTranslate(-10, -20, 0);
+ EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
+ break;
+ case 2:
+ EXPECT_EQ(RecordedOpId::EndLayerOp, op.opId);
+ // Don't bother asserting recording state data - it's not used
+ break;
+ default:
+ ADD_FAILURE();
+ }
+ });
+ EXPECT_EQ(3, count);
+}
+
+TEST(RecordingCanvas, saveLayerViewportCrop) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+ // shouldn't matter, since saveLayer will clip to its bounds
+ canvas.clipRect(-1000, -1000, 1000, 1000, SkRegion::kReplace_Op);
+
+ canvas.saveLayerAlpha(100, 100, 300, 300, 128, SkCanvas::kARGB_ClipLayer_SaveFlag);
+ canvas.drawRect(0, 0, 400, 400, SkPaint());
+ canvas.restore();
+ });
+ int count = 0;
+ playbackOps(*dl, [&count](const RecordedOp& op) {
+ if (count++ == 1) {
+ Matrix4 expectedMatrix;
+ EXPECT_EQ(RecordedOpId::RectOp, op.opId);
+ EXPECT_EQ(Rect(0, 0, 100, 100), op.localClipRect) << "Recorded clip rect should be"
+ " intersection of viewport and saveLayer bounds, in layer space";
+ EXPECT_EQ(Rect(0, 0, 400, 400), op.unmappedBounds);
+ expectedMatrix.loadTranslate(-100, -100, 0);
+ EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
+ }
+ });
+ EXPECT_EQ(3, count);
+}
+
+TEST(RecordingCanvas, saveLayerRotateUnclipped) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+ canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.translate(100, 100);
+ canvas.rotate(45);
+ canvas.translate(-50, -50);
+
+ canvas.saveLayerAlpha(0, 0, 100, 100, 128, SkCanvas::kARGB_ClipLayer_SaveFlag);
+ canvas.drawRect(0, 0, 100, 100, SkPaint());
+ canvas.restore();
+
+ canvas.restore();
+ });
+ int count = 0;
+ playbackOps(*dl, [&count](const RecordedOp& op) {
+ if (count++ == 1) {
+ EXPECT_EQ(RecordedOpId::RectOp, op.opId);
+ EXPECT_EQ(Rect(0, 0, 100, 100), op.localClipRect);
+ EXPECT_EQ(Rect(0, 0, 100, 100), op.unmappedBounds);
+ EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.localMatrix)
+ << "Recorded op shouldn't see any canvas transform before the saveLayer";
+ }
+ });
+ EXPECT_EQ(3, count);
+}
+
+TEST(RecordingCanvas, saveLayerRotateClipped) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+ canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.translate(100, 100);
+ canvas.rotate(45);
+ canvas.translate(-200, -200);
+
+ // area of saveLayer will be clipped to parent viewport, so we ask for 400x400...
+ canvas.saveLayerAlpha(0, 0, 400, 400, 128, SkCanvas::kARGB_ClipLayer_SaveFlag);
+ canvas.drawRect(0, 0, 400, 400, SkPaint());
+ canvas.restore();
+
+ canvas.restore();
+ });
+ int count = 0;
+ playbackOps(*dl, [&count](const RecordedOp& op) {
+ if (count++ == 1) {
+ Matrix4 expectedMatrix;
+ EXPECT_EQ(RecordedOpId::RectOp, op.opId);
+
+ // ...and get about 58.6, 58.6, 341.4 341.4, because the bounds are clipped by
+ // the parent 200x200 viewport, but prior to rotation
+ EXPECT_RECT_APPROX_EQ(Rect(58.57864, 58.57864, 341.42136, 341.42136), op.localClipRect);
+ EXPECT_EQ(Rect(0, 0, 400, 400), op.unmappedBounds);
+ expectedMatrix.loadIdentity();
+ EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
+ }
+ });
+ EXPECT_EQ(3, count);
+}
+
+TEST(RecordingCanvas, testReorderBarrier) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+ canvas.drawRect(0, 0, 400, 400, SkPaint());
+ canvas.insertReorderBarrier(true);
+ canvas.insertReorderBarrier(false);
+ canvas.insertReorderBarrier(false);
+ canvas.insertReorderBarrier(true);
+ canvas.drawRect(0, 0, 400, 400, SkPaint());
+ canvas.insertReorderBarrier(false);
+ });
+
+ auto chunks = dl->getChunks();
+ EXPECT_EQ(0u, chunks[0].beginOpIndex);
+ EXPECT_EQ(1u, chunks[0].endOpIndex);
+ EXPECT_FALSE(chunks[0].reorderChildren);
+
+ EXPECT_EQ(1u, chunks[1].beginOpIndex);
+ EXPECT_EQ(2u, chunks[1].endOpIndex);
+ EXPECT_TRUE(chunks[1].reorderChildren);
+}
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/unit_tests/StringUtilsTests.cpp b/libs/hwui/unit_tests/StringUtilsTests.cpp
index 5174ae9..6b2e265 100644
--- a/libs/hwui/unit_tests/StringUtilsTests.cpp
+++ b/libs/hwui/unit_tests/StringUtilsTests.cpp
@@ -16,13 +16,13 @@
#include <gtest/gtest.h>
-#include "utils/StringUtils.h"
+#include <utils/StringUtils.h>
-namespace android {
-namespace uirenderer {
+using namespace android;
+using namespace android::uirenderer;
TEST(StringUtils, simpleBuildSet) {
- StringCollection collection("a b c");
+ auto collection = StringUtils::split("a b c");
EXPECT_TRUE(collection.has("a"));
EXPECT_TRUE(collection.has("b"));
@@ -31,11 +31,8 @@
}
TEST(StringUtils, advancedBuildSet) {
- StringCollection collection("GL_ext1 GL_ext2 GL_ext3");
+ auto collection = StringUtils::split("GL_ext1 GL_ext2 GL_ext3");
EXPECT_TRUE(collection.has("GL_ext1"));
EXPECT_FALSE(collection.has("GL_ext")); // string present, but not in list
}
-
-};
-};
diff --git a/libs/hwui/unit_tests/TestUtils.h b/libs/hwui/unit_tests/TestUtils.h
new file mode 100644
index 0000000..28e0fd8
--- /dev/null
+++ b/libs/hwui/unit_tests/TestUtils.h
@@ -0,0 +1,173 @@
+/*
+ * 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 TEST_UTILS_H
+#define TEST_UTILS_H
+
+#include <DeviceInfo.h>
+#include <DisplayList.h>
+#include <Matrix.h>
+#include <Rect.h>
+#include <RenderNode.h>
+#include <renderstate/RenderState.h>
+#include <renderthread/RenderThread.h>
+#include <Snapshot.h>
+
+#if HWUI_NEW_OPS
+#include <RecordedOp.h>
+#else
+#include <DisplayListOp.h>
+#endif
+
+#include <memory>
+
+namespace android {
+namespace uirenderer {
+
+#define EXPECT_MATRIX_APPROX_EQ(a, b) \
+ EXPECT_TRUE(TestUtils::matricesAreApproxEqual(a, b))
+
+#define EXPECT_RECT_APPROX_EQ(a, b) \
+ EXPECT_TRUE(MathUtils::areEqual(a.left, b.left) \
+ && MathUtils::areEqual(a.top, b.top) \
+ && MathUtils::areEqual(a.right, b.right) \
+ && MathUtils::areEqual(a.bottom, b.bottom));
+
+class TestUtils {
+public:
+ class SignalingDtor {
+ public:
+ SignalingDtor()
+ : mSignal(nullptr) {}
+ SignalingDtor(int* signal)
+ : mSignal(signal) {}
+ void setSignal(int* signal) {
+ mSignal = signal;
+ }
+ ~SignalingDtor() {
+ if (mSignal) {
+ (*mSignal)++;
+ }
+ }
+ private:
+ int* mSignal;
+ };
+
+ static bool matricesAreApproxEqual(const Matrix4& a, const Matrix4& b) {
+ for (int i = 0; i < 16; i++) {
+ if (!MathUtils::areEqual(a[i], b[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ static std::unique_ptr<Snapshot> makeSnapshot(const Matrix4& transform, const Rect& clip) {
+ std::unique_ptr<Snapshot> snapshot(new Snapshot());
+ snapshot->clip(clip.left, clip.top, clip.right, clip.bottom, SkRegion::kReplace_Op);
+ *(snapshot->transform) = transform;
+ return snapshot;
+ }
+
+ static SkBitmap createSkBitmap(int width, int height) {
+ SkBitmap bitmap;
+ SkImageInfo info = SkImageInfo::MakeUnknown(width, height);
+ bitmap.setInfo(info);
+ bitmap.allocPixels(info);
+ return bitmap;
+ }
+
+ template<class CanvasType>
+ static std::unique_ptr<DisplayList> createDisplayList(int width, int height,
+ std::function<void(CanvasType& canvas)> canvasCallback) {
+ CanvasType canvas(width, height);
+ canvasCallback(canvas);
+ return std::unique_ptr<DisplayList>(canvas.finishRecording());
+ }
+
+ static sp<RenderNode> createNode(int left, int top, int right, int bottom, bool onLayer = false) {
+ // if RenderNodes are being sync'd/used, device info will be needed, since
+ // DeviceInfo::maxTextureSize() affects layer property
+ DeviceInfo::initialize();
+
+ sp<RenderNode> node = new RenderNode();
+ node->mutateStagingProperties().setLeftTopRightBottom(left, top, right, bottom);
+ node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ if (onLayer) {
+ node->mutateStagingProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
+ node->setPropertyFieldsDirty(RenderNode::GENERIC);
+ }
+ return node;
+ }
+
+ template<class CanvasType>
+ static sp<RenderNode> createNode(int left, int top, int right, int bottom,
+ std::function<void(CanvasType& canvas)> canvasCallback, bool onLayer = false) {
+ sp<RenderNode> node = createNode(left, top, right, bottom, onLayer);
+
+ auto&& props = node->stagingProperties(); // staging, since not sync'd yet
+ CanvasType canvas(props.getWidth(), props.getHeight());
+ canvasCallback(canvas);
+ node->setStagingDisplayList(canvas.finishRecording());
+ return node;
+ }
+
+ static void syncHierarchyPropertiesAndDisplayList(sp<RenderNode>& node) {
+ syncHierarchyPropertiesAndDisplayListImpl(node.get());
+ }
+
+ typedef std::function<void(renderthread::RenderThread& thread)> RtCallback;
+
+ class TestTask : public renderthread::RenderTask {
+ public:
+ TestTask(RtCallback rtCallback)
+ : rtCallback(rtCallback) {}
+ virtual ~TestTask() {}
+ virtual void run() override {
+ // RenderState only valid once RenderThread is running, so queried here
+ RenderState& renderState = renderthread::RenderThread::getInstance().renderState();
+
+ renderState.onGLContextCreated();
+ rtCallback(renderthread::RenderThread::getInstance());
+ renderState.onGLContextDestroyed();
+ };
+ RtCallback rtCallback;
+ };
+
+ /**
+ * NOTE: requires surfaceflinger to run, otherwise this method will wait indefinitely.
+ */
+ static void runOnRenderThread(RtCallback rtCallback) {
+ TestTask task(rtCallback);
+ renderthread::RenderThread::getInstance().queueAndWait(&task);
+ }
+private:
+ static void syncHierarchyPropertiesAndDisplayListImpl(RenderNode* node) {
+ node->syncProperties();
+ node->syncDisplayList();
+ auto displayList = node->getDisplayList();
+ if (displayList) {
+ for (auto&& childOp : displayList->getChildren()) {
+ syncHierarchyPropertiesAndDisplayListImpl(childOp->renderNode);
+ }
+ }
+ }
+
+}; // class TestUtils
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* TEST_UTILS_H */
diff --git a/libs/hwui/utils/FatVector.h b/libs/hwui/utils/FatVector.h
new file mode 100644
index 0000000..93d37c2
--- /dev/null
+++ b/libs/hwui/utils/FatVector.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2015, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ANDROID_FAT_VECTOR_H
+#define ANDROID_FAT_VECTOR_H
+
+#include "utils/Macros.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <type_traits>
+#include <utils/Log.h>
+
+#include <vector>
+
+namespace android {
+namespace uirenderer {
+
+template <typename T, size_t SIZE>
+class InlineStdAllocator {
+public:
+ struct Allocation {
+ PREVENT_COPY_AND_ASSIGN(Allocation);
+ public:
+ Allocation() {};
+ // char array instead of T array, so memory is uninitialized, with no destructors run
+ char array[sizeof(T) * SIZE];
+ bool inUse = false;
+ };
+
+ typedef T value_type; // needed to implement std::allocator
+ typedef T* pointer; // needed to implement std::allocator
+
+ InlineStdAllocator(Allocation& allocation)
+ : mAllocation(allocation) {}
+ InlineStdAllocator(const InlineStdAllocator& other)
+ : mAllocation(other.mAllocation) {}
+ ~InlineStdAllocator() {}
+
+ T* allocate(size_t num, const void* = 0) {
+ if (!mAllocation.inUse && num <= SIZE) {
+ mAllocation.inUse = true;
+ return (T*) mAllocation.array;
+ } else {
+ return (T*) malloc(num * sizeof(T));
+ }
+ }
+
+ void deallocate(pointer p, size_t num) {
+ if (p == (T*)mAllocation.array) {
+ mAllocation.inUse = false;
+ } else {
+ // 'free' instead of delete here - destruction handled separately
+ free(p);
+ }
+ }
+ Allocation& mAllocation;
+};
+
+/**
+ * std::vector with SIZE elements preallocated into an internal buffer.
+ *
+ * Useful for avoiding the cost of malloc in cases where only SIZE or
+ * fewer elements are needed in the common case.
+ */
+template <typename T, size_t SIZE>
+class FatVector : public std::vector<T, InlineStdAllocator<T, SIZE>> {
+public:
+ FatVector() : std::vector<T, InlineStdAllocator<T, SIZE>>(
+ InlineStdAllocator<T, SIZE>(mAllocation)) {
+ this->reserve(SIZE);
+ }
+
+ FatVector(size_t capacity) : FatVector() {
+ this->resize(capacity);
+ }
+
+private:
+ typename InlineStdAllocator<T, SIZE>::Allocation mAllocation;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_FAT_VECTOR_H
diff --git a/libs/hwui/utils/LinearAllocator.cpp b/libs/hwui/utils/LinearAllocator.cpp
index 0abe88b..e6a4c03 100644
--- a/libs/hwui/utils/LinearAllocator.cpp
+++ b/libs/hwui/utils/LinearAllocator.cpp
@@ -52,8 +52,8 @@
#define ALIGN_PTR(p) ((void*)(ALIGN((size_t)p)))
#if LOG_NDEBUG
-#define ADD_ALLOCATION(size)
-#define RM_ALLOCATION(size)
+#define ADD_ALLOCATION()
+#define RM_ALLOCATION()
#else
#include <utils/Thread.h>
#include <utils/Timers.h>
@@ -65,18 +65,18 @@
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
if (now > s_nextLog) {
s_nextLog = now + milliseconds_to_nanoseconds(10);
- ALOGV("Total memory usage: %zu kb", s_totalAllocations / 1024);
+ ALOGV("Total pages allocated: %zu", s_totalAllocations);
}
}
-static void _addAllocation(size_t size) {
+static void _addAllocation(int count) {
android::AutoMutex lock(s_mutex);
- s_totalAllocations += size;
+ s_totalAllocations += count;
_logUsageLocked();
}
-#define ADD_ALLOCATION(size) _addAllocation(size);
-#define RM_ALLOCATION(size) _addAllocation(-size);
+#define ADD_ALLOCATION(size) _addAllocation(1);
+#define RM_ALLOCATION(size) _addAllocation(-1);
#endif
#define min(x,y) (((x) < (y)) ? (x) : (y))
@@ -134,7 +134,7 @@
Page* next = p->next();
p->~Page();
free(p);
- RM_ALLOCATION(mPageSize);
+ RM_ALLOCATION();
p = next;
}
}
@@ -238,7 +238,7 @@
LinearAllocator::Page* LinearAllocator::newPage(size_t pageSize) {
pageSize = ALIGN(pageSize + sizeof(LinearAllocator::Page));
- ADD_ALLOCATION(pageSize);
+ ADD_ALLOCATION();
mTotalAllocated += pageSize;
mPageCount++;
void* buf = malloc(pageSize);
diff --git a/libs/hwui/utils/LinearAllocator.h b/libs/hwui/utils/LinearAllocator.h
index d90dd82..e1c6f6c 100644
--- a/libs/hwui/utils/LinearAllocator.h
+++ b/libs/hwui/utils/LinearAllocator.h
@@ -29,6 +29,8 @@
#include <stddef.h>
#include <type_traits>
+#include <vector>
+
namespace android {
namespace uirenderer {
@@ -134,6 +136,54 @@
size_t mDedicatedPageCount;
};
+template <class T>
+class LinearStdAllocator {
+public:
+ typedef T value_type; // needed to implement std::allocator
+ typedef T* pointer; // needed to implement std::allocator
+
+ LinearStdAllocator(LinearAllocator& allocator)
+ : linearAllocator(allocator) {}
+ LinearStdAllocator(const LinearStdAllocator& other)
+ : linearAllocator(other.linearAllocator) {}
+ ~LinearStdAllocator() {}
+
+ // rebind marks that allocators can be rebound to different types
+ template <class U>
+ struct rebind {
+ typedef LinearStdAllocator<U> other;
+ };
+ // enable allocators to be constructed from other templated types
+ template <class U>
+ LinearStdAllocator(const LinearStdAllocator<U>& other)
+ : linearAllocator(other.linearAllocator) {}
+
+ T* allocate(size_t num, const void* = 0) {
+ return (T*)(linearAllocator.alloc(num * sizeof(T)));
+ }
+
+ void deallocate(pointer p, size_t num) {
+ // attempt to rewind, but no guarantees
+ linearAllocator.rewindIfLastAlloc(p, num * sizeof(T));
+ }
+
+ // public so template copy constructor can access
+ LinearAllocator& linearAllocator;
+};
+
+// return that all specializations of LinearStdAllocator are interchangeable
+template <class T1, class T2>
+bool operator== (const LinearStdAllocator<T1>&, const LinearStdAllocator<T2>&) { return true; }
+template <class T1, class T2>
+bool operator!= (const LinearStdAllocator<T1>&, const LinearStdAllocator<T2>&) { return false; }
+
+template <class T>
+class LsaVector : public std::vector<T, LinearStdAllocator<T>> {
+public:
+ LsaVector(const LinearStdAllocator<T>& allocator)
+ : std::vector<T, LinearStdAllocator<T>>(allocator) {}
+};
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/utils/NinePatchImpl.cpp b/libs/hwui/utils/NinePatchImpl.cpp
index f51f5df..985f3fb 100644
--- a/libs/hwui/utils/NinePatchImpl.cpp
+++ b/libs/hwui/utils/NinePatchImpl.cpp
@@ -82,7 +82,7 @@
}
} else {
SLOW_CASE:
- canvas->drawBitmapRect(bitmap, &src, dst, &paint);
+ canvas->drawBitmapRect(bitmap, SkRect::Make(src), dst, &paint);
}
}
diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h
index d00236e..db53713 100644
--- a/libs/hwui/utils/PaintUtils.h
+++ b/libs/hwui/utils/PaintUtils.h
@@ -20,6 +20,7 @@
#include <SkColorFilter.h>
#include <SkDrawLooper.h>
+#include <SkShader.h>
#include <SkXfermode.h>
namespace android {
diff --git a/libs/hwui/utils/StringUtils.cpp b/libs/hwui/utils/StringUtils.cpp
index a1df0e7..64a5970 100644
--- a/libs/hwui/utils/StringUtils.cpp
+++ b/libs/hwui/utils/StringUtils.cpp
@@ -19,21 +19,19 @@
namespace android {
namespace uirenderer {
-StringCollection::StringCollection(const char* spacedList) {
+unordered_string_set StringUtils::split(const char* spacedList) {
+ unordered_string_set set;
const char* current = spacedList;
const char* head = current;
do {
head = strchr(current, ' ');
std::string s(current, head ? head - current : strlen(current));
if (s.length()) {
- mSet.insert(s);
+ set.insert(std::move(s));
}
current = head + 1;
} while (head);
-}
-
-bool StringCollection::has(const char* s) {
- return mSet.find(std::string(s)) != mSet.end();
+ return set;
}
}; // namespace uirenderer
diff --git a/libs/hwui/utils/StringUtils.h b/libs/hwui/utils/StringUtils.h
index ef2a6d5..055869f 100644
--- a/libs/hwui/utils/StringUtils.h
+++ b/libs/hwui/utils/StringUtils.h
@@ -22,12 +22,16 @@
namespace android {
namespace uirenderer {
-class StringCollection {
+class unordered_string_set : public std::unordered_set<std::string> {
public:
- StringCollection(const char* spacedList);
- bool has(const char* string);
-private:
- std::unordered_set<std::string> mSet;
+ bool has(const char* str) {
+ return find(std::string(str)) != end();
+ }
+};
+
+class StringUtils {
+public:
+ static unordered_string_set split(const char* spacedList);
};
} /* namespace uirenderer */
diff --git a/libs/hwui/utils/TestWindowContext.cpp b/libs/hwui/utils/TestWindowContext.cpp
new file mode 100644
index 0000000..84aae75
--- /dev/null
+++ b/libs/hwui/utils/TestWindowContext.cpp
@@ -0,0 +1,209 @@
+/*
+ * 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.
+ */
+#include "TestWindowContext.h"
+
+#include "AnimationContext.h"
+#include "DisplayListCanvas.h"
+#include "IContextFactory.h"
+#include "RenderNode.h"
+#include "SkTypes.h"
+#include "gui/BufferQueue.h"
+#include "gui/CpuConsumer.h"
+#include "gui/IGraphicBufferConsumer.h"
+#include "gui/IGraphicBufferProducer.h"
+#include "gui/Surface.h"
+#include "renderthread/RenderProxy.h"
+
+
+namespace {
+
+/**
+ * Helper class for setting up android::uirenderer::renderthread::RenderProxy.
+ */
+class ContextFactory : public android::uirenderer::IContextFactory {
+public:
+ android::uirenderer::AnimationContext* createAnimationContext
+ (android::uirenderer::renderthread::TimeLord& clock) override {
+ return new android::uirenderer::AnimationContext(clock);
+ }
+};
+
+} // anonymous namespace
+
+namespace android {
+namespace uirenderer {
+
+/**
+ Android strong pointers (android::sp) can't hold forward-declared classes,
+ so we have to use pointer-to-implementation here if we want to hide the
+ details from our non-framework users.
+*/
+
+class TestWindowContext::TestWindowData {
+
+public:
+
+ TestWindowData(SkISize size) : mSize(size) {
+ android::BufferQueue::createBufferQueue(&mProducer, &mConsumer);
+ mCpuConsumer = new android::CpuConsumer(mConsumer, 1);
+ mCpuConsumer->setName(android::String8("TestWindowContext"));
+ mCpuConsumer->setDefaultBufferSize(mSize.width(), mSize.height());
+ mAndroidSurface = new android::Surface(mProducer);
+ native_window_set_buffers_dimensions(mAndroidSurface.get(),
+ mSize.width(), mSize.height());
+ native_window_set_buffers_format(mAndroidSurface.get(),
+ android::PIXEL_FORMAT_RGBA_8888);
+ native_window_set_usage(mAndroidSurface.get(),
+ GRALLOC_USAGE_SW_READ_OFTEN |
+ GRALLOC_USAGE_SW_WRITE_NEVER |
+ GRALLOC_USAGE_HW_RENDER);
+ mRootNode.reset(new android::uirenderer::RenderNode());
+ mRootNode->incStrong(nullptr);
+ mRootNode->mutateStagingProperties().setLeftTopRightBottom
+ (0, 0, mSize.width(), mSize.height());
+ mRootNode->mutateStagingProperties().setClipToBounds(false);
+ mRootNode->setPropertyFieldsDirty(android::uirenderer::RenderNode::GENERIC);
+ ContextFactory factory;
+ mProxy.reset
+ (new android::uirenderer::renderthread::RenderProxy(false,
+ mRootNode.get(),
+ &factory));
+ mProxy->loadSystemProperties();
+ mProxy->initialize(mAndroidSurface.get());
+ float lightX = mSize.width() / 2.0f;
+ android::uirenderer::Vector3 lightVector { lightX, -200.0f, 800.0f };
+ mProxy->setup(mSize.width(), mSize.height(), 800.0f,
+ 255 * 0.075f, 255 * 0.15f);
+ mProxy->setLightCenter(lightVector);
+ mCanvas.reset(new
+ android::uirenderer::DisplayListCanvas(mSize.width(),
+ mSize.height()));
+ }
+
+ SkCanvas* prepareToDraw() {
+ //mCanvas->reset(mSize.width(), mSize.height());
+ mCanvas->clipRect(0, 0, mSize.width(), mSize.height(),
+ SkRegion::Op::kReplace_Op);
+ return mCanvas->asSkCanvas();
+ }
+
+ void finishDrawing() {
+ mRootNode->setStagingDisplayList(mCanvas->finishRecording());
+ mProxy->syncAndDrawFrame();
+ // Surprisingly, calling mProxy->fence() here appears to make no difference to
+ // the timings we record.
+ }
+
+ void fence() {
+ mProxy->fence();
+ }
+
+ bool capturePixels(SkBitmap* bmp) {
+ SkImageInfo destinationConfig =
+ SkImageInfo::Make(mSize.width(), mSize.height(),
+ kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+ bmp->allocPixels(destinationConfig);
+ sk_memset32((uint32_t*) bmp->getPixels(), SK_ColorRED,
+ mSize.width() * mSize.height());
+
+ android::CpuConsumer::LockedBuffer nativeBuffer;
+ android::status_t retval = mCpuConsumer->lockNextBuffer(&nativeBuffer);
+ if (retval == android::BAD_VALUE) {
+ SkDebugf("write_canvas_png() got no buffer; returning transparent");
+ // No buffer ready to read - commonly triggered by dm sending us
+ // a no-op source, or calling code that doesn't do anything on this
+ // backend.
+ bmp->eraseColor(SK_ColorTRANSPARENT);
+ return false;
+ } else if (retval) {
+ SkDebugf("Failed to lock buffer to read pixels: %d.", retval);
+ return false;
+ }
+
+ // Move the pixels into the destination SkBitmap
+
+ SK_ALWAYSBREAK(nativeBuffer.format == android::PIXEL_FORMAT_RGBA_8888 &&
+ "Native buffer not RGBA!");
+ SkImageInfo nativeConfig =
+ SkImageInfo::Make(nativeBuffer.width, nativeBuffer.height,
+ kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+
+ // Android stride is in pixels, Skia stride is in bytes
+ SkBitmap nativeWrapper;
+ bool success =
+ nativeWrapper.installPixels(nativeConfig, nativeBuffer.data, nativeBuffer.stride * 4);
+ if (!success) {
+ SkDebugf("Failed to wrap HWUI buffer in a SkBitmap");
+ return false;
+ }
+
+ SK_ALWAYSBREAK(bmp->colorType() == kRGBA_8888_SkColorType &&
+ "Destination buffer not RGBA!");
+ success =
+ nativeWrapper.readPixels(destinationConfig, bmp->getPixels(), bmp->rowBytes(), 0, 0);
+ if (!success) {
+ SkDebugf("Failed to extract pixels from HWUI buffer");
+ return false;
+ }
+
+ mCpuConsumer->unlockBuffer(nativeBuffer);
+
+ return true;
+ }
+
+private:
+
+ std::unique_ptr<android::uirenderer::RenderNode> mRootNode;
+ std::unique_ptr<android::uirenderer::renderthread::RenderProxy> mProxy;
+ std::unique_ptr<android::uirenderer::DisplayListCanvas> mCanvas;
+ android::sp<android::IGraphicBufferProducer> mProducer;
+ android::sp<android::IGraphicBufferConsumer> mConsumer;
+ android::sp<android::CpuConsumer> mCpuConsumer;
+ android::sp<android::Surface> mAndroidSurface;
+ SkISize mSize;
+};
+
+
+TestWindowContext::TestWindowContext() :
+ mData (nullptr) { }
+
+void TestWindowContext::initialize(int width, int height) {
+ mData = new TestWindowData(SkISize::Make(width, height));
+}
+
+SkCanvas* TestWindowContext::prepareToDraw() {
+ return mData ? mData->prepareToDraw() : nullptr;
+}
+
+void TestWindowContext::finishDrawing() {
+ if (mData) {
+ mData->finishDrawing();
+ }
+}
+
+void TestWindowContext::fence() {
+ if (mData) {
+ mData->fence();
+ }
+}
+
+bool TestWindowContext::capturePixels(SkBitmap* bmp) {
+ return mData ? mData->capturePixels(bmp) : false;
+}
+
+} // namespace uirenderer
+} // namespace android
+
diff --git a/libs/hwui/utils/TestWindowContext.h b/libs/hwui/utils/TestWindowContext.h
new file mode 100644
index 0000000..445a11b
--- /dev/null
+++ b/libs/hwui/utils/TestWindowContext.h
@@ -0,0 +1,67 @@
+/*
+ * 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 TESTWINDOWCONTEXT_H_
+#define TESTWINDOWCONTEXT_H_
+
+#include <cutils/compiler.h>
+
+class SkBitmap;
+class SkCanvas;
+
+namespace android {
+
+namespace uirenderer {
+
+/**
+ Wraps all libui/libgui classes and types that external tests depend on,
+ exposing only primitive Skia types.
+*/
+
+class ANDROID_API TestWindowContext {
+
+public:
+
+ TestWindowContext();
+
+ /// We need to know the size of the window.
+ void initialize(int width, int height);
+
+ /// Returns a canvas to draw into; NULL if not yet initialize()d.
+ SkCanvas* prepareToDraw();
+
+ /// Flushes all drawing commands to HWUI; no-op if not yet initialize()d.
+ void finishDrawing();
+
+ /// Blocks until HWUI has processed all pending drawing commands;
+ /// no-op if not yet initialize()d.
+ void fence();
+
+ /// Returns false if not yet initialize()d.
+ bool capturePixels(SkBitmap* bmp);
+
+private:
+ /// Hidden implementation.
+ class TestWindowData;
+
+ TestWindowData* mData;
+
+};
+
+} // namespace uirenderer
+} // namespace android
+
+#endif // TESTWINDOWCONTEXT_H_
+
diff --git a/libs/hwui/utils/TimeUtils.h b/libs/hwui/utils/TimeUtils.h
new file mode 100644
index 0000000..8d42d7e
--- /dev/null
+++ b/libs/hwui/utils/TimeUtils.h
@@ -0,0 +1,31 @@
+/*
+ * 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 UTILS_TIMEUTILS_H
+#define UTILS_TIMEUTILS_H
+
+#include <utils/Timers.h>
+
+namespace android {
+namespace uirenderer {
+
+constexpr nsecs_t operator"" _ms (unsigned long long ms) {
+ return milliseconds_to_nanoseconds(ms);
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* UTILS_TIMEUTILS_H */
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 1152737..c9586e4 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -42,15 +42,14 @@
static const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL = 15 * 1000 * 1000000LL; // 15 seconds
static const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_SHORT = 3 * 1000 * 1000000LL; // 3 seconds
-// Time to wait between animation frames.
-static const nsecs_t ANIMATION_FRAME_INTERVAL = 1000000000LL / 60;
-
// Time to spend fading out the spot completely.
static const nsecs_t SPOT_FADE_DURATION = 200 * 1000000LL; // 200 ms
// Time to spend fading out the pointer completely.
static const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms
+// The number of events to be read at once for DisplayEventReceiver.
+static const int EVENT_BUFFER_SIZE = 100;
// --- PointerController ---
@@ -59,6 +58,13 @@
mPolicy(policy), mLooper(looper), mSpriteController(spriteController) {
mHandler = new WeakMessageHandler(this);
+ if (mDisplayEventReceiver.initCheck() == NO_ERROR) {
+ mLooper->addFd(mDisplayEventReceiver.getFd(), Looper::POLL_CALLBACK,
+ Looper::EVENT_INPUT, this, nullptr);
+ } else {
+ ALOGE("Failed to initialize DisplayEventReceiver.");
+ }
+
AutoMutex _l(mLock);
mLocked.animationPending = false;
@@ -78,6 +84,7 @@
mLocked.pointerAlpha = 0.0f; // pointer is initially faded
mLocked.pointerSprite = mSpriteController->createSprite();
mLocked.pointerIconChanged = false;
+ mLocked.requestedPointerShape = mPolicy->getDefaultPointerIconId();
mLocked.buttonState = 0;
@@ -231,6 +238,10 @@
void PointerController::setPresentation(Presentation presentation) {
AutoMutex _l(mLock);
+ if (presentation == PRESENTATION_POINTER && mLocked.additionalMouseResources.empty()) {
+ mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources);
+ }
+
if (mLocked.presentation != presentation) {
mLocked.presentation = presentation;
mLocked.presentationChanged = true;
@@ -391,6 +402,15 @@
updatePointerLocked();
}
+void PointerController::updatePointerShape(int iconId) {
+ AutoMutex _l(mLock);
+ if (mLocked.requestedPointerShape != iconId) {
+ mLocked.requestedPointerShape = iconId;
+ mLocked.presentationChanged = true;
+ updatePointerLocked();
+ }
+}
+
void PointerController::setPointerIcon(const SpriteIcon& icon) {
AutoMutex _l(mLock);
@@ -402,21 +422,49 @@
void PointerController::handleMessage(const Message& message) {
switch (message.what) {
- case MSG_ANIMATE:
- doAnimate();
- break;
case MSG_INACTIVITY_TIMEOUT:
doInactivityTimeout();
break;
}
}
-void PointerController::doAnimate() {
+int PointerController::handleEvent(int /* fd */, int events, void* /* data */) {
+ if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
+ ALOGE("Display event receiver pipe was closed or an error occurred. "
+ "events=0x%x", events);
+ return 0; // remove the callback
+ }
+
+ if (!(events & Looper::EVENT_INPUT)) {
+ ALOGW("Received spurious callback for unhandled poll event. "
+ "events=0x%x", events);
+ return 1; // keep the callback
+ }
+
+ bool gotVsync = false;
+ ssize_t n;
+ nsecs_t timestamp;
+ DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
+ while ((n = mDisplayEventReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
+ for (size_t i = 0; i < static_cast<size_t>(n); ++i) {
+ if (buf[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
+ timestamp = buf[i].header.timestamp;
+ gotVsync = true;
+ }
+ }
+ }
+ if (gotVsync) {
+ doAnimate(timestamp);
+ }
+ return 1; // keep the callback
+}
+
+void PointerController::doAnimate(nsecs_t timestamp) {
AutoMutex _l(mLock);
bool keepAnimating = false;
mLocked.animationPending = false;
- nsecs_t frameDelay = systemTime(SYSTEM_TIME_MONOTONIC) - mLocked.animationTime;
+ nsecs_t frameDelay = timestamp - mLocked.animationTime;
// Animate pointer fade.
if (mLocked.pointerFadeDirection < 0) {
@@ -467,7 +515,7 @@
if (!mLocked.animationPending) {
mLocked.animationPending = true;
mLocked.animationTime = systemTime(SYSTEM_TIME_MONOTONIC);
- mLooper->sendMessageDelayed(ANIMATION_FRAME_INTERVAL, mHandler, Message(MSG_ANIMATE));
+ mDisplayEventReceiver.requestNextVsync();
}
}
@@ -497,8 +545,22 @@
}
if (mLocked.pointerIconChanged || mLocked.presentationChanged) {
- mLocked.pointerSprite->setIcon(mLocked.presentation == PRESENTATION_POINTER
- ? mLocked.pointerIcon : mResources.spotAnchor);
+ if (mLocked.presentation == PRESENTATION_POINTER) {
+ if (mLocked.requestedPointerShape == mPolicy->getDefaultPointerIconId()) {
+ mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
+ } else {
+ std::map<int, SpriteIcon>::const_iterator iter =
+ mLocked.additionalMouseResources.find(mLocked.requestedPointerShape);
+ if (iter != mLocked.additionalMouseResources.end()) {
+ mLocked.pointerSprite->setIcon(iter->second);
+ } else {
+ ALOGW("Can't find the resource for icon id %d", mLocked.requestedPointerShape);
+ mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
+ }
+ }
+ } else {
+ mLocked.pointerSprite->setIcon(mResources.spotAnchor);
+ }
mLocked.pointerIconChanged = false;
mLocked.presentationChanged = false;
}
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index b9e4ce7..6d840db 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -19,6 +19,8 @@
#include "SpriteController.h"
+#include <map>
+
#include <ui/DisplayInfo.h>
#include <input/Input.h>
#include <inputflinger/PointerControllerInterface.h>
@@ -26,6 +28,7 @@
#include <utils/RefBase.h>
#include <utils/Looper.h>
#include <utils/String8.h>
+#include <gui/DisplayEventReceiver.h>
#include <SkBitmap.h>
@@ -40,7 +43,6 @@
SpriteIcon spotAnchor;
};
-
/*
* Pointer controller policy interface.
*
@@ -57,6 +59,8 @@
public:
virtual void loadPointerResources(PointerResources* outResources) = 0;
+ virtual void loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources) = 0;
+ virtual int32_t getDefaultPointerIconId() = 0;
};
@@ -65,7 +69,8 @@
*
* Handles pointer acceleration and animation.
*/
-class PointerController : public PointerControllerInterface, public MessageHandler {
+class PointerController : public PointerControllerInterface, public MessageHandler,
+ public LooperCallback {
protected:
virtual ~PointerController();
@@ -93,6 +98,7 @@
const uint32_t* spotIdToIndex, BitSet32 spotIdBits);
virtual void clearSpots();
+ void updatePointerShape(int iconId);
void setDisplayViewport(int32_t width, int32_t height, int32_t orientation);
void setPointerIcon(const SpriteIcon& icon);
void setInactivityTimeout(InactivityTimeout inactivityTimeout);
@@ -102,7 +108,6 @@
static const size_t MAX_SPOTS = 12;
enum {
- MSG_ANIMATE,
MSG_INACTIVITY_TIMEOUT,
};
@@ -132,6 +137,8 @@
sp<SpriteController> mSpriteController;
sp<WeakMessageHandler> mHandler;
+ DisplayEventReceiver mDisplayEventReceiver;
+
PointerResources mResources;
struct Locked {
@@ -155,6 +162,10 @@
SpriteIcon pointerIcon;
bool pointerIconChanged;
+ std::map<int, SpriteIcon> additionalMouseResources;
+
+ int32_t requestedPointerShape;
+
int32_t buttonState;
Vector<Spot*> spots;
@@ -165,7 +176,8 @@
void setPositionLocked(float x, float y);
void handleMessage(const Message& message);
- void doAnimate();
+ int handleEvent(int fd, int events, void* data);
+ void doAnimate(nsecs_t timestamp);
void doInactivityTimeout();
void startAnimationLocked();
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index bf3387b..4d0d1bd 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -78,32 +78,51 @@
*/
public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
+ /**
+ * Bit mask for mFieldsMask indicating the presence of mAltitude.
+ */
+ private static final byte HAS_ALTITUDE_MASK = 1;
+ /**
+ * Bit mask for mFieldsMask indicating the presence of mSpeed.
+ */
+ private static final byte HAS_SPEED_MASK = 2;
+ /**
+ * Bit mask for mFieldsMask indicating the presence of mBearing.
+ */
+ private static final byte HAS_BEARING_MASK = 4;
+ /**
+ * Bit mask for mFieldsMask indicating the presence of mAccuracy.
+ */
+ private static final byte HAS_ACCURACY_MASK = 8;
+ /**
+ * Bit mask for mFieldsMask indicating location is from a mock provider.
+ */
+ private static final byte HAS_MOCK_PROVIDER_MASK = 16;
+
+ // Cached data to make bearing/distance computations more efficient for the case
+ // where distanceTo and bearingTo are called in sequence. Assume this typically happens
+ // on the same thread for caching purposes.
+ private static ThreadLocal<BearingDistanceCache> sBearingDistanceCache
+ = new ThreadLocal<BearingDistanceCache>() {
+ @Override
+ protected BearingDistanceCache initialValue() {
+ return new BearingDistanceCache();
+ }
+ };
+
private String mProvider;
private long mTime = 0;
private long mElapsedRealtimeNanos = 0;
private double mLatitude = 0.0;
private double mLongitude = 0.0;
- private boolean mHasAltitude = false;
private double mAltitude = 0.0f;
- private boolean mHasSpeed = false;
private float mSpeed = 0.0f;
- private boolean mHasBearing = false;
private float mBearing = 0.0f;
- private boolean mHasAccuracy = false;
private float mAccuracy = 0.0f;
private Bundle mExtras = null;
- private boolean mIsFromMockProvider = false;
- // Cache the inputs and outputs of computeDistanceAndBearing
- // so calls to distanceTo() and bearingTo() can share work
- private double mLat1 = 0.0;
- private double mLon1 = 0.0;
- private double mLat2 = 0.0;
- private double mLon2 = 0.0;
- private float mDistance = 0.0f;
- private float mInitialBearing = 0.0f;
- // Scratchpad
- private final float[] mResults = new float[2];
+ // A bitmask of fields present in this object (see HAS_* constants defined above).
+ private byte mFieldsMask = 0;
/**
* Construct a new Location with a named provider.
@@ -131,18 +150,14 @@
mProvider = l.mProvider;
mTime = l.mTime;
mElapsedRealtimeNanos = l.mElapsedRealtimeNanos;
+ mFieldsMask = l.mFieldsMask;
mLatitude = l.mLatitude;
mLongitude = l.mLongitude;
- mHasAltitude = l.mHasAltitude;
mAltitude = l.mAltitude;
- mHasSpeed = l.mHasSpeed;
mSpeed = l.mSpeed;
- mHasBearing = l.mHasBearing;
mBearing = l.mBearing;
- mHasAccuracy = l.mHasAccuracy;
mAccuracy = l.mAccuracy;
mExtras = (l.mExtras == null) ? null : new Bundle(l.mExtras);
- mIsFromMockProvider = l.mIsFromMockProvider;
}
/**
@@ -152,18 +167,14 @@
mProvider = null;
mTime = 0;
mElapsedRealtimeNanos = 0;
+ mFieldsMask = 0;
mLatitude = 0;
mLongitude = 0;
- mHasAltitude = false;
mAltitude = 0;
- mHasSpeed = false;
mSpeed = 0;
- mHasBearing = false;
mBearing = 0;
- mHasAccuracy = false;
mAccuracy = 0;
mExtras = null;
- mIsFromMockProvider = false;
}
/**
@@ -257,11 +268,13 @@
int deg = Integer.parseInt(degrees);
double min;
double sec = 0.0;
+ boolean secPresent = false;
if (st.hasMoreTokens()) {
min = Integer.parseInt(minutes);
String seconds = st.nextToken();
sec = Double.parseDouble(seconds);
+ secPresent = true;
} else {
min = Double.parseDouble(minutes);
}
@@ -273,11 +286,15 @@
if ((deg < 0.0) || (deg > 179 && !isNegative180)) {
throw new IllegalArgumentException("coordinate=" + coordinate);
}
- if (min < 0 || min > 59) {
+
+ // min must be in [0, 59] if seconds are present, otherwise [0.0, 60.0)
+ if (min < 0 || min >= 60 || (secPresent && (min > 59))) {
throw new IllegalArgumentException("coordinate=" +
coordinate);
}
- if (sec < 0 || sec > 59) {
+
+ // sec must be in [0.0, 60.0)
+ if (sec < 0 || sec >= 60) {
throw new IllegalArgumentException("coordinate=" +
coordinate);
}
@@ -291,7 +308,7 @@
}
private static void computeDistanceAndBearing(double lat1, double lon1,
- double lat2, double lon2, float[] results) {
+ double lat2, double lon2, BearingDistanceCache results) {
// Based on http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf
// using the "Inverse Formula" (section 4)
@@ -376,19 +393,19 @@
}
float distance = (float) (b * A * (sigma - deltaSigma));
- results[0] = distance;
- if (results.length > 1) {
- float initialBearing = (float) Math.atan2(cosU2 * sinLambda,
- cosU1 * sinU2 - sinU1 * cosU2 * cosLambda);
- initialBearing *= 180.0 / Math.PI;
- results[1] = initialBearing;
- if (results.length > 2) {
- float finalBearing = (float) Math.atan2(cosU1 * sinLambda,
- -sinU1 * cosU2 + cosU1 * sinU2 * cosLambda);
- finalBearing *= 180.0 / Math.PI;
- results[2] = finalBearing;
- }
- }
+ results.mDistance = distance;
+ float initialBearing = (float) Math.atan2(cosU2 * sinLambda,
+ cosU1 * sinU2 - sinU1 * cosU2 * cosLambda);
+ initialBearing *= 180.0 / Math.PI;
+ results.mInitialBearing = initialBearing;
+ float finalBearing = (float) Math.atan2(cosU1 * sinLambda,
+ -sinU1 * cosU2 + cosU1 * sinU2 * cosLambda);
+ finalBearing *= 180.0 / Math.PI;
+ results.mFinalBearing = finalBearing;
+ results.mLat1 = lat1;
+ results.mLat2 = lat2;
+ results.mLon1 = lon1;
+ results.mLon2 = lon2;
}
/**
@@ -414,8 +431,16 @@
if (results == null || results.length < 1) {
throw new IllegalArgumentException("results is null or has length < 1");
}
+ BearingDistanceCache cache = sBearingDistanceCache.get();
computeDistanceAndBearing(startLatitude, startLongitude,
- endLatitude, endLongitude, results);
+ endLatitude, endLongitude, cache);
+ results[0] = cache.mDistance;
+ if (results.length > 1) {
+ results[1] = cache.mInitialBearing;
+ if (results.length > 2) {
+ results[2] = cache.mFinalBearing;
+ }
+ }
}
/**
@@ -427,21 +452,14 @@
* @return the approximate distance in meters
*/
public float distanceTo(Location dest) {
+ BearingDistanceCache cache = sBearingDistanceCache.get();
// See if we already have the result
- synchronized (mResults) {
- if (mLatitude != mLat1 || mLongitude != mLon1 ||
- dest.mLatitude != mLat2 || dest.mLongitude != mLon2) {
- computeDistanceAndBearing(mLatitude, mLongitude,
- dest.mLatitude, dest.mLongitude, mResults);
- mLat1 = mLatitude;
- mLon1 = mLongitude;
- mLat2 = dest.mLatitude;
- mLon2 = dest.mLongitude;
- mDistance = mResults[0];
- mInitialBearing = mResults[1];
- }
- return mDistance;
+ if (mLatitude != cache.mLat1 || mLongitude != cache.mLon1 ||
+ dest.mLatitude != cache.mLat2 || dest.mLongitude != cache.mLon2) {
+ computeDistanceAndBearing(mLatitude, mLongitude,
+ dest.mLatitude, dest.mLongitude, cache);
}
+ return cache.mDistance;
}
/**
@@ -455,21 +473,14 @@
* @return the initial bearing in degrees
*/
public float bearingTo(Location dest) {
- synchronized (mResults) {
- // See if we already have the result
- if (mLatitude != mLat1 || mLongitude != mLon1 ||
- dest.mLatitude != mLat2 || dest.mLongitude != mLon2) {
- computeDistanceAndBearing(mLatitude, mLongitude,
- dest.mLatitude, dest.mLongitude, mResults);
- mLat1 = mLatitude;
- mLon1 = mLongitude;
- mLat2 = dest.mLatitude;
- mLon2 = dest.mLongitude;
- mDistance = mResults[0];
- mInitialBearing = mResults[1];
- }
- return mInitialBearing;
+ BearingDistanceCache cache = sBearingDistanceCache.get();
+ // See if we already have the result
+ if (mLatitude != cache.mLat1 || mLongitude != cache.mLon1 ||
+ dest.mLatitude != cache.mLat2 || dest.mLongitude != cache.mLon2) {
+ computeDistanceAndBearing(mLatitude, mLongitude,
+ dest.mLatitude, dest.mLongitude, cache);
}
+ return cache.mInitialBearing;
}
/**
@@ -585,7 +596,7 @@
* True if this location has an altitude.
*/
public boolean hasAltitude() {
- return mHasAltitude;
+ return (mFieldsMask & HAS_ALTITUDE_MASK) != 0;
}
/**
@@ -605,7 +616,7 @@
*/
public void setAltitude(double altitude) {
mAltitude = altitude;
- mHasAltitude = true;
+ mFieldsMask |= HAS_ALTITUDE_MASK;
}
/**
@@ -616,14 +627,14 @@
*/
public void removeAltitude() {
mAltitude = 0.0f;
- mHasAltitude = false;
+ mFieldsMask &= ~HAS_ALTITUDE_MASK;
}
/**
* True if this location has a speed.
*/
public boolean hasSpeed() {
- return mHasSpeed;
+ return (mFieldsMask & HAS_SPEED_MASK) != 0;
}
/**
@@ -642,7 +653,7 @@
*/
public void setSpeed(float speed) {
mSpeed = speed;
- mHasSpeed = true;
+ mFieldsMask |= HAS_SPEED_MASK;
}
/**
@@ -653,14 +664,14 @@
*/
public void removeSpeed() {
mSpeed = 0.0f;
- mHasSpeed = false;
+ mFieldsMask &= ~HAS_SPEED_MASK;
}
/**
* True if this location has a bearing.
*/
public boolean hasBearing() {
- return mHasBearing;
+ return (mFieldsMask & HAS_BEARING_MASK) != 0;
}
/**
@@ -692,7 +703,7 @@
bearing -= 360.0f;
}
mBearing = bearing;
- mHasBearing = true;
+ mFieldsMask |= HAS_BEARING_MASK;
}
/**
@@ -703,7 +714,7 @@
*/
public void removeBearing() {
mBearing = 0.0f;
- mHasBearing = false;
+ mFieldsMask &= ~HAS_BEARING_MASK;
}
/**
@@ -713,7 +724,7 @@
* accuracy.
*/
public boolean hasAccuracy() {
- return mHasAccuracy;
+ return (mFieldsMask & HAS_ACCURACY_MASK) != 0;
}
/**
@@ -751,7 +762,7 @@
*/
public void setAccuracy(float accuracy) {
mAccuracy = accuracy;
- mHasAccuracy = true;
+ mFieldsMask |= HAS_ACCURACY_MASK;
}
/**
@@ -762,7 +773,7 @@
*/
public void removeAccuracy() {
mAccuracy = 0.0f;
- mHasAccuracy = false;
+ mFieldsMask &= ~HAS_ACCURACY_MASK;
}
/**
@@ -780,7 +791,7 @@
@SystemApi
public boolean isComplete() {
if (mProvider == null) return false;
- if (!mHasAccuracy) return false;
+ if (!hasAccuracy()) return false;
if (mTime == 0) return false;
if (mElapsedRealtimeNanos == 0) return false;
return true;
@@ -798,8 +809,8 @@
@SystemApi
public void makeComplete() {
if (mProvider == null) mProvider = "?";
- if (!mHasAccuracy) {
- mHasAccuracy = true;
+ if (!hasAccuracy()) {
+ mFieldsMask |= HAS_ACCURACY_MASK;
mAccuracy = 100.0f;
}
if (mTime == 0) mTime = System.currentTimeMillis();
@@ -838,7 +849,7 @@
s.append("Location[");
s.append(mProvider);
s.append(String.format(" %.6f,%.6f", mLatitude, mLongitude));
- if (mHasAccuracy) s.append(String.format(" acc=%.0f", mAccuracy));
+ if (hasAccuracy()) s.append(String.format(" acc=%.0f", mAccuracy));
else s.append(" acc=???");
if (mTime == 0) {
s.append(" t=?!?");
@@ -849,10 +860,10 @@
s.append(" et=");
TimeUtils.formatDuration(mElapsedRealtimeNanos / 1000000L, s);
}
- if (mHasAltitude) s.append(" alt=").append(mAltitude);
- if (mHasSpeed) s.append(" vel=").append(mSpeed);
- if (mHasBearing) s.append(" bear=").append(mBearing);
- if (mIsFromMockProvider) s.append(" mock");
+ if (hasAltitude()) s.append(" alt=").append(mAltitude);
+ if (hasSpeed()) s.append(" vel=").append(mSpeed);
+ if (hasBearing()) s.append(" bear=").append(mBearing);
+ if (isFromMockProvider()) s.append(" mock");
if (mExtras != null) {
s.append(" {").append(mExtras).append('}');
@@ -873,18 +884,14 @@
Location l = new Location(provider);
l.mTime = in.readLong();
l.mElapsedRealtimeNanos = in.readLong();
+ l.mFieldsMask = in.readByte();
l.mLatitude = in.readDouble();
l.mLongitude = in.readDouble();
- l.mHasAltitude = in.readInt() != 0;
l.mAltitude = in.readDouble();
- l.mHasSpeed = in.readInt() != 0;
l.mSpeed = in.readFloat();
- l.mHasBearing = in.readInt() != 0;
l.mBearing = in.readFloat();
- l.mHasAccuracy = in.readInt() != 0;
l.mAccuracy = in.readFloat();
l.mExtras = in.readBundle();
- l.mIsFromMockProvider = in.readInt() != 0;
return l;
}
@@ -904,18 +911,14 @@
parcel.writeString(mProvider);
parcel.writeLong(mTime);
parcel.writeLong(mElapsedRealtimeNanos);
+ parcel.writeByte(mFieldsMask);
parcel.writeDouble(mLatitude);
parcel.writeDouble(mLongitude);
- parcel.writeInt(mHasAltitude ? 1 : 0);
parcel.writeDouble(mAltitude);
- parcel.writeInt(mHasSpeed ? 1 : 0);
parcel.writeFloat(mSpeed);
- parcel.writeInt(mHasBearing ? 1 : 0);
parcel.writeFloat(mBearing);
- parcel.writeInt(mHasAccuracy ? 1 : 0);
parcel.writeFloat(mAccuracy);
parcel.writeBundle(mExtras);
- parcel.writeInt(mIsFromMockProvider? 1 : 0);
}
/**
@@ -940,7 +943,7 @@
* Attaches an extra {@link Location} to this Location.
*
* @param key the key associated with the Location extra
- * @param location the Location to attach
+ * @param value the Location to attach
* @hide
*/
public void setExtraLocation(String key, Location value) {
@@ -956,7 +959,7 @@
* @return true if this Location came from a mock provider, false otherwise
*/
public boolean isFromMockProvider() {
- return mIsFromMockProvider;
+ return (mFieldsMask & HAS_MOCK_PROVIDER_MASK) != 0;
}
/**
@@ -967,6 +970,24 @@
*/
@SystemApi
public void setIsFromMockProvider(boolean isFromMockProvider) {
- mIsFromMockProvider = isFromMockProvider;
+ if (isFromMockProvider) {
+ mFieldsMask |= HAS_MOCK_PROVIDER_MASK;
+ } else {
+ mFieldsMask &= ~HAS_MOCK_PROVIDER_MASK;
+ }
+ }
+
+ /**
+ * Caches data used to compute distance and bearing (so successive calls to {@link #distanceTo}
+ * and {@link #bearingTo} don't duplicate work.
+ */
+ private static class BearingDistanceCache {
+ private double mLat1 = 0.0;
+ private double mLon1 = 0.0;
+ private double mLat2 = 0.0;
+ private double mLon2 = 0.0;
+ private float mDistance = 0.0f;
+ private float mInitialBearing = 0.0f;
+ private float mFinalBearing = 0.0f;
}
}
diff --git a/location/tests/locationtests/src/android/location/LocationTest.java b/location/tests/locationtests/src/android/location/LocationTest.java
index 847ac7a..dc8d0f7 100644
--- a/location/tests/locationtests/src/android/location/LocationTest.java
+++ b/location/tests/locationtests/src/android/location/LocationTest.java
@@ -16,6 +16,7 @@
package android.location;
+import android.os.Parcel;
import android.test.suitebuilder.annotation.SmallTest;
import junit.framework.TestCase;
@@ -225,6 +226,40 @@
assertEquals(message, loc.getBearing(), 0, 0);
}
+ public void testParcel() {
+ final double expectedLat = 33;
+ final double expectedLon = -122;
+ final float expectedAccuracy = 15;
+ final float expectedSpeed = 5;
+ Location loc = new Location("test");
+ loc.setLatitude(expectedLat);
+ loc.setLongitude(expectedLon);
+ loc.setAccuracy(expectedAccuracy);
+ loc.setSpeed(expectedSpeed);
+
+ // Serialize location object into bytes via parcelable capability
+ Parcel parcel = Parcel.obtain();
+ loc.writeToParcel(parcel, 0);
+ byte[] rawBytes = parcel.marshall();
+ parcel.recycle();
+
+ // Turn the bytes back into a location object
+ parcel = Parcel.obtain();
+ parcel.unmarshall(rawBytes, 0, rawBytes.length);
+ parcel.setDataPosition(0);
+ Location deserialized = Location.CREATOR.createFromParcel(parcel);
+ parcel.recycle();
+
+ assertEquals(expectedLat, deserialized.getLatitude());
+ assertEquals(expectedLon, deserialized.getLongitude());
+ assertEquals(expectedAccuracy, deserialized.getAccuracy());
+ assertTrue(deserialized.hasAccuracy());
+ assertEquals(expectedSpeed, deserialized.getSpeed());
+ assertTrue(deserialized.hasSpeed());
+ assertFalse(deserialized.hasBearing());
+ assertFalse(deserialized.hasAltitude());
+ assertFalse(deserialized.isFromMockProvider());
+ }
}
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index 7f22b8a..45529ef 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -221,8 +221,9 @@
* @return An array of audio encodings (e.g. {@link AudioFormat#ENCODING_PCM_16BIT},
* {@link AudioFormat#ENCODING_PCM_FLOAT}) supported by the audio device.
* <code>ENCODING_PCM_FLOAT</code> indicates the device supports more
- * than 16 bits of integer precision. Specifying <code>ENCODING_PCM_FLOAT</code>
- * with {@link AudioTrack} or {@link AudioRecord} can preserve at least 24 bits of
+ * than 16 bits of integer precision. As there is no AudioFormat constant
+ * specifically defined for 24-bit PCM, the value <code>ENCODING_PCM_FLOAT</code>
+ * indicates that {@link AudioTrack} or {@link AudioRecord} can preserve at least 24 bits of
* integer precision to that device.
*
* @see AudioFormat
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 875e716..50df556 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -3199,8 +3199,10 @@
/**
* Returns the value of the property with the specified key.
* @param key One of the strings corresponding to a property key: either
- * {@link #PROPERTY_OUTPUT_SAMPLE_RATE} or
- * {@link #PROPERTY_OUTPUT_FRAMES_PER_BUFFER}
+ * {@link #PROPERTY_OUTPUT_SAMPLE_RATE},
+ * {@link #PROPERTY_OUTPUT_FRAMES_PER_BUFFER},
+ * {@link #PROPERTY_SUPPORT_MIC_NEAR_ULTRASOUND}, or
+ * {@link #PROPERTY_SUPPORT_SPEAKER_NEAR_ULTRASOUND}.
* @return A string representing the associated value for that property key,
* or null if there is no value for that key.
*/
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 6c224e5..478fd99 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -2179,6 +2179,7 @@
// in media/hardware/CryptoAPI.h !
public static final int CRYPTO_MODE_UNENCRYPTED = 0;
public static final int CRYPTO_MODE_AES_CTR = 1;
+ public static final int CRYPTO_MODE_AES_CBC = 2;
/**
* Metadata describing the structure of a (at least partially) encrypted
@@ -2186,27 +2187,14 @@
* A buffer's data is considered to be partitioned into "subSamples",
* each subSample starts with a (potentially empty) run of plain,
* unencrypted bytes followed by a (also potentially empty) run of
- * encrypted bytes.
- * numBytesOfClearData can be null to indicate that all data is encrypted.
- * This information encapsulates per-sample metadata as outlined in
- * ISO/IEC FDIS 23001-7:2011 "Common encryption in ISO base media file format files".
+ * encrypted bytes. If pattern encryption applies, each of the latter runs
+ * is encrypted only partly, according to a repeating pattern of "encrypt"
+ * and "skip" blocks. numBytesOfClearData can be null to indicate that all
+ * data is encrypted. This information encapsulates per-sample metadata as
+ * outlined in ISO/IEC FDIS 23001-7:2011 "Common encryption in ISO base
+ * media file format files".
*/
public final static class CryptoInfo {
- public void set(
- int newNumSubSamples,
- @NonNull int[] newNumBytesOfClearData,
- @NonNull int[] newNumBytesOfEncryptedData,
- @NonNull byte[] newKey,
- @NonNull byte[] newIV,
- int newMode) {
- numSubSamples = newNumSubSamples;
- numBytesOfClearData = newNumBytesOfClearData;
- numBytesOfEncryptedData = newNumBytesOfEncryptedData;
- key = newKey;
- iv = newIV;
- mode = newMode;
- }
-
/**
* The number of subSamples that make up the buffer's contents.
*/
@@ -2220,7 +2208,7 @@
*/
public int[] numBytesOfEncryptedData;
/**
- * A 16-byte opaque key
+ * A 16-byte key id
*/
public byte[] key;
/**
@@ -2229,10 +2217,84 @@
public byte[] iv;
/**
* The type of encryption that has been applied,
- * see {@link #CRYPTO_MODE_UNENCRYPTED} and {@link #CRYPTO_MODE_AES_CTR}.
+ * see {@link #CRYPTO_MODE_UNENCRYPTED}, {@link #CRYPTO_MODE_AES_CTR}
+ * and {@link #CRYPTO_MODE_AES_CBC}
*/
public int mode;
+ /**
+ * Metadata describing encryption pattern for the protected bytes in a subsample.
+ */
+ public final static class Pattern {
+ /**
+ * Number of blocks to be encrypted in the pattern. If zero, pattern
+ * encryption is inoperative.
+ */
+ private int mEncryptBlocks;
+
+ /**
+ * Number of blocks to be skipped (left clear) in the pattern. If zero,
+ * pattern encryption is inoperative.
+ */
+ private int mSkipBlocks;
+
+ /**
+ * Construct a sample encryption pattern given the number of blocks to
+ * encrypt and skip in the pattern.
+ */
+ public Pattern(int blocksToEncrypt, int blocksToSkip) {
+ set(blocksToEncrypt, blocksToSkip);
+ }
+
+ /**
+ * Set the number of blocks to encrypt and skip in a sample encryption
+ * pattern.
+ */
+ public void set(int blocksToEncrypt, int blocksToSkip) {
+ mEncryptBlocks = blocksToEncrypt;
+ mSkipBlocks = blocksToSkip;
+ }
+
+ /**
+ * Return the number of blocks to skip in a sample encryption pattern.
+ */
+ public int getSkipBlocks() {
+ return mSkipBlocks;
+ }
+
+ /**
+ * Return the number of blocks to encrypt in a sample encryption pattern.
+ */
+ public int getEncryptBlocks() {
+ return mEncryptBlocks;
+ }
+ };
+
+ /**
+ * The pattern applicable to the protected data in each subsample.
+ */
+ private Pattern pattern;
+
+ public void set(
+ int newNumSubSamples,
+ @NonNull int[] newNumBytesOfClearData,
+ @NonNull int[] newNumBytesOfEncryptedData,
+ @NonNull byte[] newKey,
+ @NonNull byte[] newIV,
+ int newMode) {
+ numSubSamples = newNumSubSamples;
+ numBytesOfClearData = newNumBytesOfClearData;
+ numBytesOfEncryptedData = newNumBytesOfEncryptedData;
+ key = newKey;
+ iv = newIV;
+ mode = newMode;
+ pattern = new Pattern(0, 0);
+ }
+
+ public void setPattern(Pattern newPattern) {
+ pattern = newPattern;
+ }
+
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index ba867e1..e0a8026 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -249,7 +249,7 @@
*/
public @NonNull String getRoot() {
if (!isConnected()) {
- throw new IllegalStateException("getSessionToken() called while not connected (state="
+ throw new IllegalStateException("getRoot() called while not connected (state="
+ getStateLabel(mState) + ")");
}
return mRootId;
@@ -776,68 +776,88 @@
*/
private class MediaServiceConnection implements ServiceConnection {
@Override
- public void onServiceConnected(ComponentName name, IBinder binder) {
- if (DBG) {
- Log.d(TAG, "MediaServiceConnection.onServiceConnected name=" + name
- + " binder=" + binder);
- dump();
- }
+ public void onServiceConnected(final ComponentName name, final IBinder binder) {
+ postOrRun(new Runnable() {
+ @Override
+ public void run() {
+ if (DBG) {
+ Log.d(TAG, "MediaServiceConnection.onServiceConnected name=" + name
+ + " binder=" + binder);
+ dump();
+ }
- // Make sure we are still the current connection, and that they haven't called
- // disconnect().
- if (!isCurrent("onServiceConnected")) {
- return;
- }
+ // Make sure we are still the current connection, and that they haven't called
+ // disconnect().
+ if (!isCurrent("onServiceConnected")) {
+ return;
+ }
- // Save their binder
- mServiceBinder = IMediaBrowserService.Stub.asInterface(binder);
+ // Save their binder
+ mServiceBinder = IMediaBrowserService.Stub.asInterface(binder);
- // We make a new mServiceCallbacks each time we connect so that we can drop
- // responses from previous connections.
- mServiceCallbacks = getNewServiceCallbacks();
- mState = CONNECT_STATE_CONNECTING;
+ // We make a new mServiceCallbacks each time we connect so that we can drop
+ // responses from previous connections.
+ mServiceCallbacks = getNewServiceCallbacks();
+ mState = CONNECT_STATE_CONNECTING;
- // Call connect, which is async. When we get a response from that we will
- // say that we're connected.
- try {
- if (DBG) {
- Log.d(TAG, "ServiceCallbacks.onConnect...");
- dump();
+ // Call connect, which is async. When we get a response from that we will
+ // say that we're connected.
+ try {
+ if (DBG) {
+ Log.d(TAG, "ServiceCallbacks.onConnect...");
+ dump();
+ }
+ mServiceBinder.connect(mContext.getPackageName(), mRootHints,
+ mServiceCallbacks);
+ } catch (RemoteException ex) {
+ // Connect failed, which isn't good. But the auto-reconnect on the service
+ // will take over and we will come back. We will also get the
+ // onServiceDisconnected, which has all the cleanup code. So let that do
+ // it.
+ Log.w(TAG, "RemoteException during connect for " + mServiceComponent);
+ if (DBG) {
+ Log.d(TAG, "ServiceCallbacks.onConnect...");
+ dump();
+ }
+ }
}
- mServiceBinder.connect(mContext.getPackageName(), mRootHints, mServiceCallbacks);
- } catch (RemoteException ex) {
- // Connect failed, which isn't good. But the auto-reconnect on the service
- // will take over and we will come back. We will also get the
- // onServiceDisconnected, which has all the cleanup code. So let that do it.
- Log.w(TAG, "RemoteException during connect for " + mServiceComponent);
- if (DBG) {
- Log.d(TAG, "ServiceCallbacks.onConnect...");
- dump();
- }
- }
+ });
}
@Override
- public void onServiceDisconnected(ComponentName name) {
- if (DBG) {
- Log.d(TAG, "MediaServiceConnection.onServiceDisconnected name=" + name
- + " this=" + this + " mServiceConnection=" + mServiceConnection);
- dump();
+ public void onServiceDisconnected(final ComponentName name) {
+ postOrRun(new Runnable() {
+ @Override
+ public void run() {
+ if (DBG) {
+ Log.d(TAG, "MediaServiceConnection.onServiceDisconnected name=" + name
+ + " this=" + this + " mServiceConnection=" + mServiceConnection);
+ dump();
+ }
+
+ // Make sure we are still the current connection, and that they haven't called
+ // disconnect().
+ if (!isCurrent("onServiceDisconnected")) {
+ return;
+ }
+
+ // Clear out what we set in onServiceConnected
+ mServiceBinder = null;
+ mServiceCallbacks = null;
+
+ // And tell the app that it's suspended.
+ mState = CONNECT_STATE_SUSPENDED;
+ mCallback.onConnectionSuspended();
+ }
+ });
+ }
+
+ private void postOrRun(Runnable r) {
+ if (Thread.currentThread() == mHandler.getLooper().getThread()) {
+ r.run();
+ } else {
+ mHandler.post(r);
}
-
- // Make sure we are still the current connection, and that they haven't called
- // disconnect().
- if (!isCurrent("onServiceDisconnected")) {
- return;
- }
-
- // Clear out what we set in onServiceConnected
- mServiceBinder = null;
- mServiceCallbacks = null;
-
- // And tell the app that it's suspended.
- mState = CONNECT_STATE_SUSPENDED;
- mCallback.onConnectionSuspended();
}
/**
diff --git a/media/java/android/media/midi/package.html b/media/java/android/media/midi/package.html
index 45fb579..93dbf3f 100644
--- a/media/java/android/media/midi/package.html
+++ b/media/java/android/media/midi/package.html
@@ -180,7 +180,8 @@
Log.e(TAG, "could not open device " + info);
} else {
...
- }, new Handler(Looper.getMainLooper())
+ }
+ }, new Handler(Looper.getMainLooper())
);
</pre>
diff --git a/media/java/android/mtp/MtpEvent.java b/media/java/android/mtp/MtpEvent.java
index 0fa7d93..4c8a742 100644
--- a/media/java/android/mtp/MtpEvent.java
+++ b/media/java/android/mtp/MtpEvent.java
@@ -18,13 +18,33 @@
/**
* This class encapsulates information about a MTP event.
+ * Event constants are defined by the USB-IF MTP specification.
*/
public class MtpEvent {
- private int mEventCode;
+ public static final int EVENT_UNDEFINED = 0x4000;
+ public static final int EVENT_CANCEL_TRANSACTION = 0x4001;
+ public static final int EVENT_OBJECT_ADDED = 0x4002;
+ public static final int EVENT_OBJECT_REMOVED = 0x4003;
+ public static final int EVENT_STORE_ADDED = 0x4004;
+ public static final int EVENT_STORE_REMOVED = 0x4005;
+ public static final int EVENT_DEVICE_PROP_CHANGED = 0x4006;
+ public static final int EVENT_OBJECT_INFO_CHANGED = 0x4007;
+ public static final int EVENT_DEVICE_INFO_CHANGED = 0x4008;
+ public static final int EVENT_REQUEST_OBJECT_TRANSFER = 0x4009;
+ public static final int EVENT_STORE_FULL = 0x400A;
+ public static final int EVENT_DEVICE_RESET = 0x400B;
+ public static final int EVENT_STORAGE_INFO_CHANGED = 0x400C;
+ public static final int EVENT_CAPTURE_COMPLETE = 0x400D;
+ public static final int EVENT_UNREPORTED_STATUS = 0x400E;
+ public static final int EVENT_OBJECT_PROP_CHANGED = 0xC801;
+ public static final int EVENT_OBJECT_PROP_DESC_CHANGED = 0xC802;
+ public static final int EVENT_OBJECT_REFERENCES_CHANGED = 0xC803;
+
+ private int mEventCode = EVENT_UNDEFINED;
/**
* Returns event code of MTP event.
- *
+ * See the USB-IF MTP specification for the details of event constants.
* @return event code
*/
public int getEventCode() { return mEventCode; }
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index e8f680f..49b579c 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -834,29 +834,36 @@
env->GetMethodID(clazz.get(), "<init>", "(ILjava/lang/String;)V");
CHECK(constructID != NULL);
- jstring msgObj = env->NewStringUTF(msg != NULL ? msg : "Unknown Error");
+ const char *defaultMsg = "Unknown Error";
/* translate OS errors to Java API CryptoException errorCodes (which are positive) */
switch (err) {
case ERROR_DRM_NO_LICENSE:
err = gCryptoErrorCodes.cryptoErrorNoKey;
+ defaultMsg = "Crypto key not available";
break;
case ERROR_DRM_LICENSE_EXPIRED:
err = gCryptoErrorCodes.cryptoErrorKeyExpired;
+ defaultMsg = "License expired";
break;
case ERROR_DRM_RESOURCE_BUSY:
err = gCryptoErrorCodes.cryptoErrorResourceBusy;
+ defaultMsg = "Resource busy or unavailable";
break;
case ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION:
err = gCryptoErrorCodes.cryptoErrorInsufficientOutputProtection;
+ defaultMsg = "Required output protections are not active";
break;
case ERROR_DRM_SESSION_NOT_OPENED:
err = gCryptoErrorCodes.cryptoErrorSessionNotOpened;
+ defaultMsg = "Attempted to use a closed session";
break;
default: /* Other negative DRM error codes go out as is. */
break;
}
+ jstring msgObj = env->NewStringUTF(msg != NULL ? msg : defaultMsg);
+
jthrowable exception =
(jthrowable)env->NewObject(clazz.get(), constructID, err, msgObj);
diff --git a/opengl/java/android/opengl/GLES31.java b/opengl/java/android/opengl/GLES31.java
index 3cbaa60..805930e 100644
--- a/opengl/java/android/opengl/GLES31.java
+++ b/opengl/java/android/opengl/GLES31.java
@@ -24,9 +24,14 @@
public static final int GL_VERTEX_SHADER_BIT = 0x00000001;
public static final int GL_FRAGMENT_SHADER_BIT = 0x00000002;
+ public static final int GL_COMPUTE_SHADER_BIT = 0x00000020;
+ public static final int GL_ALL_SHADER_BITS = -1; // 0xFFFFFFFF
+
+ public static final int GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT = 0x00000001;
+ public static final int GL_ELEMENT_ARRAY_BARRIER_BIT = 0x00000002;
public static final int GL_UNIFORM_BARRIER_BIT = 0x00000004;
public static final int GL_TEXTURE_FETCH_BARRIER_BIT = 0x00000008;
- public static final int GL_COMPUTE_SHADER_BIT = 0x00000020;
+ public static final int GL_SHADER_IMAGE_ACCESS_BARRIER_BIT = 0x00000020;
public static final int GL_COMMAND_BARRIER_BIT = 0x00000040;
public static final int GL_PIXEL_BUFFER_BARRIER_BIT = 0x00000080;
public static final int GL_TEXTURE_UPDATE_BARRIER_BIT = 0x00000100;
@@ -35,7 +40,8 @@
public static final int GL_TRANSFORM_FEEDBACK_BARRIER_BIT = 0x00000800;
public static final int GL_ATOMIC_COUNTER_BARRIER_BIT = 0x00001000;
public static final int GL_SHADER_STORAGE_BARRIER_BIT = 0x00002000;
- public static final int GL_ALL_SHADER_BITS = -1; // 0xFFFFFFFF
+ public static final int GL_ALL_BARRIER_BITS = -1; // 0xFFFFFFFF
+
public static final int GL_TEXTURE_WIDTH = 0x1000;
public static final int GL_TEXTURE_HEIGHT = 0x1001;
diff --git a/opengl/java/android/opengl/GLUtils.java b/opengl/java/android/opengl/GLUtils.java
index 4d890c9..d097335 100644
--- a/opengl/java/android/opengl/GLUtils.java
+++ b/opengl/java/android/opengl/GLUtils.java
@@ -261,12 +261,6 @@
}
}
- /**
- * Set OpenGL Tracing level for this application.
- * @hide
- */
- native public static void setTracingLevel(int level);
-
native private static int native_getInternalFormat(Bitmap bitmap);
native private static int native_getType(Bitmap bitmap);
native private static int native_texImage2D(int target, int level, int internalformat,
diff --git a/packages/BackupRestoreConfirmation/res/values-ne-rNP/strings.xml b/packages/BackupRestoreConfirmation/res/values-ne-rNP/strings.xml
index 473802e..66d340c 100644
--- a/packages/BackupRestoreConfirmation/res/values-ne-rNP/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-ne-rNP/strings.xml
@@ -21,14 +21,14 @@
<string name="backup_confirm_text" msgid="1878021282758896593">"एउटा जोडिएको डेस्कटप कम्प्युटरमा सबै डेटाको एउटा पूर्ण जगेडाको अनुरोध गरिएको छ। के तपाईँ यो हुन दिन चाहनुहुन्छ? \n\nयदि तपाईँले जगेडाको लागि आफैँ अनुरोध गर्नु भएन भने प्रक्रियालाई अगाडि बढ्न अनुमति नदिनुहोस्।"</string>
<string name="allow_backup_button_label" msgid="4217228747769644068">"मेरो डेटा ब्याकअप गर्नुहोस्"</string>
<string name="deny_backup_button_label" msgid="6009119115581097708">"जगेडा नगर्नुहोस्"</string>
- <string name="restore_confirm_text" msgid="7499866728030461776">"एउटा जडित डेस्कटप कम्प्युटरबाट सबै डेटाको पूर्ण पुनःबहाली अनुरोध गरियो। के तपाईं यो हुन अनुमति दिनुहुन्छ?\n\nयदि तपाईं आफैं पुनःबहाली अनुरोध गर्नुहुन्न भने अपरेसनलाई अघि बढाउन अनुमति नदिनुहोस्। यसले उपकरणमा भएको कुनै पनि डेटालाई बदल्ने छ!"</string>
+ <string name="restore_confirm_text" msgid="7499866728030461776">"एउटा जडित डेस्कटप कम्प्युटरबाट सबै डेटाको पूर्ण पुनःबहाली अनुरोध गरियो। के तपाईँ यो हुन अनुमति दिनुहुन्छ?\n\nयदि तपाईँ आफैं पुनःबहाली अनुरोध गर्नुहुन्न भने अपरेसनलाई अघि बढाउन अनुमति नदिनुहोस्। यसले उपकरणमा भएको कुनै पनि डेटालाई बदल्ने छ!"</string>
<string name="allow_restore_button_label" msgid="3081286752277127827">"मेरो डेटा पुनःबहाली गर्नुहोस्"</string>
<string name="deny_restore_button_label" msgid="1724367334453104378">"पुन:स्थापना नगर्नुहोस्"</string>
<string name="current_password_text" msgid="8268189555578298067">"कृपया तल तपाईंको हालको ब्याकअप पासवर्ड प्रविष्टि गर्नुहोस्:"</string>
<string name="device_encryption_restore_text" msgid="1570864916855208992">"कृपया तल तपाईंको उपकरण एन्क्रिप्सन पासवर्ड प्रविष्टि गर्नुहोस्:"</string>
<string name="device_encryption_backup_text" msgid="5866590762672844664">"कृपया तल तपाईंको उपकरण एन्क्रिप्सन पासवर्ड प्रविष्टि गर्नुहोस्: यो ब्याकप सँग्रह एन्क्रिप्ट गर्न पनि प्रयोग हुने छ।"</string>
<string name="backup_enc_password_text" msgid="4981585714795233099">"ब्याकप डेटालाई encrypt गर्न पासवर्ड प्रविष्टि गर्नुहोस्, यदि यो खालि छोडिएको खण्डमा तपाईको पुरानै पासवर्ड प्रयोग हुने छ।"</string>
- <string name="backup_enc_password_optional" msgid="1350137345907579306">"यदि तपाईं पूर्ण ब्याकअप डेटा इन्क्रिप्ट गर्न चाहनु हुन्छ भने तल पासवर्ड प्रविष्टि गर्नुहोस्।"</string>
+ <string name="backup_enc_password_optional" msgid="1350137345907579306">"यदि तपाईँ पूर्ण ब्याकअप डेटा इन्क्रिप्ट गर्न चाहनु हुन्छ भने तल पासवर्ड प्रविष्टि गर्नुहोस्।"</string>
<string name="backup_enc_password_required" msgid="7889652203371654149">"तपाईँको उपकरण गुप्तिकरण गरिए देखि, तपाईंले आफ्नो जगेडा गुप्तिकरण गर्न आवश्यक छ। कृपया तल पासवर्ड प्रविष्ट गर्नुहोस्:"</string>
<string name="restore_enc_password_text" msgid="6140898525580710823">"यदि पुनःबहाली डेटा इन्क्रिप्ट छ भने कृपया तल पासवर्ड प्रविष्टि गर्नुहोस्:"</string>
<string name="toast_backup_started" msgid="550354281452756121">"जगेडा राख्न सुरु हुँदै..."</string>
diff --git a/packages/BackupRestoreConfirmation/res/values-ro/strings.xml b/packages/BackupRestoreConfirmation/res/values-ro/strings.xml
index b762059..b5731f0918 100644
--- a/packages/BackupRestoreConfirmation/res/values-ro/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-ro/strings.xml
@@ -19,8 +19,8 @@
<string name="backup_confirm_title" msgid="827563724209303345">"Copiere de rezervă completă"</string>
<string name="restore_confirm_title" msgid="5469365809567486602">"Restabilire completă"</string>
<string name="backup_confirm_text" msgid="1878021282758896593">"S-a solicitat crearea unei copii de rezervă complete a tuturor datelor pe un computer desktop conectat. Doriți să permiteți acest lucru?\n\nDacă nu aţi solicitat dvs. copierea de rezervă, nu permiteți ca operațiunea să continue."</string>
- <string name="allow_backup_button_label" msgid="4217228747769644068">"Creaţi copii de rezervă pentru datele dvs."</string>
- <string name="deny_backup_button_label" msgid="6009119115581097708">"Nu creaţi copii de rezervă"</string>
+ <string name="allow_backup_button_label" msgid="4217228747769644068">"Creați copii de rezervă pentru datele dvs."</string>
+ <string name="deny_backup_button_label" msgid="6009119115581097708">"Nu creați copii de rezervă"</string>
<string name="restore_confirm_text" msgid="7499866728030461776">"S-a solicitat o restabilire completă a tuturor datelor de pe un computer desktop conectat. Doriți să permiteți acest lucru?\n\nDacă nu dvs. aţi solicitat această restabilire, nu permiteți continuarea operațiunii. Acest proces va înlocui toate datele existente în prezent pe dispozitiv!"</string>
<string name="allow_restore_button_label" msgid="3081286752277127827">"Restabiliţi datele dvs."</string>
<string name="deny_restore_button_label" msgid="1724367334453104378">"Nu restabiliţi"</string>
diff --git a/packages/DefaultContainerService/AndroidManifest.xml b/packages/DefaultContainerService/AndroidManifest.xml
index e67c554..ccf1501 100644
--- a/packages/DefaultContainerService/AndroidManifest.xml
+++ b/packages/DefaultContainerService/AndroidManifest.xml
@@ -12,7 +12,9 @@
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
<application android:label="@string/service_name"
- android:allowBackup="false">
+ android:allowBackup="false"
+ android:forceDeviceEncrypted="true"
+ android:encryptionAware="true">
<service android:name=".DefaultContainerService"
android:enabled="true"
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index f9e8027..d45345e 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -40,7 +40,7 @@
<activity
android:name=".ManageRootActivity"
- android:theme="@style/DocumentsNonDialogTheme"
+ android:theme="@style/DocumentsFullScreenTheme"
android:icon="@drawable/ic_doc_text">
<intent-filter>
<action android:name="android.provider.action.MANAGE_ROOT" />
@@ -63,7 +63,7 @@
<activity
android:name=".FilesActivity"
- android:theme="@style/FilesTheme"
+ android:theme="@style/DocumentsFullScreenTheme"
android:icon="@drawable/ic_files_app"
android:label="@string/files_label"
android:documentLaunchMode="intoExisting">
diff --git a/packages/DocumentsUI/res/color/item_doc_grid_border.xml b/packages/DocumentsUI/res/color/item_doc_grid_border.xml
new file mode 100644
index 0000000..e144af8
--- /dev/null
+++ b/packages/DocumentsUI/res/color/item_doc_grid_border.xml
@@ -0,0 +1,23 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:state_focused="true"
+ android:color="?android:attr/colorAccent"/>
+ <item
+ android:color="@android:color/transparent" />
+</selector>
diff --git a/packages/DocumentsUI/res/color/item_doc_grid_overlay.xml b/packages/DocumentsUI/res/color/item_doc_grid_overlay.xml
deleted file mode 100644
index ab414a9..0000000
--- a/packages/DocumentsUI/res/color/item_doc_grid_overlay.xml
+++ /dev/null
@@ -1,32 +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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item
- android:state_focused="true"
- android:color="@color/platform_blue_a200"
- android:alpha="0.1" />
- <item
- android:state_activated="true"
- android:color="?android:attr/colorAccent"
- android:alpha="0.1" />
- <item
- android:state_enabled="false"
- android:color="?android:attr/colorBackground"
- android:alpha="0.5" />
- <item
- android:color="@android:color/transparent" />
-</selector>
diff --git a/packages/DocumentsUI/res/color/item_doc_list_background_activated.xml b/packages/DocumentsUI/res/color/item_doc_list_background_activated.xml
index 90e2b7e..7d7a110 100644
--- a/packages/DocumentsUI/res/color/item_doc_list_background_activated.xml
+++ b/packages/DocumentsUI/res/color/item_doc_list_background_activated.xml
@@ -16,10 +16,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
- android:state_focused="true"
- android:color="@color/platform_blue_a200"
- android:alpha="0.1" />
- <item
android:state_activated="true"
android:color="?android:attr/colorAccent"
android:alpha="0.1" />
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_grid_selection_check.png b/packages/DocumentsUI/res/drawable-hdpi/ic_grid_selection_check.png
deleted file mode 100644
index f3007c2..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_grid_selection_check.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_grid_selection_check.png b/packages/DocumentsUI/res/drawable-mdpi/ic_grid_selection_check.png
deleted file mode 100644
index 16f2ab9..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_grid_selection_check.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_grid_selection_check.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_grid_selection_check.png
deleted file mode 100644
index 0885320..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_grid_selection_check.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_grid_selection_check.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_grid_selection_check.png
deleted file mode 100644
index 083bbcc..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_grid_selection_check.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_grid_selection_check.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_grid_selection_check.png
deleted file mode 100644
index 74b1ca5..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_grid_selection_check.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable/item_doc_grid_border.xml b/packages/DocumentsUI/res/drawable/item_doc_grid_border.xml
new file mode 100644
index 0000000..db66094
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable/item_doc_grid_border.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <stroke
+ android:width="2dp"
+ android:color="@color/item_doc_grid_border"/>
+</shape>
diff --git a/packages/DocumentsUI/res/drawable/item_doc_grid_overlay.xml b/packages/DocumentsUI/res/drawable/item_doc_grid_overlay.xml
deleted file mode 100644
index 3f50fb9..0000000
--- a/packages/DocumentsUI/res/drawable/item_doc_grid_overlay.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <solid android:color="@color/item_doc_grid_overlay" />
-</shape>
diff --git a/packages/DocumentsUI/res/drawable/item_doc_grid_overlay_icon.xml b/packages/DocumentsUI/res/drawable/item_doc_grid_overlay_icon.xml
deleted file mode 100644
index d40de1e..0000000
--- a/packages/DocumentsUI/res/drawable/item_doc_grid_overlay_icon.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 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.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android" android:constantSize="true">
- <item android:state_focused="true" android:state_activated="true" android:drawable="@drawable/ic_grid_selection_check" />
- <item android:state_focused="false" android:state_activated="true" android:drawable="@drawable/ic_grid_selection_check" />
- <item android:drawable="@android:color/transparent" />
-</selector>
diff --git a/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml b/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml
index d124320..381e1c89 100644
--- a/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml
+++ b/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml
@@ -14,10 +14,17 @@
limitations under the License.
-->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.documentsui.ListItem xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@drawable/item_doc_list_background">
+ android:background="@drawable/item_doc_list_background"
+ android:orientation="horizontal"
+ android:focusable="true">
+
+ <View
+ android:id="@+id/focus_indicator"
+ android:layout_width="4dp"
+ android:layout_height="match_parent" />
<LinearLayout
android:layout_width="match_parent"
@@ -121,4 +128,4 @@
</LinearLayout>
-</FrameLayout>
+</com.android.documentsui.ListItem>
diff --git a/packages/DocumentsUI/res/layout/fragment_directory.xml b/packages/DocumentsUI/res/layout/fragment_directory.xml
index 4019d02..ada7f49 100644
--- a/packages/DocumentsUI/res/layout/fragment_directory.xml
+++ b/packages/DocumentsUI/res/layout/fragment_directory.xml
@@ -72,10 +72,10 @@
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:paddingStart="@dimen/grid_padding_horiz"
- android:paddingEnd="@dimen/grid_padding_horiz"
- android:paddingTop="@dimen/grid_padding_vert"
- android:paddingBottom="@dimen/grid_padding_vert"
+ android:paddingStart="0dp"
+ android:paddingEnd="0dp"
+ android:paddingTop="0dp"
+ android:paddingBottom="0dp"
android:clipToPadding="false"
android:scrollbarStyle="outsideOverlay"
android:drawSelectorOnTop="true"
diff --git a/packages/DocumentsUI/res/layout/item_doc_grid.xml b/packages/DocumentsUI/res/layout/item_doc_grid.xml
index 71e618b..1dfb34a 100644
--- a/packages/DocumentsUI/res/layout/item_doc_grid.xml
+++ b/packages/DocumentsUI/res/layout/item_doc_grid.xml
@@ -14,130 +14,110 @@
limitations under the License.
-->
-<com.android.documentsui.GridItem xmlns:android="http://schemas.android.com/apk/res/android"
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="@dimen/grid_item_height"
+ android:layout_height="wrap_content"
android:layout_margin="@dimen/grid_item_margin"
- android:background="@color/item_doc_grid_background">
+ android:background="@color/item_doc_grid_background"
+ android:focusable="true">
- <ImageView
- android:id="@+id/icon_thumb"
+ <!-- Main item thumbnail. Comprised of two overlapping images, the
+ visibility of which is controlled by code in
+ DirectoryFragment.java. -->
+ <FrameLayout
+ android:id="@+id/thumbnail"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="centerCrop"
- android:contentDescription="@null" />
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
+ android:layout_height="wrap_content"
+ android:paddingBottom="8dp">
+
+ <com.android.documentsui.GridItemThumbnail
+ android:id="@+id/icon_thumb"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="centerCrop"
+ android:contentDescription="@null" />
+
<ImageView
android:id="@+id/icon_mime"
android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
+ android:layout_height="wrap_content"
android:scaleType="centerInside"
android:contentDescription="@null" />
+
+ </FrameLayout>
+
+ <!-- Item nameplate. Has a mime-type icon and some text fields (title,
+ size, mod-time, etc). -->
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/thumbnail"
+ android:layout_toEndOf="@android:id/icon1"
+ android:singleLine="true"
+ android:ellipsize="middle"
+ android:textAlignment="viewStart"
+ android:paddingEnd="12dp"
+ android:textAppearance="@android:style/TextAppearance.Material.Subhead"
+ android:textColor="@*android:color/primary_text_default_material_light" />
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@color/item_doc_grid_protect_background"
- android:orientation="vertical"
- android:paddingStart="16dp"
- android:paddingEnd="12dp"
- android:paddingTop="8dp"
- android:paddingBottom="8dp">
+ <TextView
+ android:id="@+id/size"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/title"
+ android:layout_toEndOf="@android:id/icon1"
+ android:paddingEnd="4dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:textAlignment="viewStart"
+ android:textAppearance="@android:style/TextAppearance.Material.Caption"
+ android:textColor="@*android:color/primary_text_default_material_light" />
- <LinearLayout
- android:id="@+id/line1"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center_vertical"
- android:orientation="horizontal"
- android:baselineAligned="false">
-
- <TextView
- android:id="@android:id/title"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:singleLine="true"
- android:ellipsize="middle"
- android:textAlignment="viewStart"
- android:textAppearance="@android:style/TextAppearance.Material.Subhead"
- android:textColor="@*android:color/primary_text_default_material_dark" />
-
- <ImageView
- android:id="@android:id/icon1"
- android:layout_width="@dimen/root_icon_size"
- android:layout_height="@dimen/root_icon_size"
- android:layout_marginStart="8dp"
- android:scaleType="centerInside"
- android:contentDescription="@null" />
-
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/line2"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center_vertical"
- android:orientation="horizontal"
- android:baselineAligned="false">
-
- <TextView
- android:id="@+id/date"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="0.5"
- android:singleLine="true"
- android:ellipsize="end"
- android:textAlignment="viewStart"
- android:textAppearance="@android:style/TextAppearance.Material.Caption"
- android:textColor="@*android:color/primary_text_default_material_dark" />
-
- <TextView
- android:id="@+id/size"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="0.5"
- android:layout_marginStart="8dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:textAlignment="viewStart"
- android:textAppearance="@android:style/TextAppearance.Material.Caption"
- android:textColor="@*android:color/primary_text_default_material_dark" />
-
- <ImageView
- android:id="@android:id/icon2"
- android:layout_width="@dimen/root_icon_size"
- android:layout_height="@dimen/root_icon_size"
- android:layout_marginStart="8dp"
- android:scaleType="centerInside"
- android:contentDescription="@null"
- android:visibility="gone" />
-
- </LinearLayout>
-
- </LinearLayout>
-
- </LinearLayout>
-
+ <TextView
+ android:id="@+id/date"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/title"
+ android:layout_toEndOf="@id/size"
+ android:paddingEnd="12dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:textAlignment="viewStart"
+ android:textAppearance="@android:style/TextAppearance.Material.Caption"
+ android:textColor="@*android:color/primary_text_default_material_light" />
+
<ImageView
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:src="@drawable/item_doc_grid_overlay"
+ android:id="@android:id/icon1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="8dp"
+ android:layout_below="@id/thumbnail"
+ android:layout_alignParentLeft="true"
+ android:layout_alignBottom="@id/size"
+ android:layout_alignTop="@android:id/title"
+ android:scaleType="centerInside"
android:contentDescription="@null"
+ android:paddingStart="12dp"
+ android:paddingEnd="8dp"/>
+
+ <!-- Use an explicit spacer so we can align things to it. -->
+ <Space
+ android:id="@+id/bottomPadding"
+ android:layout_width="match_parent"
+ android:layout_height="8dp"
+ android:layout_below="@id/size" />
+
+ <!-- An overlay that draws the item border when it is focused. -->
+ <View
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBottom="@id/bottomPadding"
+ android:layout_alignTop="@id/thumbnail"
+ android:layout_alignLeft="@id/thumbnail"
+ android:layout_alignRight="@id/thumbnail"
+ android:contentDescription="@null"
+ android:background="@drawable/item_doc_grid_border"
android:duplicateParentState="true" />
- <ImageView
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:src="@drawable/item_doc_grid_overlay_icon"
- android:scaleType="center"
- android:contentDescription="@null"
- android:duplicateParentState="true" />
-
-</com.android.documentsui.GridItem>
+</RelativeLayout>
diff --git a/packages/DocumentsUI/res/layout/item_doc_list.xml b/packages/DocumentsUI/res/layout/item_doc_list.xml
index c576669..c409166 100644
--- a/packages/DocumentsUI/res/layout/item_doc_list.xml
+++ b/packages/DocumentsUI/res/layout/item_doc_list.xml
@@ -14,10 +14,17 @@
limitations under the License.
-->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.documentsui.ListItem xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@drawable/item_doc_list_background">
+ android:background="@drawable/item_doc_list_background"
+ android:orientation="horizontal"
+ android:focusable="true">
+
+ <View
+ android:id="@+id/focus_indicator"
+ android:layout_width="4dp"
+ android:layout_height="match_parent" />
<LinearLayout
android:layout_width="match_parent"
@@ -131,4 +138,4 @@
</LinearLayout>
-</FrameLayout>
+</com.android.documentsui.ListItem>
diff --git a/packages/DocumentsUI/res/values-af/config.xml b/packages/DocumentsUI/res/values-af/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-af/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-am/config.xml b/packages/DocumentsUI/res/values-am/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-am/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-ar/config.xml b/packages/DocumentsUI/res/values-ar/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-ar/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-az-rAZ/config.xml b/packages/DocumentsUI/res/values-az-rAZ/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-az-rAZ/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-bg/config.xml b/packages/DocumentsUI/res/values-bg/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-bg/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-bn-rBD/config.xml b/packages/DocumentsUI/res/values-bn-rBD/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-bn-rBD/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-ca/config.xml b/packages/DocumentsUI/res/values-ca/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-ca/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-cs/config.xml b/packages/DocumentsUI/res/values-cs/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-cs/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-da/config.xml b/packages/DocumentsUI/res/values-da/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-da/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-de/config.xml b/packages/DocumentsUI/res/values-de/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-de/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-el/config.xml b/packages/DocumentsUI/res/values-el/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-el/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-en-rAU/config.xml b/packages/DocumentsUI/res/values-en-rAU/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-en-rAU/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-en-rGB/config.xml b/packages/DocumentsUI/res/values-en-rGB/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-en-rGB/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-en-rIN/config.xml b/packages/DocumentsUI/res/values-en-rIN/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-en-rIN/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-es-rUS/config.xml b/packages/DocumentsUI/res/values-es-rUS/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-es-rUS/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-es/config.xml b/packages/DocumentsUI/res/values-es/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-es/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-et-rEE/config.xml b/packages/DocumentsUI/res/values-et-rEE/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-et-rEE/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-eu-rES/config.xml b/packages/DocumentsUI/res/values-eu-rES/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-eu-rES/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-fa/config.xml b/packages/DocumentsUI/res/values-fa/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-fa/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-fa/strings.xml b/packages/DocumentsUI/res/values-fa/strings.xml
index b504db5..432205e 100644
--- a/packages/DocumentsUI/res/values-fa/strings.xml
+++ b/packages/DocumentsUI/res/values-fa/strings.xml
@@ -58,7 +58,7 @@
<string name="root_recent" msgid="4470053704320518133">"اخیر"</string>
<string name="root_available_bytes" msgid="8568452858617033281">"<xliff:g id="SIZE">%1$s</xliff:g> آزاد"</string>
<string name="root_type_service" msgid="2178854894416775409">"خدمات ذخیرهسازی"</string>
- <string name="root_type_shortcut" msgid="3318760609471618093">"میانبرها"</string>
+ <string name="root_type_shortcut" msgid="3318760609471618093">"میانبرها"</string>
<string name="root_type_device" msgid="7121342474653483538">"دستگاهها"</string>
<string name="root_type_apps" msgid="8838065367985945189">"برنامههای بیشتر"</string>
<string name="empty" msgid="7858882803708117596">"موردی موجود نیست"</string>
diff --git a/packages/DocumentsUI/res/values-fi/config.xml b/packages/DocumentsUI/res/values-fi/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-fi/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-fr-rCA/config.xml b/packages/DocumentsUI/res/values-fr-rCA/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-fr-rCA/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-fr/config.xml b/packages/DocumentsUI/res/values-fr/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-fr/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-gl-rES/config.xml b/packages/DocumentsUI/res/values-gl-rES/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-gl-rES/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-gu-rIN/config.xml b/packages/DocumentsUI/res/values-gu-rIN/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-gu-rIN/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-hi/config.xml b/packages/DocumentsUI/res/values-hi/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-hi/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-hr/config.xml b/packages/DocumentsUI/res/values-hr/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-hr/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-hu/config.xml b/packages/DocumentsUI/res/values-hu/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-hu/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-hy-rAM/config.xml b/packages/DocumentsUI/res/values-hy-rAM/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-hy-rAM/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-in/config.xml b/packages/DocumentsUI/res/values-in/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-in/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-is-rIS/config.xml b/packages/DocumentsUI/res/values-is-rIS/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-is-rIS/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-it/config.xml b/packages/DocumentsUI/res/values-it/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-it/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-iw/config.xml b/packages/DocumentsUI/res/values-iw/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-iw/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-ja/config.xml b/packages/DocumentsUI/res/values-ja/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-ja/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-ka-rGE/config.xml b/packages/DocumentsUI/res/values-ka-rGE/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-ka-rGE/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-kk-rKZ/config.xml b/packages/DocumentsUI/res/values-kk-rKZ/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-kk-rKZ/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-km-rKH/config.xml b/packages/DocumentsUI/res/values-km-rKH/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-km-rKH/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-kn-rIN/config.xml b/packages/DocumentsUI/res/values-kn-rIN/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-kn-rIN/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-ko/config.xml b/packages/DocumentsUI/res/values-ko/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-ko/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-ky-rKG/config.xml b/packages/DocumentsUI/res/values-ky-rKG/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-ky-rKG/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-lo-rLA/config.xml b/packages/DocumentsUI/res/values-lo-rLA/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-lo-rLA/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-lt/config.xml b/packages/DocumentsUI/res/values-lt/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-lt/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-lv/config.xml b/packages/DocumentsUI/res/values-lv/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-lv/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-mk-rMK/config.xml b/packages/DocumentsUI/res/values-mk-rMK/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-mk-rMK/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-ml-rIN/config.xml b/packages/DocumentsUI/res/values-ml-rIN/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-ml-rIN/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-mn-rMN/config.xml b/packages/DocumentsUI/res/values-mn-rMN/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-mn-rMN/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-mr-rIN/config.xml b/packages/DocumentsUI/res/values-mr-rIN/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-mr-rIN/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-ms-rMY/config.xml b/packages/DocumentsUI/res/values-ms-rMY/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-ms-rMY/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-my-rMM/config.xml b/packages/DocumentsUI/res/values-my-rMM/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-my-rMM/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-nb/config.xml b/packages/DocumentsUI/res/values-nb/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-nb/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-ne-rNP/config.xml b/packages/DocumentsUI/res/values-ne-rNP/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-ne-rNP/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-nl/config.xml b/packages/DocumentsUI/res/values-nl/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-nl/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-pa-rIN/config.xml b/packages/DocumentsUI/res/values-pa-rIN/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-pa-rIN/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-pl/config.xml b/packages/DocumentsUI/res/values-pl/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-pl/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-pt-rBR/config.xml b/packages/DocumentsUI/res/values-pt-rBR/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-pt-rBR/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-pt-rPT/config.xml b/packages/DocumentsUI/res/values-pt-rPT/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-pt-rPT/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-pt/config.xml b/packages/DocumentsUI/res/values-pt/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-pt/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-ro/config.xml b/packages/DocumentsUI/res/values-ro/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-ro/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-ru/config.xml b/packages/DocumentsUI/res/values-ru/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-ru/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-si-rLK/config.xml b/packages/DocumentsUI/res/values-si-rLK/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-si-rLK/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-sk/config.xml b/packages/DocumentsUI/res/values-sk/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-sk/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-sl/config.xml b/packages/DocumentsUI/res/values-sl/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-sl/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-sq-rAL/config.xml b/packages/DocumentsUI/res/values-sq-rAL/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-sq-rAL/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-sr/config.xml b/packages/DocumentsUI/res/values-sr/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-sr/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-sv/config.xml b/packages/DocumentsUI/res/values-sv/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-sv/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-sw/config.xml b/packages/DocumentsUI/res/values-sw/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-sw/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-sw720dp/styles.xml b/packages/DocumentsUI/res/values-sw720dp/styles.xml
index d415a84..a8dcbb0 100644
--- a/packages/DocumentsUI/res/values-sw720dp/styles.xml
+++ b/packages/DocumentsUI/res/values-sw720dp/styles.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <style name="DocumentsBaseTheme" parent="@style/Theme.AppCompat.Dialog">
+ <style name="DocumentsBaseTheme" parent="@style/Theme.AppCompat.Light.Dialog">
<!-- We do not specify width of window here because the max size of
floating window specified by windowFixedWidthis is limited. -->
<item name="*android:windowFixedHeightMajor">80%</item>
diff --git a/packages/DocumentsUI/res/values-ta-rIN/config.xml b/packages/DocumentsUI/res/values-ta-rIN/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-ta-rIN/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-te-rIN/config.xml b/packages/DocumentsUI/res/values-te-rIN/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-te-rIN/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-th/config.xml b/packages/DocumentsUI/res/values-th/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-th/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-tl/config.xml b/packages/DocumentsUI/res/values-tl/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-tl/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-tr/config.xml b/packages/DocumentsUI/res/values-tr/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-tr/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-uk/config.xml b/packages/DocumentsUI/res/values-uk/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-uk/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-ur-rPK/config.xml b/packages/DocumentsUI/res/values-ur-rPK/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-ur-rPK/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-uz-rUZ/config.xml b/packages/DocumentsUI/res/values-uz-rUZ/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-uz-rUZ/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-vi/config.xml b/packages/DocumentsUI/res/values-vi/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-vi/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-zh-rCN/config.xml b/packages/DocumentsUI/res/values-zh-rCN/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-zh-rCN/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-zh-rHK/config.xml b/packages/DocumentsUI/res/values-zh-rHK/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-zh-rHK/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-zh-rTW/config.xml b/packages/DocumentsUI/res/values-zh-rTW/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-zh-rTW/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-zu/config.xml b/packages/DocumentsUI/res/values-zu/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-zu/config.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values/colors.xml b/packages/DocumentsUI/res/values/colors.xml
index cb6957d..68c8b65 100644
--- a/packages/DocumentsUI/res/values/colors.xml
+++ b/packages/DocumentsUI/res/values/colors.xml
@@ -15,24 +15,16 @@
-->
<resources>
- <color name="material_grey_50">#fffafafa</color>
- <color name="material_grey_300">#ffeeeeee</color>
- <color name="material_grey_600">#ff757575</color>
- <color name="material_grey_800">#ff424242</color>
+ <color name="material_grey_400">#ffbdbdbd</color>
- <color name="primary_dark">@*android:color/material_blue_grey_900</color>
- <color name="primary">@*android:color/material_blue_grey_800</color>
- <color name="accent">@*android:color/material_deep_teal_500</color>
-
- <color name="platform_blue_100">#ffd0d9ff</color>
- <color name="platform_blue_500">#ff5677fc</color>
- <color name="platform_blue_700">#ff455ede</color>
- <color name="platform_blue_a100">#ffa6baff</color>
- <color name="platform_blue_a200">#ffff5177</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="action_mode">@color/material_grey_400</color>
- <color name="directory_background">@color/material_grey_300</color>
- <color name="item_doc_grid_background">#FFFFFFFF</color>
- <color name="item_doc_grid_protect_background">#88000000</color>
+ <color name="directory_background">@*android:color/material_grey_300</color>
+ <color name="item_doc_grid_background">@android:color/white</color>
+ <color name="item_doc_grid_protect_background">@android:color/white</color>
<color name="band_select_background">#88ffffff</color>
<color name="band_select_border">#44000000</color>
</resources>
diff --git a/packages/DocumentsUI/res/values/config.xml b/packages/DocumentsUI/res/values/config.xml
index ed7820b..ad419aa 100644
--- a/packages/DocumentsUI/res/values/config.xml
+++ b/packages/DocumentsUI/res/values/config.xml
@@ -16,4 +16,6 @@
<resources>
<bool name="productivity_device">true</bool>
+ <!-- Intentionally unset. Vendors should set this in an overlay. -->
+ <string name="trusted_quick_viewer_package"></string>
</resources>
diff --git a/packages/DocumentsUI/res/values/styles.xml b/packages/DocumentsUI/res/values/styles.xml
index c13f144..15d17cc 100644
--- a/packages/DocumentsUI/res/values/styles.xml
+++ b/packages/DocumentsUI/res/values/styles.xml
@@ -28,7 +28,7 @@
<item name="android:colorPrimaryDark">@color/primary_dark</item>
<item name="android:colorPrimary">@color/primary</item>
<item name="android:colorAccent">@color/accent</item>
- <item name="colorActionMode">@color/material_grey_800</item>
+ <item name="colorActionMode">@color/action_mode</item>
<item name="android:listDivider">@*android:drawable/list_divider_material</item>
@@ -37,14 +37,17 @@
<item name="android:windowNoTitle">true</item>
<item name="android:windowSoftInputMode">stateUnspecified|adjustUnspecified</item>
- <item name="android:alertDialogTheme">@android:style/Theme.Material.Light.Dialog.Alert</item>
</style>
- <style name="DocumentsBaseTheme.FullScreen" parent="@style/Theme.AppCompat.Light.DarkActionBar">
+ <style name="DocumentsFullScreenTheme" parent="@style/Theme.AppCompat.Light.DarkActionBar">
<item name="actionBarWidgetTheme">@null</item>
<item name="actionBarTheme">@style/ActionBarTheme</item>
<item name="actionBarPopupTheme">@style/ActionBarPopupTheme</item>
- <item name="colorActionMode">@color/material_grey_800</item>
+
+ <item name="android:colorPrimaryDark">@color/primary_dark</item>
+ <item name="android:colorPrimary">@color/primary</item>
+ <item name="android:colorAccent">@color/accent</item>
+ <item name="colorActionMode">@color/action_mode</item>
<item name="android:listDivider">@*android:drawable/list_divider_material</item>
@@ -55,38 +58,6 @@
<item name="android:windowSoftInputMode">stateUnspecified|adjustUnspecified</item>
</style>
- <style name="DocumentsNonDialogTheme" parent="@style/DocumentsBaseTheme.FullScreen">
- <item name="android:colorPrimaryDark">@color/primary_dark</item>
- <item name="android:colorPrimary">@color/primary</item>
- <item name="android:colorAccent">@color/accent</item>
-
- <item name="android:actionModeStyle">@style/ActionModeStyle</item>
-
- <item name="android:alertDialogTheme">@style/AlertDialogTheme</item>
- </style>
-
- <style name="ActionModeStyle" parent="@android:style/Widget.Material.Light.ActionMode">
- <item name="android:background">@color/material_grey_600</item>
- </style>
-
- <style name="AlertDialogTheme" parent="@android:style/Theme.Material.Light.Dialog.Alert">
- <item name="android:colorAccent">@color/platform_blue_700</item>
- </style>
-
- <style name="FilesTheme" parent="@style/DocumentsBaseTheme.FullScreen">
- <item name="android:colorPrimaryDark">@color/platform_blue_700</item>
- <item name="android:colorPrimary">@color/platform_blue_500</item>
- <item name="android:colorAccent">@color/platform_blue_700</item>
- <item name="colorControlActivated">@color/platform_blue_a100</item>
- <item name="android:actionModeStyle">@style/FilesActionModeStyle</item>
- <item name="colorActionMode">@color/platform_blue_700</item>
- <item name="android:alertDialogTheme">@style/AlertDialogTheme</item>
- </style>
-
- <style name="FilesActionModeStyle" parent="@android:style/Widget.Material.Light.ActionMode">
- <item name="android:background">@color/platform_blue_100</item>
- </style>
-
<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>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index 9d2d171..0ee970d 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -16,11 +16,11 @@
package com.android.documentsui;
-import static com.android.documentsui.DirectoryFragment.ANIM_DOWN;
-import static com.android.documentsui.DirectoryFragment.ANIM_NONE;
-import static com.android.documentsui.DirectoryFragment.ANIM_SIDE;
-import static com.android.documentsui.DirectoryFragment.ANIM_UP;
import static com.android.documentsui.Shared.DEBUG;
+import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_DOWN;
+import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_NONE;
+import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_SIDE;
+import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_UP;
import static com.android.internal.util.Preconditions.checkArgument;
import android.app.Activity;
@@ -55,6 +55,7 @@
import android.widget.TextView;
import com.android.documentsui.RecentsProvider.ResumeColumns;
+import com.android.documentsui.dirlist.DirectoryFragment;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.DocumentStack;
import com.android.documentsui.model.DurableUtils;
@@ -69,7 +70,7 @@
import java.util.List;
import java.util.concurrent.Executor;
-abstract class BaseActivity extends Activity {
+public abstract class BaseActivity extends Activity {
static final String EXTRA_STATE = "state";
@@ -383,7 +384,7 @@
invalidateOptionsMenu();
}
- void onStateChanged() {
+ public void onStateChanged() {
invalidateOptionsMenu();
}
@@ -421,7 +422,7 @@
super.onRestoreInstanceState(state);
}
- RootInfo getCurrentRoot() {
+ public RootInfo getCurrentRoot() {
if (mState.stack.root != null) {
return mState.stack.root;
} else {
@@ -466,21 +467,6 @@
}
}
- @Override
- public boolean onKeyUp(int keyCode, KeyEvent event) {
- if (DEBUG) Log.d(mTag, "onKeyUp: keycode = " + keyCode);
- DirectoryFragment dir = DirectoryFragment.get(getFragmentManager());
- switch (keyCode) {
- case KeyEvent.KEYCODE_MOVE_HOME:
- dir.focusFirstFile();
- return true;
- case KeyEvent.KEYCODE_MOVE_END:
- dir.focusLastFile();
- return true;
- }
- return super.onKeyUp(keyCode, event);
- }
-
public void onStackPicked(DocumentStack stack) {
try {
// Update the restored stack to ensure we have freshest data
@@ -840,7 +826,7 @@
* Interface providing access to current view of documents
* even when all documents are not homed to the same parent.
*/
- interface DocumentContext {
+ public interface DocumentContext {
/**
* Returns the cursor for the selected document. The cursor can be used to retrieve
* details about a document and its siblings.
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
index 047949f..55a123f 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
@@ -66,7 +66,6 @@
public static final String EXTRA_FAILURE = "com.android.documentsui.FAILURE";
public static final String EXTRA_TRANSFER_MODE = "com.android.documentsui.TRANSFER_MODE";
- public static final int TRANSFER_MODE_NONE = 0;
public static final int TRANSFER_MODE_COPY = 1;
public static final int TRANSFER_MODE_MOVE = 2;
@@ -410,6 +409,7 @@
*
* @param srcInfo DocumentInfos for the documents to copy.
* @param dstDirInfo The destination directory.
+ * @param mode The transfer mode (copy or move).
* @throws RemoteException
*/
private void copy(DocumentInfo srcInfo, DocumentInfo dstDirInfo, int mode)
@@ -417,11 +417,37 @@
if (DEBUG) Log.d(TAG, "Copying " + srcInfo.displayName + " (" + srcInfo.derivedUri + ")" +
" to " + dstDirInfo.displayName + " (" + dstDirInfo.derivedUri + ")");
+ // When copying within the same provider, try to use optimized copying and moving.
+ // If not supported, then fallback to byte-by-byte copy/move.
+ if (srcInfo.authority.equals(dstDirInfo.authority)) {
+ switch (mode) {
+ case TRANSFER_MODE_COPY:
+ if ((srcInfo.flags & Document.FLAG_SUPPORTS_COPY) != 0) {
+ if (DocumentsContract.copyDocument(mSrcClient, srcInfo.derivedUri,
+ dstDirInfo.derivedUri) == null) {
+ mFailedFiles.add(srcInfo);
+ }
+ return;
+ }
+ break;
+ case TRANSFER_MODE_MOVE:
+ if ((srcInfo.flags & Document.FLAG_SUPPORTS_MOVE) != 0) {
+ if (DocumentsContract.moveDocument(mSrcClient, srcInfo.derivedUri,
+ dstDirInfo.derivedUri) == null) {
+ mFailedFiles.add(srcInfo);
+ }
+ return;
+ }
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown transfer mode.");
+ }
+ }
+
final Uri dstUri = DocumentsContract.createDocument(mDstClient, dstDirInfo.derivedUri,
srcInfo.mimeType, srcInfo.displayName);
if (dstUri == null) {
// If this is a directory, the entire subdir will not be copied over.
- Log.e(TAG, "Error while copying " + srcInfo.displayName);
mFailedFiles.add(srcInfo);
return;
}
@@ -522,8 +548,6 @@
try {
DocumentInfo info = DocumentInfo.fromUri(getContentResolver(), srcUri);
mFailedFiles.add(info);
- Log.e(TAG, "Error while copying " + info.displayName + " (" + info.derivedUri + ")",
- copyError);
} catch (FileNotFoundException ignore) {
// Generate a dummy DocumentInfo so an error still gets reflected in the UI for this
// file.
@@ -531,7 +555,6 @@
info.derivedUri = srcUri;
info.displayName = "Unknown [" + srcUri + "]";
mFailedFiles.add(info);
- Log.e(TAG, "Error while copying " + srcUri, copyError);
}
if (dstFile != null) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
deleted file mode 100644
index 94fce59..0000000
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ /dev/null
@@ -1,2028 +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.documentsui;
-
-import static com.android.documentsui.Shared.DEBUG;
-import static com.android.documentsui.Shared.TAG;
-import static com.android.documentsui.State.ACTION_BROWSE;
-import static com.android.documentsui.State.ACTION_CREATE;
-import static com.android.documentsui.State.ACTION_MANAGE;
-import static com.android.documentsui.State.MODE_GRID;
-import static com.android.documentsui.State.MODE_LIST;
-import static com.android.documentsui.State.MODE_UNKNOWN;
-import static com.android.documentsui.State.SORT_ORDER_UNKNOWN;
-import static com.android.documentsui.model.DocumentInfo.getCursorInt;
-import static com.android.documentsui.model.DocumentInfo.getCursorLong;
-import static com.android.documentsui.model.DocumentInfo.getCursorString;
-import static com.android.internal.util.Preconditions.checkNotNull;
-import static com.android.internal.util.Preconditions.checkState;
-
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.Fragment;
-import android.app.FragmentManager;
-import android.app.FragmentTransaction;
-import android.app.LoaderManager.LoaderCallbacks;
-import android.content.ClipData;
-import android.content.ContentProviderClient;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.content.Loader;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Point;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.CancellationSignal;
-import android.os.Looper;
-import android.os.OperationCanceledException;
-import android.os.Parcelable;
-import android.os.SystemProperties;
-import android.provider.DocumentsContract;
-import android.provider.DocumentsContract.Document;
-import android.support.annotation.Nullable;
-import android.support.annotation.VisibleForTesting;
-import android.support.design.widget.Snackbar;
-import android.support.v7.widget.GridLayoutManager;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.RecyclerView.LayoutManager;
-import android.support.v7.widget.RecyclerView.RecyclerListener;
-import android.support.v7.widget.RecyclerView.ViewHolder;
-import android.text.TextUtils;
-import android.text.format.DateUtils;
-import android.text.format.Formatter;
-import android.text.format.Time;
-import android.util.Log;
-import android.util.SparseArray;
-import android.util.SparseBooleanArray;
-import android.util.TypedValue;
-import android.view.ActionMode;
-import android.view.DragEvent;
-import android.view.GestureDetector;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.View.OnLayoutChangeListener;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.documentsui.BaseActivity.DocumentContext;
-import com.android.documentsui.MultiSelectManager.Selection;
-import com.android.documentsui.ProviderExecutor.Preemptable;
-import com.android.documentsui.RecentsProvider.StateColumns;
-import com.android.documentsui.model.DocumentInfo;
-import com.android.documentsui.model.DocumentStack;
-import com.android.documentsui.model.RootInfo;
-
-import com.google.common.collect.Lists;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Display the documents inside a single directory.
- */
-public class DirectoryFragment extends Fragment {
-
- public static final int TYPE_NORMAL = 1;
- public static final int TYPE_SEARCH = 2;
- public static final int TYPE_RECENT_OPEN = 3;
-
- public static final int ANIM_NONE = 1;
- public static final int ANIM_SIDE = 2;
- public static final int ANIM_DOWN = 3;
- public static final int ANIM_UP = 4;
-
- public static final int REQUEST_COPY_DESTINATION = 1;
-
- private static final int LOADER_ID = 42;
- private static final boolean DEBUG_ENABLE_DND = false;
-
- private static final String EXTRA_TYPE = "type";
- private static final String EXTRA_ROOT = "root";
- private static final String EXTRA_DOC = "doc";
- private static final String EXTRA_QUERY = "query";
- private static final String EXTRA_IGNORE_STATE = "ignoreState";
-
- private Model mModel;
- private Model.UpdateListener mModelUpdateListener = new ModelUpdateListener();
-
- private View mEmptyView;
- private RecyclerView mRecView;
-
- private int mType = TYPE_NORMAL;
- private String mStateKey;
-
- private int mLastMode = MODE_UNKNOWN;
- private int mLastSortOrder = SORT_ORDER_UNKNOWN;
- private boolean mLastShowSize;
- private boolean mHideGridTitles;
- private boolean mSvelteRecents;
- private Point mThumbSize;
- private DocumentsAdapter mAdapter;
- private LoaderCallbacks<DirectoryResult> mCallbacks;
- private FragmentTuner mFragmentTuner;
- private DocumentClipper mClipper;
- // These are lazily initialized.
- private LinearLayoutManager mListLayout;
- private GridLayoutManager mGridLayout;
- private int mColumnCount = 1; // This will get updated when layout changes.
-
- private MessageBar mMessageBar;
- private View mProgressBar;
-
- public static void showNormal(FragmentManager fm, RootInfo root, DocumentInfo doc, int anim) {
- show(fm, TYPE_NORMAL, root, doc, null, anim);
- }
-
- public static void showSearch(FragmentManager fm, RootInfo root, String query, int anim) {
- show(fm, TYPE_SEARCH, root, null, query, anim);
- }
-
- public static void showRecentsOpen(FragmentManager fm, int anim) {
- show(fm, TYPE_RECENT_OPEN, null, null, null, anim);
- }
-
- private static void show(FragmentManager fm, int type, RootInfo root, DocumentInfo doc,
- String query, int anim) {
- final Bundle args = new Bundle();
- args.putInt(EXTRA_TYPE, type);
- args.putParcelable(EXTRA_ROOT, root);
- args.putParcelable(EXTRA_DOC, doc);
- args.putString(EXTRA_QUERY, query);
-
- final FragmentTransaction ft = fm.beginTransaction();
- switch (anim) {
- case ANIM_SIDE:
- args.putBoolean(EXTRA_IGNORE_STATE, true);
- break;
- case ANIM_DOWN:
- args.putBoolean(EXTRA_IGNORE_STATE, true);
- ft.setCustomAnimations(R.animator.dir_down, R.animator.dir_frozen);
- break;
- case ANIM_UP:
- ft.setCustomAnimations(R.animator.dir_frozen, R.animator.dir_up);
- break;
- }
-
- final DirectoryFragment fragment = new DirectoryFragment();
- fragment.setArguments(args);
-
- ft.replace(R.id.container_directory, fragment);
- ft.commitAllowingStateLoss();
- }
-
- private static String buildStateKey(RootInfo root, DocumentInfo doc) {
- final StringBuilder builder = new StringBuilder();
- builder.append(root != null ? root.authority : "null").append(';');
- builder.append(root != null ? root.rootId : "null").append(';');
- builder.append(doc != null ? doc.documentId : "null");
- return builder.toString();
- }
-
- public static DirectoryFragment get(FragmentManager fm) {
- // TODO: deal with multiple directories shown at once
- return (DirectoryFragment) fm.findFragmentById(R.id.container_directory);
- }
-
- @Override
- public View onCreateView(
- LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- final View view = inflater.inflate(R.layout.fragment_directory, container, false);
-
- mMessageBar = MessageBar.create(getChildFragmentManager());
- mProgressBar = view.findViewById(R.id.progressbar);
-
- mEmptyView = view.findViewById(android.R.id.empty);
-
- mRecView = (RecyclerView) view.findViewById(R.id.list);
- mRecView.setRecyclerListener(
- new RecyclerListener() {
- @Override
- public void onViewRecycled(ViewHolder holder) {
- cancelThumbnailTask(holder.itemView);
- }
- });
-
- // TODO: Rather than update columns on layout changes, push this
- // code (or something like it) into GridLayoutManager.
- mRecView.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) {
- mColumnCount = calculateColumnCount();
- if (mGridLayout != null) {
- mGridLayout.setSpanCount(mColumnCount);
- }
- }
- });
-
- // TODO: Add a divider between views (which might use RecyclerView.ItemDecoration).
- if (DEBUG_ENABLE_DND) {
- setupDragAndDropOnDirectoryView(mRecView);
- }
-
- return view;
- }
-
- @Override
- public void onDestroyView() {
- super.onDestroyView();
-
- // Cancel any outstanding thumbnail requests
- final int count = mRecView.getChildCount();
- for (int i = 0; i < count; i++) {
- final View view = mRecView.getChildAt(i);
- cancelThumbnailTask(view);
- }
-
- // Clear any outstanding selection
- mModel.clearSelection();
- }
-
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
-
- final Context context = getActivity();
- final State state = getDisplayState(DirectoryFragment.this);
-
- final RootInfo root = getArguments().getParcelable(EXTRA_ROOT);
- final DocumentInfo doc = getArguments().getParcelable(EXTRA_DOC);
-
- mAdapter = new DocumentsAdapter(context);
- mRecView.setAdapter(mAdapter);
-
- GestureDetector.SimpleOnGestureListener listener =
- new GestureDetector.SimpleOnGestureListener() {
- @Override
- public boolean onSingleTapUp(MotionEvent e) {
- return DirectoryFragment.this.onSingleTapUp(e);
- }
- @Override
- public boolean onDoubleTap(MotionEvent e) {
- Log.d(TAG, "Handling double tap.");
- return DirectoryFragment.this.onDoubleTap(e);
- }
- };
-
- // TODO: instead of inserting the view into the constructor, extract listener-creation code
- // and set the listener on the view after the fact. Then the view doesn't need to be passed
- // into the selection manager which is passed into the model.
- MultiSelectManager selMgr= new MultiSelectManager(
- mRecView,
- listener,
- state.allowMultiple
- ? MultiSelectManager.MODE_MULTIPLE
- : MultiSelectManager.MODE_SINGLE);
- selMgr.addCallback(new SelectionModeListener());
-
- mModel = new Model(context, selMgr, mAdapter);
- mModel.addUpdateListener(mModelUpdateListener);
-
- mType = getArguments().getInt(EXTRA_TYPE);
- mStateKey = buildStateKey(root, doc);
-
- mFragmentTuner = pickFragmentTuner(state);
- mClipper = new DocumentClipper(context);
-
- if (mType == TYPE_RECENT_OPEN) {
- // Hide titles when showing recents for picking images/videos
- mHideGridTitles = MimePredicate.mimeMatches(
- MimePredicate.VISUAL_MIMES, state.acceptMimes);
- } else {
- mHideGridTitles = (doc != null) && doc.isGridTitlesHidden();
- }
-
- final ActivityManager am = (ActivityManager) context.getSystemService(
- Context.ACTIVITY_SERVICE);
- mSvelteRecents = am.isLowRamDevice() && (mType == TYPE_RECENT_OPEN);
-
- mCallbacks = new LoaderCallbacks<DirectoryResult>() {
- @Override
- public Loader<DirectoryResult> onCreateLoader(int id, Bundle args) {
- final String query = getArguments().getString(EXTRA_QUERY);
-
- Uri contentsUri;
- switch (mType) {
- case TYPE_NORMAL:
- contentsUri = DocumentsContract.buildChildDocumentsUri(
- doc.authority, doc.documentId);
- if (state.action == ACTION_MANAGE) {
- contentsUri = DocumentsContract.setManageMode(contentsUri);
- }
- return new DirectoryLoader(
- context, mType, root, doc, contentsUri, state.userSortOrder);
- case TYPE_SEARCH:
- contentsUri = DocumentsContract.buildSearchDocumentsUri(
- root.authority, root.rootId, query);
- if (state.action == ACTION_MANAGE) {
- contentsUri = DocumentsContract.setManageMode(contentsUri);
- }
- return new DirectoryLoader(
- context, mType, root, doc, contentsUri, state.userSortOrder);
- case TYPE_RECENT_OPEN:
- final RootsCache roots = DocumentsApplication.getRootsCache(context);
- return new RecentLoader(context, roots, state);
- default:
- throw new IllegalStateException("Unknown type " + mType);
- }
- }
-
- @Override
- public void onLoadFinished(Loader<DirectoryResult> loader, DirectoryResult result) {
- if (!isAdded()) return;
-
- mModel.update(result);
-
- // Push latest state up to UI
- // TODO: if mode change was racing with us, don't overwrite it
- if (result.mode != MODE_UNKNOWN) {
- state.derivedMode = result.mode;
- }
- state.derivedSortOrder = result.sortOrder;
- ((BaseActivity) context).onStateChanged();
-
- updateDisplayState();
-
- // When launched into empty recents, show drawer
- if (mType == TYPE_RECENT_OPEN && mModel.isEmpty() && !state.stackTouched &&
- context instanceof DocumentsActivity) {
- ((DocumentsActivity) context).setRootsDrawerOpen(true);
- }
-
- // Restore any previous instance state
- final SparseArray<Parcelable> container = state.dirState.remove(mStateKey);
- if (container != null && !getArguments().getBoolean(EXTRA_IGNORE_STATE, false)) {
- getView().restoreHierarchyState(container);
- } else if (mLastSortOrder != state.derivedSortOrder) {
- mRecView.smoothScrollToPosition(0);
- }
-
- mLastSortOrder = state.derivedSortOrder;
- }
-
- @Override
- public void onLoaderReset(Loader<DirectoryResult> loader) {
- mModel.update(null);
- }
- };
-
- // Kick off loader at least once
- getLoaderManager().restartLoader(LOADER_ID, null, mCallbacks);
-
- mFragmentTuner.afterActivityCreated(this);
- updateDisplayState();
- }
-
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
- // There's only one request code right now. Replace this with a switch statement or
- // something more scalable when more codes are added.
- if (requestCode != REQUEST_COPY_DESTINATION) {
- return;
- }
- if (resultCode == Activity.RESULT_CANCELED || data == null) {
- // User pressed the back button or otherwise cancelled the destination pick. Don't
- // proceed with the copy.
- return;
- }
-
- CopyService.start(getActivity(), getDisplayState(this).selectedDocumentsForCopy,
- (DocumentStack) data.getParcelableExtra(Shared.EXTRA_STACK),
- data.getIntExtra(CopyService.EXTRA_TRANSFER_MODE, CopyService.TRANSFER_MODE_NONE));
- }
-
- private int getEventAdapterPosition(MotionEvent e) {
- View view = mRecView.findChildViewUnder(e.getX(), e.getY());
- return view != null ? mRecView.getChildAdapterPosition(view) : RecyclerView.NO_POSITION;
- }
-
- private boolean onSingleTapUp(MotionEvent e) {
- if (Events.isTouchEvent(e) && mModel.getSelection().isEmpty()) {
- int position = getEventAdapterPosition(e);
- if (position != RecyclerView.NO_POSITION) {
- return handleViewItem(position);
- }
- }
- return false;
- }
-
- protected boolean onDoubleTap(MotionEvent e) {
- if (Events.isMouseEvent(e)) {
- Log.d(TAG, "Handling double tap from mouse.");
- int position = getEventAdapterPosition(e);
- if (position != RecyclerView.NO_POSITION) {
- return handleViewItem(position);
- }
- }
- return false;
- }
-
- private boolean handleViewItem(int position) {
- final Cursor cursor = mModel.getItem(position);
- checkNotNull(cursor, "Cursor cannot be null.");
- final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
- final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
- if (isDocumentEnabled(docMimeType, docFlags)) {
- final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(cursor);
- ((BaseActivity) getActivity()).onDocumentPicked(doc, mModel);
- mModel.clearSelection();
- return true;
- }
- return false;
- }
-
- @Override
- public void onStop() {
- super.onStop();
-
- // Remember last scroll location
- final SparseArray<Parcelable> container = new SparseArray<Parcelable>();
- getView().saveHierarchyState(container);
- final State state = getDisplayState(this);
- state.dirState.put(mStateKey, container);
- }
-
- @Override
- public void onResume() {
- super.onResume();
- updateDisplayState();
- }
-
- public void onDisplayStateChanged() {
- updateDisplayState();
- }
-
- public void onUserSortOrderChanged() {
- // Sort order change always triggers reload; we'll trigger state change
- // on the flip side.
- getLoaderManager().restartLoader(LOADER_ID, null, mCallbacks);
- }
-
- public void onUserModeChanged() {
- final ContentResolver resolver = getActivity().getContentResolver();
- final State state = getDisplayState(this);
-
- final RootInfo root = getArguments().getParcelable(EXTRA_ROOT);
- final DocumentInfo doc = getArguments().getParcelable(EXTRA_DOC);
-
- if (root != null && doc != null) {
- final Uri stateUri = RecentsProvider.buildState(
- root.authority, root.rootId, doc.documentId);
- final ContentValues values = new ContentValues();
- values.put(StateColumns.MODE, state.userMode);
-
- new AsyncTask<Void, Void, Void>() {
- @Override
- protected Void doInBackground(Void... params) {
- resolver.insert(stateUri, values);
- return null;
- }
- }.execute();
- }
-
- // Mode change is just visual change; no need to kick loader, and
- // deliver change event immediately.
- state.derivedMode = state.userMode;
- ((BaseActivity) getActivity()).onStateChanged();
-
- updateDisplayState();
- }
-
- private void updateDisplayState() {
- final State state = getDisplayState(this);
-
- if (mLastMode == state.derivedMode && mLastShowSize == state.showSize) return;
- mLastMode = state.derivedMode;
- mLastShowSize = state.showSize;
-
- updateLayout(state.derivedMode);
-
- mRecView.setAdapter(mAdapter);
- }
-
- /**
- * Returns a {@code LayoutManager} for {@code mode}, lazily initializing
- * classes as needed.
- */
- private void updateLayout(int mode) {
- final int thumbSize;
-
- final LayoutManager layout;
- switch (mode) {
- case MODE_GRID:
- thumbSize = getResources().getDimensionPixelSize(R.dimen.grid_width);
- if (mGridLayout == null) {
- mGridLayout = new GridLayoutManager(getContext(), mColumnCount );
- }
- layout = mGridLayout;
- break;
- case MODE_LIST:
- thumbSize = getResources().getDimensionPixelSize(R.dimen.icon_size);
- if (mListLayout == null) {
- mListLayout = new LinearLayoutManager(getContext());
- }
- layout = mListLayout;
- break;
- case MODE_UNKNOWN:
- default:
- throw new IllegalArgumentException("Unsupported layout mode: " + mode);
- }
-
- mRecView.setLayoutManager(layout);
- // TODO: Once b/23691541 is resolved, use a listener within MultiSelectManager instead of
- // imperatively calling this function.
- mModel.mSelectionManager.handleLayoutChanged();
- // setting layout manager automatically invalidates existing ViewHolders.
- mThumbSize = new Point(thumbSize, thumbSize);
- }
-
- private int calculateColumnCount() {
- int cellWidth = getResources().getDimensionPixelSize(R.dimen.grid_width);
- int cellMargin = 2 * getResources().getDimensionPixelSize(R.dimen.grid_item_margin);
- int viewPadding = mRecView.getPaddingLeft() + mRecView.getPaddingRight();
-
- checkState(mRecView.getWidth() > 0);
- int columnCount = Math.max(1,
- (mRecView.getWidth() - viewPadding) / (cellWidth + cellMargin));
-
- return columnCount;
- }
-
- /**
- * Manages the integration between our ActionMode and MultiSelectManager, initiating
- * ActionMode when there is a selection, canceling it when there is no selection,
- * and clearing selection when action mode is explicitly exited by the user.
- */
- private final class SelectionModeListener
- implements MultiSelectManager.Callback, ActionMode.Callback {
-
- private Selection mSelected = new Selection();
- private ActionMode mActionMode;
- private int mNoDeleteCount = 0;
- private Menu mMenu;
-
- @Override
- public boolean onBeforeItemStateChange(int position, boolean selected) {
- // Directories cannot be checked
- if (selected) {
- final Cursor cursor = mModel.getItem(position);
- checkNotNull(cursor, "Cursor cannot be null.");
- final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
- final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
- return isDocumentEnabled(docMimeType, docFlags);
- }
- return true;
- }
-
- @Override
- public void onItemStateChanged(int position, boolean selected) {
- final Cursor cursor = mModel.getItem(position);
- checkNotNull(cursor, "Cursor cannot be null.");
-
- final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
- if ((docFlags & Document.FLAG_SUPPORTS_DELETE) == 0) {
- mNoDeleteCount += selected ? 1 : -1;
- }
- }
-
- @Override
- public void onSelectionChanged() {
- mModel.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) {
- mActionMode.setTitle(TextUtils.formatSelectedCount(mSelected.size()));
- }
- }
-
- // Called when the user exits the action mode
- @Override
- public void onDestroyActionMode(ActionMode mode) {
- if (DEBUG) Log.d(TAG, "Handling action mode destroyed.");
- mActionMode = null;
- // clear selection
- mModel.clearSelection();
- mSelected.clear();
- mNoDeleteCount = 0;
- }
-
- @Override
- public boolean onCreateActionMode(ActionMode mode, Menu menu) {
- mode.getMenuInflater().inflate(R.menu.mode_directory, menu);
- mode.setTitle(TextUtils.formatSelectedCount(mModel.getSelection().size()));
- return mModel.getSelection().size() > 0;
- }
-
- @Override
- public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
- mMenu = menu;
- updateActionMenu();
- return true;
- }
-
- private void updateActionMenu() {
- checkNotNull(mMenu);
- // Delegate update logic to our owning action, since specialized logic is desired.
- mFragmentTuner.updateActionMenu(mMenu, mType, mNoDeleteCount == 0);
- Menus.disableHiddenItems(mMenu);
- }
-
- @Override
- public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
-
- Selection selection = mModel.getSelection(new Selection());
-
- final int id = item.getItemId();
- if (id == R.id.menu_open) {
- openDocuments(selection);
- mode.finish();
- return true;
-
- } else if (id == R.id.menu_share) {
- shareDocuments(selection);
- mode.finish();
- return true;
-
- } else if (id == R.id.menu_delete) {
- // Exit selection mode first, so we avoid deselecting deleted documents.
- mode.finish();
- deleteDocuments(selection);
- return true;
-
- } else if (id == R.id.menu_copy_to) {
- transferDocuments(selection, CopyService.TRANSFER_MODE_COPY);
- mode.finish();
- return true;
-
- } else if (id == R.id.menu_move_to) {
- // Exit selection mode first, so we avoid deselecting deleted documents.
- mode.finish();
- transferDocuments(selection, CopyService.TRANSFER_MODE_MOVE);
- return true;
-
- } else if (id == R.id.menu_copy_to_clipboard) {
- copySelectionToClipboard(selection);
- mode.finish();
- return true;
-
- } else if (id == R.id.menu_select_all) {
- selectAllFiles();
- return true;
-
- } else {
- return false;
- }
- }
- }
-
- private static void cancelThumbnailTask(View view) {
- final ImageView iconThumb = (ImageView) view.findViewById(R.id.icon_thumb);
- if (iconThumb != null) {
- final ThumbnailAsyncTask oldTask = (ThumbnailAsyncTask) iconThumb.getTag();
- if (oldTask != null) {
- oldTask.preempt();
- iconThumb.setTag(null);
- }
- }
- }
-
- private void openDocuments(final Selection selected) {
- new GetDocumentsTask() {
- @Override
- void onDocumentsReady(List<DocumentInfo> docs) {
- // TODO: Implement support in Files activity for opening multiple docs.
- BaseActivity.get(DirectoryFragment.this).onDocumentsPicked(docs);
- }
- }.execute(selected);
- }
-
- private void shareDocuments(final Selection selected) {
- new GetDocumentsTask() {
- @Override
- void onDocumentsReady(List<DocumentInfo> docs) {
- Intent intent;
-
- // Filter out directories - those can't be shared.
- List<DocumentInfo> docsForSend = new ArrayList<>();
- for (DocumentInfo doc: docs) {
- if (!Document.MIME_TYPE_DIR.equals(doc.mimeType)) {
- docsForSend.add(doc);
- }
- }
-
- if (docsForSend.size() == 1) {
- final DocumentInfo doc = docsForSend.get(0);
-
- intent = new Intent(Intent.ACTION_SEND);
- intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- intent.addCategory(Intent.CATEGORY_DEFAULT);
- intent.setType(doc.mimeType);
- intent.putExtra(Intent.EXTRA_STREAM, doc.derivedUri);
-
- } else if (docsForSend.size() > 1) {
- intent = new Intent(Intent.ACTION_SEND_MULTIPLE);
- intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- intent.addCategory(Intent.CATEGORY_DEFAULT);
-
- final ArrayList<String> mimeTypes = new ArrayList<>();
- final ArrayList<Uri> uris = new ArrayList<>();
- for (DocumentInfo doc : docsForSend) {
- mimeTypes.add(doc.mimeType);
- uris.add(doc.derivedUri);
- }
-
- intent.setType(findCommonMimeType(mimeTypes));
- intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
-
- } else {
- return;
- }
-
- intent = Intent.createChooser(intent, getActivity().getText(R.string.share_via));
- startActivity(intent);
- }
- }.execute(selected);
- }
-
- private void deleteDocuments(final Selection selected) {
- Context context = getActivity();
- String message = Shared.getQuantityString(context, R.plurals.deleting, selected.size());
-
- mModel.markForDeletion(selected);
-
- final Activity activity = getActivity();
- Snackbars.makeSnackbar(activity, message, Snackbar.LENGTH_LONG)
- .setAction(
- R.string.undo,
- new android.view.View.OnClickListener() {
- @Override
- public void onClick(View view) {}
- })
- .setCallback(
- new Snackbar.Callback() {
- @Override
- public void onDismissed(Snackbar snackbar, int event) {
- if (event == Snackbar.Callback.DISMISS_EVENT_ACTION) {
- mModel.undoDeletion();
- } else {
- mModel.finalizeDeletion(
- new Model.DeletionListener() {
- @Override
- public void onError() {
- Snackbars.makeSnackbar(
- activity,
- R.string.toast_failed_delete,
- Snackbar.LENGTH_LONG)
- .show();
-
- }
- });
- }
- }
- })
- .show();
- }
-
- private void transferDocuments(final Selection selected, final int mode) {
- // Pop up a dialog to pick a destination. This is inadequate but works for now.
- // TODO: Implement a picker that is to spec.
- final Intent intent = new Intent(
- BaseActivity.DocumentsIntent.ACTION_OPEN_COPY_DESTINATION,
- Uri.EMPTY,
- getActivity(),
- DocumentsActivity.class);
-
- new GetDocumentsTask() {
- @Override
- void onDocumentsReady(List<DocumentInfo> docs) {
- getDisplayState(DirectoryFragment.this).selectedDocumentsForCopy = docs;
-
- boolean directoryCopy = false;
- for (DocumentInfo info : docs) {
- if (Document.MIME_TYPE_DIR.equals(info.mimeType)) {
- directoryCopy = true;
- break;
- }
- }
- intent.putExtra(BaseActivity.DocumentsIntent.EXTRA_DIRECTORY_COPY, directoryCopy);
- intent.putExtra(CopyService.EXTRA_TRANSFER_MODE, mode);
- startActivityForResult(intent, REQUEST_COPY_DESTINATION);
- }
- }.execute(selected);
- }
-
- private static State getDisplayState(Fragment fragment) {
- return ((BaseActivity) fragment.getActivity()).getDisplayState();
- }
-
- // Provide a reference to the views for each data item
- // Complex data items may need more than one view per item, and
- // you provide access to all the views for a data item in a view holder
- private static final class DocumentHolder extends RecyclerView.ViewHolder {
- // each data item is just a string in this case
- public View view;
- public String docId; // The stable document id.
- public DocumentHolder(View view) {
- super(view);
- this.view = view;
- // Setting this using android:focusable in the item layouts doesn't work for list items.
- // So we set it here. Note that touch mode focus is a separate issue - see
- // View.setFocusableInTouchMode and View.isInTouchMode for more info.
- this.view.setFocusable(true);
- }
- }
-
- void showEmptyView() {
- mEmptyView.setVisibility(View.VISIBLE);
- mRecView.setVisibility(View.GONE);
- TextView msg = (TextView) mEmptyView.findViewById(R.id.message);
- msg.setText(R.string.empty);
- // No retry button for the empty view.
- mEmptyView.findViewById(R.id.button_retry).setVisibility(View.GONE);
- }
-
- void showErrorView() {
- mEmptyView.setVisibility(View.VISIBLE);
- mRecView.setVisibility(View.GONE);
- TextView msg = (TextView) mEmptyView.findViewById(R.id.message);
- msg.setText(R.string.query_error);
- // TODO: Enable this once the retry button does something.
- mEmptyView.findViewById(R.id.button_retry).setVisibility(View.GONE);
- }
-
- void showRecyclerView() {
- mEmptyView.setVisibility(View.GONE);
- mRecView.setVisibility(View.VISIBLE);
- }
-
- private final class DocumentsAdapter extends RecyclerView.Adapter<DocumentHolder> {
-
- private final Context mContext;
- private final LayoutInflater mInflater;
-
- public DocumentsAdapter(Context context) {
- mContext = context;
- mInflater = LayoutInflater.from(context);
- }
-
- @Override
- public DocumentHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- final State state = getDisplayState(DirectoryFragment.this);
- final LayoutInflater inflater = LayoutInflater.from(getContext());
- switch (state.derivedMode) {
- case MODE_GRID:
- return new DocumentHolder(inflater.inflate(R.layout.item_doc_grid, parent, false));
- case MODE_LIST:
- return new DocumentHolder(inflater.inflate(R.layout.item_doc_list, parent, false));
- case MODE_UNKNOWN:
- default:
- throw new IllegalStateException("Unsupported layout mode.");
- }
- }
-
- @Override
- public void onBindViewHolder(DocumentHolder holder, int position) {
-
- final Context context = getContext();
- final State state = getDisplayState(DirectoryFragment.this);
- final DocumentInfo doc = getArguments().getParcelable(EXTRA_DOC);
- final RootsCache roots = DocumentsApplication.getRootsCache(context);
- final ThumbnailCache thumbs = DocumentsApplication.getThumbnailsCache(
- context, mThumbSize);
-
- final Cursor cursor = mModel.getItem(position);
- checkNotNull(cursor, "Cursor cannot be null.");
-
- final String docAuthority = getCursorString(cursor, RootCursorWrapper.COLUMN_AUTHORITY);
- final String docRootId = getCursorString(cursor, RootCursorWrapper.COLUMN_ROOT_ID);
- final String docId = getCursorString(cursor, Document.COLUMN_DOCUMENT_ID);
- final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
- final String docDisplayName = getCursorString(cursor, Document.COLUMN_DISPLAY_NAME);
- final long docLastModified = getCursorLong(cursor, Document.COLUMN_LAST_MODIFIED);
- final int docIcon = getCursorInt(cursor, Document.COLUMN_ICON);
- final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
- final String docSummary = getCursorString(cursor, Document.COLUMN_SUMMARY);
- final long docSize = getCursorLong(cursor, Document.COLUMN_SIZE);
-
- holder.docId = docId;
- final View itemView = holder.view;
- itemView.setActivated(mModel.isSelected(position));
-
- final View line1 = itemView.findViewById(R.id.line1);
- final View line2 = itemView.findViewById(R.id.line2);
-
- final ImageView iconMime = (ImageView) itemView.findViewById(R.id.icon_mime);
- final ImageView iconThumb = (ImageView) itemView.findViewById(R.id.icon_thumb);
- final TextView title = (TextView) itemView.findViewById(android.R.id.title);
- final ImageView icon1 = (ImageView) itemView.findViewById(android.R.id.icon1);
- final ImageView icon2 = (ImageView) itemView.findViewById(android.R.id.icon2);
- final TextView summary = (TextView) itemView.findViewById(android.R.id.summary);
- final TextView date = (TextView) itemView.findViewById(R.id.date);
- final TextView size = (TextView) itemView.findViewById(R.id.size);
-
- final ThumbnailAsyncTask oldTask = (ThumbnailAsyncTask) iconThumb.getTag();
- if (oldTask != null) {
- oldTask.preempt();
- iconThumb.setTag(null);
- }
-
- iconMime.animate().cancel();
- iconThumb.animate().cancel();
-
- final boolean supportsThumbnail = (docFlags & Document.FLAG_SUPPORTS_THUMBNAIL) != 0;
- final boolean allowThumbnail = (state.derivedMode == MODE_GRID)
- || MimePredicate.mimeMatches(MimePredicate.VISUAL_MIMES, docMimeType);
- final boolean showThumbnail = supportsThumbnail && allowThumbnail && !mSvelteRecents;
-
- final boolean enabled = isDocumentEnabled(docMimeType, docFlags);
- final float iconAlpha = (state.derivedMode == MODE_LIST && !enabled) ? 0.5f : 1f;
-
- boolean cacheHit = false;
- if (showThumbnail) {
- final Uri uri = DocumentsContract.buildDocumentUri(docAuthority, docId);
- final Bitmap cachedResult = thumbs.get(uri);
- if (cachedResult != null) {
- iconThumb.setImageBitmap(cachedResult);
- cacheHit = true;
- } else {
- iconThumb.setImageDrawable(null);
- // TODO: Hang this off DocumentHolder?
- final ThumbnailAsyncTask task = new ThumbnailAsyncTask(
- uri, iconMime, iconThumb, mThumbSize, iconAlpha);
- iconThumb.setTag(task);
- ProviderExecutor.forAuthority(docAuthority).execute(task);
- }
- }
-
- // Always throw MIME icon into place, even when a thumbnail is being
- // loaded in background.
- if (cacheHit) {
- iconMime.setAlpha(0f);
- iconMime.setImageDrawable(null);
- iconThumb.setAlpha(1f);
- } else {
- iconMime.setAlpha(1f);
- iconThumb.setAlpha(0f);
- iconThumb.setImageDrawable(null);
- iconMime.setImageDrawable(
- getDocumentIcon(mContext, docAuthority, docId, docMimeType, docIcon, state));
- }
-
- boolean hasLine1 = false;
- boolean hasLine2 = false;
-
- final boolean hideTitle = (state.derivedMode == MODE_GRID) && mHideGridTitles;
- if (!hideTitle) {
- title.setText(docDisplayName);
- hasLine1 = true;
- }
-
- Drawable iconDrawable = null;
- if (mType == TYPE_RECENT_OPEN) {
- // We've already had to enumerate roots before any results can
- // be shown, so this will never block.
- final RootInfo root = roots.getRootBlocking(docAuthority, docRootId);
- if (state.derivedMode == MODE_GRID) {
- iconDrawable = root.loadGridIcon(mContext);
- } else {
- iconDrawable = root.loadIcon(mContext);
- }
-
- if (summary != null) {
- final boolean alwaysShowSummary = getResources()
- .getBoolean(R.bool.always_show_summary);
- if (alwaysShowSummary) {
- summary.setText(root.getDirectoryString());
- summary.setVisibility(View.VISIBLE);
- hasLine2 = true;
- } else {
- if (iconDrawable != null && roots.isIconUniqueBlocking(root)) {
- // No summary needed if icon speaks for itself
- summary.setVisibility(View.INVISIBLE);
- } else {
- summary.setText(root.getDirectoryString());
- summary.setVisibility(View.VISIBLE);
- summary.setTextAlignment(TextView.TEXT_ALIGNMENT_TEXT_END);
- hasLine2 = true;
- }
- }
- }
- } else {
- // Directories showing thumbnails in grid mode get a little icon
- // hint to remind user they're a directory.
- if (Document.MIME_TYPE_DIR.equals(docMimeType) && state.derivedMode == MODE_GRID
- && showThumbnail) {
- iconDrawable = IconUtils.applyTintAttr(mContext, R.drawable.ic_doc_folder,
- android.R.attr.textColorPrimaryInverse);
- }
-
- if (summary != null) {
- if (docSummary != null) {
- summary.setText(docSummary);
- summary.setVisibility(View.VISIBLE);
- hasLine2 = true;
- } else {
- summary.setVisibility(View.INVISIBLE);
- }
- }
- }
-
- if (icon1 != null) icon1.setVisibility(View.GONE);
- if (icon2 != null) icon2.setVisibility(View.GONE);
-
- if (iconDrawable != null) {
- if (hasLine1) {
- icon1.setVisibility(View.VISIBLE);
- icon1.setImageDrawable(iconDrawable);
- } else {
- icon2.setVisibility(View.VISIBLE);
- icon2.setImageDrawable(iconDrawable);
- }
- }
-
- if (docLastModified == -1) {
- date.setText(null);
- } else {
- date.setText(formatTime(mContext, docLastModified));
- hasLine2 = true;
- }
-
- if (state.showSize) {
- size.setVisibility(View.VISIBLE);
- if (Document.MIME_TYPE_DIR.equals(docMimeType) || docSize == -1) {
- size.setText(null);
- } else {
- size.setText(Formatter.formatFileSize(mContext, docSize));
- hasLine2 = true;
- }
- } else {
- size.setVisibility(View.GONE);
- }
-
- if (line1 != null) {
- line1.setVisibility(hasLine1 ? View.VISIBLE : View.GONE);
- }
- if (line2 != null) {
- line2.setVisibility(hasLine2 ? View.VISIBLE : View.GONE);
- }
-
- setEnabledRecursive(itemView, enabled);
-
- iconMime.setAlpha(iconAlpha);
- iconThumb.setAlpha(iconAlpha);
- if (icon1 != null) icon1.setAlpha(iconAlpha);
- if (icon2 != null) icon2.setAlpha(iconAlpha);
-
- if (DEBUG_ENABLE_DND) {
- setupDragAndDropOnDocumentView(itemView, cursor);
- }
- }
-
- @Override
- public int getItemCount() {
- return mModel.getItemCount();
- }
-
- }
-
- private static String formatTime(Context context, long when) {
- // TODO: DateUtils should make this easier
- Time then = new Time();
- then.set(when);
- Time now = new Time();
- now.setToNow();
-
- int flags = DateUtils.FORMAT_NO_NOON | DateUtils.FORMAT_NO_MIDNIGHT
- | DateUtils.FORMAT_ABBREV_ALL;
-
- if (then.year != now.year) {
- flags |= DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_SHOW_DATE;
- } else if (then.yearDay != now.yearDay) {
- flags |= DateUtils.FORMAT_SHOW_DATE;
- } else {
- flags |= DateUtils.FORMAT_SHOW_TIME;
- }
-
- return DateUtils.formatDateTime(context, when, flags);
- }
-
- private String findCommonMimeType(List<String> mimeTypes) {
- String[] commonType = mimeTypes.get(0).split("/");
- if (commonType.length != 2) {
- return "*/*";
- }
-
- for (int i = 1; i < mimeTypes.size(); i++) {
- String[] type = mimeTypes.get(i).split("/");
- if (type.length != 2) continue;
-
- if (!commonType[1].equals(type[1])) {
- commonType[1] = "*";
- }
-
- if (!commonType[0].equals(type[0])) {
- commonType[0] = "*";
- commonType[1] = "*";
- break;
- }
- }
-
- return commonType[0] + "/" + commonType[1];
- }
-
- private void setEnabledRecursive(View v, boolean enabled) {
- if (v == null) return;
- if (v.isEnabled() == enabled) return;
- v.setEnabled(enabled);
-
- if (v instanceof ViewGroup) {
- final ViewGroup vg = (ViewGroup) v;
- for (int i = vg.getChildCount() - 1; i >= 0; i--) {
- setEnabledRecursive(vg.getChildAt(i), enabled);
- }
- }
- }
-
- private boolean isDocumentEnabled(String docMimeType, int docFlags) {
- final State state = getDisplayState(DirectoryFragment.this);
-
- // Directories are always enabled
- if (Document.MIME_TYPE_DIR.equals(docMimeType)) {
- return true;
- }
-
- // Read-only files are disabled when creating
- if (state.action == ACTION_CREATE && (docFlags & Document.FLAG_SUPPORTS_WRITE) == 0) {
- return false;
- }
-
- return MimePredicate.mimeMatches(state.acceptMimes, docMimeType);
- }
-
- private void copyFromClipboard() {
- new AsyncTask<Void, Void, List<DocumentInfo>>() {
-
- @Override
- protected List<DocumentInfo> doInBackground(Void... params) {
- return mClipper.getClippedDocuments();
- }
-
- @Override
- protected void onPostExecute(List<DocumentInfo> docs) {
- DocumentInfo destination =
- ((BaseActivity) getActivity()).getCurrentDirectory();
- copyDocuments(docs, destination);
- }
- }.execute();
- }
-
- private void copyFromClipData(final ClipData clipData, final DocumentInfo destination) {
- checkNotNull(clipData);
- new AsyncTask<Void, Void, List<DocumentInfo>>() {
-
- @Override
- protected List<DocumentInfo> doInBackground(Void... params) {
- return mClipper.getDocumentsFromClipData(clipData);
- }
-
- @Override
- protected void onPostExecute(List<DocumentInfo> docs) {
- copyDocuments(docs, destination);
- }
- }.execute();
- }
-
- private void copyDocuments(final List<DocumentInfo> docs, final DocumentInfo destination) {
- if (!canCopy(docs, destination)) {
- Snackbars.makeSnackbar(
- getActivity(),
- R.string.clipboard_files_cannot_paste,
- Snackbar.LENGTH_SHORT)
- .show();
- return;
- }
-
- if (docs.isEmpty()) {
- return;
- }
-
- final DocumentStack curStack = getDisplayState(DirectoryFragment.this).stack;
- DocumentStack tmpStack = new DocumentStack();
- if (destination != null) {
- tmpStack.push(destination);
- tmpStack.addAll(curStack);
- } else {
- tmpStack = curStack;
- }
-
- CopyService.start(getActivity(), docs, tmpStack, CopyService.TRANSFER_MODE_COPY);
- }
-
- private ClipData getClipDataFromDocuments(List<DocumentInfo> docs) {
- Context context = getActivity();
- final ContentResolver resolver = context.getContentResolver();
- ClipData clipData = null;
- for (DocumentInfo doc : docs) {
- final Uri uri = DocumentsContract.buildDocumentUri(doc.authority, doc.documentId);
- if (clipData == null) {
- // TODO: figure out what this string should be.
- // Currently it is not displayed anywhere in the UI, but this might change.
- final String label = "";
- clipData = ClipData.newUri(resolver, label, uri);
- } else {
- // TODO: update list of mime types in ClipData.
- clipData.addItem(new ClipData.Item(uri));
- }
- }
- return clipData;
- }
-
- void copySelectedToClipboard() {
- Selection sel = mModel.getSelection(new Selection());
- copySelectionToClipboard(sel);
- }
-
- void copySelectionToClipboard(Selection items) {
- new GetDocumentsTask() {
- @Override
- void onDocumentsReady(List<DocumentInfo> docs) {
- mClipper.clipDocuments(docs);
- Activity activity = getActivity();
- Snackbars.makeSnackbar(activity,
- activity.getResources().getQuantityString(
- R.plurals.clipboard_files_clipped, docs.size(), docs.size()),
- Snackbar.LENGTH_SHORT).show();
- }
- }.execute(items);
- }
-
- void pasteFromClipboard() {
- copyFromClipboard();
- getActivity().invalidateOptionsMenu();
- }
-
- /**
- * Returns true if the list of files can be copied to destination. Note that this
- * is a policy check only. Currently the method does not attempt to verify
- * available space or any other environmental aspects possibly resulting in
- * failure to copy.
- *
- * @return true if the list of files can be copied to destination.
- */
- boolean canCopy(List<DocumentInfo> files, DocumentInfo dest) {
- BaseActivity activity = (BaseActivity) getActivity();
-
- final RootInfo root = activity.getCurrentRoot();
-
- // Can't copy folders to Downloads.
- if (root.isDownloads()) {
- for (DocumentInfo docs : files) {
- if (docs.isDirectory()) {
- return false;
- }
- }
- }
-
- return dest != null && dest.isDirectory() && dest.isCreateSupported();
- }
-
- void selectAllFiles() {
- boolean changed = mModel.selectAll();
- if (changed) {
- updateDisplayState();
- }
- }
-
- /**
- * Scrolls to the top of the file list and focuses the first file.
- */
- void focusFirstFile() {
- focusFile(0);
- }
-
- /**
- * Scrolls to the bottom of the file list and focuses the last file.
- */
- void focusLastFile() {
- focusFile(mAdapter.getItemCount() - 1);
- }
-
- /**
- * Scrolls to and then focuses on the file at the given position.
- */
- private void focusFile(final int pos) {
- // Don't smooth scroll; that taxes the system unnecessarily and makes the scroll handling
- // logic below more complicated.
- mRecView.scrollToPosition(pos);
-
- // If the item is already in view, focus it; otherwise, set a one-time listener to focus it
- // when the scroll is completed.
- RecyclerView.ViewHolder vh = mRecView.findViewHolderForAdapterPosition(pos);
- if (vh != null) {
- vh.itemView.requestFocus();
- } else {
- mRecView.addOnScrollListener(
- new RecyclerView.OnScrollListener() {
- @Override
- public void onScrolled(RecyclerView view, int dx, int dy) {
- view.findViewHolderForAdapterPosition(pos).itemView.requestFocus();
- view.removeOnScrollListener(this);
- }
- });
- }
- }
-
- private void setupDragAndDropOnDirectoryView(View view) {
- // Listen for drops on non-directory items and empty space.
- view.setOnDragListener(mOnDragListener);
- }
-
- private void setupDragAndDropOnDocumentView(View view, Cursor cursor) {
- final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
- if (Document.MIME_TYPE_DIR.equals(docMimeType)) {
- // Make a directory item a drop target. Drop on non-directories and empty space
- // is handled at the list/grid view level.
- view.setOnDragListener(mOnDragListener);
- }
-
- // Temporary: attaching the listener to the title only.
- // Attaching to the entire item conflicts with the item long click handler responsible
- // for item selection.
- final View title = view.findViewById(android.R.id.title);
- title.setOnLongClickListener(mLongClickListener);
- }
-
- private View.OnDragListener mOnDragListener = new View.OnDragListener() {
- @Override
- public boolean onDrag(View v, DragEvent event) {
- switch (event.getAction()) {
- case DragEvent.ACTION_DRAG_STARTED:
- // TODO: Check if the event contains droppable data.
- return true;
-
- // TODO: Highlight potential drop target directory?
- // TODO: Expand drop target directory on hover?
- case DragEvent.ACTION_DRAG_ENTERED:
- case DragEvent.ACTION_DRAG_LOCATION:
- case DragEvent.ACTION_DRAG_EXITED:
- case DragEvent.ACTION_DRAG_ENDED:
- return true;
-
- case DragEvent.ACTION_DROP:
- int dstPosition = mRecView.getChildAdapterPosition(getContainingItemView(v));
- DocumentInfo dstDir = null;
- if (dstPosition != android.widget.AdapterView.INVALID_POSITION) {
- Cursor dstCursor = mModel.getItem(dstPosition);
- checkNotNull(dstCursor, "Cursor cannot be null.");
- dstDir = DocumentInfo.fromDirectoryCursor(dstCursor);
- // TODO: Do not drop into the directory where the documents came from.
- }
- copyFromClipData(event.getClipData(), dstDir);
- return true;
- }
- return false;
- }
- };
-
- private View getContainingItemView(View view) {
- while (true) {
- if (view.getLayoutParams() instanceof RecyclerView.LayoutParams) {
- return view;
- }
- ViewParent parent = view.getParent();
- if (parent == null || !(parent instanceof View)) {
- return null;
- }
- view = (View) parent;
- }
- }
-
- private View.OnLongClickListener mLongClickListener = new View.OnLongClickListener() {
- @Override
- public boolean onLongClick(View v) {
- final List<DocumentInfo> docs = getDraggableDocuments(v);
- if (docs.isEmpty()) {
- return false;
- }
- v.startDrag(
- getClipDataFromDocuments(docs),
- new DrawableShadowBuilder(getDragShadowIcon(docs)),
- null,
- View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ |
- View.DRAG_FLAG_GLOBAL_URI_WRITE
- );
- return true;
- }
- };
-
- private List<DocumentInfo> getDraggableDocuments(View currentItemView) {
- int position = mRecView.getChildAdapterPosition(getContainingItemView(currentItemView));
- if (position == android.widget.AdapterView.INVALID_POSITION) {
- return Collections.EMPTY_LIST;
- }
-
- final List<DocumentInfo> selectedDocs = mModel.getSelectedDocuments();
- if (!selectedDocs.isEmpty()) {
- if (!mModel.isSelected(position)) {
- // There is a selection that does not include the current item, drag nothing.
- return Collections.EMPTY_LIST;
- }
- return selectedDocs;
- }
-
- final Cursor cursor = mModel.getItem(position);
- checkNotNull(cursor, "Cursor cannot be null.");
- final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(cursor);
-
- return Lists.newArrayList(doc);
- }
-
- private Drawable getDragShadowIcon(List<DocumentInfo> docs) {
- if (docs.size() == 1) {
- final DocumentInfo doc = docs.get(0);
- return getDocumentIcon(getActivity(), doc.authority, doc.documentId,
- doc.mimeType, doc.icon, getDisplayState(this));
- }
- return getActivity().getDrawable(R.drawable.ic_doc_generic);
- }
-
- public static Drawable getDocumentIcon(Context context, String docAuthority, String docId,
- String docMimeType, int docIcon, State state) {
- if (docIcon != 0) {
- return IconUtils.loadPackageIcon(context, docAuthority, docIcon);
- } else {
- return IconUtils.loadMimeIcon(context, docMimeType, docAuthority, docId,
- state.derivedMode);
- }
- }
-
- private static class ThumbnailAsyncTask extends AsyncTask<Uri, Void, Bitmap>
- implements Preemptable {
- private final Uri mUri;
- private final ImageView mIconMime;
- private final ImageView mIconThumb;
- private final Point mThumbSize;
- private final float mTargetAlpha;
- private final CancellationSignal mSignal;
-
- public ThumbnailAsyncTask(Uri uri, ImageView iconMime, ImageView iconThumb, Point thumbSize,
- float targetAlpha) {
- mUri = uri;
- mIconMime = iconMime;
- mIconThumb = iconThumb;
- mThumbSize = thumbSize;
- mTargetAlpha = targetAlpha;
- mSignal = new CancellationSignal();
- }
-
- @Override
- public void preempt() {
- cancel(false);
- mSignal.cancel();
- }
-
- @Override
- protected Bitmap doInBackground(Uri... params) {
- if (isCancelled()) return null;
-
- final Context context = mIconThumb.getContext();
- final ContentResolver resolver = context.getContentResolver();
-
- ContentProviderClient client = null;
- Bitmap result = null;
- try {
- client = DocumentsApplication.acquireUnstableProviderOrThrow(
- resolver, mUri.getAuthority());
- result = DocumentsContract.getDocumentThumbnail(client, mUri, mThumbSize, mSignal);
- if (result != null) {
- final ThumbnailCache thumbs = DocumentsApplication.getThumbnailsCache(
- context, mThumbSize);
- thumbs.put(mUri, result);
- }
- } catch (Exception e) {
- if (!(e instanceof OperationCanceledException)) {
- Log.w(TAG, "Failed to load thumbnail for " + mUri + ": " + e);
- }
- } finally {
- ContentProviderClient.releaseQuietly(client);
- }
- return result;
- }
-
- @Override
- protected void onPostExecute(Bitmap result) {
- if (mIconThumb.getTag() == this && result != null) {
- mIconThumb.setTag(null);
- mIconThumb.setImageBitmap(result);
-
- mIconMime.setAlpha(mTargetAlpha);
- mIconMime.animate().alpha(0f).start();
- mIconThumb.setAlpha(0f);
- mIconThumb.animate().alpha(mTargetAlpha).start();
- }
- }
- }
-
- private class DrawableShadowBuilder extends View.DragShadowBuilder {
-
- 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);
- }
-
- @Override
- public void onProvideShadowMetrics(
- Point shadowSize, Point shadowTouchPoint) {
- shadowSize.set(mShadowDimension, mShadowDimension);
- shadowTouchPoint.set(mShadowDimension / 2, mShadowDimension / 2);
- }
-
- @Override
- public void onDrawShadow(Canvas canvas) {
- mShadow.draw(canvas);
- }
- }
-
- private FragmentTuner pickFragmentTuner(final State state) {
- return state.action == ACTION_BROWSE
- ? new FilesTuner()
- : new DefaultTuner(state.action);
- }
-
- /**
- * Interface for specializing the Fragment for the "host" Activity.
- * Feel free to expand the role of this class to handle other specializations.
- */
- private interface FragmentTuner {
- void updateActionMenu(Menu menu, int dirType, boolean canDelete);
- void afterActivityCreated(DirectoryFragment fragment);
- }
-
- /**
- * Abstract task providing support for loading documents *off*
- * the main thread. And if it isn't obvious, creating a list
- * of documents (especially large lists) can be pretty expensive.
- */
- private abstract class GetDocumentsTask
- extends AsyncTask<Selection, Void, List<DocumentInfo>> {
- @Override
- protected final List<DocumentInfo> doInBackground(Selection... selected) {
- return mModel.getDocuments(selected[0]);
- }
-
- @Override
- protected final void onPostExecute(List<DocumentInfo> docs) {
- onDocumentsReady(docs);
- }
-
- abstract void onDocumentsReady(List<DocumentInfo> docs);
- }
-
- /**
- * Provides support for Platform specific specializations of DirectoryFragment.
- */
- private static final class DefaultTuner implements FragmentTuner {
-
- private final boolean mManaging;
-
- public DefaultTuner(int action) {
- mManaging = (action == ACTION_MANAGE);
- }
-
- @Override
- public void updateActionMenu(Menu menu, int dirType, boolean canDelete) {
- boolean copyEnabled = mManaging && dirType != TYPE_RECENT_OPEN;
- // TODO: The selection needs to be deletable.
- boolean moveEnabled =
- SystemProperties.getBoolean("debug.documentsui.enable_move", false);
- menu.findItem(R.id.menu_copy_to_clipboard).setEnabled(copyEnabled);
-
- final MenuItem open = menu.findItem(R.id.menu_open);
- final MenuItem share = menu.findItem(R.id.menu_share);
- final MenuItem delete = menu.findItem(R.id.menu_delete);
- final MenuItem copyTo = menu.findItem(R.id.menu_copy_to);
- final MenuItem moveTo = menu.findItem(R.id.menu_move_to);
-
- open.setVisible(!mManaging);
- share.setVisible(mManaging);
- delete.setVisible(mManaging && canDelete);
- copyTo.setVisible(copyEnabled);
- copyTo.setEnabled(copyEnabled);
- moveTo.setVisible(moveEnabled);
- moveTo.setEnabled(moveEnabled);
- }
-
- @Override
- public void afterActivityCreated(DirectoryFragment fragment) {}
- }
-
- /**
- * Provides support for Files activity specific specializations of DirectoryFragment.
- */
- private static final class FilesTuner implements FragmentTuner {
- @Override
- public void updateActionMenu(Menu menu, int dirType, boolean canDelete) {
-
- menu.findItem(R.id.menu_copy_to_clipboard).setEnabled(dirType != TYPE_RECENT_OPEN);
-
- menu.findItem(R.id.menu_share).setVisible(true);
- menu.findItem(R.id.menu_delete).setVisible(canDelete);
-
- menu.findItem(R.id.menu_open).setVisible(false);
- menu.findItem(R.id.menu_copy_to).setVisible(true);
- menu.findItem(R.id.menu_move_to).setVisible(true);
- }
-
- @Override
- public void afterActivityCreated(DirectoryFragment fragment) {}
- }
-
- /**
- * The data model for the current loaded directory.
- */
- @VisibleForTesting
- public static final class Model implements DocumentContext {
- private MultiSelectManager mSelectionManager;
- private RecyclerView.Adapter<?> mViewAdapter;
- private Context mContext;
- private int mCursorCount;
- private boolean mIsLoading;
- private SparseBooleanArray mMarkedForDeletion = new SparseBooleanArray();
- private UpdateListener mUpdateListener;
- @Nullable private Cursor mCursor;
- @Nullable private String info;
- @Nullable private String error;
-
- Model(Context context, MultiSelectManager selectionManager,
- RecyclerView.Adapter<?> viewAdapter) {
- mContext = context;
- mSelectionManager = selectionManager;
- mViewAdapter = viewAdapter;
- }
-
- /**
- * Selects all files in the current directory.
- * @return true if the selection state changed for any files.
- */
- boolean selectAll() {
- return mSelectionManager.setItemsSelected(0, mCursorCount, true);
- }
-
- /**
- * Clones the current selection into the given Selection object.
- * @param selection
- * @return The selection that was passed in, for convenience.
- */
- Selection getSelection(Selection selection) {
- return mSelectionManager.getSelection(selection);
- }
-
- /**
- * @return The current selection (the live instance, not a copy).
- */
- Selection getSelection() {
- return mSelectionManager.getSelection();
- }
-
- boolean isSelected(int position) {
- return mSelectionManager.getSelection().contains(position);
- }
-
- void clearSelection() {
- mSelectionManager.clearSelection();
- }
-
- void update(DirectoryResult result) {
- if (DEBUG) Log.i(TAG, "Updating model with new result set.");
-
- if (result == null) {
- mCursor = null;
- mCursorCount = 0;
- info = null;
- error = null;
- mIsLoading = false;
- mUpdateListener.onModelUpdate(this);
- return;
- }
-
- if (result.exception != null) {
- Log.e(TAG, "Error while loading directory contents", result.exception);
- mUpdateListener.onModelUpdateFailed(result.exception);
- return;
- }
-
- mCursor = result.cursor;
- mCursorCount = mCursor.getCount();
-
- final Bundle extras = mCursor.getExtras();
- if (extras != null) {
- info = extras.getString(DocumentsContract.EXTRA_INFO);
- error = extras.getString(DocumentsContract.EXTRA_ERROR);
- mIsLoading = extras.getBoolean(DocumentsContract.EXTRA_LOADING, false);
- }
-
- mUpdateListener.onModelUpdate(this);
- }
-
- int getItemCount() {
- return mCursorCount - mMarkedForDeletion.size();
- }
-
- Cursor getItem(int position) {
- // Items marked for deletion are masked out of the UI. To do this, for every marked
- // item whose position is less than the requested item position, advance the requested
- // position by 1.
- final int originalPos = position;
- final int size = mMarkedForDeletion.size();
- for (int i = 0; i < size; ++i) {
- // It'd be more concise, but less efficient, to iterate over positions while calling
- // mMarkedForDeletion.get. Instead, iterate over deleted entries.
- if (mMarkedForDeletion.keyAt(i) <= position && mMarkedForDeletion.valueAt(i)) {
- ++position;
- }
- }
-
- if (DEBUG && position != originalPos) {
- Log.d(TAG, "Item position adjusted for deletion. Original: " + originalPos
- + " Adjusted: " + position);
- }
-
- if (position >= mCursorCount) {
- throw new IndexOutOfBoundsException("Attempt to retrieve " + position + " of " +
- mCursorCount + " items");
- }
-
- mCursor.moveToPosition(position);
- return mCursor;
- }
-
- private boolean isEmpty() {
- return mCursorCount == 0;
- }
-
- private boolean isLoading() {
- return mIsLoading;
- }
-
- private List<DocumentInfo> getSelectedDocuments() {
- Selection sel = getSelection(new Selection());
- return getDocuments(sel);
- }
-
- List<DocumentInfo> getDocuments(Selection items) {
- final int size = (items != null) ? items.size() : 0;
-
- final List<DocumentInfo> docs = new ArrayList<>(size);
- for (int i = 0; i < size; i++) {
- final Cursor cursor = getItem(items.get(i));
- checkNotNull(cursor, "Cursor cannot be null.");
- final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(cursor);
- docs.add(doc);
- }
- return docs;
- }
-
- @Override
- public Cursor getCursor() {
- if (Looper.myLooper() != Looper.getMainLooper()) {
- throw new IllegalStateException("Can't call getCursor from non-main thread.");
- }
- return mCursor;
- }
-
- List<DocumentInfo> getDocumentsMarkedForDeletion() {
- final int size = mMarkedForDeletion.size();
- List<DocumentInfo> docs = new ArrayList<>(size);
-
- for (int i = 0; i < size; ++i) {
- final int position = mMarkedForDeletion.keyAt(i);
- checkState(position < mCursorCount);
- mCursor.moveToPosition(position);
- final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(mCursor);
- docs.add(doc);
- }
- return docs;
- }
-
- /**
- * Marks the given files for deletion. This will remove them from the UI. Clients must then
- * call either {@link #undoDeletion()} or {@link #finalizeDeletion()} to cancel or confirm
- * the deletion, respectively. Only one deletion operation is allowed at a time.
- *
- * @param selected A selection representing the files to delete.
- */
- void markForDeletion(Selection selected) {
- // Only one deletion operation at a time.
- checkState(mMarkedForDeletion.size() == 0);
- // There should never be more to delete than what exists.
- checkState(mCursorCount >= selected.size());
-
- final int size = selected.size();
- for (int i = 0; i < size; ++i) {
- int position = selected.get(i);
- if (DEBUG) Log.d(TAG, "Marked position " + position + " for deletion");
- mMarkedForDeletion.append(position, true);
- mViewAdapter.notifyItemRemoved(position);
- }
- }
-
- /**
- * Cancels an ongoing deletion operation. All files currently marked for deletion will be
- * unmarked, and restored in the UI. See {@link #markForDeletion(Selection)}.
- */
- void undoDeletion() {
- // Iterate over deleted items, temporarily marking them false in the deletion list, and
- // re-adding them to the UI.
- final int size = mMarkedForDeletion.size();
- for (int i = 0; i < size; ++i) {
- final int position = mMarkedForDeletion.keyAt(i);
- mMarkedForDeletion.put(position, false);
- mViewAdapter.notifyItemInserted(position);
- }
-
- // Then, clear the deletion list.
- mMarkedForDeletion.clear();
- }
-
- /**
- * Finalizes an ongoing deletion operation. All files currently marked for deletion will be
- * deleted. See {@link #markForDeletion(Selection)}.
- *
- * @param view The view which will be used to interact with the user (e.g. surfacing
- * snackbars) for errors, info, etc.
- */
- void finalizeDeletion(DeletionListener listener) {
- final ContentResolver resolver = mContext.getContentResolver();
- DeleteFilesTask task = new DeleteFilesTask(resolver, listener);
- task.execute();
- }
-
- /**
- * A Task which collects the DocumentInfo for documents that have been marked for deletion,
- * and actually deletes them.
- */
- private class DeleteFilesTask extends AsyncTask<Void, Void, List<DocumentInfo>> {
- private ContentResolver mResolver;
- private DeletionListener mListener;
-
- /**
- * @param resolver A ContentResolver for performing the actual file deletions.
- * @param errorCallback A Runnable that is executed in the event that one or more errors
- * occured while copying files. Execution will occur on the UI thread.
- */
- public DeleteFilesTask(ContentResolver resolver, DeletionListener listener) {
- mResolver = resolver;
- mListener = listener;
- }
-
- @Override
- protected List<DocumentInfo> doInBackground(Void... params) {
- return getDocumentsMarkedForDeletion();
- }
-
- @Override
- protected void onPostExecute(List<DocumentInfo> docs) {
- boolean hadTrouble = false;
- for (DocumentInfo doc : docs) {
- if (!doc.isDeleteSupported()) {
- Log.w(TAG, doc + " could not be deleted. Skipping...");
- hadTrouble = true;
- continue;
- }
-
- ContentProviderClient client = null;
- try {
- if (DEBUG) Log.d(TAG, "Deleting: " + doc.displayName);
- client = DocumentsApplication.acquireUnstableProviderOrThrow(
- mResolver, doc.derivedUri.getAuthority());
- DocumentsContract.deleteDocument(client, doc.derivedUri);
- } catch (Exception e) {
- Log.w(TAG, "Failed to delete " + doc);
- hadTrouble = true;
- } finally {
- ContentProviderClient.releaseQuietly(client);
- }
- }
-
- if (hadTrouble) {
- // TODO show which files failed? b/23720103
- mListener.onError();
- if (DEBUG) Log.d(TAG, "Deletion task completed. Some deletions failed.");
- } else {
- if (DEBUG) Log.d(TAG, "Deletion task completed successfully.");
- }
- mMarkedForDeletion.clear();
-
- mListener.onCompletion();
- }
- }
-
- static class DeletionListener {
- /**
- * Called when deletion has completed (regardless of whether an error occurred).
- */
- void onCompletion() {}
-
- /**
- * Called at the end of a deletion operation that produced one or more errors.
- */
- void onError() {}
- }
-
- void addUpdateListener(UpdateListener listener) {
- checkState(mUpdateListener == null);
- mUpdateListener = listener;
- }
-
- static class UpdateListener {
- /**
- * Called when a successful update has occurred.
- */
- void onModelUpdate(Model model) {}
-
- /**
- * Called when an update has been attempted but failed.
- */
- void onModelUpdateFailed(Exception e) {}
- }
- }
-
- private class ModelUpdateListener extends Model.UpdateListener {
- @Override
- public void onModelUpdate(Model model) {
- if (model.info != null || model.error != null) {
- mMessageBar.setInfo(model.info);
- mMessageBar.setError(model.error);
- mMessageBar.show();
- }
-
- mProgressBar.setVisibility(model.isLoading() ? View.VISIBLE : View.GONE);
-
- if (model.isEmpty()) {
- showEmptyView();
- } else {
- showRecyclerView();
- mAdapter.notifyDataSetChanged();
- }
- }
-
- @Override
- public void onModelUpdateFailed(Exception e) {
- showErrorView();
- }
- }
-}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
index bb82b38..b0bbec3 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
@@ -17,11 +17,9 @@
package com.android.documentsui;
import static com.android.documentsui.Shared.TAG;
-import static com.android.documentsui.State.MODE_UNKNOWN;
import static com.android.documentsui.State.SORT_ORDER_DISPLAY_NAME;
import static com.android.documentsui.State.SORT_ORDER_LAST_MODIFIED;
import static com.android.documentsui.State.SORT_ORDER_SIZE;
-import static com.android.documentsui.State.SORT_ORDER_UNKNOWN;
import static com.android.documentsui.model.DocumentInfo.getCursorInt;
import android.content.AsyncTaskLoader;
@@ -38,6 +36,7 @@
import android.util.Log;
import com.android.documentsui.RecentsProvider.StateColumns;
+import com.android.documentsui.dirlist.DirectoryFragment;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.RootInfo;
@@ -45,23 +44,6 @@
import java.io.FileNotFoundException;
-class DirectoryResult implements AutoCloseable {
- ContentProviderClient client;
- Cursor cursor;
- Exception exception;
-
- int mode = MODE_UNKNOWN;
- int sortOrder = SORT_ORDER_UNKNOWN;
-
- @Override
- public void close() {
- IoUtils.closeQuietly(cursor);
- ContentProviderClient.releaseQuietly(client);
- cursor = null;
- client = null;
- }
-}
-
public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
private static final String[] SEARCH_REJECT_MIMES = new String[] { Document.MIME_TYPE_DIR };
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryResult.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryResult.java
new file mode 100644
index 0000000..e7e4f73
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryResult.java
@@ -0,0 +1,42 @@
+/*
+ * 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.documentsui;
+
+import static com.android.documentsui.State.MODE_UNKNOWN;
+import static com.android.documentsui.State.SORT_ORDER_UNKNOWN;
+
+import android.content.ContentProviderClient;
+import android.database.Cursor;
+
+import libcore.io.IoUtils;
+
+public class DirectoryResult implements AutoCloseable {
+ ContentProviderClient client;
+ public Cursor cursor;
+ public Exception exception;
+
+ public int mode = MODE_UNKNOWN;
+ public int sortOrder = SORT_ORDER_UNKNOWN;
+
+ @Override
+ public void close() {
+ IoUtils.closeQuietly(cursor);
+ ContentProviderClient.releaseQuietly(client);
+ cursor = null;
+ client = null;
+ }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentClipper.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentClipper.java
index 6ba07fbb..b3c2846 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentClipper.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentClipper.java
@@ -39,7 +39,7 @@
* ClipboardManager wrapper class providing higher level logical
* support for dealing with Documents.
*/
-final class DocumentClipper {
+public final class DocumentClipper {
private static final String TAG = "DocumentClipper";
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index 6b428f5..13c481c 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -16,12 +16,12 @@
package com.android.documentsui;
-import static com.android.documentsui.DirectoryFragment.ANIM_NONE;
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_COPY_DESTINATION;
import static com.android.documentsui.State.ACTION_OPEN_TREE;
+import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_NONE;
import android.app.Activity;
import android.app.Fragment;
@@ -53,6 +53,7 @@
import com.android.documentsui.RecentsProvider.RecentColumns;
import com.android.documentsui.RecentsProvider.ResumeColumns;
+import com.android.documentsui.dirlist.DirectoryFragment;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.DurableUtils;
import com.android.documentsui.model.RootInfo;
@@ -86,7 +87,7 @@
mShowAsDialog = res.getBoolean(R.bool.show_as_dialog);
if (!mShowAsDialog) {
- setTheme(R.style.DocumentsNonDialogTheme);
+ setTheme(R.style.DocumentsFullScreenTheme);
}
if (mShowAsDialog) {
@@ -175,7 +176,7 @@
state.directoryCopy = intent.getBooleanExtra(
BaseActivity.DocumentsIntent.EXTRA_DIRECTORY_COPY, false);
state.transferMode = intent.getIntExtra(CopyService.EXTRA_TRANSFER_MODE,
- CopyService.TRANSFER_MODE_NONE);
+ CopyService.TRANSFER_MODE_COPY);
}
return state;
@@ -337,7 +338,7 @@
if (mState.action == ACTION_CREATE) {
final FragmentManager fm = getFragmentManager();
- SaveFragment.get(fm).setSaveEnabled(cwd != null && cwd.isCreateSupported());
+ SaveFragment.get(fm).prepareForDirectory(cwd);
}
Menus.disableHiddenItems(menu);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Events.java b/packages/DocumentsUI/src/com/android/documentsui/Events.java
index c06ea0a..49dae3d 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Events.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Events.java
@@ -25,71 +25,62 @@
/**
* Utility code for dealing with MotionEvents.
*/
-final class Events {
+public final class Events {
/**
* Returns true if event was triggered by a mouse.
*/
- static boolean isMouseEvent(MotionEvent e) {
+ public static boolean isMouseEvent(MotionEvent e) {
return isMouseType(e.getToolType(0));
}
/**
* Returns true if event was triggered by a finger or stylus touch.
*/
- static boolean isTouchEvent(MotionEvent e) {
- return isTouchType(e.getToolType(0))
- // Temporarily work around uiautomator's missing tool type support.
- || isUnknownType(e.getToolType(0));
+ public static boolean isTouchEvent(MotionEvent e) {
+ return isTouchType(e.getToolType(0));
}
/**
* Returns true if event was triggered by a mouse.
*/
- static boolean isMouseType(int toolType) {
+ public static boolean isMouseType(int toolType) {
return toolType == MotionEvent.TOOL_TYPE_MOUSE;
}
/**
- * Returns true if type is finger or stylus.
- */
- static boolean isTouchType(int toolType) {
- return toolType == MotionEvent.TOOL_TYPE_FINGER
- || toolType == MotionEvent.TOOL_TYPE_STYLUS;
- }
-
- /**
- * Returns true if type is unknown.
- */
- static boolean isUnknownType(int toolType) {
- return toolType == MotionEvent.TOOL_TYPE_UNKNOWN;
- }
-
- /**
* Returns true if event was triggered by a finger or stylus touch.
*/
- static boolean isActionDown(MotionEvent e) {
+ public static boolean isTouchType(int toolType) {
+ return toolType == MotionEvent.TOOL_TYPE_FINGER
+ || toolType == MotionEvent.TOOL_TYPE_STYLUS;
+ }
+
+ /**
+ * Returns true if event was triggered by a finger or stylus touch.
+ */
+ public static boolean isActionDown(MotionEvent e) {
return e.getActionMasked() == MotionEvent.ACTION_DOWN;
}
/**
* Returns true if event was triggered by a finger or stylus touch.
*/
- static boolean isActionUp(MotionEvent e) {
+ public static boolean isActionUp(MotionEvent e) {
return e.getActionMasked() == MotionEvent.ACTION_UP;
}
/**
* Returns true if the shift is pressed.
*/
- boolean isShiftPressed(MotionEvent e) {
+ public boolean isShiftPressed(MotionEvent e) {
return hasShiftBit(e.getMetaState());
}
/**
* Returns true if the "SHIFT" bit is set.
*/
- static boolean hasShiftBit(int metaState) {
+ public static boolean hasShiftBit(int metaState) {
return (metaState & KeyEvent.META_SHIFT_ON) != 0;
}
@@ -97,7 +88,7 @@
* A facade over MotionEvent primarily designed to permit for unit testing
* of related code.
*/
- interface InputEvent {
+ public interface InputEvent {
boolean isMouseEvent();
boolean isPrimaryButtonPressed();
boolean isSecondaryButtonPressed();
@@ -118,7 +109,7 @@
int getItemPosition();
}
- static final class MotionInputEvent implements InputEvent {
+ public static final class MotionInputEvent implements InputEvent {
private final MotionEvent mEvent;
private final RecyclerView mView;
private final int mPosition;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
index 1330b3c..840f7fa 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -16,8 +16,8 @@
package com.android.documentsui;
-import static com.android.documentsui.DirectoryFragment.ANIM_NONE;
import static com.android.documentsui.Shared.DEBUG;
+import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_NONE;
import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.internal.util.Preconditions.checkState;
@@ -44,6 +44,7 @@
import android.widget.Toolbar;
import com.android.documentsui.RecentsProvider.ResumeColumns;
+import com.android.documentsui.dirlist.DirectoryFragment;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.DocumentStack;
import com.android.documentsui.model.DurableUtils;
@@ -121,7 +122,7 @@
// Show a failure dialog if there was a failed operation.
final int failure = intent.getIntExtra(CopyService.EXTRA_FAILURE, 0);
final int transferMode = intent.getIntExtra(CopyService.EXTRA_TRANSFER_MODE,
- CopyService.TRANSFER_MODE_NONE);
+ CopyService.TRANSFER_MODE_COPY);
if (failure != 0) {
final ArrayList<DocumentInfo> failedSrcList =
intent.getParcelableArrayListExtra(CopyService.EXTRA_SRC_LIST);
@@ -323,26 +324,36 @@
private void openDocument(DocumentInfo doc, @Nullable DocumentContext siblings) {
Intent intent = null;
if (siblings != null) {
- QuickViewIntentBuilder builder =
- new QuickViewIntentBuilder(getPackageManager(), doc, siblings);
+ QuickViewIntentBuilder builder = new QuickViewIntentBuilder(
+ getPackageManager(), getResources(), doc, siblings);
intent = builder.build();
}
- // fallback to traditional VIEW action...
- if (intent == null) {
- intent = new Intent(Intent.ACTION_VIEW);
- intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- intent.setData(doc.derivedUri);
+ if (intent != null) {
+ // TODO: un-work around issue b/24963914. Should be fixed soon.
+ try {
+ startActivity(intent);
+ return;
+ } catch (SecurityException e) {
+ // Carry on to regular view mode.
+ Log.e(TAG, "Caught security error: " + e.getLocalizedMessage());
+ }
}
+ // Fallback to traditional VIEW action...
+ intent = new Intent(Intent.ACTION_VIEW);
+ intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ intent.setData(doc.derivedUri);
+
if (DEBUG && intent.getClipData() != null) {
Log.d(TAG, "Starting intent w/ clip data: " + intent.getClipData());
}
try {
startActivity(intent);
- } catch (ActivityNotFoundException ex2) {
- Snackbars.makeSnackbar(this, R.string.toast_no_application, Snackbar.LENGTH_SHORT).show();
+ } catch (ActivityNotFoundException e) {
+ Snackbars.makeSnackbar(
+ this, R.string.toast_no_application, Snackbar.LENGTH_SHORT).show();
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/GridItem.java b/packages/DocumentsUI/src/com/android/documentsui/GridItem.java
deleted file mode 100644
index 990dca7..0000000
--- a/packages/DocumentsUI/src/com/android/documentsui/GridItem.java
+++ /dev/null
@@ -1,51 +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.documentsui;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.FrameLayout;
-
-/**
- * A FrameLayout subclass used by DirectoryFragment. Ensures that the resulting grid item is always
- * square.
- */
-public class GridItem extends FrameLayout {
- public GridItem(Context context) {
- super(context);
- }
-
- public GridItem(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public GridItem(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- @Override
- public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- // Grid layout uses item width to figure out the number of columns, then dynamically fits
- // rows into the view. The upshot of this is that changing the item width will mess up the
- // grid layout - so to make the items square, throw out the height and use the width for
- // both dimensions. The grid layout will correctly adjust the row height.
- //
- // Note that this code will need to be changed if the layout manager's orientation is
- // changed from VERTICAL to HORIZONTAL.
- super.onMeasure(widthMeasureSpec, widthMeasureSpec);
- }
-}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/GridItemThumbnail.java b/packages/DocumentsUI/src/com/android/documentsui/GridItemThumbnail.java
new file mode 100644
index 0000000..38d6d72
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/GridItemThumbnail.java
@@ -0,0 +1,50 @@
+/*
+ * 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.documentsui;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+
+/**
+ * Ensures that grid thumbnails are always square.
+ */
+public class GridItemThumbnail extends ImageView {
+ public GridItemThumbnail(Context context) {
+ super(context);
+ }
+
+ public GridItemThumbnail(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public GridItemThumbnail(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // Grid layout uses item width to figure out the number of columns, then dynamically fits
+ // rows into the view. The upshot of this is that changing the item width will mess up the
+ // grid layout - so to make the items square, throw out the height and use the width for
+ // both dimensions. The grid layout will correctly adjust the row height.
+ //
+ // Note that this code will need to be changed if the layout manager's orientation is
+ // changed from VERTICAL to HORIZONTAL.
+ super.onMeasure(widthMeasureSpec, widthMeasureSpec);
+ }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/ListItem.java b/packages/DocumentsUI/src/com/android/documentsui/ListItem.java
new file mode 100644
index 0000000..5c40f1b
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/ListItem.java
@@ -0,0 +1,52 @@
+/*
+ * 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.documentsui;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.LinearLayout;
+
+/**
+ * Layout for a single item in List mode. This class overrides the default focus listener in order
+ * to light up a focus indicator when it is focused.
+ */
+public class ListItem extends LinearLayout
+{
+ public ListItem(Context context) {
+ super(context);
+ }
+
+ public ListItem(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
+ View indicator = findViewById(R.id.focus_indicator);
+ if (gainFocus) {
+ TypedValue color = new TypedValue();
+ getContext().getTheme().resolveAttribute(android.R.attr.colorAccent, color, true);
+ indicator.setBackgroundColor(color.data);
+ } else {
+ indicator.setBackgroundColor(android.R.color.transparent);
+ }
+ super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+ }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java b/packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java
index 14a33f9..3045fa8 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java
@@ -16,8 +16,8 @@
package com.android.documentsui;
-import static com.android.documentsui.DirectoryFragment.ANIM_NONE;
import static com.android.documentsui.State.ACTION_MANAGE;
+import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_NONE;
import android.app.Activity;
import android.app.Fragment;
@@ -34,12 +34,14 @@
import android.support.design.widget.Snackbar;
import android.util.Log;
import android.view.Menu;
+import android.view.MenuItem;
import android.view.View;
import android.widget.BaseAdapter;
import android.widget.Spinner;
import android.widget.Toolbar;
import com.android.documentsui.RecentsProvider.ResumeColumns;
+import com.android.documentsui.dirlist.DirectoryFragment;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.DurableUtils;
import com.android.documentsui.model.RootInfo;
@@ -49,7 +51,6 @@
import java.util.List;
public class ManageRootActivity extends BaseActivity {
- private static final int CODE_FORWARD = 42;
private static final String TAG = "ManageRootsActivity";
private Toolbar mToolbar;
@@ -140,6 +141,21 @@
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
+
+ final MenuItem advanced = menu.findItem(R.id.menu_advanced);
+ final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
+ final MenuItem newWindow = menu.findItem(R.id.menu_new_window);
+ final MenuItem pasteFromCb = menu.findItem(R.id.menu_paste_from_clipboard);
+ final MenuItem fileSize = menu.findItem(R.id.menu_file_size);
+ final MenuItem search = menu.findItem(R.id.menu_search);
+
+ advanced.setVisible(false);
+ createDir.setVisible(false);
+ pasteFromCb.setEnabled(false);
+ newWindow.setEnabled(false);
+ fileSize.setVisible(false);
+ search.setVisible(false);
+
Menus.disableHiddenItems(menu);
return true;
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Menus.java b/packages/DocumentsUI/src/com/android/documentsui/Menus.java
index 3f43a3d..5277d2b 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Menus.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Menus.java
@@ -19,14 +19,14 @@
import android.view.Menu;
import android.view.MenuItem;
-final class Menus {
+public final class Menus {
private Menus() {}
/**
* Disables hidden menu items so that they are not invokable via command shortcuts
*/
- static void disableHiddenItems(Menu menu, MenuItem... exclusions) {
+ public static void disableHiddenItems(Menu menu, MenuItem... exclusions) {
for (int i = 0; i < menu.size(); i++) {
MenuItem item = menu.getItem(i);
if (item.isVisible()) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/MessageBar.java b/packages/DocumentsUI/src/com/android/documentsui/MessageBar.java
index 312d53b..5c6213f 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/MessageBar.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/MessageBar.java
@@ -105,7 +105,7 @@
return mView;
}
- void hide() {
+ public void hide() {
// The container view is used to show/hide the error bar. If a container is not provided,
// fall back to showing/hiding the error bar View, which also works, but does not provide
// the same animated transition.
@@ -116,7 +116,7 @@
}
}
- void show() {
+ public void show() {
// The container view is used to show/hide the error bar. If a container is not provided,
// fall back to showing/hiding the error bar View, which also works, but does not provide
// the same animated transition.
diff --git a/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java b/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java
deleted file mode 100644
index 858fb42..0000000
--- a/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java
+++ /dev/null
@@ -1,2044 +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.documentsui;
-
-import static com.android.documentsui.Shared.DEBUG;
-import static com.android.internal.util.Preconditions.checkArgument;
-import static com.android.internal.util.Preconditions.checkNotNull;
-import static com.android.internal.util.Preconditions.checkState;
-
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.Nullable;
-import android.support.annotation.VisibleForTesting;
-import android.support.v7.widget.GridLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.RecyclerView.Adapter;
-import android.support.v7.widget.RecyclerView.AdapterDataObserver;
-import android.support.v7.widget.RecyclerView.LayoutManager;
-import android.support.v7.widget.RecyclerView.OnScrollListener;
-import android.util.Log;
-import android.util.SparseArray;
-import android.util.SparseBooleanArray;
-import android.util.SparseIntArray;
-import android.view.GestureDetector;
-import android.view.GestureDetector.OnDoubleTapListener;
-import android.view.GestureDetector.OnGestureListener;
-import android.view.MotionEvent;
-import android.view.View;
-
-import com.android.documentsui.Events.InputEvent;
-import com.android.documentsui.Events.MotionInputEvent;
-
-import com.google.android.collect.Lists;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * MultiSelectManager provides support traditional multi-item selection support to RecyclerView.
- * Additionally it can be configured to restrict selection to a single element, @see
- * #setSelectMode.
- */
-public final class MultiSelectManager {
-
- /** Selection mode for multiple select. **/
- public static final int MODE_MULTIPLE = 0;
-
- /** Selection mode for multiple select. **/
- public static final int MODE_SINGLE = 1;
-
- private static final String TAG = "MultiSelectManager";
-
- private final Selection mSelection = new Selection();
-
- // Only created when selection is cleared.
- private Selection mIntermediateSelection;
-
- private Range mRanger;
- private final List<MultiSelectManager.Callback> mCallbacks = new ArrayList<>(1);
-
- private Adapter<?> mAdapter;
- private ItemFinder mHelper;
- private boolean mSingleSelect;
-
- @Nullable private BandController mBandManager;
-
- /**
- * @param recyclerView
- * @param gestureDelegate Option delegate gesture listener.
- * @param mode Selection mode
- * @template A gestureDelegate that implements both {@link OnGestureListener}
- * and {@link OnDoubleTapListener}
- */
- public <L extends OnGestureListener & OnDoubleTapListener> MultiSelectManager(
- final RecyclerView recyclerView, L gestureDelegate, int mode) {
-
- this(
- recyclerView.getAdapter(),
- new RuntimeItemFinder(recyclerView),
- mode);
-
- if (mode == MODE_MULTIPLE) {
- mBandManager = new BandController(
- mHelper,
- new RuntimeBandEnvironment(recyclerView));
- }
-
- GestureDetector.SimpleOnGestureListener listener =
- new GestureDetector.SimpleOnGestureListener() {
- @Override
- public boolean onSingleTapUp(MotionEvent e) {
- return MultiSelectManager.this.onSingleTapUp(
- new MotionInputEvent(e, recyclerView));
- }
- @Override
- public void onLongPress(MotionEvent e) {
- MultiSelectManager.this.onLongPress(
- new MotionInputEvent(e, recyclerView));
- }
- };
-
- CompositeOnGestureListener compositeListener =
- new CompositeOnGestureListener(
- Lists.<OnGestureListener>newArrayList(listener, gestureDelegate),
- Lists.<OnDoubleTapListener>newArrayList(listener, gestureDelegate));
-
- final GestureDetector detector =
- new GestureDetector(recyclerView.getContext(), compositeListener);
-
- detector.setOnDoubleTapListener(compositeListener);
-
- recyclerView.addOnItemTouchListener(
- new RecyclerView.OnItemTouchListener() {
- @Override
- public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
- detector.onTouchEvent(e);
-
- if (mBandManager == null) {
- return false;
- }
-
- // b/23793622 notes the fact that we *never* receiver ACTION_DOWN
- // events in onTouchEvent. Where it not for this issue, we'd
- // push start handling down into handleInputEvent.
- if (mBandManager.shouldStart(e)) {
- // endBandSelect is handled in handleInputEvent.
- mBandManager.startBandSelect(
- new Point((int) e.getX(), (int) e.getY()));
- } else if (mBandManager.isActive()
- && Events.isMouseEvent(e)
- && Events.isActionUp(e)) {
- // Same issue here w b/23793622. The ACTION_UP event
- // is only evert dispatched to onTouchEvent when
- // there is some associated motion. If a user taps
- // mouse, but doesn't move, then band select gets
- // started BUT not ended. Causing phantom
- // bands to appear when the user later clicks to start
- // band select.
- mBandManager.handleInputEvent(
- new MotionInputEvent(e, recyclerView));
- }
-
- return mBandManager.isActive();
- }
-
- @Override
- public void onTouchEvent(RecyclerView rv, MotionEvent e) {
- mBandManager.handleInputEvent(
- new MotionInputEvent(e, recyclerView));
- }
- @Override
- public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {}
- });
- }
-
- /**
- * Constructs a new instance with {@code adapter} and {@code helper}.
- * @hide
- */
- @VisibleForTesting
- MultiSelectManager(Adapter<?> adapter, ItemFinder helper, int mode) {
- checkNotNull(adapter, "'adapter' cannot be null.");
- checkNotNull(helper, "'helper' cannot be null.");
-
- mSingleSelect = mode == MODE_SINGLE;
-
- mHelper = helper;
- mAdapter = adapter;
-
- mAdapter.registerAdapterDataObserver(
- new AdapterDataObserver() {
-
- @Override
- public void onChanged() {
- mSelection.clear();
- }
-
- @Override
- public void onItemRangeChanged(
- int positionStart, int itemCount, Object payload) {
- // No change in position. Ignoring.
- }
-
- @Override
- public void onItemRangeInserted(int positionStart, int itemCount) {
- mSelection.expand(positionStart, itemCount);
- }
-
- @Override
- public void onItemRangeRemoved(int positionStart, int itemCount) {
- mSelection.collapse(positionStart, itemCount);
- }
-
- @Override
- public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
- throw new UnsupportedOperationException();
- }
- });
- }
-
- /**
- * Adds {@code callback} such that it will be notified when {@code MultiSelectManager}
- * events occur.
- *
- * @param callback
- */
- public void addCallback(MultiSelectManager.Callback callback) {
- mCallbacks.add(callback);
- }
-
- /**
- * Returns a Selection object that provides a live view
- * on the current selection.
- *
- * @see #getSelection(Selection) on how to get a snapshot
- * of the selection that will not reflect future changes
- * to selection.
- *
- * @return The current selection.
- */
- public Selection getSelection() {
- return mSelection;
- }
-
- /**
- * Updates {@code dest} to reflect the current selection.
- * @param dest
- *
- * @return The Selection instance passed in, for convenience.
- */
- public Selection getSelection(Selection dest) {
- dest.copyFrom(mSelection);
- return dest;
- }
-
- /**
- * Causes item at {@code position} in adapter to be selected.
- *
- * @param position Adapter position
- * @param selected
- * @return True if the selection state of the item changed.
- */
- @VisibleForTesting
- public boolean setItemSelected(int position, boolean selected) {
- if (mSingleSelect && !mSelection.isEmpty()) {
- clearSelectionQuietly();
- }
- return setItemsSelected(position, 1, selected);
- }
-
- /**
- * Sets the selected state of the specified items. Note that the callback will NOT
- * be consulted to see if an item can be selected.
- *
- * @return True if the selection state of any of the items changed.
- */
- public boolean setItemsSelected(int position, int length, boolean selected) {
- boolean changed = false;
- for (int i = position; i < position + length; i++) {
- boolean itemChanged = selected ? mSelection.add(i) : mSelection.remove(i);
- if (itemChanged) {
- notifyItemStateChanged(i, selected);
- }
- changed |= itemChanged;
- }
-
- notifySelectionChanged();
- return changed;
- }
-
- /**
- * Clears the selection and notifies (even if nothing changes).
- */
- public void clearSelection() {
- clearSelectionQuietly();
- notifySelectionChanged();
- }
-
- public void handleLayoutChanged() {
- if (mBandManager != null) {
- mBandManager.handleLayoutChanged();
- }
- }
-
- /**
- * Clears the selection, without notifying anyone.
- */
- private void clearSelectionQuietly() {
- mRanger = null;
-
- if (mSelection.isEmpty()) {
- return;
- }
- if (mIntermediateSelection == null) {
- mIntermediateSelection = new Selection();
- }
- getSelection(mIntermediateSelection);
- mSelection.clear();
-
- for (int i = 0; i < mIntermediateSelection.size(); i++) {
- int position = mIntermediateSelection.get(i);
- notifyItemStateChanged(position, false);
- }
- }
-
- @VisibleForTesting
- void onLongPress(InputEvent input) {
- if (DEBUG) Log.d(TAG, "Handling long press event.");
-
- if (!input.isOverItem()) {
- if (DEBUG) Log.i(TAG, "Cannot handle tap. No adapter position available.");
- }
-
- handleAdapterEvent(input);
- }
-
- @VisibleForTesting
- boolean onSingleTapUp(InputEvent input) {
- if (DEBUG) Log.d(TAG, "Processing tap event.");
- if (mSelection.isEmpty()) {
- // if this is a mouse click on an item, start selection mode.
- // TODO: && input.isPrimaryButtonPressed(), but it is returning false.
- if (input.isOverItem() && input.isMouseEvent()) {
- toggleSelection(input.getItemPosition());
- }
- return false;
- }
-
- if (!input.isOverItem()) {
- if (DEBUG) Log.d(TAG, "Activity has no position. Canceling selection.");
- clearSelection();
- return false;
- }
-
- handleAdapterEvent(input);
- return true;
- }
-
- /**
- * Handles a change caused by a click on the item with the given position. If the Shift key is
- * held down, this performs a range select; otherwise, it simply toggles the item's selection
- * state.
- */
- private void handleAdapterEvent(InputEvent input) {
- if (mRanger != null && input.isShiftKeyDown()) {
- mRanger.snapSelection(input.getItemPosition());
-
- // We're being lazy here notifying even when something might not have changed.
- // To make this more correct, we'd need to update the Ranger class to return
- // information about what has changed.
- notifySelectionChanged();
- } else {
- toggleSelection(input.getItemPosition());
- }
- }
-
- /**
- * Toggles the selection state at position. If an item does end up selected
- * a new Ranger (range selection manager) at that point is created.
- *
- * @param position
- */
- private void toggleSelection(int position) {
- // Position may be special "no position" during certain
- // transitional phases. If so, skip handling of the event.
- if (position == RecyclerView.NO_POSITION) {
- if (DEBUG) Log.d(TAG, "Ignoring toggle for element with no position.");
- return;
- }
-
- boolean changed = false;
- if (mSelection.contains(position)) {
- changed = attemptDeselect(position);
- } else {
- boolean canSelect = notifyBeforeItemStateChange(position, true);
- if (!canSelect) {
- return;
- }
- if (mSingleSelect && !mSelection.isEmpty()) {
- clearSelectionQuietly();
- }
-
- // Here we're already in selection mode. In that case
- // When a simple click/tap (without SHIFT) creates causes
- // an item to be selected.
- // By recreating Ranger at this point, we allow the user to create
- // multiple separate contiguous ranges with SHIFT+Click & Click.
- selectAndNotify(position);
- setSelectionFocusBegin(position);
- changed = true;
- }
-
- if (changed) {
- notifySelectionChanged();
- }
- }
-
- /**
- * Sets the magic location at which a selection range begins. This
- * value is consulted when determining how to extend, and modify
- * selection ranges.
- *
- * @throws IllegalStateException if {@code position} is not already be selected
- * @param position
- */
- void setSelectionFocusBegin(int position) {
- checkState(mSelection.contains(position));
- mRanger = new Range(position);
- }
-
- /**
- * Try to select all elements in range. Not that callbacks can cancel selection
- * of specific items, so some or even all items may not reflect the desired
- * state after the update is complete.
- *
- * @param begin inclusive
- * @param end inclusive
- * @param selected
- */
- private void updateRange(int begin, int end, boolean selected) {
- checkState(end >= begin);
- for (int i = begin; i <= end; i++) {
- if (selected) {
- boolean canSelect = notifyBeforeItemStateChange(i, true);
- if (canSelect) {
- if (mSingleSelect && !mSelection.isEmpty()) {
- clearSelectionQuietly();
- }
- selectAndNotify(i);
- }
- } else {
- attemptDeselect(i);
- }
- }
- }
-
- /**
- * @param position
- * @return True if the update was applied.
- */
- private boolean selectAndNotify(int position) {
- boolean changed = mSelection.add(position);
- if (changed) {
- notifyItemStateChanged(position, true);
- }
- return changed;
- }
-
- /**
- * @param position
- * @return True if the update was applied.
- */
- private boolean attemptDeselect(int position) {
- if (notifyBeforeItemStateChange(position, false)) {
- mSelection.remove(position);
- notifyItemStateChanged(position, false);
- if (DEBUG) Log.d(TAG, "Selection after deselect: " + mSelection);
- return true;
- } else {
- if (DEBUG) Log.d(TAG, "Select cancelled by listener.");
- return false;
- }
- }
-
- private boolean notifyBeforeItemStateChange(int position, boolean nextState) {
- int lastListener = mCallbacks.size() - 1;
- for (int i = lastListener; i > -1; i--) {
- if (!mCallbacks.get(i).onBeforeItemStateChange(position, nextState)) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Notifies registered listeners when the selection status of a single item
- * (identified by {@code position}) changes.
- */
- private void notifyItemStateChanged(int position, boolean selected) {
- int lastListener = mCallbacks.size() - 1;
- for (int i = lastListener; i > -1; i--) {
- mCallbacks.get(i).onItemStateChanged(position, selected);
- }
- mAdapter.notifyItemChanged(position);
- }
-
- /**
- * Notifies registered listeners when the selection has changed. This
- * notification should be sent only once a full series of changes
- * is complete, e.g. clearingSelection, or updating the single
- * selection from one item to another.
- */
- private void notifySelectionChanged() {
- int lastListener = mCallbacks.size() - 1;
- for (int i = lastListener; i > -1; i--) {
- mCallbacks.get(i).onSelectionChanged();
- }
- }
-
- /**
- * Class providing support for managing range selections.
- */
- private final class Range {
- private static final int UNDEFINED = -1;
-
- final int mBegin;
- int mEnd = UNDEFINED;
-
- public Range(int begin) {
- if (DEBUG) Log.d(TAG, "New Ranger created beginning @ " + begin);
- mBegin = begin;
- }
-
- private void snapSelection(int position) {
- checkState(mRanger != null);
- checkArgument(position != RecyclerView.NO_POSITION);
-
- if (mEnd == UNDEFINED || mEnd == mBegin) {
- // Reset mEnd so it can be established in establishRange.
- mEnd = UNDEFINED;
- establishRange(position);
- } else {
- reviseRange(position);
- }
- }
-
- private void establishRange(int position) {
- checkState(mRanger.mEnd == UNDEFINED);
-
- if (position == mBegin) {
- mEnd = position;
- }
-
- if (position > mBegin) {
- updateRange(mBegin + 1, position, true);
- } else if (position < mBegin) {
- updateRange(position, mBegin - 1, true);
- }
-
- mEnd = position;
- }
-
- private void reviseRange(int position) {
- checkState(mEnd != UNDEFINED);
- checkState(mBegin != mEnd);
-
- if (position == mEnd) {
- if (DEBUG) Log.i(TAG, "Skipping no-op revision click on mEndRange.");
- }
-
- if (mEnd > mBegin) {
- reviseAscendingRange(position);
- } else if (mEnd < mBegin) {
- reviseDescendingRange(position);
- }
- // the "else" case is covered by checkState at beginning of method.
-
- mEnd = position;
- }
-
- /**
- * Updates an existing ascending seleciton.
- * @param position
- */
- private void reviseAscendingRange(int position) {
- // Reducing or reversing the range....
- if (position < mEnd) {
- if (position < mBegin) {
- updateRange(mBegin + 1, mEnd, false);
- updateRange(position, mBegin -1, true);
- } else {
- updateRange(position + 1, mEnd, false);
- }
- }
-
- // Extending the range...
- else if (position > mEnd) {
- updateRange(mEnd + 1, position, true);
- }
- }
-
- private void reviseDescendingRange(int position) {
- // Reducing or reversing the range....
- if (position > mEnd) {
- if (position > mBegin) {
- updateRange(mEnd, mBegin - 1, false);
- updateRange(mBegin + 1, position, true);
- } else {
- updateRange(mEnd, position - 1, false);
- }
- }
-
- // Extending the range...
- else if (position < mEnd) {
- updateRange(position, mEnd - 1, true);
- }
- }
- }
-
- /**
- * Object representing the current selection. Provides read only access
- * public access, and private write access.
- */
- public static final class Selection {
-
- // This class tracks selected positions by managing two arrays: the saved selection, and
- // the total selection. Saved selections are those which have been completed by tapping an
- // item or by completing a band select operation. Provisional selections are selections
- // which have been temporarily created by an in-progress band select operation (once the
- // user releases the mouse button during a band select operation, the selected items
- // become saved). The total selection is the combination of both the saved selection and
- // the provisional selection. Tracking both separately is necessary to ensure that saved
- // selections do not become deselected when they are removed from the provisional selection;
- // for example, if item A is tapped (and selected), then an in-progress band select covers A
- // then uncovers A, A should still be selected as it has been saved. To ensure this
- // behavior, the saved selection must be tracked separately.
- private SparseBooleanArray mSavedSelection;
- private SparseBooleanArray mTotalSelection;
-
- public Selection() {
- mSavedSelection = new SparseBooleanArray();
- mTotalSelection = new SparseBooleanArray();
- }
-
- /**
- * @param position
- * @return true if the position is currently selected.
- */
- public boolean contains(int position) {
- return mTotalSelection.get(position);
- }
-
- /**
- * Useful for iterating over selection. Please note that
- * iteration should be done over a copy of the selection,
- * not the live selection.
- *
- * @see #copyTo(MultiSelectManager.Selection)
- *
- * @param index
- * @return the position value stored at specified index.
- */
- public int get(int index) {
- return mTotalSelection.keyAt(index);
- }
-
- /**
- * @return size of the selection.
- */
- public int size() {
- return mTotalSelection.size();
- }
-
- /**
- * @return true if the selection is empty.
- */
- public boolean isEmpty() {
- return mTotalSelection.size() == 0;
- }
-
- /**
- * Sets the provisional selection, which is a temporary selection that can be saved,
- * canceled, or adjusted at a later time. When a new provision selection is applied, the old
- * one (if it exists) is abandoned.
- * @return Array with entry for each position added or removed. Entries which were added
- * contain a value of true, and entries which were removed contain a value of false.
- */
- @VisibleForTesting
- protected SparseBooleanArray setProvisionalSelection(
- SparseBooleanArray provisionalSelection) {
- SparseBooleanArray delta = new SparseBooleanArray();
-
- for (int i = 0; i < mTotalSelection.size(); i++) {
- int position = mTotalSelection.keyAt(i);
- if (!provisionalSelection.get(position) && !mSavedSelection.get(position)) {
- // Remove each item that used to be in the selection but is unsaved and not in
- // the new provisional selection.
- delta.put(position, false);
- }
- }
-
- for (int i = 0; i < provisionalSelection.size(); i++) {
- int position = provisionalSelection.keyAt(i);
- if (!mTotalSelection.get(position)) {
- // Add each item that was not previously in the selection but is in the
- // new provisional selection.
- delta.put(position, true);
- }
- }
-
- // Now, iterate through the changes and actually add/remove them to/from
- // mCurrentSelection. This could not be done in the previous loops because changing the
- // size of the selection mid-iteration changes iteration order erroneously.
- for (int i = 0; i < delta.size(); i++) {
- int position = delta.keyAt(i);
- if (delta.get(position)) {
- mTotalSelection.put(position, true);
- } else {
- mTotalSelection.delete(position);
- }
- }
-
- return delta;
- }
-
- /**
- * Saves the existing provisional selection. Once the provisional selection is saved,
- * subsequent provisional selections which are different from this existing one cannot
- * cause items in this existing provisional selection to become deselected.
- */
- @VisibleForTesting
- protected void applyProvisionalSelection() {
- mSavedSelection = mTotalSelection.clone();
- }
-
- /**
- * Abandons the existing provisional selection so that all items provisionally selected are
- * now deselected.
- */
- @VisibleForTesting
- protected void cancelProvisionalSelection() {
- mTotalSelection = mSavedSelection.clone();
- }
-
- /** @hide */
- @VisibleForTesting
- boolean add(int position) {
- if (!mTotalSelection.get(position)) {
- mTotalSelection.put(position, true);
- mSavedSelection.put(position, true);
- return true;
- }
- return false;
- }
-
- /** @hide */
- @VisibleForTesting
- boolean remove(int position) {
- if (mTotalSelection.get(position)) {
- mTotalSelection.delete(position);
- mSavedSelection.delete(position);
- return true;
- }
- return false;
- }
-
- /**
- * Adjusts the selection range to reflect the existence of newly inserted values at
- * the specified positions. This has the effect of adjusting all existing selected
- * positions within the specified range accordingly. Note that this function discards any
- * provisional selections which may have been applied.
- *
- * @param startPosition
- * @param count
- * @hide
- */
- @VisibleForTesting
- void expand(int startPosition, int count) {
- checkState(startPosition >= 0);
- checkState(count > 0);
- cancelProvisionalSelection();
-
- for (int i = 0; i < mTotalSelection.size(); i++) {
- int itemPosition = mTotalSelection.keyAt(i);
- if (itemPosition >= startPosition) {
- mTotalSelection.setKeyAt(i, itemPosition + count);
- mSavedSelection.setKeyAt(i, itemPosition + count);
- }
- }
- }
-
- /**
- * Adjusts the selection range to reflect the removal specified positions. This has
- * the effect of adjusting all existing selected positions within the specified range
- * accordingly. Note that this function discards any provisional selections which may have
- * been applied.
- *
- * @param startPosition
- * @param count The length of the range to collapse. Must be greater than 0.
- * @hide
- */
- @VisibleForTesting
- void collapse(int startPosition, int count) {
- checkState(startPosition >= 0);
- checkState(count > 0);
-
- int endPosition = startPosition + count - 1;
-
- SparseBooleanArray newSelection = new SparseBooleanArray();
- for (int i = 0; i < mSavedSelection.size(); i++) {
- int itemPosition = mSavedSelection.keyAt(i);
- if (itemPosition < startPosition) {
- newSelection.append(itemPosition, true);
- } else if (itemPosition > endPosition) {
- newSelection.append(itemPosition - count, true);
- }
- }
- mSavedSelection = newSelection;
- cancelProvisionalSelection();
- }
-
- /** @hide */
- @VisibleForTesting
- void clear() {
- mSavedSelection.clear();
- mTotalSelection.clear();
- }
-
- /** @hide */
- @VisibleForTesting
- void copyFrom(Selection source) {
- mSavedSelection = source.mSavedSelection.clone();
- mTotalSelection = source.mTotalSelection.clone();
- }
-
- @Override
- public String toString() {
- if (size() <= 0) {
- return "size=0, items=[]";
- }
-
- StringBuilder buffer = new StringBuilder(mTotalSelection.size() * 28);
- buffer.append("{size=")
- .append(mTotalSelection.size())
- .append(", ")
- .append("items=[");
- for (int i=0; i < mTotalSelection.size(); i++) {
- if (i > 0) {
- buffer.append(", ");
- }
- buffer.append(mTotalSelection.keyAt(i));
- }
- buffer.append("]}");
- return buffer.toString();
- }
-
- @Override
- public int hashCode() {
- return mSavedSelection.hashCode() ^ mTotalSelection.hashCode();
- }
-
- @Override
- public boolean equals(Object that) {
- if (this == that) {
- return true;
- }
-
- if (!(that instanceof Selection)) {
- return false;
- }
-
- return mSavedSelection.equals(((Selection) that).mSavedSelection) &&
- mTotalSelection.equals(((Selection) that).mTotalSelection);
- }
- }
-
- /**
- * Provides functionality for MultiSelectManager. Exists primarily to tests that are
- * fully isolated from RecyclerView.
- */
- interface ItemFinder {
- int findItemPosition(MotionEvent e);
- }
-
- /** ItemFinder implementation backed by good ol' RecyclerView. */
- private static final class RuntimeItemFinder implements ItemFinder {
-
- private final RecyclerView mView;
-
- RuntimeItemFinder(RecyclerView view) {
- mView = view;
- }
-
- @Override
- public int findItemPosition(MotionEvent e) {
- View view = mView.findChildViewUnder(e.getX(), e.getY());
- return view != null
- ? mView.getChildAdapterPosition(view)
- : RecyclerView.NO_POSITION;
- }
- }
-
- /**
- * Provides functionality for BandController. Exists primarily to tests that are
- * fully isolated from RecyclerView.
- */
- interface BandEnvironment {
- void showBand(Rect rect);
- void hideBand();
- void addOnScrollListener(RecyclerView.OnScrollListener listener);
- void removeOnScrollListener(RecyclerView.OnScrollListener listener);
- void scrollBy(int dy);
- int getHeight();
- void invalidateView();
- void runAtNextFrame(Runnable r);
- void removeCallback(Runnable r);
- Point createAbsolutePoint(Point relativePoint);
- Rect getAbsoluteRectForChildViewAt(int index);
- int getAdapterPositionAt(int index);
- int getColumnCount();
- int getRowCount();
- int getChildCount();
- int getVisibleChildCount();
- }
-
- /** RvFacade implementation backed by good ol' RecyclerView. */
- private static final class RuntimeBandEnvironment implements BandEnvironment {
-
- private final RecyclerView mView;
- private final Drawable mBand;
-
- private boolean mIsOverlayShown = false;
-
- RuntimeBandEnvironment(RecyclerView rv) {
- mView = rv;
- mBand = mView.getContext().getTheme().getDrawable(R.drawable.band_select_overlay);
- }
-
- @Override
- public int getAdapterPositionAt(int index) {
- View child = mView.getChildAt(index);
- return mView.getChildViewHolder(child).getAdapterPosition();
- }
-
- @Override
- public void addOnScrollListener(OnScrollListener listener) {
- mView.addOnScrollListener(listener);
- }
-
- @Override
- public void removeOnScrollListener(OnScrollListener listener) {
- mView.removeOnScrollListener(listener);
- }
-
- @Override
- public Point createAbsolutePoint(Point relativePoint) {
- return new Point(relativePoint.x + mView.computeHorizontalScrollOffset(),
- relativePoint.y + mView.computeVerticalScrollOffset());
- }
-
- @Override
- public Rect getAbsoluteRectForChildViewAt(int index) {
- final View child = mView.getChildAt(index);
- final Rect childRect = new Rect();
- child.getHitRect(childRect);
- childRect.left += mView.computeHorizontalScrollOffset();
- childRect.right += mView.computeHorizontalScrollOffset();
- childRect.top += mView.computeVerticalScrollOffset();
- childRect.bottom += mView.computeVerticalScrollOffset();
- return childRect;
- }
-
- @Override
- public int getChildCount() {
- return mView.getAdapter().getItemCount();
- }
-
- @Override
- public int getVisibleChildCount() {
- return mView.getChildCount();
- }
-
- @Override
- public int getColumnCount() {
- LayoutManager layoutManager = mView.getLayoutManager();
- if (layoutManager instanceof GridLayoutManager) {
- return ((GridLayoutManager) layoutManager).getSpanCount();
- }
-
- // Otherwise, it is a list with 1 column.
- return 1;
- }
-
- @Override
- public int getRowCount() {
- int numFullColumns = getChildCount() / getColumnCount();
- boolean hasPartiallyFullColumn = getChildCount() % getColumnCount() != 0;
- return numFullColumns + (hasPartiallyFullColumn ? 1 : 0);
- }
-
- @Override
- public int getHeight() {
- return mView.getHeight();
- }
-
- @Override
- public void invalidateView() {
- mView.invalidate();
- }
-
- @Override
- public void runAtNextFrame(Runnable r) {
- mView.postOnAnimation(r);
- }
-
- @Override
- public void removeCallback(Runnable r) {
- mView.removeCallbacks(r);
- }
-
- @Override
- public void scrollBy(int dy) {
- mView.scrollBy(0, dy);
- }
-
- @Override
- public void showBand(Rect rect) {
- mBand.setBounds(rect);
-
- if (!mIsOverlayShown) {
- mView.getOverlay().add(mBand);
- }
- }
-
- @Override
- public void hideBand() {
- mView.getOverlay().remove(mBand);
- }
- }
-
- public interface Callback {
- /**
- * Called when an item is selected or unselected while in selection mode.
- *
- * @param position Adapter position of the item that was checked or unchecked
- * @param selected <code>true</code> if the item is now selected, <code>false</code>
- * if the item is now unselected.
- */
- public void onItemStateChanged(int position, boolean selected);
-
- /**
- * Called prior to an item changing state. Callbacks can cancel
- * the change at {@code position} by returning {@code false}.
- *
- * @param position Adapter position of the item that was checked or unchecked
- * @param selected <code>true</code> if the item is to be selected, <code>false</code>
- * if the item is to be unselected.
- */
- public boolean onBeforeItemStateChange(int position, boolean selected);
-
- /**
- * Called immediately after completion of any set of changes.
- */
- public void onSelectionChanged();
- }
-
- /**
- * A composite {@code OnGestureDetector} that allows us to delegate unhandled
- * events to an outside party (presumably DirectoryFragment).
- * @template A gestureDelegate that implements both {@link OnGestureListener}
- * and {@link OnDoubleTapListener}
- */
- private static final class CompositeOnGestureListener
- implements OnGestureListener, OnDoubleTapListener {
-
- private List<OnGestureListener> mGestureListeners;
- private List<OnDoubleTapListener> mTapListeners;
-
- public CompositeOnGestureListener(
- List<OnGestureListener> gestureListeners,
- List<OnDoubleTapListener> tapListeners) {
- mGestureListeners = gestureListeners;
- mTapListeners = tapListeners;
- }
-
- @Override
- public boolean onDown(MotionEvent e) {
- for (OnGestureListener l : mGestureListeners) {
- if (l.onDown(e)) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- public void onShowPress(MotionEvent e) {
- for (OnGestureListener l : mGestureListeners) {
- l.onShowPress(e);
- }
- }
-
- @Override
- public boolean onSingleTapUp(MotionEvent e) {
- for (OnGestureListener l : mGestureListeners) {
- if (l.onSingleTapUp(e)) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
- for (OnGestureListener l : mGestureListeners) {
- if (l.onScroll(e1, e2, distanceX, distanceY)) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- public void onLongPress(MotionEvent e) {
- for (OnGestureListener l : mGestureListeners) {
- l.onLongPress(e);
- }
- }
-
- @Override
- public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
- for (OnGestureListener l : mGestureListeners) {
- if (l.onFling(e1, e2, velocityX, velocityY)) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- public boolean onSingleTapConfirmed(MotionEvent e) {
- for (OnDoubleTapListener listener : mTapListeners) {
- if (listener.onSingleTapConfirmed(e)) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- public boolean onDoubleTap(MotionEvent e) {
- for (OnDoubleTapListener listener : mTapListeners) {
- if (listener.onDoubleTap(e)) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- public boolean onDoubleTapEvent(MotionEvent e) {
- for (OnDoubleTapListener listener : mTapListeners) {
- if (listener.onDoubleTapEvent(e)) {
- return true;
- }
- }
- return false;
- }
- }
-
- /**
- * Provides mouse driven band-select support when used in conjunction with {@link RecyclerView}
- * and {@link MultiSelectManager}. This class is responsible for rendering the band select
- * overlay and selecting overlaid items via MultiSelectManager.
- */
- public class BandController extends RecyclerView.OnScrollListener
- implements GridModel.OnSelectionChangedListener {
-
- private static final int NOT_SET = -1;
-
- private final ItemFinder mItemFinder;
- private final BandEnvironment mEnvironment;
- private final Runnable mModelBuilder;
-
- @Nullable private Rect mBounds;
- @Nullable private Point mCurrentPosition;
- @Nullable private Point mOrigin;
- @Nullable private GridModel mModel;
-
- // The time at which the current band selection-induced scroll began. If no scroll is in
- // progress, the value is NOT_SET.
- private long mScrollStartTime = NOT_SET;
- private final Runnable mViewScroller = new ViewScroller();
-
- public BandController(ItemFinder finder, final BandEnvironment environment) {
- mItemFinder = finder;
- mEnvironment = environment;
- mEnvironment.addOnScrollListener(this);
-
- mModelBuilder = new Runnable() {
- @Override
- public void run() {
- mModel = new GridModel(environment);
- mModel.addOnSelectionChangedListener(BandController.this);
- }
- };
- }
-
- private boolean isActive() {
- return mModel != null;
- }
-
- /**
- * Handle a change in layout by cleaning up and getting rid of the old model and creating
- * a new model which will track the new layout.
- */
- public void handleLayoutChanged() {
- if (mModel != null) {
- mModel.removeOnSelectionChangedListener(this);
- mModel.stopListening();
-
- // build a new model, all fresh and happy.
- mModelBuilder.run();
- }
- }
-
- boolean shouldStart(MotionEvent e) {
- return !isActive()
- && Events.isMouseEvent(e) // a mouse
- && Events.isActionDown(e) // the initial button press
- && mAdapter.getItemCount() > 0
- && mItemFinder.findItemPosition(e) == RecyclerView.NO_ID; // in empty space
- }
-
- boolean shouldStop(InputEvent input) {
- return isActive()
- && input.isMouseEvent()
- && input.isActionUp();
- }
-
- /**
- * Processes a MotionEvent by starting, ending, or resizing the band select overlay.
- * @param input
- */
- private void handleInputEvent(InputEvent input) {
- checkArgument(input.isMouseEvent());
-
- if (shouldStop(input)) {
- endBandSelect();
- return;
- }
-
- // We shouldn't get any events in this method when band select is not active,
- // but it turns some guests show up late to the party.
- if (!isActive()) {
- return;
- }
-
- mCurrentPosition = input.getOrigin();
- mModel.resizeSelection(input.getOrigin());
- scrollViewIfNecessary();
- resizeBandSelectRectangle();
- }
-
- /**
- * Starts band select by adding the drawable to the RecyclerView's overlay.
- */
- private void startBandSelect(Point origin) {
- if (DEBUG) Log.d(TAG, "Starting band select @ " + origin);
-
- mOrigin = origin;
- mModelBuilder.run(); // Creates a new selection model.
- mModel.startSelection(mOrigin);
- }
-
- /**
- * Scrolls the view if necessary.
- */
- private void scrollViewIfNecessary() {
- mEnvironment.removeCallback(mViewScroller);
- mViewScroller.run();
- mEnvironment.invalidateView();
- }
-
- /**
- * Resizes the band select rectangle by using the origin and the current pointer position as
- * two opposite corners of the selection.
- */
- private void resizeBandSelectRectangle() {
- mBounds = new Rect(Math.min(mOrigin.x, mCurrentPosition.x),
- Math.min(mOrigin.y, mCurrentPosition.y),
- Math.max(mOrigin.x, mCurrentPosition.x),
- Math.max(mOrigin.y, mCurrentPosition.y));
- mEnvironment.showBand(mBounds);
- }
-
- /**
- * Ends band select by removing the overlay.
- */
- private void endBandSelect() {
- if (DEBUG) Log.d(TAG, "Ending band select.");
-
- mEnvironment.hideBand();
- mSelection.applyProvisionalSelection();
- mModel.endSelection();
- int firstSelected = mModel.getPositionNearestOrigin();
- if (!mSelection.contains(firstSelected)) {
- Log.w(TAG, "First selected by band is NOT in selection!");
- // Sadly this is really happening. Need to figure out what's going on.
- } else if (firstSelected != GridModel.NOT_SET) {
- setSelectionFocusBegin(firstSelected);
- }
-
- mModel = null;
- mOrigin = null;
- }
-
- @Override
- public void onSelectionChanged(SparseBooleanArray updatedSelection) {
- SparseBooleanArray delta = mSelection.setProvisionalSelection(updatedSelection);
- for (int i = 0; i < delta.size(); i++) {
- int position = delta.keyAt(i);
- notifyItemStateChanged(position, delta.get(position));
- }
- notifySelectionChanged();
- }
-
- private class ViewScroller implements Runnable {
- /**
- * The number of milliseconds of scrolling at which scroll speed continues to increase.
- * At first, the scroll starts slowly; then, the rate of scrolling increases until it
- * reaches its maximum value at after this many milliseconds.
- */
- private static final long SCROLL_ACCELERATION_LIMIT_TIME_MS = 2000;
-
- @Override
- public void run() {
- // Compute the number of pixels the pointer's y-coordinate is past the view.
- // Negative values mean the pointer is at or before the top of the view, and
- // positive values mean that the pointer is at or after the bottom of the view. Note
- // that one additional pixel is added here so that the view still scrolls when the
- // pointer is exactly at the top or bottom.
- int pixelsPastView = 0;
- if (mCurrentPosition.y <= 0) {
- pixelsPastView = mCurrentPosition.y - 1;
- } else if (mCurrentPosition.y >= mEnvironment.getHeight() - 1) {
- pixelsPastView = mCurrentPosition.y - mEnvironment.getHeight() + 1;
- }
-
- if (!isActive() || pixelsPastView == 0) {
- // If band selection is inactive, or if it is active but not at the edge of the
- // view, no scrolling is necessary.
- mScrollStartTime = NOT_SET;
- return;
- }
-
- if (mScrollStartTime == NOT_SET) {
- // If the pointer was previously not at the edge of the view but now is, set the
- // start time for the scroll.
- mScrollStartTime = System.currentTimeMillis();
- }
-
- // Compute the number of pixels to scroll, and scroll that many pixels.
- final int numPixels = computeScrollDistance(
- pixelsPastView, System.currentTimeMillis() - mScrollStartTime);
- mEnvironment.scrollBy(numPixels);
-
- mEnvironment.removeCallback(mViewScroller);
- mEnvironment.runAtNextFrame(this);
- }
-
- /**
- * Computes the number of pixels to scroll based on how far the pointer is past the end
- * of the view and how long it has been there. Roughly based on ItemTouchHelper's
- * algorithm for computing the number of pixels to scroll when an item is dragged to the
- * end of a {@link RecyclerView}.
- * @param pixelsPastView
- * @param scrollDuration
- * @return
- */
- private int computeScrollDistance(int pixelsPastView, long scrollDuration) {
- final int maxScrollStep = mEnvironment.getHeight();
- final int direction = (int) Math.signum(pixelsPastView);
- final int absPastView = Math.abs(pixelsPastView);
-
- // Calculate the ratio of how far out of the view the pointer currently resides to
- // the entire height of the view.
- final float outOfBoundsRatio = Math.min(
- 1.0f, (float) absPastView / mEnvironment.getHeight());
- // Interpolate this ratio and use it to compute the maximum scroll that should be
- // possible for this step.
- final float cappedScrollStep =
- direction * maxScrollStep * smoothOutOfBoundsRatio(outOfBoundsRatio);
-
- // Likewise, calculate the ratio of the time spent in the scroll to the limit.
- final float timeRatio = Math.min(
- 1.0f, (float) scrollDuration / SCROLL_ACCELERATION_LIMIT_TIME_MS);
- // Interpolate this ratio and use it to compute the final number of pixels to
- // scroll.
- final int numPixels = (int) (cappedScrollStep * smoothTimeRatio(timeRatio));
-
- // If the final number of pixels to scroll ends up being 0, the view should still
- // scroll at least one pixel.
- return numPixels != 0 ? numPixels : direction;
- }
-
- /**
- * Interpolates the given out of bounds ratio on a curve which starts at (0,0) and ends
- * at (1,1) and quickly approaches 1 near the start of that interval. This ensures that
- * drags that are at the edge or barely past the edge of the view still cause sufficient
- * scrolling. The equation y=(x-1)^5+1 is used, but this could also be tweaked if
- * needed.
- * @param ratio A ratio which is in the range [0, 1].
- * @return A "smoothed" value, also in the range [0, 1].
- */
- private float smoothOutOfBoundsRatio(float ratio) {
- return (float) Math.pow(ratio - 1.0f, 5) + 1.0f;
- }
-
- /**
- * Interpolates the given time ratio on a curve which starts at (0,0) and ends at (1,1)
- * and stays close to 0 for most input values except those very close to 1. This ensures
- * that scrolls start out very slowly but speed up drastically after the scroll has been
- * in progress close to SCROLL_ACCELERATION_LIMIT_TIME_MS. The equation y=x^5 is used,
- * but this could also be tweaked if needed.
- * @param ratio A ratio which is in the range [0, 1].
- * @return A "smoothed" value, also in the range [0, 1].
- */
- private float smoothTimeRatio(float ratio) {
- return (float) Math.pow(ratio, 5);
- }
- };
-
- @Override
- public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
- if (!isActive()) {
- return;
- }
-
- // Adjust the y-coordinate of the origin the opposite number of pixels so that the
- // origin remains in the same place relative to the view's items.
- mOrigin.y -= dy;
- resizeBandSelectRectangle();
- }
- }
-
- /**
- * Provides a band selection item model for views within a RecyclerView. This class queries the
- * RecyclerView to determine where its items are placed; then, once band selection is underway,
- * it alerts listeners of which items are covered by the selections.
- */
- public static final class GridModel extends RecyclerView.OnScrollListener {
-
- public static final int NOT_SET = -1;
-
- // Enum values used to determine the corner at which the origin is located within the
- private static final int UPPER = 0x00;
- private static final int LOWER = 0x01;
- private static final int LEFT = 0x00;
- private static final int RIGHT = 0x02;
- private static final int UPPER_LEFT = UPPER | LEFT;
- private static final int UPPER_RIGHT = UPPER | RIGHT;
- private static final int LOWER_LEFT = LOWER | LEFT;
- private static final int LOWER_RIGHT = LOWER | RIGHT;
-
- private final BandEnvironment mHelper;
- private final List<OnSelectionChangedListener> mOnSelectionChangedListeners =
- new ArrayList<>();
-
- // Map from the x-value of the left side of a SparseBooleanArray of adapter positions, keyed
- // by their y-offset. For example, if the first column of the view starts at an x-value of 5,
- // mColumns.get(5) would return an array of positions in that column. Within that array, the
- // value for key y is the adapter position for the item whose y-offset is y.
- private final SparseArray<SparseIntArray> mColumns = new SparseArray<>();
-
- // List of limits along the x-axis (columns).
- // This list is sorted from furthest left to furthest right.
- private final List<Limits> mColumnBounds = new ArrayList<>();
-
- // List of limits along the y-axis (rows). Note that this list only contains items which
- // have been in the viewport.
- private final List<Limits> mRowBounds = new ArrayList<>();
-
- // The adapter positions which have been recorded so far.
- private final SparseBooleanArray mKnownPositions = new SparseBooleanArray();
-
- // Array passed to registered OnSelectionChangedListeners. One array is created and reused
- // throughout the lifetime of the object.
- private final SparseBooleanArray mSelection = new SparseBooleanArray();
-
- // The current pointer (in absolute positioning from the top of the view).
- private Point mPointer = null;
-
- // The bounds of the band selection.
- private RelativePoint mRelativeOrigin;
- private RelativePoint mRelativePointer;
-
- private boolean mIsActive;
-
- // Tracks where the band select originated from. This is used to determine where selections
- // should expand from when Shift+click is used.
- private int mPositionNearestOrigin = NOT_SET;
-
- GridModel(BandEnvironment helper) {
- mHelper = helper;
- mHelper.addOnScrollListener(this);
- }
-
- /**
- * Stops listening to the view's scrolls. Call this function before discarding a
- * BandSelecModel object to prevent memory leaks.
- */
- void stopListening() {
- mHelper.removeOnScrollListener(this);
- }
-
- /**
- * Start a band select operation at the given point.
- * @param relativeOrigin The origin of the band select operation, relative to the viewport.
- * For example, if the view is scrolled to the bottom, the top-left of the viewport
- * would have a relative origin of (0, 0), even though its absolute point has a higher
- * y-value.
- */
- void startSelection(Point relativeOrigin) {
- mIsActive = true;
- mPointer = mHelper.createAbsolutePoint(relativeOrigin);
-
- recordVisibleChildren();
- mRelativeOrigin = new RelativePoint(mPointer);
- mRelativePointer = new RelativePoint(mPointer);
- computeCurrentSelection();
- notifyListeners();
- }
-
- /**
- * Resizes the selection by adjusting the pointer (i.e., the corner of the selection
- * opposite the origin.
- * @param relativePointer The pointer (opposite of the origin) of the band select operation,
- * relative to the viewport. For example, if the view is scrolled to the bottom, the
- * top-left of the viewport would have a relative origin of (0, 0), even though its
- * absolute point has a higher y-value.
- */
- void resizeSelection(Point relativePointer) {
- mPointer = mHelper.createAbsolutePoint(relativePointer);
- updateModel();
- }
-
- /**
- * Ends the band selection.
- */
- void endSelection() {
- mIsActive = false;
- }
-
- /**
- * @return The adapter position for the item nearest the origin corresponding to the latest
- * band select operation, or NOT_SET if the selection did not cover any items.
- */
- int getPositionNearestOrigin() {
- return mPositionNearestOrigin;
- }
-
- @Override
- public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
- if (!mIsActive) {
- return;
- }
-
- mPointer.x += dx;
- mPointer.y += dy;
- recordVisibleChildren();
- updateModel();
- }
-
- /**
- * Queries the view for all children and records their location metadata.
- */
- private void recordVisibleChildren() {
- for (int i = 0; i < mHelper.getVisibleChildCount(); i++) {
- int adapterPosition = mHelper.getAdapterPositionAt(i);
- if (!mKnownPositions.get(adapterPosition)) {
- mKnownPositions.put(adapterPosition, true);
- recordItemData(
- mHelper.getAbsoluteRectForChildViewAt(i), adapterPosition);
- }
- }
- }
-
- /**
- * Updates the limits lists and column map with the given item metadata.
- * @param absoluteChildRect The absolute rectangle for the child view being processed.
- * @param adapterPosition The position of the child view being processed.
- */
- private void recordItemData(Rect absoluteChildRect, int adapterPosition) {
- if (mColumnBounds.size() != mHelper.getColumnCount()) {
- // If not all x-limits have been recorded, record this one.
- recordLimits(
- mColumnBounds, new Limits(absoluteChildRect.left, absoluteChildRect.right));
- }
-
- if (mRowBounds.size() != mHelper.getRowCount()) {
- // If not all y-limits have been recorded, record this one.
- recordLimits(
- mRowBounds, new Limits(absoluteChildRect.top, absoluteChildRect.bottom));
- }
-
- SparseIntArray columnList = mColumns.get(absoluteChildRect.left);
- if (columnList == null) {
- columnList = new SparseIntArray();
- mColumns.put(absoluteChildRect.left, columnList);
- }
- columnList.put(absoluteChildRect.top, adapterPosition);
- }
-
- /**
- * Ensures limits exists within the sorted list limitsList, and adds it to the list if it
- * does not exist.
- */
- private void recordLimits(List<Limits> limitsList, Limits limits) {
- int index = Collections.binarySearch(limitsList, limits);
- if (index < 0) {
- limitsList.add(~index, limits);
- }
- }
-
- /**
- * Handles a moved pointer; this function determines whether the pointer movement resulted
- * in a selection change and, if it has, notifies listeners of this change.
- */
- private void updateModel() {
- RelativePoint old = mRelativePointer;
- mRelativePointer = new RelativePoint(mPointer);
- if (old != null && mRelativePointer.equals(old)) {
- return;
- }
-
- computeCurrentSelection();
- notifyListeners();
- }
-
- /**
- * Computes the currently-selected items.
- */
- private void computeCurrentSelection() {
- if (areItemsCoveredByBand(mRelativePointer, mRelativeOrigin)) {
- updateSelection(computeBounds());
- } else {
- mSelection.clear();
- mPositionNearestOrigin = NOT_SET;
- }
- }
-
- /**
- * Notifies all listeners of a selection change. Note that this function simply passes
- * mSelection, so computeCurrentSelection() should be called before this
- * function.
- */
- private void notifyListeners() {
- for (OnSelectionChangedListener listener : mOnSelectionChangedListeners) {
- listener.onSelectionChanged(mSelection);
- }
- }
-
- /**
- * @param rect Rectangle including all covered items.
- */
- private void updateSelection(Rect rect) {
- int columnStartIndex =
- Collections.binarySearch(mColumnBounds, new Limits(rect.left, rect.left));
- checkState(columnStartIndex >= 0);
- int columnEndIndex = columnStartIndex;
-
- for (int i = columnStartIndex; i < mColumnBounds.size()
- && mColumnBounds.get(i).lowerLimit <= rect.right; i++) {
- columnEndIndex = i;
- }
-
- SparseIntArray firstColumn =
- mColumns.get(mColumnBounds.get(columnStartIndex).lowerLimit);
- int rowStartIndex = firstColumn.indexOfKey(rect.top);
- if (rowStartIndex < 0) {
- mPositionNearestOrigin = NOT_SET;
- return;
- }
-
- int rowEndIndex = rowStartIndex;
- for (int i = rowStartIndex;
- i < firstColumn.size() && firstColumn.keyAt(i) <= rect.bottom; i++) {
- rowEndIndex = i;
- }
-
- updateSelection(columnStartIndex, columnEndIndex, rowStartIndex, rowEndIndex);
- }
-
- /**
- * Computes the selection given the previously-computed start- and end-indices for each
- * row and column.
- */
- private void updateSelection(
- int columnStartIndex, int columnEndIndex, int rowStartIndex, int rowEndIndex) {
- mSelection.clear();
- for (int column = columnStartIndex; column <= columnEndIndex; column++) {
- SparseIntArray items = mColumns.get(mColumnBounds.get(column).lowerLimit);
- for (int row = rowStartIndex; row <= rowEndIndex; row++) {
- int position = items.get(items.keyAt(row));
- mSelection.append(position, true);
- if (isPossiblePositionNearestOrigin(column, columnStartIndex, columnEndIndex,
- row, rowStartIndex, rowEndIndex)) {
- // If this is the position nearest the origin, record it now so that it
- // can be returned by endSelection() later.
- mPositionNearestOrigin = position;
- }
- }
- }
- }
-
- /**
- * @return Returns true if the position is the nearest to the origin, or, in the case of the
- * lower-right corner, whether it is possible that the position is the nearest to the
- * origin. See comment below for reasoning for this special case.
- */
- private boolean isPossiblePositionNearestOrigin(int columnIndex, int columnStartIndex,
- int columnEndIndex, int rowIndex, int rowStartIndex, int rowEndIndex) {
- int corner = computeCornerNearestOrigin();
- switch (corner) {
- case UPPER_LEFT:
- return columnIndex == columnStartIndex && rowIndex == rowStartIndex;
- case UPPER_RIGHT:
- return columnIndex == columnEndIndex && rowIndex == rowStartIndex;
- case LOWER_LEFT:
- return columnIndex == columnStartIndex && rowIndex == rowEndIndex;
- case LOWER_RIGHT:
- // Note that in some cases, the last row will not have as many items as there
- // are columns (e.g., if there are 4 items and 3 columns, the second row will
- // only have one item in the first column). This function is invoked for each
- // position from left to right, so return true for any position in the bottom
- // row and only the right-most position in the bottom row will be recorded.
- return rowIndex == rowEndIndex;
- default:
- throw new RuntimeException("Invalid corner type.");
- }
- }
-
- /**
- * Listener for changes in which items have been band selected.
- */
- static interface OnSelectionChangedListener {
- public void onSelectionChanged(SparseBooleanArray updatedSelection);
- }
-
- void addOnSelectionChangedListener(OnSelectionChangedListener listener) {
- mOnSelectionChangedListeners.add(listener);
- }
-
- void removeOnSelectionChangedListener(OnSelectionChangedListener listener) {
- mOnSelectionChangedListeners.remove(listener);
- }
-
- /**
- * Limits of a view item. For example, if an item's left side is at x-value 5 and its right side
- * is at x-value 10, the limits would be from 5 to 10. Used to record the left- and right sides
- * of item columns and the top- and bottom sides of item rows so that it can be determined
- * whether the pointer is located within the bounds of an item.
- */
- private static class Limits implements Comparable<Limits> {
- int lowerLimit;
- int upperLimit;
-
- Limits(int lowerLimit, int upperLimit) {
- this.lowerLimit = lowerLimit;
- this.upperLimit = upperLimit;
- }
-
- @Override
- public int compareTo(Limits other) {
- return lowerLimit - other.lowerLimit;
- }
-
- @Override
- public boolean equals(Object other) {
- if (!(other instanceof Limits)) {
- return false;
- }
-
- return ((Limits) other).lowerLimit == lowerLimit &&
- ((Limits) other).upperLimit == upperLimit;
- }
- }
-
- /**
- * The location of a coordinate relative to items. This class represents a general area of the
- * view as it relates to band selection rather than an explicit point. For example, two
- * different points within an item are considered to have the same "location" because band
- * selection originating within the item would select the same items no matter which point
- * was used. Same goes for points between items as well as those at the very beginning or end
- * of the view.
- *
- * Tracking a coordinate (e.g., an x-value) as a CoordinateLocation instead of as an int has the
- * advantage of tying the value to the Limits of items along that axis. This allows easy
- * selection of items within those Limits as opposed to a search through every item to see if a
- * given coordinate value falls within those Limits.
- */
- private static class RelativeCoordinate
- implements Comparable<RelativeCoordinate> {
- /**
- * Location describing points after the last known item.
- */
- static final int AFTER_LAST_ITEM = 0;
-
- /**
- * Location describing points before the first known item.
- */
- static final int BEFORE_FIRST_ITEM = 1;
-
- /**
- * Location describing points between two items.
- */
- static final int BETWEEN_TWO_ITEMS = 2;
-
- /**
- * Location describing points within the limits of one item.
- */
- static final int WITHIN_LIMITS = 3;
-
- /**
- * The type of this coordinate, which is one of AFTER_LAST_ITEM, BEFORE_FIRST_ITEM,
- * BETWEEN_TWO_ITEMS, or WITHIN_LIMITS.
- */
- final int type;
-
- /**
- * The limits before the coordinate; only populated when type == WITHIN_LIMITS or type ==
- * BETWEEN_TWO_ITEMS.
- */
- Limits limitsBeforeCoordinate;
-
- /**
- * The limits after the coordinate; only populated when type == BETWEEN_TWO_ITEMS.
- */
- Limits limitsAfterCoordinate;
-
- // Limits of the first known item; only populated when type == BEFORE_FIRST_ITEM.
- Limits mFirstKnownItem;
- // Limits of the last known item; only populated when type == AFTER_LAST_ITEM.
- Limits mLastKnownItem;
-
- /**
- * @param limitsList The sorted limits list for the coordinate type. If this
- * CoordinateLocation is an x-value, mXLimitsList should be passed; otherwise,
- * mYLimitsList should be pased.
- * @param value The coordinate value.
- */
- RelativeCoordinate(List<Limits> limitsList, int value) {
- int index = Collections.binarySearch(limitsList, new Limits(value, value));
-
- if (index >= 0) {
- this.type = WITHIN_LIMITS;
- this.limitsBeforeCoordinate = limitsList.get(index);
- } else if (~index == 0) {
- this.type = BEFORE_FIRST_ITEM;
- this.mFirstKnownItem = limitsList.get(0);
- } else if (~index == limitsList.size()) {
- Limits lastLimits = limitsList.get(limitsList.size() - 1);
- if (lastLimits.lowerLimit <= value && value <= lastLimits.upperLimit) {
- this.type = WITHIN_LIMITS;
- this.limitsBeforeCoordinate = lastLimits;
- } else {
- this.type = AFTER_LAST_ITEM;
- this.mLastKnownItem = lastLimits;
- }
- } else {
- Limits limitsBeforeIndex = limitsList.get(~index - 1);
- if (limitsBeforeIndex.lowerLimit <= value && value <= limitsBeforeIndex.upperLimit) {
- this.type = WITHIN_LIMITS;
- this.limitsBeforeCoordinate = limitsList.get(~index - 1);
- } else {
- this.type = BETWEEN_TWO_ITEMS;
- this.limitsBeforeCoordinate = limitsList.get(~index - 1);
- this.limitsAfterCoordinate = limitsList.get(~index);
- }
- }
- }
-
- int toComparisonValue() {
- if (type == BEFORE_FIRST_ITEM) {
- return mFirstKnownItem.lowerLimit - 1;
- } else if (type == AFTER_LAST_ITEM) {
- return mLastKnownItem.upperLimit + 1;
- } else if (type == BETWEEN_TWO_ITEMS) {
- return limitsBeforeCoordinate.upperLimit + 1;
- } else {
- return limitsBeforeCoordinate.lowerLimit;
- }
- }
-
- @Override
- public boolean equals(Object other) {
- if (!(other instanceof RelativeCoordinate)) {
- return false;
- }
-
- RelativeCoordinate otherCoordinate = (RelativeCoordinate) other;
- return toComparisonValue() == otherCoordinate.toComparisonValue();
- }
-
- @Override
- public int compareTo(RelativeCoordinate other) {
- return toComparisonValue() - other.toComparisonValue();
- }
- }
-
- /**
- * The location of a point relative to the Limits of nearby items; consists of both an x- and
- * y-RelativeCoordinateLocation.
- */
- private class RelativePoint {
- final RelativeCoordinate xLocation;
- final RelativeCoordinate yLocation;
-
- RelativePoint(Point point) {
- this.xLocation = new RelativeCoordinate(mColumnBounds, point.x);
- this.yLocation = new RelativeCoordinate(mRowBounds, point.y);
- }
-
- @Override
- public boolean equals(Object other) {
- if (!(other instanceof RelativePoint)) {
- return false;
- }
-
- RelativePoint otherPoint = (RelativePoint) other;
- return xLocation.equals(otherPoint.xLocation) && yLocation.equals(otherPoint.yLocation);
- }
- }
-
- /**
- * Generates a rectangle which contains the items selected by the pointer and origin.
- * @return The rectangle, or null if no items were selected.
- */
- private Rect computeBounds() {
- Rect rect = new Rect();
- rect.left = getCoordinateValue(
- min(mRelativeOrigin.xLocation, mRelativePointer.xLocation),
- mColumnBounds,
- true);
- rect.right = getCoordinateValue(
- max(mRelativeOrigin.xLocation, mRelativePointer.xLocation),
- mColumnBounds,
- false);
- rect.top = getCoordinateValue(
- min(mRelativeOrigin.yLocation, mRelativePointer.yLocation),
- mRowBounds,
- true);
- rect.bottom = getCoordinateValue(
- max(mRelativeOrigin.yLocation, mRelativePointer.yLocation),
- mRowBounds,
- false);
- return rect;
- }
-
- /**
- * Computes the corner of the selection nearest the origin.
- * @return
- */
- private int computeCornerNearestOrigin() {
- int cornerValue = 0;
-
- if (mRelativeOrigin.yLocation ==
- min(mRelativeOrigin.yLocation, mRelativePointer.yLocation)) {
- cornerValue |= UPPER;
- } else {
- cornerValue |= LOWER;
- }
-
- if (mRelativeOrigin.xLocation ==
- min(mRelativeOrigin.xLocation, mRelativePointer.xLocation)) {
- cornerValue |= LEFT;
- } else {
- cornerValue |= RIGHT;
- }
-
- return cornerValue;
- }
-
- private RelativeCoordinate min(RelativeCoordinate first, RelativeCoordinate second) {
- return first.compareTo(second) < 0 ? first : second;
- }
-
- private RelativeCoordinate max(RelativeCoordinate first, RelativeCoordinate second) {
- return first.compareTo(second) > 0 ? first : second;
- }
-
- /**
- * @return The absolute coordinate (i.e., the x- or y-value) of the given relative
- * coordinate.
- */
- private int getCoordinateValue(RelativeCoordinate coordinate,
- List<Limits> limitsList, boolean isStartOfRange) {
- switch (coordinate.type) {
- case RelativeCoordinate.BEFORE_FIRST_ITEM:
- return limitsList.get(0).lowerLimit;
- case RelativeCoordinate.AFTER_LAST_ITEM:
- return limitsList.get(limitsList.size() - 1).upperLimit;
- case RelativeCoordinate.BETWEEN_TWO_ITEMS:
- if (isStartOfRange) {
- return coordinate.limitsAfterCoordinate.lowerLimit;
- } else {
- return coordinate.limitsBeforeCoordinate.upperLimit;
- }
- case RelativeCoordinate.WITHIN_LIMITS:
- return coordinate.limitsBeforeCoordinate.lowerLimit;
- }
-
- throw new RuntimeException("Invalid coordinate value.");
- }
-
- private boolean areItemsCoveredByBand(
- RelativePoint first, RelativePoint second) {
- return doesCoordinateLocationCoverItems(first.xLocation, second.xLocation) &&
- doesCoordinateLocationCoverItems(first.yLocation, second.yLocation);
- }
-
- private boolean doesCoordinateLocationCoverItems(
- RelativeCoordinate pointerCoordinate,
- RelativeCoordinate originCoordinate) {
- if (pointerCoordinate.type == RelativeCoordinate.BEFORE_FIRST_ITEM &&
- originCoordinate.type == RelativeCoordinate.BEFORE_FIRST_ITEM) {
- return false;
- }
-
- if (pointerCoordinate.type == RelativeCoordinate.AFTER_LAST_ITEM &&
- originCoordinate.type == RelativeCoordinate.AFTER_LAST_ITEM) {
- return false;
- }
-
- if (pointerCoordinate.type == RelativeCoordinate.BETWEEN_TWO_ITEMS &&
- originCoordinate.type == RelativeCoordinate.BETWEEN_TWO_ITEMS &&
- pointerCoordinate.limitsBeforeCoordinate.equals(
- originCoordinate.limitsBeforeCoordinate) &&
- pointerCoordinate.limitsAfterCoordinate.equals(
- originCoordinate.limitsAfterCoordinate)) {
- return false;
- }
-
- return true;
- }
- }
-}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java b/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java
index 607cb95..605c530 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java
@@ -25,11 +25,13 @@
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.database.Cursor;
import android.net.Uri;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.support.annotation.Nullable;
+import android.text.TextUtils;
import android.util.Log;
import com.android.documentsui.BaseActivity.DocumentContext;
@@ -43,13 +45,20 @@
private final DocumentInfo mDocument;
private final DocumentContext mContext;
- public ClipData mClipData;
- public int mDocumentLocation;
- private PackageManager mPkgManager;
+ private final PackageManager mPkgManager;
+ private final Resources mResources;
+
+ private ClipData mClipData;
+ private int mDocumentLocation;
public QuickViewIntentBuilder(
- PackageManager pkgManager, DocumentInfo doc, DocumentContext context) {
+ PackageManager pkgManager,
+ Resources resources,
+ DocumentInfo doc,
+ DocumentContext context) {
+
mPkgManager = pkgManager;
+ mResources = resources;
mDocument = doc;
mContext = context;
}
@@ -61,25 +70,39 @@
@Nullable Intent build() {
if (DEBUG) Log.d(TAG, "Preparing intent for doc:" + mDocument.documentId);
+ String trustedPkg = mResources.getString(R.string.trusted_quick_viewer_package);
+
Intent intent = new Intent(Intent.ACTION_QUICK_VIEW);
intent.setDataAndType(mDocument.derivedUri, mDocument.mimeType);
-
- // Try to resolve the intent. If a matching app isn't installed, it won't resolve.
- ComponentName handler = intent.resolveActivity(mPkgManager);
- if (handler == null) {
- return null;
- }
-
- Cursor cursor = mContext.getCursor();
- for (int i = 0; i < cursor.getCount(); i++) {
- onNextItem(i, cursor);
- }
-
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- intent.putExtra(Intent.EXTRA_INDEX, mDocumentLocation);
- intent.setClipData(mClipData);
- return intent;
+ if (TextUtils.isEmpty(trustedPkg)) {
+ if (hasRegisteredHandler(intent)) {
+ return intent;
+ }
+ } else {
+ intent.setPackage(trustedPkg);
+ if (hasRegisteredHandler(intent)) {
+ // We have a trusted handler. Load all of the docs into the intent.
+ Cursor cursor = mContext.getCursor();
+ for (int i = 0; i < cursor.getCount(); i++) {
+ onNextItem(i, cursor);
+ }
+ intent.putExtra(Intent.EXTRA_INDEX, mDocumentLocation);
+ intent.setClipData(mClipData);
+
+ return intent;
+ } else {
+ Log.e(TAG, "Can't resolve trusted quick view package: " + trustedPkg);
+ }
+ }
+
+ return null;
+ }
+
+ private boolean hasRegisteredHandler(Intent intent) {
+ // Try to resolve the intent. If a matching app isn't installed, it won't resolve.
+ return intent.resolveActivity(mPkgManager) != null;
}
private void onNextItem(int index, Cursor cursor) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java
index c2b64fb..4bd6ae6 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java
@@ -36,7 +36,7 @@
import android.util.Log;
import com.android.documentsui.model.RootInfo;
-
+import com.android.internal.annotations.GuardedBy;
import com.google.common.util.concurrent.AbstractFuture;
import libcore.io.IoUtils;
@@ -79,6 +79,7 @@
private final RootsCache mRoots;
private final State mState;
+ @GuardedBy("mTasks")
private final HashMap<RootInfo, RecentTask> mTasks = new HashMap<>();
private final int mSortOrder = State.SORT_ORDER_LAST_MODIFIED;
@@ -165,6 +166,12 @@
@Override
public DirectoryResult loadInBackground() {
+ synchronized (mTasks) {
+ return loadInBackgroundLocked();
+ }
+ }
+
+ private DirectoryResult loadInBackgroundLocked() {
if (mFirstPassLatch == null) {
// First time through we kick off all the recent tasks, and wait
// around to see if everyone finishes quickly.
@@ -302,8 +309,10 @@
// Ensure the loader is stopped
onStopLoading();
- for (RecentTask task : mTasks.values()) {
- IoUtils.closeQuietly(task);
+ synchronized (mTasks) {
+ for (RecentTask task : mTasks.values()) {
+ IoUtils.closeQuietly(task);
+ }
}
IoUtils.closeQuietly(mResult);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
index de35cef..4fc3788 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
@@ -102,6 +102,7 @@
mRecentsRoot.authority = null;
mRecentsRoot.rootId = null;
mRecentsRoot.derivedIcon = R.drawable.ic_root_recent;
+ mRecentsRoot.derivedType = RootInfo.TYPE_RECENTS;
mRecentsRoot.flags = Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_CREATE
| Root.FLAG_SUPPORTS_IS_CHILD;
mRecentsRoot.title = mContext.getString(R.string.root_recent);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
index c98da47..beff196 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
@@ -16,6 +16,8 @@
package com.android.documentsui;
+import static com.android.documentsui.Shared.DEBUG;
+
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
@@ -30,6 +32,7 @@
import android.provider.Settings;
import android.text.TextUtils;
import android.text.format.Formatter;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -56,12 +59,13 @@
*/
public class RootsFragment extends Fragment {
+ private static final String TAG = "RootsFragment";
+ private static final String EXTRA_INCLUDE_APPS = "includeApps";
+
private ListView mList;
private RootsAdapter mAdapter;
-
private LoaderCallbacks<Collection<RootInfo>> mCallbacks;
- private static final String EXTRA_INCLUDE_APPS = "includeApps";
public static void show(FragmentManager fm, Intent includeApps) {
final Bundle args = new Bundle();
@@ -180,6 +184,8 @@
} else if (item instanceof AppItem) {
DocumentsActivity activity = DocumentsActivity.get(RootsFragment.this);
activity.onAppPicked(((AppItem) item).info);
+ } else if (item instanceof SpacerItem) {
+ if (DEBUG) Log.d(TAG, "Ignoring click on spacer item.");
} else {
throw new IllegalStateException("Unknown root: " + item);
}
@@ -286,51 +292,25 @@
public RootsAdapter(Context context, Collection<RootInfo> roots, Intent includeApps) {
super(context, 0);
- RootItem recents = null;
- RootItem images = null;
- RootItem videos = null;
- RootItem audio = null;
- RootItem downloads = null;
+ final List<RootItem> libraries = new ArrayList<>();
+ final List<RootItem> others = new ArrayList<>();
- final List<RootInfo> clouds = new ArrayList<>();
- final List<RootInfo> locals = new ArrayList<>();
-
- for (RootInfo root : roots) {
- if (root.isRecents()) {
- recents = new RootItem(root);
- } else if (root.isExternalStorage()) {
- locals.add(root);
- } else if (root.isDownloads()) {
- downloads = new RootItem(root);
- } else if (root.isImages()) {
- images = new RootItem(root);
- } else if (root.isVideos()) {
- videos = new RootItem(root);
- } else if (root.isAudio()) {
- audio = new RootItem(root);
+ for (final RootInfo root : roots) {
+ final RootItem item = new RootItem(root);
+ if (root.isLibrary()) {
+ libraries.add(item);
} else {
- clouds.add(root);
+ others.add(item);
}
}
final RootComparator comp = new RootComparator();
- Collections.sort(clouds, comp);
- Collections.sort(locals, comp);
+ Collections.sort(libraries, comp);
+ Collections.sort(others, comp);
- if (recents != null) add(recents);
-
- for (RootInfo cloud : clouds) {
- add(new RootItem(cloud));
- }
-
- if (images != null) add(images);
- if (videos != null) add(videos);
- if (audio != null) add(audio);
- if (downloads != null) add(downloads);
-
- for (RootInfo local : locals) {
- add(new RootItem(local));
- }
+ addAll(libraries);
+ add(new SpacerItem());
+ addAll(others);
if (includeApps != null) {
final PackageManager pm = context.getPackageManager();
@@ -348,9 +328,7 @@
if (apps.size() > 0) {
add(new SpacerItem());
- for (Item item : apps) {
- add(item);
- }
+ addAll(apps);
}
}
}
@@ -387,15 +365,20 @@
}
}
- public static class RootComparator implements Comparator<RootInfo> {
+ public static class RootComparator implements Comparator<RootItem> {
@Override
- public int compare(RootInfo lhs, RootInfo rhs) {
- final int score = DocumentInfo.compareToIgnoreCaseNullable(lhs.title, rhs.title);
+ public int compare(RootItem lhs, RootItem rhs) {
+ // Sort by root type, then title, then summary.
+ int score = lhs.root.derivedType - rhs.root.derivedType;
if (score != 0) {
return score;
- } else {
- return DocumentInfo.compareToIgnoreCaseNullable(lhs.summary, rhs.summary);
}
+ score = DocumentInfo.compareToIgnoreCaseNullable(lhs.root.title, rhs.root.title);
+ if (score != 0) {
+ return score;
+ }
+
+ return DocumentInfo.compareToIgnoreCaseNullable(lhs.root.summary, rhs.root.summary);
}
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java b/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
index ce98db2..f3b750a 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
@@ -23,6 +23,7 @@
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
+import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -79,6 +80,17 @@
mDisplayName = (EditText) view.findViewById(android.R.id.title);
mDisplayName.addTextChangedListener(mDisplayNameWatcher);
mDisplayName.setText(getArguments().getString(EXTRA_DISPLAY_NAME));
+ mDisplayName.setOnKeyListener(
+ new View.OnKeyListener() {
+ @Override
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_ENTER && mSave.isEnabled()) {
+ performSave();
+ return true;
+ }
+ return false;
+ }
+ });
mSave = (Button) view.findViewById(android.R.id.button1);
mSave.setOnClickListener(mSaveListener);
@@ -113,17 +125,22 @@
private View.OnClickListener mSaveListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
- final DocumentsActivity activity = DocumentsActivity.get(SaveFragment.this);
- if (mReplaceTarget != null) {
- activity.onSaveRequested(mReplaceTarget);
- } else {
- final String mimeType = getArguments().getString(EXTRA_MIME_TYPE);
- final String displayName = mDisplayName.getText().toString();
- activity.onSaveRequested(mimeType, displayName);
- }
+ performSave();
}
+
};
+ private void performSave() {
+ final DocumentsActivity activity = DocumentsActivity.get(SaveFragment.this);
+ if (mReplaceTarget != null) {
+ activity.onSaveRequested(mReplaceTarget);
+ } else {
+ final String mimeType = getArguments().getString(EXTRA_MIME_TYPE);
+ final String displayName = mDisplayName.getText().toString();
+ activity.onSaveRequested(mimeType, displayName);
+ }
+ }
+
/**
* Set given document as target for in-place writing if user hits save
* without changing the filename. Can be set to {@code null} if user
@@ -139,7 +156,11 @@
}
}
- public void setSaveEnabled(boolean enabled) {
+ public void prepareForDirectory(DocumentInfo cwd) {
+ setSaveEnabled(cwd != null && cwd.isCreateSupported());
+ }
+
+ private void setSaveEnabled(boolean enabled) {
mSave.setEnabled(enabled);
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Snackbars.java b/packages/DocumentsUI/src/com/android/documentsui/Snackbars.java
index f48b298..48c1a73 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Snackbars.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Snackbars.java
@@ -22,7 +22,7 @@
import android.support.design.widget.Snackbar;
import android.view.View;
-final class Snackbars {
+public final class Snackbars {
private Snackbars() {}
public static final Snackbar makeSnackbar(Activity activity, int messageId, int duration) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
new file mode 100644
index 0000000..21420c8
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -0,0 +1,2014 @@
+/*
+ * 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.documentsui.dirlist;
+
+import static com.android.documentsui.Shared.DEBUG;
+import static com.android.documentsui.State.ACTION_CREATE;
+import static com.android.documentsui.State.ACTION_MANAGE;
+import static com.android.documentsui.State.MODE_GRID;
+import static com.android.documentsui.State.MODE_LIST;
+import static com.android.documentsui.State.MODE_UNKNOWN;
+import static com.android.documentsui.State.SORT_ORDER_UNKNOWN;
+import static com.android.documentsui.model.DocumentInfo.getCursorInt;
+import static com.android.documentsui.model.DocumentInfo.getCursorLong;
+import static com.android.documentsui.model.DocumentInfo.getCursorString;
+import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.internal.util.Preconditions.checkState;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
+import android.app.LoaderManager.LoaderCallbacks;
+import android.content.ClipData;
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.Loader;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Point;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.Looper;
+import android.os.OperationCanceledException;
+import android.os.Parcelable;
+import android.provider.DocumentsContract;
+import android.provider.DocumentsContract.Document;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import android.support.design.widget.Snackbar;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.RecyclerView.LayoutManager;
+import android.support.v7.widget.RecyclerView.OnItemTouchListener;
+import android.support.v7.widget.RecyclerView.RecyclerListener;
+import android.support.v7.widget.RecyclerView.ViewHolder;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
+import android.text.format.Formatter;
+import android.text.format.Time;
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import android.util.TypedValue;
+import android.view.ActionMode;
+import android.view.DragEvent;
+import android.view.GestureDetector;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.documentsui.BaseActivity;
+import com.android.documentsui.CopyService;
+import com.android.documentsui.DirectoryLoader;
+import com.android.documentsui.DirectoryResult;
+import com.android.documentsui.DocumentClipper;
+import com.android.documentsui.DocumentsActivity;
+import com.android.documentsui.DocumentsApplication;
+import com.android.documentsui.Events;
+import com.android.documentsui.IconUtils;
+import com.android.documentsui.Menus;
+import com.android.documentsui.MessageBar;
+import com.android.documentsui.MimePredicate;
+import com.android.documentsui.ProviderExecutor;
+import com.android.documentsui.R;
+import com.android.documentsui.RecentLoader;
+import com.android.documentsui.RecentsProvider;
+import com.android.documentsui.RootCursorWrapper;
+import com.android.documentsui.RootsCache;
+import com.android.documentsui.Shared;
+import com.android.documentsui.Snackbars;
+import com.android.documentsui.State;
+import com.android.documentsui.ThumbnailCache;
+import com.android.documentsui.BaseActivity.DocumentContext;
+import com.android.documentsui.BaseActivity.DocumentsIntent;
+import com.android.documentsui.ProviderExecutor.Preemptable;
+import com.android.documentsui.RecentsProvider.StateColumns;
+import com.android.documentsui.dirlist.MultiSelectManager.Callback;
+import com.android.documentsui.dirlist.MultiSelectManager.Selection;
+import com.android.documentsui.model.DocumentInfo;
+import com.android.documentsui.model.DocumentStack;
+import com.android.documentsui.model.RootInfo;
+import com.android.internal.annotations.GuardedBy;
+
+import com.google.common.collect.Lists;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Display the documents inside a single directory.
+ */
+public class DirectoryFragment extends Fragment {
+
+ public static final String TAG = "DirectoryFragment";
+
+ public static final int TYPE_NORMAL = 1;
+ public static final int TYPE_SEARCH = 2;
+ public static final int TYPE_RECENT_OPEN = 3;
+
+ public static final int ANIM_NONE = 1;
+ public static final int ANIM_SIDE = 2;
+ public static final int ANIM_DOWN = 3;
+ public static final int ANIM_UP = 4;
+
+ public static final int REQUEST_COPY_DESTINATION = 1;
+
+ private static final int LOADER_ID = 42;
+ private static final boolean DEBUG_ENABLE_DND = false;
+
+ private static final String EXTRA_TYPE = "type";
+ private static final String EXTRA_ROOT = "root";
+ private static final String EXTRA_DOC = "doc";
+ private static final String EXTRA_QUERY = "query";
+ private static final String EXTRA_IGNORE_STATE = "ignoreState";
+
+ private Model mModel;
+ private MultiSelectManager mSelectionManager;
+ private Model.UpdateListener mModelUpdateListener = new ModelUpdateListener();
+ private ItemClickListener mItemClickListener = new ItemClickListener();
+
+ private View mEmptyView;
+ private RecyclerView mRecView;
+
+ private int mType = TYPE_NORMAL;
+ private String mStateKey;
+
+ private int mLastMode = MODE_UNKNOWN;
+ private int mLastSortOrder = SORT_ORDER_UNKNOWN;
+ private boolean mLastShowSize;
+ private boolean mHideGridTitles;
+ private boolean mSvelteRecents;
+ private Point mThumbSize;
+ private DocumentsAdapter mAdapter;
+ private LoaderCallbacks<DirectoryResult> mCallbacks;
+ private FragmentTuner mTuner;
+ private DocumentClipper mClipper;
+ // These are lazily initialized.
+ private LinearLayoutManager mListLayout;
+ private GridLayoutManager mGridLayout;
+ private int mColumnCount = 1; // This will get updated when layout changes.
+
+ private MessageBar mMessageBar;
+ private View mProgressBar;
+
+ private int mSelectedItemColor;
+ private int mDefaultItemColor;
+
+ public static void showNormal(FragmentManager fm, RootInfo root, DocumentInfo doc, int anim) {
+ show(fm, TYPE_NORMAL, root, doc, null, anim);
+ }
+
+ public static void showSearch(FragmentManager fm, RootInfo root, String query, int anim) {
+ show(fm, TYPE_SEARCH, root, null, query, anim);
+ }
+
+ public static void showRecentsOpen(FragmentManager fm, int anim) {
+ show(fm, TYPE_RECENT_OPEN, null, null, null, anim);
+ }
+
+ private static void show(FragmentManager fm, int type, RootInfo root, DocumentInfo doc,
+ String query, int anim) {
+ final Bundle args = new Bundle();
+ args.putInt(EXTRA_TYPE, type);
+ args.putParcelable(EXTRA_ROOT, root);
+ args.putParcelable(EXTRA_DOC, doc);
+ args.putString(EXTRA_QUERY, query);
+
+ final FragmentTransaction ft = fm.beginTransaction();
+ switch (anim) {
+ case ANIM_SIDE:
+ args.putBoolean(EXTRA_IGNORE_STATE, true);
+ break;
+ case ANIM_DOWN:
+ args.putBoolean(EXTRA_IGNORE_STATE, true);
+ ft.setCustomAnimations(R.animator.dir_down, R.animator.dir_frozen);
+ break;
+ case ANIM_UP:
+ ft.setCustomAnimations(R.animator.dir_frozen, R.animator.dir_up);
+ break;
+ }
+
+ final DirectoryFragment fragment = new DirectoryFragment();
+ fragment.setArguments(args);
+
+ ft.replace(R.id.container_directory, fragment);
+ ft.commitAllowingStateLoss();
+ }
+
+ private static String buildStateKey(RootInfo root, DocumentInfo doc) {
+ final StringBuilder builder = new StringBuilder();
+ builder.append(root != null ? root.authority : "null").append(';');
+ builder.append(root != null ? root.rootId : "null").append(';');
+ builder.append(doc != null ? doc.documentId : "null");
+ return builder.toString();
+ }
+
+ public static @Nullable DirectoryFragment get(FragmentManager fm) {
+ // TODO: deal with multiple directories shown at once
+ Fragment fragment = fm.findFragmentById(R.id.container_directory);
+ return fragment instanceof DirectoryFragment
+ ? (DirectoryFragment) fragment
+ : null;
+ }
+
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ final View view = inflater.inflate(R.layout.fragment_directory, container, false);
+
+ mMessageBar = MessageBar.create(getChildFragmentManager());
+ mProgressBar = view.findViewById(R.id.progressbar);
+
+ mEmptyView = view.findViewById(android.R.id.empty);
+
+ mRecView = (RecyclerView) view.findViewById(R.id.list);
+ mRecView.setRecyclerListener(
+ new RecyclerListener() {
+ @Override
+ public void onViewRecycled(ViewHolder holder) {
+ cancelThumbnailTask(holder.itemView);
+ }
+ });
+
+ // TODO: Rather than update columns on layout changes, push this
+ // code (or something like it) into GridLayoutManager.
+ mRecView.addOnLayoutChangeListener(
+ new View.OnLayoutChangeListener() {
+
+ @Override
+ public void onLayoutChange(
+ View v, int left, int top, int right, int bottom, int oldLeft,
+ int oldTop, int oldRight, int oldBottom) {
+ mColumnCount = calculateColumnCount();
+ if (mGridLayout != null) {
+ mGridLayout.setSpanCount(mColumnCount);
+ }
+ }
+ });
+
+ mRecView.setItemAnimator(new DirectoryItemAnimator(getActivity()));
+
+ // TODO: Add a divider between views (which might use RecyclerView.ItemDecoration).
+ if (DEBUG_ENABLE_DND) {
+ setupDragAndDropOnDirectoryView(mRecView);
+ }
+
+ return view;
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+
+ // Cancel any outstanding thumbnail requests
+ final int count = mRecView.getChildCount();
+ for (int i = 0; i < count; i++) {
+ final View view = mRecView.getChildAt(i);
+ cancelThumbnailTask(view);
+ }
+
+ // Clear any outstanding selection
+ mSelectionManager.clearSelection();
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ final Context context = getActivity();
+ final State state = getDisplayState();
+
+ final RootInfo root = getArguments().getParcelable(EXTRA_ROOT);
+ final DocumentInfo doc = getArguments().getParcelable(EXTRA_DOC);
+
+ mAdapter = new DocumentsAdapter(context);
+ mRecView.setAdapter(mAdapter);
+
+ mDefaultItemColor = context.getResources().getColor(android.R.color.transparent);
+ // Get the accent color.
+ TypedValue selColor = new TypedValue();
+ context.getTheme().resolveAttribute(android.R.attr.colorAccent, selColor, true);
+ // Set the opacity to 10%.
+ mSelectedItemColor = (selColor.data & 0x00ffffff) | 0x16000000;
+
+ GestureDetector.SimpleOnGestureListener listener =
+ new GestureDetector.SimpleOnGestureListener() {
+ @Override
+ public boolean onSingleTapUp(MotionEvent e) {
+ return DirectoryFragment.this.onSingleTapUp(e);
+ }
+ @Override
+ public boolean onDoubleTap(MotionEvent e) {
+ Log.d(TAG, "Handling double tap.");
+ return DirectoryFragment.this.onDoubleTap(e);
+ }
+ };
+
+ final GestureDetector detector = new GestureDetector(this.getContext(), listener);
+ detector.setOnDoubleTapListener(listener);
+
+ mRecView.addOnItemTouchListener(
+ new OnItemTouchListener() {
+ @Override
+ public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
+ detector.onTouchEvent(e);
+ return false;
+ }
+
+ @Override
+ public void onTouchEvent(RecyclerView rv, MotionEvent e) {}
+
+ @Override
+ public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {}
+ });
+
+ // TODO: instead of inserting the view into the constructor, extract listener-creation code
+ // and set the listener on the view after the fact. Then the view doesn't need to be passed
+ // into the selection manager.
+ mSelectionManager = new MultiSelectManager(
+ mRecView,
+ state.allowMultiple
+ ? MultiSelectManager.MODE_MULTIPLE
+ : MultiSelectManager.MODE_SINGLE);
+ mSelectionManager.addCallback(new SelectionModeListener());
+
+ mModel = new Model(context, mAdapter);
+ mModel.addUpdateListener(mModelUpdateListener);
+
+ mType = getArguments().getInt(EXTRA_TYPE);
+ mStateKey = buildStateKey(root, doc);
+
+ mTuner = FragmentTuner.pick(state);
+ mClipper = new DocumentClipper(context);
+
+ if (mType == TYPE_RECENT_OPEN) {
+ // Hide titles when showing recents for picking images/videos
+ mHideGridTitles = MimePredicate.mimeMatches(
+ MimePredicate.VISUAL_MIMES, state.acceptMimes);
+ } else {
+ mHideGridTitles = (doc != null) && doc.isGridTitlesHidden();
+ }
+
+ final ActivityManager am = (ActivityManager) context.getSystemService(
+ Context.ACTIVITY_SERVICE);
+ mSvelteRecents = am.isLowRamDevice() && (mType == TYPE_RECENT_OPEN);
+
+ mCallbacks = new LoaderCallbacks<DirectoryResult>() {
+ @Override
+ public Loader<DirectoryResult> onCreateLoader(int id, Bundle args) {
+ final String query = getArguments().getString(EXTRA_QUERY);
+
+ Uri contentsUri;
+ switch (mType) {
+ case TYPE_NORMAL:
+ contentsUri = DocumentsContract.buildChildDocumentsUri(
+ doc.authority, doc.documentId);
+ if (state.action == ACTION_MANAGE) {
+ contentsUri = DocumentsContract.setManageMode(contentsUri);
+ }
+ return new DirectoryLoader(
+ context, mType, root, doc, contentsUri, state.userSortOrder);
+ case TYPE_SEARCH:
+ contentsUri = DocumentsContract.buildSearchDocumentsUri(
+ root.authority, root.rootId, query);
+ if (state.action == ACTION_MANAGE) {
+ contentsUri = DocumentsContract.setManageMode(contentsUri);
+ }
+ return new DirectoryLoader(
+ context, mType, root, doc, contentsUri, state.userSortOrder);
+ case TYPE_RECENT_OPEN:
+ final RootsCache roots = DocumentsApplication.getRootsCache(context);
+ return new RecentLoader(context, roots, state);
+ default:
+ throw new IllegalStateException("Unknown type " + mType);
+ }
+ }
+
+ @Override
+ public void onLoadFinished(Loader<DirectoryResult> loader, DirectoryResult result) {
+ if (!isAdded()) return;
+
+ mModel.update(result);
+
+ // Push latest state up to UI
+ // TODO: if mode change was racing with us, don't overwrite it
+ if (result.mode != MODE_UNKNOWN) {
+ state.derivedMode = result.mode;
+ }
+ state.derivedSortOrder = result.sortOrder;
+ ((BaseActivity) context).onStateChanged();
+
+ updateDisplayState();
+
+ // When launched into empty recents, show drawer
+ if (mType == TYPE_RECENT_OPEN && mModel.isEmpty() && !state.stackTouched &&
+ context instanceof DocumentsActivity) {
+ ((DocumentsActivity) context).setRootsDrawerOpen(true);
+ }
+
+ // Restore any previous instance state
+ final SparseArray<Parcelable> container = state.dirState.remove(mStateKey);
+ if (container != null && !getArguments().getBoolean(EXTRA_IGNORE_STATE, false)) {
+ getView().restoreHierarchyState(container);
+ } else if (mLastSortOrder != state.derivedSortOrder) {
+ mRecView.smoothScrollToPosition(0);
+ }
+
+ mLastSortOrder = state.derivedSortOrder;
+ }
+
+ @Override
+ public void onLoaderReset(Loader<DirectoryResult> loader) {
+ mModel.update(null);
+ }
+ };
+
+ // Kick off loader at least once
+ getLoaderManager().restartLoader(LOADER_ID, null, mCallbacks);
+
+ updateDisplayState();
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ // There's only one request code right now. Replace this with a switch statement or
+ // something more scalable when more codes are added.
+ if (requestCode != REQUEST_COPY_DESTINATION) {
+ return;
+ }
+ if (resultCode == Activity.RESULT_CANCELED || data == null) {
+ // User pressed the back button or otherwise cancelled the destination pick. Don't
+ // proceed with the copy.
+ return;
+ }
+
+ CopyService.start(getActivity(), getDisplayState().selectedDocumentsForCopy,
+ (DocumentStack) data.getParcelableExtra(Shared.EXTRA_STACK),
+ data.getIntExtra(CopyService.EXTRA_TRANSFER_MODE, CopyService.TRANSFER_MODE_COPY));
+ }
+
+ private int getEventAdapterPosition(MotionEvent e) {
+ View view = mRecView.findChildViewUnder(e.getX(), e.getY());
+ return view != null ? mRecView.getChildAdapterPosition(view) : RecyclerView.NO_POSITION;
+ }
+
+ private boolean onSingleTapUp(MotionEvent e) {
+ // Only respond to touch events. Single-click mouse events are selection events and are
+ // handled by the selection manager. Tap events that occur while the selection manager is
+ // active are also selection events.
+ if (Events.isTouchEvent(e) && !mSelectionManager.hasSelection()) {
+ int position = getEventAdapterPosition(e);
+ if (position != RecyclerView.NO_POSITION) {
+ return handleViewItem(position);
+ }
+ }
+ return false;
+ }
+
+ protected boolean onDoubleTap(MotionEvent e) {
+ if (Events.isMouseEvent(e)) {
+ Log.d(TAG, "Handling double tap from mouse.");
+ int position = getEventAdapterPosition(e);
+ if (position != RecyclerView.NO_POSITION) {
+ return handleViewItem(position);
+ }
+ }
+ return false;
+ }
+
+ private boolean handleViewItem(int position) {
+ final Cursor cursor = mModel.getItem(position);
+ checkNotNull(cursor, "Cursor cannot be null.");
+ final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
+ final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
+ if (mTuner.isDocumentEnabled(docMimeType, docFlags)) {
+ final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(cursor);
+ ((BaseActivity) getActivity()).onDocumentPicked(doc, mModel);
+ mSelectionManager.clearSelection();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+
+ // Remember last scroll location
+ final SparseArray<Parcelable> container = new SparseArray<Parcelable>();
+ getView().saveHierarchyState(container);
+ final State state = getDisplayState();
+ state.dirState.put(mStateKey, container);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ updateDisplayState();
+ }
+
+ public void onDisplayStateChanged() {
+ updateDisplayState();
+ }
+
+ public void onUserSortOrderChanged() {
+ // Sort order change always triggers reload; we'll trigger state change
+ // on the flip side.
+ getLoaderManager().restartLoader(LOADER_ID, null, mCallbacks);
+ }
+
+ public void onUserModeChanged() {
+ final ContentResolver resolver = getActivity().getContentResolver();
+ final State state = getDisplayState();
+
+ final RootInfo root = getArguments().getParcelable(EXTRA_ROOT);
+ final DocumentInfo doc = getArguments().getParcelable(EXTRA_DOC);
+
+ if (root != null && doc != null) {
+ final Uri stateUri = RecentsProvider.buildState(
+ root.authority, root.rootId, doc.documentId);
+ final ContentValues values = new ContentValues();
+ values.put(StateColumns.MODE, state.userMode);
+
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ resolver.insert(stateUri, values);
+ return null;
+ }
+ }.execute();
+ }
+
+ // Mode change is just visual change; no need to kick loader, and
+ // deliver change event immediately.
+ state.derivedMode = state.userMode;
+ ((BaseActivity) getActivity()).onStateChanged();
+
+ updateDisplayState();
+ }
+
+ private void updateDisplayState() {
+ final State state = getDisplayState();
+
+ if (mLastMode == state.derivedMode && mLastShowSize == state.showSize) return;
+ mLastMode = state.derivedMode;
+ mLastShowSize = state.showSize;
+
+ updateLayout(state.derivedMode);
+
+ mRecView.setAdapter(mAdapter);
+ }
+
+ /**
+ * Returns a {@code LayoutManager} for {@code mode}, lazily initializing
+ * classes as needed.
+ */
+ private void updateLayout(int mode) {
+ final int thumbSize;
+
+ final LayoutManager layout;
+ switch (mode) {
+ case MODE_GRID:
+ thumbSize = getResources().getDimensionPixelSize(R.dimen.grid_width);
+ if (mGridLayout == null) {
+ mGridLayout = new GridLayoutManager(getContext(), mColumnCount );
+ }
+ layout = mGridLayout;
+ break;
+ case MODE_LIST:
+ thumbSize = getResources().getDimensionPixelSize(R.dimen.icon_size);
+ if (mListLayout == null) {
+ mListLayout = new LinearLayoutManager(getContext());
+ }
+ layout = mListLayout;
+ break;
+ case MODE_UNKNOWN:
+ default:
+ throw new IllegalArgumentException("Unsupported layout mode: " + mode);
+ }
+
+ mRecView.setLayoutManager(layout);
+ // TODO: Once b/23691541 is resolved, use a listener within MultiSelectManager instead of
+ // imperatively calling this function.
+ mSelectionManager.handleLayoutChanged();
+ // setting layout manager automatically invalidates existing ViewHolders.
+ mThumbSize = new Point(thumbSize, thumbSize);
+ }
+
+ private int calculateColumnCount() {
+ int cellWidth = getResources().getDimensionPixelSize(R.dimen.grid_width);
+ int cellMargin = 2 * getResources().getDimensionPixelSize(R.dimen.grid_item_margin);
+ int viewPadding = mRecView.getPaddingLeft() + mRecView.getPaddingRight();
+
+ checkState(mRecView.getWidth() > 0);
+ int columnCount = Math.max(1,
+ (mRecView.getWidth() - viewPadding) / (cellWidth + cellMargin));
+
+ return columnCount;
+ }
+
+ /**
+ * Manages the integration between our ActionMode and MultiSelectManager, initiating
+ * ActionMode when there is a selection, canceling it when there is no selection,
+ * and clearing selection when action mode is explicitly exited by the user.
+ */
+ private final class SelectionModeListener
+ implements MultiSelectManager.Callback, ActionMode.Callback {
+
+ private Selection mSelected = new Selection();
+ private ActionMode mActionMode;
+ private int mNoDeleteCount = 0;
+ private Menu mMenu;
+
+ @Override
+ public boolean onBeforeItemStateChange(int position, boolean selected) {
+ if (selected) {
+ final Cursor cursor = mModel.getItem(position);
+ checkNotNull(cursor, "Cursor cannot be null.");
+ final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
+ final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
+ return mTuner.canSelectType(docMimeType)
+ && mTuner.isDocumentEnabled(docMimeType, docFlags);
+ }
+ return true;
+ }
+
+ @Override
+ public void onItemStateChanged(int position, boolean selected) {
+ final Cursor cursor = mModel.getItem(position);
+ checkNotNull(cursor, "Cursor cannot be null.");
+
+ final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
+ if ((docFlags & Document.FLAG_SUPPORTS_DELETE) == 0) {
+ mNoDeleteCount += selected ? 1 : -1;
+ }
+ }
+
+ @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) {
+ mActionMode.setTitle(String.valueOf(mSelected.size()));
+ }
+ }
+
+ // Called when the user exits the action mode
+ @Override
+ public void onDestroyActionMode(ActionMode mode) {
+ if (DEBUG) Log.d(TAG, "Handling action mode destroyed.");
+ mActionMode = null;
+ // clear selection
+ mSelectionManager.clearSelection();
+ mSelected.clear();
+ mNoDeleteCount = 0;
+ }
+
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ int size = mSelectionManager.getSelection().size();
+ mode.getMenuInflater().inflate(R.menu.mode_directory, menu);
+ mode.setTitle(TextUtils.formatSelectedCount(size));
+ return (size > 0);
+ }
+
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ mMenu = menu;
+ updateActionMenu();
+ return true;
+ }
+
+ private void updateActionMenu() {
+ checkNotNull(mMenu);
+ // Delegate update logic to our owning action, since specialized logic is desired.
+ mTuner.updateActionMenu(mMenu, mType, mNoDeleteCount == 0);
+ Menus.disableHiddenItems(mMenu);
+ }
+
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+
+ Selection selection = mSelectionManager.getSelection(new Selection());
+
+ final int id = item.getItemId();
+ if (id == R.id.menu_open) {
+ openDocuments(selection);
+ mode.finish();
+ return true;
+
+ } else if (id == R.id.menu_share) {
+ shareDocuments(selection);
+ mode.finish();
+ return true;
+
+ } else if (id == R.id.menu_delete) {
+ // Exit selection mode first, so we avoid deselecting deleted documents.
+ mode.finish();
+ deleteDocuments(selection);
+ return true;
+
+ } else if (id == R.id.menu_copy_to) {
+ transferDocuments(selection, CopyService.TRANSFER_MODE_COPY);
+ mode.finish();
+ return true;
+
+ } else if (id == R.id.menu_move_to) {
+ // Exit selection mode first, so we avoid deselecting deleted documents.
+ mode.finish();
+ transferDocuments(selection, CopyService.TRANSFER_MODE_MOVE);
+ return true;
+
+ } else if (id == R.id.menu_copy_to_clipboard) {
+ copySelectionToClipboard(selection);
+ mode.finish();
+ return true;
+
+ } else if (id == R.id.menu_select_all) {
+ selectAllFiles();
+ return true;
+
+ } else {
+ return false;
+ }
+ }
+ }
+
+ private static void cancelThumbnailTask(View view) {
+ final ImageView iconThumb = (ImageView) view.findViewById(R.id.icon_thumb);
+ if (iconThumb != null) {
+ final ThumbnailAsyncTask oldTask = (ThumbnailAsyncTask) iconThumb.getTag();
+ if (oldTask != null) {
+ oldTask.preempt();
+ iconThumb.setTag(null);
+ }
+ }
+ }
+
+ private void openDocuments(final Selection selected) {
+ new GetDocumentsTask() {
+ @Override
+ void onDocumentsReady(List<DocumentInfo> docs) {
+ // TODO: Implement support in Files activity for opening multiple docs.
+ BaseActivity.get(DirectoryFragment.this).onDocumentsPicked(docs);
+ }
+ }.execute(selected);
+ }
+
+ private void shareDocuments(final Selection selected) {
+ new GetDocumentsTask() {
+ @Override
+ void onDocumentsReady(List<DocumentInfo> docs) {
+ Intent intent;
+
+ // Filter out directories - those can't be shared.
+ List<DocumentInfo> docsForSend = new ArrayList<>();
+ for (DocumentInfo doc: docs) {
+ if (!Document.MIME_TYPE_DIR.equals(doc.mimeType)) {
+ docsForSend.add(doc);
+ }
+ }
+
+ if (docsForSend.size() == 1) {
+ final DocumentInfo doc = docsForSend.get(0);
+
+ intent = new Intent(Intent.ACTION_SEND);
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ intent.addCategory(Intent.CATEGORY_DEFAULT);
+ intent.setType(doc.mimeType);
+ intent.putExtra(Intent.EXTRA_STREAM, doc.derivedUri);
+
+ } else if (docsForSend.size() > 1) {
+ intent = new Intent(Intent.ACTION_SEND_MULTIPLE);
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ intent.addCategory(Intent.CATEGORY_DEFAULT);
+
+ final ArrayList<String> mimeTypes = new ArrayList<>();
+ final ArrayList<Uri> uris = new ArrayList<>();
+ for (DocumentInfo doc : docsForSend) {
+ mimeTypes.add(doc.mimeType);
+ uris.add(doc.derivedUri);
+ }
+
+ intent.setType(findCommonMimeType(mimeTypes));
+ intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
+
+ } else {
+ return;
+ }
+
+ intent = Intent.createChooser(intent, getActivity().getText(R.string.share_via));
+ startActivity(intent);
+ }
+ }.execute(selected);
+ }
+
+ private void deleteDocuments(final Selection selected) {
+ Context context = getActivity();
+ String message = Shared.getQuantityString(context, R.plurals.deleting, selected.size());
+
+ mModel.markForDeletion(selected);
+
+ final Activity activity = getActivity();
+ Snackbars.makeSnackbar(activity, message, Snackbar.LENGTH_LONG)
+ .setAction(
+ R.string.undo,
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {}
+ })
+ .setCallback(
+ new Snackbar.Callback() {
+ @Override
+ public void onDismissed(Snackbar snackbar, int event) {
+ if (event == Snackbar.Callback.DISMISS_EVENT_ACTION) {
+ mModel.undoDeletion();
+ } else {
+ mModel.finalizeDeletion(
+ new Model.DeletionListener() {
+ @Override
+ public void onError() {
+ Snackbars.makeSnackbar(
+ activity,
+ R.string.toast_failed_delete,
+ Snackbar.LENGTH_LONG)
+ .show();
+
+ }
+ });
+ }
+ }
+ })
+ .show();
+ }
+
+ private void transferDocuments(final Selection selected, final int mode) {
+ // Pop up a dialog to pick a destination. This is inadequate but works for now.
+ // TODO: Implement a picker that is to spec.
+ final Intent intent = new Intent(
+ BaseActivity.DocumentsIntent.ACTION_OPEN_COPY_DESTINATION,
+ Uri.EMPTY,
+ getActivity(),
+ DocumentsActivity.class);
+
+ new GetDocumentsTask() {
+ @Override
+ void onDocumentsReady(List<DocumentInfo> docs) {
+ getDisplayState().selectedDocumentsForCopy = docs;
+
+ boolean directoryCopy = false;
+ for (DocumentInfo info : docs) {
+ if (Document.MIME_TYPE_DIR.equals(info.mimeType)) {
+ directoryCopy = true;
+ break;
+ }
+ }
+ intent.putExtra(BaseActivity.DocumentsIntent.EXTRA_DIRECTORY_COPY, directoryCopy);
+ intent.putExtra(CopyService.EXTRA_TRANSFER_MODE, mode);
+ startActivityForResult(intent, REQUEST_COPY_DESTINATION);
+ }
+ }.execute(selected);
+ }
+
+ private State getDisplayState() {
+ return ((BaseActivity) getActivity()).getDisplayState();
+ }
+
+ // Provide a reference to the views for each data item
+ // Complex data items may need more than one view per item, and
+ // you provide access to all the views for a data item in a view holder
+ private final class DocumentHolder
+ extends RecyclerView.ViewHolder
+ implements View.OnKeyListener
+ {
+ public String docId; // The stable document id.
+ private ClickListener mClickListener;
+ private View.OnKeyListener mKeyListener;
+
+ public DocumentHolder(View view) {
+ super(view);
+ view.setOnKeyListener(this);
+ }
+
+ public void setSelected(boolean selected) {
+ itemView.setActivated(selected);
+ itemView.setBackgroundColor(selected ? mSelectedItemColor : mDefaultItemColor);
+ }
+
+ @Override
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ // Intercept enter key-up events, and treat them as clicks. Forward other events.
+ if (event.getAction() == KeyEvent.ACTION_UP &&
+ keyCode == KeyEvent.KEYCODE_ENTER) {
+ if (mClickListener != null) {
+ mClickListener.onClick(this);
+ }
+ return true;
+ } else if (mKeyListener != null) {
+ return mKeyListener.onKey(v, keyCode, event);
+ }
+ return false;
+ }
+
+ public void addClickListener(ClickListener listener) {
+ // Just handle one for now; switch to a list if necessary.
+ checkState(mClickListener == null);
+ mClickListener = listener;
+ }
+
+ public void addOnKeyListener(View.OnKeyListener listener) {
+ // Just handle one for now; switch to a list if necessary.
+ checkState(mKeyListener == null);
+ mKeyListener = listener;
+ }
+ }
+
+ interface ClickListener {
+ public void onClick(DocumentHolder doc);
+ }
+
+ void showEmptyView() {
+ mEmptyView.setVisibility(View.VISIBLE);
+ mRecView.setVisibility(View.GONE);
+ TextView msg = (TextView) mEmptyView.findViewById(R.id.message);
+ msg.setText(R.string.empty);
+ // No retry button for the empty view.
+ mEmptyView.findViewById(R.id.button_retry).setVisibility(View.GONE);
+ }
+
+ void showErrorView() {
+ mEmptyView.setVisibility(View.VISIBLE);
+ mRecView.setVisibility(View.GONE);
+ TextView msg = (TextView) mEmptyView.findViewById(R.id.message);
+ msg.setText(R.string.query_error);
+ // TODO: Enable this once the retry button does something.
+ mEmptyView.findViewById(R.id.button_retry).setVisibility(View.GONE);
+ }
+
+ void showRecyclerView() {
+ mEmptyView.setVisibility(View.GONE);
+ mRecView.setVisibility(View.VISIBLE);
+ }
+
+ private final class DocumentsAdapter extends RecyclerView.Adapter<DocumentHolder> {
+
+ private final Context mContext;
+ private final LayoutInflater mInflater;
+
+ public DocumentsAdapter(Context context) {
+ mContext = context;
+ mInflater = LayoutInflater.from(context);
+ }
+
+ @Override
+ public DocumentHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ final State state = getDisplayState();
+ final LayoutInflater inflater = LayoutInflater.from(getContext());
+ View item = null;
+ switch (state.derivedMode) {
+ case MODE_GRID:
+ item = inflater.inflate(R.layout.item_doc_grid, parent, false);
+ break;
+ case MODE_LIST:
+ item = inflater.inflate(R.layout.item_doc_list, parent, false);
+ break;
+ case MODE_UNKNOWN:
+ default:
+ throw new IllegalStateException("Unsupported layout mode.");
+ }
+
+ DocumentHolder holder = new DocumentHolder(item);
+ holder.addClickListener(mItemClickListener);
+ holder.addOnKeyListener(mSelectionManager);
+ return holder;
+ }
+
+ /**
+ * Deal with selection changed events by using a custom ItemAnimator that just changes the
+ * background color. This works around focus issues (otherwise items lose focus when their
+ * selection state changes) but also optimizes change animations for selection.
+ */
+ @Override
+ public void onBindViewHolder(DocumentHolder holder, int position, List<Object> payload) {
+ final View itemView = holder.itemView;
+
+ if (payload.contains(MultiSelectManager.SELECTION_CHANGED_MARKER)) {
+ final boolean selected = isSelected(position);
+ itemView.setActivated(selected);
+ return;
+ } else {
+ onBindViewHolder(holder, position);
+ }
+ }
+
+ @Override
+ public void onBindViewHolder(DocumentHolder holder, int position) {
+
+ final Context context = getContext();
+ final State state = getDisplayState();
+ final RootsCache roots = DocumentsApplication.getRootsCache(context);
+ final ThumbnailCache thumbs = DocumentsApplication.getThumbnailsCache(
+ context, mThumbSize);
+
+ final Cursor cursor = mModel.getItem(position);
+ checkNotNull(cursor, "Cursor cannot be null.");
+
+ final String docAuthority = getCursorString(cursor, RootCursorWrapper.COLUMN_AUTHORITY);
+ final String docRootId = getCursorString(cursor, RootCursorWrapper.COLUMN_ROOT_ID);
+ final String docId = getCursorString(cursor, Document.COLUMN_DOCUMENT_ID);
+ final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
+ final String docDisplayName = getCursorString(cursor, Document.COLUMN_DISPLAY_NAME);
+ final long docLastModified = getCursorLong(cursor, Document.COLUMN_LAST_MODIFIED);
+ final int docIcon = getCursorInt(cursor, Document.COLUMN_ICON);
+ final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
+ final String docSummary = getCursorString(cursor, Document.COLUMN_SUMMARY);
+ final long docSize = getCursorLong(cursor, Document.COLUMN_SIZE);
+
+ holder.docId = docId;
+ final View itemView = holder.itemView;
+
+ holder.setSelected(isSelected(position));
+
+ final View line2 = itemView.findViewById(R.id.line2);
+
+ final ImageView iconMime = (ImageView) itemView.findViewById(R.id.icon_mime);
+ final ImageView iconThumb = (ImageView) itemView.findViewById(R.id.icon_thumb);
+ final TextView title = (TextView) itemView.findViewById(android.R.id.title);
+ final ImageView icon1 = (ImageView) itemView.findViewById(android.R.id.icon1);
+ final TextView summary = (TextView) itemView.findViewById(android.R.id.summary);
+ final TextView date = (TextView) itemView.findViewById(R.id.date);
+ final TextView size = (TextView) itemView.findViewById(R.id.size);
+
+ final ThumbnailAsyncTask oldTask = (ThumbnailAsyncTask) iconThumb.getTag();
+ if (oldTask != null) {
+ oldTask.preempt();
+ iconThumb.setTag(null);
+ }
+
+ iconMime.animate().cancel();
+ iconThumb.animate().cancel();
+
+ final boolean supportsThumbnail = (docFlags & Document.FLAG_SUPPORTS_THUMBNAIL) != 0;
+ final boolean allowThumbnail = (state.derivedMode == MODE_GRID)
+ || MimePredicate.mimeMatches(MimePredicate.VISUAL_MIMES, docMimeType);
+ final boolean showThumbnail = supportsThumbnail && allowThumbnail && !mSvelteRecents;
+
+ final boolean enabled = mTuner.isDocumentEnabled(docMimeType, docFlags);
+ final float iconAlpha = (state.derivedMode == MODE_LIST && !enabled) ? 0.5f : 1f;
+
+ boolean cacheHit = false;
+ if (showThumbnail) {
+ final Uri uri = DocumentsContract.buildDocumentUri(docAuthority, docId);
+ final Bitmap cachedResult = thumbs.get(uri);
+ if (cachedResult != null) {
+ iconThumb.setImageBitmap(cachedResult);
+ cacheHit = true;
+ } else {
+ iconThumb.setImageDrawable(null);
+ // TODO: Hang this off DocumentHolder?
+ final ThumbnailAsyncTask task = new ThumbnailAsyncTask(
+ uri, iconMime, iconThumb, mThumbSize, iconAlpha);
+ iconThumb.setTag(task);
+ ProviderExecutor.forAuthority(docAuthority).execute(task);
+ }
+ }
+
+ // Always throw MIME icon into place, even when a thumbnail is being
+ // loaded in background.
+ if (cacheHit) {
+ iconMime.setAlpha(0f);
+ iconMime.setImageDrawable(null);
+ iconThumb.setAlpha(1f);
+ } else {
+ iconMime.setAlpha(1f);
+ iconThumb.setAlpha(0f);
+ iconThumb.setImageDrawable(null);
+ iconMime.setImageDrawable(
+ getDocumentIcon(mContext, docAuthority, docId, docMimeType, docIcon, state));
+ }
+
+ boolean hasLine2 = false;
+
+ final boolean hideTitle = (state.derivedMode == MODE_GRID) && mHideGridTitles;
+ if (!hideTitle) {
+ title.setText(docDisplayName);
+ title.setVisibility(View.VISIBLE);
+ } else {
+ title.setVisibility(View.GONE);
+ }
+
+ Drawable iconDrawable = null;
+ if (mType == TYPE_RECENT_OPEN) {
+ // We've already had to enumerate roots before any results can
+ // be shown, so this will never block.
+ final RootInfo root = roots.getRootBlocking(docAuthority, docRootId);
+ iconDrawable = root.loadIcon(mContext);
+
+ if (summary != null) {
+ final boolean alwaysShowSummary = getResources()
+ .getBoolean(R.bool.always_show_summary);
+ if (alwaysShowSummary) {
+ summary.setText(root.getDirectoryString());
+ summary.setVisibility(View.VISIBLE);
+ hasLine2 = true;
+ } else {
+ if (iconDrawable != null && roots.isIconUniqueBlocking(root)) {
+ // No summary needed if icon speaks for itself
+ summary.setVisibility(View.INVISIBLE);
+ } else {
+ summary.setText(root.getDirectoryString());
+ summary.setVisibility(View.VISIBLE);
+ summary.setTextAlignment(TextView.TEXT_ALIGNMENT_TEXT_END);
+ hasLine2 = true;
+ }
+ }
+ }
+ } else {
+ // Directories showing thumbnails in grid mode get a little icon
+ // hint to remind user they're a directory.
+ if (Document.MIME_TYPE_DIR.equals(docMimeType) && state.derivedMode == MODE_GRID
+ && showThumbnail) {
+ iconDrawable = IconUtils.applyTintAttr(mContext, R.drawable.ic_doc_folder,
+ android.R.attr.textColorPrimaryInverse);
+ }
+
+ if (summary != null) {
+ if (docSummary != null) {
+ summary.setText(docSummary);
+ summary.setVisibility(View.VISIBLE);
+ hasLine2 = true;
+ } else {
+ summary.setVisibility(View.INVISIBLE);
+ }
+ }
+ }
+
+ if (icon1 != null) icon1.setVisibility(View.GONE);
+
+ if (iconDrawable != null) {
+ icon1.setVisibility(View.VISIBLE);
+ icon1.setImageDrawable(iconDrawable);
+ }
+
+ if (docLastModified == -1) {
+ date.setText(null);
+ } else {
+ date.setText(formatTime(mContext, docLastModified));
+ hasLine2 = true;
+ }
+
+ if (state.showSize) {
+ size.setVisibility(View.VISIBLE);
+ if (Document.MIME_TYPE_DIR.equals(docMimeType) || docSize == -1) {
+ size.setText(null);
+ } else {
+ size.setText(Formatter.formatFileSize(mContext, docSize));
+ hasLine2 = true;
+ }
+ } else {
+ size.setVisibility(View.GONE);
+ }
+
+ if (line2 != null) {
+ line2.setVisibility(hasLine2 ? View.VISIBLE : View.GONE);
+ }
+
+ setEnabledRecursive(itemView, enabled);
+
+ iconMime.setAlpha(iconAlpha);
+ iconThumb.setAlpha(iconAlpha);
+ if (icon1 != null) icon1.setAlpha(iconAlpha);
+
+ if (DEBUG_ENABLE_DND) {
+ setupDragAndDropOnDocumentView(itemView, cursor);
+ }
+ }
+
+ @Override
+ public int getItemCount() {
+ return mModel.getItemCount();
+ }
+
+ }
+
+ private static String formatTime(Context context, long when) {
+ // TODO: DateUtils should make this easier
+ Time then = new Time();
+ then.set(when);
+ Time now = new Time();
+ now.setToNow();
+
+ int flags = DateUtils.FORMAT_NO_NOON | DateUtils.FORMAT_NO_MIDNIGHT
+ | DateUtils.FORMAT_ABBREV_ALL;
+
+ if (then.year != now.year) {
+ flags |= DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_SHOW_DATE;
+ } else if (then.yearDay != now.yearDay) {
+ flags |= DateUtils.FORMAT_SHOW_DATE;
+ } else {
+ flags |= DateUtils.FORMAT_SHOW_TIME;
+ }
+
+ return DateUtils.formatDateTime(context, when, flags);
+ }
+
+ private String findCommonMimeType(List<String> mimeTypes) {
+ String[] commonType = mimeTypes.get(0).split("/");
+ if (commonType.length != 2) {
+ return "*/*";
+ }
+
+ for (int i = 1; i < mimeTypes.size(); i++) {
+ String[] type = mimeTypes.get(i).split("/");
+ if (type.length != 2) continue;
+
+ if (!commonType[1].equals(type[1])) {
+ commonType[1] = "*";
+ }
+
+ if (!commonType[0].equals(type[0])) {
+ commonType[0] = "*";
+ commonType[1] = "*";
+ break;
+ }
+ }
+
+ return commonType[0] + "/" + commonType[1];
+ }
+
+ private void setEnabledRecursive(View v, boolean enabled) {
+ if (v == null) return;
+ if (v.isEnabled() == enabled) return;
+ v.setEnabled(enabled);
+
+ if (v instanceof ViewGroup) {
+ final ViewGroup vg = (ViewGroup) v;
+ for (int i = vg.getChildCount() - 1; i >= 0; i--) {
+ setEnabledRecursive(vg.getChildAt(i), enabled);
+ }
+ }
+ }
+
+ private void copyFromClipboard() {
+ new AsyncTask<Void, Void, List<DocumentInfo>>() {
+
+ @Override
+ protected List<DocumentInfo> doInBackground(Void... params) {
+ return mClipper.getClippedDocuments();
+ }
+
+ @Override
+ protected void onPostExecute(List<DocumentInfo> docs) {
+ DocumentInfo destination =
+ ((BaseActivity) getActivity()).getCurrentDirectory();
+ copyDocuments(docs, destination);
+ }
+ }.execute();
+ }
+
+ private void copyFromClipData(final ClipData clipData, final DocumentInfo destination) {
+ checkNotNull(clipData);
+ new AsyncTask<Void, Void, List<DocumentInfo>>() {
+
+ @Override
+ protected List<DocumentInfo> doInBackground(Void... params) {
+ return mClipper.getDocumentsFromClipData(clipData);
+ }
+
+ @Override
+ protected void onPostExecute(List<DocumentInfo> docs) {
+ copyDocuments(docs, destination);
+ }
+ }.execute();
+ }
+
+ private void copyDocuments(final List<DocumentInfo> docs, final DocumentInfo destination) {
+ if (!canCopy(docs, destination)) {
+ Snackbars.makeSnackbar(
+ getActivity(),
+ R.string.clipboard_files_cannot_paste,
+ Snackbar.LENGTH_SHORT)
+ .show();
+ return;
+ }
+
+ if (docs.isEmpty()) {
+ return;
+ }
+
+ final DocumentStack curStack = getDisplayState().stack;
+ DocumentStack tmpStack = new DocumentStack();
+ if (destination != null) {
+ tmpStack.push(destination);
+ tmpStack.addAll(curStack);
+ } else {
+ tmpStack = curStack;
+ }
+
+ CopyService.start(getActivity(), docs, tmpStack, CopyService.TRANSFER_MODE_COPY);
+ }
+
+ private ClipData getClipDataFromDocuments(List<DocumentInfo> docs) {
+ Context context = getActivity();
+ final ContentResolver resolver = context.getContentResolver();
+ ClipData clipData = null;
+ for (DocumentInfo doc : docs) {
+ final Uri uri = DocumentsContract.buildDocumentUri(doc.authority, doc.documentId);
+ if (clipData == null) {
+ // TODO: figure out what this string should be.
+ // Currently it is not displayed anywhere in the UI, but this might change.
+ final String label = "";
+ clipData = ClipData.newUri(resolver, label, uri);
+ } else {
+ // TODO: update list of mime types in ClipData.
+ clipData.addItem(new ClipData.Item(uri));
+ }
+ }
+ return clipData;
+ }
+
+ public void copySelectedToClipboard() {
+ Selection sel = mSelectionManager.getSelection(new Selection());
+ copySelectionToClipboard(sel);
+ }
+
+ void copySelectionToClipboard(Selection items) {
+ new GetDocumentsTask() {
+ @Override
+ void onDocumentsReady(List<DocumentInfo> docs) {
+ mClipper.clipDocuments(docs);
+ Activity activity = getActivity();
+ Snackbars.makeSnackbar(activity,
+ activity.getResources().getQuantityString(
+ R.plurals.clipboard_files_clipped, docs.size(), docs.size()),
+ Snackbar.LENGTH_SHORT).show();
+ }
+ }.execute(items);
+ }
+
+ public void pasteFromClipboard() {
+ copyFromClipboard();
+ getActivity().invalidateOptionsMenu();
+ }
+
+ /**
+ * Returns true if the list of files can be copied to destination. Note that this
+ * is a policy check only. Currently the method does not attempt to verify
+ * available space or any other environmental aspects possibly resulting in
+ * failure to copy.
+ *
+ * @return true if the list of files can be copied to destination.
+ */
+ boolean canCopy(List<DocumentInfo> files, DocumentInfo dest) {
+ BaseActivity activity = (BaseActivity) getActivity();
+
+ final RootInfo root = activity.getCurrentRoot();
+
+ // Can't copy folders to Downloads.
+ if (root.isDownloads()) {
+ for (DocumentInfo docs : files) {
+ if (docs.isDirectory()) {
+ return false;
+ }
+ }
+ }
+
+ return dest != null && dest.isDirectory() && dest.isCreateSupported();
+ }
+
+ public void selectAllFiles() {
+ boolean changed = mSelectionManager.setItemsSelected(0, mModel.getItemCount(), true);
+ if (changed) {
+ updateDisplayState();
+ }
+ }
+
+ private void setupDragAndDropOnDirectoryView(View view) {
+ // Listen for drops on non-directory items and empty space.
+ view.setOnDragListener(mOnDragListener);
+ }
+
+ private void setupDragAndDropOnDocumentView(View view, Cursor cursor) {
+ final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
+ if (Document.MIME_TYPE_DIR.equals(docMimeType)) {
+ // Make a directory item a drop target. Drop on non-directories and empty space
+ // is handled at the list/grid view level.
+ view.setOnDragListener(mOnDragListener);
+ }
+
+ // Temporary: attaching the listener to the title only.
+ // Attaching to the entire item conflicts with the item long click handler responsible
+ // for item selection.
+ final View title = view.findViewById(android.R.id.title);
+ title.setOnLongClickListener(mLongClickListener);
+ }
+
+ private View.OnDragListener mOnDragListener = new View.OnDragListener() {
+ @Override
+ public boolean onDrag(View v, DragEvent event) {
+ switch (event.getAction()) {
+ case DragEvent.ACTION_DRAG_STARTED:
+ // TODO: Check if the event contains droppable data.
+ return true;
+
+ // TODO: Highlight potential drop target directory?
+ // TODO: Expand drop target directory on hover?
+ case DragEvent.ACTION_DRAG_ENTERED:
+ case DragEvent.ACTION_DRAG_LOCATION:
+ case DragEvent.ACTION_DRAG_EXITED:
+ case DragEvent.ACTION_DRAG_ENDED:
+ return true;
+
+ case DragEvent.ACTION_DROP:
+ int dstPosition = mRecView.getChildAdapterPosition(getContainingItemView(v));
+ DocumentInfo dstDir = null;
+ if (dstPosition != android.widget.AdapterView.INVALID_POSITION) {
+ Cursor dstCursor = mModel.getItem(dstPosition);
+ checkNotNull(dstCursor, "Cursor cannot be null.");
+ dstDir = DocumentInfo.fromDirectoryCursor(dstCursor);
+ // TODO: Do not drop into the directory where the documents came from.
+ }
+ copyFromClipData(event.getClipData(), dstDir);
+ return true;
+ }
+ return false;
+ }
+ };
+
+ private View getContainingItemView(View view) {
+ while (true) {
+ if (view.getLayoutParams() instanceof RecyclerView.LayoutParams) {
+ return view;
+ }
+ ViewParent parent = view.getParent();
+ if (parent == null || !(parent instanceof View)) {
+ return null;
+ }
+ view = (View) parent;
+ }
+ }
+
+ private View.OnLongClickListener mLongClickListener = new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ final List<DocumentInfo> docs = getDraggableDocuments(v);
+ if (docs.isEmpty()) {
+ return false;
+ }
+ v.startDrag(
+ getClipDataFromDocuments(docs),
+ new DrawableShadowBuilder(getDragShadowIcon(docs)),
+ null,
+ View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ |
+ View.DRAG_FLAG_GLOBAL_URI_WRITE
+ );
+ return true;
+ }
+ };
+
+ private List<DocumentInfo> getDraggableDocuments(View currentItemView) {
+ int position = mRecView.getChildAdapterPosition(getContainingItemView(currentItemView));
+ if (position == android.widget.AdapterView.INVALID_POSITION) {
+ return Collections.EMPTY_LIST;
+ }
+
+ final List<DocumentInfo> selectedDocs =
+ mModel.getDocuments(mSelectionManager.getSelection());
+ if (!selectedDocs.isEmpty()) {
+ if (!isSelected(position)) {
+ // There is a selection that does not include the current item, drag nothing.
+ return Collections.EMPTY_LIST;
+ }
+ return selectedDocs;
+ }
+
+ final Cursor cursor = mModel.getItem(position);
+ checkNotNull(cursor, "Cursor cannot be null.");
+ final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(cursor);
+
+ return Lists.newArrayList(doc);
+ }
+
+ private Drawable getDragShadowIcon(List<DocumentInfo> docs) {
+ if (docs.size() == 1) {
+ final DocumentInfo doc = docs.get(0);
+ return getDocumentIcon(getActivity(), doc.authority, doc.documentId,
+ doc.mimeType, doc.icon, getDisplayState());
+ }
+ return getActivity().getDrawable(R.drawable.ic_doc_generic);
+ }
+
+ public static Drawable getDocumentIcon(Context context, String docAuthority, String docId,
+ String docMimeType, int docIcon, State state) {
+ if (docIcon != 0) {
+ return IconUtils.loadPackageIcon(context, docAuthority, docIcon);
+ } else {
+ return IconUtils.loadMimeIcon(context, docMimeType, docAuthority, docId,
+ state.derivedMode);
+ }
+ }
+
+ private static class ThumbnailAsyncTask extends AsyncTask<Uri, Void, Bitmap>
+ implements Preemptable {
+ private final Uri mUri;
+ private final ImageView mIconMime;
+ private final ImageView mIconThumb;
+ private final Point mThumbSize;
+ private final float mTargetAlpha;
+ private final CancellationSignal mSignal;
+
+ public ThumbnailAsyncTask(Uri uri, ImageView iconMime, ImageView iconThumb, Point thumbSize,
+ float targetAlpha) {
+ mUri = uri;
+ mIconMime = iconMime;
+ mIconThumb = iconThumb;
+ mThumbSize = thumbSize;
+ mTargetAlpha = targetAlpha;
+ mSignal = new CancellationSignal();
+ }
+
+ @Override
+ public void preempt() {
+ cancel(false);
+ mSignal.cancel();
+ }
+
+ @Override
+ protected Bitmap doInBackground(Uri... params) {
+ if (isCancelled()) return null;
+
+ final Context context = mIconThumb.getContext();
+ final ContentResolver resolver = context.getContentResolver();
+
+ ContentProviderClient client = null;
+ Bitmap result = null;
+ try {
+ client = DocumentsApplication.acquireUnstableProviderOrThrow(
+ resolver, mUri.getAuthority());
+ result = DocumentsContract.getDocumentThumbnail(client, mUri, mThumbSize, mSignal);
+ if (result != null) {
+ final ThumbnailCache thumbs = DocumentsApplication.getThumbnailsCache(
+ context, mThumbSize);
+ thumbs.put(mUri, result);
+ }
+ } catch (Exception e) {
+ if (!(e instanceof OperationCanceledException)) {
+ Log.w(TAG, "Failed to load thumbnail for " + mUri + ": " + e);
+ }
+ } finally {
+ ContentProviderClient.releaseQuietly(client);
+ }
+ return result;
+ }
+
+ @Override
+ protected void onPostExecute(Bitmap result) {
+ if (mIconThumb.getTag() == this && result != null) {
+ mIconThumb.setTag(null);
+ mIconThumb.setImageBitmap(result);
+
+ mIconMime.setAlpha(mTargetAlpha);
+ mIconMime.animate().alpha(0f).start();
+ mIconThumb.setAlpha(0f);
+ mIconThumb.animate().alpha(mTargetAlpha).start();
+ }
+ }
+ }
+
+ private class DrawableShadowBuilder extends View.DragShadowBuilder {
+
+ 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);
+ }
+
+ @Override
+ public void onProvideShadowMetrics(
+ Point shadowSize, Point shadowTouchPoint) {
+ shadowSize.set(mShadowDimension, mShadowDimension);
+ shadowTouchPoint.set(mShadowDimension / 2, mShadowDimension / 2);
+ }
+
+ @Override
+ public void onDrawShadow(Canvas canvas) {
+ mShadow.draw(canvas);
+ }
+ }
+
+ /**
+ * Abstract task providing support for loading documents *off*
+ * the main thread. And if it isn't obvious, creating a list
+ * of documents (especially large lists) can be pretty expensive.
+ */
+ private abstract class GetDocumentsTask
+ extends AsyncTask<Selection, Void, List<DocumentInfo>> {
+ @Override
+ protected final List<DocumentInfo> doInBackground(Selection... selected) {
+ return mModel.getDocuments(selected[0]);
+ }
+
+ @Override
+ protected final void onPostExecute(List<DocumentInfo> docs) {
+ onDocumentsReady(docs);
+ }
+
+ abstract void onDocumentsReady(List<DocumentInfo> docs);
+ }
+
+ boolean isSelected(int position) {
+ return mSelectionManager.getSelection().contains(position);
+ }
+
+ /**
+ * The data model for the current loaded directory.
+ */
+ @VisibleForTesting
+ public static final class Model implements DocumentContext {
+ private RecyclerView.Adapter<?> mViewAdapter;
+ private Context mContext;
+ private int mCursorCount;
+ private boolean mIsLoading;
+ @GuardedBy("mPendingDelete")
+ private Boolean mPendingDelete = false;
+ @GuardedBy("mPendingDelete")
+ private SparseBooleanArray mMarkedForDeletion = new SparseBooleanArray();
+ private UpdateListener mUpdateListener;
+ @Nullable private Cursor mCursor;
+ @Nullable private String info;
+ @Nullable private String error;
+
+ Model(Context context, RecyclerView.Adapter<?> viewAdapter) {
+ mContext = context;
+ mViewAdapter = viewAdapter;
+ }
+
+ void update(DirectoryResult result) {
+ if (DEBUG) Log.i(TAG, "Updating model with new result set.");
+
+ if (result == null) {
+ mCursor = null;
+ mCursorCount = 0;
+ info = null;
+ error = null;
+ mIsLoading = false;
+ mUpdateListener.onModelUpdate(this);
+ return;
+ }
+
+ if (result.exception != null) {
+ Log.e(TAG, "Error while loading directory contents", result.exception);
+ mUpdateListener.onModelUpdateFailed(result.exception);
+ return;
+ }
+
+ mCursor = result.cursor;
+ mCursorCount = mCursor.getCount();
+
+ final Bundle extras = mCursor.getExtras();
+ if (extras != null) {
+ info = extras.getString(DocumentsContract.EXTRA_INFO);
+ error = extras.getString(DocumentsContract.EXTRA_ERROR);
+ mIsLoading = extras.getBoolean(DocumentsContract.EXTRA_LOADING, false);
+ }
+
+ mUpdateListener.onModelUpdate(this);
+ }
+
+ int getItemCount() {
+ synchronized(mPendingDelete) {
+ return mCursorCount - mMarkedForDeletion.size();
+ }
+ }
+
+ Cursor getItem(int position) {
+ synchronized(mPendingDelete) {
+ // Items marked for deletion are masked out of the UI. To do this, for every marked
+ // item whose position is less than the requested item position, advance the requested
+ // position by 1.
+ final int originalPos = position;
+ final int size = mMarkedForDeletion.size();
+ for (int i = 0; i < size; ++i) {
+ // It'd be more concise, but less efficient, to iterate over positions while calling
+ // mMarkedForDeletion.get. Instead, iterate over deleted entries.
+ if (mMarkedForDeletion.keyAt(i) <= position && mMarkedForDeletion.valueAt(i)) {
+ ++position;
+ }
+ }
+
+ if (DEBUG && position != originalPos) {
+ Log.d(TAG, "Item position adjusted for deletion. Original: " + originalPos
+ + " Adjusted: " + position);
+ }
+
+ if (position >= mCursorCount) {
+ throw new IndexOutOfBoundsException("Attempt to retrieve " + position + " of " +
+ mCursorCount + " items");
+ }
+
+ mCursor.moveToPosition(position);
+ return mCursor;
+ }
+ }
+
+ private boolean isEmpty() {
+ return mCursorCount == 0;
+ }
+
+ private boolean isLoading() {
+ return mIsLoading;
+ }
+
+ List<DocumentInfo> getDocuments(Selection items) {
+ final int size = (items != null) ? items.size() : 0;
+
+ final List<DocumentInfo> docs = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ final Cursor cursor = getItem(items.get(i));
+ checkNotNull(cursor, "Cursor cannot be null.");
+ final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(cursor);
+ docs.add(doc);
+ }
+ return docs;
+ }
+
+ @Override
+ public Cursor getCursor() {
+ if (Looper.myLooper() != Looper.getMainLooper()) {
+ throw new IllegalStateException("Can't call getCursor from non-main thread.");
+ }
+ return mCursor;
+ }
+
+ List<DocumentInfo> getDocumentsMarkedForDeletion() {
+ synchronized (mPendingDelete) {
+ final int size = mMarkedForDeletion.size();
+ List<DocumentInfo> docs = new ArrayList<>(size);
+
+ for (int i = 0; i < size; ++i) {
+ final int position = mMarkedForDeletion.keyAt(i);
+ checkState(position < mCursorCount);
+ mCursor.moveToPosition(position);
+ final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(mCursor);
+ docs.add(doc);
+ }
+ return docs;
+ }
+ }
+
+ /**
+ * Marks the given files for deletion. This will remove them from the UI. Clients must then
+ * call either {@link #undoDeletion()} or {@link #finalizeDeletion()} to cancel or confirm
+ * the deletion, respectively. Only one deletion operation is allowed at a time.
+ *
+ * @param selected A selection representing the files to delete.
+ */
+ void markForDeletion(Selection selected) {
+ synchronized (mPendingDelete) {
+ mPendingDelete = true;
+ // Only one deletion operation at a time.
+ checkState(mMarkedForDeletion.size() == 0);
+ // There should never be more to delete than what exists.
+ checkState(mCursorCount >= selected.size());
+
+ int[] positions = selected.getAll();
+ Arrays.sort(positions);
+
+ // Walk backwards through the set, since we're removing positions.
+ // Otherwise, positions would change after the first modification.
+ for (int p = positions.length - 1; p >= 0; p--) {
+ mMarkedForDeletion.append(positions[p], true);
+ mViewAdapter.notifyItemRemoved(positions[p]);
+ if (DEBUG) Log.d(TAG, "Scheduled " + positions[p] + " for delete.");
+ }
+ }
+ }
+
+ /**
+ * Cancels an ongoing deletion operation. All files currently marked for deletion will be
+ * unmarked, and restored in the UI. See {@link #markForDeletion(Selection)}.
+ */
+ void undoDeletion() {
+ synchronized (mPendingDelete) {
+ // Iterate over deleted items, temporarily marking them false in the deletion list, and
+ // re-adding them to the UI.
+ final int size = mMarkedForDeletion.size();
+ for (int i = 0; i < size; ++i) {
+ final int position = mMarkedForDeletion.keyAt(i);
+ mMarkedForDeletion.put(position, false);
+ mViewAdapter.notifyItemInserted(position);
+ }
+ resetDeleteData();
+ }
+ }
+
+ private void resetDeleteData() {
+ synchronized (mPendingDelete) {
+ mPendingDelete = false;
+ mMarkedForDeletion.clear();
+ }
+ }
+
+ /**
+ * Finalizes an ongoing deletion operation. All files currently marked for deletion will be
+ * deleted. See {@link #markForDeletion(Selection)}.
+ *
+ * @param view The view which will be used to interact with the user (e.g. surfacing
+ * snackbars) for errors, info, etc.
+ */
+ void finalizeDeletion(DeletionListener listener) {
+ synchronized (mPendingDelete) {
+ if (mPendingDelete) {
+ // Necessary to avoid b/25072545. Even when that's resolved, this
+ // is a nice safe thing to day.
+ mPendingDelete = false;
+ final ContentResolver resolver = mContext.getContentResolver();
+ DeleteFilesTask task = new DeleteFilesTask(resolver, listener);
+ task.execute();
+ }
+ }
+ }
+
+ /**
+ * A Task which collects the DocumentInfo for documents that have been marked for deletion,
+ * and actually deletes them.
+ */
+ private class DeleteFilesTask extends AsyncTask<Void, Void, List<DocumentInfo>> {
+ private ContentResolver mResolver;
+ private DeletionListener mListener;
+
+ /**
+ * @param resolver A ContentResolver for performing the actual file deletions.
+ * @param errorCallback A Runnable that is executed in the event that one or more errors
+ * occured while copying files. Execution will occur on the UI thread.
+ */
+ public DeleteFilesTask(ContentResolver resolver, DeletionListener listener) {
+ mResolver = resolver;
+ mListener = listener;
+ }
+
+ @Override
+ protected List<DocumentInfo> doInBackground(Void... params) {
+ return getDocumentsMarkedForDeletion();
+ }
+
+ @Override
+ protected void onPostExecute(List<DocumentInfo> docs) {
+ boolean hadTrouble = false;
+ for (DocumentInfo doc : docs) {
+ if (!doc.isDeleteSupported()) {
+ Log.w(TAG, doc + " could not be deleted. Skipping...");
+ hadTrouble = true;
+ continue;
+ }
+
+ ContentProviderClient client = null;
+ try {
+ if (DEBUG) Log.d(TAG, "Deleting: " + doc.displayName);
+ client = DocumentsApplication.acquireUnstableProviderOrThrow(
+ mResolver, doc.derivedUri.getAuthority());
+ DocumentsContract.deleteDocument(client, doc.derivedUri);
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to delete " + doc);
+ hadTrouble = true;
+ } finally {
+ ContentProviderClient.releaseQuietly(client);
+ }
+ }
+
+ if (hadTrouble) {
+ // TODO show which files failed? b/23720103
+ mListener.onError();
+ if (DEBUG) Log.d(TAG, "Deletion task completed. Some deletions failed.");
+ } else {
+ if (DEBUG) Log.d(TAG, "Deletion task completed successfully.");
+ }
+ resetDeleteData();
+
+ mListener.onCompletion();
+ }
+ }
+
+ static class DeletionListener {
+ /**
+ * Called when deletion has completed (regardless of whether an error occurred).
+ */
+ void onCompletion() {}
+
+ /**
+ * Called at the end of a deletion operation that produced one or more errors.
+ */
+ void onError() {}
+ }
+
+ void addUpdateListener(UpdateListener listener) {
+ checkState(mUpdateListener == null);
+ mUpdateListener = listener;
+ }
+
+ static class UpdateListener {
+ /**
+ * Called when a successful update has occurred.
+ */
+ void onModelUpdate(Model model) {}
+
+ /**
+ * Called when an update has been attempted but failed.
+ */
+ void onModelUpdateFailed(Exception e) {}
+ }
+ }
+
+ private class ItemClickListener implements ClickListener {
+ @Override
+ public void onClick(DocumentHolder doc) {
+ final int position = doc.getAdapterPosition();
+ if (mSelectionManager.hasSelection()) {
+ mSelectionManager.toggleSelection(position);
+ } else {
+ handleViewItem(position);
+ }
+ }
+ }
+
+ private class ModelUpdateListener extends Model.UpdateListener {
+ @Override
+ public void onModelUpdate(Model model) {
+ if (model.info != null || model.error != null) {
+ mMessageBar.setInfo(model.info);
+ mMessageBar.setError(model.error);
+ mMessageBar.show();
+ }
+
+ mProgressBar.setVisibility(model.isLoading() ? View.VISIBLE : View.GONE);
+
+ if (model.isEmpty()) {
+ showEmptyView();
+ } else {
+ showRecyclerView();
+ mAdapter.notifyDataSetChanged();
+ }
+ }
+
+ @Override
+ public void onModelUpdateFailed(Exception e) {
+ showErrorView();
+ }
+ }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryItemAnimator.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryItemAnimator.java
new file mode 100644
index 0000000..0963845
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryItemAnimator.java
@@ -0,0 +1,195 @@
+/*
+ * 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.documentsui.dirlist;
+
+import android.animation.Animator;
+import android.animation.ArgbEvaluator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.support.v4.util.ArrayMap;
+import android.support.v7.widget.DefaultItemAnimator;
+import android.support.v7.widget.RecyclerView;
+import android.util.TypedValue;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Performs change animations on Items in DirectoryFragment's RecyclerView. This class overrides
+ * the way selection animations are normally performed - instead of cross fading the old Item with a
+ * new Item, this class manually animates a background color change. This enables selected Items to
+ * correctly maintain focus.
+ */
+class DirectoryItemAnimator extends DefaultItemAnimator {
+ private final List<ColorAnimation> mPendingAnimations = new ArrayList<>();
+ private final Map<RecyclerView.ViewHolder, ColorAnimation> mRunningAnimations =
+ new ArrayMap<>();
+ private final Integer mDefaultColor;
+ private final Integer mSelectedColor;
+
+ public DirectoryItemAnimator(Context context) {
+ mDefaultColor = context.getResources().getColor(android.R.color.transparent);
+ // Get the accent color.
+ TypedValue selColor = new TypedValue();
+ context.getTheme().resolveAttribute(android.R.attr.colorAccent, selColor, true);
+ // Set the opacity to 10%.
+ mSelectedColor = (selColor.data & 0x00ffffff) | 0x16000000;
+ }
+
+ @Override
+ public void runPendingAnimations() {
+ super.runPendingAnimations();
+ for (ColorAnimation anim: mPendingAnimations) {
+ anim.start();
+ mRunningAnimations.put(anim.viewHolder, anim);
+ }
+ mPendingAnimations.clear();
+ }
+
+ @Override
+ public void endAnimation(RecyclerView.ViewHolder vh) {
+ super.endAnimation(vh);
+
+ for (int i = mPendingAnimations.size() - 1; i >= 0; --i) {
+ ColorAnimation anim = mPendingAnimations.get(i);
+ if (anim.viewHolder == vh) {
+ mPendingAnimations.remove(i);
+ anim.end();
+ }
+ }
+
+ ColorAnimation anim = mRunningAnimations.get(vh);
+ if (anim != null) {
+ anim.cancel();
+ }
+ }
+
+ @Override
+ public ItemHolderInfo recordPreLayoutInformation(
+ RecyclerView.State state,
+ RecyclerView.ViewHolder viewHolder,
+ @AdapterChanges int changeFlags,
+ List<Object> payloads) {
+ ItemInfo info = (ItemInfo) super.recordPreLayoutInformation(state,
+ viewHolder, changeFlags, payloads);
+ info.isActivated = viewHolder.itemView.isActivated();
+ return info;
+ }
+
+
+ @Override
+ public ItemHolderInfo recordPostLayoutInformation(
+ RecyclerView.State state, RecyclerView.ViewHolder viewHolder) {
+ ItemInfo info = (ItemInfo) super.recordPostLayoutInformation(state,
+ viewHolder);
+ info.isActivated = viewHolder.itemView.isActivated();
+ return info;
+ }
+
+ @Override
+ public boolean animateChange(final RecyclerView.ViewHolder oldHolder,
+ RecyclerView.ViewHolder newHolder, ItemHolderInfo preInfo,
+ ItemHolderInfo postInfo) {
+ if (oldHolder != newHolder) {
+ return super.animateChange(oldHolder, newHolder, preInfo, postInfo);
+ }
+
+ ItemInfo pre = (ItemInfo)preInfo;
+ ItemInfo post = (ItemInfo)postInfo;
+
+ if (pre.isActivated == post.isActivated) {
+ dispatchAnimationFinished(oldHolder);
+ return false;
+ } else {
+ Integer startColor = pre.isActivated ? mSelectedColor : mDefaultColor;
+ Integer endColor = post.isActivated ? mSelectedColor : mDefaultColor;
+ oldHolder.itemView.setBackgroundColor(startColor);
+ mPendingAnimations.add(new ColorAnimation(oldHolder, startColor, endColor));
+ }
+ return true;
+ }
+
+ @Override
+ public ItemHolderInfo obtainHolderInfo() {
+ return new ItemInfo();
+ }
+
+ @Override
+ public boolean canReuseUpdatedViewHolder(RecyclerView.ViewHolder vh) {
+ return true;
+ }
+
+ class ItemInfo extends DefaultItemAnimator.ItemHolderInfo {
+ boolean isActivated;
+ };
+
+ /**
+ * Animates changes in background color.
+ */
+ class ColorAnimation
+ implements ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener {
+ ValueAnimator mValueAnimator;
+ final RecyclerView.ViewHolder viewHolder;
+ int mEndColor;
+
+ public ColorAnimation(RecyclerView.ViewHolder vh, int startColor, int endColor)
+ {
+ viewHolder = vh;
+ mValueAnimator = ValueAnimator.ofObject(new ArgbEvaluator(), startColor, endColor);
+ mValueAnimator.addUpdateListener(this);
+ mValueAnimator.addListener(this);
+
+ mEndColor = endColor;
+ }
+
+ public void start() {
+ mValueAnimator.start();
+ }
+
+ public void cancel() {
+ mValueAnimator.cancel();
+ }
+
+ public void end() {
+ mValueAnimator.end();
+ }
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator animator) {
+ viewHolder.itemView.setBackgroundColor((Integer)animator.getAnimatedValue());
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ viewHolder.itemView.setBackgroundColor(mEndColor);
+ mRunningAnimations.remove(viewHolder);
+ dispatchAnimationFinished(viewHolder);
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ dispatchAnimationStarted(viewHolder);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {}
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {}
+ };
+};
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
new file mode 100644
index 0000000..a0ff165
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
@@ -0,0 +1,205 @@
+/*
+ * 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.documentsui.dirlist;
+
+import static com.android.documentsui.State.ACTION_BROWSE;
+import static com.android.documentsui.State.ACTION_CREATE;
+import static com.android.documentsui.State.ACTION_GET_CONTENT;
+import static com.android.documentsui.State.ACTION_MANAGE;
+import static com.android.documentsui.State.ACTION_OPEN;
+import static com.android.documentsui.State.ACTION_OPEN_TREE;
+import static com.android.internal.util.Preconditions.checkArgument;
+
+import com.android.documentsui.Menus;
+import com.android.documentsui.MimePredicate;
+import com.android.documentsui.R;
+import com.android.documentsui.State;
+
+import android.os.SystemProperties;
+import android.provider.DocumentsContract.Document;
+import android.view.Menu;
+import android.view.MenuItem;
+
+/**
+ * Providers support for specializing the DirectoryFragment to the "host" Activity.
+ * Feel free to expand the role of this class to handle other specializations.
+ */
+public abstract class FragmentTuner {
+
+ final State mState;
+
+ public FragmentTuner(State state) {
+ mState = state;
+ }
+
+ public static FragmentTuner pick(State state) {
+ switch (state.action) {
+ case ACTION_BROWSE:
+ return new FilesTuner(state);
+ case ACTION_MANAGE:
+ return new DownloadsTuner(state);
+ default:
+ return new DocumentsTuner(state);
+ }
+ }
+
+
+ public abstract void updateActionMenu(Menu menu, int dirType, boolean canDelete);
+
+ // Subtly different from isDocumentEnabled. The reason may be illuminated as follows.
+ // A folder is enabled such that it may be double clicked, even in settings
+ // when the folder itself cannot be selected. This may also be true of container types.
+ public boolean canSelectType(String docMimeType) {
+ return true;
+ }
+
+ public boolean isDocumentEnabled(String docMimeType, int docFlags) {
+ if (isDirectory(docMimeType)) {
+ return true;
+ }
+
+ return MimePredicate.mimeMatches(mState.acceptMimes, docMimeType);
+ }
+
+ /**
+ * Provides support for Platform specific specializations of DirectoryFragment.
+ */
+ private static final class DocumentsTuner extends FragmentTuner {
+
+ public DocumentsTuner(State state) {
+ super(state);
+ }
+
+ @Override
+ public boolean canSelectType(String docMimeType) {
+ switch (mState.action) {
+ case ACTION_OPEN:
+ case ACTION_CREATE:
+ case ACTION_GET_CONTENT:
+ return !isDirectory(docMimeType);
+ case ACTION_OPEN_TREE:
+ // In this case nothing *ever* is selectable...the expected user behavior is
+ // they navigate *into* a folder, then click a confirmation button indicating
+ // that the current directory is the directory they are picking.
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean isDocumentEnabled(String docMimeType, int docFlags) {
+ // Directories are always enabled
+ if (isDirectory(docMimeType)) {
+ return true;
+ }
+
+ // Read-only files are disabled when creating
+ if (mState.action == ACTION_CREATE && (docFlags & Document.FLAG_SUPPORTS_WRITE) == 0) {
+ return false;
+ }
+
+ return MimePredicate.mimeMatches(mState.acceptMimes, docMimeType);
+ }
+
+ @Override
+ public void updateActionMenu(Menu menu, int dirType, boolean canDelete) {
+
+ boolean copyEnabled = dirType != DirectoryFragment.TYPE_RECENT_OPEN;
+ boolean moveEnabled =
+ SystemProperties.getBoolean("debug.documentsui.enable_move", false);
+ menu.findItem(R.id.menu_copy_to_clipboard).setEnabled(copyEnabled);
+
+ final MenuItem open = menu.findItem(R.id.menu_open);
+ final MenuItem share = menu.findItem(R.id.menu_share);
+ final MenuItem delete = menu.findItem(R.id.menu_delete);
+ final MenuItem copyTo = menu.findItem(R.id.menu_copy_to);
+ final MenuItem moveTo = menu.findItem(R.id.menu_move_to);
+
+ open.setVisible(true);
+ share.setVisible(false);
+ delete.setVisible(false);
+ copyTo.setVisible(copyEnabled);
+ copyTo.setEnabled(copyEnabled);
+ moveTo.setVisible(moveEnabled);
+ moveTo.setEnabled(moveEnabled);
+ }
+ }
+
+ /**
+ * Provides support for Platform specific specializations of DirectoryFragment.
+ */
+ private static final class DownloadsTuner extends FragmentTuner {
+
+ public DownloadsTuner(State state) {
+ super(state);
+ }
+
+ @Override
+ public void updateActionMenu(Menu menu, int dirType, boolean canDelete) {
+ checkArgument(dirType != DirectoryFragment.TYPE_RECENT_OPEN);
+
+ boolean moveEnabled =
+ SystemProperties.getBoolean("debug.documentsui.enable_move", false);
+ menu.findItem(R.id.menu_copy_to_clipboard).setEnabled(true);
+
+ final MenuItem open = menu.findItem(R.id.menu_open);
+ final MenuItem share = menu.findItem(R.id.menu_share);
+ final MenuItem delete = menu.findItem(R.id.menu_delete);
+ final MenuItem copyTo = menu.findItem(R.id.menu_copy_to);
+ final MenuItem moveTo = menu.findItem(R.id.menu_move_to);
+
+ open.setVisible(false);
+ share.setVisible(false);
+ delete.setVisible(canDelete);
+ copyTo.setVisible(true);
+ copyTo.setEnabled(true);
+ moveTo.setVisible(moveEnabled);
+ moveTo.setEnabled(moveEnabled);
+ }
+ }
+
+ /**
+ * Provides support for Files activity specific specializations of DirectoryFragment.
+ */
+ private static final class FilesTuner extends FragmentTuner {
+
+ public FilesTuner(State state) {
+ super(state);
+ }
+
+ @Override
+ public void updateActionMenu(Menu menu, int dirType, boolean canDelete) {
+
+ MenuItem copy = menu.findItem(R.id.menu_copy_to_clipboard);
+ MenuItem paste = menu.findItem(R.id.menu_paste_from_clipboard);
+ copy.setEnabled(dirType != DirectoryFragment.TYPE_RECENT_OPEN);
+
+ menu.findItem(R.id.menu_share).setVisible(true);
+ menu.findItem(R.id.menu_delete).setVisible(canDelete);
+
+ menu.findItem(R.id.menu_open).setVisible(false);
+ menu.findItem(R.id.menu_copy_to).setVisible(true);
+ menu.findItem(R.id.menu_move_to).setVisible(true);
+
+ Menus.disableHiddenItems(menu, copy, paste);
+ }
+ }
+
+ private static boolean isDirectory(String mimeType) {
+ return Document.MIME_TYPE_DIR.equals(mimeType);
+ }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
new file mode 100644
index 0000000..9eafcc3
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
@@ -0,0 +1,2017 @@
+/*
+ * 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.documentsui.dirlist;
+
+import static com.android.documentsui.Shared.DEBUG;
+import static com.android.internal.util.Preconditions.checkArgument;
+import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.internal.util.Preconditions.checkState;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.RecyclerView.Adapter;
+import android.support.v7.widget.RecyclerView.AdapterDataObserver;
+import android.support.v7.widget.RecyclerView.LayoutManager;
+import android.support.v7.widget.RecyclerView.OnScrollListener;
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
+import android.view.GestureDetector;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+
+import com.android.documentsui.Events;
+import com.android.documentsui.R;
+import com.android.documentsui.Events.InputEvent;
+import com.android.documentsui.Events.MotionInputEvent;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * MultiSelectManager provides support traditional multi-item selection support to RecyclerView.
+ * Additionally it can be configured to restrict selection to a single element, @see
+ * #setSelectMode.
+ */
+public final class MultiSelectManager implements View.OnKeyListener {
+
+ /** Selection mode for multiple select. **/
+ public static final int MODE_MULTIPLE = 0;
+
+ /** Selection mode for multiple select. **/
+ public static final int MODE_SINGLE = 1;
+
+ private static final String TAG = "MultiSelectManager";
+
+ private final Selection mSelection = new Selection();
+
+ // Only created when selection is cleared.
+ private Selection mIntermediateSelection;
+
+ private Range mRanger;
+ private SelectionEnvironment mEnvironment;
+
+ private final List<MultiSelectManager.Callback> mCallbacks = new ArrayList<>(1);
+
+ private Adapter<?> mAdapter;
+ private boolean mSingleSelect;
+
+ // Payloads for notifyItemChange to distinguish between selection and other events.
+ public static final String SELECTION_CHANGED_MARKER = "Selection-Changed";
+
+ @Nullable private BandController mBandManager;
+
+ /**
+ * @param recyclerView
+ * @param mode Selection mode
+ */
+ public MultiSelectManager(final RecyclerView recyclerView, int mode) {
+ this(recyclerView.getAdapter(), mode);
+
+ mEnvironment = new RuntimeSelectionEnvironment(recyclerView);
+
+ if (mode == MODE_MULTIPLE) {
+ mBandManager = new BandController();
+ }
+
+ GestureDetector.SimpleOnGestureListener listener =
+ new GestureDetector.SimpleOnGestureListener() {
+ @Override
+ public boolean onSingleTapUp(MotionEvent e) {
+ return MultiSelectManager.this.onSingleTapUp(
+ new MotionInputEvent(e, recyclerView));
+ }
+ @Override
+ public void onLongPress(MotionEvent e) {
+ MultiSelectManager.this.onLongPress(
+ new MotionInputEvent(e, recyclerView));
+ }
+ };
+
+ final GestureDetector detector = new GestureDetector(recyclerView.getContext(), listener);
+ detector.setOnDoubleTapListener(listener);
+
+ recyclerView.addOnItemTouchListener(
+ new RecyclerView.OnItemTouchListener() {
+ @Override
+ public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
+ detector.onTouchEvent(e);
+
+ if (mBandManager != null) {
+ return mBandManager.handleEvent(new MotionInputEvent(e, recyclerView));
+ }
+ return false;
+ }
+
+ @Override
+ public void onTouchEvent(RecyclerView rv, MotionEvent e) {
+ mBandManager.processInputEvent(
+ new MotionInputEvent(e, recyclerView));
+ }
+ @Override
+ public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {}
+ });
+ }
+
+ /**
+ * Constructs a new instance with {@code adapter} and {@code helper}.
+ * @hide
+ */
+ @VisibleForTesting
+ MultiSelectManager(Adapter<?> adapter, int mode) {
+ checkNotNull(adapter, "'adapter' cannot be null.");
+
+ mSingleSelect = mode == MODE_SINGLE;
+
+ mAdapter = adapter;
+
+ mAdapter.registerAdapterDataObserver(
+ new AdapterDataObserver() {
+
+ @Override
+ public void onChanged() {
+ mSelection.clear();
+ }
+
+ @Override
+ public void onItemRangeChanged(
+ int positionStart, int itemCount, Object payload) {
+ // No change in position. Ignoring.
+ }
+
+ @Override
+ public void onItemRangeInserted(int positionStart, int itemCount) {
+ mSelection.expand(positionStart, itemCount);
+ }
+
+ @Override
+ public void onItemRangeRemoved(int positionStart, int itemCount) {
+ mSelection.collapse(positionStart, itemCount);
+ }
+
+ @Override
+ public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
+ throw new UnsupportedOperationException();
+ }
+ });
+ }
+
+ /**
+ * Adds {@code callback} such that it will be notified when {@code MultiSelectManager}
+ * events occur.
+ *
+ * @param callback
+ */
+ public void addCallback(MultiSelectManager.Callback callback) {
+ mCallbacks.add(callback);
+ }
+
+ public boolean hasSelection() {
+ return !mSelection.isEmpty();
+ }
+
+ /**
+ * Returns a Selection object that provides a live view
+ * on the current selection.
+ *
+ * @see #getSelection(Selection) on how to get a snapshot
+ * of the selection that will not reflect future changes
+ * to selection.
+ *
+ * @return The current selection.
+ */
+ public Selection getSelection() {
+ return mSelection;
+ }
+
+ /**
+ * Updates {@code dest} to reflect the current selection.
+ * @param dest
+ *
+ * @return The Selection instance passed in, for convenience.
+ */
+ public Selection getSelection(Selection dest) {
+ dest.copyFrom(mSelection);
+ return dest;
+ }
+
+ /**
+ * Causes item at {@code position} in adapter to be selected.
+ *
+ * @param position Adapter position
+ * @param selected
+ * @return True if the selection state of the item changed.
+ */
+ @VisibleForTesting
+ public boolean setItemSelected(int position, boolean selected) {
+ if (mSingleSelect && hasSelection()) {
+ clearSelectionQuietly();
+ }
+ return setItemsSelected(position, 1, selected);
+ }
+
+ /**
+ * Sets the selected state of the specified items. Note that the callback will NOT
+ * be consulted to see if an item can be selected.
+ *
+ * @return True if the selection state of any of the items changed.
+ */
+ public boolean setItemsSelected(int position, int length, boolean selected) {
+ boolean changed = false;
+ for (int i = position; i < position + length; i++) {
+ boolean itemChanged = selected ? mSelection.add(i) : mSelection.remove(i);
+ if (itemChanged) {
+ notifyItemStateChanged(i, selected);
+ }
+ changed |= itemChanged;
+ }
+
+ notifySelectionChanged();
+ return changed;
+ }
+
+ /**
+ * Clears the selection and notifies (even if nothing changes).
+ */
+ public void clearSelection() {
+ clearSelectionQuietly();
+ notifySelectionChanged();
+ }
+
+ public void handleLayoutChanged() {
+ if (mBandManager != null) {
+ mBandManager.handleLayoutChanged();
+ }
+ }
+
+ /**
+ * Clears the selection, without notifying anyone.
+ */
+ private void clearSelectionQuietly() {
+ mRanger = null;
+
+ if (!hasSelection()) {
+ return;
+ }
+ if (mIntermediateSelection == null) {
+ mIntermediateSelection = new Selection();
+ }
+ getSelection(mIntermediateSelection);
+ mSelection.clear();
+
+ for (int i = 0; i < mIntermediateSelection.size(); i++) {
+ int position = mIntermediateSelection.get(i);
+ notifyItemStateChanged(position, false);
+ }
+ }
+
+ @VisibleForTesting
+ void onLongPress(InputEvent input) {
+ if (DEBUG) Log.d(TAG, "Handling long press event.");
+
+ if (!input.isOverItem()) {
+ if (DEBUG) Log.i(TAG, "Cannot handle tap. No adapter position available.");
+ }
+
+ handleAdapterEvent(input);
+ }
+
+ @VisibleForTesting
+ boolean onSingleTapUp(InputEvent input) {
+ if (DEBUG) Log.d(TAG, "Processing tap event.");
+ if (!hasSelection()) {
+ // if this is a mouse click on an item, start selection mode.
+ // TODO: && input.isPrimaryButtonPressed(), but it is returning false.
+ if (input.isOverItem() && input.isMouseEvent()) {
+ toggleSelection(input.getItemPosition());
+ }
+ return false;
+ }
+
+ if (!input.isOverItem()) {
+ if (DEBUG) Log.d(TAG, "Activity has no position. Canceling selection.");
+ clearSelection();
+ return false;
+ }
+
+ handleAdapterEvent(input);
+ return true;
+ }
+
+ /**
+ * Handles a change caused by a click on the item with the given position. If the Shift key is
+ * held down, this performs a range select; otherwise, it simply toggles the item's selection
+ * state.
+ */
+ private void handleAdapterEvent(InputEvent input) {
+ if (mRanger != null && input.isShiftKeyDown()) {
+ mRanger.snapSelection(input.getItemPosition());
+
+ // We're being lazy here notifying even when something might not have changed.
+ // To make this more correct, we'd need to update the Ranger class to return
+ // information about what has changed.
+ notifySelectionChanged();
+ } else {
+ toggleSelection(input.getItemPosition());
+ }
+ }
+
+ /**
+ * Toggles the selection state at position. If an item does end up selected
+ * a new Ranger (range selection manager) at that point is created.
+ *
+ * @param position
+ */
+ public void toggleSelection(int position) {
+ // Position may be special "no position" during certain
+ // transitional phases. If so, skip handling of the event.
+ if (position == RecyclerView.NO_POSITION) {
+ if (DEBUG) Log.d(TAG, "Ignoring toggle for element with no position.");
+ return;
+ }
+
+ boolean changed = false;
+ if (mSelection.contains(position)) {
+ changed = attemptDeselect(position);
+ } else {
+ boolean canSelect = notifyBeforeItemStateChange(position, true);
+ if (!canSelect) {
+ return;
+ }
+ if (mSingleSelect && hasSelection()) {
+ clearSelectionQuietly();
+ }
+
+ // Here we're already in selection mode. In that case
+ // When a simple click/tap (without SHIFT) creates causes
+ // an item to be selected.
+ // By recreating Ranger at this point, we allow the user to create
+ // multiple separate contiguous ranges with SHIFT+Click & Click.
+ selectAndNotify(position);
+ setSelectionFocusBegin(position);
+ changed = true;
+ }
+
+ if (changed) {
+ notifySelectionChanged();
+ }
+ }
+
+ /**
+ * Sets the magic location at which a selection range begins. This
+ * value is consulted when determining how to extend, and modify
+ * selection ranges.
+ *
+ * @throws IllegalStateException if {@code position} is not already be selected
+ * @param position
+ */
+ void setSelectionFocusBegin(int position) {
+ checkState(mSelection.contains(position));
+ mRanger = new Range(position);
+ }
+
+ /**
+ * Try to select all elements in range. Not that callbacks can cancel selection
+ * of specific items, so some or even all items may not reflect the desired
+ * state after the update is complete.
+ *
+ * @param begin inclusive
+ * @param end inclusive
+ * @param selected
+ */
+ private void updateRange(int begin, int end, boolean selected) {
+ checkState(end >= begin);
+ for (int i = begin; i <= end; i++) {
+ if (selected) {
+ boolean canSelect = notifyBeforeItemStateChange(i, true);
+ if (canSelect) {
+ if (mSingleSelect && hasSelection()) {
+ clearSelectionQuietly();
+ }
+ selectAndNotify(i);
+ }
+ } else {
+ attemptDeselect(i);
+ }
+ }
+ }
+
+ /**
+ * @param position
+ * @return True if the update was applied.
+ */
+ private boolean selectAndNotify(int position) {
+ boolean changed = mSelection.add(position);
+ if (changed) {
+ notifyItemStateChanged(position, true);
+ }
+ return changed;
+ }
+
+ /**
+ * @param position
+ * @return True if the update was applied.
+ */
+ private boolean attemptDeselect(int position) {
+ if (notifyBeforeItemStateChange(position, false)) {
+ mSelection.remove(position);
+ notifyItemStateChanged(position, false);
+ if (DEBUG) Log.d(TAG, "Selection after deselect: " + mSelection);
+ return true;
+ } else {
+ if (DEBUG) Log.d(TAG, "Select cancelled by listener.");
+ return false;
+ }
+ }
+
+ private boolean notifyBeforeItemStateChange(int position, boolean nextState) {
+ int lastListener = mCallbacks.size() - 1;
+ for (int i = lastListener; i > -1; i--) {
+ if (!mCallbacks.get(i).onBeforeItemStateChange(position, nextState)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Notifies registered listeners when the selection status of a single item
+ * (identified by {@code position}) changes.
+ */
+ private void notifyItemStateChanged(int position, boolean selected) {
+ int lastListener = mCallbacks.size() - 1;
+ for (int i = lastListener; i > -1; i--) {
+ mCallbacks.get(i).onItemStateChanged(position, selected);
+ }
+ mAdapter.notifyItemChanged(position, SELECTION_CHANGED_MARKER);
+ }
+
+ /**
+ * Notifies registered listeners when the selection has changed. This
+ * notification should be sent only once a full series of changes
+ * is complete, e.g. clearingSelection, or updating the single
+ * selection from one item to another.
+ */
+ private void notifySelectionChanged() {
+ int lastListener = mCallbacks.size() - 1;
+ for (int i = lastListener; i > -1; i--) {
+ mCallbacks.get(i).onSelectionChanged();
+ }
+ }
+
+ /**
+ * Class providing support for managing range selections.
+ */
+ private final class Range {
+ private static final int UNDEFINED = -1;
+
+ final int mBegin;
+ int mEnd = UNDEFINED;
+
+ public Range(int begin) {
+ if (DEBUG) Log.d(TAG, "New Ranger created beginning @ " + begin);
+ mBegin = begin;
+ }
+
+ private void snapSelection(int position) {
+ checkState(mRanger != null);
+ checkArgument(position != RecyclerView.NO_POSITION);
+
+ if (mEnd == UNDEFINED || mEnd == mBegin) {
+ // Reset mEnd so it can be established in establishRange.
+ mEnd = UNDEFINED;
+ establishRange(position);
+ } else {
+ reviseRange(position);
+ }
+ }
+
+ private void establishRange(int position) {
+ checkState(mRanger.mEnd == UNDEFINED);
+
+ if (position == mBegin) {
+ mEnd = position;
+ }
+
+ if (position > mBegin) {
+ updateRange(mBegin + 1, position, true);
+ } else if (position < mBegin) {
+ updateRange(position, mBegin - 1, true);
+ }
+
+ mEnd = position;
+ }
+
+ private void reviseRange(int position) {
+ checkState(mEnd != UNDEFINED);
+ checkState(mBegin != mEnd);
+
+ if (position == mEnd) {
+ if (DEBUG) Log.i(TAG, "Skipping no-op revision click on mEndRange.");
+ }
+
+ if (mEnd > mBegin) {
+ reviseAscendingRange(position);
+ } else if (mEnd < mBegin) {
+ reviseDescendingRange(position);
+ }
+ // the "else" case is covered by checkState at beginning of method.
+
+ mEnd = position;
+ }
+
+ /**
+ * Updates an existing ascending seleciton.
+ * @param position
+ */
+ private void reviseAscendingRange(int position) {
+ // Reducing or reversing the range....
+ if (position < mEnd) {
+ if (position < mBegin) {
+ updateRange(mBegin + 1, mEnd, false);
+ updateRange(position, mBegin -1, true);
+ } else {
+ updateRange(position + 1, mEnd, false);
+ }
+ }
+
+ // Extending the range...
+ else if (position > mEnd) {
+ updateRange(mEnd + 1, position, true);
+ }
+ }
+
+ private void reviseDescendingRange(int position) {
+ // Reducing or reversing the range....
+ if (position > mEnd) {
+ if (position > mBegin) {
+ updateRange(mEnd, mBegin - 1, false);
+ updateRange(mBegin + 1, position, true);
+ } else {
+ updateRange(mEnd, position - 1, false);
+ }
+ }
+
+ // Extending the range...
+ else if (position < mEnd) {
+ updateRange(position, mEnd - 1, true);
+ }
+ }
+ }
+
+ /**
+ * Object representing the current selection. Provides read only access
+ * public access, and private write access.
+ */
+ public static final class Selection {
+
+ // This class tracks selected positions by managing two arrays: the saved selection, and
+ // the total selection. Saved selections are those which have been completed by tapping an
+ // item or by completing a band select operation. Provisional selections are selections
+ // which have been temporarily created by an in-progress band select operation (once the
+ // user releases the mouse button during a band select operation, the selected items
+ // become saved). The total selection is the combination of both the saved selection and
+ // the provisional selection. Tracking both separately is necessary to ensure that saved
+ // selections do not become deselected when they are removed from the provisional selection;
+ // for example, if item A is tapped (and selected), then an in-progress band select covers A
+ // then uncovers A, A should still be selected as it has been saved. To ensure this
+ // behavior, the saved selection must be tracked separately.
+ private SparseBooleanArray mSavedSelection;
+ private SparseBooleanArray mTotalSelection;
+
+ public Selection() {
+ mSavedSelection = new SparseBooleanArray();
+ mTotalSelection = new SparseBooleanArray();
+ }
+
+ @VisibleForTesting
+ public Selection(int... positions) {
+ this();
+ for (int i = 0; i < positions.length; i++) {
+ add(positions[i]);
+ }
+ }
+
+ /**
+ * @param position
+ * @return true if the position is currently selected.
+ */
+ public boolean contains(int position) {
+ return mTotalSelection.get(position);
+ }
+
+ /**
+ * Useful for iterating over selection. Please note that
+ * iteration should be done over a copy of the selection,
+ * not the live selection.
+ *
+ * @see #copyTo(MultiSelectManager.Selection)
+ *
+ * @param index
+ * @return the position value stored at specified index.
+ */
+ public int get(int index) {
+ return mTotalSelection.keyAt(index);
+ }
+
+ /**
+ * Returns an unordered array of selected positions.
+ */
+ public int[] getAll() {
+ final int size = size();
+ int[] positions = new int[size];
+ for (int i = 0; i < size; i++) {
+ positions[i] = get(i);
+ }
+ return positions;
+ }
+
+ /**
+ * @return size of the selection.
+ */
+ public int size() {
+ return mTotalSelection.size();
+ }
+
+ /**
+ * @return true if the selection is empty.
+ */
+ public boolean isEmpty() {
+ return mTotalSelection.size() == 0;
+ }
+
+ /**
+ * Sets the provisional selection, which is a temporary selection that can be saved,
+ * canceled, or adjusted at a later time. When a new provision selection is applied, the old
+ * one (if it exists) is abandoned.
+ * @return Array with entry for each position added or removed. Entries which were added
+ * contain a value of true, and entries which were removed contain a value of false.
+ */
+ @VisibleForTesting
+ protected SparseBooleanArray setProvisionalSelection(
+ SparseBooleanArray provisionalSelection) {
+ SparseBooleanArray delta = new SparseBooleanArray();
+
+ for (int i = 0; i < mTotalSelection.size(); i++) {
+ int position = mTotalSelection.keyAt(i);
+ if (!provisionalSelection.get(position) && !mSavedSelection.get(position)) {
+ // Remove each item that used to be in the selection but is unsaved and not in
+ // the new provisional selection.
+ delta.put(position, false);
+ }
+ }
+
+ for (int i = 0; i < provisionalSelection.size(); i++) {
+ int position = provisionalSelection.keyAt(i);
+ if (!mTotalSelection.get(position)) {
+ // Add each item that was not previously in the selection but is in the
+ // new provisional selection.
+ delta.put(position, true);
+ }
+ }
+
+ // Now, iterate through the changes and actually add/remove them to/from
+ // mCurrentSelection. This could not be done in the previous loops because changing the
+ // size of the selection mid-iteration changes iteration order erroneously.
+ for (int i = 0; i < delta.size(); i++) {
+ int position = delta.keyAt(i);
+ if (delta.get(position)) {
+ mTotalSelection.put(position, true);
+ } else {
+ mTotalSelection.delete(position);
+ }
+ }
+
+ return delta;
+ }
+
+ /**
+ * Saves the existing provisional selection. Once the provisional selection is saved,
+ * subsequent provisional selections which are different from this existing one cannot
+ * cause items in this existing provisional selection to become deselected.
+ */
+ @VisibleForTesting
+ protected void applyProvisionalSelection() {
+ mSavedSelection = mTotalSelection.clone();
+ }
+
+ /**
+ * Abandons the existing provisional selection so that all items provisionally selected are
+ * now deselected.
+ */
+ @VisibleForTesting
+ protected void cancelProvisionalSelection() {
+ mTotalSelection = mSavedSelection.clone();
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ boolean add(int position) {
+ if (!mTotalSelection.get(position)) {
+ mTotalSelection.put(position, true);
+ mSavedSelection.put(position, true);
+ return true;
+ }
+ return false;
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ boolean remove(int position) {
+ if (mTotalSelection.get(position)) {
+ mTotalSelection.delete(position);
+ mSavedSelection.delete(position);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Adjusts the selection range to reflect the existence of newly inserted values at
+ * the specified positions. This has the effect of adjusting all existing selected
+ * positions within the specified range accordingly. Note that this function discards any
+ * provisional selections which may have been applied.
+ *
+ * @param startPosition
+ * @param count
+ * @hide
+ */
+ @VisibleForTesting
+ void expand(int startPosition, int count) {
+ checkState(startPosition >= 0);
+ checkState(count > 0);
+ cancelProvisionalSelection();
+
+ for (int i = 0; i < mTotalSelection.size(); i++) {
+ int itemPosition = mTotalSelection.keyAt(i);
+ if (itemPosition >= startPosition) {
+ mTotalSelection.setKeyAt(i, itemPosition + count);
+ mSavedSelection.setKeyAt(i, itemPosition + count);
+ }
+ }
+ }
+
+ /**
+ * Adjusts the selection range to reflect the removal specified positions. This has
+ * the effect of adjusting all existing selected positions within the specified range
+ * accordingly. Note that this function discards any provisional selections which may have
+ * been applied.
+ *
+ * @param startPosition
+ * @param count The length of the range to collapse. Must be greater than 0.
+ * @hide
+ */
+ @VisibleForTesting
+ void collapse(int startPosition, int count) {
+ checkState(startPosition >= 0);
+ checkState(count > 0);
+
+ int endPosition = startPosition + count - 1;
+
+ SparseBooleanArray newSelection = new SparseBooleanArray();
+ for (int i = 0; i < mSavedSelection.size(); i++) {
+ int itemPosition = mSavedSelection.keyAt(i);
+ if (itemPosition < startPosition) {
+ newSelection.append(itemPosition, true);
+ } else if (itemPosition > endPosition) {
+ newSelection.append(itemPosition - count, true);
+ }
+ }
+ mSavedSelection = newSelection;
+ cancelProvisionalSelection();
+ }
+
+ public void clear() {
+ mSavedSelection.clear();
+ mTotalSelection.clear();
+ }
+
+ @VisibleForTesting
+ void copyFrom(Selection source) {
+ mSavedSelection = source.mSavedSelection.clone();
+ mTotalSelection = source.mTotalSelection.clone();
+ }
+
+ @Override
+ public String toString() {
+ if (size() <= 0) {
+ return "size=0, items=[]";
+ }
+
+ StringBuilder buffer = new StringBuilder(mTotalSelection.size() * 28);
+ buffer.append("{size=")
+ .append(mTotalSelection.size())
+ .append(", ")
+ .append("items=[");
+ for (int i=0; i < mTotalSelection.size(); i++) {
+ if (i > 0) {
+ buffer.append(", ");
+ }
+ buffer.append(mTotalSelection.keyAt(i));
+ }
+ buffer.append("]}");
+ return buffer.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return mSavedSelection.hashCode() ^ mTotalSelection.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object that) {
+ if (this == that) {
+ return true;
+ }
+
+ if (!(that instanceof Selection)) {
+ return false;
+ }
+
+ return mSavedSelection.equals(((Selection) that).mSavedSelection) &&
+ mTotalSelection.equals(((Selection) that).mTotalSelection);
+ }
+ }
+
+ /**
+ * Provides functionality for BandController. Exists primarily to tests that are
+ * fully isolated from RecyclerView.
+ */
+ interface SelectionEnvironment {
+ void showBand(Rect rect);
+ void hideBand();
+ void addOnScrollListener(RecyclerView.OnScrollListener listener);
+ void removeOnScrollListener(RecyclerView.OnScrollListener listener);
+ void scrollBy(int dy);
+ int getHeight();
+ void invalidateView();
+ void runAtNextFrame(Runnable r);
+ void removeCallback(Runnable r);
+ Point createAbsolutePoint(Point relativePoint);
+ Rect getAbsoluteRectForChildViewAt(int index);
+ int getAdapterPositionAt(int index);
+ int getAdapterPositionForChildView(View view);
+ int getColumnCount();
+ int getRowCount();
+ int getChildCount();
+ int getVisibleChildCount();
+ void focusItem(int position);
+ }
+
+ /** RvFacade implementation backed by good ol' RecyclerView. */
+ private static final class RuntimeSelectionEnvironment implements SelectionEnvironment {
+
+ private final RecyclerView mView;
+ private final Drawable mBand;
+
+ private boolean mIsOverlayShown = false;
+
+ RuntimeSelectionEnvironment(RecyclerView rv) {
+ mView = rv;
+ mBand = mView.getContext().getTheme().getDrawable(R.drawable.band_select_overlay);
+ }
+
+ @Override
+ public int getAdapterPositionForChildView(View view) {
+ if (view.getParent() == mView) {
+ return mView.getChildAdapterPosition(view);
+ } else {
+ return RecyclerView.NO_POSITION;
+ }
+ }
+
+ @Override
+ public int getAdapterPositionAt(int index) {
+ return getAdapterPositionForChildView(mView.getChildAt(index));
+ }
+
+ @Override
+ public void addOnScrollListener(OnScrollListener listener) {
+ mView.addOnScrollListener(listener);
+ }
+
+ @Override
+ public void removeOnScrollListener(OnScrollListener listener) {
+ mView.removeOnScrollListener(listener);
+ }
+
+ @Override
+ public Point createAbsolutePoint(Point relativePoint) {
+ return new Point(relativePoint.x + mView.computeHorizontalScrollOffset(),
+ relativePoint.y + mView.computeVerticalScrollOffset());
+ }
+
+ @Override
+ public Rect getAbsoluteRectForChildViewAt(int index) {
+ final View child = mView.getChildAt(index);
+ final Rect childRect = new Rect();
+ child.getHitRect(childRect);
+ childRect.left += mView.computeHorizontalScrollOffset();
+ childRect.right += mView.computeHorizontalScrollOffset();
+ childRect.top += mView.computeVerticalScrollOffset();
+ childRect.bottom += mView.computeVerticalScrollOffset();
+ return childRect;
+ }
+
+ @Override
+ public int getChildCount() {
+ return mView.getAdapter().getItemCount();
+ }
+
+ @Override
+ public int getVisibleChildCount() {
+ return mView.getChildCount();
+ }
+
+ @Override
+ public int getColumnCount() {
+ LayoutManager layoutManager = mView.getLayoutManager();
+ if (layoutManager instanceof GridLayoutManager) {
+ return ((GridLayoutManager) layoutManager).getSpanCount();
+ }
+
+ // Otherwise, it is a list with 1 column.
+ return 1;
+ }
+
+ @Override
+ public int getRowCount() {
+ int numFullColumns = getChildCount() / getColumnCount();
+ boolean hasPartiallyFullColumn = getChildCount() % getColumnCount() != 0;
+ return numFullColumns + (hasPartiallyFullColumn ? 1 : 0);
+ }
+
+ @Override
+ public int getHeight() {
+ return mView.getHeight();
+ }
+
+ @Override
+ public void invalidateView() {
+ mView.invalidate();
+ }
+
+ @Override
+ public void runAtNextFrame(Runnable r) {
+ mView.postOnAnimation(r);
+ }
+
+ @Override
+ public void removeCallback(Runnable r) {
+ mView.removeCallbacks(r);
+ }
+
+ @Override
+ public void scrollBy(int dy) {
+ mView.scrollBy(0, dy);
+ }
+
+ @Override
+ public void showBand(Rect rect) {
+ mBand.setBounds(rect);
+
+ if (!mIsOverlayShown) {
+ mView.getOverlay().add(mBand);
+ }
+ }
+
+ @Override
+ public void hideBand() {
+ mView.getOverlay().remove(mBand);
+ }
+
+ @Override
+ public void focusItem(final int pos) {
+ // If the item is already in view, focus it; otherwise, scroll to it and focus it.
+ RecyclerView.ViewHolder vh = mView.findViewHolderForAdapterPosition(pos);
+ if (vh != null) {
+ vh.itemView.requestFocus();
+ } else {
+ // Don't smooth scroll; that taxes the system unnecessarily and makes the scroll
+ // handling logic below more complicated. See b/24865658.
+ mView.scrollToPosition(pos);
+ // Set a one-time listener to request focus when the scroll has completed.
+ mView.addOnScrollListener(
+ new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrolled(RecyclerView view, int dx, int dy) {
+ view.findViewHolderForAdapterPosition(pos).itemView.requestFocus();
+ view.removeOnScrollListener(this);
+ }
+ });
+ }
+ }
+ }
+
+ public interface Callback {
+ /**
+ * Called when an item is selected or unselected while in selection mode.
+ *
+ * @param position Adapter position of the item that was checked or unchecked
+ * @param selected <code>true</code> if the item is now selected, <code>false</code>
+ * if the item is now unselected.
+ */
+ public void onItemStateChanged(int position, boolean selected);
+
+ /**
+ * Called prior to an item changing state. Callbacks can cancel
+ * the change at {@code position} by returning {@code false}.
+ *
+ * @param position Adapter position of the item that was checked or unchecked
+ * @param selected <code>true</code> if the item is to be selected, <code>false</code>
+ * if the item is to be unselected.
+ */
+ public boolean onBeforeItemStateChange(int position, boolean selected);
+
+ /**
+ * Called immediately after completion of any set of changes.
+ */
+ public void onSelectionChanged();
+ }
+
+ /**
+ * Provides mouse driven band-select support when used in conjunction with {@link RecyclerView}
+ * and {@link MultiSelectManager}. This class is responsible for rendering the band select
+ * overlay and selecting overlaid items via MultiSelectManager.
+ */
+ public class BandController extends RecyclerView.OnScrollListener
+ implements GridModel.OnSelectionChangedListener {
+
+ private static final int NOT_SET = -1;
+
+ private final Runnable mModelBuilder;
+
+ @Nullable private Rect mBounds;
+ @Nullable private Point mCurrentPosition;
+ @Nullable private Point mOrigin;
+ @Nullable private GridModel mModel;
+
+ // The time at which the current band selection-induced scroll began. If no scroll is in
+ // progress, the value is NOT_SET.
+ private long mScrollStartTime = NOT_SET;
+ private final Runnable mViewScroller = new ViewScroller();
+
+ public BandController() {
+ mEnvironment.addOnScrollListener(this);
+
+ mModelBuilder = new Runnable() {
+ @Override
+ public void run() {
+ mModel = new GridModel(mEnvironment);
+ mModel.addOnSelectionChangedListener(BandController.this);
+ }
+ };
+ }
+
+ public boolean handleEvent(MotionInputEvent e) {
+ // b/23793622 notes the fact that we *never* receive ACTION_DOWN
+ // events in onTouchEvent. Where it not for this issue, we'd
+ // push start handling down into handleInputEvent.
+ if (mBandManager.shouldStart(e)) {
+ // endBandSelect is handled in handleInputEvent.
+ mBandManager.startBandSelect(e.getOrigin());
+ } else if (mBandManager.isActive()
+ && e.isMouseEvent()
+ && e.isActionUp()) {
+ // Same issue here w b/23793622. The ACTION_UP event
+ // is only evert dispatched to onTouchEvent when
+ // there is some associated motion. If a user taps
+ // mouse, but doesn't move, then band select gets
+ // started BUT not ended. Causing phantom
+ // bands to appear when the user later clicks to start
+ // band select.
+ mBandManager.processInputEvent(e);
+ }
+
+ return isActive();
+ }
+
+ private boolean isActive() {
+ return mModel != null;
+ }
+
+ /**
+ * Handle a change in layout by cleaning up and getting rid of the old model and creating
+ * a new model which will track the new layout.
+ */
+ public void handleLayoutChanged() {
+ if (mModel != null) {
+ mModel.removeOnSelectionChangedListener(this);
+ mModel.stopListening();
+
+ // build a new model, all fresh and happy.
+ mModelBuilder.run();
+ }
+ }
+
+ boolean shouldStart(MotionInputEvent e) {
+ return !isActive()
+ && e.isMouseEvent() // a mouse
+ && e.isActionDown() // the initial button press
+ && mAdapter.getItemCount() > 0
+ && e.getItemPosition() == RecyclerView.NO_ID; // in empty space
+ }
+
+ boolean shouldStop(InputEvent input) {
+ return isActive()
+ && input.isMouseEvent()
+ && input.isActionUp();
+ }
+
+ /**
+ * Processes a MotionEvent by starting, ending, or resizing the band select overlay.
+ * @param input
+ */
+ private void processInputEvent(InputEvent input) {
+ checkArgument(input.isMouseEvent());
+
+ if (shouldStop(input)) {
+ endBandSelect();
+ return;
+ }
+
+ // We shouldn't get any events in this method when band select is not active,
+ // but it turns some guests show up late to the party.
+ if (!isActive()) {
+ return;
+ }
+
+ mCurrentPosition = input.getOrigin();
+ mModel.resizeSelection(input.getOrigin());
+ scrollViewIfNecessary();
+ resizeBandSelectRectangle();
+ }
+
+ /**
+ * Starts band select by adding the drawable to the RecyclerView's overlay.
+ */
+ private void startBandSelect(Point origin) {
+ if (DEBUG) Log.d(TAG, "Starting band select @ " + origin);
+
+ mOrigin = origin;
+ mModelBuilder.run(); // Creates a new selection model.
+ mModel.startSelection(mOrigin);
+ }
+
+ /**
+ * Scrolls the view if necessary.
+ */
+ private void scrollViewIfNecessary() {
+ mEnvironment.removeCallback(mViewScroller);
+ mViewScroller.run();
+ mEnvironment.invalidateView();
+ }
+
+ /**
+ * Resizes the band select rectangle by using the origin and the current pointer position as
+ * two opposite corners of the selection.
+ */
+ private void resizeBandSelectRectangle() {
+ mBounds = new Rect(Math.min(mOrigin.x, mCurrentPosition.x),
+ Math.min(mOrigin.y, mCurrentPosition.y),
+ Math.max(mOrigin.x, mCurrentPosition.x),
+ Math.max(mOrigin.y, mCurrentPosition.y));
+ mEnvironment.showBand(mBounds);
+ }
+
+ /**
+ * Ends band select by removing the overlay.
+ */
+ private void endBandSelect() {
+ if (DEBUG) Log.d(TAG, "Ending band select.");
+
+ mEnvironment.hideBand();
+ mSelection.applyProvisionalSelection();
+ mModel.endSelection();
+ int firstSelected = mModel.getPositionNearestOrigin();
+ if (!mSelection.contains(firstSelected)) {
+ Log.w(TAG, "First selected by band is NOT in selection!");
+ // Sadly this is really happening. Need to figure out what's going on.
+ } else if (firstSelected != GridModel.NOT_SET) {
+ setSelectionFocusBegin(firstSelected);
+ }
+
+ mModel = null;
+ mOrigin = null;
+ }
+
+ @Override
+ public void onSelectionChanged(SparseBooleanArray updatedSelection) {
+ SparseBooleanArray delta = mSelection.setProvisionalSelection(updatedSelection);
+ for (int i = 0; i < delta.size(); i++) {
+ int position = delta.keyAt(i);
+ notifyItemStateChanged(position, delta.get(position));
+ }
+ notifySelectionChanged();
+ }
+
+ private class ViewScroller implements Runnable {
+ /**
+ * The number of milliseconds of scrolling at which scroll speed continues to increase.
+ * At first, the scroll starts slowly; then, the rate of scrolling increases until it
+ * reaches its maximum value at after this many milliseconds.
+ */
+ private static final long SCROLL_ACCELERATION_LIMIT_TIME_MS = 2000;
+
+ @Override
+ public void run() {
+ // Compute the number of pixels the pointer's y-coordinate is past the view.
+ // Negative values mean the pointer is at or before the top of the view, and
+ // positive values mean that the pointer is at or after the bottom of the view. Note
+ // that one additional pixel is added here so that the view still scrolls when the
+ // pointer is exactly at the top or bottom.
+ int pixelsPastView = 0;
+ if (mCurrentPosition.y <= 0) {
+ pixelsPastView = mCurrentPosition.y - 1;
+ } else if (mCurrentPosition.y >= mEnvironment.getHeight() - 1) {
+ pixelsPastView = mCurrentPosition.y - mEnvironment.getHeight() + 1;
+ }
+
+ if (!isActive() || pixelsPastView == 0) {
+ // If band selection is inactive, or if it is active but not at the edge of the
+ // view, no scrolling is necessary.
+ mScrollStartTime = NOT_SET;
+ return;
+ }
+
+ if (mScrollStartTime == NOT_SET) {
+ // If the pointer was previously not at the edge of the view but now is, set the
+ // start time for the scroll.
+ mScrollStartTime = System.currentTimeMillis();
+ }
+
+ // Compute the number of pixels to scroll, and scroll that many pixels.
+ final int numPixels = computeScrollDistance(
+ pixelsPastView, System.currentTimeMillis() - mScrollStartTime);
+ mEnvironment.scrollBy(numPixels);
+
+ mEnvironment.removeCallback(mViewScroller);
+ mEnvironment.runAtNextFrame(this);
+ }
+
+ /**
+ * Computes the number of pixels to scroll based on how far the pointer is past the end
+ * of the view and how long it has been there. Roughly based on ItemTouchHelper's
+ * algorithm for computing the number of pixels to scroll when an item is dragged to the
+ * end of a {@link RecyclerView}.
+ * @param pixelsPastView
+ * @param scrollDuration
+ * @return
+ */
+ private int computeScrollDistance(int pixelsPastView, long scrollDuration) {
+ final int maxScrollStep = mEnvironment.getHeight();
+ final int direction = (int) Math.signum(pixelsPastView);
+ final int absPastView = Math.abs(pixelsPastView);
+
+ // Calculate the ratio of how far out of the view the pointer currently resides to
+ // the entire height of the view.
+ final float outOfBoundsRatio = Math.min(
+ 1.0f, (float) absPastView / mEnvironment.getHeight());
+ // Interpolate this ratio and use it to compute the maximum scroll that should be
+ // possible for this step.
+ final float cappedScrollStep =
+ direction * maxScrollStep * smoothOutOfBoundsRatio(outOfBoundsRatio);
+
+ // Likewise, calculate the ratio of the time spent in the scroll to the limit.
+ final float timeRatio = Math.min(
+ 1.0f, (float) scrollDuration / SCROLL_ACCELERATION_LIMIT_TIME_MS);
+ // Interpolate this ratio and use it to compute the final number of pixels to
+ // scroll.
+ final int numPixels = (int) (cappedScrollStep * smoothTimeRatio(timeRatio));
+
+ // If the final number of pixels to scroll ends up being 0, the view should still
+ // scroll at least one pixel.
+ return numPixels != 0 ? numPixels : direction;
+ }
+
+ /**
+ * Interpolates the given out of bounds ratio on a curve which starts at (0,0) and ends
+ * at (1,1) and quickly approaches 1 near the start of that interval. This ensures that
+ * drags that are at the edge or barely past the edge of the view still cause sufficient
+ * scrolling. The equation y=(x-1)^5+1 is used, but this could also be tweaked if
+ * needed.
+ * @param ratio A ratio which is in the range [0, 1].
+ * @return A "smoothed" value, also in the range [0, 1].
+ */
+ private float smoothOutOfBoundsRatio(float ratio) {
+ return (float) Math.pow(ratio - 1.0f, 5) + 1.0f;
+ }
+
+ /**
+ * Interpolates the given time ratio on a curve which starts at (0,0) and ends at (1,1)
+ * and stays close to 0 for most input values except those very close to 1. This ensures
+ * that scrolls start out very slowly but speed up drastically after the scroll has been
+ * in progress close to SCROLL_ACCELERATION_LIMIT_TIME_MS. The equation y=x^5 is used,
+ * but this could also be tweaked if needed.
+ * @param ratio A ratio which is in the range [0, 1].
+ * @return A "smoothed" value, also in the range [0, 1].
+ */
+ private float smoothTimeRatio(float ratio) {
+ return (float) Math.pow(ratio, 5);
+ }
+ };
+
+ @Override
+ public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+ if (!isActive()) {
+ return;
+ }
+
+ // Adjust the y-coordinate of the origin the opposite number of pixels so that the
+ // origin remains in the same place relative to the view's items.
+ mOrigin.y -= dy;
+ resizeBandSelectRectangle();
+ }
+ }
+
+ /**
+ * Provides a band selection item model for views within a RecyclerView. This class queries the
+ * RecyclerView to determine where its items are placed; then, once band selection is underway,
+ * it alerts listeners of which items are covered by the selections.
+ */
+ public static final class GridModel extends RecyclerView.OnScrollListener {
+
+ public static final int NOT_SET = -1;
+
+ // Enum values used to determine the corner at which the origin is located within the
+ private static final int UPPER = 0x00;
+ private static final int LOWER = 0x01;
+ private static final int LEFT = 0x00;
+ private static final int RIGHT = 0x02;
+ private static final int UPPER_LEFT = UPPER | LEFT;
+ private static final int UPPER_RIGHT = UPPER | RIGHT;
+ private static final int LOWER_LEFT = LOWER | LEFT;
+ private static final int LOWER_RIGHT = LOWER | RIGHT;
+
+ private final SelectionEnvironment mHelper;
+ private final List<OnSelectionChangedListener> mOnSelectionChangedListeners =
+ new ArrayList<>();
+
+ // Map from the x-value of the left side of a SparseBooleanArray of adapter positions, keyed
+ // by their y-offset. For example, if the first column of the view starts at an x-value of 5,
+ // mColumns.get(5) would return an array of positions in that column. Within that array, the
+ // value for key y is the adapter position for the item whose y-offset is y.
+ private final SparseArray<SparseIntArray> mColumns = new SparseArray<>();
+
+ // List of limits along the x-axis (columns).
+ // This list is sorted from furthest left to furthest right.
+ private final List<Limits> mColumnBounds = new ArrayList<>();
+
+ // List of limits along the y-axis (rows). Note that this list only contains items which
+ // have been in the viewport.
+ private final List<Limits> mRowBounds = new ArrayList<>();
+
+ // The adapter positions which have been recorded so far.
+ private final SparseBooleanArray mKnownPositions = new SparseBooleanArray();
+
+ // Array passed to registered OnSelectionChangedListeners. One array is created and reused
+ // throughout the lifetime of the object.
+ private final SparseBooleanArray mSelection = new SparseBooleanArray();
+
+ // The current pointer (in absolute positioning from the top of the view).
+ private Point mPointer = null;
+
+ // The bounds of the band selection.
+ private RelativePoint mRelativeOrigin;
+ private RelativePoint mRelativePointer;
+
+ private boolean mIsActive;
+
+ // Tracks where the band select originated from. This is used to determine where selections
+ // should expand from when Shift+click is used.
+ private int mPositionNearestOrigin = NOT_SET;
+
+ GridModel(SelectionEnvironment helper) {
+ mHelper = helper;
+ mHelper.addOnScrollListener(this);
+ }
+
+ /**
+ * Stops listening to the view's scrolls. Call this function before discarding a
+ * BandSelecModel object to prevent memory leaks.
+ */
+ void stopListening() {
+ mHelper.removeOnScrollListener(this);
+ }
+
+ /**
+ * Start a band select operation at the given point.
+ * @param relativeOrigin The origin of the band select operation, relative to the viewport.
+ * For example, if the view is scrolled to the bottom, the top-left of the viewport
+ * would have a relative origin of (0, 0), even though its absolute point has a higher
+ * y-value.
+ */
+ void startSelection(Point relativeOrigin) {
+ mIsActive = true;
+ mPointer = mHelper.createAbsolutePoint(relativeOrigin);
+
+ recordVisibleChildren();
+ mRelativeOrigin = new RelativePoint(mPointer);
+ mRelativePointer = new RelativePoint(mPointer);
+ computeCurrentSelection();
+ notifyListeners();
+ }
+
+ /**
+ * Resizes the selection by adjusting the pointer (i.e., the corner of the selection
+ * opposite the origin.
+ * @param relativePointer The pointer (opposite of the origin) of the band select operation,
+ * relative to the viewport. For example, if the view is scrolled to the bottom, the
+ * top-left of the viewport would have a relative origin of (0, 0), even though its
+ * absolute point has a higher y-value.
+ */
+ void resizeSelection(Point relativePointer) {
+ mPointer = mHelper.createAbsolutePoint(relativePointer);
+ updateModel();
+ }
+
+ /**
+ * Ends the band selection.
+ */
+ void endSelection() {
+ mIsActive = false;
+ }
+
+ /**
+ * @return The adapter position for the item nearest the origin corresponding to the latest
+ * band select operation, or NOT_SET if the selection did not cover any items.
+ */
+ int getPositionNearestOrigin() {
+ return mPositionNearestOrigin;
+ }
+
+ @Override
+ public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+ if (!mIsActive) {
+ return;
+ }
+
+ mPointer.x += dx;
+ mPointer.y += dy;
+ recordVisibleChildren();
+ updateModel();
+ }
+
+ /**
+ * Queries the view for all children and records their location metadata.
+ */
+ private void recordVisibleChildren() {
+ for (int i = 0; i < mHelper.getVisibleChildCount(); i++) {
+ int adapterPosition = mHelper.getAdapterPositionAt(i);
+ if (!mKnownPositions.get(adapterPosition)) {
+ mKnownPositions.put(adapterPosition, true);
+ recordItemData(
+ mHelper.getAbsoluteRectForChildViewAt(i), adapterPosition);
+ }
+ }
+ }
+
+ /**
+ * Updates the limits lists and column map with the given item metadata.
+ * @param absoluteChildRect The absolute rectangle for the child view being processed.
+ * @param adapterPosition The position of the child view being processed.
+ */
+ private void recordItemData(Rect absoluteChildRect, int adapterPosition) {
+ if (mColumnBounds.size() != mHelper.getColumnCount()) {
+ // If not all x-limits have been recorded, record this one.
+ recordLimits(
+ mColumnBounds, new Limits(absoluteChildRect.left, absoluteChildRect.right));
+ }
+
+ if (mRowBounds.size() != mHelper.getRowCount()) {
+ // If not all y-limits have been recorded, record this one.
+ recordLimits(
+ mRowBounds, new Limits(absoluteChildRect.top, absoluteChildRect.bottom));
+ }
+
+ SparseIntArray columnList = mColumns.get(absoluteChildRect.left);
+ if (columnList == null) {
+ columnList = new SparseIntArray();
+ mColumns.put(absoluteChildRect.left, columnList);
+ }
+ columnList.put(absoluteChildRect.top, adapterPosition);
+ }
+
+ /**
+ * Ensures limits exists within the sorted list limitsList, and adds it to the list if it
+ * does not exist.
+ */
+ private void recordLimits(List<Limits> limitsList, Limits limits) {
+ int index = Collections.binarySearch(limitsList, limits);
+ if (index < 0) {
+ limitsList.add(~index, limits);
+ }
+ }
+
+ /**
+ * Handles a moved pointer; this function determines whether the pointer movement resulted
+ * in a selection change and, if it has, notifies listeners of this change.
+ */
+ private void updateModel() {
+ RelativePoint old = mRelativePointer;
+ mRelativePointer = new RelativePoint(mPointer);
+ if (old != null && mRelativePointer.equals(old)) {
+ return;
+ }
+
+ computeCurrentSelection();
+ notifyListeners();
+ }
+
+ /**
+ * Computes the currently-selected items.
+ */
+ private void computeCurrentSelection() {
+ if (areItemsCoveredByBand(mRelativePointer, mRelativeOrigin)) {
+ updateSelection(computeBounds());
+ } else {
+ mSelection.clear();
+ mPositionNearestOrigin = NOT_SET;
+ }
+ }
+
+ /**
+ * Notifies all listeners of a selection change. Note that this function simply passes
+ * mSelection, so computeCurrentSelection() should be called before this
+ * function.
+ */
+ private void notifyListeners() {
+ for (OnSelectionChangedListener listener : mOnSelectionChangedListeners) {
+ listener.onSelectionChanged(mSelection);
+ }
+ }
+
+ /**
+ * @param rect Rectangle including all covered items.
+ */
+ private void updateSelection(Rect rect) {
+ int columnStartIndex =
+ Collections.binarySearch(mColumnBounds, new Limits(rect.left, rect.left));
+ checkState(columnStartIndex >= 0);
+ int columnEndIndex = columnStartIndex;
+
+ for (int i = columnStartIndex; i < mColumnBounds.size()
+ && mColumnBounds.get(i).lowerLimit <= rect.right; i++) {
+ columnEndIndex = i;
+ }
+
+ SparseIntArray firstColumn =
+ mColumns.get(mColumnBounds.get(columnStartIndex).lowerLimit);
+ int rowStartIndex = firstColumn.indexOfKey(rect.top);
+ if (rowStartIndex < 0) {
+ mPositionNearestOrigin = NOT_SET;
+ return;
+ }
+
+ int rowEndIndex = rowStartIndex;
+ for (int i = rowStartIndex;
+ i < firstColumn.size() && firstColumn.keyAt(i) <= rect.bottom; i++) {
+ rowEndIndex = i;
+ }
+
+ updateSelection(columnStartIndex, columnEndIndex, rowStartIndex, rowEndIndex);
+ }
+
+ /**
+ * Computes the selection given the previously-computed start- and end-indices for each
+ * row and column.
+ */
+ private void updateSelection(
+ int columnStartIndex, int columnEndIndex, int rowStartIndex, int rowEndIndex) {
+ mSelection.clear();
+ for (int column = columnStartIndex; column <= columnEndIndex; column++) {
+ SparseIntArray items = mColumns.get(mColumnBounds.get(column).lowerLimit);
+ for (int row = rowStartIndex; row <= rowEndIndex; row++) {
+ int position = items.get(items.keyAt(row));
+ mSelection.append(position, true);
+ if (isPossiblePositionNearestOrigin(column, columnStartIndex, columnEndIndex,
+ row, rowStartIndex, rowEndIndex)) {
+ // If this is the position nearest the origin, record it now so that it
+ // can be returned by endSelection() later.
+ mPositionNearestOrigin = position;
+ }
+ }
+ }
+ }
+
+ /**
+ * @return Returns true if the position is the nearest to the origin, or, in the case of the
+ * lower-right corner, whether it is possible that the position is the nearest to the
+ * origin. See comment below for reasoning for this special case.
+ */
+ private boolean isPossiblePositionNearestOrigin(int columnIndex, int columnStartIndex,
+ int columnEndIndex, int rowIndex, int rowStartIndex, int rowEndIndex) {
+ int corner = computeCornerNearestOrigin();
+ switch (corner) {
+ case UPPER_LEFT:
+ return columnIndex == columnStartIndex && rowIndex == rowStartIndex;
+ case UPPER_RIGHT:
+ return columnIndex == columnEndIndex && rowIndex == rowStartIndex;
+ case LOWER_LEFT:
+ return columnIndex == columnStartIndex && rowIndex == rowEndIndex;
+ case LOWER_RIGHT:
+ // Note that in some cases, the last row will not have as many items as there
+ // are columns (e.g., if there are 4 items and 3 columns, the second row will
+ // only have one item in the first column). This function is invoked for each
+ // position from left to right, so return true for any position in the bottom
+ // row and only the right-most position in the bottom row will be recorded.
+ return rowIndex == rowEndIndex;
+ default:
+ throw new RuntimeException("Invalid corner type.");
+ }
+ }
+
+ /**
+ * Listener for changes in which items have been band selected.
+ */
+ static interface OnSelectionChangedListener {
+ public void onSelectionChanged(SparseBooleanArray updatedSelection);
+ }
+
+ void addOnSelectionChangedListener(OnSelectionChangedListener listener) {
+ mOnSelectionChangedListeners.add(listener);
+ }
+
+ void removeOnSelectionChangedListener(OnSelectionChangedListener listener) {
+ mOnSelectionChangedListeners.remove(listener);
+ }
+
+ /**
+ * Limits of a view item. For example, if an item's left side is at x-value 5 and its right side
+ * is at x-value 10, the limits would be from 5 to 10. Used to record the left- and right sides
+ * of item columns and the top- and bottom sides of item rows so that it can be determined
+ * whether the pointer is located within the bounds of an item.
+ */
+ private static class Limits implements Comparable<Limits> {
+ int lowerLimit;
+ int upperLimit;
+
+ Limits(int lowerLimit, int upperLimit) {
+ this.lowerLimit = lowerLimit;
+ this.upperLimit = upperLimit;
+ }
+
+ @Override
+ public int compareTo(Limits other) {
+ return lowerLimit - other.lowerLimit;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof Limits)) {
+ return false;
+ }
+
+ return ((Limits) other).lowerLimit == lowerLimit &&
+ ((Limits) other).upperLimit == upperLimit;
+ }
+ }
+
+ /**
+ * The location of a coordinate relative to items. This class represents a general area of the
+ * view as it relates to band selection rather than an explicit point. For example, two
+ * different points within an item are considered to have the same "location" because band
+ * selection originating within the item would select the same items no matter which point
+ * was used. Same goes for points between items as well as those at the very beginning or end
+ * of the view.
+ *
+ * Tracking a coordinate (e.g., an x-value) as a CoordinateLocation instead of as an int has the
+ * advantage of tying the value to the Limits of items along that axis. This allows easy
+ * selection of items within those Limits as opposed to a search through every item to see if a
+ * given coordinate value falls within those Limits.
+ */
+ private static class RelativeCoordinate
+ implements Comparable<RelativeCoordinate> {
+ /**
+ * Location describing points after the last known item.
+ */
+ static final int AFTER_LAST_ITEM = 0;
+
+ /**
+ * Location describing points before the first known item.
+ */
+ static final int BEFORE_FIRST_ITEM = 1;
+
+ /**
+ * Location describing points between two items.
+ */
+ static final int BETWEEN_TWO_ITEMS = 2;
+
+ /**
+ * Location describing points within the limits of one item.
+ */
+ static final int WITHIN_LIMITS = 3;
+
+ /**
+ * The type of this coordinate, which is one of AFTER_LAST_ITEM, BEFORE_FIRST_ITEM,
+ * BETWEEN_TWO_ITEMS, or WITHIN_LIMITS.
+ */
+ final int type;
+
+ /**
+ * The limits before the coordinate; only populated when type == WITHIN_LIMITS or type ==
+ * BETWEEN_TWO_ITEMS.
+ */
+ Limits limitsBeforeCoordinate;
+
+ /**
+ * The limits after the coordinate; only populated when type == BETWEEN_TWO_ITEMS.
+ */
+ Limits limitsAfterCoordinate;
+
+ // Limits of the first known item; only populated when type == BEFORE_FIRST_ITEM.
+ Limits mFirstKnownItem;
+ // Limits of the last known item; only populated when type == AFTER_LAST_ITEM.
+ Limits mLastKnownItem;
+
+ /**
+ * @param limitsList The sorted limits list for the coordinate type. If this
+ * CoordinateLocation is an x-value, mXLimitsList should be passed; otherwise,
+ * mYLimitsList should be pased.
+ * @param value The coordinate value.
+ */
+ RelativeCoordinate(List<Limits> limitsList, int value) {
+ int index = Collections.binarySearch(limitsList, new Limits(value, value));
+
+ if (index >= 0) {
+ this.type = WITHIN_LIMITS;
+ this.limitsBeforeCoordinate = limitsList.get(index);
+ } else if (~index == 0) {
+ this.type = BEFORE_FIRST_ITEM;
+ this.mFirstKnownItem = limitsList.get(0);
+ } else if (~index == limitsList.size()) {
+ Limits lastLimits = limitsList.get(limitsList.size() - 1);
+ if (lastLimits.lowerLimit <= value && value <= lastLimits.upperLimit) {
+ this.type = WITHIN_LIMITS;
+ this.limitsBeforeCoordinate = lastLimits;
+ } else {
+ this.type = AFTER_LAST_ITEM;
+ this.mLastKnownItem = lastLimits;
+ }
+ } else {
+ Limits limitsBeforeIndex = limitsList.get(~index - 1);
+ if (limitsBeforeIndex.lowerLimit <= value && value <= limitsBeforeIndex.upperLimit) {
+ this.type = WITHIN_LIMITS;
+ this.limitsBeforeCoordinate = limitsList.get(~index - 1);
+ } else {
+ this.type = BETWEEN_TWO_ITEMS;
+ this.limitsBeforeCoordinate = limitsList.get(~index - 1);
+ this.limitsAfterCoordinate = limitsList.get(~index);
+ }
+ }
+ }
+
+ int toComparisonValue() {
+ if (type == BEFORE_FIRST_ITEM) {
+ return mFirstKnownItem.lowerLimit - 1;
+ } else if (type == AFTER_LAST_ITEM) {
+ return mLastKnownItem.upperLimit + 1;
+ } else if (type == BETWEEN_TWO_ITEMS) {
+ return limitsBeforeCoordinate.upperLimit + 1;
+ } else {
+ return limitsBeforeCoordinate.lowerLimit;
+ }
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof RelativeCoordinate)) {
+ return false;
+ }
+
+ RelativeCoordinate otherCoordinate = (RelativeCoordinate) other;
+ return toComparisonValue() == otherCoordinate.toComparisonValue();
+ }
+
+ @Override
+ public int compareTo(RelativeCoordinate other) {
+ return toComparisonValue() - other.toComparisonValue();
+ }
+ }
+
+ /**
+ * The location of a point relative to the Limits of nearby items; consists of both an x- and
+ * y-RelativeCoordinateLocation.
+ */
+ private class RelativePoint {
+ final RelativeCoordinate xLocation;
+ final RelativeCoordinate yLocation;
+
+ RelativePoint(Point point) {
+ this.xLocation = new RelativeCoordinate(mColumnBounds, point.x);
+ this.yLocation = new RelativeCoordinate(mRowBounds, point.y);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof RelativePoint)) {
+ return false;
+ }
+
+ RelativePoint otherPoint = (RelativePoint) other;
+ return xLocation.equals(otherPoint.xLocation) && yLocation.equals(otherPoint.yLocation);
+ }
+ }
+
+ /**
+ * Generates a rectangle which contains the items selected by the pointer and origin.
+ * @return The rectangle, or null if no items were selected.
+ */
+ private Rect computeBounds() {
+ Rect rect = new Rect();
+ rect.left = getCoordinateValue(
+ min(mRelativeOrigin.xLocation, mRelativePointer.xLocation),
+ mColumnBounds,
+ true);
+ rect.right = getCoordinateValue(
+ max(mRelativeOrigin.xLocation, mRelativePointer.xLocation),
+ mColumnBounds,
+ false);
+ rect.top = getCoordinateValue(
+ min(mRelativeOrigin.yLocation, mRelativePointer.yLocation),
+ mRowBounds,
+ true);
+ rect.bottom = getCoordinateValue(
+ max(mRelativeOrigin.yLocation, mRelativePointer.yLocation),
+ mRowBounds,
+ false);
+ return rect;
+ }
+
+ /**
+ * Computes the corner of the selection nearest the origin.
+ * @return
+ */
+ private int computeCornerNearestOrigin() {
+ int cornerValue = 0;
+
+ if (mRelativeOrigin.yLocation ==
+ min(mRelativeOrigin.yLocation, mRelativePointer.yLocation)) {
+ cornerValue |= UPPER;
+ } else {
+ cornerValue |= LOWER;
+ }
+
+ if (mRelativeOrigin.xLocation ==
+ min(mRelativeOrigin.xLocation, mRelativePointer.xLocation)) {
+ cornerValue |= LEFT;
+ } else {
+ cornerValue |= RIGHT;
+ }
+
+ return cornerValue;
+ }
+
+ private RelativeCoordinate min(RelativeCoordinate first, RelativeCoordinate second) {
+ return first.compareTo(second) < 0 ? first : second;
+ }
+
+ private RelativeCoordinate max(RelativeCoordinate first, RelativeCoordinate second) {
+ return first.compareTo(second) > 0 ? first : second;
+ }
+
+ /**
+ * @return The absolute coordinate (i.e., the x- or y-value) of the given relative
+ * coordinate.
+ */
+ private int getCoordinateValue(RelativeCoordinate coordinate,
+ List<Limits> limitsList, boolean isStartOfRange) {
+ switch (coordinate.type) {
+ case RelativeCoordinate.BEFORE_FIRST_ITEM:
+ return limitsList.get(0).lowerLimit;
+ case RelativeCoordinate.AFTER_LAST_ITEM:
+ return limitsList.get(limitsList.size() - 1).upperLimit;
+ case RelativeCoordinate.BETWEEN_TWO_ITEMS:
+ if (isStartOfRange) {
+ return coordinate.limitsAfterCoordinate.lowerLimit;
+ } else {
+ return coordinate.limitsBeforeCoordinate.upperLimit;
+ }
+ case RelativeCoordinate.WITHIN_LIMITS:
+ return coordinate.limitsBeforeCoordinate.lowerLimit;
+ }
+
+ throw new RuntimeException("Invalid coordinate value.");
+ }
+
+ private boolean areItemsCoveredByBand(
+ RelativePoint first, RelativePoint second) {
+ return doesCoordinateLocationCoverItems(first.xLocation, second.xLocation) &&
+ doesCoordinateLocationCoverItems(first.yLocation, second.yLocation);
+ }
+
+ private boolean doesCoordinateLocationCoverItems(
+ RelativeCoordinate pointerCoordinate,
+ RelativeCoordinate originCoordinate) {
+ if (pointerCoordinate.type == RelativeCoordinate.BEFORE_FIRST_ITEM &&
+ originCoordinate.type == RelativeCoordinate.BEFORE_FIRST_ITEM) {
+ return false;
+ }
+
+ if (pointerCoordinate.type == RelativeCoordinate.AFTER_LAST_ITEM &&
+ originCoordinate.type == RelativeCoordinate.AFTER_LAST_ITEM) {
+ return false;
+ }
+
+ if (pointerCoordinate.type == RelativeCoordinate.BETWEEN_TWO_ITEMS &&
+ originCoordinate.type == RelativeCoordinate.BETWEEN_TWO_ITEMS &&
+ pointerCoordinate.limitsBeforeCoordinate.equals(
+ originCoordinate.limitsBeforeCoordinate) &&
+ pointerCoordinate.limitsAfterCoordinate.equals(
+ originCoordinate.limitsAfterCoordinate)) {
+ return false;
+ }
+
+ return true;
+ }
+ }
+
+ // TODO: Might have to move this to a more global level. e.g. What should happen if the
+ // user taps a file and then presses shift-down? Currently the RecyclerView never even sees
+ // the key event. Perhaps install a global key handler to catch those events while in
+ // selection mode?
+ @Override
+ public boolean onKey(View view, int keyCode, KeyEvent event) {
+ // Listen for key-down events. This allows the handler to respond appropriately when
+ // the user holds down the arrow keys for navigation.
+ if (event.getAction() != KeyEvent.ACTION_DOWN) {
+ return false;
+ }
+
+ int target = RecyclerView.NO_POSITION;
+ if (keyCode == KeyEvent.KEYCODE_MOVE_HOME) {
+ target = 0;
+ } else if (keyCode == KeyEvent.KEYCODE_MOVE_END) {
+ target = mAdapter.getItemCount() - 1;
+ } else {
+ // Find a navigation target based on the arrow key that the user pressed. Ignore
+ // navigation targets that aren't items in the recycler view.
+ int searchDir = -1;
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_DPAD_UP:
+ searchDir = View.FOCUS_UP;
+ break;
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ searchDir = View.FOCUS_DOWN;
+ break;
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ searchDir = View.FOCUS_LEFT;
+ break;
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ searchDir = View.FOCUS_RIGHT;
+ break;
+ }
+ if (searchDir != -1) {
+ View targetView = view.focusSearch(searchDir);
+ // TargetView can be null, for example, if the user pressed <down> at the bottom of
+ // the list.
+ if (targetView != null) {
+ target = mEnvironment.getAdapterPositionForChildView(targetView);
+ }
+ }
+ }
+
+ if (target == RecyclerView.NO_POSITION) {
+ // If there is no valid navigation target, don't handle the keypress.
+ return false;
+ }
+
+ // Focus the new file.
+ mEnvironment.focusItem(target);
+
+ if (event.isShiftPressed()) {
+ if (!hasSelection()) {
+ // If there is no selection, start a selection when the user presses shift-arrow.
+ toggleSelection(mEnvironment.getAdapterPositionForChildView(view));
+ }
+
+ mRanger.snapSelection(target);
+ notifySelectionChanged();
+ }
+
+ return true;
+ }
+
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
index ecf4d6c..723700d 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
@@ -44,6 +44,16 @@
private static final int VERSION_INIT = 1;
private static final int VERSION_DROP_TYPE = 2;
+ // The values of these constants determine the sort order of various roots in the RootsFragment.
+ public static final int TYPE_IMAGES = 1;
+ public static final int TYPE_VIDEO = 2;
+ public static final int TYPE_AUDIO = 3;
+ public static final int TYPE_RECENTS = 4;
+ public static final int TYPE_DOWNLOADS = 5;
+ public static final int TYPE_LOCAL = 6;
+ public static final int TYPE_MTP = 7;
+ public static final int TYPE_CLOUD = 8;
+
public String authority;
public String rootId;
public int flags;
@@ -57,6 +67,7 @@
/** Derived fields that aren't persisted */
public String[] derivedMimeTypes;
public int derivedIcon;
+ public int derivedType;
public RootInfo() {
reset();
@@ -76,6 +87,7 @@
derivedMimeTypes = null;
derivedIcon = 0;
+ derivedType = 0;
}
@Override
@@ -158,14 +170,25 @@
// TODO: remove these special case icons
if (isExternalStorage()) {
derivedIcon = R.drawable.ic_root_sdcard;
+ derivedType = TYPE_LOCAL;
} else if (isDownloads()) {
derivedIcon = R.drawable.ic_root_download;
+ derivedType = TYPE_DOWNLOADS;
} else if (isImages()) {
derivedIcon = R.drawable.ic_doc_image;
+ derivedType = TYPE_IMAGES;
} else if (isVideos()) {
derivedIcon = R.drawable.ic_doc_video;
+ derivedType = TYPE_VIDEO;
} else if (isAudio()) {
derivedIcon = R.drawable.ic_doc_audio;
+ derivedType = TYPE_AUDIO;
+ } else if (isRecents()) {
+ derivedType = TYPE_RECENTS;
+ } else if (isMtp()) {
+ derivedType = TYPE_MTP;
+ } else {
+ derivedType = TYPE_CLOUD;
}
}
@@ -196,6 +219,15 @@
&& "audio_root".equals(rootId);
}
+ public boolean isMtp() {
+ return "com.android.mtp.documents".equals(authority);
+ }
+
+ public boolean isLibrary() {
+ return derivedType == TYPE_IMAGES || derivedType == TYPE_VIDEO || derivedType == TYPE_AUDIO
+ || derivedType == TYPE_RECENTS || derivedType == TYPE_DOWNLOADS;
+ }
+
@Override
public String toString() {
return "Root{authority=" + authority + ", rootId=" + rootId + ", title=" + title + "}";
@@ -217,15 +249,6 @@
}
}
- public Drawable loadGridIcon(Context context) {
- if (derivedIcon != 0) {
- return IconUtils.applyTintAttr(context, derivedIcon,
- android.R.attr.textColorPrimaryInverse);
- } else {
- return IconUtils.loadPackageIcon(context, authority, icon);
- }
- }
-
public Drawable loadToolbarIcon(Context context) {
if (derivedIcon != 0) {
return IconUtils.applyTintAttr(context, derivedIcon,
diff --git a/packages/DocumentsUI/tests/Android.mk b/packages/DocumentsUI/tests/Android.mk
index 3f191a9..b65ac98 100644
--- a/packages/DocumentsUI/tests/Android.mk
+++ b/packages/DocumentsUI/tests/Android.mk
@@ -3,11 +3,12 @@
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
+#LOCAL_SDK_VERSION := current
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 mockito-target guava
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 mockito-target ub-uiautomator
LOCAL_PACKAGE_NAME := DocumentsUITests
LOCAL_INSTRUMENTATION_FOR := DocumentsUI
@@ -15,3 +16,4 @@
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)
+
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java
index fc42c3b..6f1a89b 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java
@@ -28,6 +28,7 @@
import android.os.Parcelable;
import android.os.RemoteException;
import android.provider.DocumentsContract;
+import android.provider.DocumentsContract.Document;
import android.test.MoreAsserts;
import android.test.ServiceTestCase;
import android.test.mock.MockContentResolver;
@@ -36,6 +37,7 @@
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.DocumentStack;
import com.android.documentsui.model.RootInfo;
+
import com.google.common.collect.Lists;
import libcore.io.IoUtils;
@@ -52,6 +54,404 @@
public class CopyTest extends ServiceTestCase<CopyService> {
+ public CopyTest() {
+ super(CopyService.class);
+ }
+
+ private static String AUTHORITY = "com.android.documentsui.stubprovider";
+ private static String SRC_ROOT = StubProvider.ROOT_0_ID;
+ private static String DST_ROOT = StubProvider.ROOT_1_ID;
+ private static String TAG = "CopyTest";
+
+ private Context mContext;
+ private TestContentResolver mResolver;
+ private ContentProviderClient mClient;
+ private DocumentsProviderHelper mDocHelper;
+ private StubProvider mStorage;
+ private Context mSystemContext;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ setupTestContext();
+ mClient = mResolver.acquireContentProviderClient(AUTHORITY);
+
+ // Reset the stub provider's storage.
+ mStorage.clearCacheAndBuildRoots();
+
+ mDocHelper = new DocumentsProviderHelper(AUTHORITY, mClient);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ mClient.release();
+ super.tearDown();
+ }
+
+ /**
+ * Test copying a single file.
+ */
+ public void testCopyFile() throws Exception {
+ String srcPath = "/test0.txt";
+ Uri testFile = mStorage.createFile(SRC_ROOT, srcPath, "text/plain",
+ "The five boxing wizards jump quickly".getBytes());
+
+ assertDstFileCountEquals(0);
+
+ startService(createCopyIntent(Lists.newArrayList(testFile)));
+
+ // 2 operations: file creation, then writing data.
+ mResolver.waitForChanges(2);
+
+ // Verify that one file was copied; check file contents.
+ assertDstFileCountEquals(1);
+ assertCopied(srcPath);
+ }
+
+ public void testMoveFile() throws Exception {
+ String srcPath = "/test0.txt";
+ String testContent = "The five boxing wizards jump quickly";
+ Uri testFile = mStorage.createFile(SRC_ROOT, srcPath, "text/plain", testContent.getBytes());
+
+ assertDstFileCountEquals(0);
+
+ Intent moveIntent = createCopyIntent(Lists.newArrayList(testFile));
+ moveIntent.putExtra(CopyService.EXTRA_TRANSFER_MODE, CopyService.TRANSFER_MODE_MOVE);
+ startService(moveIntent);
+
+ // 3 operations: file creation, writing data, deleting original.
+ mResolver.waitForChanges(3);
+
+ // Verify that one file was moved; check file contents.
+ assertDstFileCountEquals(1);
+ assertDoesNotExist(SRC_ROOT, srcPath);
+
+ byte[] dstContent = readFile(DST_ROOT, srcPath);
+ MoreAsserts.assertEquals("Moved file contents differ", testContent.getBytes(), dstContent);
+ }
+
+ /**
+ * Test copying multiple files.
+ */
+ public void testCopyMultipleFiles() throws Exception {
+ String testContent[] = {
+ "The five boxing wizards jump quickly",
+ "The quick brown fox jumps over the lazy dog",
+ "Jackdaws love my big sphinx of quartz"
+ };
+ String srcPaths[] = {
+ "/test0.txt",
+ "/test1.txt",
+ "/test2.txt"
+ };
+ List<Uri> testFiles = Lists.newArrayList(
+ mStorage.createFile(SRC_ROOT, srcPaths[0], "text/plain", testContent[0].getBytes()),
+ mStorage.createFile(SRC_ROOT, srcPaths[1], "text/plain", testContent[1].getBytes()),
+ mStorage.createFile(SRC_ROOT, srcPaths[2], "text/plain", testContent[2].getBytes()));
+
+ assertDstFileCountEquals(0);
+
+ // Copy all the test files.
+ startService(createCopyIntent(testFiles));
+
+ // 3 file creations, 3 file writes.
+ mResolver.waitForChanges(6);
+
+ assertDstFileCountEquals(3);
+ for (String path : srcPaths) {
+ assertCopied(path);
+ }
+ }
+
+ public void testCopyEmptyDir() throws Exception {
+ String srcPath = "/emptyDir";
+ Uri testDir = mStorage.createFile(SRC_ROOT, srcPath, DocumentsContract.Document.MIME_TYPE_DIR,
+ null);
+
+ assertDstFileCountEquals(0);
+
+ startService(createCopyIntent(Lists.newArrayList(testDir)));
+
+ // Just 1 operation: Directory creation.
+ mResolver.waitForChanges(1);
+
+ assertDstFileCountEquals(1);
+
+ // Verify that the dst exists and is a directory.
+ File dst = mStorage.getFile(DST_ROOT, srcPath);
+ assertTrue(dst.isDirectory());
+ }
+
+ public void testMoveEmptyDir() throws Exception {
+ String srcPath = "/emptyDir";
+ Uri testDir = mStorage.createFile(SRC_ROOT, srcPath, DocumentsContract.Document.MIME_TYPE_DIR,
+ null);
+
+ assertDstFileCountEquals(0);
+
+ Intent moveIntent = createCopyIntent(Lists.newArrayList(testDir));
+ moveIntent.putExtra(CopyService.EXTRA_TRANSFER_MODE, CopyService.TRANSFER_MODE_MOVE);
+ startService(moveIntent);
+
+ // 2 operations: Directory creation, and removal of the original.
+ mResolver.waitForChanges(2);
+
+ assertDstFileCountEquals(1);
+
+ // Verify that the dst exists and is a directory.
+ File dst = mStorage.getFile(DST_ROOT, srcPath);
+ assertTrue(dst.isDirectory());
+
+ // Verify that the src was cleaned up.
+ assertDoesNotExist(SRC_ROOT, srcPath);
+ }
+
+ public void testMovePopulatedDir() throws Exception {
+ String testContent[] = {
+ "The five boxing wizards jump quickly",
+ "The quick brown fox jumps over the lazy dog",
+ "Jackdaws love my big sphinx of quartz"
+ };
+ String srcDir = "/testdir";
+ String srcFiles[] = {
+ srcDir + "/test0.txt",
+ srcDir + "/test1.txt",
+ srcDir + "/test2.txt"
+ };
+ // Create test dir; put some files in it.
+ Uri testDir = mStorage.createFile(SRC_ROOT, srcDir, DocumentsContract.Document.MIME_TYPE_DIR,
+ null);
+ mStorage.createFile(SRC_ROOT, srcFiles[0], "text/plain", testContent[0].getBytes());
+ mStorage.createFile(SRC_ROOT, srcFiles[1], "text/plain", testContent[1].getBytes());
+ mStorage.createFile(SRC_ROOT, srcFiles[2], "text/plain", testContent[2].getBytes());
+
+ Intent moveIntent = createCopyIntent(Lists.newArrayList(testDir));
+ moveIntent.putExtra(CopyService.EXTRA_TRANSFER_MODE, CopyService.TRANSFER_MODE_MOVE);
+ startService(moveIntent);
+
+ // dir creation, then creation and writing of 3 files, then removal of src dir and 3 src
+ // files.
+ mResolver.waitForChanges(11);
+
+ // Check the content of the moved files.
+ File dst = mStorage.getFile(DST_ROOT, srcDir);
+ assertTrue(dst.isDirectory());
+ for (int i = 0; i < testContent.length; ++i) {
+ byte[] dstContent = readFile(DST_ROOT, srcFiles[i]);
+ MoreAsserts.assertEquals("Copied file contents differ", testContent[i].getBytes(),
+ dstContent);
+ }
+
+ // Check that the src files were removed.
+ assertDoesNotExist(SRC_ROOT, srcDir);
+ for (String srcFile : srcFiles) {
+ assertDoesNotExist(SRC_ROOT, srcFile);
+ }
+ }
+
+ public void testCopyFileWithReadErrors() throws Exception {
+ String srcPath = "/test0.txt";
+ Uri testFile = mStorage.createFile(SRC_ROOT, srcPath, "text/plain",
+ "The five boxing wizards jump quickly".getBytes());
+
+ assertDstFileCountEquals(0);
+
+ mStorage.simulateReadErrorsForFile(testFile);
+
+ startService(createCopyIntent(Lists.newArrayList(testFile)));
+
+ // 3 operations: file creation, writing, then deletion (due to failed copy).
+ mResolver.waitForChanges(3);
+
+ // Verify that the failed copy was cleaned up.
+ assertDstFileCountEquals(0);
+ }
+
+ public void testMoveFileWithReadErrors() throws Exception {
+ String srcPath = "/test0.txt";
+ Uri testFile = mStorage.createFile(SRC_ROOT, srcPath, "text/plain",
+ "The five boxing wizards jump quickly".getBytes());
+
+ assertDstFileCountEquals(0);
+
+ mStorage.simulateReadErrorsForFile(testFile);
+
+ Intent moveIntent = createCopyIntent(Lists.newArrayList(testFile));
+ moveIntent.putExtra(CopyService.EXTRA_TRANSFER_MODE, CopyService.TRANSFER_MODE_MOVE);
+ startService(moveIntent);
+
+ try {
+ // There should be 3 operations: file creation, writing, then deletion (due to failed
+ // copy). Wait for 4, in case the CopyService also attempts to do extra stuff (like
+ // delete the src file). This should time out.
+ mResolver.waitForChanges(4);
+ } catch (TimeoutException e) {
+ // Success path
+ return;
+ } finally {
+ // Verify that the failed copy was cleaned up, and the src file wasn't removed.
+ assertDstFileCountEquals(0);
+ assertExists(SRC_ROOT, srcPath);
+ }
+ // The asserts above didn't fail, but the CopyService did something unexpected.
+ fail("Extra file operations were detected");
+ }
+
+ public void testMoveDirectoryWithReadErrors() throws Exception {
+ String testContent[] = {
+ "The five boxing wizards jump quickly",
+ "The quick brown fox jumps over the lazy dog",
+ "Jackdaws love my big sphinx of quartz"
+ };
+ String srcDir = "/testdir";
+ String srcFiles[] = {
+ srcDir + "/test0.txt",
+ srcDir + "/test1.txt",
+ srcDir + "/test2.txt"
+ };
+ // Create test dir; put some files in it.
+ Uri testDir = mStorage.createFile(SRC_ROOT, srcDir, DocumentsContract.Document.MIME_TYPE_DIR,
+ null);
+ mStorage.createFile(SRC_ROOT, srcFiles[0], "text/plain", testContent[0].getBytes());
+ Uri errFile = mStorage
+ .createFile(SRC_ROOT, srcFiles[1], "text/plain", testContent[1].getBytes());
+ mStorage.createFile(SRC_ROOT, srcFiles[2], "text/plain", testContent[2].getBytes());
+
+ mStorage.simulateReadErrorsForFile(errFile);
+
+ Intent moveIntent = createCopyIntent(Lists.newArrayList(testDir));
+ moveIntent.putExtra(CopyService.EXTRA_TRANSFER_MODE, CopyService.TRANSFER_MODE_MOVE);
+ startService(moveIntent);
+
+ // - dst dir creation,
+ // - creation and writing of 2 files, removal of 2 src files
+ // - creation and writing of 1 file, then removal of that file (due to error)
+ mResolver.waitForChanges(10);
+
+ // Check that both the src and dst dirs exist. The src dir shouldn't have been removed,
+ // because it should contain the one errFile.
+ assertTrue(mStorage.getFile(SRC_ROOT, srcDir).isDirectory());
+ assertTrue(mStorage.getFile(DST_ROOT, srcDir).isDirectory());
+
+ // Check the content of the moved files.
+ MoreAsserts.assertEquals("Copied file contents differ", testContent[0].getBytes(),
+ readFile(DST_ROOT, srcFiles[0]));
+ MoreAsserts.assertEquals("Copied file contents differ", testContent[2].getBytes(),
+ readFile(DST_ROOT, srcFiles[2]));
+
+ // Check that the src files were removed.
+ assertDoesNotExist(SRC_ROOT, srcFiles[0]);
+ assertDoesNotExist(SRC_ROOT, srcFiles[2]);
+
+ // Check that the error file was not copied over.
+ assertDoesNotExist(DST_ROOT, srcFiles[1]);
+ assertExists(SRC_ROOT, srcFiles[1]);
+ }
+
+ /**
+ * Copies the given files to a pre-determined destination.
+ *
+ * @throws FileNotFoundException
+ */
+ private Intent createCopyIntent(List<Uri> srcs) throws Exception {
+ final ArrayList<DocumentInfo> srcDocs = Lists.newArrayList();
+ for (Uri src : srcs) {
+ srcDocs.add(DocumentInfo.fromUri(mResolver, src));
+ }
+
+ RootInfo root = mDocHelper.getRoot(DST_ROOT);
+ final Uri dst = DocumentsContract.buildDocumentUri(AUTHORITY, root.documentId);
+ DocumentStack stack = new DocumentStack();
+ stack.push(DocumentInfo.fromUri(mResolver, dst));
+ final Intent copyIntent = new Intent(mContext, CopyService.class);
+ copyIntent.putParcelableArrayListExtra(CopyService.EXTRA_SRC_LIST, srcDocs);
+ copyIntent.putExtra(Shared.EXTRA_STACK, (Parcelable) stack);
+
+ // startService(copyIntent);
+ return copyIntent;
+ }
+
+ /**
+ * Returns a count of the files in the given directory.
+ */
+ private void assertDstFileCountEquals(int expected) throws RemoteException {
+ RootInfo dest = mDocHelper.getRoot(DST_ROOT);
+ final Uri queryUri = DocumentsContract.buildChildDocumentsUri(AUTHORITY,
+ dest.documentId);
+ Cursor c = null;
+ int count = 0;
+ try {
+ c = mClient.query(queryUri, null, null, null, null);
+ count = c.getCount();
+ } finally {
+ IoUtils.closeQuietly(c);
+ }
+ assertEquals("Incorrect file count after copy", expected, count);
+ }
+
+ private void assertExists(String rootId, String path) throws Exception {
+ assertNotNull("An expected file was not found: " + path + " on root " + rootId,
+ mStorage.getFile(rootId, path));
+ }
+
+ private void assertDoesNotExist(String rootId, String path) throws Exception {
+ assertNull("Unexpected file found: " + path + " on root " + rootId,
+ mStorage.getFile(rootId, path));
+ }
+
+ private byte[] readFile(String rootId, String path) throws Exception {
+ File file = mStorage.getFile(rootId, path);
+ byte[] buf = null;
+ assertNotNull(file);
+
+ FileInputStream in = null;
+ try {
+ in = new FileInputStream(file);
+ buf = Streams.readFully(in);
+ } finally {
+ IoUtils.closeQuietly(in);
+ }
+ return buf;
+ }
+
+ private void assertCopied(String path) throws Exception {
+ MoreAsserts.assertEquals("Copied file contents differ", readFile(SRC_ROOT, path),
+ readFile(DST_ROOT, path));
+ }
+
+ /**
+ * Sets up a ContextWrapper that substitutes a stub NotificationManager. This allows the test to
+ * listen for notification events, to gauge copy progress.
+ *
+ * @throws FileNotFoundException
+ */
+ private void setupTestContext() throws FileNotFoundException {
+ mSystemContext = getSystemContext();
+
+ // Set up the context with the test content resolver.
+ mResolver = new TestContentResolver();
+ mContext = new ContextWrapper(mSystemContext) {
+ @Override
+ public ContentResolver getContentResolver() {
+ return mResolver;
+ }
+ };
+ setContext(mContext);
+
+ // Create a local stub provider and add it to the content resolver.
+ ProviderInfo info = new ProviderInfo();
+ info.authority = AUTHORITY;
+ info.exported = true;
+ info.grantUriPermissions = true;
+ info.readPermission = android.Manifest.permission.MANAGE_DOCUMENTS;
+ info.writePermission = android.Manifest.permission.MANAGE_DOCUMENTS;
+
+ mStorage = new StubProvider();
+ mStorage.attachInfo(mContext, info);
+ mResolver.addProvider(AUTHORITY, mStorage);
+ }
+
/**
* A test resolver that enables this test suite to listen for notifications that mark when copy
* operations are done.
@@ -103,410 +503,4 @@
}
}
};
-
- public CopyTest() {
- super(CopyService.class);
- }
-
- private static String AUTHORITY = "com.android.documentsui.stubprovider";
- private static String DST = "sd1";
- private static String SRC = "sd0";
- private static String TAG = "CopyTest";
- private List<RootInfo> mRoots;
- private Context mContext;
- private TestContentResolver mResolver;
- private ContentProviderClient mClient;
- private StubProvider mStorage;
- private Context mSystemContext;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- setupTestContext();
- mClient = mResolver.acquireContentProviderClient(AUTHORITY);
-
- // Reset the stub provider's storage.
- mStorage.clearCacheAndBuildRoots();
-
- mRoots = Lists.newArrayList();
- Uri queryUri = DocumentsContract.buildRootsUri(AUTHORITY);
- Cursor cursor = null;
- try {
- cursor = mClient.query(queryUri, null, null, null, null);
- while (cursor.moveToNext()) {
- mRoots.add(RootInfo.fromRootsCursor(AUTHORITY, cursor));
- }
- } finally {
- IoUtils.closeQuietly(cursor);
- }
-
- }
-
- @Override
- protected void tearDown() throws Exception {
- mClient.release();
- super.tearDown();
- }
-
- /**
- * Test copying a single file.
- */
- public void testCopyFile() throws Exception {
- String srcPath = "/test0.txt";
- Uri testFile = mStorage.createFile(SRC, srcPath, "text/plain",
- "The five boxing wizards jump quickly".getBytes());
-
- assertDstFileCountEquals(0);
-
- startService(createCopyIntent(Lists.newArrayList(testFile)));
-
- // 2 operations: file creation, then writing data.
- mResolver.waitForChanges(2);
-
- // Verify that one file was copied; check file contents.
- assertDstFileCountEquals(1);
- assertCopied(srcPath);
- }
-
- public void testMoveFile() throws Exception {
- String srcPath = "/test0.txt";
- String testContent = "The five boxing wizards jump quickly";
- Uri testFile = mStorage.createFile(SRC, srcPath, "text/plain", testContent.getBytes());
-
- assertDstFileCountEquals(0);
-
- Intent moveIntent = createCopyIntent(Lists.newArrayList(testFile));
- moveIntent.putExtra(CopyService.EXTRA_TRANSFER_MODE, CopyService.TRANSFER_MODE_MOVE);
- startService(moveIntent);
-
- // 3 operations: file creation, writing data, deleting original.
- mResolver.waitForChanges(3);
-
- // Verify that one file was moved; check file contents.
- assertDstFileCountEquals(1);
- assertDoesNotExist(SRC, srcPath);
-
- byte[] dstContent = readFile(DST, srcPath);
- MoreAsserts.assertEquals("Moved file contents differ", testContent.getBytes(), dstContent);
- }
-
- /**
- * Test copying multiple files.
- */
- public void testCopyMultipleFiles() throws Exception {
- String testContent[] = {
- "The five boxing wizards jump quickly",
- "The quick brown fox jumps over the lazy dog",
- "Jackdaws love my big sphinx of quartz"
- };
- String srcPaths[] = {
- "/test0.txt",
- "/test1.txt",
- "/test2.txt"
- };
- List<Uri> testFiles = Lists.newArrayList(
- mStorage.createFile(SRC, srcPaths[0], "text/plain", testContent[0].getBytes()),
- mStorage.createFile(SRC, srcPaths[1], "text/plain", testContent[1].getBytes()),
- mStorage.createFile(SRC, srcPaths[2], "text/plain", testContent[2].getBytes()));
-
- assertDstFileCountEquals(0);
-
- // Copy all the test files.
- startService(createCopyIntent(testFiles));
-
- // 3 file creations, 3 file writes.
- mResolver.waitForChanges(6);
-
- assertDstFileCountEquals(3);
- for (String path : srcPaths) {
- assertCopied(path);
- }
- }
-
- public void testCopyEmptyDir() throws Exception {
- String srcPath = "/emptyDir";
- Uri testDir = mStorage.createFile(SRC, srcPath, DocumentsContract.Document.MIME_TYPE_DIR,
- null);
-
- assertDstFileCountEquals(0);
-
- startService(createCopyIntent(Lists.newArrayList(testDir)));
-
- // Just 1 operation: Directory creation.
- mResolver.waitForChanges(1);
-
- assertDstFileCountEquals(1);
-
- // Verify that the dst exists and is a directory.
- File dst = mStorage.getFile(DST, srcPath);
- assertTrue(dst.isDirectory());
- }
-
- public void testMoveEmptyDir() throws Exception {
- String srcPath = "/emptyDir";
- Uri testDir = mStorage.createFile(SRC, srcPath, DocumentsContract.Document.MIME_TYPE_DIR,
- null);
-
- assertDstFileCountEquals(0);
-
- Intent moveIntent = createCopyIntent(Lists.newArrayList(testDir));
- moveIntent.putExtra(CopyService.EXTRA_TRANSFER_MODE, CopyService.TRANSFER_MODE_MOVE);
- startService(moveIntent);
-
- // 2 operations: Directory creation, and removal of the original.
- mResolver.waitForChanges(2);
-
- assertDstFileCountEquals(1);
-
- // Verify that the dst exists and is a directory.
- File dst = mStorage.getFile(DST, srcPath);
- assertTrue(dst.isDirectory());
-
- // Verify that the src was cleaned up.
- assertDoesNotExist(SRC, srcPath);
- }
-
- public void testMovePopulatedDir() throws Exception {
- String testContent[] = {
- "The five boxing wizards jump quickly",
- "The quick brown fox jumps over the lazy dog",
- "Jackdaws love my big sphinx of quartz"
- };
- String srcDir = "/testdir";
- String srcFiles[] = {
- srcDir + "/test0.txt",
- srcDir + "/test1.txt",
- srcDir + "/test2.txt"
- };
- // Create test dir; put some files in it.
- Uri testDir = mStorage.createFile(SRC, srcDir, DocumentsContract.Document.MIME_TYPE_DIR,
- null);
- mStorage.createFile(SRC, srcFiles[0], "text/plain", testContent[0].getBytes());
- mStorage.createFile(SRC, srcFiles[1], "text/plain", testContent[1].getBytes());
- mStorage.createFile(SRC, srcFiles[2], "text/plain", testContent[2].getBytes());
-
- Intent moveIntent = createCopyIntent(Lists.newArrayList(testDir));
- moveIntent.putExtra(CopyService.EXTRA_TRANSFER_MODE, CopyService.TRANSFER_MODE_MOVE);
- startService(moveIntent);
-
- // dir creation, then creation and writing of 3 files, then removal of src dir and 3 src
- // files.
- mResolver.waitForChanges(11);
-
- // Check the content of the moved files.
- File dst = mStorage.getFile(DST, srcDir);
- assertTrue(dst.isDirectory());
- for (int i = 0; i < testContent.length; ++i) {
- byte[] dstContent = readFile(DST, srcFiles[i]);
- MoreAsserts.assertEquals("Copied file contents differ", testContent[i].getBytes(),
- dstContent);
- }
-
- // Check that the src files were removed.
- assertDoesNotExist(SRC, srcDir);
- for (String srcFile : srcFiles) {
- assertDoesNotExist(SRC, srcFile);
- }
- }
-
- public void testCopyFileWithReadErrors() throws Exception {
- String srcPath = "/test0.txt";
- Uri testFile = mStorage.createFile(SRC, srcPath, "text/plain",
- "The five boxing wizards jump quickly".getBytes());
-
- assertDstFileCountEquals(0);
-
- mStorage.simulateReadErrorsForFile(testFile);
-
- startService(createCopyIntent(Lists.newArrayList(testFile)));
-
- // 3 operations: file creation, writing, then deletion (due to failed copy).
- mResolver.waitForChanges(3);
-
- // Verify that the failed copy was cleaned up.
- assertDstFileCountEquals(0);
- }
-
- public void testMoveFileWithReadErrors() throws Exception {
- String srcPath = "/test0.txt";
- Uri testFile = mStorage.createFile(SRC, srcPath, "text/plain",
- "The five boxing wizards jump quickly".getBytes());
-
- assertDstFileCountEquals(0);
-
- mStorage.simulateReadErrorsForFile(testFile);
-
- Intent moveIntent = createCopyIntent(Lists.newArrayList(testFile));
- moveIntent.putExtra(CopyService.EXTRA_TRANSFER_MODE, CopyService.TRANSFER_MODE_MOVE);
- startService(moveIntent);
-
- try {
- // There should be 3 operations: file creation, writing, then deletion (due to failed
- // copy). Wait for 4, in case the CopyService also attempts to do extra stuff (like
- // delete the src file). This should time out.
- mResolver.waitForChanges(4);
- } catch (TimeoutException e) {
- // Success path
- return;
- } finally {
- // Verify that the failed copy was cleaned up, and the src file wasn't removed.
- assertDstFileCountEquals(0);
- assertExists(SRC, srcPath);
- }
- // The asserts above didn't fail, but the CopyService did something unexpected.
- fail("Extra file operations were detected");
- }
-
- public void testMoveDirectoryWithReadErrors() throws Exception {
- String testContent[] = {
- "The five boxing wizards jump quickly",
- "The quick brown fox jumps over the lazy dog",
- "Jackdaws love my big sphinx of quartz"
- };
- String srcDir = "/testdir";
- String srcFiles[] = {
- srcDir + "/test0.txt",
- srcDir + "/test1.txt",
- srcDir + "/test2.txt"
- };
- // Create test dir; put some files in it.
- Uri testDir = mStorage.createFile(SRC, srcDir, DocumentsContract.Document.MIME_TYPE_DIR,
- null);
- mStorage.createFile(SRC, srcFiles[0], "text/plain", testContent[0].getBytes());
- Uri errFile = mStorage
- .createFile(SRC, srcFiles[1], "text/plain", testContent[1].getBytes());
- mStorage.createFile(SRC, srcFiles[2], "text/plain", testContent[2].getBytes());
-
- mStorage.simulateReadErrorsForFile(errFile);
-
- Intent moveIntent = createCopyIntent(Lists.newArrayList(testDir));
- moveIntent.putExtra(CopyService.EXTRA_TRANSFER_MODE, CopyService.TRANSFER_MODE_MOVE);
- startService(moveIntent);
-
- // - dst dir creation,
- // - creation and writing of 2 files, removal of 2 src files
- // - creation and writing of 1 file, then removal of that file (due to error)
- mResolver.waitForChanges(10);
-
- // Check that both the src and dst dirs exist. The src dir shouldn't have been removed,
- // because it should contain the one errFile.
- assertTrue(mStorage.getFile(SRC, srcDir).isDirectory());
- assertTrue(mStorage.getFile(DST, srcDir).isDirectory());
-
- // Check the content of the moved files.
- MoreAsserts.assertEquals("Copied file contents differ", testContent[0].getBytes(),
- readFile(DST, srcFiles[0]));
- MoreAsserts.assertEquals("Copied file contents differ", testContent[2].getBytes(),
- readFile(DST, srcFiles[2]));
-
- // Check that the src files were removed.
- assertDoesNotExist(SRC, srcFiles[0]);
- assertDoesNotExist(SRC, srcFiles[2]);
-
- // Check that the error file was not copied over.
- assertDoesNotExist(DST, srcFiles[1]);
- assertExists(SRC, srcFiles[1]);
- }
-
- /**
- * Copies the given files to a pre-determined destination.
- *
- * @throws FileNotFoundException
- */
- private Intent createCopyIntent(List<Uri> srcs) throws FileNotFoundException {
- final ArrayList<DocumentInfo> srcDocs = Lists.newArrayList();
- for (Uri src : srcs) {
- srcDocs.add(DocumentInfo.fromUri(mResolver, src));
- }
-
- final Uri dst = DocumentsContract.buildDocumentUri(AUTHORITY, mRoots.get(1).documentId);
- DocumentStack stack = new DocumentStack();
- stack.push(DocumentInfo.fromUri(mResolver, dst));
- final Intent copyIntent = new Intent(mContext, CopyService.class);
- copyIntent.putParcelableArrayListExtra(CopyService.EXTRA_SRC_LIST, srcDocs);
- copyIntent.putExtra(Shared.EXTRA_STACK, (Parcelable) stack);
-
- // startService(copyIntent);
- return copyIntent;
- }
-
- /**
- * Returns a count of the files in the given directory.
- */
- private void assertDstFileCountEquals(int expected) throws RemoteException {
- final Uri queryUri = DocumentsContract.buildChildDocumentsUri(AUTHORITY,
- mRoots.get(1).documentId);
- Cursor c = null;
- int count = 0;
- try {
- c = mClient.query(queryUri, null, null, null, null);
- count = c.getCount();
- } finally {
- IoUtils.closeQuietly(c);
- }
- assertEquals("Incorrect file count after copy", expected, count);
- }
-
- private void assertExists(String rootId, String path) throws Exception {
- assertNotNull("An expected file was not found: " + path + " on root " + rootId,
- mStorage.getFile(rootId, path));
- }
-
- private void assertDoesNotExist(String rootId, String path) throws Exception {
- assertNull("Unexpected file found: " + path + " on root " + rootId,
- mStorage.getFile(rootId, path));
- }
-
- private byte[] readFile(String rootId, String path) throws Exception {
- File file = mStorage.getFile(rootId, path);
- byte[] buf = null;
- assertNotNull(file);
-
- FileInputStream in = null;
- try {
- in = new FileInputStream(file);
- buf = Streams.readFully(in);
- } finally {
- IoUtils.closeQuietly(in);
- }
- return buf;
- }
-
- private void assertCopied(String path) throws Exception {
- MoreAsserts.assertEquals("Copied file contents differ", readFile(SRC, path),
- readFile(DST, path));
- }
-
- /**
- * Sets up a ContextWrapper that substitutes a stub NotificationManager. This allows the test to
- * listen for notification events, to gauge copy progress.
- *
- * @throws FileNotFoundException
- */
- private void setupTestContext() throws FileNotFoundException {
- mSystemContext = getSystemContext();
-
- // Set up the context with the test content resolver.
- mResolver = new TestContentResolver();
- mContext = new ContextWrapper(mSystemContext) {
- @Override
- public ContentResolver getContentResolver() {
- return mResolver;
- }
- };
- setContext(mContext);
-
- // Create a local stub provider and add it to the content resolver.
- ProviderInfo info = new ProviderInfo();
- info.authority = AUTHORITY;
- info.exported = true;
- info.grantUriPermissions = true;
- info.readPermission = android.Manifest.permission.MANAGE_DOCUMENTS;
- info.writePermission = android.Manifest.permission.MANAGE_DOCUMENTS;
-
- mStorage = new StubProvider();
- mStorage.attachInfo(mContext, info);
- mResolver.addProvider(AUTHORITY, mStorage);
- }
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/DirectoryFragmentModelTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/DirectoryFragmentModelTest.java
deleted file mode 100644
index 98ffb77..0000000
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/DirectoryFragmentModelTest.java
+++ /dev/null
@@ -1,198 +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.documentsui;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.database.Cursor;
-import android.database.MatrixCursor;
-import android.provider.DocumentsContract.Document;
-import android.support.v7.widget.RecyclerView.ViewHolder;
-import android.support.v7.widget.RecyclerView;
-import android.test.AndroidTestCase;
-import android.test.MoreAsserts;
-import android.test.mock.MockContentResolver;
-import android.view.ViewGroup;
-
-import com.android.documentsui.DirectoryFragment.Model;
-import com.android.documentsui.MultiSelectManager.Selection;
-import com.android.documentsui.model.DocumentInfo;
-
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-
-
-public class DirectoryFragmentModelTest extends AndroidTestCase {
-
- private static final int ITEM_COUNT = 5;
- private static final String[] COLUMNS = new String[]{
- Document.COLUMN_DOCUMENT_ID
- };
- private static Cursor cursor;
-
- private Context mContext;
- private Model model;
-
- public void setUp() {
- setupTestContext();
-
- MatrixCursor c = new MatrixCursor(COLUMNS);
- for (int i = 0; i < ITEM_COUNT; ++i) {
- MatrixCursor.RowBuilder row = c.newRow();
- row.add(COLUMNS[0], i);
- }
- cursor = c;
-
- DirectoryResult r = new DirectoryResult();
- r.cursor = cursor;
-
- // Instantiate the model with a dummy view adapter and listener that (for now) do nothing.
- model = new Model(mContext, null, new DummyAdapter());
- model.addUpdateListener(new DummyListener());
- model.update(r);
- }
-
- // Tests that the item count is correct.
- public void testItemCount() {
- assertEquals(ITEM_COUNT, model.getItemCount());
- }
-
- // Tests that the item count is correct after a deletion.
- public void testItemCount_WithDeletion() {
- // Simulate deleting 2 files.
- delete(2, 4);
-
- assertEquals(ITEM_COUNT - 2, model.getItemCount());
- }
-
- // Tests that the item count is correct after a deletion is undone.
- public void testItemCount_WithUndoneDeletion() {
- // Simulate deleting 2 files.
- delete(0, 3);
-
- // Undo the deletion
- model.undoDeletion();
- assertEquals(ITEM_COUNT, model.getItemCount());
- }
-
- // Tests that the right things are marked for deletion.
- public void testMarkForDeletion() {
- delete(1, 3);
-
- List<DocumentInfo> docs = model.getDocumentsMarkedForDeletion();
- assertEquals(2, docs.size());
- assertEquals("1", docs.get(0).documentId);
- assertEquals("3", docs.get(1).documentId);
- }
-
- // Tests the base case for Model.getItem.
- public void testGetItem() {
- for (int i = 0; i < ITEM_COUNT; ++i) {
- Cursor c = model.getItem(i);
- assertEquals(i, c.getPosition());
- }
- }
-
- // Tests that Model.getItem returns the right items after a deletion.
- public void testGetItem_WithDeletion() {
- // Simulate deleting 2 files.
- delete(2, 3);
-
- List<DocumentInfo> docs = getDocumentInfo(0, 1, 2);
- assertEquals("0", docs.get(0).documentId);
- assertEquals("1", docs.get(1).documentId);
- assertEquals("4", docs.get(2).documentId);
-
- TestDeletionListener testListener = new TestDeletionListener();
- model.finalizeDeletion(testListener);
- testListener.waitForDone();
-
- docs = getDocumentInfo(0, 1, 2);
- assertEquals("0", docs.get(0).documentId);
- assertEquals("1", docs.get(1).documentId);
- assertEquals("2", docs.get(2).documentId);
- }
-
- // Tests that Model.getItem returns the right items after a deletion is undone.
- public void testGetItem_WithCancelledDeletion() {
- delete(0, 1);
- model.undoDeletion();
-
- // Test that all documents are accounted for, in the right position.
- for (int i = 0; i < ITEM_COUNT; ++i) {
- assertEquals(Integer.toString(i), getDocumentInfo(i).get(0).documentId);
- }
- }
-
- private void setupTestContext() {
- final MockContentResolver resolver = new MockContentResolver();
- mContext = new ContextWrapper(getContext()) {
- @Override
- public ContentResolver getContentResolver() {
- return resolver;
- }
- };
- }
-
- private void delete(int... items) {
- Selection sel = new Selection();
- for (int item: items) {
- sel.add(item);
- }
- model.markForDeletion(sel);
- }
-
- private List<DocumentInfo> getDocumentInfo(int... items) {
- Selection sel = new Selection();
- for (int item: items) {
- sel.add(item);
- }
- return model.getDocuments(sel);
- }
-
- private static class DummyListener extends Model.UpdateListener {
- public void onModelUpdate(Model model) {}
- public void onModelUpdateFailed(Exception e) {}
- }
-
- private static class DummyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
- public int getItemCount() { return 0; }
- public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {}
- public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- return null;
- }
- }
-
- private static class TestDeletionListener extends Model.DeletionListener {
- final CountDownLatch mSignal = new CountDownLatch(1);
-
- @Override
- public void onCompletion() {
- mSignal.countDown();
- }
-
- public void waitForDone() {
- try {
- boolean timeout = mSignal.await(10, TimeUnit.SECONDS);
- assertTrue("Timed out waiting for deletion completion", timeout);
- } catch (InterruptedException e) {}
- }
- }
-}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/DocumentsProviderHelper.java b/packages/DocumentsUI/tests/src/com/android/documentsui/DocumentsProviderHelper.java
new file mode 100644
index 0000000..7abc99c
--- /dev/null
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/DocumentsProviderHelper.java
@@ -0,0 +1,88 @@
+/*
+ * 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.documentsui;
+
+import static com.android.documentsui.model.DocumentInfo.getCursorString;
+
+import android.content.ContentProviderClient;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.provider.DocumentsContract;
+import android.provider.DocumentsContract.Document;
+import android.provider.DocumentsContract.Root;
+
+import com.android.documentsui.model.RootInfo;
+
+import libcore.io.IoUtils;
+
+/**
+ * Provides support for creation of documents in a test settings.
+ */
+public class DocumentsProviderHelper {
+
+ private final ContentProviderClient mClient;
+ private final String mAuthority;
+
+ public DocumentsProviderHelper(String authority, ContentProviderClient client) {
+ mClient = client;
+ mAuthority = authority;
+ }
+
+ public RootInfo getRoot(String id) throws RemoteException {
+ final Uri rootsUri = DocumentsContract.buildRootsUri(mAuthority);
+
+ Cursor cursor = null;
+ try {
+ cursor = mClient.query(rootsUri, null, null, null, null);
+ while (cursor.moveToNext()) {
+ if (id.equals(getCursorString(cursor, Root.COLUMN_ROOT_ID))) {
+ return RootInfo.fromRootsCursor(mAuthority, cursor);
+ }
+ }
+ throw new IllegalArgumentException("Can't find matching root for id=" + id);
+ } catch (Exception e) {
+ throw new RuntimeException("Can't load root for id=" + id , e);
+ } finally {
+ IoUtils.closeQuietly(cursor);
+ }
+ }
+
+ public Uri createDocument(Uri parentUri, String mimeType, String name) {
+ if (name.contains("/")) {
+ throw new IllegalArgumentException("Name and mimetype probably interposed.");
+ }
+ try {
+ return DocumentsContract.createDocument(mClient, parentUri, mimeType, name);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Couldn't create document: " + name + " with mimetype " + mimeType, e);
+ }
+ }
+
+ public Uri createFolder(Uri parentUri, String name) {
+ return createDocument(parentUri, Document.MIME_TYPE_DIR, name);
+ }
+
+ public Uri createDocument(RootInfo root, String mimeType, String name) {
+ Uri rootUri = DocumentsContract.buildDocumentUri(mAuthority, root.documentId);
+ return createDocument(rootUri, mimeType, name);
+ }
+
+ public Uri createFolder(RootInfo root, String name) {
+ return createDocument(root, Document.MIME_TYPE_DIR, name);
+ }
+}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
new file mode 100644
index 0000000..9060516
--- /dev/null
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
@@ -0,0 +1,173 @@
+/*
+ * 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.documentsui;
+
+import static com.android.documentsui.StubProvider.DEFAULT_AUTHORITY;
+import static com.android.documentsui.StubProvider.ROOT_0_ID;
+import static com.android.documentsui.StubProvider.ROOT_1_ID;
+
+import android.app.Instrumentation;
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.Configurator;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.Until;
+import android.test.InstrumentationTestCase;
+import android.util.Log;
+import android.view.MotionEvent;
+
+import com.android.documentsui.model.RootInfo;
+
+public class FilesActivityUiTest extends InstrumentationTestCase {
+
+ private static final int TIMEOUT = 5000;
+ private static final String TAG = "FilesActivityUiTest";
+ private static final String TARGET_PKG = "com.android.documentsui";
+ private static final String LAUNCHER_PKG = "com.android.launcher";
+
+ private UiBot mBot;
+ private UiDevice mDevice;
+ private Context mContext;
+ private ContentResolver mResolver;
+ private DocumentsProviderHelper mDocsHelper;
+ private ContentProviderClient mClient;
+ private RootInfo mRoot_0;
+ private RootInfo mRoot_1;
+
+ public void setUp() throws Exception {
+ // Initialize UiDevice instance.
+ Instrumentation instrumentation = getInstrumentation();
+
+ mDevice = UiDevice.getInstance(instrumentation);
+
+ Configurator.getInstance().setToolType(MotionEvent.TOOL_TYPE_MOUSE);
+
+ // Start from the home screen.
+ mDevice.pressHome();
+ mDevice.wait(Until.hasObject(By.pkg(LAUNCHER_PKG).depth(0)), TIMEOUT);
+
+ // NOTE: Must be the "target" context, else security checks in content provider will fail.
+ mContext = instrumentation.getTargetContext();
+ mResolver = mContext.getContentResolver();
+
+ mClient = mResolver.acquireUnstableContentProviderClient(DEFAULT_AUTHORITY);
+ mDocsHelper = new DocumentsProviderHelper(DEFAULT_AUTHORITY, mClient);
+
+ // Launch app.
+ Intent intent = mContext.getPackageManager().getLaunchIntentForPackage(TARGET_PKG);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ mContext.startActivity(intent);
+
+ // Wait for the app to appear.
+ mDevice.wait(Until.hasObject(By.pkg(TARGET_PKG).depth(0)), TIMEOUT);
+ mDevice.waitForIdle();
+
+ mBot = new UiBot(mDevice, TIMEOUT);
+
+ resetStorage(); // Just incase a test failed and tearDown didn't happen.
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ Log.d(TAG, "Resetting storage from setUp");
+ resetStorage();
+ mClient.release();
+ }
+
+ private void resetStorage() throws RemoteException {
+ mClient.call("clear", null, null);
+ // TODO: Would be nice to have an event to wait on here.
+ mDevice.waitForIdle();
+ }
+
+ private void initTestFiles() throws RemoteException {
+ mRoot_0 = mDocsHelper.getRoot(ROOT_0_ID);
+ mRoot_1 = mDocsHelper.getRoot(ROOT_1_ID);
+
+ mDocsHelper.createDocument(mRoot_0, "text/plain", "file0.log");
+ mDocsHelper.createDocument(mRoot_0, "image/png", "file1.png");
+ mDocsHelper.createDocument(mRoot_0, "text/csv", "file2.csv");
+
+ mDocsHelper.createDocument(mRoot_1, "text/plain", "anotherFile0.log");
+ mDocsHelper.createDocument(mRoot_1, "text/plain", "poodles.text");
+ }
+
+ public void testRootsListed() throws Exception {
+ initTestFiles();
+
+ mBot.openRoot(ROOT_0_ID);
+
+ // Should also have Drive, but that requires pre-configuration of devices
+ // We omit for now.
+ mBot.assertHasRoots(
+ "Images",
+ "Videos",
+ "Audio",
+ "Downloads",
+ ROOT_0_ID,
+ ROOT_1_ID);
+ }
+
+ public void testFilesListed() throws Exception {
+ initTestFiles();
+
+ mBot.openRoot(ROOT_0_ID);
+ mBot.assertHasDocuments("file0.log", "file1.png", "file2.csv");
+ }
+
+ public void testFilesList_LiveUpdate() throws Exception {
+ initTestFiles();
+
+ mBot.openRoot(ROOT_0_ID);
+ mDocsHelper.createDocument(mRoot_0, "yummers/sandwich", "Ham & Cheese.sandwich");
+ mBot.assertHasDocuments("file0.log", "file1.png", "file2.csv", "Ham & Cheese.sandwich");
+ }
+
+ public void testDeleteDocument() throws Exception {
+ initTestFiles();
+
+ mBot.openRoot(ROOT_0_ID);
+
+ mBot.clickDocument("file1.png");
+ mDevice.waitForIdle();
+ mBot.menuDelete().click();
+
+ mBot.waitForDeleteSnackbar();
+ assertFalse(mBot.hasDocuments("file1.png"));
+
+ mBot.waitForDeleteSnackbarGone();
+ assertFalse(mBot.hasDocuments("file1.png"));
+
+ // Now delete from another root.
+ mBot.openRoot(ROOT_1_ID);
+
+ mBot.clickDocument("poodles.text");
+ mDevice.waitForIdle();
+ mBot.menuDelete().click();
+
+ mBot.waitForDeleteSnackbar();
+ assertFalse(mBot.hasDocuments("poodles.text"));
+
+ mBot.waitForDeleteSnackbarGone();
+ assertFalse(mBot.hasDocuments("poodles.text"));
+ }
+}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManagerTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManagerTest.java
deleted file mode 100644
index 2447469..0000000
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManagerTest.java
+++ /dev/null
@@ -1,339 +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.documentsui;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import android.support.v7.widget.RecyclerView;
-import android.test.AndroidTestCase;
-import android.util.SparseBooleanArray;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.documentsui.MultiSelectManager.Selection;
-
-import org.mockito.Mockito;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-public class MultiSelectManagerTest extends AndroidTestCase {
-
- private static final List<String> items;
- static {
- items = new ArrayList<String>();
- items.add("aaa");
- items.add("bbb");
- items.add("ccc");
- items.add("111");
- items.add("222");
- items.add("333");
- }
-
- private MultiSelectManager mManager;
- private TestAdapter mAdapter;
- private TestCallback mCallback;
- private EventHelper mEventHelper;
-
- public void setUp() throws Exception {
- mAdapter = new TestAdapter(items);
- mCallback = new TestCallback();
- mEventHelper = new EventHelper();
- mManager = new MultiSelectManager(mAdapter, mEventHelper, MultiSelectManager.MODE_MULTIPLE);
- mManager.addCallback(mCallback);
- }
-
- public void testMouseClick_StartsSelectionMode() {
- click(7);
- assertSelection(7);
- }
-
- public void testMouseClick_NotifiesSelectionChanged() {
- click(7);
- mCallback.assertSelectionChanged();
- }
-
- public void testMouseClick_ShiftClickExtendsSelection() {
- longPress(7);
- shiftClick(11);
- assertRangeSelection(7, 11);
- }
-
- public void testMouseClick_NoPosition_ClearsSelection() {
- longPress(7);
- click(11);
- click(RecyclerView.NO_POSITION);
- assertSelection();
- }
-
- public void testSetSelectionFocusBegin() {
- mManager.setItemSelected(7, true);
- mManager.setSelectionFocusBegin(7);
- shiftClick(11);
- assertRangeSelection(7, 11);
- }
-
- public void testLongPress_StartsSelectionMode() {
- longPress(7);
- assertSelection(7);
- }
-
- public void testLongPress_SecondPressExtendsSelection() {
- longPress(7);
- longPress(99);
- assertSelection(7, 99);
- }
-
- public void testSingleTapUp_UnselectsSelectedItem() {
- longPress(7);
- tap(7);
- assertSelection();
- }
-
- public void testSingleTapUp_NoPosition_ClearsSelection() {
- longPress(7);
- tap(11);
- tap(RecyclerView.NO_POSITION);
- assertSelection();
- }
-
- public void testSingleTapUp_ExtendsSelection() {
- longPress(99);
- tap(7);
- tap(13);
- tap(129899);
- assertSelection(7, 99, 13, 129899);
- }
-
- public void testSingleTapUp_ShiftCreatesRangeSelection() {
- longPress(7);
- shiftTap(17);
- assertRangeSelection(7, 17);
- }
-
- public void testSingleTapUp_ShiftCreatesRangeSeletion_Backwards() {
- longPress(17);
- shiftTap(7);
- assertRangeSelection(7, 17);
- }
-
- public void testSingleTapUp_SecondShiftClickExtendsSelection() {
- longPress(7);
- shiftTap(11);
- shiftTap(17);
- assertRangeSelection(7, 17);
- }
-
- public void testSingleTapUp_MultipleContiguousRangesSelected() {
- longPress(7);
- shiftTap(11);
- tap(20);
- shiftTap(25);
- assertRangeSelected(7, 11);
- assertRangeSelected(20, 25);
- assertSelectionSize(11);
- }
-
- public void testSingleTapUp_ShiftReducesSelectionRange_FromPreviousShiftClick() {
- longPress(7);
- shiftTap(17);
- shiftTap(10);
- assertRangeSelection(7, 10);
- }
-
- public void testSingleTapUp_ShiftReducesSelectionRange_FromPreviousShiftClick_Backwards() {
- mManager.onLongPress(TestInputEvent.tap(17));
- shiftTap(7);
- shiftTap(14);
- assertRangeSelection(14, 17);
- }
-
-
- public void testSingleTapUp_ShiftReversesSelectionDirection() {
- longPress(7);
- shiftTap(17);
- shiftTap(0);
- assertRangeSelection(0, 7);
- }
-
- public void testSingleSelectMode() {
- mManager = new MultiSelectManager(mAdapter, mEventHelper, MultiSelectManager.MODE_SINGLE);
- mManager.addCallback(mCallback);
- longPress(20);
- tap(13);
- assertSelection(13);
- }
-
- public void testSingleSelectMode_ShiftTap() {
- mManager = new MultiSelectManager(mAdapter, mEventHelper, MultiSelectManager.MODE_SINGLE);
- mManager.addCallback(mCallback);
- longPress(13);
- shiftTap(20);
- assertSelection(20);
- }
-
- public void testProvisionalSelection() {
- Selection s = mManager.getSelection();
- assertSelection();
-
- SparseBooleanArray provisional = new SparseBooleanArray();
- provisional.append(1, true);
- provisional.append(2, true);
- s.setProvisionalSelection(provisional);
- assertSelection(1, 2);
-
- provisional.delete(1);
- provisional.append(3, true);
- s.setProvisionalSelection(provisional);
- assertSelection(2, 3);
-
- s.applyProvisionalSelection();
- assertSelection(2, 3);
-
- provisional.clear();
- provisional.append(3, true);
- provisional.append(4, true);
- s.setProvisionalSelection(provisional);
- assertSelection(2, 3, 4);
-
- provisional.delete(3);
- s.setProvisionalSelection(provisional);
- assertSelection(2, 3, 4);
- }
-
- private void longPress(int position) {
- mManager.onLongPress(TestInputEvent.tap(position));
- }
-
- private void tap(int position) {
- mManager.onSingleTapUp(TestInputEvent.tap(position));
- }
-
- private void shiftTap(int position) {
- mManager.onSingleTapUp(TestInputEvent.shiftTap(position));
- }
-
- private void click(int position) {
- mManager.onSingleTapUp(TestInputEvent.click(position));
- }
-
- private void shiftClick(int position) {
- mManager.onSingleTapUp(TestInputEvent.shiftClick(position));
- }
-
- private void assertSelected(int... expected) {
- for (int i = 0; i < expected.length; i++) {
- Selection selection = mManager.getSelection();
- String err = String.format(
- "Selection %s does not contain %d", selection, expected[i]);
- assertTrue(err, selection.contains(expected[i]));
- }
- }
-
- private void assertSelection(int... expected) {
- assertSelectionSize(expected.length);
- assertSelected(expected);
- }
-
- private void assertRangeSelected(int begin, int end) {
- for (int i = begin; i <= end; i++) {
- assertSelected(i);
- }
- }
-
- private void assertRangeSelection(int begin, int end) {
- assertSelectionSize(end - begin + 1);
- assertRangeSelected(begin, end);
- }
-
- private void assertSelectionSize(int expected) {
- Selection selection = mManager.getSelection();
- assertEquals(selection.toString(), expected, selection.size());
- }
-
- private static final class EventHelper implements MultiSelectManager.ItemFinder {
-
- @Override
- public int findItemPosition(MotionEvent e) {
- throw new UnsupportedOperationException();
- }
- }
-
- private static final class TestCallback implements MultiSelectManager.Callback {
-
- Set<Integer> ignored = new HashSet<>();
- private int mLastChangedPosition;
- private boolean mLastChangedSelected;
- private boolean mSelectionChanged = false;
-
- @Override
- public void onItemStateChanged(int position, boolean selected) {
- this.mLastChangedPosition = position;
- this.mLastChangedSelected = selected;
- }
-
- @Override
- public boolean onBeforeItemStateChange(int position, boolean selected) {
- return !ignored.contains(position);
- }
-
- @Override
- public void onSelectionChanged() {
- mSelectionChanged = true;
- }
-
- void assertSelectionChanged() {
- assertTrue(mSelectionChanged);
- }
- }
-
- private static final class TestHolder extends RecyclerView.ViewHolder {
- // each data item is just a string in this case
- public View view;
- public String string;
- public TestHolder(View view) {
- super(view);
- this.view = view;
- }
- }
-
- private static final class TestAdapter extends RecyclerView.Adapter<TestHolder> {
-
- private List<String> mItems;
-
- public TestAdapter(List<String> items) {
- mItems = items;
- }
-
- @Override
- public TestHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- return new TestHolder(Mockito.mock(ViewGroup.class));
- }
-
- @Override
- public void onBindViewHolder(TestHolder holder, int position) {}
-
- @Override
- public int getItemCount() {
- return mItems.size();
- }
- }
-}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManager_GridModelTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManager_GridModelTest.java
deleted file mode 100644
index aa50b48..0000000
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManager_GridModelTest.java
+++ /dev/null
@@ -1,311 +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.documentsui;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.support.v7.widget.RecyclerView.OnScrollListener;
-import android.test.AndroidTestCase;
-import android.util.SparseBooleanArray;
-
-import com.android.documentsui.MultiSelectManager.GridModel;
-
-public class MultiSelectManager_GridModelTest extends AndroidTestCase {
-
- private static final int VIEW_PADDING_PX = 5;
- private static final int CHILD_VIEW_EDGE_PX = 100;
- private static final int VIEWPORT_HEIGHT = 500;
-
- private static GridModel model;
- private static TestHelper helper;
- private static SparseBooleanArray lastSelection;
- private static int viewWidth;
-
- private static void setUp(int numChildren, int numColumns) {
- helper = new TestHelper(numChildren, numColumns);
- viewWidth = VIEW_PADDING_PX + numColumns * (VIEW_PADDING_PX + CHILD_VIEW_EDGE_PX);
- model = new GridModel(helper);
- model.addOnSelectionChangedListener(
- new GridModel.OnSelectionChangedListener() {
- @Override
- public void onSelectionChanged(SparseBooleanArray updatedSelection) {
- lastSelection = updatedSelection;
- }
- });
- }
-
- @Override
- public void tearDown() {
- model = null;
- helper = null;
- lastSelection = null;
- }
-
- public void testSelectionLeftOfItems() {
- setUp(20, 5);
- model.startSelection(new Point(0, 10));
- model.resizeSelection(new Point(1, 11));
- assertSelected(new int[0]);
- assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin());
- }
-
- public void testSelectionRightOfItems() {
- setUp(20, 4);
- model.startSelection(new Point(viewWidth - 1, 10));
- model.resizeSelection(new Point(viewWidth - 2, 11));
- assertSelected(new int[0]);
- assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin());
- }
-
- public void testSelectionAboveItems() {
- setUp(20, 4);
- model.startSelection(new Point(10, 0));
- model.resizeSelection(new Point(11, 1));
- assertSelected(new int[0]);
- assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin());
- }
-
- public void testSelectionBelowItems() {
- setUp(5, 4);
- model.startSelection(new Point(10, VIEWPORT_HEIGHT - 1));
- model.resizeSelection(new Point(11, VIEWPORT_HEIGHT - 2));
- assertSelected(new int[0]);
- assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin());
- }
-
- public void testVerticalSelectionBetweenItems() {
- setUp(20, 4);
- model.startSelection(new Point(106, 0));
- model.resizeSelection(new Point(107, 200));
- assertSelected(new int[0]);
- assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin());
- }
-
- public void testHorizontalSelectionBetweenItems() {
- setUp(20, 4);
- model.startSelection(new Point(0, 105));
- model.resizeSelection(new Point(200, 106));
- assertSelected(new int[0]);
- assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin());
- }
-
- public void testGrowingAndShrinkingSelection() {
- setUp(20, 4);
- model.startSelection(new Point(0, 0));
- model.resizeSelection(new Point(5, 5));
- assertSelected(new int[] {0});
- model.resizeSelection(new Point(109, 109));
- assertSelected(new int[] {0});
- model.resizeSelection(new Point(110, 109));
- assertSelected(new int[] {0, 1});
- model.resizeSelection(new Point(110, 110));
- assertSelected(new int[] {0, 1, 4, 5});
- model.resizeSelection(new Point(214, 214));
- assertSelected(new int[] {0, 1, 4, 5});
- model.resizeSelection(new Point(215, 214));
- assertSelected(new int[] {0, 1, 2, 4, 5, 6});
- model.resizeSelection(new Point(214, 214));
- assertSelected(new int[] {0, 1, 4, 5});
- model.resizeSelection(new Point(110, 110));
- assertSelected(new int[] {0, 1, 4, 5});
- model.resizeSelection(new Point(110, 109));
- assertSelected(new int[] {0, 1});
- model.resizeSelection(new Point(109, 109));
- assertSelected(new int[] {0});
- model.resizeSelection(new Point(5, 5));
- assertSelected(new int[] {0});
- model.resizeSelection(new Point(0, 0));
- assertSelected(new int[0]);
- assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin());
- }
-
- public void testSelectionMovingAroundOrigin() {
- setUp(16, 4);
- model.startSelection(new Point(210, 210));
- model.resizeSelection(new Point(viewWidth - 1, 0));
- assertSelected(new int[] {2, 3, 6, 7});
- model.resizeSelection(new Point(0, 0));
- assertSelected(new int[] {0, 1, 4, 5});
- model.resizeSelection(new Point(0, 420));
- assertSelected(new int[] {8, 9, 12, 13});
- model.resizeSelection(new Point(viewWidth - 1, 420));
- assertSelected(new int[] {10, 11, 14, 15});
- assertEquals(10, model.getPositionNearestOrigin());
- }
-
- public void testScrollingBandSelect() {
- setUp(40, 4);
- model.startSelection(new Point(0, 0));
- model.resizeSelection(new Point(100, VIEWPORT_HEIGHT - 1));
- assertSelected(new int[] {0, 4, 8, 12, 16});
- scroll(CHILD_VIEW_EDGE_PX);
- assertSelected(new int[] {0, 4, 8, 12, 16, 20});
- model.resizeSelection(new Point(200, VIEWPORT_HEIGHT - 1));
- assertSelected(new int[] {0, 1, 4, 5, 8, 9, 12, 13, 16, 17, 20, 21});
- scroll(CHILD_VIEW_EDGE_PX);
- assertSelected(new int[] {0, 1, 4, 5, 8, 9, 12, 13, 16, 17, 20, 21, 24, 25});
- scroll(-2 * CHILD_VIEW_EDGE_PX);
- assertSelected(new int[] {0, 1, 4, 5, 8, 9, 12, 13, 16, 17});
- model.resizeSelection(new Point(100, VIEWPORT_HEIGHT - 1));
- assertSelected(new int[] {0, 4, 8, 12, 16});
- assertEquals(0, model.getPositionNearestOrigin());
- }
-
- private static void assertSelected(int[] selectedPositions) {
- assertEquals(selectedPositions.length, lastSelection.size());
- for (int position : selectedPositions) {
- assertTrue(lastSelection.get(position));
- }
- }
-
- private static void scroll(int dy) {
- assertTrue(helper.verticalOffset + VIEWPORT_HEIGHT + dy <= helper.getTotalHeight());
- helper.verticalOffset += dy;
- model.onScrolled(null, 0, dy);
- }
-
- private static final class TestHelper implements MultiSelectManager.BandEnvironment {
-
- public int horizontalOffset = 0;
- public int verticalOffset = 0;
- private final int mNumColumns;
- private final int mNumRows;
- private final int mNumChildren;
-
- public TestHelper(int numChildren, int numColumns) {
- mNumChildren = numChildren;
- mNumColumns = numColumns;
- mNumRows = (int) Math.ceil((double) numChildren / mNumColumns);
- }
-
- private int getTotalHeight() {
- return CHILD_VIEW_EDGE_PX * mNumRows + VIEW_PADDING_PX * (mNumRows + 1);
- }
-
- private int getFirstVisibleRowIndex() {
- return verticalOffset / (CHILD_VIEW_EDGE_PX + VIEW_PADDING_PX);
- }
-
- private int getLastVisibleRowIndex() {
- int lastVisibleRowUncapped =
- (VIEWPORT_HEIGHT + verticalOffset - 1) / (CHILD_VIEW_EDGE_PX + VIEW_PADDING_PX);
- return Math.min(lastVisibleRowUncapped, mNumRows - 1);
- }
-
- private int getNumItemsInRow(int index) {
- assertTrue(index >= 0 && index < mNumRows);
- if (index == mNumRows - 1 && mNumChildren % mNumColumns != 0) {
- return mNumChildren % mNumColumns;
- }
-
- return mNumColumns;
- }
-
- @Override
- public void addOnScrollListener(OnScrollListener listener) {}
-
- @Override
- public void removeOnScrollListener(OnScrollListener listener) {}
-
- @Override
- public Point createAbsolutePoint(Point relativePoint) {
- return new Point(
- relativePoint.x + horizontalOffset, relativePoint.y + verticalOffset);
- }
-
- @Override
- public int getVisibleChildCount() {
- int childCount = 0;
- for (int i = getFirstVisibleRowIndex(); i <= getLastVisibleRowIndex(); i++) {
- childCount += getNumItemsInRow(i);
- }
- return childCount;
- }
-
- @Override
- public int getAdapterPositionAt(int index) {
- return index + mNumColumns * (getFirstVisibleRowIndex());
- }
-
- @Override
- public Rect getAbsoluteRectForChildViewAt(int index) {
- int adapterPosition = getAdapterPositionAt(index);
- int rowIndex = adapterPosition / mNumColumns;
- int columnIndex = adapterPosition % mNumColumns;
-
- Rect rect = new Rect();
- rect.top = VIEW_PADDING_PX + rowIndex * (CHILD_VIEW_EDGE_PX + VIEW_PADDING_PX);
- rect.bottom = rect.top + CHILD_VIEW_EDGE_PX - 1;
- rect.left = VIEW_PADDING_PX + columnIndex * (CHILD_VIEW_EDGE_PX + VIEW_PADDING_PX);
- rect.right = rect.left + CHILD_VIEW_EDGE_PX - 1;
- return rect;
- }
-
- @Override
- public int getChildCount() {
- return mNumChildren;
- }
-
- @Override
- public int getColumnCount() {
- return mNumColumns;
- }
-
- @Override
- public int getRowCount() {
- return mNumRows;
- }
-
- @Override
- public void showBand(Rect rect) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void hideBand() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void scrollBy(int dy) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int getHeight() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void invalidateView() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void runAtNextFrame(Runnable r) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void removeCallback(Runnable r) {
- throw new UnsupportedOperationException();
- }
- }
-}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManager_SelectionTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManager_SelectionTest.java
deleted file mode 100644
index eddf4ef..0000000
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManager_SelectionTest.java
+++ /dev/null
@@ -1,155 +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.documentsui;
-
-import static org.junit.Assert.*;
-
-import android.test.AndroidTestCase;
-
-import com.android.documentsui.MultiSelectManager.Selection;
-
-
-public class MultiSelectManager_SelectionTest extends AndroidTestCase{
-
- private Selection selection;
-
- @Override
- public void setUp() throws Exception {
- selection = new Selection();
- selection.add(3);
- selection.add(5);
- selection.add(9);
- }
-
- public void testAdd() {
- // We added in setUp.
- assertEquals(3, selection.size());
- assertContains(3);
- assertContains(5);
- assertContains(9);
- }
-
- public void testRemove() {
- selection.remove(3);
- selection.remove(5);
- assertEquals(1, selection.size());
- assertContains(9);
- }
-
- public void testClear() {
- selection.clear();
- assertEquals(0, selection.size());
- }
-
- public void testIsEmpty() {
- assertTrue(new Selection().isEmpty());
- selection.clear();
- assertTrue(selection.isEmpty());
- }
-
- public void testSizeAndGet() {
- Selection other = new Selection();
- for (int i = 0; i < selection.size(); i++) {
- other.add(selection.get(i));
- }
- assertEquals(selection.size(), other.size());
- }
-
- public void testEqualsSelf() {
- assertEquals(selection, selection);
- }
-
- public void testEqualsOther() {
- Selection other = new Selection();
- other.add(3);
- other.add(5);
- other.add(9);
- assertEquals(selection, other);
- assertEquals(selection.hashCode(), other.hashCode());
- }
-
- public void testEqualsCopy() {
- Selection other = new Selection();
- other.copyFrom(selection);
- assertEquals(selection, other);
- assertEquals(selection.hashCode(), other.hashCode());
- }
-
- public void testNotEquals() {
- Selection other = new Selection();
- other.add(789);
- assertFalse(selection.equals(other));
- }
-
- public void testExpandBefore() {
- selection.expand(2, 10);
- assertEquals(3, selection.size());
- assertContains(13);
- assertContains(15);
- assertContains(19);
- }
-
- public void testExpandAfter() {
- selection.expand(10, 10);
- assertEquals(3, selection.size());
- assertContains(3);
- assertContains(5);
- assertContains(9);
- }
-
- public void testExpandSplit() {
- selection.expand(5, 10);
- assertEquals(3, selection.size());
- assertContains(3);
- assertContains(15);
- assertContains(19);
- }
-
- public void testExpandEncompased() {
- selection.expand(2, 10);
- assertEquals(3, selection.size());
- assertContains(13);
- assertContains(15);
- assertContains(19);
- }
-
- public void testCollapseBefore() {
- selection.collapse(0, 2);
- assertEquals(3, selection.size());
- assertContains(1);
- assertContains(3);
- assertContains(7);
- }
-
- public void testCollapseAfter() {
- selection.collapse(10, 10);
- assertEquals(3, selection.size());
- assertContains(3);
- assertContains(5);
- assertContains(9);
- }
-
- public void testCollapseAcross() {
- selection.collapse(0, 10);
- assertEquals(0, selection.size());
- }
-
- private void assertContains(int i) {
- String err = String.format("Selection %s does not contain %d", selection, i);
- assertTrue(err, selection.contains(i));
- }
-}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
index 6a2e03a..2d42ddc 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
@@ -50,12 +50,17 @@
import java.util.Map;
public class StubProvider extends DocumentsProvider {
+
+ public static final String DEFAULT_AUTHORITY = "com.android.documentsui.stubprovider";
+ public static final String ROOT_0_ID = "TEST_ROOT_0";
+ public static final String ROOT_1_ID = "TEST_ROOT_1";
+
+ private static final String TAG = "StubProvider";
private static final String EXTRA_SIZE = "com.android.documentsui.stubprovider.SIZE";
private static final String EXTRA_ROOT = "com.android.documentsui.stubprovider.ROOT";
private static final String STORAGE_SIZE_KEY = "documentsui.stubprovider.size";
- private static int DEFAULT_SIZE = 1024 * 1024; // 1 MB.
- private static final String TAG = "StubProvider";
- private static final String MY_ROOT_ID = "sd0";
+ private static int DEFAULT_ROOT_SIZE = 1024 * 1024 * 100; // 100 MB.
+
private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_TITLE, Root.COLUMN_DOCUMENT_ID,
Root.COLUMN_AVAILABLE_BYTES
@@ -65,11 +70,12 @@
Document.COLUMN_LAST_MODIFIED, Document.COLUMN_FLAGS, Document.COLUMN_SIZE,
};
- private HashMap<String, StubDocument> mStorage = new HashMap<String, StubDocument>();
- private Object mWriteLock = new Object();
- private String mAuthority;
+ private final Map<String, StubDocument> mStorage = new HashMap<>();
+ private final Map<String, RootInfo> mRoots = new HashMap<>();
+ private final Object mWriteLock = new Object();
+
+ private String mAuthority = DEFAULT_AUTHORITY;
private SharedPreferences mPrefs;
- private Map<String, RootInfo> mRoots;
private String mSimulateReadErrors;
@Override
@@ -86,20 +92,18 @@
@VisibleForTesting
public void clearCacheAndBuildRoots() {
- final File cacheDir = getContext().getCacheDir();
- removeRecursively(cacheDir);
+ Log.d(TAG, "Resetting storage.");
+ removeChildrenRecursively(getContext().getCacheDir());
mStorage.clear();
mPrefs = getContext().getSharedPreferences(
"com.android.documentsui.stubprovider.preferences", Context.MODE_PRIVATE);
Collection<String> rootIds = mPrefs.getStringSet("roots", null);
if (rootIds == null) {
- rootIds = Arrays.asList(new String[] {
- "sd0", "sd1"
- });
+ rootIds = Arrays.asList(new String[] { ROOT_0_ID, ROOT_1_ID });
}
- // Create new roots.
- mRoots = new HashMap<>();
+
+ mRoots.clear();
for (String rootId : rootIds) {
final RootInfo rootInfo = new RootInfo(rootId, getSize(rootId));
mRoots.put(rootId, rootInfo);
@@ -111,7 +115,7 @@
*/
private long getSize(String rootId) {
final String key = STORAGE_SIZE_KEY + "." + rootId;
- return mPrefs.getLong(key, DEFAULT_SIZE);
+ return mPrefs.getLong(key, DEFAULT_ROOT_SIZE);
}
@Override
@@ -125,7 +129,7 @@
row.add(Root.COLUMN_ROOT_ID, id);
row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_CREATE | Root.FLAG_SUPPORTS_IS_CHILD);
row.add(Root.COLUMN_TITLE, id);
- row.add(Root.COLUMN_DOCUMENT_ID, info.rootDocument.documentId);
+ row.add(Root.COLUMN_DOCUMENT_ID, info.document.documentId);
row.add(Root.COLUMN_AVAILABLE_BYTES, info.getRemainingCapacity());
}
return result;
@@ -152,28 +156,48 @@
}
@Override
- public String createDocument(String parentDocumentId, String mimeType, String displayName)
+ public String createDocument(String parentId, String mimeType, String displayName)
throws FileNotFoundException {
- final StubDocument parentDocument = mStorage.get(parentDocumentId);
- if (parentDocument == null || !parentDocument.file.isDirectory()) {
- throw new FileNotFoundException();
+
+ final StubDocument parent = mStorage.get(parentId);
+ if (parent == null) {
+ throw new IllegalArgumentException(
+ "Can't create file " + displayName + " in null parent.");
}
- final File file = new File(parentDocument.file, displayName);
- if (mimeType.equals(Document.MIME_TYPE_DIR)) {
- if (!file.mkdirs()) {
- throw new FileNotFoundException();
- }
- } else {
- try {
- if (!file.createNewFile()) {
- throw new IllegalStateException("The file " + file.getPath() + " already exists");
- }
- } catch (IOException e) {
- throw new FileNotFoundException();
- }
+ if (!parent.file.isDirectory()) {
+ throw new IllegalArgumentException(
+ "Can't create file " + displayName + " inside non-directory parent "
+ + parent.file.getName());
}
- final StubDocument document = new StubDocument(file, mimeType, parentDocument);
+ final File file = new File(parent.file, displayName);
+ if (file.exists()) {
+ throw new FileNotFoundException(
+ "Duplicate file names not supported for " + file);
+ }
+
+ if (mimeType.equals(Document.MIME_TYPE_DIR)) {
+ if (!file.mkdirs()) {
+ throw new FileNotFoundException(
+ "Failed to create directory(s): " + file);
+ }
+ Log.i(TAG, "Created new directory: " + file);
+ } else {
+ boolean created = false;
+ try {
+ created = file.createNewFile();
+ } catch (IOException e) {
+ // We'll throw an FNF exception later :)
+ Log.e(TAG, "createnewFile operation failed for file: " + file, e);
+ }
+ if (!created) {
+ throw new FileNotFoundException(
+ "createNewFile operation failed for: " + file);
+ }
+ Log.i(TAG, "Created new file: " + file);
+ }
+
+ final StubDocument document = new StubDocument(file, mimeType, parent);
Log.d(TAG, "Created document " + document.documentId);
notifyParentChanged(document.parentId);
getContext().getContentResolver().notifyChange(
@@ -349,7 +373,7 @@
private void configure(String arg, Bundle extras) {
Log.d(TAG, "Configure " + arg);
- String rootName = extras.getString(EXTRA_ROOT, MY_ROOT_ID);
+ String rootName = extras.getString(EXTRA_ROOT, ROOT_0_ID);
long rootSize = extras.getLong(EXTRA_SIZE, 1) * 1024 * 1024;
setSize(rootName, rootSize);
}
@@ -379,10 +403,10 @@
row.add(Document.COLUMN_LAST_MODIFIED, document.file.lastModified());
}
- private void removeRecursively(File file) {
+ private void removeChildrenRecursively(File file) {
for (File childFile : file.listFiles()) {
if (childFile.isDirectory()) {
- removeRecursively(childFile);
+ removeChildrenRecursively(childFile);
}
childFile.delete();
}
@@ -411,8 +435,8 @@
@VisibleForTesting
public Uri createFile(String rootId, String path, String mimeType, byte[] content)
throws FileNotFoundException, IOException {
- Log.d(TAG, "Creating file " + rootId + ":" + path);
- StubDocument root = mRoots.get(rootId).rootDocument;
+ Log.d(TAG, "Creating test file " + rootId + ":" + path);
+ StubDocument root = mRoots.get(rootId).document;
if (root == null) {
throw new FileNotFoundException("No roots with the ID " + rootId + " were found");
}
@@ -445,7 +469,7 @@
@VisibleForTesting
public File getFile(String rootId, String path) throws FileNotFoundException {
- StubDocument root = mRoots.get(rootId).rootDocument;
+ StubDocument root = mRoots.get(rootId).document;
if (root == null) {
throw new FileNotFoundException("No roots with the ID " + rootId + " were found");
}
@@ -461,7 +485,7 @@
final class RootInfo {
public final String name;
- public final StubDocument rootDocument;
+ public final StubDocument document;
public long capacity;
public long size;
@@ -469,9 +493,11 @@
this.name = name;
this.capacity = 1024 * 1024;
// Make a subdir in the cache dir for each root.
- File rootDir = new File(getContext().getCacheDir(), name);
- rootDir.mkdir();
- this.rootDocument = new StubDocument(rootDir, Document.MIME_TYPE_DIR, this);
+ File file = new File(getContext().getCacheDir(), name);
+ if (file.mkdir()) {
+ Log.i(TAG, "Created new root directory @ " + file.getPath());
+ }
+ this.document = new StubDocument(file, Document.MIME_TYPE_DIR, this);
this.capacity = capacity;
this.size = 0;
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/TestInputEvent.java b/packages/DocumentsUI/tests/src/com/android/documentsui/TestInputEvent.java
index e83f9e0..ec5321a 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/TestInputEvent.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/TestInputEvent.java
@@ -3,7 +3,7 @@
import android.graphics.Point;
import android.support.v7.widget.RecyclerView;
-class TestInputEvent implements Events.InputEvent {
+public class TestInputEvent implements Events.InputEvent {
public boolean mouseEvent;
public boolean primaryButtonPressed;
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java b/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java
new file mode 100644
index 0000000..5c09794
--- /dev/null
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java
@@ -0,0 +1,170 @@
+/*
+ * 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.documentsui;
+
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.BySelector;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.UiObjectNotFoundException;
+import android.support.test.uiautomator.UiScrollable;
+import android.support.test.uiautomator.UiSelector;
+import android.support.test.uiautomator.Until;
+import android.util.Log;
+
+import junit.framework.Assert;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Pattern;
+
+/**
+ * A test helper class that provides support for controlling DocumentsUI activities
+ * programmatically, and making assertions against the state of the UI.
+ */
+class UiBot {
+
+ private static final String TAG = "UiBot";
+
+ private static final BySelector SNACK_DELETE =
+ By.desc(Pattern.compile("^Deleting [0-9]+ file.+"));
+
+ private UiDevice mDevice;
+ private int mTimeout;
+
+ public UiBot(UiDevice device, int timeout) {
+ mDevice = device;
+ mTimeout = timeout;
+ }
+
+ UiObject findRoot(String label) throws UiObjectNotFoundException {
+ final UiSelector rootsList = new UiSelector().resourceId(
+ "com.android.documentsui:id/container_roots").childSelector(
+ new UiSelector().resourceId("android:id/list"));
+
+ // We might need to expand drawer if not visible
+ if (!new UiObject(rootsList).waitForExists(mTimeout)) {
+ Log.d(TAG, "Failed to find roots list; trying to expand");
+ final UiSelector hamburger = new UiSelector().resourceId(
+ "com.android.documentsui:id/toolbar").childSelector(
+ new UiSelector().className("android.widget.ImageButton").clickable(true));
+ new UiObject(hamburger).click();
+ }
+
+ // Wait for the first list item to appear
+ new UiObject(rootsList.childSelector(new UiSelector())).waitForExists(mTimeout);
+
+ // Now scroll around to find our item
+ new UiScrollable(rootsList).scrollIntoView(new UiSelector().text(label));
+ return new UiObject(rootsList.childSelector(new UiSelector().text(label)));
+ }
+
+ void openRoot(String label) throws UiObjectNotFoundException {
+ findRoot(label).click();
+ mDevice.waitForIdle();
+ }
+
+ void assertHasRoots(String... labels) throws UiObjectNotFoundException {
+ List<String> missing = new ArrayList<>();
+ for (String label : labels) {
+ if (!findRoot(label).exists()) {
+ missing.add(label);
+ }
+ }
+ if (!missing.isEmpty()) {
+ Assert.fail(
+ "Expected roots " + Arrays.asList(labels) + ", but missing " + missing);
+ }
+ }
+
+ UiObject findDocument(String label) throws UiObjectNotFoundException {
+ final UiSelector docList = new UiSelector().resourceId(
+ "com.android.documentsui:id/container_directory").childSelector(
+ new UiSelector().resourceId("com.android.documentsui:id/list"));
+
+ // Wait for the first list item to appear
+ new UiObject(docList.childSelector(new UiSelector())).waitForExists(mTimeout);
+
+ // new UiScrollable(docList).scrollIntoView(new UiSelector().text(label));
+ return mDevice.findObject(docList.childSelector(new UiSelector().text(label)));
+ }
+
+ boolean hasDocuments(String... labels) throws UiObjectNotFoundException {
+ for (String label : labels) {
+ if (!findDocument(label).exists()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ void assertHasDocuments(String... labels) throws UiObjectNotFoundException {
+ List<String> missing = new ArrayList<>();
+ for (String label : labels) {
+ if (!findDocument(label).exists()) {
+ missing.add(label);
+ }
+ }
+ if (!missing.isEmpty()) {
+ Assert.fail(
+ "Expected documents " + Arrays.asList(labels) + ", but missing " + missing);
+ }
+ }
+
+ void clickDocument(String label) throws UiObjectNotFoundException {
+ findDocument(label).click();
+ }
+
+ void waitForDeleteSnackbar() {
+ mDevice.wait(Until.findObject(SNACK_DELETE), mTimeout);
+ }
+
+ void waitForDeleteSnackbarGone() {
+ // wait a little longer for snackbar to go away, as it disappears after a timeout.
+ mDevice.wait(Until.gone(SNACK_DELETE), mTimeout * 2);
+ }
+
+ void switchViewMode() {
+ UiObject2 mode = menuGridMode();
+ if (mode != null) {
+ mode.click();
+ } else {
+ menuListMode().click();
+ }
+ }
+
+ UiObject2 menuGridMode() {
+ // Note that we're using By.desc rather than By.res, because of b/25285770
+ return find(By.desc("Grid view"));
+ }
+
+ UiObject2 menuListMode() {
+ // Note that we're using By.desc rather than By.res, because of b/25285770
+ return find(By.desc("List view"));
+ }
+
+ UiObject2 menuDelete() {
+ return find(By.res("com.android.documentsui:id/menu_delete"));
+ }
+
+ private UiObject2 find(BySelector selector) {
+ mDevice.wait(Until.findObject(selector), mTimeout);
+ return mDevice.findObject(selector);
+ }
+}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/DirectoryFragmentModelTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/DirectoryFragmentModelTest.java
new file mode 100644
index 0000000..746e2117
--- /dev/null
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/DirectoryFragmentModelTest.java
@@ -0,0 +1,160 @@
+/*
+ * 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.documentsui.dirlist;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.provider.DocumentsContract.Document;
+import android.support.v7.widget.RecyclerView;
+import android.test.AndroidTestCase;
+import android.test.mock.MockContentResolver;
+import android.view.ViewGroup;
+
+import com.android.documentsui.DirectoryResult;
+import com.android.documentsui.dirlist.DirectoryFragment.Model;
+import com.android.documentsui.dirlist.MultiSelectManager.Selection;
+import com.android.documentsui.model.DocumentInfo;
+
+import java.util.List;
+
+public class DirectoryFragmentModelTest extends AndroidTestCase {
+
+ private static final int ITEM_COUNT = 5;
+ private static final String[] COLUMNS = new String[]{
+ Document.COLUMN_DOCUMENT_ID
+ };
+ private static Cursor cursor;
+
+ private Context mContext;
+ private Model model;
+
+ public void setUp() {
+ setupTestContext();
+
+ MatrixCursor c = new MatrixCursor(COLUMNS);
+ for (int i = 0; i < ITEM_COUNT; ++i) {
+ MatrixCursor.RowBuilder row = c.newRow();
+ row.add(COLUMNS[0], i);
+ }
+ cursor = c;
+
+ DirectoryResult r = new DirectoryResult();
+ r.cursor = cursor;
+
+ // Instantiate the model with a dummy view adapter and listener that (for now) do nothing.
+ model = new Model(mContext, new DummyAdapter());
+ model.addUpdateListener(new DummyListener());
+ model.update(r);
+ }
+
+ // Tests that the item count is correct.
+ public void testItemCount() {
+ assertEquals(ITEM_COUNT, model.getItemCount());
+ }
+
+ // Tests that the item count is correct after a deletion.
+ public void testItemCount_WithDeletion() {
+ // Simulate deleting 2 files.
+ delete(2, 4);
+
+ assertEquals(ITEM_COUNT - 2, model.getItemCount());
+ }
+
+ // Tests that the item count is correct after a deletion is undone.
+ public void testItemCount_WithUndoneDeletion() {
+ // Simulate deleting 2 files.
+ delete(0, 3);
+
+ // Undo the deletion
+ model.undoDeletion();
+ assertEquals(ITEM_COUNT, model.getItemCount());
+ }
+
+ // Tests that the right things are marked for deletion.
+ public void testMarkForDeletion() {
+ delete(1, 3);
+
+ List<DocumentInfo> docs = model.getDocumentsMarkedForDeletion();
+ assertEquals(2, docs.size());
+ assertEquals("1", docs.get(0).documentId);
+ assertEquals("3", docs.get(1).documentId);
+ }
+
+ // Tests the base case for Model.getItem.
+ public void testGetItem() {
+ for (int i = 0; i < ITEM_COUNT; ++i) {
+ Cursor c = model.getItem(i);
+ assertEquals(i, c.getPosition());
+ }
+ }
+
+ // Tests that Model.getItem returns the right items after a deletion.
+ public void testGetItem_WithDeletion() {
+ // Simulate deleting 2 files.
+ delete(2, 3);
+
+ List<DocumentInfo> docs = getDocumentInfo(0, 1, 2);
+ assertEquals("0", docs.get(0).documentId);
+ assertEquals("1", docs.get(1).documentId);
+ assertEquals("4", docs.get(2).documentId);
+ }
+
+ // Tests that Model.getItem returns the right items after a deletion is undone.
+ public void testGetItem_WithCancelledDeletion() {
+ delete(0, 1);
+ model.undoDeletion();
+
+ // Test that all documents are accounted for, in the right position.
+ for (int i = 0; i < ITEM_COUNT; ++i) {
+ assertEquals(Integer.toString(i), getDocumentInfo(i).get(0).documentId);
+ }
+ }
+
+ private void setupTestContext() {
+ final MockContentResolver resolver = new MockContentResolver();
+ mContext = new ContextWrapper(getContext()) {
+ @Override
+ public ContentResolver getContentResolver() {
+ return resolver;
+ }
+ };
+ }
+
+ private void delete(int... positions) {
+ model.markForDeletion(new Selection(positions));
+ }
+
+ private List<DocumentInfo> getDocumentInfo(int... positions) {
+ return model.getDocuments(new Selection(positions));
+ }
+
+ private static class DummyListener extends Model.UpdateListener {
+ public void onModelUpdate(Model model) {}
+ public void onModelUpdateFailed(Exception e) {}
+ }
+
+ private static class DummyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+ public int getItemCount() { return 0; }
+ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {}
+ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ return null;
+ }
+ }
+}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java
new file mode 100644
index 0000000..24f5c9e
--- /dev/null
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java
@@ -0,0 +1,322 @@
+/*
+ * 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.documentsui.dirlist;
+
+import android.support.v7.widget.RecyclerView;
+import android.test.AndroidTestCase;
+import android.util.SparseBooleanArray;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.documentsui.TestInputEvent;
+import com.android.documentsui.dirlist.MultiSelectManager;
+import com.android.documentsui.dirlist.MultiSelectManager.Selection;
+
+import org.mockito.Mockito;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class MultiSelectManagerTest extends AndroidTestCase {
+
+ private static final List<String> items;
+ static {
+ items = new ArrayList<String>();
+ items.add("aaa");
+ items.add("bbb");
+ items.add("ccc");
+ items.add("111");
+ items.add("222");
+ items.add("333");
+ }
+
+ private MultiSelectManager mManager;
+ private TestAdapter mAdapter;
+ private TestCallback mCallback;
+
+ public void setUp() throws Exception {
+ mAdapter = new TestAdapter(items);
+ mCallback = new TestCallback();
+ mManager = new MultiSelectManager(mAdapter, MultiSelectManager.MODE_MULTIPLE);
+ mManager.addCallback(mCallback);
+ }
+
+ public void testMouseClick_StartsSelectionMode() {
+ click(7);
+ assertSelection(7);
+ }
+
+ public void testMouseClick_NotifiesSelectionChanged() {
+ click(7);
+ mCallback.assertSelectionChanged();
+ }
+
+ public void testMouseClick_ShiftClickExtendsSelection() {
+ longPress(7);
+ shiftClick(11);
+ assertRangeSelection(7, 11);
+ }
+
+ public void testMouseClick_NoPosition_ClearsSelection() {
+ longPress(7);
+ click(11);
+ click(RecyclerView.NO_POSITION);
+ assertSelection();
+ }
+
+ public void testSetSelectionFocusBegin() {
+ mManager.setItemSelected(7, true);
+ mManager.setSelectionFocusBegin(7);
+ shiftClick(11);
+ assertRangeSelection(7, 11);
+ }
+
+ public void testLongPress_StartsSelectionMode() {
+ longPress(7);
+ assertSelection(7);
+ }
+
+ public void testLongPress_SecondPressExtendsSelection() {
+ longPress(7);
+ longPress(99);
+ assertSelection(7, 99);
+ }
+
+ public void testSingleTapUp_UnselectsSelectedItem() {
+ longPress(7);
+ tap(7);
+ assertSelection();
+ }
+
+ public void testSingleTapUp_NoPosition_ClearsSelection() {
+ longPress(7);
+ tap(11);
+ tap(RecyclerView.NO_POSITION);
+ assertSelection();
+ }
+
+ public void testSingleTapUp_ExtendsSelection() {
+ longPress(99);
+ tap(7);
+ tap(13);
+ tap(129899);
+ assertSelection(7, 99, 13, 129899);
+ }
+
+ public void testSingleTapUp_ShiftCreatesRangeSelection() {
+ longPress(7);
+ shiftTap(17);
+ assertRangeSelection(7, 17);
+ }
+
+ public void testSingleTapUp_ShiftCreatesRangeSeletion_Backwards() {
+ longPress(17);
+ shiftTap(7);
+ assertRangeSelection(7, 17);
+ }
+
+ public void testSingleTapUp_SecondShiftClickExtendsSelection() {
+ longPress(7);
+ shiftTap(11);
+ shiftTap(17);
+ assertRangeSelection(7, 17);
+ }
+
+ public void testSingleTapUp_MultipleContiguousRangesSelected() {
+ longPress(7);
+ shiftTap(11);
+ tap(20);
+ shiftTap(25);
+ assertRangeSelected(7, 11);
+ assertRangeSelected(20, 25);
+ assertSelectionSize(11);
+ }
+
+ public void testSingleTapUp_ShiftReducesSelectionRange_FromPreviousShiftClick() {
+ longPress(7);
+ shiftTap(17);
+ shiftTap(10);
+ assertRangeSelection(7, 10);
+ }
+
+ public void testSingleTapUp_ShiftReducesSelectionRange_FromPreviousShiftClick_Backwards() {
+ mManager.onLongPress(TestInputEvent.tap(17));
+ shiftTap(7);
+ shiftTap(14);
+ assertRangeSelection(14, 17);
+ }
+
+
+ public void testSingleTapUp_ShiftReversesSelectionDirection() {
+ longPress(7);
+ shiftTap(17);
+ shiftTap(0);
+ assertRangeSelection(0, 7);
+ }
+
+ public void testSingleSelectMode() {
+ mManager = new MultiSelectManager(mAdapter, MultiSelectManager.MODE_SINGLE);
+ mManager.addCallback(mCallback);
+ longPress(20);
+ tap(13);
+ assertSelection(13);
+ }
+
+ public void testSingleSelectMode_ShiftTap() {
+ mManager = new MultiSelectManager(mAdapter, MultiSelectManager.MODE_SINGLE);
+ mManager.addCallback(mCallback);
+ longPress(13);
+ shiftTap(20);
+ assertSelection(20);
+ }
+
+ public void testProvisionalSelection() {
+ Selection s = mManager.getSelection();
+ assertSelection();
+
+ SparseBooleanArray provisional = new SparseBooleanArray();
+ provisional.append(1, true);
+ provisional.append(2, true);
+ s.setProvisionalSelection(provisional);
+ assertSelection(1, 2);
+
+ provisional.delete(1);
+ provisional.append(3, true);
+ s.setProvisionalSelection(provisional);
+ assertSelection(2, 3);
+
+ s.applyProvisionalSelection();
+ assertSelection(2, 3);
+
+ provisional.clear();
+ provisional.append(3, true);
+ provisional.append(4, true);
+ s.setProvisionalSelection(provisional);
+ assertSelection(2, 3, 4);
+
+ provisional.delete(3);
+ s.setProvisionalSelection(provisional);
+ assertSelection(2, 3, 4);
+ }
+
+ private void longPress(int position) {
+ mManager.onLongPress(TestInputEvent.tap(position));
+ }
+
+ private void tap(int position) {
+ mManager.onSingleTapUp(TestInputEvent.tap(position));
+ }
+
+ private void shiftTap(int position) {
+ mManager.onSingleTapUp(TestInputEvent.shiftTap(position));
+ }
+
+ private void click(int position) {
+ mManager.onSingleTapUp(TestInputEvent.click(position));
+ }
+
+ private void shiftClick(int position) {
+ mManager.onSingleTapUp(TestInputEvent.shiftClick(position));
+ }
+
+ private void assertSelected(int... expected) {
+ for (int i = 0; i < expected.length; i++) {
+ Selection selection = mManager.getSelection();
+ String err = String.format(
+ "Selection %s does not contain %d", selection, expected[i]);
+ assertTrue(err, selection.contains(expected[i]));
+ }
+ }
+
+ private void assertSelection(int... expected) {
+ assertSelectionSize(expected.length);
+ assertSelected(expected);
+ }
+
+ private void assertRangeSelected(int begin, int end) {
+ for (int i = begin; i <= end; i++) {
+ assertSelected(i);
+ }
+ }
+
+ private void assertRangeSelection(int begin, int end) {
+ assertSelectionSize(end - begin + 1);
+ assertRangeSelected(begin, end);
+ }
+
+ private void assertSelectionSize(int expected) {
+ Selection selection = mManager.getSelection();
+ assertEquals(selection.toString(), expected, selection.size());
+ }
+
+ private static final class TestCallback implements MultiSelectManager.Callback {
+
+ Set<Integer> ignored = new HashSet<>();
+ private boolean mSelectionChanged = false;
+
+ @Override
+ public void onItemStateChanged(int position, boolean selected) {}
+
+ @Override
+ public boolean onBeforeItemStateChange(int position, boolean selected) {
+ return !ignored.contains(position);
+ }
+
+ @Override
+ public void onSelectionChanged() {
+ mSelectionChanged = true;
+ }
+
+ void assertSelectionChanged() {
+ assertTrue(mSelectionChanged);
+ }
+ }
+
+ private static final class TestHolder extends RecyclerView.ViewHolder {
+ // each data item is just a string in this case
+ public View view;
+ public String string;
+ public TestHolder(View view) {
+ super(view);
+ this.view = view;
+ }
+ }
+
+ private static final class TestAdapter extends RecyclerView.Adapter<TestHolder> {
+
+ private List<String> mItems;
+
+ public TestAdapter(List<String> items) {
+ mItems = items;
+ }
+
+ @Override
+ public TestHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ return new TestHolder(Mockito.mock(ViewGroup.class));
+ }
+
+ @Override
+ public void onBindViewHolder(TestHolder holder, int position) {}
+
+ @Override
+ public int getItemCount() {
+ return mItems.size();
+ }
+ }
+}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_GridModelTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_GridModelTest.java
new file mode 100644
index 0000000..c4b6ce5
--- /dev/null
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_GridModelTest.java
@@ -0,0 +1,319 @@
+/*
+ * 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.documentsui.dirlist;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.support.v7.widget.RecyclerView.OnScrollListener;
+import android.test.AndroidTestCase;
+import android.util.SparseBooleanArray;
+import android.view.View;
+
+import com.android.documentsui.dirlist.MultiSelectManager.GridModel;
+
+public class MultiSelectManager_GridModelTest extends AndroidTestCase {
+
+ private static final int VIEW_PADDING_PX = 5;
+ private static final int CHILD_VIEW_EDGE_PX = 100;
+ private static final int VIEWPORT_HEIGHT = 500;
+
+ private static GridModel model;
+ private static TestEnvironment env;
+ private static SparseBooleanArray lastSelection;
+ private static int viewWidth;
+
+ private static void setUp(int numChildren, int numColumns) {
+ env = new TestEnvironment(numChildren, numColumns);
+ viewWidth = VIEW_PADDING_PX + numColumns * (VIEW_PADDING_PX + CHILD_VIEW_EDGE_PX);
+ model = new GridModel(env);
+ model.addOnSelectionChangedListener(
+ new GridModel.OnSelectionChangedListener() {
+ @Override
+ public void onSelectionChanged(SparseBooleanArray updatedSelection) {
+ lastSelection = updatedSelection;
+ }
+ });
+ }
+
+ @Override
+ public void tearDown() {
+ model = null;
+ env = null;
+ lastSelection = null;
+ }
+
+ public void testSelectionLeftOfItems() {
+ setUp(20, 5);
+ model.startSelection(new Point(0, 10));
+ model.resizeSelection(new Point(1, 11));
+ assertSelected(new int[0]);
+ assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin());
+ }
+
+ public void testSelectionRightOfItems() {
+ setUp(20, 4);
+ model.startSelection(new Point(viewWidth - 1, 10));
+ model.resizeSelection(new Point(viewWidth - 2, 11));
+ assertSelected(new int[0]);
+ assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin());
+ }
+
+ public void testSelectionAboveItems() {
+ setUp(20, 4);
+ model.startSelection(new Point(10, 0));
+ model.resizeSelection(new Point(11, 1));
+ assertSelected(new int[0]);
+ assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin());
+ }
+
+ public void testSelectionBelowItems() {
+ setUp(5, 4);
+ model.startSelection(new Point(10, VIEWPORT_HEIGHT - 1));
+ model.resizeSelection(new Point(11, VIEWPORT_HEIGHT - 2));
+ assertSelected(new int[0]);
+ assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin());
+ }
+
+ public void testVerticalSelectionBetweenItems() {
+ setUp(20, 4);
+ model.startSelection(new Point(106, 0));
+ model.resizeSelection(new Point(107, 200));
+ assertSelected(new int[0]);
+ assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin());
+ }
+
+ public void testHorizontalSelectionBetweenItems() {
+ setUp(20, 4);
+ model.startSelection(new Point(0, 105));
+ model.resizeSelection(new Point(200, 106));
+ assertSelected(new int[0]);
+ assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin());
+ }
+
+ public void testGrowingAndShrinkingSelection() {
+ setUp(20, 4);
+ model.startSelection(new Point(0, 0));
+ model.resizeSelection(new Point(5, 5));
+ assertSelected(new int[] {0});
+ model.resizeSelection(new Point(109, 109));
+ assertSelected(new int[] {0});
+ model.resizeSelection(new Point(110, 109));
+ assertSelected(new int[] {0, 1});
+ model.resizeSelection(new Point(110, 110));
+ assertSelected(new int[] {0, 1, 4, 5});
+ model.resizeSelection(new Point(214, 214));
+ assertSelected(new int[] {0, 1, 4, 5});
+ model.resizeSelection(new Point(215, 214));
+ assertSelected(new int[] {0, 1, 2, 4, 5, 6});
+ model.resizeSelection(new Point(214, 214));
+ assertSelected(new int[] {0, 1, 4, 5});
+ model.resizeSelection(new Point(110, 110));
+ assertSelected(new int[] {0, 1, 4, 5});
+ model.resizeSelection(new Point(110, 109));
+ assertSelected(new int[] {0, 1});
+ model.resizeSelection(new Point(109, 109));
+ assertSelected(new int[] {0});
+ model.resizeSelection(new Point(5, 5));
+ assertSelected(new int[] {0});
+ model.resizeSelection(new Point(0, 0));
+ assertSelected(new int[0]);
+ assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin());
+ }
+
+ public void testSelectionMovingAroundOrigin() {
+ setUp(16, 4);
+ model.startSelection(new Point(210, 210));
+ model.resizeSelection(new Point(viewWidth - 1, 0));
+ assertSelected(new int[] {2, 3, 6, 7});
+ model.resizeSelection(new Point(0, 0));
+ assertSelected(new int[] {0, 1, 4, 5});
+ model.resizeSelection(new Point(0, 420));
+ assertSelected(new int[] {8, 9, 12, 13});
+ model.resizeSelection(new Point(viewWidth - 1, 420));
+ assertSelected(new int[] {10, 11, 14, 15});
+ assertEquals(10, model.getPositionNearestOrigin());
+ }
+
+ public void testScrollingBandSelect() {
+ setUp(40, 4);
+ model.startSelection(new Point(0, 0));
+ model.resizeSelection(new Point(100, VIEWPORT_HEIGHT - 1));
+ assertSelected(new int[] {0, 4, 8, 12, 16});
+ scroll(CHILD_VIEW_EDGE_PX);
+ assertSelected(new int[] {0, 4, 8, 12, 16, 20});
+ model.resizeSelection(new Point(200, VIEWPORT_HEIGHT - 1));
+ assertSelected(new int[] {0, 1, 4, 5, 8, 9, 12, 13, 16, 17, 20, 21});
+ scroll(CHILD_VIEW_EDGE_PX);
+ assertSelected(new int[] {0, 1, 4, 5, 8, 9, 12, 13, 16, 17, 20, 21, 24, 25});
+ scroll(-2 * CHILD_VIEW_EDGE_PX);
+ assertSelected(new int[] {0, 1, 4, 5, 8, 9, 12, 13, 16, 17});
+ model.resizeSelection(new Point(100, VIEWPORT_HEIGHT - 1));
+ assertSelected(new int[] {0, 4, 8, 12, 16});
+ assertEquals(0, model.getPositionNearestOrigin());
+ }
+
+ private static void assertSelected(int[] selectedPositions) {
+ assertEquals(selectedPositions.length, lastSelection.size());
+ for (int position : selectedPositions) {
+ assertTrue(lastSelection.get(position));
+ }
+ }
+
+ private static void scroll(int dy) {
+ assertTrue(env.verticalOffset + VIEWPORT_HEIGHT + dy <= env.getTotalHeight());
+ env.verticalOffset += dy;
+ model.onScrolled(null, 0, dy);
+ }
+
+ private static final class TestEnvironment implements MultiSelectManager.SelectionEnvironment {
+
+ public int horizontalOffset = 0;
+ public int verticalOffset = 0;
+ private final int mNumColumns;
+ private final int mNumRows;
+ private final int mNumChildren;
+
+ public TestEnvironment(int numChildren, int numColumns) {
+ mNumChildren = numChildren;
+ mNumColumns = numColumns;
+ mNumRows = (int) Math.ceil((double) numChildren / mNumColumns);
+ }
+
+ private int getTotalHeight() {
+ return CHILD_VIEW_EDGE_PX * mNumRows + VIEW_PADDING_PX * (mNumRows + 1);
+ }
+
+ private int getFirstVisibleRowIndex() {
+ return verticalOffset / (CHILD_VIEW_EDGE_PX + VIEW_PADDING_PX);
+ }
+
+ private int getLastVisibleRowIndex() {
+ int lastVisibleRowUncapped =
+ (VIEWPORT_HEIGHT + verticalOffset - 1) / (CHILD_VIEW_EDGE_PX + VIEW_PADDING_PX);
+ return Math.min(lastVisibleRowUncapped, mNumRows - 1);
+ }
+
+ private int getNumItemsInRow(int index) {
+ assertTrue(index >= 0 && index < mNumRows);
+ if (index == mNumRows - 1 && mNumChildren % mNumColumns != 0) {
+ return mNumChildren % mNumColumns;
+ }
+
+ return mNumColumns;
+ }
+
+ @Override
+ public void addOnScrollListener(OnScrollListener listener) {}
+
+ @Override
+ public void removeOnScrollListener(OnScrollListener listener) {}
+
+ @Override
+ public Point createAbsolutePoint(Point relativePoint) {
+ return new Point(
+ relativePoint.x + horizontalOffset, relativePoint.y + verticalOffset);
+ }
+
+ @Override
+ public int getVisibleChildCount() {
+ int childCount = 0;
+ for (int i = getFirstVisibleRowIndex(); i <= getLastVisibleRowIndex(); i++) {
+ childCount += getNumItemsInRow(i);
+ }
+ return childCount;
+ }
+
+ @Override
+ public int getAdapterPositionAt(int index) {
+ return index + mNumColumns * (getFirstVisibleRowIndex());
+ }
+
+ @Override
+ public Rect getAbsoluteRectForChildViewAt(int index) {
+ int adapterPosition = getAdapterPositionAt(index);
+ int rowIndex = adapterPosition / mNumColumns;
+ int columnIndex = adapterPosition % mNumColumns;
+
+ Rect rect = new Rect();
+ rect.top = VIEW_PADDING_PX + rowIndex * (CHILD_VIEW_EDGE_PX + VIEW_PADDING_PX);
+ rect.bottom = rect.top + CHILD_VIEW_EDGE_PX - 1;
+ rect.left = VIEW_PADDING_PX + columnIndex * (CHILD_VIEW_EDGE_PX + VIEW_PADDING_PX);
+ rect.right = rect.left + CHILD_VIEW_EDGE_PX - 1;
+ return rect;
+ }
+
+ @Override
+ public int getChildCount() {
+ return mNumChildren;
+ }
+
+ @Override
+ public int getColumnCount() {
+ return mNumColumns;
+ }
+
+ @Override
+ public int getRowCount() {
+ return mNumRows;
+ }
+
+ @Override
+ public void showBand(Rect rect) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void hideBand() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void scrollBy(int dy) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getHeight() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void invalidateView() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void runAtNextFrame(Runnable r) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void removeCallback(Runnable r) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getAdapterPositionForChildView(View view) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void focusItem(int i) {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_SelectionTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_SelectionTest.java
new file mode 100644
index 0000000..64da750
--- /dev/null
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_SelectionTest.java
@@ -0,0 +1,153 @@
+/*
+ * 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.documentsui.dirlist;
+
+import android.test.AndroidTestCase;
+
+import com.android.documentsui.dirlist.MultiSelectManager.Selection;
+
+
+public class MultiSelectManager_SelectionTest extends AndroidTestCase{
+
+ private Selection selection;
+
+ @Override
+ public void setUp() throws Exception {
+ selection = new Selection();
+ selection.add(3);
+ selection.add(5);
+ selection.add(9);
+ }
+
+ public void testAdd() {
+ // We added in setUp.
+ assertEquals(3, selection.size());
+ assertContains(3);
+ assertContains(5);
+ assertContains(9);
+ }
+
+ public void testRemove() {
+ selection.remove(3);
+ selection.remove(5);
+ assertEquals(1, selection.size());
+ assertContains(9);
+ }
+
+ public void testClear() {
+ selection.clear();
+ assertEquals(0, selection.size());
+ }
+
+ public void testIsEmpty() {
+ assertTrue(new Selection().isEmpty());
+ selection.clear();
+ assertTrue(selection.isEmpty());
+ }
+
+ public void testSizeAndGet() {
+ Selection other = new Selection();
+ for (int i = 0; i < selection.size(); i++) {
+ other.add(selection.get(i));
+ }
+ assertEquals(selection.size(), other.size());
+ }
+
+ public void testEqualsSelf() {
+ assertEquals(selection, selection);
+ }
+
+ public void testEqualsOther() {
+ Selection other = new Selection();
+ other.add(3);
+ other.add(5);
+ other.add(9);
+ assertEquals(selection, other);
+ assertEquals(selection.hashCode(), other.hashCode());
+ }
+
+ public void testEqualsCopy() {
+ Selection other = new Selection();
+ other.copyFrom(selection);
+ assertEquals(selection, other);
+ assertEquals(selection.hashCode(), other.hashCode());
+ }
+
+ public void testNotEquals() {
+ Selection other = new Selection();
+ other.add(789);
+ assertFalse(selection.equals(other));
+ }
+
+ public void testExpandBefore() {
+ selection.expand(2, 10);
+ assertEquals(3, selection.size());
+ assertContains(13);
+ assertContains(15);
+ assertContains(19);
+ }
+
+ public void testExpandAfter() {
+ selection.expand(10, 10);
+ assertEquals(3, selection.size());
+ assertContains(3);
+ assertContains(5);
+ assertContains(9);
+ }
+
+ public void testExpandSplit() {
+ selection.expand(5, 10);
+ assertEquals(3, selection.size());
+ assertContains(3);
+ assertContains(15);
+ assertContains(19);
+ }
+
+ public void testExpandEncompased() {
+ selection.expand(2, 10);
+ assertEquals(3, selection.size());
+ assertContains(13);
+ assertContains(15);
+ assertContains(19);
+ }
+
+ public void testCollapseBefore() {
+ selection.collapse(0, 2);
+ assertEquals(3, selection.size());
+ assertContains(1);
+ assertContains(3);
+ assertContains(7);
+ }
+
+ public void testCollapseAfter() {
+ selection.collapse(10, 10);
+ assertEquals(3, selection.size());
+ assertContains(3);
+ assertContains(5);
+ assertContains(9);
+ }
+
+ public void testCollapseAcross() {
+ selection.collapse(0, 10);
+ assertEquals(0, selection.size());
+ }
+
+ private void assertContains(int i) {
+ String err = String.format("Selection %s does not contain %d", selection, i);
+ assertTrue(err, selection.contains(i));
+ }
+}
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 18335b6..fcd45f2 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -273,10 +273,12 @@
flags |= Document.FLAG_DIR_SUPPORTS_CREATE;
flags |= Document.FLAG_SUPPORTS_DELETE;
flags |= Document.FLAG_SUPPORTS_RENAME;
+ flags |= Document.FLAG_SUPPORTS_MOVE;
} else {
flags |= Document.FLAG_SUPPORTS_WRITE;
flags |= Document.FLAG_SUPPORTS_DELETE;
flags |= Document.FLAG_SUPPORTS_RENAME;
+ flags |= Document.FLAG_SUPPORTS_MOVE;
}
}
@@ -409,6 +411,21 @@
}
@Override
+ public String moveDocument(String sourceDocumentId, String targetParentDocumentId)
+ throws FileNotFoundException {
+ final File before = getFileForDocId(sourceDocumentId);
+ final File after = new File(getFileForDocId(targetParentDocumentId), before.getName());
+
+ if (after.exists()) {
+ throw new IllegalStateException("Already exists " + after);
+ }
+ if (!before.renameTo(after)) {
+ throw new IllegalStateException("Failed to move to " + after);
+ }
+ return getDocIdForFile(after);
+ }
+
+ @Override
public Cursor queryDocument(String documentId, String[] projection)
throws FileNotFoundException {
final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
diff --git a/packages/FakeOemFeatures/AndroidManifest.xml b/packages/FakeOemFeatures/AndroidManifest.xml
index 93b8b47..fe74ad8 100644
--- a/packages/FakeOemFeatures/AndroidManifest.xml
+++ b/packages/FakeOemFeatures/AndroidManifest.xml
@@ -11,7 +11,9 @@
android:allowClearUserData="false"
android:allowBackup="false"
android:hardwareAccelerated="true"
- android:label="Fake OEM Features">
+ android:label="Fake OEM Features"
+ android:forceDeviceEncrypted="true"
+ android:encryptionAware="true">
<service android:name=".FakeCoreService" android:process=":core"
android:label="Fake OEM Core Service" />
diff --git a/packages/FusedLocation/AndroidManifest.xml b/packages/FusedLocation/AndroidManifest.xml
index 6a4d4bf..ed84c0d 100644
--- a/packages/FusedLocation/AndroidManifest.xml
+++ b/packages/FusedLocation/AndroidManifest.xml
@@ -28,7 +28,9 @@
<application
android:label="@string/app_label"
- android:process="system">
+ android:process="system"
+ android:forceDeviceEncrypted="true"
+ android:encryptionAware="true">
<uses-library android:name="com.android.location.provider" />
@@ -37,7 +39,7 @@
version. -->
<service android:name="com.android.location.fused.FusedLocationService"
android:exported="true"
- android:permission="android.permission.WRITE_SECURE_SETTINGS" >
+ android:permission="android.permission.WRITE_SECURE_SETTINGS">
<intent-filter>
<action android:name="com.android.location.service.FusedLocationProvider" />
</intent-filter>
diff --git a/packages/InputDevices/AndroidManifest.xml b/packages/InputDevices/AndroidManifest.xml
index f0e4abc..07885ea 100644
--- a/packages/InputDevices/AndroidManifest.xml
+++ b/packages/InputDevices/AndroidManifest.xml
@@ -6,7 +6,9 @@
<application
android:allowClearUserData="false"
android:label="@string/app_label"
- android:process="system">
+ android:process="system"
+ android:forceDeviceEncrypted="true"
+ android:encryptionAware="true">
<receiver android:name=".InputDeviceReceiver"
android:label="@string/keyboard_layouts_label">
diff --git a/packages/InputDevices/res/raw/keyboard_layout_english_us_workman.kcm b/packages/InputDevices/res/raw/keyboard_layout_english_us_workman.kcm
new file mode 100644
index 0000000..fe82c8d
--- /dev/null
+++ b/packages/InputDevices/res/raw/keyboard_layout_english_us_workman.kcm
@@ -0,0 +1,334 @@
+# 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.
+
+#
+# English (US), Workman keyboard layout.
+# Unlike the default (generic) keyboard layout, English (US) does not contain any
+# special ALT characters.
+#
+
+type OVERLAY
+
+map key 17 D
+map key 18 R
+map key 19 W
+map key 20 B
+map key 21 J
+map key 22 F
+map key 23 U
+map key 24 P
+map key 25 SEMICOLON
+map key 32 H
+map key 33 T
+map key 34 G
+map key 35 Y
+map key 36 N
+map key 37 E
+map key 38 O
+map key 39 I
+map key 46 M
+map key 47 C
+map key 48 V
+map key 49 K
+map key 50 L
+
+### ROW 1
+
+key GRAVE {
+ label: '`'
+ base: '`'
+ shift: '~'
+}
+
+key 1 {
+ label: '1'
+ base: '1'
+ shift: '!'
+}
+
+key 2 {
+ label: '2'
+ base: '2'
+ shift: '@'
+}
+
+key 3 {
+ label: '3'
+ base: '3'
+ shift: '#'
+}
+
+key 4 {
+ label: '4'
+ base: '4'
+ shift: '$'
+}
+
+key 5 {
+ label: '5'
+ base: '5'
+ shift: '%'
+}
+
+key 6 {
+ label: '6'
+ base: '6'
+ shift: '^'
+}
+
+key 7 {
+ label: '7'
+ base: '7'
+ shift: '&'
+}
+
+key 8 {
+ label: '8'
+ base: '8'
+ shift: '*'
+}
+
+key 9 {
+ label: '9'
+ base: '9'
+ shift: '('
+}
+
+key 0 {
+ label: '0'
+ base: '0'
+ shift: ')'
+}
+
+key MINUS {
+ label: '-'
+ base: '-'
+ shift: '_'
+}
+
+key EQUALS {
+ label: '='
+ base: '='
+ shift: '+'
+}
+
+### ROW 2
+
+key Q {
+ label: 'Q'
+ base: 'q'
+ shift, capslock: 'Q'
+}
+
+key D {
+ label: 'D'
+ base: 'd'
+ shift, capslock: 'D'
+}
+
+key R {
+ label: 'R'
+ base: 'r'
+ shift, capslock: 'R'
+}
+
+key W {
+ label: 'W'
+ base: 'w'
+ shift, capslock: 'W'
+}
+
+key B {
+ label: 'B'
+ base: 'b'
+ shift, capslock: 'B'
+}
+
+key J {
+ label: 'J'
+ base: 'j'
+ shift, capslock: 'J'
+}
+
+key F {
+ label: 'F'
+ base: 'f'
+ shift, capslock: 'F'
+}
+
+key U {
+ label: 'U'
+ base: 'u'
+ shift, capslock: 'U'
+}
+
+key P {
+ label: 'P'
+ base: 'p'
+ shift, capslock: 'P'
+}
+
+key SEMICOLON {
+ label: ';'
+ base: ';'
+ shift, capslock: ':'
+}
+
+key LEFT_BRACKET {
+ label: '['
+ base: '['
+ shift: '{'
+}
+
+key RIGHT_BRACKET {
+ label: ']'
+ base: ']'
+ shift: '}'
+}
+
+key BACKSLASH {
+ label: '\\'
+ base: '\\'
+ shift: '|'
+}
+
+### ROW 3
+
+key A {
+ label: 'A'
+ base: 'a'
+ shift, capslock: 'A'
+}
+
+key S {
+ label: 'S'
+ base: 's'
+ shift, capslock: 'S'
+}
+
+key H {
+ label: 'H'
+ base: 'h'
+ shift, capslock: 'H'
+}
+
+key T {
+ label: 'T'
+ base: 't'
+ shift, capslock: 'T'
+}
+
+key G {
+ label: 'G'
+ base: 'g'
+ shift, capslock: 'G'
+}
+
+key Y {
+ label: 'Y'
+ base: 'y'
+ shift, capslock: 'Y'
+}
+
+key N {
+ label: 'N'
+ base: 'n'
+ shift, capslock: 'N'
+}
+
+key E {
+ label: 'E'
+ base: 'e'
+ shift, capslock: 'E'
+}
+
+key O {
+ label: 'O'
+ base: 'o'
+ shift: 'O'
+}
+
+key I {
+ label: 'I'
+ base: 'i'
+ shift, capslock: 'I'
+}
+
+key APOSTROPHE {
+ label: '\''
+ base: '\''
+ shift: '"'
+}
+
+### ROW 4
+
+key Z {
+ label: 'Z'
+ base: 'z'
+ shift, capslock: 'Z'
+}
+
+key X {
+ label: 'X'
+ base: 'x'
+ shift, capslock: 'X'
+}
+
+key M {
+ label: 'M'
+ base: 'm'
+ shift, capslock: 'M'
+}
+
+key C {
+ label: 'C'
+ base: 'c'
+ shift, capslock: 'C'
+}
+
+key V {
+ label: 'V'
+ base: 'v'
+ shift, capslock: 'V'
+}
+
+key K {
+ label: 'K'
+ base: 'k'
+ shift, capslock: 'K'
+}
+
+key L {
+ label: 'L'
+ base: 'l'
+ shift, capslock: 'L'
+}
+
+key COMMA {
+ label: ','
+ base: ','
+ shift: '<'
+}
+
+key PERIOD {
+ label: '.'
+ base: '.'
+ shift: '>'
+}
+
+key SLASH {
+ label: '/'
+ base: '/'
+ shift: '?'
+}
diff --git a/packages/InputDevices/res/values-af/strings.xml b/packages/InputDevices/res/values-af/strings.xml
index d67a9fd..a36d01e 100644
--- a/packages/InputDevices/res/values-af/strings.xml
+++ b/packages/InputDevices/res/values-af/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Engels (VS), internasionale styl"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Engels (VS), Colemak-styl"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Engels (VS), Dvorak-styl"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Engels (VSA), Workman-styl"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Duits"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Frans"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Frans (Kanada)"</string>
diff --git a/packages/InputDevices/res/values-am/strings.xml b/packages/InputDevices/res/values-am/strings.xml
index 3e84794..3186add 100644
--- a/packages/InputDevices/res/values-am/strings.xml
+++ b/packages/InputDevices/res/values-am/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"እንግሊዘኛ (ዩ. ኤስ.)፣ አለም አቀፍ ቅጥ"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"እንግሊዘኛ (ዩ. ኤስ.)፣ የኮልማርክ ቅጥ"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"እንግሊዘኛ (ዩ. ኤስ.)፣ የድቮራክ ቅጥ"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"እንግሊዝኛ (አሜሪካ)፣ የሥራሰው ቅጥ"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"ጀርመን"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"ፈረንሳይኛ"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"ፈረንሳይኛ (ካናዳ)"</string>
diff --git a/packages/InputDevices/res/values-ar/strings.xml b/packages/InputDevices/res/values-ar/strings.xml
index a922a46..1d3d9f5 100644
--- a/packages/InputDevices/res/values-ar/strings.xml
+++ b/packages/InputDevices/res/values-ar/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"الإنجليزية (الولايات المتحدة)، النمط الدولي"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"الإنجليزية (الولايات المتحدة)، نمط Colemak"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"الإنجليزية (الولايات المتحدة)، نمط Dvorak"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"الإنجليزية (الولايات المتحدة)، الحرفيّون"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"الألمانية"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"الفرنسية"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"الفرنسية (كندا)"</string>
diff --git a/packages/InputDevices/res/values-az-rAZ/strings.xml b/packages/InputDevices/res/values-az-rAZ/strings.xml
index d69059c..25d7c91 100644
--- a/packages/InputDevices/res/values-az-rAZ/strings.xml
+++ b/packages/InputDevices/res/values-az-rAZ/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"İngilis (ABŞ), Beynəlxalq üslub"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"İngilis (ABŞ), Colemak üslubu"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"İngilis (ABŞ), Dvorak üslubu"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"İngilis dili (ABŞ), Workman üslubu"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Alman"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Fransız"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Fransız dili (Kanada)"</string>
diff --git a/packages/InputDevices/res/values-bg/strings.xml b/packages/InputDevices/res/values-bg/strings.xml
index d68a347..e5e2c96 100644
--- a/packages/InputDevices/res/values-bg/strings.xml
+++ b/packages/InputDevices/res/values-bg/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"англ. (САЩ) – стил „Mеждународна“"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"английски (САЩ) – стил „Коулмак“"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"английски (САЩ) – стил „Дворак“"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"англ. (САЩ) – стил „Работническа“"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"немски"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"френски"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"френски (Канада)"</string>
diff --git a/packages/InputDevices/res/values-bn-rBD/strings.xml b/packages/InputDevices/res/values-bn-rBD/strings.xml
index 1676a95..a0ce313 100644
--- a/packages/InputDevices/res/values-bn-rBD/strings.xml
+++ b/packages/InputDevices/res/values-bn-rBD/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"ইংরেজি (US), আন্তর্জাতিক শৈলী"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"ইংরেজি (US), কোলেম্যাক শৈলী"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"ইংরেজি (US), ডিভোরাক শৈলী"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"ইংরেজি (US), ওয়ার্কম্যান শৈলী"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"জার্মান"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"ফরাসী"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"ফরাসী (কানাডা)"</string>
diff --git a/packages/InputDevices/res/values-ca/strings.xml b/packages/InputDevices/res/values-ca/strings.xml
index 6baa5b8..ee25a74 100644
--- a/packages/InputDevices/res/values-ca/strings.xml
+++ b/packages/InputDevices/res/values-ca/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Anglès (EUA), estil internacional"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Anglès (EUA), estil Colemak"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Anglès (EUA), estil Dvorak"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Anglès (EUA), estil Workman"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Alemany"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Francès"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Francès (Canadà)"</string>
diff --git a/packages/InputDevices/res/values-cs/strings.xml b/packages/InputDevices/res/values-cs/strings.xml
index 1c502fe..3e7b9c8 100644
--- a/packages/InputDevices/res/values-cs/strings.xml
+++ b/packages/InputDevices/res/values-cs/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"anglické (USA), mezinárodní"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"anglické (USA), styl Colemak"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"anglické (USA), styl Dvorak"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"anglické (USA), styl Workman"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"německé"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"francouzské"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"francouzské (Kanada)"</string>
diff --git a/packages/InputDevices/res/values-da/strings.xml b/packages/InputDevices/res/values-da/strings.xml
index 043a5b3..228c51a 100644
--- a/packages/InputDevices/res/values-da/strings.xml
+++ b/packages/InputDevices/res/values-da/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Engelsk (USA), international stil"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Engelsk (USA), Colemak-stil"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Engelsk (USA), Dvorak-stil"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Engelsk (USA), Workman-stil"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Tysk"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Fransk"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Fransk (Canada)"</string>
diff --git a/packages/InputDevices/res/values-de/strings.xml b/packages/InputDevices/res/values-de/strings.xml
index 40722f6..6af0030 100644
--- a/packages/InputDevices/res/values-de/strings.xml
+++ b/packages/InputDevices/res/values-de/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Englisch (USA), international"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Englisch (USA), Colemak"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Englisch (USA), Dvorak"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Englisch (USA), Workman"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Deutsch"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Französisch"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Französisch (Kanada)"</string>
diff --git a/packages/InputDevices/res/values-el/strings.xml b/packages/InputDevices/res/values-el/strings.xml
index 025a288..da6dca2 100644
--- a/packages/InputDevices/res/values-el/strings.xml
+++ b/packages/InputDevices/res/values-el/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Αγγλικά (ΗΠΑ), τύπου International"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Αγγλικά (ΗΠΑ), τύπου Colemak"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Αγγλικά (ΗΠΑ), τύπου Dvorak"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Αγγλικά (ΗΠΑ), στυλ Workman"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Γερμανικά"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Γαλλικά"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Γαλλικά (Καναδά)"</string>
diff --git a/packages/InputDevices/res/values-en-rAU/strings.xml b/packages/InputDevices/res/values-en-rAU/strings.xml
index d5797a0..01c2979 100644
--- a/packages/InputDevices/res/values-en-rAU/strings.xml
+++ b/packages/InputDevices/res/values-en-rAU/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"English (US), International style"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"English (US), Colemak style"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"English (US), Dvorak style"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"English (US), Workman style"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"German"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"French"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"French (Canada)"</string>
diff --git a/packages/InputDevices/res/values-en-rGB/strings.xml b/packages/InputDevices/res/values-en-rGB/strings.xml
index d5797a0..01c2979 100644
--- a/packages/InputDevices/res/values-en-rGB/strings.xml
+++ b/packages/InputDevices/res/values-en-rGB/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"English (US), International style"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"English (US), Colemak style"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"English (US), Dvorak style"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"English (US), Workman style"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"German"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"French"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"French (Canada)"</string>
diff --git a/packages/InputDevices/res/values-en-rIN/strings.xml b/packages/InputDevices/res/values-en-rIN/strings.xml
index d5797a0..01c2979 100644
--- a/packages/InputDevices/res/values-en-rIN/strings.xml
+++ b/packages/InputDevices/res/values-en-rIN/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"English (US), International style"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"English (US), Colemak style"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"English (US), Dvorak style"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"English (US), Workman style"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"German"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"French"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"French (Canada)"</string>
diff --git a/packages/InputDevices/res/values-es-rUS/strings.xml b/packages/InputDevices/res/values-es-rUS/strings.xml
index 0a9d2f3..6acf07b 100644
--- a/packages/InputDevices/res/values-es-rUS/strings.xml
+++ b/packages/InputDevices/res/values-es-rUS/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Inglés (EE. UU.), internacional"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Inglés (EE. UU.), Colemak"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Inglés (EE. UU.), Dvorak"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Inglés (EE. UU.), Workman"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Alemán"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Francés"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Francés (Canadá)"</string>
diff --git a/packages/InputDevices/res/values-es/strings.xml b/packages/InputDevices/res/values-es/strings.xml
index 6e41abf..af1492a 100644
--- a/packages/InputDevices/res/values-es/strings.xml
+++ b/packages/InputDevices/res/values-es/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Inglés (EE.UU.), estilo internacional"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Inglés (EE.UU.), estilo Colemak"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Inglés (EE.UU.), estilo Dvorak"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Inglés (EE. UU.) estilo Workman"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Alemán"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Francés"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Francés (Canadá)"</string>
diff --git a/packages/InputDevices/res/values-et-rEE/strings.xml b/packages/InputDevices/res/values-et-rEE/strings.xml
index 0d931ce..d03b82e 100644
--- a/packages/InputDevices/res/values-et-rEE/strings.xml
+++ b/packages/InputDevices/res/values-et-rEE/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Inglise (USA), rahvusvaheline stiil"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Inglise (USA), Colemaki stiil"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Inglise (USA), Dvoraki stiil"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Inglise (USA), Workmani stiil"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Saksa"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Prantsuse"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Prantsuse (Kanada)"</string>
diff --git a/packages/InputDevices/res/values-eu-rES/strings.xml b/packages/InputDevices/res/values-eu-rES/strings.xml
index d4d7b6a..d18c6f8 100644
--- a/packages/InputDevices/res/values-eu-rES/strings.xml
+++ b/packages/InputDevices/res/values-eu-rES/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Ingelesa (AEB), nazioarteko estiloa"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Ingelesa (AEB), Colemak estiloa"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Ingelesa (AEB), Dvorak estiloa"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Ingelesa (AEB), Workman estiloa"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Alemaniarra"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Frantsesa"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Frantsesa (Kanada)"</string>
diff --git a/packages/InputDevices/res/values-fa/strings.xml b/packages/InputDevices/res/values-fa/strings.xml
index e87fbad..f2848a9 100644
--- a/packages/InputDevices/res/values-fa/strings.xml
+++ b/packages/InputDevices/res/values-fa/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"انگلیسی (ایالات متحده)، سبک بینالمللی"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"انگلیسی (ایالات متحده)، سبک Colemak"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"انگلیسی (ایالات متحده)، سبک Dvorak"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"انگلیسی (ایالات متحده)، سبک ورکمن"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"آلمانی"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"فرانسوی"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"فرانسوی (کانادا)"</string>
diff --git a/packages/InputDevices/res/values-fi/strings.xml b/packages/InputDevices/res/values-fi/strings.xml
index 5b39dfd..284efc8 100644
--- a/packages/InputDevices/res/values-fi/strings.xml
+++ b/packages/InputDevices/res/values-fi/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"englanti (Yhdysvallat), kansainvälinen"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"englanti (Yhdysvallat), Colemak"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"englanti (Yhdysvallat), Dvorak"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"englanti (Yhdysvallat), Workman"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"saksa"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"ranska"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"ranska (Kanada)"</string>
diff --git a/packages/InputDevices/res/values-fr-rCA/strings.xml b/packages/InputDevices/res/values-fr-rCA/strings.xml
index 9973918..b26a0ea 100644
--- a/packages/InputDevices/res/values-fr-rCA/strings.xml
+++ b/packages/InputDevices/res/values-fr-rCA/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Anglais (États-Unis), international"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Anglais (États-Unis), type Colemak"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Anglais (États-Unis), type Dvorak"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Anglais (États-Unis), style Workman"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Allemand"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Français"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Français (Canada)"</string>
diff --git a/packages/InputDevices/res/values-fr/strings.xml b/packages/InputDevices/res/values-fr/strings.xml
index fa2977b..a428a23 100644
--- a/packages/InputDevices/res/values-fr/strings.xml
+++ b/packages/InputDevices/res/values-fr/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Anglais (États-Unis), international"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Anglais (États-Unis), type Colemak"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Anglais (États-Unis), type Dvorak"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Anglais (États-Unis), style Workman"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Allemand"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Français"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Français (Canada)"</string>
diff --git a/packages/InputDevices/res/values-gl-rES/strings.xml b/packages/InputDevices/res/values-gl-rES/strings.xml
index d9babd5..bb0f2a0 100644
--- a/packages/InputDevices/res/values-gl-rES/strings.xml
+++ b/packages/InputDevices/res/values-gl-rES/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Inglés (EUA), estilo internacional"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Inglés (EUA), estilo Colemak"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Inglés (EUA), estilo Dvorak"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Inglés (EUA), estilo Workman"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Alemán"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Francés"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Francés (Canadá)"</string>
diff --git a/packages/InputDevices/res/values-gu-rIN/strings.xml b/packages/InputDevices/res/values-gu-rIN/strings.xml
index 3abae49..e83b0ca 100644
--- a/packages/InputDevices/res/values-gu-rIN/strings.xml
+++ b/packages/InputDevices/res/values-gu-rIN/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"અંગ્રેજી (યુએસ), આંતરરાષ્ટ્રીય શૈલી"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"અંગ્રેજી (યુએસ), કોલેમેક શૈલી"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"અંગ્રેજી (યુએસ), ડ્વોરક શૈલી"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"અંગ્રેજી (યુએસ), વર્કમૅન શૈલી"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"જર્મન"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"ફ્રેન્ચ"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"ફ્રેન્ચ (કેનેડા)"</string>
diff --git a/packages/InputDevices/res/values-hi/strings.xml b/packages/InputDevices/res/values-hi/strings.xml
index fcd7f84..41966e4 100644
--- a/packages/InputDevices/res/values-hi/strings.xml
+++ b/packages/InputDevices/res/values-hi/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"अंग्रेज़ी (यूएस), अंतर्राष्ट्रीय शैली"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"अंग्रेज़ी (यूएस), कोलमैक शैली"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"अंग्रेज़ी (यूएस), ड्वोरक शैली"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"अंग्रेज़ी (यूएस), वर्कमैन शैली"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"जर्मन"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"फ़्रांसीसी"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"फ़्रांसीसी (कनाडा)"</string>
diff --git a/packages/InputDevices/res/values-hr/strings.xml b/packages/InputDevices/res/values-hr/strings.xml
index bad973d..27066ad 100644
--- a/packages/InputDevices/res/values-hr/strings.xml
+++ b/packages/InputDevices/res/values-hr/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"engleska (SAD), međunarodna"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"engleska (SAD), Colemakov raspored"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"engleska (SAD), Dvorakov raspored"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Engleska (SAD), raspored Workman"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"njemačka"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"francuska"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"francuska (Kanada)"</string>
diff --git a/packages/InputDevices/res/values-hu/strings.xml b/packages/InputDevices/res/values-hu/strings.xml
index 510591d..6fbc3eb 100644
--- a/packages/InputDevices/res/values-hu/strings.xml
+++ b/packages/InputDevices/res/values-hu/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"angol (USA), nemzetközi stílus"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"angol (USA), Colemak-stílus"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"angol (USA), Dvorak-stílus"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"angol (USA), „Workman” stílus"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"német"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"francia"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"francia (Kanada)"</string>
diff --git a/packages/InputDevices/res/values-hy-rAM/strings.xml b/packages/InputDevices/res/values-hy-rAM/strings.xml
index 9ffa8bb..0d11645 100644
--- a/packages/InputDevices/res/values-hy-rAM/strings.xml
+++ b/packages/InputDevices/res/values-hy-rAM/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Անգլերեն (ԱՄՆ), միջազգային տեսակ"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Անգլերեն (ԱՄՆ), Colemak տեսակ"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Անգլերեն (ԱՄՆ), Dvorak տեսակ"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Անգլերեն (ԱՄՆ), Workman տեսակ"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Գերմաներեն"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Ֆրանսերեն"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Ֆրանսերեն (Կանադա)"</string>
diff --git a/packages/InputDevices/res/values-in/strings.xml b/packages/InputDevices/res/values-in/strings.xml
index fccfa67..b2cbd6e 100644
--- a/packages/InputDevices/res/values-in/strings.xml
+++ b/packages/InputDevices/res/values-in/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Inggris (AS), gaya Internasional"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Inggris (AS), gaya Colemak"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Inggris (AS), gaya Dvorak"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Inggris (AS), gaya Workman"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Jerman"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Prancis"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Prancis (Kanada)"</string>
diff --git a/packages/InputDevices/res/values-is-rIS/strings.xml b/packages/InputDevices/res/values-is-rIS/strings.xml
index e48fa49..de91275 100644
--- a/packages/InputDevices/res/values-is-rIS/strings.xml
+++ b/packages/InputDevices/res/values-is-rIS/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Enskt (Bandaríkin), alþjóðlegt"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Enskt (Bandaríkin), Colemak-stíll"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Enskt (Bandaríkin), Dvorak-stíll"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Enska (Bandaríkin), Workman-stíll"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Þýskt"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Franskt"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Franskt (Kanada)"</string>
diff --git a/packages/InputDevices/res/values-it/strings.xml b/packages/InputDevices/res/values-it/strings.xml
index 83dba70..c1c7faa 100644
--- a/packages/InputDevices/res/values-it/strings.xml
+++ b/packages/InputDevices/res/values-it/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Inglese (USA), stile internazionale"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Inglese (USA), stile Colemak"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Inglese (USA), stile Dvorak"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Inglese (USA), stile Workman"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Tedesco"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Francese"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Francese (Canada)"</string>
diff --git a/packages/InputDevices/res/values-iw/strings.xml b/packages/InputDevices/res/values-iw/strings.xml
index 26fe662..b3bd576 100644
--- a/packages/InputDevices/res/values-iw/strings.xml
+++ b/packages/InputDevices/res/values-iw/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"אנגלית (ארה\"ב), סגנון בינ\"ל"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"אנגלית (ארה\"ב), סגנון Colemak"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"אנגלית (ארה\"ב), סגנון Dvorak"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"אנגלית (ארה\"ב), סגנון Workman"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"גרמנית"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"צרפתית"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"צרפתית (קנדה)"</string>
diff --git a/packages/InputDevices/res/values-ja/strings.xml b/packages/InputDevices/res/values-ja/strings.xml
index e2b154d..2b3daf5 100644
--- a/packages/InputDevices/res/values-ja/strings.xml
+++ b/packages/InputDevices/res/values-ja/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"英語(アメリカ)、インターナショナル配列"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"英語(アメリカ)、Colemak配列"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"英語(アメリカ)、Dvorak配列"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"英語(アメリカ)、Workman配列"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"ドイツ語"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"フランス語"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"フランス語(カナダ)"</string>
diff --git a/packages/InputDevices/res/values-ka-rGE/strings.xml b/packages/InputDevices/res/values-ka-rGE/strings.xml
index eff4b04..66d147e 100644
--- a/packages/InputDevices/res/values-ka-rGE/strings.xml
+++ b/packages/InputDevices/res/values-ka-rGE/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"ინგლისური (აშშ), საერთაშორისო სტილი"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"ინგლისური (აშშ), Colemak სტილი"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"ინგლისური (აშშ), Dvorak სტილი"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"ინგლისური (აშშ), Workman სტილი"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"გერმანული"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"ფრანგული"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"ფრანგული (კანადა)"</string>
diff --git a/packages/InputDevices/res/values-kk-rKZ/strings.xml b/packages/InputDevices/res/values-kk-rKZ/strings.xml
index 7086bf7..d253542 100644
--- a/packages/InputDevices/res/values-kk-rKZ/strings.xml
+++ b/packages/InputDevices/res/values-kk-rKZ/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Ағылшын (АҚШ), Халықаралық стилі"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Ағылшын (АҚШ), Colemak стилі"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Ағылшын (АҚШ), Dvorak стилі"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Ағылшын (АҚШ), Workman стилі"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Неміс"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Француз"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Француз (Канада)"</string>
diff --git a/packages/InputDevices/res/values-km-rKH/strings.xml b/packages/InputDevices/res/values-km-rKH/strings.xml
index 60a28b1..acd01ee 100644
--- a/packages/InputDevices/res/values-km-rKH/strings.xml
+++ b/packages/InputDevices/res/values-km-rKH/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"អង់គ្លេស (សហរដ្ឋអាមេរិក), រចនាប័ទ្មអន្តរជាតិ"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"អង់គ្លេស (សហរដ្ឋអាមេរិក), រចនាប័ទ្ម Colemak"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"អង់គ្លេស (សហរដ្ឋអាមេរិក), រចនាប័ទ្ម Dvorak"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"អង់គ្លេស (អាមេរិក) រចនាបទ Workman"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"អាល្លឺម៉ង់"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"បារាំង"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"បារាំង (កាណាដា)"</string>
diff --git a/packages/InputDevices/res/values-kn-rIN/strings.xml b/packages/InputDevices/res/values-kn-rIN/strings.xml
index 6b43517..243e6597 100644
--- a/packages/InputDevices/res/values-kn-rIN/strings.xml
+++ b/packages/InputDevices/res/values-kn-rIN/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"ಇಂಗ್ಲಿಷ್ (US), ಅಂತರರಾಷ್ಟ್ರೀಯ ಶೈಲಿ"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"ಇಂಗ್ಲಿಷ್ (US), ಕೋಲ್ಮಾರ್ಕ್ ಶೈಲಿ"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"ಇಂಗ್ಲಿಷ್ (US), ಡಿವೊರಾಕ್ ಶೈಲಿ"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"ಇಂಗ್ಲೀಷ್ (US), ವರ್ಕ್ಮನ್ ಶೈಲಿ"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"ಜರ್ಮನ್"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"ಫ್ರೆಂಚ್"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"ಫ್ರೆಂಚ್ (ಕೆನಡಾ)"</string>
diff --git a/packages/InputDevices/res/values-ko/strings.xml b/packages/InputDevices/res/values-ko/strings.xml
index 3f563d1..7758210 100644
--- a/packages/InputDevices/res/values-ko/strings.xml
+++ b/packages/InputDevices/res/values-ko/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"영어(미국), 글로벌 스타일"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"영어(미국), 콜맥 스타일"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"영어(미국), 드보락 스타일"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"영어(미국), 워크맨 스타일"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"독일어"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"프랑스어"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"프랑스어(캐나다)"</string>
diff --git a/packages/InputDevices/res/values-ky-rKG/strings.xml b/packages/InputDevices/res/values-ky-rKG/strings.xml
index 0828222..aa74733 100644
--- a/packages/InputDevices/res/values-ky-rKG/strings.xml
+++ b/packages/InputDevices/res/values-ky-rKG/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Англис (АКШ), эл аралык"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Англис (АКШ), Colemak стили"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Англис (АКШ), Dvorak стили"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Англисче (АКШ), Workman стили"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Немис"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Француз"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Француз (Канада)"</string>
diff --git a/packages/InputDevices/res/values-lo-rLA/strings.xml b/packages/InputDevices/res/values-lo-rLA/strings.xml
index fb3fe17..05b1b83 100644
--- a/packages/InputDevices/res/values-lo-rLA/strings.xml
+++ b/packages/InputDevices/res/values-lo-rLA/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"ອັງກິດ (ສະຫະລັດຯ), ແບບສາກົນ"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"ອັງກິດ (ສະຫະລັດຯ), ແບບ Colemak"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"ອັງກິດ (ສະຫະລັດຯ), ແບບ Dvorak"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"ອັງກິດ (ສະຫະລັດ), ແບບຄົນເຮັດວຽກ"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"ເຢຍລະມັນ"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"ຝຣັ່ງ"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"ຝຣັ່ງ (ຄານາດາ)"</string>
diff --git a/packages/InputDevices/res/values-lt/strings.xml b/packages/InputDevices/res/values-lt/strings.xml
index d0eb1f6..1dae850 100644
--- a/packages/InputDevices/res/values-lt/strings.xml
+++ b/packages/InputDevices/res/values-lt/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Anglų k. (JAV), tarptautinis stilius"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Anglų k. (JAV), „Colemak“ stilius"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Anglų k. (JAV), „Dvorak“ stilius"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Anglų k. (JAV), „Workman“ stilius"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Vokiečių k."</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Prancūzų k."</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Prancūzų k. (Kanada)"</string>
diff --git a/packages/InputDevices/res/values-lv/strings.xml b/packages/InputDevices/res/values-lv/strings.xml
index 0608bf0..96dc53b 100644
--- a/packages/InputDevices/res/values-lv/strings.xml
+++ b/packages/InputDevices/res/values-lv/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Angļu (ASV), starptautiska"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Angļu (ASV), Colemak"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Angļu (ASV), Dvorak"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Angļu (ASV), Workman"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Vācu"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Franču"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Franču (Kanāda)"</string>
diff --git a/packages/InputDevices/res/values-mk-rMK/strings.xml b/packages/InputDevices/res/values-mk-rMK/strings.xml
index 8662984..2fae1cf 100644
--- a/packages/InputDevices/res/values-mk-rMK/strings.xml
+++ b/packages/InputDevices/res/values-mk-rMK/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Англиски (САД), меѓународен стил"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Англиски (САД), Colemak стил"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Англиски (САД), Dvorak стил"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Англиски (САД), Workman стил"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Германски"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Француски"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Француски (Канада)"</string>
diff --git a/packages/InputDevices/res/values-ml-rIN/strings.xml b/packages/InputDevices/res/values-ml-rIN/strings.xml
index 4f846a0..0faa40e 100644
--- a/packages/InputDevices/res/values-ml-rIN/strings.xml
+++ b/packages/InputDevices/res/values-ml-rIN/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"ഇംഗ്ലീഷ് (യു.എസ്), അന്തർദ്ദേശീയ ശൈലി"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"ഇംഗ്ലീഷ് (യു.എസ്), കോൽമാക് ശൈലി"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"ഇംഗ്ലീഷ് (യു.എസ്), ദ്വരോക്ക് ശൈലി"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"ഇംഗ്ലീഷ് (യുഎസ്), വർക്ക്മാൻ ശൈലി"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"ജര്മ്മന്"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"ഫ്രഞ്ച്"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"ഫ്രഞ്ച് (കാനഡ)"</string>
diff --git a/packages/InputDevices/res/values-mn-rMN/strings.xml b/packages/InputDevices/res/values-mn-rMN/strings.xml
index a28fd2a..f1354fe 100644
--- a/packages/InputDevices/res/values-mn-rMN/strings.xml
+++ b/packages/InputDevices/res/values-mn-rMN/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Англи (АНУ), Олон улсын стиль"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Англи (АНУ), Колемак стиль"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Англи (АНУ), Дворак стиль"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Aнгли хэл (АНУ), Aжилчин загвар"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Герман"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Франц"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Франц (Канад)"</string>
diff --git a/packages/InputDevices/res/values-mr-rIN/strings.xml b/packages/InputDevices/res/values-mr-rIN/strings.xml
index 119ae6c..9ffcc70 100644
--- a/packages/InputDevices/res/values-mr-rIN/strings.xml
+++ b/packages/InputDevices/res/values-mr-rIN/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"इंग्रजी (यूएस), आंतरराष्ट्रीय शैली"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"इंग्रजी (यूएस), कोलमॅक शैली"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"इंग्रजी (यूएस), ड्वोरॅक शैली"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"इंग्रजी (यूएस), कामगार शैली"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"जर्मन"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"फ्रेंच"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"फ्रेंच (कॅनडा)"</string>
diff --git a/packages/InputDevices/res/values-ms-rMY/strings.xml b/packages/InputDevices/res/values-ms-rMY/strings.xml
index a1a6d00..04983e3 100644
--- a/packages/InputDevices/res/values-ms-rMY/strings.xml
+++ b/packages/InputDevices/res/values-ms-rMY/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Bahasa Inggeris (AS), gaya A/bangsa"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Bahasa Inggeris (AS), gaya Colemak"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Bahasa Inggeris (AS), gaya Dvorak"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Bahasa Inggeris (AS), gaya Workman"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Bahasa Jerman"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Bahasa Perancis"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Bahasa Perancis (Kanada)"</string>
diff --git a/packages/InputDevices/res/values-my-rMM/strings.xml b/packages/InputDevices/res/values-my-rMM/strings.xml
index 91bdc55..f3762b4 100644
--- a/packages/InputDevices/res/values-my-rMM/strings.xml
+++ b/packages/InputDevices/res/values-my-rMM/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"အင်္ဂလိပ်(ယူအက်စ်)၊အပြည်ပြည်ဆိုင်ရာပုံစံ"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"အင်္ဂလိပ်(ယူအက်စ်)၊Colemak ပုံစံ"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"အင်္ဂလိပ် (ယူအက်စ်)၊Dvorak ပုံစံ"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"အင်္ဂလိပ် (ယူအက်စ်)၊ အလုပ်လုပ်သူ ပုံစံ"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"ဂျာမန်"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"ပြင်သစ်"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"ပြင်သစ် (ကနေဒါ)"</string>
diff --git a/packages/InputDevices/res/values-nb/strings.xml b/packages/InputDevices/res/values-nb/strings.xml
index ad4b704..37604e3 100644
--- a/packages/InputDevices/res/values-nb/strings.xml
+++ b/packages/InputDevices/res/values-nb/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Engelsk (USA), internasjonal stil"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Engelsk (USA), Colemak-stil"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Engelsk (USA), Dvorak-stil"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Engelsk (USA), workman-stil"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Tysk"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Fransk"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Fransk (Canada)"</string>
diff --git a/packages/InputDevices/res/values-ne-rNP/strings.xml b/packages/InputDevices/res/values-ne-rNP/strings.xml
index 4e5c2b5..4c3dec3 100644
--- a/packages/InputDevices/res/values-ne-rNP/strings.xml
+++ b/packages/InputDevices/res/values-ne-rNP/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"अङ्ग्रेजी (अमेरिकी), अन्तर्राष्ट्रिय शैली"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"अङ्ग्रेजी (अमेरिकी), कोलमाक शैली"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"अङ्ग्रेजी (अमेरिकी), डेभोर्याक शैली"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"अंग्रेजी (अमेरिका), वर्कम्यान शैली"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"जर्मन"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"फ्रान्सेली"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"फ्रेंच (क्यानाडा)"</string>
diff --git a/packages/InputDevices/res/values-nl/strings.xml b/packages/InputDevices/res/values-nl/strings.xml
index c57251e..31ade05 100644
--- a/packages/InputDevices/res/values-nl/strings.xml
+++ b/packages/InputDevices/res/values-nl/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Engels (VS), internationaal"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Engels (VS), Colemak"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Engels (VS), Dvorak"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Engels (VS), Workman"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Duits"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Frans"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Frans (Canada)"</string>
diff --git a/packages/InputDevices/res/values-pa-rIN/strings.xml b/packages/InputDevices/res/values-pa-rIN/strings.xml
index a885088..437352c 100644
--- a/packages/InputDevices/res/values-pa-rIN/strings.xml
+++ b/packages/InputDevices/res/values-pa-rIN/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"ਅੰਗ੍ਰੇਜ਼ੀ (ਅਮਰੀਕਾ), ਅੰਤਰਰਾਸ਼ਟਰੀ ਸਟਾਈਲ"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"ਅੰਗ੍ਰੇਜ਼ੀ (ਅਮਰੀਕਾ), ਕੋਲਮਾਰਕ ਸਟਾਈਲ"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"ਅੰਗ੍ਰੇਜ਼ੀ (ਅਮਰੀਕਾ), ਵੋਰਕ ਸਟਾਈਲ"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"ਅੰਗਰੇਜ਼ੀ (US), ਵਰਕਮੈਨ ਸਟਾਈਲ"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"ਜਰਮਨ"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"ਫਰਾਂਸੀਸੀ"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"ਫ੍ਰੈਂਚ (ਕੈਨੇਡਾ)"</string>
diff --git a/packages/InputDevices/res/values-pl/strings.xml b/packages/InputDevices/res/values-pl/strings.xml
index 39fb3ec..2ae815e32 100644
--- a/packages/InputDevices/res/values-pl/strings.xml
+++ b/packages/InputDevices/res/values-pl/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Angielski (USA), międzynarodowy"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Angielski (USA), Colemak"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Angielski (USA), Dvorak"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Angielski (USA), Workman"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Niemiecki"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Francuski"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Francuski (Kanada)"</string>
diff --git a/packages/InputDevices/res/values-pt-rBR/strings.xml b/packages/InputDevices/res/values-pt-rBR/strings.xml
index e9a0a38..a1503a4 100644
--- a/packages/InputDevices/res/values-pt-rBR/strings.xml
+++ b/packages/InputDevices/res/values-pt-rBR/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Inglês (EUA), estilo internacional"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Inglês (EUA), estilo Colemak"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Inglês (EUA), estilo Dvorak"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Inglês (EUA), estilo Workman"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Alemão"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Francês"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Francês (Canadá)"</string>
diff --git a/packages/InputDevices/res/values-pt-rPT/strings.xml b/packages/InputDevices/res/values-pt-rPT/strings.xml
index 3ac3b84..b806fc2 100644
--- a/packages/InputDevices/res/values-pt-rPT/strings.xml
+++ b/packages/InputDevices/res/values-pt-rPT/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Inglês (EUA), est. Internacional"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Inglês (EUA), estilo Colemak"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Inglês (EUA), estilo Dvorak"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Inglês (EUA), estilo Workman"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Alemão"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Francês"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Francês (Canadá)"</string>
diff --git a/packages/InputDevices/res/values-pt/strings.xml b/packages/InputDevices/res/values-pt/strings.xml
index e9a0a38..a1503a4 100644
--- a/packages/InputDevices/res/values-pt/strings.xml
+++ b/packages/InputDevices/res/values-pt/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Inglês (EUA), estilo internacional"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Inglês (EUA), estilo Colemak"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Inglês (EUA), estilo Dvorak"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Inglês (EUA), estilo Workman"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Alemão"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Francês"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Francês (Canadá)"</string>
diff --git a/packages/InputDevices/res/values-ro/strings.xml b/packages/InputDevices/res/values-ro/strings.xml
index c2392b1..795e9a2 100644
--- a/packages/InputDevices/res/values-ro/strings.xml
+++ b/packages/InputDevices/res/values-ro/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Engleză (SUA), stil internațional"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Engleză (SUA), stil Colemak"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Engleză (SUA), stil Dvorak"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Engleză (SUA), stil Workman"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Germană"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Franceză"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Franceză (Canada)"</string>
diff --git a/packages/InputDevices/res/values-ru/strings.xml b/packages/InputDevices/res/values-ru/strings.xml
index 70ecf6e..ac4c81b 100644
--- a/packages/InputDevices/res/values-ru/strings.xml
+++ b/packages/InputDevices/res/values-ru/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"английский (США, международная)"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"английский (США, Colemak)"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"английский (США, Dvorak)"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"английский (США, Workman)"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"немецкий"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"французский"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"французский (Канада)"</string>
diff --git a/packages/InputDevices/res/values-si-rLK/strings.xml b/packages/InputDevices/res/values-si-rLK/strings.xml
index 67563c4..945a097 100644
--- a/packages/InputDevices/res/values-si-rLK/strings.xml
+++ b/packages/InputDevices/res/values-si-rLK/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"ඉංග්රීසි (US), අන්තර්ජාතික ආකාරය"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"ඉංග්රීසි (US), Colemak ආකාරය"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"ඉංග්රීසි (US), Dvorak ආකාරය"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"ඉංග්රීසි (එ.ජ.), වර්ක්මන් ආකාරය"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"ජර්මානු"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"ප්රංශ"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"ප්රංශ (කැනඩාව)"</string>
diff --git a/packages/InputDevices/res/values-sk/strings.xml b/packages/InputDevices/res/values-sk/strings.xml
index d2ee0cf..2e76024f 100644
--- a/packages/InputDevices/res/values-sk/strings.xml
+++ b/packages/InputDevices/res/values-sk/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"anglické (USA), medzinárodné"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"anglické (USA), štýl Colemak"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"anglické (USA), štýl Dvorak"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"anglické (USA), štýl Workman"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"nemecké"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"francúzske"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"francúzske (Kanada)"</string>
diff --git a/packages/InputDevices/res/values-sl/strings.xml b/packages/InputDevices/res/values-sl/strings.xml
index 38542ef..a643c8e 100644
--- a/packages/InputDevices/res/values-sl/strings.xml
+++ b/packages/InputDevices/res/values-sl/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"angleška (ZDA), mednarodni slog"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"angleška (ZDA), slog Colemak"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"angleška (ZDA), slog Dvorak"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"angleška (ZDA), slog Workman"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"nemška"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"francoska"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"francoska (Kanada)"</string>
diff --git a/packages/InputDevices/res/values-sq-rAL/strings.xml b/packages/InputDevices/res/values-sq-rAL/strings.xml
index 2092926..8a9000d 100644
--- a/packages/InputDevices/res/values-sq-rAL/strings.xml
+++ b/packages/InputDevices/res/values-sq-rAL/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"anglisht (SHBA), stili \"ndërkombëtar\""</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"anglisht (SHBA), stili \"colemak\""</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"anglisht (SHBA), stili \"dvorak\""</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"anglisht (SHBA), stili \"workman\""</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"gjermanisht"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"frëngjisht"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"frëngjisht (Kanada)"</string>
diff --git a/packages/InputDevices/res/values-sr/strings.xml b/packages/InputDevices/res/values-sr/strings.xml
index dd500e6..b06f6fc 100644
--- a/packages/InputDevices/res/values-sr/strings.xml
+++ b/packages/InputDevices/res/values-sr/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"енглеска (САД), међународни стил"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"енглеска (САД), Colemak стил"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"енглеска (САД), Dvorak стил"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"енглеска (САД), Workman стил"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"немачка"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"француска"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"француска (Канада)"</string>
diff --git a/packages/InputDevices/res/values-sv/strings.xml b/packages/InputDevices/res/values-sv/strings.xml
index c2406c0..89cb54e 100644
--- a/packages/InputDevices/res/values-sv/strings.xml
+++ b/packages/InputDevices/res/values-sv/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Engelskt (USA), internationellt"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Engelskt (USA), colemak"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Engelskt (USA), dvorak"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Engelskt (USA), workman"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Tyskt"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Franskt"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Franskt (Kanada)"</string>
diff --git a/packages/InputDevices/res/values-sw/strings.xml b/packages/InputDevices/res/values-sw/strings.xml
index f71a696..1f447b0 100644
--- a/packages/InputDevices/res/values-sw/strings.xml
+++ b/packages/InputDevices/res/values-sw/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Kiingereza (Marekani), Muundo wa Kimataifa"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Kiingereza (Marekani), Muundo wa Colemak"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Kiingereza (Marekani), Muundo wa Dvorak"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Kiingereza (US), mtindo wa Workman"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Kijerumani"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Kifaransa"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Kifaransa (Kanada)"</string>
diff --git a/packages/InputDevices/res/values-ta-rIN/strings.xml b/packages/InputDevices/res/values-ta-rIN/strings.xml
index 30d653d..32efe7b 100644
--- a/packages/InputDevices/res/values-ta-rIN/strings.xml
+++ b/packages/InputDevices/res/values-ta-rIN/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"ஆங்கிலம் (யூஎஸ்), சர்வதேச நடை"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"ஆங்கிலம் (யூஎஸ்), கோல்மாக் நடை"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"ஆங்கிலம் (யூஎஸ்), டிவாரக் நடை"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"ஆங்கிலம் (யூஎஸ்), வொர்க்மென் நடை"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"ஜெர்மன்"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"ஃபிரெஞ்ச்"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"ஃபிரெஞ்ச் (கனடா)"</string>
diff --git a/packages/InputDevices/res/values-te-rIN/strings.xml b/packages/InputDevices/res/values-te-rIN/strings.xml
index cc1b14d..e07d4c8 100644
--- a/packages/InputDevices/res/values-te-rIN/strings.xml
+++ b/packages/InputDevices/res/values-te-rIN/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"ఇంగ్లీష్ (US), అంతర్జాతీయ శైలి"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"ఇంగ్లీష్ (US), కొల్మాక్ శైలి"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"ఇంగ్లీష్ (US), ద్వోరక్ శైలి"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"ఆంగ్లం (US), వర్క్మాన్ శైలి"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"జర్మన్"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"ఫ్రెంచ్"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"ఫ్రెంచ్ (కెనడా)"</string>
diff --git a/packages/InputDevices/res/values-th/strings.xml b/packages/InputDevices/res/values-th/strings.xml
index 487b051..50dc1e4 100644
--- a/packages/InputDevices/res/values-th/strings.xml
+++ b/packages/InputDevices/res/values-th/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"อังกฤษ (อเมริกัน), รูปแบบนานาชาติ"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"อังกฤษ (อเมริกัน), รูปแบบ Colemak"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"อังกฤษ (อเมริกัน), รูปแบบ Dvorak"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"อังกฤษ (อเมริกัน), รูปแบบ Workman"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"เยอรมัน"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"ฝรั่งเศส"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"ฝรั่งเศส (แคนาดา)"</string>
diff --git a/packages/InputDevices/res/values-tl/strings.xml b/packages/InputDevices/res/values-tl/strings.xml
index d7920ed..4bd857d 100644
--- a/packages/InputDevices/res/values-tl/strings.xml
+++ b/packages/InputDevices/res/values-tl/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Ingles (US), istilong International"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Ingles (US), istilong Colemak"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Ingles (US), istilong Dvorak"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"English (US), Workman style"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"German"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"French"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"French (Canada)"</string>
diff --git a/packages/InputDevices/res/values-tr/strings.xml b/packages/InputDevices/res/values-tr/strings.xml
index c0c70be..a8d9a0f 100644
--- a/packages/InputDevices/res/values-tr/strings.xml
+++ b/packages/InputDevices/res/values-tr/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"İngilizce (ABD) Uluslararası stil"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"İngilizce (ABD) Colemak stili"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"İngilizce (ABD) Dvorak stili"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"İngilizce (ABD), Workman stili"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Almanca"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Fransızca"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Fransızca (Kanada)"</string>
diff --git a/packages/InputDevices/res/values-uk/strings.xml b/packages/InputDevices/res/values-uk/strings.xml
index 43a3fe6..c818001 100644
--- a/packages/InputDevices/res/values-uk/strings.xml
+++ b/packages/InputDevices/res/values-uk/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"англійська (США), міжнародна"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"англійська (США), розкладка Colemak"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"англійська (США), розкладка Дворака"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"англійська (США), розкладка Workman"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"німецька"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"французька"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"французька (Канада)"</string>
diff --git a/packages/InputDevices/res/values-ur-rPK/strings.xml b/packages/InputDevices/res/values-ur-rPK/strings.xml
index eee1eb2..3d2f618 100644
--- a/packages/InputDevices/res/values-ur-rPK/strings.xml
+++ b/packages/InputDevices/res/values-ur-rPK/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"انگریزی (امریکہ)، انٹرنیشنل سٹائل"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"انگریزی (امریکہ)، کول مارک سٹائل"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"انگریزی (امریکہ)، ڈوراک سٹائل"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"انگریزی (US)، ورک مین اسٹائل"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"جرمن"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"فرانسیسی"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"فرانسیسی (کینیڈا)"</string>
diff --git a/packages/InputDevices/res/values-uz-rUZ/strings.xml b/packages/InputDevices/res/values-uz-rUZ/strings.xml
index 3b6772b..9c55615 100644
--- a/packages/InputDevices/res/values-uz-rUZ/strings.xml
+++ b/packages/InputDevices/res/values-uz-rUZ/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Inglizcha (AQSH), xalqaro uslubda"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Inglizcha (AQSH), Kolemak uslubida"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Inglizcha (AQSH), Dvorak uslubida"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Ingliz (AQSH), ishchi uslubda"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Nemischa"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Fransuzcha"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Fransuzcha (Kanada)"</string>
diff --git a/packages/InputDevices/res/values-vi/strings.xml b/packages/InputDevices/res/values-vi/strings.xml
index a90c1cd..3ca715b 100644
--- a/packages/InputDevices/res/values-vi/strings.xml
+++ b/packages/InputDevices/res/values-vi/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Tiếng Anh (Mỹ), kiểu Quốc tế"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Tiếng Anh (Mỹ), kiểu Colemak"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Tiếng Anh (Mỹ), kiểu Dvorak"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Tiếng Anh (Mỹ), kiểu Workman"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Tiếng Đức"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Tiếng Pháp"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Tiếng Pháp (Canada)"</string>
diff --git a/packages/InputDevices/res/values-zh-rCN/strings.xml b/packages/InputDevices/res/values-zh-rCN/strings.xml
index c050ebd..c61dccb 100644
--- a/packages/InputDevices/res/values-zh-rCN/strings.xml
+++ b/packages/InputDevices/res/values-zh-rCN/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"英语(美国),国际风格"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"英语(美国),Colemak 风格"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"英语(美国),Dvorak 风格"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"英语(美国),Workman 风格"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"德语"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"法语"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"法语(加拿大)"</string>
diff --git a/packages/InputDevices/res/values-zh-rHK/strings.xml b/packages/InputDevices/res/values-zh-rHK/strings.xml
index ff5570e..9385df8 100644
--- a/packages/InputDevices/res/values-zh-rHK/strings.xml
+++ b/packages/InputDevices/res/values-zh-rHK/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"英文 (美國),國際樣式"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"英文 (美國),Colemak 樣式"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"英文 (美國),Dvorak 樣式"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"英文 (美國),Workman 樣式"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"德文"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"法文"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"法文 (加拿大)"</string>
diff --git a/packages/InputDevices/res/values-zh-rTW/strings.xml b/packages/InputDevices/res/values-zh-rTW/strings.xml
index 859983d..89ef558 100644
--- a/packages/InputDevices/res/values-zh-rTW/strings.xml
+++ b/packages/InputDevices/res/values-zh-rTW/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"英文 (美國),國際樣式"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"英文 (美國),Colemak 樣式"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"英文 (美國),Dvorak 樣式"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"英文 (美國),Workman 樣式"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"德文"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"法文"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"法文 (加拿大)"</string>
diff --git a/packages/InputDevices/res/values-zu/strings.xml b/packages/InputDevices/res/values-zu/strings.xml
index 9f30f7a..0dcffb0 100644
--- a/packages/InputDevices/res/values-zu/strings.xml
+++ b/packages/InputDevices/res/values-zu/strings.xml
@@ -8,6 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"I-English (US), isitayela sakwamanye amazwe"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"I-English (US), isitayela se-Colemak"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"I-English (US), isitayela se-Dvorak"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"I-English (US), isitayela sokusebenza"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Isi-German"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Isi-French"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Isi-French (Canada)"</string>
diff --git a/packages/InputDevices/res/values/strings.xml b/packages/InputDevices/res/values/strings.xml
index 968961a..5644c9a 100644
--- a/packages/InputDevices/res/values/strings.xml
+++ b/packages/InputDevices/res/values/strings.xml
@@ -21,6 +21,9 @@
<!-- US English (Dvorak style) keyboard layout label. [CHAR LIMIT=35] -->
<string name="keyboard_layout_english_us_dvorak_label">English (US), Dvorak style</string>
+ <!-- US English (Workman style) keyboard layout label. [CHAR LIMIT=35] -->
+ <string name="keyboard_layout_english_us_workman_label">English (US), Workman style</string>
+
<!-- German keyboard layout label. [CHAR LIMIT=35] -->
<string name="keyboard_layout_german_label">German</string>
diff --git a/packages/InputDevices/res/xml/keyboard_layouts.xml b/packages/InputDevices/res/xml/keyboard_layouts.xml
index 6f7253c..a302162 100644
--- a/packages/InputDevices/res/xml/keyboard_layouts.xml
+++ b/packages/InputDevices/res/xml/keyboard_layouts.xml
@@ -20,6 +20,10 @@
android:label="@string/keyboard_layout_english_us_dvorak_label"
android:keyboardLayout="@raw/keyboard_layout_english_us_dvorak" />
+ <keyboard-layout android:name="keyboard_layout_english_us_workman"
+ android:label="@string/keyboard_layout_english_us_workman_label"
+ android:keyboardLayout="@raw/keyboard_layout_english_us_workman" />
+
<keyboard-layout android:name="keyboard_layout_german"
android:label="@string/keyboard_layout_german_label"
android:keyboardLayout="@raw/keyboard_layout_german" />
diff --git a/packages/Keyguard/AndroidManifest.xml b/packages/Keyguard/AndroidManifest.xml
index e19246c..54972b4 100644
--- a/packages/Keyguard/AndroidManifest.xml
+++ b/packages/Keyguard/AndroidManifest.xml
@@ -45,7 +45,9 @@
<application android:label="@string/app_name"
android:process="com.android.systemui"
android:persistent="true"
- android:supportsRtl="true">
+ android:supportsRtl="true"
+ android:forceDeviceEncrypted="true"
+ android:encryptionAware="true">
</application>
</manifest>
diff --git a/packages/Keyguard/res/values-hu/strings.xml b/packages/Keyguard/res/values-hu/strings.xml
index 6f8f189..55cf24f 100644
--- a/packages/Keyguard/res/values-hu/strings.xml
+++ b/packages/Keyguard/res/values-hu/strings.xml
@@ -24,11 +24,11 @@
<string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Írja be a PIN kódot"</string>
<string name="keyguard_password_enter_puk_code" msgid="3035856550289724338">"Írja be a SIM kártya PUK kódját, majd az új PIN kódot"</string>
<string name="keyguard_password_enter_puk_prompt" msgid="1801941051094974609">"SIM kártya PUK kódja"</string>
- <string name="keyguard_password_enter_pin_prompt" msgid="3201151840570492538">"Új PIN kód a SIM kártyához"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="3201151840570492538">"Új PIN-kód a SIM kártyához"</string>
<string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Érintsen jelszó megadásához"</font></string>
<string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"A feloldáshoz írja be a jelszót"</string>
<string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Feloldáshoz írja be a PIN kódot"</string>
- <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Helytelen PIN kód."</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Helytelen PIN-kód."</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Feltöltve"</string>
<string name="keyguard_plugged_in" msgid="9087497435553252863">"Töltés"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="6671162730167305479">"Gyors töltés folyamatban"</string>
@@ -51,8 +51,8 @@
<string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Feloldás jelszóval"</string>
<string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Mintaterület"</string>
<string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Csúsztatási terület"</string>
- <string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN kód területe"</string>
- <string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"SIM PIN kód területe"</string>
+ <string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN-kód területe"</string>
+ <string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"SIM PIN-kód területe"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"SIM PUK kód területe"</string>
<string name="keyguard_accessibility_next_alarm" msgid="7269583073750518672">"A következő riasztás beállított ideje: <xliff:g id="ALARM">%1$s</xliff:g>"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
@@ -60,7 +60,7 @@
<string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Elfelejtett minta"</string>
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Helytelen minta"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Helytelen jelszó"</string>
- <string name="kg_wrong_pin" msgid="1131306510833563801">"Helytelen PIN kód"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Helytelen PIN-kód"</string>
<string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Próbálkozzon újra <xliff:g id="NUMBER">%d</xliff:g> másodperc múlva."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Rajzolja le a mintát"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Adja meg a SIM kártya PIN kódját"</string>
@@ -69,8 +69,8 @@
<string name="kg_password_instructions" msgid="5753646556186936819">"Írja be a jelszót"</string>
<string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"A SIM kártya le van tiltva. A folytatáshoz adja meg a PUK kódot. A részletekért vegye fel a kapcsolatot szolgáltatójával."</string>
<string name="kg_puk_enter_puk_hint_multi" msgid="363822494559783025">"A(z) „<xliff:g id="CARRIER">%1$s</xliff:g>” SIM kártyát a rendszer letiltotta. A folytatáshoz adja meg a PUK kódot. A részletekért vegye a fel a kapcsolatot szolgáltatójával."</string>
- <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Kívánt PIN kód megadása"</string>
- <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Kívánt PIN kód megerősítése"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Kívánt PIN-kód megadása"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Kívánt PIN-kód megerősítése"</string>
<string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"SIM kártya feloldása..."</string>
<string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"4–8 számjegyű PIN kódot írjon be."</string>
<string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"A PUK kód legalább 8 számjegyből kell, hogy álljon."</string>
@@ -94,7 +94,7 @@
<string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4951507352869831265">"<xliff:g id="NUMBER">%d</xliff:g> alkalommal próbálkozott sikertelenül a telefon zárolásának feloldásával. A rendszer eltávolítja munkahelyi profilját, és összes profiladata törlődik."</string>
<string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"<xliff:g id="NUMBER_0">%d</xliff:g> alkalommal helytelenül rajzolta le a feloldási mintát. További <xliff:g id="NUMBER_1">%d</xliff:g> sikertelen kísérlet után egy e-mail fiók használatával kell feloldania a táblagépét.\n\n Kérjük, próbálja újra <xliff:g id="NUMBER_2">%d</xliff:g> másodperc múlva."</string>
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"<xliff:g id="NUMBER_0">%d</xliff:g> alkalommal helytelenül rajzolta le a feloldási mintát. További <xliff:g id="NUMBER_1">%d</xliff:g> sikertelen kísérlet után egy e-mail fiók használatával kell feloldania a telefonját.\n\n Kérjük, próbálja újra <xliff:g id="NUMBER_2">%d</xliff:g> másodperc múlva."</string>
- <string name="kg_password_wrong_pin_code_pukked" msgid="30531039455764924">"Helytelen PIN kód a SIM kártyához; vegye fel a kapcsolatot szolgáltatójával az eszköz feloldásához."</string>
+ <string name="kg_password_wrong_pin_code_pukked" msgid="30531039455764924">"Helytelen PIN-kód a SIM kártyához; vegye fel a kapcsolatot szolgáltatójával az eszköz feloldásához."</string>
<plurals name="kg_password_wrong_pin_code" formatted="false" msgid="6721575017538162249">
<item quantity="other">A SIM kártya PIN kódja helytelen. <xliff:g id="NUMBER_1">%d</xliff:g> próbálkozás maradt.</item>
<item quantity="one">A SIM kártya PIN kódja helytelen. <xliff:g id="NUMBER_0">%d</xliff:g> próbálkozás maradt. Utána a szolgáltatótól kell feloldást kérnie.</item>
@@ -114,7 +114,7 @@
<string name="kg_prompt_reason_restart_pin" msgid="994878216570694974">"Ha újraindítja az eszközt, meg kell adnia a PIN kódot."</string>
<string name="kg_prompt_reason_restart_password" msgid="2375742919528461664">"Ha újraindítja az eszközt, meg kell adnia a jelszót."</string>
<string name="kg_prompt_reason_timeout_pattern" msgid="8930047492617900785">"Minta szükséges a nagyobb biztonság érdekében."</string>
- <string name="kg_prompt_reason_timeout_pin" msgid="7470468607947726377">"PIN kód szükséges a nagyobb biztonság érdekében."</string>
+ <string name="kg_prompt_reason_timeout_pin" msgid="7470468607947726377">"PIN-kód szükséges a nagyobb biztonság érdekében."</string>
<string name="kg_prompt_reason_timeout_password" msgid="1177412542773936957">"Jelszó szükséges a nagyobb biztonság érdekében."</string>
<string name="kg_prompt_reason_switch_profiles_pattern" msgid="3802056699323773969">"Ha vált a profilok között, meg kell adnia a mintát."</string>
<string name="kg_prompt_reason_switch_profiles_pin" msgid="8108020184731052246">"Ha vált a profilok között, meg kell adnia a PIN kódot."</string>
diff --git a/packages/Keyguard/res/values-ne-rNP/strings.xml b/packages/Keyguard/res/values-ne-rNP/strings.xml
index d18d0f2..4a5907e0 100644
--- a/packages/Keyguard/res/values-ne-rNP/strings.xml
+++ b/packages/Keyguard/res/values-ne-rNP/strings.xml
@@ -82,22 +82,22 @@
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"तपाईँले तपाईँको अनलक ढाँचा गलत तरिकाले <xliff:g id="NUMBER_0">%d</xliff:g> पटक खिच्नु भएको छ। \n\n <xliff:g id="NUMBER_1">%d</xliff:g> सेकेन्डमा फेरि कोसिस गर्नुहोस्।"</string>
<string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="8774056606869646621">"तपाईंले गलत तरिकाले ट्याब्लेट अनलक गर्ने प्रयास गर्नु भएको छ<xliff:g id="NUMBER_0">%d</xliff:g>पटक। <xliff:g id="NUMBER_1">%d</xliff:g> थप असफल प्रयासहरूपछि, यो ट्याब्लेट रिसेट हुनेछ जसले आफ्नो सम्पूर्ण डेटा मेट्नेछ।"</string>
<string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="1843331751334128428">"तपाईंले गलत तरिकाले फोन अनलक गर्ने प्रयास गर्नु भएको छ <xliff:g id="NUMBER_0">%d</xliff:g> पटक। पछि <xliff:g id="NUMBER_1">%d</xliff:g> थप असफल प्रयासहरूपछि, यो फोन रिसेट हुनेछ जसले सम्पूर्ण डेटा मेटाउनेछ।।"</string>
- <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="258925501999698032">"तपाईं गलत तरिकाले ट्याब्लेट अनलक गर्ने प्रयास गर्नु भएको छ <xliff:g id="NUMBER">%d</xliff:g> पटक। यो ट्याब्लेट रिेसेट गरिनेछ जसले सम्पूर्ण डेटा मेट्नेछ।"</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="258925501999698032">"तपाईँ गलत तरिकाले ट्याब्लेट अनलक गर्ने प्रयास गर्नु भएको छ <xliff:g id="NUMBER">%d</xliff:g> पटक। यो ट्याब्लेट रिेसेट गरिनेछ जसले सम्पूर्ण डेटा मेट्नेछ।"</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="7154028908459817066">"तपाईंले गलत तरिकाले फोन अनलक गर्ने प्रयास गर्नु भएको छ <xliff:g id="NUMBER">%d</xliff:g> पटक। यो फोन रिसेट गरिनेछ जसले सम्पूर्ण डेटा मेट्नेछ।"</string>
<string name="kg_failed_attempts_almost_at_erase_user" product="tablet" msgid="6159955099372112688">"तपाईंले गलत तरिकाले ट्याब्लेट अनलक गर्ने प्रयास गर्नु भएको छ<xliff:g id="NUMBER_0">%d</xliff:g> पटक। <xliff:g id="NUMBER_1">%d</xliff:g> थप असफल प्रयासहरूपछि, यो प्रयोगकर्ता हटाइनेछ जसले सम्पूर्ण प्रयोगकर्ता डेटा मेट्नेछ।"</string>
<string name="kg_failed_attempts_almost_at_erase_user" product="default" msgid="6945823186629369880">"तपाईंले गलत तरिकाले फोन अनलक गर्ने प्रयास गर्नु भएको छ <xliff:g id="NUMBER_0">%d</xliff:g> पटक। <xliff:g id="NUMBER_1">%d</xliff:g> थप असफल प्रयासहरूपछि, यो प्रयोगकर्ता हटाइनेछ जसले सबै प्रयोगकर्ता डेटा मेट्नेछ।"</string>
<string name="kg_failed_attempts_now_erasing_user" product="tablet" msgid="3963486905355778734">"तपाईंले गलत तरिकाले ट्याब्लेट अनलक गर्ने प्रयास गर्नु भएको छ<xliff:g id="NUMBER">%d</xliff:g> पटक। यो प्रयोगकर्ता हटाइनेछ जसले सम्पूर्ण प्रयोगकर्ता डेटा मेट्नेछ ।"</string>
<string name="kg_failed_attempts_now_erasing_user" product="default" msgid="7729009752252111673">"तपाईंले गलत तरिकाले फोन अनलक गर्ने प्रयास गर्नु भएको छ<xliff:g id="NUMBER">%d</xliff:g> पटक। यो प्रयोगकर्ता हटाइनेछ जसले सम्पूर्ण प्रयोगकर्ता डेटा मेट्नेछ।"</string>
- <string name="kg_failed_attempts_almost_at_erase_profile" product="tablet" msgid="4621778507387853694">"तपाईं गलत तरिकाले ट्याब्लेट अनलक गर्ने प्रयास गर्नु भएको छ<xliff:g id="NUMBER_0">%d</xliff:g> पटक। <xliff:g id="NUMBER_1">%d</xliff:g> थप असफल प्रयासहरूपछि, काम प्रोफाइल हटाइनेछ जसले सबै प्रोफाइल डेटा मेट्नेछ।"</string>
+ <string name="kg_failed_attempts_almost_at_erase_profile" product="tablet" msgid="4621778507387853694">"तपाईँ गलत तरिकाले ट्याब्लेट अनलक गर्ने प्रयास गर्नु भएको छ<xliff:g id="NUMBER_0">%d</xliff:g> पटक। <xliff:g id="NUMBER_1">%d</xliff:g> थप असफल प्रयासहरूपछि, काम प्रोफाइल हटाइनेछ जसले सबै प्रोफाइल डेटा मेट्नेछ।"</string>
<string name="kg_failed_attempts_almost_at_erase_profile" product="default" msgid="6853071165802933545">"तपाईंले गलत तरिकाले फोन अनलक गर्ने प्रयास गर्नु भएको छ <xliff:g id="NUMBER_0">%d</xliff:g> पटक। <xliff:g id="NUMBER_1">%d</xliff:g> थप असफल प्रयासहरूपछि , काम प्रोफाइल हटाइनेछ जसले सबै प्रोफाइल डेटा मेट्नेछ।"</string>
- <string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="4686386497449912146">"तपाईं गलत तरिकाले ट्याब्लेट अनलक गर्ने प्रयास गर्नु भएको छ<xliff:g id="NUMBER">%d</xliff:g> पटक। काम प्रोफाइल हटाइनेछ जसले सम्पूर्ण प्रोफाइल डेटा मेट्नेछ ।"</string>
+ <string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="4686386497449912146">"तपाईँ गलत तरिकाले ट्याब्लेट अनलक गर्ने प्रयास गर्नु भएको छ<xliff:g id="NUMBER">%d</xliff:g> पटक। काम प्रोफाइल हटाइनेछ जसले सम्पूर्ण प्रोफाइल डेटा मेट्नेछ ।"</string>
<string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4951507352869831265">"तपाईंले गलत तरिकाले फोन अनलक गर्ने प्रयास गर्नु भएको छ <xliff:g id="NUMBER">%d</xliff:g> पटक। काम प्रोफाइल हटाइनेछ जसले सम्पूर्ण प्रोफाइल डेटा मेट्नेछ।"</string>
<string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"तपाईंले गलत तरिकाले आफ्नो अनलक ढाँचा <xliff:g id="NUMBER_0">%d</xliff:g> पटक कोर्नुभयो। <xliff:g id="NUMBER_1">%d</xliff:g> विफल प्रयत्नहरू पछि, तपाईंलाई आफ्नो ट्याब्लेट इमेल खाता प्रयोग गरेर अनलक गर्न सोधिने छ।\n\n फेरि प्रयास गर्नुहोस् <xliff:g id="NUMBER_2">%d</xliff:g> सेकेन्डहरूमा।"</string>
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"तपाईँले आफ्नो अनलक ढाँचा गलत रूपमा <xliff:g id="NUMBER_0">%d</xliff:g> पटक तान्नु भएको छ। <xliff:g id="NUMBER_1">%d</xliff:g> धेरै असफल प्रयासहरूपछि, तपाईँलाई एउटा इमेल खाताको प्रयोग गरेर तपाईँको फोन अनलक गर्न सोधिने छ।\n\n फेरि <xliff:g id="NUMBER_2">%d</xliff:g> सेकेन्डमा प्रयास गर्नुहोस्।"</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="30531039455764924">"SIM PIN कोड गलत छ। अब तपाईंले अाफ्नो उपकरण खोल्नलाई तपाईंको वाहकसँग सम्पर्क गर्नै पर्दर।"</string>
<plurals name="kg_password_wrong_pin_code" formatted="false" msgid="6721575017538162249">
- <item quantity="other"> गलत SIM PIN कोड, तपाईं सँग <xliff:g id="NUMBER_1">%d</xliff:g> पटक प्रयास बाँकी छ।</item>
- <item quantity="one">SIM PIN कोड गलत छ, तपाईंले अाफ्नो यन्त्र खोल्नलाई तपाईंको वाहकसँग सम्पर्क गर्नै पर्न अघि तपाईंसँग <xliff:g id="NUMBER_0">%d</xliff:g> पटक प्रयास बाँकी छ।</item>
+ <item quantity="other"> गलत SIM PIN कोड, तपाईँ सँग <xliff:g id="NUMBER_1">%d</xliff:g> पटक प्रयास बाँकी छ।</item>
+ <item quantity="one">SIM PIN कोड गलत छ, तपाईँले अाफ्नो यन्त्र खोल्नलाई तपाईँको वाहकसँग सम्पर्क गर्नै पर्न अघि तपाईँसँग <xliff:g id="NUMBER_0">%d</xliff:g> पटक प्रयास बाँकी छ।</item>
</plurals>
<string name="kg_password_wrong_puk_code_dead" msgid="7077536808291316208">"SIM प्रयोग बिहिन छ। तपाईंको वाहकलाई सम्पर्क गर्नुहोस्।"</string>
<plurals name="kg_password_wrong_puk_code" formatted="false" msgid="7576227366999858780">
diff --git a/packages/Keyguard/res/values-uz-rUZ/strings.xml b/packages/Keyguard/res/values-uz-rUZ/strings.xml
index b87298d..eae5ff8 100644
--- a/packages/Keyguard/res/values-uz-rUZ/strings.xml
+++ b/packages/Keyguard/res/values-uz-rUZ/strings.xml
@@ -22,9 +22,9 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="719438068451601849">"Keyguard"</string>
<string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"PIN-kodni kiriting"</string>
- <string name="keyguard_password_enter_puk_code" msgid="3035856550289724338">"SIM-karta PUK kodi va yangi PIN kodni tering"</string>
- <string name="keyguard_password_enter_puk_prompt" msgid="1801941051094974609">"SIM-karta PUK kodi"</string>
- <string name="keyguard_password_enter_pin_prompt" msgid="3201151840570492538">"Yangi SIM-karta PIN kodi"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="3035856550289724338">"SIM karta PUK kodi va yangi PIN kodni tering"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1801941051094974609">"SIM karta PUK kodi"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="3201151840570492538">"Yangi SIM karta PIN kodi"</string>
<string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Parolni kiritish uchun bosing"</font></string>
<string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Qulfni ochish uchun parolni kiriting"</string>
<string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Qulfni ochish uchun PIN-kodni kiriting"</string>
@@ -36,24 +36,24 @@
<string name="keyguard_low_battery" msgid="8143808018719173859">"Zaryadlagichni ulang."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Qulfni ochish uchun \"Menyu\"ga bosing."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Tarmoq qulflangan"</string>
- <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"SIM-karta yo‘q"</string>
- <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"Ushbu planshetda SIM-karta yo‘q."</string>
- <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"Ushbu telefonda SIM-karta yo‘q."</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"SIM karta yo‘q"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"Ushbu planshetda SIM karta yo‘q."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"Ushbu telefonda SIM karta yo‘q."</string>
<string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Telefonga SIM kartani joylashtiring."</string>
- <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"SIM-karta qo‘yilmagan yoki o‘qib bo‘lmayapti. SIM-kartani joylashtiring."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"SIM karta qo‘yilmagan yoki o‘qib bo‘lmayapti. SIM kartani joylashtiring."</string>
<string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"SIM kartadan foydalanib bo‘lmaydi."</string>
- <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"SIM-kartangiz butunlay o‘chirilgan.\n Boshqa SIM-karta olish uchun aloqa operatori bilan bog‘laning."</string>
- <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM-karta qulflangan."</string>
- <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM-karta PUK kod bilan qulflangan."</string>
- <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"SIM-karta qulfi ochilmoqda…"</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"SIM kartangiz butunlay o‘chirilgan.\n Boshqa SIM karta olish uchun aloqa operatori bilan bog‘laning."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM karta qulflangan."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM karta PUK kod bilan qulflangan."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"SIM karta qulfi ochilmoqda…"</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Chizmali qulfni ochish."</string>
<string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Pin qulfini ochish."</string>
<string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Parolli qulfni ochish."</string>
<string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Chizmali qulf maydoni."</string>
<string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Maydonni silang"</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN-kod maydoni"</string>
- <string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"SIM-karta PIN kodi maydoni"</string>
- <string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"SIM-karta PUK kodi maydoni"</string>
+ <string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"SIM karta PIN kodi maydoni"</string>
+ <string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"SIM karta PUK kodi maydoni"</string>
<string name="keyguard_accessibility_next_alarm" msgid="7269583073750518672">"Uyg‘otkich signali <xliff:g id="ALARM">%1$s</xliff:g> da chalinadi."</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"O‘chirish"</string>
<string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Kiritish"</string>
@@ -67,11 +67,11 @@
<string name="kg_sim_pin_instructions_multi" msgid="7818515973197201434">"“<xliff:g id="CARRIER">%1$s</xliff:g>” SIM kartasi uchun PIN kodni kiriting"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"PIN kodni tering"</string>
<string name="kg_password_instructions" msgid="5753646556186936819">"Parol kiriting"</string>
- <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"SIM-karta hozir o‘chirilgan. Davom etish uchun PUK kodni kiriting. To‘liqroq ma’lumot olish uchun tarmoq operatori bilan bog‘laning."</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"SIM karta hozir o‘chirilgan. Davom etish uchun PUK kodni kiriting. To‘liqroq ma’lumot olish uchun tarmoq operatori bilan bog‘laning."</string>
<string name="kg_puk_enter_puk_hint_multi" msgid="363822494559783025">"“<xliff:g id="CARRIER">%1$s</xliff:g>” SIM kartasi o‘chirib qo‘yildi. Davom etish uchun PUK kodni kiriting. Tafsilotlar uchun aloqa operatoringizga murojaat qiling."</string>
<string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"So‘ralgan PIN kodni kiriting"</string>
<string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"So‘ralgan PIN kodni tasdiqlang"</string>
- <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"SIM-karta qulfi ochilmoqda…"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"SIM karta qulfi ochilmoqda…"</string>
<string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"4 tadan 8 ta raqamgacha bo‘lgan PIN kodni kiriting."</string>
<string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"PUK kod kamida 8 ta raqam bo‘lishi shart."</string>
<string name="kg_invalid_puk" msgid="3638289409676051243">"To‘g‘ri PUK kodni qayta kiriting. Qayta-qayta urinishlar SIM kartani butunlay o‘chirib qo‘yadi."</string>
@@ -94,18 +94,18 @@
<string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4951507352869831265">"Siz telefonni qulfdan chiqarish uchun <xliff:g id="NUMBER">%d</xliff:g> marta noto‘g‘ri urinish qildingiz. Endi, ishchi profil o‘chirib tashlanadi va undagi barcha ma’lumotlar ham o‘chib ketadi."</string>
<string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Siz chizmali kalitni <xliff:g id="NUMBER_0">%d</xliff:g> marta noto‘g‘ri kiritdingiz. <xliff:g id="NUMBER_1">%d</xliff:g> marta muvaffaqiyatsiz urinishdan so‘ng, sizdan e-pochtangizdan foydalanib, planshet qulfini ochishingiz so‘raladi.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> soniyadan so‘ng yana urinib ko‘ring."</string>
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Siz chizmali kalitni <xliff:g id="NUMBER_0">%d</xliff:g> marta noto‘g‘ri chizdingiz. <xliff:g id="NUMBER_1">%d</xliff:g> marta muvaffaqiyatsiz urinishdan so‘ng, sizdan e-pochtangizdan foydalanib, telefon qulfini ochishingiz so‘raladi.\n\n <xliff:g id="NUMBER_2">%d</xliff:g> soniyadan so‘ng yana urinib ko‘ring."</string>
- <string name="kg_password_wrong_pin_code_pukked" msgid="30531039455764924">"SIM-karta PIN kodi noto‘g‘ri. Qurilma qulfini ochish uchun aloqa operatoringiz bilan bog‘laning."</string>
+ <string name="kg_password_wrong_pin_code_pukked" msgid="30531039455764924">"SIM karta PIN kodi noto‘g‘ri. Qurilma qulfini ochish uchun aloqa operatoringiz bilan bog‘laning."</string>
<plurals name="kg_password_wrong_pin_code" formatted="false" msgid="6721575017538162249">
<item quantity="other">SIM kartaning PIN kodi noto‘g‘ri. Sizda yana <xliff:g id="NUMBER_1">%d</xliff:g> ta urinish qoldi.</item>
<item quantity="one">SIM kartaning PIN kodi noto‘g‘ri. Qurilmani qulfdan chiqarish uchun sizda yana <xliff:g id="NUMBER_0">%d</xliff:g> ta urinish qoldi.</item>
</plurals>
<string name="kg_password_wrong_puk_code_dead" msgid="7077536808291316208">"SIM kartadan foydalanib bo‘lmaydi. Aloqa operatoringiz bilan bog‘laning."</string>
<plurals name="kg_password_wrong_puk_code" formatted="false" msgid="7576227366999858780">
- <item quantity="other">SIM-kartaning PUK kodi noto‘g‘ri kiritildi. Yana <xliff:g id="NUMBER_1">%d</xliff:g> ta muvaffaqiyatsiz urinishdan so‘ng SIM-karta butunlay ishdan chiqadi.</item>
- <item quantity="one">SIM-kartaning PUK kodi noto‘g‘ri kiritildi. Yana <xliff:g id="NUMBER_0">%d</xliff:g> ta muvaffaqiyatsiz urinishdan so‘ng SIM-karta butunlay ishdan chiqadi.</item>
+ <item quantity="other">SIM kartaning PUK kodi noto‘g‘ri kiritildi. Yana <xliff:g id="NUMBER_1">%d</xliff:g> ta muvaffaqiyatsiz urinishdan so‘ng SIM karta butunlay ishdan chiqadi.</item>
+ <item quantity="one">SIM kartaning PUK kodi noto‘g‘ri kiritildi. Yana <xliff:g id="NUMBER_0">%d</xliff:g> ta muvaffaqiyatsiz urinishdan so‘ng SIM karta butunlay ishdan chiqadi.</item>
</plurals>
- <string name="kg_password_pin_failed" msgid="6268288093558031564">"SIM-karta PIN jarayoni amalga oshmadi!"</string>
- <string name="kg_password_puk_failed" msgid="2838824369502455984">"SIM-karta PUK jarayoni amalga oshmadi!"</string>
+ <string name="kg_password_pin_failed" msgid="6268288093558031564">"SIM karta PIN jarayoni amalga oshmadi!"</string>
+ <string name="kg_password_puk_failed" msgid="2838824369502455984">"SIM karta PUK jarayoni amalga oshmadi!"</string>
<string name="kg_pin_accepted" msgid="1448241673570020097">"Kod qabul qilindi!"</string>
<string name="keyguard_carrier_default" msgid="8700650403054042153">"Aloqa yo‘q."</string>
<string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Kiritish uslubi tugmasini almashtirish."</string>
diff --git a/packages/Keyguard/res/values-zh-rCN/strings.xml b/packages/Keyguard/res/values-zh-rCN/strings.xml
index 5b1e45d..c7b217c 100644
--- a/packages/Keyguard/res/values-zh-rCN/strings.xml
+++ b/packages/Keyguard/res/values-zh-rCN/strings.xml
@@ -92,8 +92,8 @@
<string name="kg_failed_attempts_almost_at_erase_profile" product="default" msgid="6853071165802933545">"您已经 <xliff:g id="NUMBER_0">%d</xliff:g> 次错误地尝试解锁手机。如果再尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次后仍不成功,系统将移除此工作资料,这会删除所有的工作资料数据。"</string>
<string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="4686386497449912146">"您已经 <xliff:g id="NUMBER">%d</xliff:g> 次错误地尝试解锁平板电脑。系统将移除此工作资料,这会删除所有的工作资料数据。"</string>
<string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4951507352869831265">"您已经 <xliff:g id="NUMBER">%d</xliff:g> 次错误地尝试解锁手机。系统将移除此工作资料,这会删除所有的工作资料数据。"</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"您已连续 <xliff:g id="NUMBER_0">%d</xliff:g> 次画错解锁图案。如果再尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次后仍不成功,系统就会要求您使用自己的电子邮件帐户解锁平板电脑。\n\n请在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒后重试。"</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"您已连续 <xliff:g id="NUMBER_0">%d</xliff:g> 次画错解锁图案。如果再尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次后仍不成功,系统就会要求您使用自己的电子邮件帐户解锁手机。\n\n请在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒后重试。"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"您已连续 <xliff:g id="NUMBER_0">%d</xliff:g> 次画错解锁图案。如果再尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次后仍不成功,系统就会要求您使用自己的电子邮件帐号解锁平板电脑。\n\n请在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒后重试。"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"您已连续 <xliff:g id="NUMBER_0">%d</xliff:g> 次画错解锁图案。如果再尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次后仍不成功,系统就会要求您使用自己的电子邮件帐号解锁手机。\n\n请在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒后重试。"</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="30531039455764924">"SIM卡PIN码不正确,您现在必须联系运营商为您解锁设备。"</string>
<plurals name="kg_password_wrong_pin_code" formatted="false" msgid="6721575017538162249">
<item quantity="other">SIM 卡 PIN 码不正确,您还可尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次。</item>
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
index 9a91ca4..29b319f 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
@@ -133,10 +133,6 @@
mLockPatternView.setSaveEnabled(false);
mLockPatternView.setOnPatternListener(new UnlockPatternListener());
- // stealth mode will be the same for the life of this screen
- mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled(
- KeyguardUpdateMonitor.getCurrentUser()));
-
// vibrate mode will be the same for the life of this screen
mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
@@ -176,6 +172,8 @@
@Override
public void reset() {
// reset lock pattern
+ mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled(
+ KeyguardUpdateMonitor.getCurrentUser()));
mLockPatternView.enableInput();
mLockPatternView.setEnabled(true);
mLockPatternView.clearPattern();
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 57ee319..3d78028 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -603,6 +603,7 @@
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (DEBUG) Log.d(TAG, "received broadcast " + action);
@@ -656,6 +657,7 @@
private final BroadcastReceiver mBroadcastAllReceiver = new BroadcastReceiver() {
+ @Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED.equals(action)) {
@@ -788,6 +790,7 @@
return new SimData(state, slotId, subId);
}
+ @Override
public String toString() {
return "SimData{state=" + simState + ",slotId=" + slotId + ",subId=" + subId + "}";
}
@@ -1693,5 +1696,19 @@
for (int subId : mServiceStates.keySet()) {
pw.println(" " + subId + "=" + mServiceStates.get(subId));
}
+ if (mFpm != null && mFpm.isHardwareDetected()) {
+ final int userId = ActivityManager.getCurrentUser();
+ final int strongAuthFlags = mStrongAuthTracker.getStrongAuthForUser(userId);
+ pw.println(" Fingerprint state (user=" + userId + ")");
+ pw.println(" allowed=" + isUnlockingWithFingerprintAllowed());
+ pw.println(" auth'd=" + mUserFingerprintAuthenticated.get(userId));
+ pw.println(" authSinceBoot="
+ + getStrongAuthTracker().hasUserAuthenticatedSinceBoot());
+ pw.println(" disabled(DPM)=" + isFingerprintDisabled(userId));
+ pw.println(" possible=" + isUnlockWithFingerprintPossible(userId));
+ pw.println(" strongAuthFlags=" + Integer.toHexString(strongAuthFlags));
+ pw.println(" timedout=" + hasFingerprintUnlockTimedOut(userId));
+ pw.println(" trustManaged=" + getUserTrustIsManaged(userId));
+ }
}
}
diff --git a/packages/MtpDocumentsProvider/res/values/strings.xml b/packages/MtpDocumentsProvider/res/values/strings.xml
index 37efdb5..7a4087b 100644
--- a/packages/MtpDocumentsProvider/res/values/strings.xml
+++ b/packages/MtpDocumentsProvider/res/values/strings.xml
@@ -14,7 +14,11 @@
limitations under the License.
-->
-<resources>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Title of the external storage application [CHAR LIMIT=32] -->
<string name="app_label">MTP Storage</string>
+ <!-- Name of MTP root shown in UI. Please align the two strings (device
+ model and storage name) in proper order in the language.
+ [CHAR LIMIT=32] -->
+ <string name="root_name"><xliff:g id="device_model" example="Nexus 9">%1$s</xliff:g> <xliff:g id="storage_name" example="Internal Storage">%2$s</xliff:g></string>
</resources>
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/CursorHelper.java b/packages/MtpDocumentsProvider/src/com/android/mtp/CursorHelper.java
index b5694b7..844b216 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/CursorHelper.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/CursorHelper.java
@@ -16,25 +16,28 @@
package com.android.mtp;
+import android.content.res.Resources;
import android.database.MatrixCursor;
+import android.media.MediaFile;
import android.mtp.MtpConstants;
import android.mtp.MtpObjectInfo;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
-import java.util.Date;
-
+/**
+ * TODO Remove this class after we switch to use MtpDatabase.
+ */
final class CursorHelper {
static final int DUMMY_HANDLE_FOR_ROOT = 0;
private CursorHelper() {
}
- static void addToCursor(MtpRoot root, MatrixCursor.RowBuilder builder) {
+ static void addToCursor(Resources resources, MtpRoot root, MatrixCursor.RowBuilder builder) {
final Identifier identifier = new Identifier(
root.mDeviceId, root.mStorageId, DUMMY_HANDLE_FOR_ROOT);
builder.add(Document.COLUMN_DOCUMENT_ID, identifier.toDocumentId());
- builder.add(Document.COLUMN_DISPLAY_NAME, root.mDescription);
+ builder.add(Document.COLUMN_DISPLAY_NAME, root.getRootName(resources));
builder.add(Document.COLUMN_MIME_TYPE, DocumentsContract.Document.MIME_TYPE_DIR);
builder.add(Document.COLUMN_LAST_MODIFIED, null);
builder.add(Document.COLUMN_FLAGS, 0);
@@ -71,30 +74,18 @@
}
static String formatTypeToMimeType(int format) {
- // TODO: Add complete list of mime types.
- switch (format) {
- case MtpConstants.FORMAT_ASSOCIATION:
- return DocumentsContract.Document.MIME_TYPE_DIR;
- case MtpConstants.FORMAT_MP3:
- return "audio/mp3";
- case MtpConstants.FORMAT_EXIF_JPEG:
- return "image/jpeg";
- default:
- return "application/octet-stream";
+ if (format == MtpConstants.FORMAT_ASSOCIATION) {
+ return DocumentsContract.Document.MIME_TYPE_DIR;
+ } else {
+ return MediaFile.getMimeTypeForFormatCode(format);
}
}
- static int mimeTypeToFormatType(String mimeType) {
- // TODO: Add complete list of mime types.
- switch (mimeType.toLowerCase()) {
- case Document.MIME_TYPE_DIR:
- return MtpConstants.FORMAT_ASSOCIATION;
- case "audio/mp3":
- return MtpConstants.FORMAT_MP3;
- case "image/jpeg":
- return MtpConstants.FORMAT_EXIF_JPEG;
- default:
- return MtpConstants.FORMAT_UNDEFINED;
+ static int mimeTypeToFormatType(String fileName, String mimeType) {
+ if (Document.MIME_TYPE_DIR.equals(mimeType)) {
+ return MtpConstants.FORMAT_ASSOCIATION;
+ } else {
+ return MediaFile.getFormatCode(fileName, mimeType);
}
}
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
new file mode 100644
index 0000000..0f31e2c
--- /dev/null
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
@@ -0,0 +1,566 @@
+/*
+ * 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.mtp;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteQueryBuilder;
+import android.mtp.MtpObjectInfo;
+import android.provider.DocumentsContract.Document;
+import android.provider.DocumentsContract.Root;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Objects;
+
+/**
+ * Database for MTP objects.
+ * The object handle which is identifier for object in MTP protocol is not stable over sessions.
+ * When we resume the process, we need to remap our document ID with MTP's object handle.
+ *
+ * If the remote MTP device is backed by typical file system, the file name
+ * is unique among files in a directory. However, MTP protocol itself does
+ * not guarantee the uniqueness of name so we cannot use fullpath as ID.
+ *
+ * Instead of fullpath, we use artificial ID generated by MtpDatabase itself. The database object
+ * remembers the map of document ID and object handle, and remaps new object handle with document ID
+ * by comparing the directory structure and object name.
+ *
+ * TODO: Remove @VisibleForTesting annotation when we start to use this class.
+ * TODO: Improve performance by SQL optimization.
+ */
+@VisibleForTesting
+class MtpDatabase {
+ private static final int VERSION = 1;
+ private static final String NAME = "mtp";
+
+ /**
+ * Table representing documents including root documents.
+ */
+ private static final String TABLE_DOCUMENTS = "Documents";
+
+ /**
+ * Table containing additional information only available for root documents.
+ * The table uses same primary keys with corresponding documents.
+ */
+ private static final String TABLE_ROOT_EXTRA = "RootExtra";
+
+ /**
+ * View to join Documents and RootExtra tables to provide roots information.
+ */
+ private static final String VIEW_ROOTS = "Roots";
+
+ static final String COLUMN_DEVICE_ID = "device_id";
+ static final String COLUMN_STORAGE_ID = "storage_id";
+ static final String COLUMN_OBJECT_HANDLE = "object_handle";
+ static final String COLUMN_PARENT_DOCUMENT_ID = "parent_document_id";
+ static final String COLUMN_ROW_STATE = "row_state";
+
+ /**
+ * The state represents that the row has a valid object handle.
+ */
+ static final int ROW_STATE_MAPPED = 0;
+
+ /**
+ * The state represents that the object handle was cleared because the MTP session closed.
+ * External application can still fetch the unmapped documents. If the external application
+ * tries to open an unmapped document, the provider resolves the document with new object handle
+ * ahead.
+ */
+ static final int ROW_STATE_UNMAPPED = 1;
+
+ /**
+ * The state represents the raw has a valid object handle but it may be going to be merged into
+ * another unmapped row. After fetching all documents under the parent, the database tries to
+ * map the mapping document and the unmapped document in order to keep old document ID alive.
+ */
+ static final int ROW_STATE_MAPPING = 2;
+
+ private static final String SELECTION_DOCUMENT_ID = Document.COLUMN_DOCUMENT_ID + " = ?";
+ private static final String SELECTION_ROOT_ID = Root.COLUMN_ROOT_ID + " = ?";
+ private static final String SELECTION_ROOT_DOCUMENTS =
+ COLUMN_DEVICE_ID + " = ? AND " + COLUMN_PARENT_DOCUMENT_ID + " IS NULL";
+ private static final String SELECTION_CHILD_DOCUMENTS = COLUMN_PARENT_DOCUMENT_ID + " = ?";
+
+ static class ParentNotFoundException extends Exception {}
+
+ private static class OpenHelper extends SQLiteOpenHelper {
+ private static final String QUERY_CREATE_DOCUMENTS =
+ "CREATE TABLE " + TABLE_DOCUMENTS + " (" +
+ Document.COLUMN_DOCUMENT_ID +
+ " INTEGER PRIMARY KEY AUTOINCREMENT," +
+ COLUMN_DEVICE_ID + " INTEGER NOT NULL," +
+ COLUMN_STORAGE_ID + " INTEGER," +
+ COLUMN_OBJECT_HANDLE + " INTEGER," +
+ COLUMN_PARENT_DOCUMENT_ID + " INTEGER," +
+ COLUMN_ROW_STATE + " INTEGER NOT NULL," +
+ Document.COLUMN_MIME_TYPE + " TEXT," +
+ Document.COLUMN_DISPLAY_NAME + " TEXT NOT NULL," +
+ Document.COLUMN_SUMMARY + " TEXT," +
+ Document.COLUMN_LAST_MODIFIED + " INTEGER," +
+ Document.COLUMN_ICON + " INTEGER," +
+ Document.COLUMN_FLAGS + " INTEGER NOT NULL," +
+ Document.COLUMN_SIZE + " INTEGER NOT NULL);";
+
+ private static final String QUERY_CREATE_ROOT_EXTRA =
+ "CREATE TABLE " + TABLE_ROOT_EXTRA + " (" +
+ Root.COLUMN_ROOT_ID + " INTEGER PRIMARY KEY," +
+ Root.COLUMN_FLAGS + " INTEGER NOT NULL," +
+ Root.COLUMN_AVAILABLE_BYTES + " INTEGER NOT NULL," +
+ Root.COLUMN_CAPACITY_BYTES + " INTEGER NOT NULL," +
+ Root.COLUMN_MIME_TYPES + " TEXT NOT NULL);";
+
+ /**
+ * Creates a view to join Documents table and RootExtra table on their primary keys to
+ * provide DocumentContract.Root equivalent information.
+ */
+ private static final String QUERY_CREATE_VIEW_ROOTS =
+ "CREATE VIEW " + VIEW_ROOTS + " AS SELECT " +
+ TABLE_DOCUMENTS + "." + Document.COLUMN_DOCUMENT_ID + " AS " +
+ Root.COLUMN_ROOT_ID + "," +
+ TABLE_ROOT_EXTRA + "." + Root.COLUMN_FLAGS + "," +
+ TABLE_DOCUMENTS + "." + Document.COLUMN_ICON + " AS " +
+ Root.COLUMN_ICON + "," +
+ TABLE_DOCUMENTS + "." + Document.COLUMN_DISPLAY_NAME + " AS " +
+ Root.COLUMN_TITLE + "," +
+ TABLE_DOCUMENTS + "." + Document.COLUMN_SUMMARY + " AS " +
+ Root.COLUMN_SUMMARY + "," +
+ TABLE_DOCUMENTS + "." + Document.COLUMN_DOCUMENT_ID + " AS " +
+ Root.COLUMN_DOCUMENT_ID + "," +
+ TABLE_ROOT_EXTRA + "." + Root.COLUMN_AVAILABLE_BYTES + "," +
+ TABLE_ROOT_EXTRA + "." + Root.COLUMN_CAPACITY_BYTES + "," +
+ TABLE_ROOT_EXTRA + "." + Root.COLUMN_MIME_TYPES + "," +
+ TABLE_DOCUMENTS + "." + COLUMN_ROW_STATE +
+ " FROM " + TABLE_DOCUMENTS + " INNER JOIN " + TABLE_ROOT_EXTRA +
+ " ON " +
+ COLUMN_PARENT_DOCUMENT_ID + " IS NULL AND " +
+ TABLE_DOCUMENTS + "." + Document.COLUMN_DOCUMENT_ID +
+ "=" +
+ TABLE_ROOT_EXTRA + "." + Root.COLUMN_ROOT_ID;
+
+ public OpenHelper(Context context) {
+ super(context, NAME, null, VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL(QUERY_CREATE_DOCUMENTS);
+ db.execSQL(QUERY_CREATE_ROOT_EXTRA);
+ db.execSQL(QUERY_CREATE_VIEW_ROOTS);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ private final SQLiteDatabase mDatabase;
+
+ @VisibleForTesting
+ MtpDatabase(Context context) {
+ final OpenHelper helper = new OpenHelper(context);
+ mDatabase = helper.getWritableDatabase();
+ }
+
+ @VisibleForTesting
+ static void deleteDatabase(Context context) {
+ SQLiteDatabase.deleteDatabase(context.getDatabasePath(NAME));
+ }
+
+ @VisibleForTesting
+ Cursor queryRoots(String[] columnNames) {
+ return mDatabase.query(
+ VIEW_ROOTS,
+ columnNames,
+ COLUMN_ROW_STATE + " IN (?, ?)",
+ strings(ROW_STATE_MAPPED, ROW_STATE_UNMAPPED),
+ null,
+ null,
+ null);
+ }
+
+ @VisibleForTesting
+ Cursor queryRootDocuments(String[] columnNames) {
+ return mDatabase.query(
+ TABLE_DOCUMENTS,
+ columnNames,
+ COLUMN_ROW_STATE + " IN (?, ?)",
+ strings(ROW_STATE_MAPPED, ROW_STATE_UNMAPPED),
+ null,
+ null,
+ null);
+ }
+
+ @VisibleForTesting
+ Cursor queryChildDocuments(String[] columnNames, String parentDocumentId) {
+ return mDatabase.query(
+ TABLE_DOCUMENTS,
+ columnNames,
+ COLUMN_ROW_STATE + " IN (?, ?) AND " + COLUMN_PARENT_DOCUMENT_ID + " = ?",
+ strings(ROW_STATE_MAPPED, ROW_STATE_UNMAPPED, parentDocumentId),
+ null,
+ null,
+ null);
+ }
+
+ @VisibleForTesting
+ void putRootDocuments(int deviceId, Resources resources, MtpRoot[] roots) {
+ mDatabase.beginTransaction();
+ try {
+ final ContentValues[] valuesList = new ContentValues[roots.length];
+ for (int i = 0; i < roots.length; i++) {
+ if (roots[i].mDeviceId != deviceId) {
+ throw new IllegalArgumentException();
+ }
+ valuesList[i] = new ContentValues();
+ getRootDocumentValues(valuesList[i], resources, roots[i]);
+ }
+ final long[] documentIds =
+ putDocuments(valuesList, SELECTION_ROOT_DOCUMENTS, Integer.toString(deviceId));
+ final ContentValues values = new ContentValues();
+ int i = 0;
+ for (final MtpRoot root : roots) {
+ // Use the same value for the root ID and the corresponding document ID.
+ values.put(Root.COLUMN_ROOT_ID, documentIds[i++]);
+ values.put(Root.COLUMN_FLAGS,
+ Root.FLAG_SUPPORTS_IS_CHILD |
+ Root.FLAG_SUPPORTS_CREATE);
+ values.put(Root.COLUMN_AVAILABLE_BYTES, root.mFreeSpace);
+ values.put(Root.COLUMN_CAPACITY_BYTES, root.mMaxCapacity);
+ values.put(Root.COLUMN_MIME_TYPES, "");
+ mDatabase.insert(TABLE_ROOT_EXTRA, null, values);
+ }
+ mDatabase.setTransactionSuccessful();
+ } finally {
+ mDatabase.endTransaction();
+ }
+ }
+
+ @VisibleForTesting
+ void putChildDocuments(int deviceId, String parentId, MtpObjectInfo[] documents) {
+ final ContentValues[] valuesList = new ContentValues[documents.length];
+ for (int i = 0; i < documents.length; i++) {
+ valuesList[i] = new ContentValues();
+ getChildDocumentValues(valuesList[i], deviceId, parentId, documents[i]);
+ }
+ putDocuments(valuesList, SELECTION_CHILD_DOCUMENTS, parentId);
+ }
+
+ /**
+ * Clears MTP related identifier.
+ * It clears MTP's object handle and storage ID that are not stable over MTP sessions and mark
+ * the all documents as 'unmapped'. It also remove 'mapping' rows as mapping is cancelled now.
+ */
+ @VisibleForTesting
+ void clearMapping() {
+ mDatabase.beginTransaction();
+ try {
+ deleteDocumentsAndRoots(COLUMN_ROW_STATE + " = ?", strings(ROW_STATE_MAPPING));
+ final ContentValues values = new ContentValues();
+ values.putNull(COLUMN_OBJECT_HANDLE);
+ values.putNull(COLUMN_STORAGE_ID);
+ values.put(COLUMN_ROW_STATE, ROW_STATE_UNMAPPED);
+ mDatabase.update(TABLE_DOCUMENTS, values, null, null);
+ mDatabase.setTransactionSuccessful();
+ } finally {
+ mDatabase.endTransaction();
+ }
+ }
+
+ @VisibleForTesting
+ void resolveRootDocuments(int deviceId) {
+ resolveDocuments(SELECTION_ROOT_DOCUMENTS, Integer.toString(deviceId));
+ }
+
+ @VisibleForTesting
+ void resolveChildDocuments(String parentId) {
+ resolveDocuments(SELECTION_CHILD_DOCUMENTS, parentId);
+ }
+
+ /**
+ * Puts the documents into the database.
+ * If the database found another unmapped document that shares the same name and parent,
+ * the document may be merged into the unmapped document. In that case, the database marks the
+ * root as 'mapping' and wait for {@link #resolveRootDocuments(int)} is invoked.
+ * @param valuesList Values that are stored in the database.
+ * @param selection SQL where closure to select rows that shares the same parent.
+ * @param arg Argument for selection SQL.
+ * @return List of Document ID inserted to the table.
+ */
+ private long[] putDocuments(ContentValues[] valuesList, String selection, String arg) {
+ mDatabase.beginTransaction();
+ try {
+ final long[] documentIds = new long[valuesList.length];
+ int i = 0;
+ for (final ContentValues values : valuesList) {
+ final String displayName =
+ values.getAsString(Document.COLUMN_DISPLAY_NAME);
+ final long numUnmapped = DatabaseUtils.queryNumEntries(
+ mDatabase,
+ TABLE_DOCUMENTS,
+ selection + " AND " +
+ COLUMN_ROW_STATE + " = ? AND " +
+ Document.COLUMN_DISPLAY_NAME + " = ?",
+ strings(arg, ROW_STATE_UNMAPPED, displayName));
+ if (numUnmapped != 0) {
+ values.put(COLUMN_ROW_STATE, ROW_STATE_MAPPING);
+ }
+ // Document ID is a primary integer key of the table. So the returned row IDs should
+ // be same with the document ID.
+ documentIds[i++] = mDatabase.insert(TABLE_DOCUMENTS, null, values);
+ }
+
+ mDatabase.setTransactionSuccessful();
+ return documentIds;
+ } finally {
+ mDatabase.endTransaction();
+ }
+ }
+
+ /**
+ * Maps 'unmapped' document and 'mapping' document that don't have document but shares the same
+ * name.
+ * If the database does not find corresponding 'mapping' document, it just removes 'unmapped'
+ * document from the database.
+ * @param selection Query to select rows for resolving.
+ * @param arg Argument for selection SQL.
+ */
+ private void resolveDocuments(String selection, String arg) {
+ mDatabase.beginTransaction();
+ try {
+ // Get 1-to-1 mapping of unmapped document and mapping document.
+ final String unmappedIdQuery = createStateFilter(
+ ROW_STATE_UNMAPPED, Document.COLUMN_DOCUMENT_ID);
+ final String mappingIdQuery = createStateFilter(
+ ROW_STATE_MAPPING, Document.COLUMN_DOCUMENT_ID);
+ // SQL should be like:
+ // SELECT group_concat(CASE WHEN raw_state = 1 THEN document_id ELSE NULL END),
+ // group_concat(CASE WHEN raw_state = 2 THEN document_id ELSE NULL END)
+ // WHERE device_id = ? AND parent_document_id IS NULL
+ // GROUP BY display_name
+ // HAVING count(CASE WHEN raw_state = 1 THEN document_id ELSE NULL END) = 1 AND
+ // count(CASE WHEN raw_state = 2 THEN document_id ELSE NULL END) = 1
+ final Cursor mergingCursor = mDatabase.query(
+ TABLE_DOCUMENTS,
+ new String[] {
+ "group_concat(" + unmappedIdQuery + ")",
+ "group_concat(" + mappingIdQuery + ")"
+ },
+ selection,
+ strings(arg),
+ Document.COLUMN_DISPLAY_NAME,
+ "count(" + unmappedIdQuery + ") = 1 AND count(" + mappingIdQuery + ") = 1",
+ null);
+
+ final ContentValues values = new ContentValues();
+ while (mergingCursor.moveToNext()) {
+ final String unmappedId = mergingCursor.getString(0);
+ final String mappingId = mergingCursor.getString(1);
+
+ // Obtain the new values including the latest object handle from mapping row.
+ getFirstRow(
+ TABLE_DOCUMENTS,
+ SELECTION_DOCUMENT_ID,
+ new String[] { mappingId },
+ values);
+ values.remove(Document.COLUMN_DOCUMENT_ID);
+ values.put(COLUMN_ROW_STATE, ROW_STATE_MAPPED);
+ mDatabase.update(
+ TABLE_DOCUMENTS,
+ values,
+ SELECTION_DOCUMENT_ID,
+ new String[] { unmappedId });
+
+ getFirstRow(
+ TABLE_ROOT_EXTRA,
+ SELECTION_ROOT_ID,
+ new String[] { mappingId },
+ values);
+ if (values.size() > 0) {
+ values.remove(Root.COLUMN_ROOT_ID);
+ mDatabase.update(
+ TABLE_ROOT_EXTRA,
+ values,
+ SELECTION_ROOT_ID,
+ new String[] { unmappedId });
+ }
+
+ // Delete 'mapping' row.
+ deleteDocumentsAndRoots(SELECTION_DOCUMENT_ID, new String[] { mappingId });
+ }
+ mergingCursor.close();
+
+ // Delete all unmapped rows that cannot be mapped.
+ deleteDocumentsAndRoots(
+ COLUMN_ROW_STATE + " = ? AND " + selection,
+ strings(ROW_STATE_UNMAPPED, arg));
+
+ // The database cannot find old document ID for the mapping rows.
+ // Turn the all mapping rows into mapped state, which means the rows become to be
+ // valid with new document ID.
+ values.clear();
+ values.put(COLUMN_ROW_STATE, ROW_STATE_MAPPED);
+ mDatabase.update(
+ TABLE_DOCUMENTS,
+ values,
+ COLUMN_ROW_STATE + " = ? AND " + selection,
+ strings(ROW_STATE_MAPPING, arg));
+ mDatabase.setTransactionSuccessful();
+ } finally {
+ mDatabase.endTransaction();
+ }
+ }
+
+ /**
+ * Gets {@link ContentValues} for the given root.
+ * @param values {@link ContentValues} that receives values.
+ * @param resources Resources used to get localized root name.
+ * @param root Root to be converted {@link ContentValues}.
+ */
+ private static void getRootDocumentValues(
+ ContentValues values, Resources resources, MtpRoot root) {
+ values.clear();
+ values.put(COLUMN_DEVICE_ID, root.mDeviceId);
+ values.put(COLUMN_STORAGE_ID, root.mStorageId);
+ values.putNull(COLUMN_OBJECT_HANDLE);
+ values.putNull(COLUMN_PARENT_DOCUMENT_ID);
+ values.put(COLUMN_ROW_STATE, ROW_STATE_MAPPED);
+ values.put(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
+ values.put(Document.COLUMN_DISPLAY_NAME, root.getRootName(resources));
+ values.putNull(Document.COLUMN_SUMMARY);
+ values.putNull(Document.COLUMN_LAST_MODIFIED);
+ values.putNull(Document.COLUMN_ICON);
+ values.put(Document.COLUMN_FLAGS, 0);
+ values.put(Document.COLUMN_SIZE,
+ (int) Math.min(root.mMaxCapacity - root.mFreeSpace, Integer.MAX_VALUE));
+ }
+
+ /**
+ * Gets {@link ContentValues} for the given MTP object.
+ * @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.
+ */
+ private void getChildDocumentValues(
+ ContentValues values, int deviceId, String parentId, MtpObjectInfo info) {
+ values.clear();
+ final String mimeType = CursorHelper.formatTypeToMimeType(info.getFormat());
+ int flag = 0;
+ if (info.getProtectionStatus() == 0) {
+ flag |= Document.FLAG_SUPPORTS_DELETE |
+ Document.FLAG_SUPPORTS_WRITE;
+ if (mimeType == Document.MIME_TYPE_DIR) {
+ flag |= Document.FLAG_DIR_SUPPORTS_CREATE;
+ }
+ }
+ if (info.getThumbCompressedSize() > 0) {
+ flag |= Document.FLAG_SUPPORTS_THUMBNAIL;
+ }
+ values.put(COLUMN_DEVICE_ID, deviceId);
+ values.put(COLUMN_STORAGE_ID, info.getStorageId());
+ values.put(COLUMN_OBJECT_HANDLE, info.getObjectHandle());
+ values.put(COLUMN_PARENT_DOCUMENT_ID, parentId);
+ values.put(COLUMN_ROW_STATE, ROW_STATE_MAPPED);
+ values.put(Document.COLUMN_MIME_TYPE, mimeType);
+ values.put(Document.COLUMN_DISPLAY_NAME, info.getName());
+ values.putNull(Document.COLUMN_SUMMARY);
+ values.put(
+ Document.COLUMN_LAST_MODIFIED,
+ info.getDateModified() != 0 ? info.getDateModified() : null);
+ values.putNull(Document.COLUMN_ICON);
+ values.put(Document.COLUMN_FLAGS, flag);
+ values.put(Document.COLUMN_SIZE, info.getCompressedSize());
+ }
+
+ /**
+ * Obtains values of the first row for the query.
+ * @param values ContentValues that the values are stored to.
+ * @param table Target table.
+ * @param selection Query to select rows.
+ * @param args Argument for query.
+ */
+ private void getFirstRow(String table, String selection, String[] args, ContentValues values) {
+ values.clear();
+ final Cursor cursor = mDatabase.query(table, null, selection, args, null, null, null, "1");
+ if (cursor.getCount() == 0) {
+ return;
+ }
+ cursor.moveToNext();
+ DatabaseUtils.cursorRowToContentValues(cursor, values);
+ cursor.close();
+ }
+
+ /**
+ * Deletes a document, and its root information if the document is a root document.
+ * @param selection Query to select documents.
+ * @param args Arguments for selection.
+ */
+ private void deleteDocumentsAndRoots(String selection, String[] args) {
+ mDatabase.beginTransaction();
+ try {
+ mDatabase.delete(
+ TABLE_ROOT_EXTRA,
+ Root.COLUMN_ROOT_ID + " IN (" + SQLiteQueryBuilder.buildQueryString(
+ false,
+ TABLE_DOCUMENTS,
+ new String[] { Document.COLUMN_DOCUMENT_ID },
+ selection,
+ null,
+ null,
+ null,
+ null) + ")",
+ args);
+ mDatabase.delete(TABLE_DOCUMENTS, selection, args);
+ mDatabase.setTransactionSuccessful();
+ } finally {
+ mDatabase.endTransaction();
+ }
+ }
+
+ /**
+ * Converts values into string array.
+ * @param args Values converted into string array.
+ * @return String array.
+ */
+ private static String[] strings(Object... args) {
+ final String[] results = new String[args.length];
+ for (int i = 0; i < args.length; i++) {
+ results[i] = Objects.toString(args[i]);
+ }
+ return results;
+ }
+
+ /**
+ * Gets SQL expression that represents the given value or NULL depends on the row state.
+ * @param state Expected row state.
+ * @param a SQL value.
+ * @return Expression that represents a if the row state is expected one, and represents NULL
+ * otherwise.
+ */
+ private static String createStateFilter(int state, String a) {
+ return "CASE WHEN " + COLUMN_ROW_STATE + " = " + Integer.toString(state) +
+ " THEN " + a + " ELSE NULL END";
+ }
+}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
index a1a43c1..7883e61 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
@@ -18,6 +18,7 @@
import android.content.ContentResolver;
import android.content.res.AssetFileDescriptor;
+import android.content.res.Resources;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.graphics.Point;
@@ -59,8 +60,8 @@
private MtpManager mMtpManager;
private ContentResolver mResolver;
private Map<Integer, DeviceToolkit> mDeviceToolkits;
- private DocumentLoader mDocumentLoaders;
private RootScanner mRootScanner;
+ private Resources mResources;
/**
* Provides singleton instance to MtpDocumentsService.
@@ -72,6 +73,7 @@
@Override
public boolean onCreate() {
sSingleton = this;
+ mResources = getContext().getResources();
mMtpManager = new MtpManager(getContext());
mResolver = getContext().getContentResolver();
mDeviceToolkits = new HashMap<Integer, DeviceToolkit>();
@@ -80,7 +82,8 @@
}
@VisibleForTesting
- void onCreateForTesting(MtpManager mtpManager, ContentResolver resolver) {
+ void onCreateForTesting(Resources resources, MtpManager mtpManager, ContentResolver resolver) {
+ mResources = resources;
mMtpManager = mtpManager;
mResolver = resolver;
mDeviceToolkits = new HashMap<Integer, DeviceToolkit>();
@@ -99,10 +102,8 @@
final MatrixCursor.RowBuilder builder = cursor.newRow();
builder.add(Root.COLUMN_ROOT_ID, rootIdentifier.toRootId());
builder.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE);
- builder.add(Root.COLUMN_TITLE, root.mDescription);
- builder.add(
- Root.COLUMN_DOCUMENT_ID,
- rootIdentifier.toDocumentId());
+ builder.add(Root.COLUMN_TITLE, root.getRootName(mResources));
+ builder.add(Root.COLUMN_DOCUMENT_ID, rootIdentifier.toDocumentId());
builder.add(Root.COLUMN_AVAILABLE_BYTES , root.mFreeSpace);
}
cursor.setNotificationUri(
@@ -143,7 +144,7 @@
if (identifier.mStorageId != root.mStorageId)
continue;
final MatrixCursor cursor = new MatrixCursor(projection);
- CursorHelper.addToCursor(root, cursor.newRow());
+ CursorHelper.addToCursor(mResources, root, cursor.newRow());
return cursor;
}
}
@@ -241,7 +242,7 @@
new MtpObjectInfo.Builder()
.setStorageId(parentId.mStorageId)
.setParent(parentId.mObjectHandle)
- .setFormat(CursorHelper.mimeTypeToFormatType(mimeType))
+ .setFormat(CursorHelper.mimeTypeToFormatType(displayName, mimeType))
.setName(displayName)
.build(), pipe[1]);
final String documentId = new Identifier(parentId.mDeviceId, parentId.mStorageId,
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
index af7f691..714936d 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
@@ -107,7 +107,10 @@
}
final MtpRoot[] results = new MtpRoot[storageIds.length];
for (int i = 0; i < storageIds.length; i++) {
- results[i] = new MtpRoot(deviceId, device.getStorageInfo(storageIds[i]));
+ results[i] = new MtpRoot(
+ device.getDeviceId(),
+ device.getDeviceInfo().getModel(),
+ device.getStorageInfo(storageIds[i]));
}
return results;
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpRoot.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpRoot.java
index 9dd53c0..ec338c3 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpRoot.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpRoot.java
@@ -16,6 +16,7 @@
package com.android.mtp;
+import android.content.res.Resources;
import android.mtp.MtpStorageInfo;
import com.android.internal.annotations.VisibleForTesting;
@@ -23,6 +24,7 @@
class MtpRoot {
final int mDeviceId;
final int mStorageId;
+ final String mDeviceModelName;
final String mDescription;
final long mFreeSpace;
final long mMaxCapacity;
@@ -31,21 +33,24 @@
@VisibleForTesting
MtpRoot(int deviceId,
int storageId,
+ String deviceName,
String description,
long freeSpace,
long maxCapacity,
String volumeIdentifier) {
mDeviceId = deviceId;
mStorageId = storageId;
+ mDeviceModelName = deviceName;
mDescription = description;
mFreeSpace = freeSpace;
mMaxCapacity = maxCapacity;
mVolumeIdentifier = volumeIdentifier;
}
- MtpRoot(int deviceId, MtpStorageInfo storageInfo) {
+ MtpRoot(int deviceId, String deviceModelName, MtpStorageInfo storageInfo) {
mDeviceId = deviceId;
mStorageId = storageInfo.getStorageId();
+ mDeviceModelName = deviceModelName;
mDescription = storageInfo.getDescription();
mFreeSpace = storageInfo.getFreeSpace();
mMaxCapacity = storageInfo.getMaxCapacity();
@@ -59,6 +64,7 @@
final MtpRoot other = (MtpRoot) object;
return mDeviceId == other.mDeviceId &&
mStorageId == other.mStorageId &&
+ mDeviceModelName.equals(other.mDeviceModelName) &&
mDescription.equals(other.mDescription) &&
mFreeSpace == other.mFreeSpace &&
mMaxCapacity == other.mMaxCapacity &&
@@ -67,7 +73,19 @@
@Override
public int hashCode() {
- return mDeviceId ^ mStorageId ^ mDescription.hashCode() ^ ((int) mFreeSpace) ^
- ((int) mMaxCapacity) ^ mVolumeIdentifier.hashCode();
+ return mDeviceId ^ mStorageId ^ mDeviceModelName.hashCode() ^ mDescription.hashCode() ^
+ ((int) mFreeSpace) ^ ((int) mMaxCapacity) ^ mVolumeIdentifier.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "MtpRoot{Name: " + mDeviceModelName + " " + mDescription + "}";
+ }
+
+ String getRootName(Resources resources) {
+ return String.format(
+ resources.getString(R.string.root_name),
+ mDeviceModelName,
+ mDescription);
}
}
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
new file mode 100644
index 0000000..3878ba6
--- /dev/null
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
@@ -0,0 +1,625 @@
+/*
+ * 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.mtp;
+
+import android.database.Cursor;
+import android.mtp.MtpConstants;
+import android.mtp.MtpObjectInfo;
+import android.provider.DocumentsContract;
+import android.provider.DocumentsContract.Root;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+@SmallTest
+public class MtpDatabaseTest extends AndroidTestCase {
+ private final String[] COLUMN_NAMES = new String[] {
+ DocumentsContract.Document.COLUMN_DOCUMENT_ID,
+ MtpDatabase.COLUMN_DEVICE_ID,
+ MtpDatabase.COLUMN_STORAGE_ID,
+ MtpDatabase.COLUMN_OBJECT_HANDLE,
+ DocumentsContract.Document.COLUMN_MIME_TYPE,
+ DocumentsContract.Document.COLUMN_DISPLAY_NAME,
+ DocumentsContract.Document.COLUMN_SUMMARY,
+ DocumentsContract.Document.COLUMN_LAST_MODIFIED,
+ DocumentsContract.Document.COLUMN_ICON,
+ DocumentsContract.Document.COLUMN_FLAGS,
+ DocumentsContract.Document.COLUMN_SIZE
+ };
+
+ private final TestResources resources = new TestResources();
+
+ @Override
+ public void tearDown() {
+ MtpDatabase.deleteDatabase(getContext());
+ }
+
+ public void testPutRootDocuments() throws Exception {
+ final MtpDatabase database = new MtpDatabase(getContext());
+ database.putRootDocuments(0, resources, new MtpRoot[] {
+ new MtpRoot(0, 1, "Device", "Storage", 1000, 2000, ""),
+ new MtpRoot(0, 2, "Device", "Storage", 2000, 4000, ""),
+ new MtpRoot(0, 3, "Device", "/@#%&<>Storage", 3000, 6000,"")
+ });
+
+ {
+ final Cursor cursor = database.queryRootDocuments(COLUMN_NAMES);
+ assertEquals(3, cursor.getCount());
+
+ cursor.moveToNext();
+ assertEquals("documentId", 1, cursor.getInt(0));
+ assertEquals("deviceId", 0, cursor.getInt(1));
+ assertEquals("storageId", 1, cursor.getInt(2));
+ assertTrue("objectHandle", cursor.isNull(3));
+ assertEquals("mimeType", DocumentsContract.Document.MIME_TYPE_DIR, cursor.getString(4));
+ assertEquals("displayName", "Device Storage", cursor.getString(5));
+ assertTrue("summary", cursor.isNull(6));
+ assertTrue("lastModified", cursor.isNull(7));
+ assertTrue("icon", cursor.isNull(8));
+ assertEquals("flag", 0, cursor.getInt(9));
+ assertEquals("size", 1000, cursor.getInt(10));
+
+ cursor.moveToNext();
+ assertEquals("documentId", 2, cursor.getInt(0));
+ assertEquals("displayName", "Device Storage", cursor.getString(5));
+
+ cursor.moveToNext();
+ assertEquals("documentId", 3, cursor.getInt(0));
+ assertEquals("displayName", "Device /@#%&<>Storage", cursor.getString(5));
+
+ cursor.close();
+ }
+
+ {
+ final Cursor cursor = database.queryRoots(new String [] {
+ Root.COLUMN_ROOT_ID,
+ Root.COLUMN_FLAGS,
+ Root.COLUMN_ICON,
+ Root.COLUMN_TITLE,
+ Root.COLUMN_SUMMARY,
+ Root.COLUMN_DOCUMENT_ID,
+ Root.COLUMN_AVAILABLE_BYTES,
+ Root.COLUMN_CAPACITY_BYTES
+ });
+ assertEquals(3, cursor.getCount());
+
+ cursor.moveToNext();
+ assertEquals(1, cursor.getInt(0));
+ assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
+ assertTrue(cursor.isNull(2));
+ assertEquals("Device Storage", cursor.getString(3));
+ assertTrue(cursor.isNull(4));
+ assertEquals(1, cursor.getInt(5));
+ assertEquals(1000, cursor.getInt(6));
+ assertEquals(2000, cursor.getInt(7));
+
+ cursor.moveToNext();
+ assertEquals(2, cursor.getInt(0));
+ assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
+ assertTrue(cursor.isNull(2));
+ assertEquals("Device Storage", cursor.getString(3));
+ assertTrue(cursor.isNull(4));
+ assertEquals(2, cursor.getInt(5));
+ assertEquals(2000, cursor.getInt(6));
+ assertEquals(4000, cursor.getInt(7));
+
+ cursor.moveToNext();
+ assertEquals(3, cursor.getInt(0));
+ assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
+ assertTrue(cursor.isNull(2));
+ assertEquals("Device /@#%&<>Storage", cursor.getString(3));
+ assertTrue(cursor.isNull(4));
+ assertEquals(3, cursor.getInt(5));
+ assertEquals(3000, cursor.getInt(6));
+ assertEquals(6000, cursor.getInt(7));
+
+ cursor.close();
+ }
+ }
+
+ private MtpObjectInfo createDocument(int objectHandle, String name, int format, int size) {
+ final MtpObjectInfo.Builder builder = new MtpObjectInfo.Builder();
+ builder.setObjectHandle(objectHandle);
+ builder.setName(name);
+ builder.setFormat(format);
+ builder.setCompressedSize(size);
+ return builder.build();
+ }
+
+ public void testPutChildDocuments() throws Exception {
+ final MtpDatabase database = new MtpDatabase(getContext());
+
+ database.putChildDocuments(0, "parentId", new MtpObjectInfo[] {
+ 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)
+ });
+
+ final Cursor cursor = database.queryChildDocuments(COLUMN_NAMES, "parentId");
+ assertEquals(3, cursor.getCount());
+
+ cursor.moveToNext();
+ assertEquals("documentId", 1, cursor.getInt(0));
+ assertEquals("deviceId", 0, cursor.getInt(1));
+ assertEquals("storageId", 0, cursor.getInt(2));
+ assertEquals("objectHandle", 100, cursor.getInt(3));
+ assertEquals("mimeType", "text/plain", cursor.getString(4));
+ assertEquals("displayName", "note.txt", cursor.getString(5));
+ assertTrue("summary", cursor.isNull(6));
+ assertTrue("lastModified", cursor.isNull(7));
+ assertTrue("icon", cursor.isNull(8));
+ assertEquals(
+ "flag",
+ DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
+ DocumentsContract.Document.FLAG_SUPPORTS_WRITE,
+ cursor.getInt(9));
+ assertEquals("size", 1024, cursor.getInt(10));
+
+ cursor.moveToNext();
+ assertEquals("documentId", 2, cursor.getInt(0));
+ assertEquals("deviceId", 0, cursor.getInt(1));
+ assertEquals("storageId", 0, cursor.getInt(2));
+ assertEquals("objectHandle", 101, cursor.getInt(3));
+ assertEquals("mimeType", "image/jpeg", cursor.getString(4));
+ assertEquals("displayName", "image.jpg", cursor.getString(5));
+ assertTrue("summary", cursor.isNull(6));
+ assertTrue("lastModified", cursor.isNull(7));
+ assertTrue("icon", cursor.isNull(8));
+ assertEquals(
+ "flag",
+ DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
+ DocumentsContract.Document.FLAG_SUPPORTS_WRITE,
+ cursor.getInt(9));
+ assertEquals("size", 2 * 1024 * 1024, cursor.getInt(10));
+
+ cursor.moveToNext();
+ assertEquals("documentId", 3, cursor.getInt(0));
+ assertEquals("deviceId", 0, cursor.getInt(1));
+ assertEquals("storageId", 0, cursor.getInt(2));
+ assertEquals("objectHandle", 102, cursor.getInt(3));
+ assertEquals("mimeType", "audio/mpeg", cursor.getString(4));
+ assertEquals("displayName", "music.mp3", cursor.getString(5));
+ assertTrue("summary", cursor.isNull(6));
+ assertTrue("lastModified", cursor.isNull(7));
+ assertTrue("icon", cursor.isNull(8));
+ assertEquals(
+ "flag",
+ DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
+ DocumentsContract.Document.FLAG_SUPPORTS_WRITE,
+ cursor.getInt(9));
+ assertEquals("size", 3 * 1024 * 1024, cursor.getInt(10));
+
+ cursor.close();
+ }
+
+ public void testRestoreIdForRootDocuments() throws Exception {
+ final MtpDatabase database = new MtpDatabase(getContext());
+ final String[] columns = new String[] {
+ DocumentsContract.Document.COLUMN_DOCUMENT_ID,
+ MtpDatabase.COLUMN_STORAGE_ID,
+ DocumentsContract.Document.COLUMN_DISPLAY_NAME
+ };
+ final String[] rootColumns = new String[] {
+ Root.COLUMN_ROOT_ID,
+ Root.COLUMN_AVAILABLE_BYTES
+ };
+ database.putRootDocuments(0, resources, new MtpRoot[] {
+ new MtpRoot(0, 100, "Device", "Storage A", 1000, 0, ""),
+ new MtpRoot(0, 101, "Device", "Storage B", 1001, 0, "")
+ });
+
+ {
+ final Cursor cursor = database.queryRootDocuments(columns);
+ assertEquals(2, cursor.getCount());
+ cursor.moveToNext();
+ assertEquals("documentId", 1, cursor.getInt(0));
+ assertEquals("storageId", 100, cursor.getInt(1));
+ assertEquals("name", "Device Storage A", cursor.getString(2));
+ cursor.moveToNext();
+ assertEquals("documentId", 2, cursor.getInt(0));
+ assertEquals("storageId", 101, cursor.getInt(1));
+ assertEquals("name", "Device Storage B", cursor.getString(2));
+ cursor.close();
+ }
+
+ {
+ final Cursor cursor = database.queryRoots(rootColumns);
+ assertEquals(2, cursor.getCount());
+ cursor.moveToNext();
+ assertEquals("rootId", 1, cursor.getInt(0));
+ assertEquals("availableBytes", 1000, cursor.getInt(1));
+ cursor.moveToNext();
+ assertEquals("rootId", 2, cursor.getInt(0));
+ assertEquals("availableBytes", 1001, cursor.getInt(1));
+ cursor.close();
+ }
+
+ database.clearMapping();
+
+ {
+ final Cursor cursor = database.queryRootDocuments(columns);
+ assertEquals(2, cursor.getCount());
+ cursor.moveToNext();
+ assertEquals("documentId", 1, cursor.getInt(0));
+ assertTrue("storageId", cursor.isNull(1));
+ assertEquals("name", "Device Storage A", cursor.getString(2));
+ cursor.moveToNext();
+ assertEquals("documentId", 2, cursor.getInt(0));
+ assertTrue("storageId", cursor.isNull(1));
+ assertEquals("name", "Device Storage B", cursor.getString(2));
+ cursor.close();
+ }
+
+ {
+ final Cursor cursor = database.queryRoots(rootColumns);
+ assertEquals(2, cursor.getCount());
+ cursor.moveToNext();
+ assertEquals("rootId", 1, cursor.getInt(0));
+ assertEquals("availableBytes", 1000, cursor.getInt(1));
+ cursor.moveToNext();
+ assertEquals("rootId", 2, cursor.getInt(0));
+ assertEquals("availableBytes", 1001, cursor.getInt(1));
+ cursor.close();
+ }
+
+ database.putRootDocuments(0, resources, new MtpRoot[] {
+ new MtpRoot(0, 200, "Device", "Storage A", 2000, 0, ""),
+ new MtpRoot(0, 202, "Device", "Storage C", 2002, 0, "")
+ });
+
+ {
+ final Cursor cursor = database.queryRootDocuments(columns);
+ assertEquals(3, cursor.getCount());
+ cursor.moveToNext();
+ assertEquals("documentId", 1, cursor.getInt(0));
+ assertTrue("storageId", cursor.isNull(1));
+ assertEquals("name", "Device Storage A", cursor.getString(2));
+ cursor.moveToNext();
+ assertEquals("documentId", 2, cursor.getInt(0));
+ assertTrue("storageId", cursor.isNull(1));
+ assertEquals("name", "Device Storage B", cursor.getString(2));
+ cursor.moveToNext();
+ assertEquals("documentId", 4, cursor.getInt(0));
+ assertEquals("storageId", 202, cursor.getInt(1));
+ assertEquals("name", "Device Storage C", cursor.getString(2));
+ cursor.close();
+ }
+
+ {
+ final Cursor cursor = database.queryRoots(rootColumns);
+ assertEquals(3, cursor.getCount());
+ cursor.moveToNext();
+ assertEquals("rootId", 1, cursor.getInt(0));
+ assertEquals("availableBytes", 1000, cursor.getInt(1));
+ cursor.moveToNext();
+ assertEquals("rootId", 2, cursor.getInt(0));
+ assertEquals("availableBytes", 1001, cursor.getInt(1));
+ cursor.moveToNext();
+ assertEquals("rootId", 4, cursor.getInt(0));
+ assertEquals("availableBytes", 2002, cursor.getInt(1));
+ cursor.close();
+ }
+
+ database.resolveRootDocuments(0);
+
+ {
+ final Cursor cursor = database.queryRootDocuments(columns);
+ assertEquals(2, cursor.getCount());
+ cursor.moveToNext();
+ assertEquals("documentId", 1, cursor.getInt(0));
+ assertEquals("storageId", 200, cursor.getInt(1));
+ assertEquals("name", "Device Storage A", cursor.getString(2));
+ cursor.moveToNext();
+ assertEquals("documentId", 4, cursor.getInt(0));
+ assertEquals("storageId", 202, cursor.getInt(1));
+ assertEquals("name", "Device Storage C", cursor.getString(2));
+ cursor.close();
+ }
+
+ {
+ final Cursor cursor = database.queryRoots(rootColumns);
+ assertEquals(2, cursor.getCount());
+ cursor.moveToNext();
+ assertEquals("rootId", 1, cursor.getInt(0));
+ assertEquals("availableBytes", 2000, cursor.getInt(1));
+ cursor.moveToNext();
+ assertEquals("rootId", 4, cursor.getInt(0));
+ assertEquals("availableBytes", 2002, cursor.getInt(1));
+ cursor.close();
+ }
+ }
+
+ public void testRestoreIdForChildDocuments() throws Exception {
+ final MtpDatabase database = new MtpDatabase(getContext());
+ final String[] columns = new String[] {
+ DocumentsContract.Document.COLUMN_DOCUMENT_ID,
+ MtpDatabase.COLUMN_OBJECT_HANDLE,
+ DocumentsContract.Document.COLUMN_DISPLAY_NAME
+ };
+ database.putChildDocuments(0, "parentId", new MtpObjectInfo[] {
+ 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)
+ });
+ database.clearMapping();
+
+ {
+ final Cursor cursor = database.queryChildDocuments(columns, "parentId");
+ assertEquals(3, cursor.getCount());
+
+ cursor.moveToNext();
+ assertEquals("documentId", 1, cursor.getInt(0));
+ assertTrue("objectHandle", cursor.isNull(1));
+ assertEquals("name", "note.txt", cursor.getString(2));
+
+ cursor.moveToNext();
+ assertEquals("documentId", 2, cursor.getInt(0));
+ assertTrue("objectHandle", cursor.isNull(1));
+ assertEquals("name", "image.jpg", cursor.getString(2));
+
+ cursor.moveToNext();
+ assertEquals("documentId", 3, cursor.getInt(0));
+ assertTrue("objectHandle", cursor.isNull(1));
+ assertEquals("name", "music.mp3", cursor.getString(2));
+
+ cursor.close();
+ }
+
+ database.putChildDocuments(0, "parentId", new MtpObjectInfo[] {
+ createDocument(200, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
+ createDocument(203, "video.mp4", MtpConstants.FORMAT_MP4_CONTAINER, 1024),
+ });
+
+ {
+ final Cursor cursor = database.queryChildDocuments(columns, "parentId");
+ assertEquals(4, cursor.getCount());
+
+ cursor.moveToPosition(3);
+ assertEquals("documentId", 5, cursor.getInt(0));
+ assertEquals("objectHandle", 203, cursor.getInt(1));
+ assertEquals("name", "video.mp4", cursor.getString(2));
+
+ cursor.close();
+ }
+
+ database.resolveChildDocuments("parentId");
+
+ {
+ final Cursor cursor = database.queryChildDocuments(columns, "parentId");
+ assertEquals(2, cursor.getCount());
+
+ cursor.moveToNext();
+ assertEquals("documentId", 1, cursor.getInt(0));
+ assertEquals("objectHandle", 200, cursor.getInt(1));
+ assertEquals("name", "note.txt", cursor.getString(2));
+
+ cursor.moveToNext();
+ assertEquals("documentId", 5, cursor.getInt(0));
+ assertEquals("objectHandle", 203, cursor.getInt(1));
+ assertEquals("name", "video.mp4", cursor.getString(2));
+ cursor.close();
+ }
+ }
+
+ public void testRestoreIdForDifferentDevices() throws Exception {
+ final MtpDatabase database = new MtpDatabase(getContext());
+ final String[] columns = new String[] {
+ DocumentsContract.Document.COLUMN_DOCUMENT_ID,
+ MtpDatabase.COLUMN_STORAGE_ID,
+ DocumentsContract.Document.COLUMN_DISPLAY_NAME
+ };
+ final String[] rootColumns = new String[] {
+ Root.COLUMN_ROOT_ID,
+ Root.COLUMN_AVAILABLE_BYTES
+ };
+ database.putRootDocuments(0, resources, new MtpRoot[] {
+ new MtpRoot(0, 100, "Device", "Storage", 0, 0, "")
+ });
+ database.putRootDocuments(1, resources, new MtpRoot[] {
+ new MtpRoot(1, 100, "Device", "Storage", 0, 0, "")
+ });
+
+ {
+ final Cursor cursor = database.queryRootDocuments(columns);
+ assertEquals(2, cursor.getCount());
+ cursor.moveToNext();
+ assertEquals("documentId", 1, cursor.getInt(0));
+ assertEquals("storageId", 100, cursor.getInt(1));
+ assertEquals("name", "Device Storage", cursor.getString(2));
+ cursor.moveToNext();
+ assertEquals("documentId", 2, cursor.getInt(0));
+ assertEquals("storageId", 100, cursor.getInt(1));
+ assertEquals("name", "Device Storage", cursor.getString(2));
+ cursor.close();
+ }
+
+ {
+ final Cursor cursor = database.queryRoots(rootColumns);
+ assertEquals(2, cursor.getCount());
+ cursor.moveToNext();
+ assertEquals("rootId", 1, cursor.getInt(0));
+ assertEquals("availableBytes", 0, cursor.getInt(1));
+ cursor.moveToNext();
+ assertEquals("rootId", 2, cursor.getInt(0));
+ assertEquals("availableBytes", 0, cursor.getInt(1));
+ cursor.close();
+ }
+
+ database.clearMapping();
+
+ database.putRootDocuments(0, resources, new MtpRoot[] {
+ new MtpRoot(0, 200, "Device", "Storage", 2000, 0, "")
+ });
+ database.putRootDocuments(1, resources, new MtpRoot[] {
+ new MtpRoot(1, 300, "Device", "Storage", 3000, 0, "")
+ });
+ database.resolveRootDocuments(0);
+ database.resolveRootDocuments(1);
+
+ {
+ final Cursor cursor = database.queryRootDocuments(columns);
+ assertEquals(2, cursor.getCount());
+ cursor.moveToNext();
+ assertEquals("documentId", 1, cursor.getInt(0));
+ assertEquals("storageId", 200, cursor.getInt(1));
+ assertEquals("name", "Device Storage", cursor.getString(2));
+ cursor.moveToNext();
+ assertEquals("documentId", 2, cursor.getInt(0));
+ assertEquals("storageId", 300, cursor.getInt(1));
+ assertEquals("name", "Device Storage", cursor.getString(2));
+ cursor.close();
+ }
+
+ {
+ final Cursor cursor = database.queryRoots(rootColumns);
+ assertEquals(2, cursor.getCount());
+ cursor.moveToNext();
+ assertEquals("rootId", 1, cursor.getInt(0));
+ assertEquals("availableBytes", 2000, cursor.getInt(1));
+ cursor.moveToNext();
+ assertEquals("rootId", 2, cursor.getInt(0));
+ assertEquals("availableBytes", 3000, cursor.getInt(1));
+ cursor.close();
+ }
+ }
+
+ public void testRestoreIdForDifferentParents() throws Exception {
+ final MtpDatabase database = new MtpDatabase(getContext());
+ final String[] columns = new String[] {
+ DocumentsContract.Document.COLUMN_DOCUMENT_ID,
+ MtpDatabase.COLUMN_OBJECT_HANDLE
+ };
+ database.putChildDocuments(0, "parentId1", new MtpObjectInfo[] {
+ createDocument(100, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
+ });
+ database.putChildDocuments(0, "parentId2", new MtpObjectInfo[] {
+ createDocument(101, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
+ });
+ database.clearMapping();
+ database.putChildDocuments(0, "parentId1", new MtpObjectInfo[] {
+ createDocument(200, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
+ });
+ database.putChildDocuments(0, "parentId2", new MtpObjectInfo[] {
+ createDocument(201, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
+ });
+ database.resolveChildDocuments("parentId1");
+
+ {
+ final Cursor cursor = database.queryChildDocuments(columns, "parentId1");
+ assertEquals(1, cursor.getCount());
+ cursor.moveToNext();
+ assertEquals("documentId", 1, cursor.getInt(0));
+ assertEquals("objectHandle", 200, cursor.getInt(1));
+ cursor.close();
+ }
+ {
+ final Cursor cursor = database.queryChildDocuments(columns, "parentId2");
+ assertEquals(1, cursor.getCount());
+ cursor.moveToNext();
+ assertEquals("documentId", 2, cursor.getInt(0));
+ assertTrue("objectHandle", cursor.isNull(1));
+ cursor.close();
+ }
+ }
+
+ public void testClearMtpIdentifierBeforeResolveRootDocuments() {
+ final MtpDatabase database = new MtpDatabase(getContext());
+ final String[] columns = new String[] {
+ DocumentsContract.Document.COLUMN_DOCUMENT_ID,
+ MtpDatabase.COLUMN_STORAGE_ID,
+ DocumentsContract.Document.COLUMN_DISPLAY_NAME
+ };
+ final String[] rootColumns = new String[] {
+ Root.COLUMN_ROOT_ID,
+ Root.COLUMN_AVAILABLE_BYTES
+ };
+ database.putRootDocuments(0, resources, new MtpRoot[] {
+ new MtpRoot(0, 100, "Device", "Storage", 0, 0, ""),
+ });
+ database.clearMapping();
+ database.putRootDocuments(0, resources, new MtpRoot[] {
+ new MtpRoot(0, 200, "Device", "Storage", 2000, 0, ""),
+ });
+ database.clearMapping();
+ database.putRootDocuments(0, resources, new MtpRoot[] {
+ new MtpRoot(0, 300, "Device", "Storage", 3000, 0, ""),
+ });
+ database.resolveRootDocuments(0);
+ {
+ final Cursor cursor = database.queryRootDocuments(columns);
+ assertEquals(1, cursor.getCount());
+ cursor.moveToNext();
+ assertEquals("documentId", 1, cursor.getInt(0));
+ assertEquals("storageId", 300, cursor.getInt(1));
+ assertEquals("name", "Device Storage", cursor.getString(2));
+ cursor.close();
+ }
+ {
+ final Cursor cursor = database.queryRoots(rootColumns);
+ assertEquals(1, cursor.getCount());
+ cursor.moveToNext();
+ assertEquals("rootId", 1, cursor.getInt(0));
+ assertEquals("availableBytes", 3000, cursor.getInt(1));
+ cursor.close();
+ }
+ }
+
+ public void testPutSameNameRootsAfterClearing() throws Exception {
+ final MtpDatabase database = new MtpDatabase(getContext());
+ final String[] columns = new String[] {
+ DocumentsContract.Document.COLUMN_DOCUMENT_ID,
+ MtpDatabase.COLUMN_STORAGE_ID,
+ DocumentsContract.Document.COLUMN_DISPLAY_NAME
+ };
+ final String[] rootColumns = new String[] {
+ Root.COLUMN_ROOT_ID,
+ Root.COLUMN_AVAILABLE_BYTES
+ };
+ database.putRootDocuments(0, resources, new MtpRoot[] {
+ new MtpRoot(0, 100, "Device", "Storage", 0, 0, ""),
+ });
+ database.clearMapping();
+ database.putRootDocuments(0, resources, new MtpRoot[] {
+ new MtpRoot(0, 200, "Device", "Storage", 2000, 0, ""),
+ new MtpRoot(0, 201, "Device", "Storage", 2001, 0, ""),
+ });
+ database.resolveRootDocuments(0);
+ {
+ final Cursor cursor = database.queryRootDocuments(columns);
+ assertEquals(2, cursor.getCount());
+ cursor.moveToNext();
+ assertEquals("documentId", 2, cursor.getInt(0));
+ assertEquals("storageId", 200, cursor.getInt(1));
+ assertEquals("name", "Device Storage", cursor.getString(2));
+ cursor.moveToNext();
+ assertEquals("documentId", 3, cursor.getInt(0));
+ assertEquals("storageId", 201, cursor.getInt(1));
+ assertEquals("name", "Device Storage", cursor.getString(2));
+ cursor.close();
+ }
+ {
+ final Cursor cursor = database.queryRoots(rootColumns);
+ assertEquals(2, cursor.getCount());
+ cursor.moveToNext();
+ assertEquals("rootId", 2, cursor.getInt(0));
+ assertEquals("availableBytes", 2000, cursor.getInt(1));
+ cursor.moveToNext();
+ assertEquals("rootId", 3, cursor.getInt(0));
+ assertEquals("availableBytes", 2001, cursor.getInt(1));
+ cursor.close();
+ }
+ }
+}
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
index 4b3a5cd..5765f0a 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
@@ -18,7 +18,6 @@
import android.database.Cursor;
import android.mtp.MtpConstants;
-import android.mtp.MtpObjectInfo.Builder;
import android.mtp.MtpObjectInfo;
import android.net.Uri;
import android.provider.DocumentsContract.Root;
@@ -28,7 +27,6 @@
import java.io.FileNotFoundException;
import java.io.IOException;
-import java.util.Date;
@SmallTest
public class MtpDocumentsProviderTest extends AndroidTestCase {
@@ -37,13 +35,14 @@
private TestContentResolver mResolver;
private MtpDocumentsProvider mProvider;
private TestMtpManager mMtpManager;
+ private final TestResources mResources = new TestResources();
@Override
public void setUp() throws IOException {
mResolver = new TestContentResolver();
mMtpManager = new TestMtpManager(getContext());
mProvider = new MtpDocumentsProvider();
- mProvider.onCreateForTesting(mMtpManager, mResolver);
+ mProvider.onCreateForTesting(mResources, mMtpManager, mResolver);
}
public void testOpenAndCloseDevice() throws Exception {
@@ -52,6 +51,7 @@
new MtpRoot(
0 /* deviceId */,
1 /* storageId */,
+ "Device A" /* device model name */,
"Storage A" /* volume description */,
1024 /* free space */,
2048 /* total space */,
@@ -86,6 +86,7 @@
new MtpRoot(
0 /* deviceId */,
1 /* storageId */,
+ "Device A" /* device model name */,
"Storage A" /* volume description */,
1024 /* free space */,
2048 /* total space */,
@@ -101,6 +102,7 @@
new MtpRoot(
0 /* deviceId */,
1 /* storageId */,
+ "Device A" /* device model name */,
"Storage A" /* volume description */,
1024 /* free space */,
2048 /* total space */,
@@ -121,6 +123,7 @@
new MtpRoot(
0 /* deviceId */,
1 /* storageId */,
+ "Device A" /* device model name */,
"Storage A" /* volume description */,
1024 /* free space */,
2048 /* total space */,
@@ -130,6 +133,7 @@
new MtpRoot(
1 /* deviceId */,
1 /* storageId */,
+ "Device B" /* device model name */,
"Storage B" /* volume description */,
2048 /* free space */,
4096 /* total space */,
@@ -146,7 +150,7 @@
assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
// TODO: Add storage icon for MTP devices.
assertTrue(cursor.isNull(2) /* icon */);
- assertEquals("Storage A", cursor.getString(3));
+ assertEquals("Device A Storage A", cursor.getString(3));
assertEquals("0_1_0", cursor.getString(4));
assertEquals(1024, cursor.getInt(5));
}
@@ -162,7 +166,7 @@
assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
// TODO: Add storage icon for MTP devices.
assertTrue(cursor.isNull(2) /* icon */);
- assertEquals("Storage B", cursor.getString(3));
+ assertEquals("Device B Storage B", cursor.getString(3));
assertEquals("1_1_0", cursor.getString(4));
assertEquals(2048, cursor.getInt(5));
}
@@ -183,6 +187,7 @@
new MtpRoot(
1 /* deviceId */,
1 /* storageId */,
+ "Device B" /* device model name */,
"Storage B" /* volume description */,
2048 /* free space */,
4096 /* total space */,
@@ -200,7 +205,7 @@
assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
// TODO: Add storage icon for MTP devices.
assertTrue(cursor.isNull(2) /* icon */);
- assertEquals("Storage B", cursor.getString(3));
+ assertEquals("Device B Storage B", cursor.getString(3));
assertEquals("1_1_0", cursor.getString(4));
assertEquals(2048, cursor.getInt(5));
}
@@ -265,6 +270,7 @@
new MtpRoot(
0 /* deviceId */,
1 /* storageId */,
+ "Device A" /* device model name */,
"Storage A" /* volume description */,
1024 /* free space */,
4096 /* total space */,
@@ -276,7 +282,7 @@
cursor.moveToNext();
assertEquals("0_1_0", cursor.getString(0));
assertEquals(DocumentsContract.Document.MIME_TYPE_DIR, cursor.getString(1));
- assertEquals("Storage A", cursor.getString(2));
+ assertEquals("Device A Storage A", cursor.getString(2));
assertTrue(cursor.isNull(3));
assertEquals(0, cursor.getInt(4));
assertEquals(3072, cursor.getInt(5));
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
index 5605388..3d92cc2 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
@@ -18,12 +18,10 @@
import android.content.Context;
import android.mtp.MtpObjectInfo;
-import android.mtp.MtpObjectInfo.Builder;
import android.os.ParcelFileDescriptor;
import java.io.IOException;
import java.util.Arrays;
-import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResources.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResources.java
new file mode 100644
index 0000000..eb80e3b
--- /dev/null
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResources.java
@@ -0,0 +1,30 @@
+/*
+ * 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.mtp;
+
+import android.test.mock.MockResources;
+
+class TestResources extends MockResources {
+ @Override
+ public String getString(int id) throws NotFoundException {
+ switch (id) {
+ case R.string.root_name:
+ return "%1$s %2$s";
+ }
+ throw new NotFoundException();
+ }
+}
diff --git a/packages/PrintSpooler/res/layout/print_activity.xml b/packages/PrintSpooler/res/layout/print_activity.xml
index 6b8aa47..a2f710d 100644
--- a/packages/PrintSpooler/res/layout/print_activity.xml
+++ b/packages/PrintSpooler/res/layout/print_activity.xml
@@ -69,7 +69,9 @@
android:layout_height="wrap_content"
android:layout_marginStart="16dip"
android:textAppearance="?android:attr/textAppearanceMedium"
- android:textColor="?android:attr/textColorPrimary">
+ android:textColor="?android:attr/textColorPrimary"
+ android:singleLine="true"
+ android:ellipsize="end">
</TextView>
<TextView
@@ -87,7 +89,9 @@
android:layout_height="wrap_content"
android:layout_marginStart="16dip"
android:textAppearance="?android:attr/textAppearanceMedium"
- android:textColor="?android:attr/textColorPrimary">
+ android:textColor="?android:attr/textColorPrimary"
+ android:singleLine="true"
+ android:ellipsize="end">
</TextView>
</LinearLayout>
diff --git a/packages/PrintSpooler/res/values-af/strings.xml b/packages/PrintSpooler/res/values-af/strings.xml
index 0cfa0de..1c39a7d 100644
--- a/packages/PrintSpooler/res/values-af/strings.xml
+++ b/packages/PrintSpooler/res/values-af/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Swart en wit"</item>
<item msgid="2762241247228983754">"Kleur"</item>
diff --git a/packages/PrintSpooler/res/values-am/strings.xml b/packages/PrintSpooler/res/values-am/strings.xml
index 72fc67b..4d8b85e 100644
--- a/packages/PrintSpooler/res/values-am/strings.xml
+++ b/packages/PrintSpooler/res/values-am/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"ጥቁር እና ነጭ"</item>
<item msgid="2762241247228983754">"ቀለም"</item>
diff --git a/packages/PrintSpooler/res/values-ar/strings.xml b/packages/PrintSpooler/res/values-ar/strings.xml
index e28c7bd..0fa6ec2 100644
--- a/packages/PrintSpooler/res/values-ar/strings.xml
+++ b/packages/PrintSpooler/res/values-ar/strings.xml
@@ -82,6 +82,8 @@
<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">
<item msgid="7602948745415174937">"أبيض وأسود"</item>
<item msgid="2762241247228983754">"ملونة"</item>
diff --git a/packages/PrintSpooler/res/values-az-rAZ/strings.xml b/packages/PrintSpooler/res/values-az-rAZ/strings.xml
index c102745..1837db4 100644
--- a/packages/PrintSpooler/res/values-az-rAZ/strings.xml
+++ b/packages/PrintSpooler/res/values-az-rAZ/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Qara & Ağ"</item>
<item msgid="2762241247228983754">"Rəng"</item>
diff --git a/packages/PrintSpooler/res/values-bg/strings.xml b/packages/PrintSpooler/res/values-bg/strings.xml
index c88d0d4..9a8ccef 100644
--- a/packages/PrintSpooler/res/values-bg/strings.xml
+++ b/packages/PrintSpooler/res/values-bg/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Черно-бяло"</item>
<item msgid="2762241247228983754">"Цветно"</item>
diff --git a/packages/PrintSpooler/res/values-bn-rBD/strings.xml b/packages/PrintSpooler/res/values-bn-rBD/strings.xml
index 45f20a0..4f1646d 100644
--- a/packages/PrintSpooler/res/values-bn-rBD/strings.xml
+++ b/packages/PrintSpooler/res/values-bn-rBD/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"কালো এবং সাদা"</item>
<item msgid="2762241247228983754">"রঙ"</item>
diff --git a/packages/PrintSpooler/res/values-ca/strings.xml b/packages/PrintSpooler/res/values-ca/strings.xml
index 612be97..fa34c52 100644
--- a/packages/PrintSpooler/res/values-ca/strings.xml
+++ b/packages/PrintSpooler/res/values-ca/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Blanc i negre"</item>
<item msgid="2762241247228983754">"Color"</item>
diff --git a/packages/PrintSpooler/res/values-cs/strings.xml b/packages/PrintSpooler/res/values-cs/strings.xml
index 99ed75d..5c98d616 100644
--- a/packages/PrintSpooler/res/values-cs/strings.xml
+++ b/packages/PrintSpooler/res/values-cs/strings.xml
@@ -78,6 +78,8 @@
<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">
<item msgid="7602948745415174937">"Černobíle"</item>
<item msgid="2762241247228983754">"Barevně"</item>
diff --git a/packages/PrintSpooler/res/values-da/strings.xml b/packages/PrintSpooler/res/values-da/strings.xml
index 526e976..e57d435 100644
--- a/packages/PrintSpooler/res/values-da/strings.xml
+++ b/packages/PrintSpooler/res/values-da/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Sort/hvid"</item>
<item msgid="2762241247228983754">"Farve"</item>
diff --git a/packages/PrintSpooler/res/values-de/strings.xml b/packages/PrintSpooler/res/values-de/strings.xml
index ef5e34c..1005b45 100644
--- a/packages/PrintSpooler/res/values-de/strings.xml
+++ b/packages/PrintSpooler/res/values-de/strings.xml
@@ -74,6 +74,8 @@
<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">"Ihr Dokument passiert bei der Übermittlung an den Drucker möglicherweise einen oder mehrere Server."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Schwarz-weiß"</item>
<item msgid="2762241247228983754">"Farbe"</item>
diff --git a/packages/PrintSpooler/res/values-el/strings.xml b/packages/PrintSpooler/res/values-el/strings.xml
index a4bb04c..75b8447 100644
--- a/packages/PrintSpooler/res/values-el/strings.xml
+++ b/packages/PrintSpooler/res/values-el/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Ασπρόμαυρο"</item>
<item msgid="2762241247228983754">"Χρώμα"</item>
diff --git a/packages/PrintSpooler/res/values-en-rAU/strings.xml b/packages/PrintSpooler/res/values-en-rAU/strings.xml
index 9f0d7e5..87349c2 100644
--- a/packages/PrintSpooler/res/values-en-rAU/strings.xml
+++ b/packages/PrintSpooler/res/values-en-rAU/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Black & White"</item>
<item msgid="2762241247228983754">"Colour"</item>
diff --git a/packages/PrintSpooler/res/values-en-rGB/strings.xml b/packages/PrintSpooler/res/values-en-rGB/strings.xml
index 9f0d7e5..87349c2 100644
--- a/packages/PrintSpooler/res/values-en-rGB/strings.xml
+++ b/packages/PrintSpooler/res/values-en-rGB/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Black & White"</item>
<item msgid="2762241247228983754">"Colour"</item>
diff --git a/packages/PrintSpooler/res/values-en-rIN/strings.xml b/packages/PrintSpooler/res/values-en-rIN/strings.xml
index 9f0d7e5..87349c2 100644
--- a/packages/PrintSpooler/res/values-en-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-en-rIN/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Black & White"</item>
<item msgid="2762241247228983754">"Colour"</item>
diff --git a/packages/PrintSpooler/res/values-es-rUS/strings.xml b/packages/PrintSpooler/res/values-es-rUS/strings.xml
index ef0dbb1..ffd4d79 100644
--- a/packages/PrintSpooler/res/values-es-rUS/strings.xml
+++ b/packages/PrintSpooler/res/values-es-rUS/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Blanco y negro"</item>
<item msgid="2762241247228983754">"Color"</item>
diff --git a/packages/PrintSpooler/res/values-es/strings.xml b/packages/PrintSpooler/res/values-es/strings.xml
index 067c134..5660d1b 100644
--- a/packages/PrintSpooler/res/values-es/strings.xml
+++ b/packages/PrintSpooler/res/values-es/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Blanco y negro"</item>
<item msgid="2762241247228983754">"Color"</item>
diff --git a/packages/PrintSpooler/res/values-et-rEE/strings.xml b/packages/PrintSpooler/res/values-et-rEE/strings.xml
index 0227131..fb0f320 100644
--- a/packages/PrintSpooler/res/values-et-rEE/strings.xml
+++ b/packages/PrintSpooler/res/values-et-rEE/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Mustvalge"</item>
<item msgid="2762241247228983754">"Värv"</item>
diff --git a/packages/PrintSpooler/res/values-eu-rES/strings.xml b/packages/PrintSpooler/res/values-eu-rES/strings.xml
index 461a92f..8402a9c 100644
--- a/packages/PrintSpooler/res/values-eu-rES/strings.xml
+++ b/packages/PrintSpooler/res/values-eu-rES/strings.xml
@@ -74,6 +74,8 @@
<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 bat edo gehiagotan zehar igarotzea inprimagailurako bidean."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Zuri-beltza"</item>
<item msgid="2762241247228983754">"Koloretakoa"</item>
diff --git a/packages/PrintSpooler/res/values-fa/strings.xml b/packages/PrintSpooler/res/values-fa/strings.xml
index 0e1629f..ba78460 100644
--- a/packages/PrintSpooler/res/values-fa/strings.xml
+++ b/packages/PrintSpooler/res/values-fa/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"سیاه و سفید"</item>
<item msgid="2762241247228983754">"رنگی"</item>
diff --git a/packages/PrintSpooler/res/values-fi/strings.xml b/packages/PrintSpooler/res/values-fi/strings.xml
index faf40e2..3a82702 100644
--- a/packages/PrintSpooler/res/values-fi/strings.xml
+++ b/packages/PrintSpooler/res/values-fi/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Mustavalkoinen"</item>
<item msgid="2762241247228983754">"Väri"</item>
diff --git a/packages/PrintSpooler/res/values-fr-rCA/strings.xml b/packages/PrintSpooler/res/values-fr-rCA/strings.xml
index f949bb7..f49885a 100644
--- a/packages/PrintSpooler/res/values-fr-rCA/strings.xml
+++ b/packages/PrintSpooler/res/values-fr-rCA/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Noir et blanc"</item>
<item msgid="2762241247228983754">"Couleur"</item>
diff --git a/packages/PrintSpooler/res/values-fr/strings.xml b/packages/PrintSpooler/res/values-fr/strings.xml
index 1f5c716..4dcfa65 100644
--- a/packages/PrintSpooler/res/values-fr/strings.xml
+++ b/packages/PrintSpooler/res/values-fr/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Noir et blanc"</item>
<item msgid="2762241247228983754">"Couleur"</item>
diff --git a/packages/PrintSpooler/res/values-gl-rES/strings.xml b/packages/PrintSpooler/res/values-gl-rES/strings.xml
index 0d4b040..c805e30 100644
--- a/packages/PrintSpooler/res/values-gl-rES/strings.xml
+++ b/packages/PrintSpooler/res/values-gl-rES/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Branco e negro"</item>
<item msgid="2762241247228983754">"Cor"</item>
diff --git a/packages/PrintSpooler/res/values-gu-rIN/strings.xml b/packages/PrintSpooler/res/values-gu-rIN/strings.xml
index 28ffc2a..51820f2 100644
--- a/packages/PrintSpooler/res/values-gu-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-gu-rIN/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"શ્યામ અને શ્વેત"</item>
<item msgid="2762241247228983754">"રંગ"</item>
diff --git a/packages/PrintSpooler/res/values-hi/strings.xml b/packages/PrintSpooler/res/values-hi/strings.xml
index 1a0f7d1..e2bc012 100644
--- a/packages/PrintSpooler/res/values-hi/strings.xml
+++ b/packages/PrintSpooler/res/values-hi/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"श्याम और श्वेत"</item>
<item msgid="2762241247228983754">"रंग"</item>
diff --git a/packages/PrintSpooler/res/values-hr/strings.xml b/packages/PrintSpooler/res/values-hr/strings.xml
index 3e38d65..64c4a58 100644
--- a/packages/PrintSpooler/res/values-hr/strings.xml
+++ b/packages/PrintSpooler/res/values-hr/strings.xml
@@ -76,6 +76,8 @@
<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">
<item msgid="7602948745415174937">"Crno-bijelo"</item>
<item msgid="2762241247228983754">"U boji"</item>
diff --git a/packages/PrintSpooler/res/values-hu/strings.xml b/packages/PrintSpooler/res/values-hu/strings.xml
index 2b24b1f..58e7852 100644
--- a/packages/PrintSpooler/res/values-hu/strings.xml
+++ b/packages/PrintSpooler/res/values-hu/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Fekete-fehér"</item>
<item msgid="2762241247228983754">"Szín"</item>
diff --git a/packages/PrintSpooler/res/values-hy-rAM/strings.xml b/packages/PrintSpooler/res/values-hy-rAM/strings.xml
index 56f94b4..88e9192 100644
--- a/packages/PrintSpooler/res/values-hy-rAM/strings.xml
+++ b/packages/PrintSpooler/res/values-hy-rAM/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Սև ու սպիտակ"</item>
<item msgid="2762241247228983754">"Գույնը"</item>
diff --git a/packages/PrintSpooler/res/values-in/strings.xml b/packages/PrintSpooler/res/values-in/strings.xml
index d09fcfe..de76536 100644
--- a/packages/PrintSpooler/res/values-in/strings.xml
+++ b/packages/PrintSpooler/res/values-in/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Hitam & Putih"</item>
<item msgid="2762241247228983754">"Warna"</item>
diff --git a/packages/PrintSpooler/res/values-is-rIS/strings.xml b/packages/PrintSpooler/res/values-is-rIS/strings.xml
index bf13ebf..3f8aa65 100644
--- a/packages/PrintSpooler/res/values-is-rIS/strings.xml
+++ b/packages/PrintSpooler/res/values-is-rIS/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Svarthvítt"</item>
<item msgid="2762241247228983754">"Í lit"</item>
diff --git a/packages/PrintSpooler/res/values-it/strings.xml b/packages/PrintSpooler/res/values-it/strings.xml
index b052341..394dc88 100644
--- a/packages/PrintSpooler/res/values-it/strings.xml
+++ b/packages/PrintSpooler/res/values-it/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Bianco e nero"</item>
<item msgid="2762241247228983754">"A colori"</item>
diff --git a/packages/PrintSpooler/res/values-iw/strings.xml b/packages/PrintSpooler/res/values-iw/strings.xml
index bb1967d..5c5e5d9 100644
--- a/packages/PrintSpooler/res/values-iw/strings.xml
+++ b/packages/PrintSpooler/res/values-iw/strings.xml
@@ -78,6 +78,8 @@
<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">
<item msgid="7602948745415174937">"שחור ולבן"</item>
<item msgid="2762241247228983754">"צבע"</item>
diff --git a/packages/PrintSpooler/res/values-ja/strings.xml b/packages/PrintSpooler/res/values-ja/strings.xml
index 8ef7517..6a396b3 100644
--- a/packages/PrintSpooler/res/values-ja/strings.xml
+++ b/packages/PrintSpooler/res/values-ja/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"モノクロ"</item>
<item msgid="2762241247228983754">"カラー"</item>
diff --git a/packages/PrintSpooler/res/values-ka-rGE/strings.xml b/packages/PrintSpooler/res/values-ka-rGE/strings.xml
index b65d013..44e77d7 100644
--- a/packages/PrintSpooler/res/values-ka-rGE/strings.xml
+++ b/packages/PrintSpooler/res/values-ka-rGE/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"შავ-თეთრი"</item>
<item msgid="2762241247228983754">"ფერი"</item>
diff --git a/packages/PrintSpooler/res/values-kk-rKZ/strings.xml b/packages/PrintSpooler/res/values-kk-rKZ/strings.xml
index ef914f0c..a79a511 100644
--- a/packages/PrintSpooler/res/values-kk-rKZ/strings.xml
+++ b/packages/PrintSpooler/res/values-kk-rKZ/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Қара & Ақ"</item>
<item msgid="2762241247228983754">"Түс"</item>
diff --git a/packages/PrintSpooler/res/values-km-rKH/strings.xml b/packages/PrintSpooler/res/values-km-rKH/strings.xml
index 3aac97f..87f4fde 100644
--- a/packages/PrintSpooler/res/values-km-rKH/strings.xml
+++ b/packages/PrintSpooler/res/values-km-rKH/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"ស & ខ្មៅ"</item>
<item msgid="2762241247228983754">"ពណ៌"</item>
diff --git a/packages/PrintSpooler/res/values-kn-rIN/strings.xml b/packages/PrintSpooler/res/values-kn-rIN/strings.xml
index 72e0bac..eff1127 100644
--- a/packages/PrintSpooler/res/values-kn-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-kn-rIN/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"ಕಪ್ಪು & ಬಿಳುಪು"</item>
<item msgid="2762241247228983754">"ಬಣ್ಣ"</item>
diff --git a/packages/PrintSpooler/res/values-ko/strings.xml b/packages/PrintSpooler/res/values-ko/strings.xml
index 0d9a4dd..7613ad7 100644
--- a/packages/PrintSpooler/res/values-ko/strings.xml
+++ b/packages/PrintSpooler/res/values-ko/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"흑백"</item>
<item msgid="2762241247228983754">"컬러"</item>
diff --git a/packages/PrintSpooler/res/values-ky-rKG/strings.xml b/packages/PrintSpooler/res/values-ky-rKG/strings.xml
index 7f6eb10..2ea1b57 100644
--- a/packages/PrintSpooler/res/values-ky-rKG/strings.xml
+++ b/packages/PrintSpooler/res/values-ky-rKG/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Кара-ак"</item>
<item msgid="2762241247228983754">"Түстүү"</item>
diff --git a/packages/PrintSpooler/res/values-lo-rLA/strings.xml b/packages/PrintSpooler/res/values-lo-rLA/strings.xml
index 7e65b72..511a72f 100644
--- a/packages/PrintSpooler/res/values-lo-rLA/strings.xml
+++ b/packages/PrintSpooler/res/values-lo-rLA/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"ຂາວດຳ"</item>
<item msgid="2762241247228983754">"ສີ"</item>
diff --git a/packages/PrintSpooler/res/values-lt/strings.xml b/packages/PrintSpooler/res/values-lt/strings.xml
index a3129cc..ce912be 100644
--- a/packages/PrintSpooler/res/values-lt/strings.xml
+++ b/packages/PrintSpooler/res/values-lt/strings.xml
@@ -78,6 +78,8 @@
<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">
<item msgid="7602948745415174937">"Nespalvotas"</item>
<item msgid="2762241247228983754">"Spalva"</item>
diff --git a/packages/PrintSpooler/res/values-lv/strings.xml b/packages/PrintSpooler/res/values-lv/strings.xml
index daadf99..bfba6c8 100644
--- a/packages/PrintSpooler/res/values-lv/strings.xml
+++ b/packages/PrintSpooler/res/values-lv/strings.xml
@@ -76,6 +76,8 @@
<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">
<item msgid="7602948745415174937">"Melnbalts"</item>
<item msgid="2762241247228983754">"Krāsa"</item>
diff --git a/packages/PrintSpooler/res/values-mk-rMK/strings.xml b/packages/PrintSpooler/res/values-mk-rMK/strings.xml
index 3ebc1c9..a81db01 100644
--- a/packages/PrintSpooler/res/values-mk-rMK/strings.xml
+++ b/packages/PrintSpooler/res/values-mk-rMK/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Црно-бела"</item>
<item msgid="2762241247228983754">"Во боја"</item>
diff --git a/packages/PrintSpooler/res/values-ml-rIN/strings.xml b/packages/PrintSpooler/res/values-ml-rIN/strings.xml
index 63b4060..c14c5ad 100644
--- a/packages/PrintSpooler/res/values-ml-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-ml-rIN/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"കറുപ്പ് & വെള്ള"</item>
<item msgid="2762241247228983754">"നിറം"</item>
diff --git a/packages/PrintSpooler/res/values-mn-rMN/strings.xml b/packages/PrintSpooler/res/values-mn-rMN/strings.xml
index a8628c9..789f085 100644
--- a/packages/PrintSpooler/res/values-mn-rMN/strings.xml
+++ b/packages/PrintSpooler/res/values-mn-rMN/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Хар & Цагаан"</item>
<item msgid="2762241247228983754">"Өнгө"</item>
diff --git a/packages/PrintSpooler/res/values-mr-rIN/strings.xml b/packages/PrintSpooler/res/values-mr-rIN/strings.xml
index c08e440..1d859cf 100644
--- a/packages/PrintSpooler/res/values-mr-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-mr-rIN/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"कृष्ण धवल"</item>
<item msgid="2762241247228983754">"रंग"</item>
diff --git a/packages/PrintSpooler/res/values-ms-rMY/strings.xml b/packages/PrintSpooler/res/values-ms-rMY/strings.xml
index 4ed1019..c86d0bf 100644
--- a/packages/PrintSpooler/res/values-ms-rMY/strings.xml
+++ b/packages/PrintSpooler/res/values-ms-rMY/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Hitam & Putih"</item>
<item msgid="2762241247228983754">"Warna"</item>
diff --git a/packages/PrintSpooler/res/values-my-rMM/strings.xml b/packages/PrintSpooler/res/values-my-rMM/strings.xml
index 0a65a11..9b2760a 100644
--- a/packages/PrintSpooler/res/values-my-rMM/strings.xml
+++ b/packages/PrintSpooler/res/values-my-rMM/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"အဖြူ အမည်း"</item>
<item msgid="2762241247228983754">"ရောင်စုံ"</item>
diff --git a/packages/PrintSpooler/res/values-nb/strings.xml b/packages/PrintSpooler/res/values-nb/strings.xml
index 7c74b39..cdec56f 100644
--- a/packages/PrintSpooler/res/values-nb/strings.xml
+++ b/packages/PrintSpooler/res/values-nb/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Svart og hvitt"</item>
<item msgid="2762241247228983754">"Farge"</item>
diff --git a/packages/PrintSpooler/res/values-ne-rNP/strings.xml b/packages/PrintSpooler/res/values-ne-rNP/strings.xml
index 42cd98b..427a9ae 100644
--- a/packages/PrintSpooler/res/values-ne-rNP/strings.xml
+++ b/packages/PrintSpooler/res/values-ne-rNP/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"कालो & सेतो"</item>
<item msgid="2762241247228983754">"रङ्ग"</item>
diff --git a/packages/PrintSpooler/res/values-nl/strings.xml b/packages/PrintSpooler/res/values-nl/strings.xml
index 4855e31..12d52f9 100644
--- a/packages/PrintSpooler/res/values-nl/strings.xml
+++ b/packages/PrintSpooler/res/values-nl/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Zwart-wit"</item>
<item msgid="2762241247228983754">"Kleur"</item>
diff --git a/packages/PrintSpooler/res/values-pa-rIN/strings.xml b/packages/PrintSpooler/res/values-pa-rIN/strings.xml
index 912cf4a..b8d0602 100644
--- a/packages/PrintSpooler/res/values-pa-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-pa-rIN/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"ਕਾਲਾ & ਚਿੱਟਾ"</item>
<item msgid="2762241247228983754">"ਰੰਗ"</item>
diff --git a/packages/PrintSpooler/res/values-pl/strings.xml b/packages/PrintSpooler/res/values-pl/strings.xml
index 055fb5f..dac4b0822 100644
--- a/packages/PrintSpooler/res/values-pl/strings.xml
+++ b/packages/PrintSpooler/res/values-pl/strings.xml
@@ -78,6 +78,8 @@
<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">
<item msgid="7602948745415174937">"Czarno-białe"</item>
<item msgid="2762241247228983754">"Kolor"</item>
diff --git a/packages/PrintSpooler/res/values-pt-rBR/strings.xml b/packages/PrintSpooler/res/values-pt-rBR/strings.xml
index 9e38506..b073ce1 100644
--- a/packages/PrintSpooler/res/values-pt-rBR/strings.xml
+++ b/packages/PrintSpooler/res/values-pt-rBR/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Preto e branco"</item>
<item msgid="2762241247228983754">"Cor"</item>
diff --git a/packages/PrintSpooler/res/values-pt-rPT/strings.xml b/packages/PrintSpooler/res/values-pt-rPT/strings.xml
index 73fabab..da512b1 100644
--- a/packages/PrintSpooler/res/values-pt-rPT/strings.xml
+++ b/packages/PrintSpooler/res/values-pt-rPT/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Preto e branco"</item>
<item msgid="2762241247228983754">"Cor"</item>
diff --git a/packages/PrintSpooler/res/values-pt/strings.xml b/packages/PrintSpooler/res/values-pt/strings.xml
index 9e38506..b073ce1 100644
--- a/packages/PrintSpooler/res/values-pt/strings.xml
+++ b/packages/PrintSpooler/res/values-pt/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Preto e branco"</item>
<item msgid="2762241247228983754">"Cor"</item>
diff --git a/packages/PrintSpooler/res/values-ro/strings.xml b/packages/PrintSpooler/res/values-ro/strings.xml
index 3ff1045..1a44182 100644
--- a/packages/PrintSpooler/res/values-ro/strings.xml
+++ b/packages/PrintSpooler/res/values-ro/strings.xml
@@ -76,6 +76,8 @@
<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">
<item msgid="7602948745415174937">"Alb-negru"</item>
<item msgid="2762241247228983754">"Color"</item>
diff --git a/packages/PrintSpooler/res/values-ru/strings.xml b/packages/PrintSpooler/res/values-ru/strings.xml
index a4e12fe..a6ef092 100644
--- a/packages/PrintSpooler/res/values-ru/strings.xml
+++ b/packages/PrintSpooler/res/values-ru/strings.xml
@@ -78,6 +78,8 @@
<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">
<item msgid="7602948745415174937">"Черно-белая"</item>
<item msgid="2762241247228983754">"Цветная"</item>
diff --git a/packages/PrintSpooler/res/values-si-rLK/strings.xml b/packages/PrintSpooler/res/values-si-rLK/strings.xml
index e8052d6..7520459 100644
--- a/packages/PrintSpooler/res/values-si-rLK/strings.xml
+++ b/packages/PrintSpooler/res/values-si-rLK/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"කළු සහ සුදු"</item>
<item msgid="2762241247228983754">"වර්ණය"</item>
diff --git a/packages/PrintSpooler/res/values-sk/strings.xml b/packages/PrintSpooler/res/values-sk/strings.xml
index 3e82076..4575040 100644
--- a/packages/PrintSpooler/res/values-sk/strings.xml
+++ b/packages/PrintSpooler/res/values-sk/strings.xml
@@ -78,6 +78,8 @@
<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">
<item msgid="7602948745415174937">"Čiernobiele"</item>
<item msgid="2762241247228983754">"Farba"</item>
diff --git a/packages/PrintSpooler/res/values-sl/strings.xml b/packages/PrintSpooler/res/values-sl/strings.xml
index 75c2abe..c0dfdb4 100644
--- a/packages/PrintSpooler/res/values-sl/strings.xml
+++ b/packages/PrintSpooler/res/values-sl/strings.xml
@@ -78,6 +78,8 @@
<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">
<item msgid="7602948745415174937">"Črno-belo"</item>
<item msgid="2762241247228983754">"Barvno"</item>
diff --git a/packages/PrintSpooler/res/values-sq-rAL/strings.xml b/packages/PrintSpooler/res/values-sq-rAL/strings.xml
index adbf700..dbbf238 100644
--- a/packages/PrintSpooler/res/values-sq-rAL/strings.xml
+++ b/packages/PrintSpooler/res/values-sq-rAL/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Bardhezi"</item>
<item msgid="2762241247228983754">"Ngjyra"</item>
diff --git a/packages/PrintSpooler/res/values-sr/strings.xml b/packages/PrintSpooler/res/values-sr/strings.xml
index 58e9622..5589298 100644
--- a/packages/PrintSpooler/res/values-sr/strings.xml
+++ b/packages/PrintSpooler/res/values-sr/strings.xml
@@ -76,6 +76,8 @@
<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">
<item msgid="7602948745415174937">"Црно-бело"</item>
<item msgid="2762241247228983754">"Боја"</item>
diff --git a/packages/PrintSpooler/res/values-sv/strings.xml b/packages/PrintSpooler/res/values-sv/strings.xml
index 0434903..a97430e 100644
--- a/packages/PrintSpooler/res/values-sv/strings.xml
+++ b/packages/PrintSpooler/res/values-sv/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Svartvit"</item>
<item msgid="2762241247228983754">"Färg"</item>
diff --git a/packages/PrintSpooler/res/values-sw/strings.xml b/packages/PrintSpooler/res/values-sw/strings.xml
index 7c08316..f4d51bf 100644
--- a/packages/PrintSpooler/res/values-sw/strings.xml
+++ b/packages/PrintSpooler/res/values-sw/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Nyeusi na Nyeupe"</item>
<item msgid="2762241247228983754">"Rangi"</item>
diff --git a/packages/PrintSpooler/res/values-ta-rIN/strings.xml b/packages/PrintSpooler/res/values-ta-rIN/strings.xml
index 4c9f135..4e292d2 100644
--- a/packages/PrintSpooler/res/values-ta-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-ta-rIN/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"கருப்பு & வெள்ளை"</item>
<item msgid="2762241247228983754">"வண்ணம்"</item>
diff --git a/packages/PrintSpooler/res/values-te-rIN/strings.xml b/packages/PrintSpooler/res/values-te-rIN/strings.xml
index ffa994b..8cb5aa6 100644
--- a/packages/PrintSpooler/res/values-te-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-te-rIN/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"నలుపు & తెలుపు"</item>
<item msgid="2762241247228983754">"రంగు"</item>
diff --git a/packages/PrintSpooler/res/values-th/strings.xml b/packages/PrintSpooler/res/values-th/strings.xml
index 89e3082..c501cf9 100644
--- a/packages/PrintSpooler/res/values-th/strings.xml
+++ b/packages/PrintSpooler/res/values-th/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"ขาวดำ"</item>
<item msgid="2762241247228983754">"สี"</item>
diff --git a/packages/PrintSpooler/res/values-tl/strings.xml b/packages/PrintSpooler/res/values-tl/strings.xml
index 445d8b0..18942e2 100644
--- a/packages/PrintSpooler/res/values-tl/strings.xml
+++ b/packages/PrintSpooler/res/values-tl/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Black & White"</item>
<item msgid="2762241247228983754">"Kulay"</item>
diff --git a/packages/PrintSpooler/res/values-tr/strings.xml b/packages/PrintSpooler/res/values-tr/strings.xml
index 66d1e99..a7ef18c 100644
--- a/packages/PrintSpooler/res/values-tr/strings.xml
+++ b/packages/PrintSpooler/res/values-tr/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Siyah Beyaz"</item>
<item msgid="2762241247228983754">"Renkli"</item>
diff --git a/packages/PrintSpooler/res/values-uk/strings.xml b/packages/PrintSpooler/res/values-uk/strings.xml
index fcd3fa6..1e21c06 100644
--- a/packages/PrintSpooler/res/values-uk/strings.xml
+++ b/packages/PrintSpooler/res/values-uk/strings.xml
@@ -78,6 +78,8 @@
<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">
<item msgid="7602948745415174937">"Чорно-білий"</item>
<item msgid="2762241247228983754">"Колір"</item>
diff --git a/packages/PrintSpooler/res/values-ur-rPK/strings.xml b/packages/PrintSpooler/res/values-ur-rPK/strings.xml
index 2c2c460..7356cdb 100644
--- a/packages/PrintSpooler/res/values-ur-rPK/strings.xml
+++ b/packages/PrintSpooler/res/values-ur-rPK/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"سیاہ و سفید"</item>
<item msgid="2762241247228983754">"رنگ"</item>
diff --git a/packages/PrintSpooler/res/values-uz-rUZ/strings.xml b/packages/PrintSpooler/res/values-uz-rUZ/strings.xml
index f88eca5..4b11608 100644
--- a/packages/PrintSpooler/res/values-uz-rUZ/strings.xml
+++ b/packages/PrintSpooler/res/values-uz-rUZ/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Oq & qora"</item>
<item msgid="2762241247228983754">"Rang"</item>
diff --git a/packages/PrintSpooler/res/values-vi/strings.xml b/packages/PrintSpooler/res/values-vi/strings.xml
index 2d1e8fa..af237a4 100644
--- a/packages/PrintSpooler/res/values-vi/strings.xml
+++ b/packages/PrintSpooler/res/values-vi/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Đen trắng"</item>
<item msgid="2762241247228983754">"Màu"</item>
diff --git a/packages/PrintSpooler/res/values-zh-rCN/strings.xml b/packages/PrintSpooler/res/values-zh-rCN/strings.xml
index 553bc89..9e8e166 100644
--- a/packages/PrintSpooler/res/values-zh-rCN/strings.xml
+++ b/packages/PrintSpooler/res/values-zh-rCN/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"黑白"</item>
<item msgid="2762241247228983754">"彩色"</item>
diff --git a/packages/PrintSpooler/res/values-zh-rHK/strings.xml b/packages/PrintSpooler/res/values-zh-rHK/strings.xml
index 74b301f..41a9f8e 100644
--- a/packages/PrintSpooler/res/values-zh-rHK/strings.xml
+++ b/packages/PrintSpooler/res/values-zh-rHK/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"黑白"</item>
<item msgid="2762241247228983754">"彩色"</item>
diff --git a/packages/PrintSpooler/res/values-zh-rTW/strings.xml b/packages/PrintSpooler/res/values-zh-rTW/strings.xml
index d91fe52..5e391fa 100644
--- a/packages/PrintSpooler/res/values-zh-rTW/strings.xml
+++ b/packages/PrintSpooler/res/values-zh-rTW/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"黑白"</item>
<item msgid="2762241247228983754">"彩色"</item>
diff --git a/packages/PrintSpooler/res/values-zu/strings.xml b/packages/PrintSpooler/res/values-zu/strings.xml
index 6ef2499..a2c3172 100644
--- a/packages/PrintSpooler/res/values-zu/strings.xml
+++ b/packages/PrintSpooler/res/values-zu/strings.xml
@@ -74,6 +74,8 @@
<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">
<item msgid="7602948745415174937">"Okumnyama nokumhlophe"</item>
<item msgid="2762241247228983754">"Umbala"</item>
diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml
index e92f74c..50237832 100644
--- a/packages/PrintSpooler/res/values/strings.xml
+++ b/packages/PrintSpooler/res/values/strings.xml
@@ -187,6 +187,18 @@
<!-- Label for a printer that is not available. [CHAR LIMIT=25] -->
<string name="printer_unavailable"><xliff:g id="print_job_name" example="Canon-123GHT">%1$s</xliff:g> – unavailable</string>
+ <!-- Title for a warning message about security implications of using a print service,
+ displayed as a dialog message when the user prints using a print service that has not been
+ used before. [CHAR LIMIT=NONE] -->
+ <string name="print_service_security_warning_title">Use
+ <xliff:g id="service" example="My Print Service">%1$s</xliff:g>?</string>
+
+ <!-- Summary for a warning message about security implications of using a print service,
+ displayed as a dialog message when the user prints using a print service that has not been
+ used before. [CHAR LIMIT=NONE] -->
+ <string name="print_service_security_warning_summary">Your document may pass through one or
+ more servers on its way to the printer.</string>
+
<!-- Arrays -->
<!-- Color mode labels. -->
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
index bafccae..7adcfec 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
@@ -50,6 +50,7 @@
import com.android.internal.os.HandlerCaller;
import com.android.internal.util.FastXmlSerializer;
import com.android.printspooler.R;
+import com.android.printspooler.util.ApprovedPrintServices;
import libcore.io.IoUtils;
@@ -67,6 +68,7 @@
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
/**
* Service for exposing some of the {@link PrintSpooler} functionality to
@@ -136,10 +138,10 @@
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- synchronized (mLock) {
- String prefix = (args.length > 0) ? args[0] : "";
- String tab = " ";
+ String prefix = (args.length > 0) ? args[0] : "";
+ String tab = " ";
+ synchronized (mLock) {
pw.append(prefix).append("print jobs:").println();
final int printJobCount = mPrintJobs.size();
for (int i = 0; i < printJobCount; i++) {
@@ -160,6 +162,14 @@
}
}
}
+
+ pw.append(prefix).append("approved print services:").println();
+ Set<String> approvedPrintServices = (new ApprovedPrintServices(this)).getApprovedServices();
+ if (approvedPrintServices != null) {
+ for (String approvedService : approvedPrintServices) {
+ pw.append(prefix).append(tab).append(approvedService).println();
+ }
+ }
}
private void sendOnPrintJobQueued(PrintJobInfo printJob) {
@@ -1307,6 +1317,12 @@
PrintSpoolerService.this.setPrintJobCancelling(printJobId, cancelling);
}
+ @Override
+ public void removeApprovedPrintService(ComponentName serviceToRemove) {
+ (new ApprovedPrintServices(PrintSpoolerService.this))
+ .removeApprovedService(serviceToRemove);
+ }
+
public PrintSpoolerService getService() {
return PrintSpoolerService.this;
}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index e8a5e43..53e07e9 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -17,14 +17,21 @@
package com.android.printspooler.ui;
import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
@@ -81,6 +88,7 @@
import com.android.printspooler.model.RemotePrintDocument.RemotePrintDocumentInfo;
import com.android.printspooler.renderer.IPdfEditor;
import com.android.printspooler.renderer.PdfManipulationService;
+import com.android.printspooler.util.ApprovedPrintServices;
import com.android.printspooler.util.MediaSizeUtils;
import com.android.printspooler.util.MediaSizeUtils.MediaSizeComparator;
import com.android.printspooler.util.PageRangeUtils;
@@ -88,6 +96,7 @@
import com.android.printspooler.widget.PrintContentView;
import com.android.printspooler.widget.PrintContentView.OptionsStateChangeListener;
import com.android.printspooler.widget.PrintContentView.OptionsStateController;
+
import libcore.io.IoUtils;
import libcore.io.Streams;
@@ -655,9 +664,11 @@
}
}
- PrinterId printerId = mCurrentPrinter.getId();
- final int index = mDestinationSpinnerAdapter.getPrinterIndex(printerId);
- mDestinationSpinner.setSelection(index);
+ if (mCurrentPrinter != null) {
+ PrinterId printerId = mCurrentPrinter.getId();
+ final int index = mDestinationSpinnerAdapter.getPrinterIndex(printerId);
+ mDestinationSpinner.setSelection(index);
+ }
}
private void startAdvancedPrintOptionsActivity(PrinterInfo printer) {
@@ -1184,12 +1195,125 @@
mPrintButton.setOnClickListener(clickListener);
}
+ /**
+ * A dialog that asks the user to approve a {@link PrintService}. This dialog is automatically
+ * dismissed if the same {@link PrintService} gets approved by another
+ * {@link PrintServiceApprovalDialog}.
+ */
+ private static final class PrintServiceApprovalDialog extends DialogFragment
+ implements OnSharedPreferenceChangeListener {
+ private static final String PRINTSERVICE_KEY = "PRINTSERVICE";
+ private ApprovedPrintServices mApprovedServices;
+
+ /**
+ * Create a new {@link PrintServiceApprovalDialog} that ask the user to approve a
+ * {@link PrintService}.
+ *
+ * @param printService The {@link ComponentName} of the service to approve
+ * @return A new {@link PrintServiceApprovalDialog} that might approve the service
+ */
+ static PrintServiceApprovalDialog newInstance(ComponentName printService) {
+ PrintServiceApprovalDialog dialog = new PrintServiceApprovalDialog();
+
+ Bundle args = new Bundle();
+ args.putParcelable(PRINTSERVICE_KEY, printService);
+ dialog.setArguments(args);
+
+ return dialog;
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+
+ mApprovedServices.unregisterChangeListener(this);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ ComponentName printService = getArguments().getParcelable(PRINTSERVICE_KEY);
+ synchronized (ApprovedPrintServices.sLock) {
+ if (mApprovedServices.isApprovedService(printService)) {
+ dismiss();
+ } else {
+ mApprovedServices.registerChangeListenerLocked(this);
+ }
+ }
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ super.onCreateDialog(savedInstanceState);
+
+ mApprovedServices = new ApprovedPrintServices(getActivity());
+
+ PackageManager packageManager = getActivity().getPackageManager();
+ CharSequence serviceLabel;
+ try {
+ ComponentName printService = getArguments().getParcelable(PRINTSERVICE_KEY);
+
+ serviceLabel = packageManager.getApplicationInfo(printService.getPackageName(), 0)
+ .loadLabel(packageManager);
+ } catch (NameNotFoundException e) {
+ serviceLabel = null;
+ }
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder.setTitle(getString(R.string.print_service_security_warning_title,
+ serviceLabel))
+ .setMessage(getString(R.string.print_service_security_warning_summary,
+ serviceLabel))
+ .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ ComponentName printService =
+ getArguments().getParcelable(PRINTSERVICE_KEY);
+ // Prevent onSharedPreferenceChanged from getting triggered
+ mApprovedServices
+ .unregisterChangeListener(PrintServiceApprovalDialog.this);
+
+ mApprovedServices.addApprovedService(printService);
+ ((PrintActivity) getActivity()).confirmPrint();
+ }
+ })
+ .setNegativeButton(android.R.string.cancel, null);
+
+ return builder.create();
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ ComponentName printService = getArguments().getParcelable(PRINTSERVICE_KEY);
+
+ synchronized (ApprovedPrintServices.sLock) {
+ if (mApprovedServices.isApprovedService(printService)) {
+ dismiss();
+ }
+ }
+ }
+ }
+
private final class MyClickListener implements OnClickListener {
@Override
public void onClick(View view) {
if (view == mPrintButton) {
if (mCurrentPrinter != null) {
- confirmPrint();
+ if (mDestinationSpinnerAdapter.getPdfPrinter() == mCurrentPrinter) {
+ confirmPrint();
+ } else {
+ ApprovedPrintServices approvedServices =
+ new ApprovedPrintServices(PrintActivity.this);
+
+ ComponentName printService = mCurrentPrinter.getId().getServiceName();
+ if (approvedServices.isApprovedService(printService)) {
+ confirmPrint();
+ } else {
+ PrintServiceApprovalDialog.newInstance(printService)
+ .show(getFragmentManager(), "approve");
+ }
+ }
} else {
cancelPrint();
}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/util/ApprovedPrintServices.java b/packages/PrintSpooler/src/com/android/printspooler/util/ApprovedPrintServices.java
new file mode 100644
index 0000000..dd10567
--- /dev/null
+++ b/packages/PrintSpooler/src/com/android/printspooler/util/ApprovedPrintServices.java
@@ -0,0 +1,156 @@
+/*
+ * 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.printspooler.util;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.printservice.PrintService;
+import android.util.ArraySet;
+
+import java.util.Set;
+
+/**
+ * Manage approved print services. These services are stored in the shared preferences.
+ */
+public class ApprovedPrintServices {
+ /**
+ * Used for locking accesses to the approved services.
+ */
+ static final public Object sLock = new Object();
+
+ private static final String APPROVED_SERVICES_PREFERENCE = "PRINT_SPOOLER_APPROVED_SERVICES";
+ private final SharedPreferences mPreferences;
+
+ /**
+ * Create a new {@link ApprovedPrintServices}
+ *
+ * @param owner The {@link Context} using this object.
+ */
+ public ApprovedPrintServices(Context owner) {
+ mPreferences = owner.getSharedPreferences(APPROVED_SERVICES_PREFERENCE,
+ Context.MODE_PRIVATE);
+ }
+
+ /**
+ * Get {@link Set} of approved services.
+ *
+ * @return A {@link Set} containing all currently approved services.
+ */
+ public Set<String> getApprovedServices() {
+ return mPreferences.getStringSet(APPROVED_SERVICES_PREFERENCE, null);
+ }
+
+ /**
+ * Check if a {@link PrintService} is approved.
+ *
+ * This function does not acquire the {@link #sLock}.
+ *
+ * @param service The {@link ComponentName} of the {@link PrintService} that might be approved
+ * @return true iff the service is currently approved
+ */
+ public boolean isApprovedService(ComponentName service) {
+ final Set<String> approvedServices = getApprovedServices();
+
+ if (approvedServices != null) {
+ final String flattenedString = service.flattenToShortString();
+
+ for (String approvedService : approvedServices) {
+ if (approvedService.equals(flattenedString)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Add a {@link PrintService} to the list of approved print services.
+ *
+ * @param serviceToAdd The {@link ComponentName} of the {@link PrintService} to be approved.
+ */
+ public void addApprovedService(ComponentName serviceToAdd) {
+ synchronized (sLock) {
+ Set<String> oldApprovedServices =
+ mPreferences.getStringSet(APPROVED_SERVICES_PREFERENCE, null);
+
+ Set<String> newApprovedServices;
+ if (oldApprovedServices == null) {
+ newApprovedServices = new ArraySet<String>(1);
+ } else {
+ // Copy approved services.
+ newApprovedServices = new ArraySet<String>(oldApprovedServices);
+ }
+ newApprovedServices.add(serviceToAdd.flattenToShortString());
+
+ SharedPreferences.Editor editor = mPreferences.edit();
+ editor.putStringSet(APPROVED_SERVICES_PREFERENCE, newApprovedServices);
+ editor.apply();
+ }
+ }
+
+ /**
+ * Add a {@link OnSharedPreferenceChangeListener} that listens for changes to the approved
+ * services. Should only be called while holding {@link #sLock} to synchronize against
+ * {@link #addApprovedService}.
+ *
+ * @param listener {@link OnSharedPreferenceChangeListener} to register
+ */
+ public void registerChangeListenerLocked(OnSharedPreferenceChangeListener listener) {
+ mPreferences.registerOnSharedPreferenceChangeListener(listener);
+ }
+
+ /**
+ * Unregister a listener registered in {@link #registerChangeListenerLocked}.
+ *
+ * @param listener {@link OnSharedPreferenceChangeListener} to unregister
+ */
+ public void unregisterChangeListener(OnSharedPreferenceChangeListener listener) {
+ mPreferences.unregisterOnSharedPreferenceChangeListener(listener);
+ }
+
+ /**
+ * If a {@link PrintService} is approved, remove it from the list of approved services.
+ *
+ * @param serviceToRemove The {@link ComponentName} of the {@link PrintService} to be removed
+ */
+ public void removeApprovedService(ComponentName serviceToRemove) {
+ synchronized (sLock) {
+ if (isApprovedService(serviceToRemove)) {
+ // Copy approved services.
+ ArraySet<String> approvedServices = new ArraySet<String>(
+ mPreferences.getStringSet(APPROVED_SERVICES_PREFERENCE, null));
+
+ SharedPreferences.Editor editor = mPreferences.edit();
+
+ final int numApprovedServices = approvedServices.size();
+ for (int i = 0; i < numApprovedServices; i++) {
+ if (approvedServices.valueAt(i)
+ .equals(serviceToRemove.flattenToShortString())) {
+ approvedServices.removeAt(i);
+ break;
+ }
+ }
+
+ editor.putStringSet(APPROVED_SERVICES_PREFERENCE, approvedServices);
+ editor.apply();
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/Android.mk b/packages/SettingsLib/Android.mk
index 0245ed3..c860668 100644
--- a/packages/SettingsLib/Android.mk
+++ b/packages/SettingsLib/Android.mk
@@ -3,6 +3,8 @@
LOCAL_MODULE := SettingsLib
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4
+
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/packages/SettingsLib/res/drawable/ic_menu.xml b/packages/SettingsLib/res/drawable/ic_menu.xml
new file mode 100644
index 0000000..910a3d0
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_menu.xml
@@ -0,0 +1,24 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M3.0,18.0l18.0,0.0l0.0,-2.0L3.0,16.0l0.0,2.0zm0.0,-5.0l18.0,0.0l0.0,-2.0L3.0,11.0l0.0,2.0zm0.0,-7.0l0.0,2.0l18.0,0.0L21.0,6.0L3.0,6.0z"/>
+</vector>
diff --git a/packages/SettingsLib/res/layout-television/settings_with_drawer.xml b/packages/SettingsLib/res/layout-television/settings_with_drawer.xml
new file mode 100644
index 0000000..e8ca691
--- /dev/null
+++ b/packages/SettingsLib/res/layout-television/settings_with_drawer.xml
@@ -0,0 +1,20 @@
+<!--
+ 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.
+-->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/content_frame"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
diff --git a/packages/SettingsLib/res/layout-watch/settings_with_drawer.xml b/packages/SettingsLib/res/layout-watch/settings_with_drawer.xml
new file mode 100644
index 0000000..e8ca691
--- /dev/null
+++ b/packages/SettingsLib/res/layout-watch/settings_with_drawer.xml
@@ -0,0 +1,20 @@
+<!--
+ 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.
+-->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/content_frame"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
diff --git a/packages/SettingsLib/res/layout/drawer_category.xml b/packages/SettingsLib/res/layout/drawer_category.xml
new file mode 100644
index 0000000..20afcd4
--- /dev/null
+++ b/packages/SettingsLib/res/layout/drawer_category.xml
@@ -0,0 +1,36 @@
+<!--
+ 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.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?android:attr/listDivider" />
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="12dp"
+ android:paddingBottom="12dp"
+ android:paddingStart="16dp"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+</LinearLayout>
diff --git a/packages/SettingsLib/res/layout/drawer_item.xml b/packages/SettingsLib/res/layout/drawer_item.xml
new file mode 100644
index 0000000..4b53049
--- /dev/null
+++ b/packages/SettingsLib/res/layout/drawer_item.xml
@@ -0,0 +1,40 @@
+<!--
+ 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.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/tile_item"
+ android:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:orientation="horizontal" >
+
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="72dp"
+ android:layout_height="24dp"
+ android:layout_gravity="center_vertical"
+ android:tint="?android:attr/colorAccent"
+ android:paddingStart="16dp"
+ android:paddingEnd="32dp" />
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:textColor="?android:attr/colorControlNormal"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+</LinearLayout>
diff --git a/packages/SettingsLib/res/layout/settings_with_drawer.xml b/packages/SettingsLib/res/layout/settings_with_drawer.xml
new file mode 100644
index 0000000..a9a78e8
--- /dev/null
+++ b/packages/SettingsLib/res/layout/settings_with_drawer.xml
@@ -0,0 +1,55 @@
+<!--
+ 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.
+-->
+<android.support.v4.widget.DrawerLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/drawer_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <!-- The main content view -->
+ <LinearLayout
+ android:id="@+id/content_parent"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" >
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="?android:attr/actionBarStyle">
+ <Toolbar
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/action_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:navigationContentDescription="@*android:string/action_bar_up_description"
+ android:theme="?android:attr/actionBarTheme"
+ style="?android:attr/toolbarStyle"
+ android:background="?android:attr/colorPrimary" />
+ </FrameLayout>
+ <FrameLayout
+ android:id="@+id/content_frame"
+ android:layout_width="match_parent"
+ android:layout_height="fill_parent" />
+ </LinearLayout>
+ <!-- The navigation drawer -->
+ <ListView android:id="@+id/left_drawer"
+ android:layout_width="300dp"
+ android:layout_height="match_parent"
+ android:layout_gravity="start"
+ android:choiceMode="singleChoice"
+ android:divider="@android:color/transparent"
+ android:dividerHeight="0dp"
+ android:background="?android:attr/colorBackground" />
+</android.support.v4.widget.DrawerLayout>
diff --git a/packages/SettingsLib/res/layout/user_preference.xml b/packages/SettingsLib/res/layout/user_preference.xml
new file mode 100644
index 0000000..75031fd
--- /dev/null
+++ b/packages/SettingsLib/res/layout/user_preference.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 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.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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:id="@android:id/widget_frame"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/user_spinner_item_height"
+ android:paddingStart="@dimen/user_spinner_padding_sides"
+ android:paddingEnd="@dimen/user_spinner_padding_sides"
+ android:orientation="horizontal" >
+
+ <ImageView
+ android:id="@+android:id/icon"
+ android:layout_width="@dimen/user_icon_view_height"
+ android:layout_height="@dimen/user_icon_view_height"
+ android:layout_gravity="center"
+ android:scaleType="fitCenter"
+ android:paddingBottom="@dimen/user_spinner_padding"
+ android:paddingTop="@dimen/user_spinner_padding" />
+
+ <TextView
+ android:id="@+android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:layout_gravity="center"
+ android:labelFor="@+android:id/icon"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ android:paddingStart="@dimen/user_spinner_padding"
+ android:paddingEnd="@dimen/user_spinner_padding"
+ style="?android:attr/textAppearanceMedium" />
+
+</LinearLayout>
diff --git a/packages/SettingsLib/res/values-af/arrays.xml b/packages/SettingsLib/res/values-af/arrays.xml
new file mode 100644
index 0000000..6693997
--- /dev/null
+++ b/packages/SettingsLib/res/values-af/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Skandeer tans…"</item>
+ <item msgid="8513729475867537913">"Koppel tans…"</item>
+ <item msgid="515055375277271756">"Staaf tans…"</item>
+ <item msgid="1943354004029184381">"Bekom tans IP-adres…"</item>
+ <item msgid="4221763391123233270">"Gekoppel"</item>
+ <item msgid="624838831631122137">"Opgeskort"</item>
+ <item msgid="7979680559596111948">"Ontkoppel tans…"</item>
+ <item msgid="1634960474403853625">"Ontkoppel"</item>
+ <item msgid="746097431216080650">"Onsuksesvol"</item>
+ <item msgid="6367044185730295334">"Geblokkeer"</item>
+ <item msgid="503942654197908005">"Vermy tydelik swak verbinding"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Skandeer tans…"</item>
+ <item msgid="355508996603873860">"Koppel tans aan <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="554971459996405634">"Staaf tans met <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="7928343808033020343">"Verkry tans IP-adres van <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="8937994881315223448">"Gekoppel aan <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Opgeskort"</item>
+ <item msgid="7698638434317271902">"Ontkoppel tans van <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="197508606402264311">"Ontkoppel"</item>
+ <item msgid="8578370891960825148">"Onsuksesvol"</item>
+ <item msgid="5660739516542454527">"Geblokkeer"</item>
+ <item msgid="1805837518286731242">"Vermy tydelik swak verbinding"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
new file mode 100644
index 0000000..afffe6d
--- /dev/null
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Kan nie skandeer vir netwerke nie"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Geen"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Gestoor"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Gedeaktiveer"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-opstelling het misluk"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi-verbinding het misluk"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Stawingsprobleem"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Nie binne ontvangs nie"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Geen internettoegang bespeur nie, sal nie outomaties herkoppel nie."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Gestoor deur <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Gekoppel via Wi-Fi-assistent"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Gekoppel via %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Beskikbaar via %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Gekoppel, geen internet nie"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Ontkoppel"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Ontkoppel tans…"</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Verbind tans…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Gekoppel"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Verbind tans…"</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Gekoppel (geen foon nie)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Gekoppel (geen media nie)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Gekoppel (geen boodskaptoegang nie)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Gekoppel (geen foon of media nie)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Media-oudio"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Foonoudio"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Lêeroordrag"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Invoertoestel"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Internettoegang"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Kontakdeling"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Gebruik vir kontakdeling"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Deling van internetverbinding"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Boodskaptoegang"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM-toegang"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Gekoppel aan media-oudio"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Gekoppel aan foonoudio"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Gekoppel aan lêeroordragbediener"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Gekoppel aan kaart"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Aan SAP gekoppel"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Nie gekoppel aan lêeroordragbediener nie"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Gekoppel aan invoertoestel"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Aan toestel gekoppel vir internettoegang"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Deel plaaslike internetverbinding met toestel"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Gebruik vir internettoegang"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Gebruik vir kaart"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Gebruik vir SIM-toegang"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Gebruik vir media-oudio"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Gebruik vir foonoudio"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Gebruik vir lêeroordrag"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Gebruik vir invoer"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Bind saam"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"BIND SAAM"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Kanselleer"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Saambinding bied toegang tot jou kontakte en oproepgeskiedenis wanneer dit gekoppel is."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Kon nie saambind met <xliff:g id="DEVICE_NAME">%1$s</xliff:g> nie."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Kon nie met <xliff:g id="DEVICE_NAME">%1$s</xliff:g> saambind nie weens \'n verkeerde PIN of wagwoordsleutel."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Kan nie met <xliff:g id="DEVICE_NAME">%1$s</xliff:g> kommunikeer nie."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Saambinding verwerp deur <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi af."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi is ontkoppel."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi een staaf."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wi-Fi twee stawe."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wi-Fi drie stawe."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Wi-Fi-sein vol."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android-bedryfstelsel"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Verwyderde programme"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Verwyderde programme en gebruikers"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB-verbinding"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Wi-Fi-warmkol"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth-verbinding"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Verbinding"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"USB-verbinding en Wi-Fi-warmkol"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Werkprofiel"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Gas"</string>
+ <string name="unknown" msgid="1592123443519355854">"Onbekend"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Gebruiker: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Sommige verstekke gestel"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Geen verstekke gestel nie"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Teks-na-spraak-instellings"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Teks-na-spraak-uitset"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Spraaktempo"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Spoed waarteen die teks gepraat word"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Taal"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Gebruik stelseltaal"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Taal nie gekies nie"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Stel die taalspesifieke stem vir gesproke teks"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Luister na \'n voorbeeld"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Speel \'n kort demonstrasie van spraaksintese"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Installeer stemdata"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Installeer die stemdata vereis vir spraaksintese"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Hierdie spraaksintese-enjin kan dalk al die teks versamel wat uitgespreek sal word, insluitend persoonlike data soos wagwoorde en kredietkaartnommers. Dit kom van die <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> enjin. Aktiveer die gebruik van hierdie spraaksintese-enjin?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Hierdie taal vereis \'n werkende netwerkverbinding vir teks-na-spraak-uitvoer."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Dit is \'n voorbeeld van spraaksintese"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Verstek taalstatus"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> word ten volle ondersteun"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> vereis netwerkverbinding"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> word nie ondersteun nie"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Kontroleer tans …"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Instellings vir <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Lanseer enjin-instellings"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Voorkeur-enjin"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Algemeen"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Baie stadig"</item>
+ <item msgid="4795095314303559268">"Stadig"</item>
+ <item msgid="8903157781070679765">"Normaal"</item>
+ <item msgid="164347302621392996">"Vinnig"</item>
+ <item msgid="5794028588101562009">"Vinniger"</item>
+ <item msgid="7163942783888652942">"Baie vinnig"</item>
+ <item msgid="7831712693748700507">"Snel"</item>
+ <item msgid="5194774745031751806">"Baie snel"</item>
+ <item msgid="9085102246155045744">"Vinnigste"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Kies profiel"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Persoonlik"</string>
+ <string name="category_work" msgid="8699184680584175622">"Werk"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-am/arrays.xml b/packages/SettingsLib/res/values-am/arrays.xml
new file mode 100644
index 0000000..46790d6
--- /dev/null
+++ b/packages/SettingsLib/res/values-am/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"በመቃኘት ላይ....."</item>
+ <item msgid="8513729475867537913">"በማገናኘት ላይ…"</item>
+ <item msgid="515055375277271756">"በማረጋገጥ ላይ...."</item>
+ <item msgid="1943354004029184381">"የIP አድራሻ በማግኘት ላይ..."</item>
+ <item msgid="4221763391123233270">"ተገናኝቷል"</item>
+ <item msgid="624838831631122137">"ታግዷል"</item>
+ <item msgid="7979680559596111948">"በማለያየት ላይ...."</item>
+ <item msgid="1634960474403853625">"ተለያየ"</item>
+ <item msgid="746097431216080650">"አልተሳካም"</item>
+ <item msgid="6367044185730295334">"ታግዷል"</item>
+ <item msgid="503942654197908005">"ለጊዜያዊነት ከደካማ ግኑኝነት በመታቀብ ላይ"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"በመቃኘት ላይ....."</item>
+ <item msgid="355508996603873860">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> በማያያዝ ላይ..."</item>
+ <item msgid="554971459996405634">"በ<xliff:g id="NETWORK_NAME">%1$s</xliff:g> በማረጋገጥ ላይ..."</item>
+ <item msgid="7928343808033020343">" ከ<xliff:g id="NETWORK_NAME">%1$s</xliff:g> የIP አድራሻ በማግኘት ላይ..."</item>
+ <item msgid="8937994881315223448">"ለ<xliff:g id="NETWORK_NAME">%1$s</xliff:g> የተገናኘ"</item>
+ <item msgid="1330262655415760617">"ታግዷል"</item>
+ <item msgid="7698638434317271902">"ከ<xliff:g id="NETWORK_NAME">%1$s</xliff:g> በማለያየት ላይ...."</item>
+ <item msgid="197508606402264311">"ተለያየ"</item>
+ <item msgid="8578370891960825148">"አልተሳካም"</item>
+ <item msgid="5660739516542454527">"ታግዷል"</item>
+ <item msgid="1805837518286731242">"ለጊዜያዊነት ከደካማ ግኑኝነት በመታቀብ ላይ"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
new file mode 100644
index 0000000..0e29d8e
--- /dev/null
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"ለአውታረመረቦች መቃኘት አይቻልም"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"የለም"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"ተቀምጧል"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"ተሰነክሏል"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"የአይ.ፒ. ውቅረት መሰናከል"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"የWiFi ግንኙነት መሰናከል"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"የማረጋገጫ ችግር"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"በክልል ውስጥ የለም"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"ምንም የበይነ መረብ መዳረሻ ተፈልጎ አልተገኘም፣ በራስ-ሰር እንደገና እንዲገናኝ አይደረግም።"</string>
+ <string name="saved_network" msgid="4352716707126620811">"የተቀመጠው በ<xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"በWi‑Fi ረዳት አማካኝነት ተገናኝቷል"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"በ%1$s በኩል መገናኘት"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"በ%1$s በኩል የሚገኝ"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"ተገናኝቷል፣ ምንም በይነመረብ የለም"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"ተለያይቷል"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"በመለያየት ላይ..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"በማገናኘት ላይ…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"ተገናኝቷል"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"በማገናኘት ላይ..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"ተያይዟል (ምንም ስልክ የለም)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"ተያይዟል (ምንም ማህደረ መረጃ የለም)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"ተገናኝቷል (ምንም የመልዕክት መዳረሻ የለም)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"ተያይዟል (ምንም ስልክ ወይም ማህደረ መረጃ የለም)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"የማህደረ መረጃ ኦዲዮ"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"የስልክ ኦዲዮ"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"ፋይል ማስተላለፍ"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"ግቤት መሣሪያ"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"የበይነመረብ ድረስ"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"እውቂያ ማጋራት"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"እውቂያን ለማጋራት ተጠቀም"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"የበይነ መረብ ተያያዥ ማጋሪያ"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"የመልዕክት መዳረሻ"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"የሲም መዳረሻ"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"ወደ ማህደረ መረጃ አውዲዮ ተያይዟል"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"ወደ ስልክ አውዲዮ ተያይዟል"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ወደ ፋይል ዝውውር አገልጋይ ተያይዟል"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"ከካርታ ጋር ተገናኝቷል"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"ከSAP ጋር ተገናኝቷል"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"ከፋይል ዝውውር አገልጋይ ጋር አልተያያዘም"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"ወደ ግቤት መሣሪያ ተያይዟል"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"ለበይነመረብ ድረስ ወደ መሣሪያ ተያይዟል"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"የአካባቢያዊ በይነመረብ ተያያዥ ከመሣሪያ ጋር በማጋራት ላይ"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"ለበይነ መረብ ድረስ ተጠቀም"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"ለካርታ ይጠቀሙ"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"ለሲም መዳረሻ መጠቀም"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"ለማህደረመረጃ ድምፅተጠቀም"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"ለስልክ ድምፅ ተጠቀም"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ለፋይል ዝውውር ተጠቀም"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ለውፅአት ተጠቀም"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"አጣምር"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"አጣምር"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"ይቅር"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"ማጣመር በግንኙነት ጊዜ የእርስዎ የእውቂያዎች እና የጥሪ ታሪክ መዳረሻን ይሰጣል።"</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"ከ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ማጣመር አልተቻለም::"</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"ከ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ጋር ትክክለኛ ባልሆነ ፒን ወይም የይለፍቁልፍ ምክንያት ማጣመር አልተቻለም::"</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"ከ<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ጋር ግንኙነት መመስረት አልተቻለም።"</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"ማጣመር በ<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ተገፍቷል።"</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wifi ጠፍቷል።"</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"የWifi ግንኙነት ተቋርጧል።"</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"አንድ የWiFi አሞሌ።"</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"ሁለት የWiFi አሞሌዎች።"</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"ሦስት የWiFi አሞሌዎች።"</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"የWiFi ምልክት ሙሉ ነው።"</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android ስርዓተ ክወና"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"የተወገዱ መተግበሪያዎች"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"የተወገዱ መተግበሪያዎች እና ተጠቃሚዎች"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB መሰካት"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ተጓጓዥ ድረስ ነጥቦች"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ብሉቱዝ ማያያዝ"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"መሰካት"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"ተጓጓዥ መዳረሻ ነጥብ እና ማገናኛ"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"የስራ መገለጫ"</string>
+ <string name="user_guest" msgid="8475274842845401871">"እንግዳ"</string>
+ <string name="unknown" msgid="1592123443519355854">"ያልታወቀ"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"ተጠቃሚ፦ <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"አንዳንድ ነባሪዎ ተዘጋጅተዋል"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"ምንም ነባሪዎች አልተዘጋጁም"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"ፅሁፍ-ወደ-ንግግር ቅንብሮች"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"የፅሁፍ- ወደ- ንግግር ውፅዓት"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">" የንግግር ደረጃ"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"የተነገረበትን ፅሁፍ አፍጥን"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"ቋንቋ"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"የስርዓት ቋንቋ ተጠቀም"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"ቋንቋ አልተመረጠም"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"ለሚነገረው ፅሁፍ ቋንቋ-ተኮር ድምፅ አዘጋጅ"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"ምሳሌውን አዳምጥ"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"አጭር የንግግር ልምምድ ማሳያ አጫውት"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"የድምፅ ውሂብ ጫን"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"ለንግግር ልምምድ የሚጠየቀውን የድምፅ ውሂብ ጫን"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"ይህ የንግግር ልምምድ አንቀሳቃሽ የሚነገረውን ፅሁፍ ሁሉ እንደ ይለፍ ቃል እና የዱቤ ካርድ ቁጥሮች፣ የግል ውሂብ ጨምሮ ለመሰብሰብ ይችል ይሆናል። ከ <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> አንቀሳቃሽ ይመጣል። የዚህን የንግግር ልምምድ አንቀሳቃሽ አጠቃቀም ይንቃ?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"ይህ ቋንቋ የጽሑፍ-ወደ-ንግግር ውጽዓት እንዲኖረው የሚሰራ የአውታረ መረብ ግንኙነት ያስፈልገዋል።"</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"ይህ የተሰራ ንግግር ምሳሌ ነው"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"የነባሪ ቋንቋ ሁኔታ"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> ሙሉ ለሙሉ ይደገፋል"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> የአውታረ መረብ ግንኙነት ያስፈልጋል"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> አይደገፍም"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"በማረጋገጥ ላይ…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"የ<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g> ቅንብሮች"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"የፍርግም ቅንብሮችን ያስጀምሩ"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"የተመረጠ ፍርግም"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"አጠቃላይ"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"በጣም ቀርፋፋ"</item>
+ <item msgid="4795095314303559268">"ቀርፋፋ"</item>
+ <item msgid="8903157781070679765">"መደበኛ"</item>
+ <item msgid="164347302621392996">"ፈጣን"</item>
+ <item msgid="5794028588101562009">"በጣም ፈጣን"</item>
+ <item msgid="7163942783888652942">"እጅግ በጣም ፈጣን"</item>
+ <item msgid="7831712693748700507">"ቀልጣፋ"</item>
+ <item msgid="5194774745031751806">"በጣም ቀልጣፋ"</item>
+ <item msgid="9085102246155045744">"እጅግ በጣም ቀልጣፋ"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"መገለጫ ይምረጡ"</string>
+ <string name="category_personal" msgid="1299663247844969448">"የግል"</string>
+ <string name="category_work" msgid="8699184680584175622">"ስራ"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-ar/arrays.xml b/packages/SettingsLib/res/values-ar/arrays.xml
new file mode 100644
index 0000000..866959f
--- /dev/null
+++ b/packages/SettingsLib/res/values-ar/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"جارٍ الفحص..."</item>
+ <item msgid="8513729475867537913">"جارٍ الاتصال…"</item>
+ <item msgid="515055375277271756">"جارٍ المصادقة…"</item>
+ <item msgid="1943354004029184381">"جارٍ الحصول على عنوان IP…"</item>
+ <item msgid="4221763391123233270">"متصل"</item>
+ <item msgid="624838831631122137">"معلق"</item>
+ <item msgid="7979680559596111948">"جارٍ فصل الاتصال..."</item>
+ <item msgid="1634960474403853625">"غير متصل"</item>
+ <item msgid="746097431216080650">"غير ناجح"</item>
+ <item msgid="6367044185730295334">"تم حظرها"</item>
+ <item msgid="503942654197908005">"يتم تجنب الاتصال الضعيف مؤقتًا"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"جارٍ الفحص..."</item>
+ <item msgid="355508996603873860">"جارٍ الاتصال بجهاز <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="554971459996405634">"جارٍ المصادقة مع <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="7928343808033020343">"جارٍ الحصول على عنوان IP من <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="8937994881315223448">"متصل بـ <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"معلق"</item>
+ <item msgid="7698638434317271902">"جارٍ قطع الاتصال بـ <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="197508606402264311">"غير متصل"</item>
+ <item msgid="8578370891960825148">"غير ناجح"</item>
+ <item msgid="5660739516542454527">"تم حظرها"</item>
+ <item msgid="1805837518286731242">"يتم تجنب الاتصال الضعيف مؤقتًا"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
new file mode 100644
index 0000000..89c661c
--- /dev/null
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"لا يمكن فحص الشبكات"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"لا شيء"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"تم الحفظ"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"معطلة"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"أخفقت تهيئة عنوان IP"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"أخفق اتصال WiFi"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"حدثت مشكلة في المصادقة"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"ليست في النطاق"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"لم يتم اكتشاف اتصال بالإنترنت."</string>
+ <string name="saved_network" msgid="4352716707126620811">"تم الحفظ بواسطة <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"تم التوصيل عبر مساعد Wi-Fi"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"تم الاتصال عبر %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"متوفرة عبر %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"متصلة، ولا يتوفر إنترنت"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"غير متصل"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"جارٍ قطع الاتصال..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"جارٍ الاتصال…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"متصل"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"جارٍ الاقتران..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"متصل (بجهاز غير الهاتف)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"متصل (بجهاز غير الوسائط)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"تم الاتصال (يتعذر الدخول إلى الرسائل)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"متصل (بجهاز غير الهاتف أو الوسائط)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"الإعدادات الصوتية للوسائط"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"الإعدادات الصوتية للهاتف"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"نقل الملف"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"جهاز الإرسال"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"الدخول إلى الإنترنت"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"مشاركة جهة الاتصال"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"استخدام مع مشاركة جهة الاتصال"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"مشاركة اتصال الإنترنت"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"الدخول إلى الرسائل"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"الوصول إلى شريحة SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"متصل بالإعدادات الصوتية للوسائط"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"متصل بالإعدادات الصوتية للهاتف"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"متصل بخادم نقل الملف"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"تم الاتصال بملف تعريف الدخول إلى الرسائل"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"تم الاتصال بـ SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"غير متصل بخادم نقل الملفات"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"متصل بجهاز الإدخال"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"متصل بالجهاز لدخول الإنترنت"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"مشاركة الاتصال المحلي بالإنترنت مع الجهاز"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"الاستخدام للدخول على الإنترنت"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"استخدام لملف تعريف الدخول إلى الرسائل"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"الاستخدام للوصول إلى شريحة SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"الاستخدام للإعدادات الصوتية للوسائط"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"الاستخدام لإعدادات الهاتف الصوتية"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"استخدامه لنقل الملفات"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"استخدام للإدخال"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"اقتران"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"إقران"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"إلغاء"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"يضمن لك الإقران إمكانية الدخول إلى جهات اتصالك وسجل المكالمات عند الاتصال."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"تعذر الإقران مع <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"تعذر الإقران مع <xliff:g id="DEVICE_NAME">%1$s</xliff:g> نظرًا لوجود رقم تعريف شخصي أو مفتاح مرور غير صحيح."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"لا يمكن الاتصال بـ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"تم رفض الاقتران بواسطة <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"تم إيقاف Wi-Fi."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"تم قطع اتصال Wi-Fi."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"إشارة Wi-Fi تتكون من شريط واحد."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"إشارة Wi-Fi تتكون من شريطين."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"إشارة Wi-Fi تتكون من ثلاثة أشرطة."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"إشارة Wi-Fi كاملة."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"نظام التشغيل Android"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"التطبيقات المزالة"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"التطبيقات والمستخدمون الذين تمت إزالتهم"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"ربط USB"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"نقطة اتصال محمولة"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ربط البلوتوث"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"ربط"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"الربط ونقطة الاتصال المحمولة"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"الملف الشخصي للعمل"</string>
+ <string name="user_guest" msgid="8475274842845401871">"مدعو"</string>
+ <string name="unknown" msgid="1592123443519355854">"غير معروف"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"المستخدم: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"تم تعيين بعض الإعدادات الافتراضية"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"لم يتم تعيين إعدادات افتراضية"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"إعدادات تحويل النص إلى كلام"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"إخراج تحويل النص إلى كلام"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"معدل سرعة الكلام"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"سرعة نطق الكلام"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"اللغة"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"استخدام لغة النظام"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"اللغة غير محددة"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"تعيين الصوت الخاص بلغة النص المنطوق"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"الاستماع إلى مثال"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"تشغيل عرض توضيحي قصير لتجميع الكلام"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"تثبيت البيانات الصوتية"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"تثبيت البيانات الصوتية المطلوبة لتجميع الكلام"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"ربما يمكن لمحرك اصطناع الحديث جمع كل النص التي سيتم نطقه، بما في ذلك البيانات الشخصية مثل كلمات المرور وأرقام بطاقة الائتمان. يتم إحضار ذلك من المحرك <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. هل تريد تمكين استخدام محرك اصطناع الحديث هذا؟"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"تتطلب هذه اللغة اتصال شبكة سليمًا لتحويل النص إلى كلام."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"هذا مثال لتركيب الكلام"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"حالة اللغة الافتراضية"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> متوافقة تمامًا"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> تتطلب اتصالاً بالشبكة"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> غير متوافقة"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"جارٍ التحقق…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"إعدادات <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"تشغيل إعدادات المحرك"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"المحرك المفضل"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"عامة"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"بطيء جدًا"</item>
+ <item msgid="4795095314303559268">"بطيء"</item>
+ <item msgid="8903157781070679765">"عادي"</item>
+ <item msgid="164347302621392996">"سريع"</item>
+ <item msgid="5794028588101562009">"أسرع"</item>
+ <item msgid="7163942783888652942">"سريع جدًا"</item>
+ <item msgid="7831712693748700507">"خاطف"</item>
+ <item msgid="5194774745031751806">"خاطف جدًا"</item>
+ <item msgid="9085102246155045744">"الأسرع"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"اختيار ملف شخصي"</string>
+ <string name="category_personal" msgid="1299663247844969448">"شخصي"</string>
+ <string name="category_work" msgid="8699184680584175622">"العمل"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-az-rAZ/arrays.xml b/packages/SettingsLib/res/values-az-rAZ/arrays.xml
new file mode 100644
index 0000000..442d155
--- /dev/null
+++ b/packages/SettingsLib/res/values-az-rAZ/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Skan edilir ..."</item>
+ <item msgid="8513729475867537913">"Qoşulur ..."</item>
+ <item msgid="515055375277271756">"Autentifikasiya edilir..."</item>
+ <item msgid="1943354004029184381">"IP ünvanı əldə edilir..."</item>
+ <item msgid="4221763391123233270">"Qoşulu"</item>
+ <item msgid="624838831631122137">"Durdurulmuş"</item>
+ <item msgid="7979680559596111948">"Ayrılır ..."</item>
+ <item msgid="1634960474403853625">"Əlaqə kəsildi"</item>
+ <item msgid="746097431216080650">"Uğursuz"</item>
+ <item msgid="6367044185730295334">"Bloklanıb"</item>
+ <item msgid="503942654197908005">"Keyfiyyətsiz bağlantıdan müvəqqəti qaçış"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Skan edilir..."</item>
+ <item msgid="355508996603873860">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> qoşulur…"</item>
+ <item msgid="554971459996405634">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> ilə autentifikasiya edilir..."</item>
+ <item msgid="7928343808033020343">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> şəbəkəsindən IP ünvan əldə olunur..."</item>
+ <item msgid="8937994881315223448">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> qoşuldu"</item>
+ <item msgid="1330262655415760617">"Dayandırılıb"</item>
+ <item msgid="7698638434317271902">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> şəbəkəsindən ayrılır..."</item>
+ <item msgid="197508606402264311">"Bağlantı kəsildi"</item>
+ <item msgid="8578370891960825148">"Uğursuz"</item>
+ <item msgid="5660739516542454527">"Bloklanıb"</item>
+ <item msgid="1805837518286731242">"Zəif bağlantı müvəqqəti aradan qaldırılır"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-az-rAZ/strings.xml b/packages/SettingsLib/res/values-az-rAZ/strings.xml
new file mode 100644
index 0000000..cb73beb
--- /dev/null
+++ b/packages/SettingsLib/res/values-az-rAZ/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Şəbəkə axtarmaq olmur"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Heç biri"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Yadda saxlanılan"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Deaktiv"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Konfiqurasiya Uğursuzluğu"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi Bağlantı Uğursuzluğu"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Autentifikasiya problemi"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Diapazonda deyil"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"İnternet bağlantısı tapılmadı, avtomatik olaraq yenidən qoşulmayacaq."</string>
+ <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> tərəfindən saxlandı"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fi köməkçisi vasitəsilə qoşulub"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s vasitəsilə qoşuludur"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s vasitəsilə əlçatandır"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Qoşuludur, internet yoxdur"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Ayrıldı"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Ayrılır..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Qoşulur..."</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Qoşulu"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Cütləşdirmə"</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Qoşulub (telefonsuz)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Qoşuludur (media yoxdur)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Qoşulu (mesaj girişi yoxdur)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Bağlantı yaradılıb (telefon və ya media deyil)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Media audio"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Telefon audio"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Fayl transferi"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Daxiletmə cihazı"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"İnternet girişi"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Kontakt paylaşımı"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Kontakt paylaşımı üçün istifadə edin"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"internet bağlantı paylaşımı"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Mesaj Girişi"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM Girişi"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Media audioya birləşdirilib"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Telefon audiosuna qoşulu"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Fayl transfer serverinə qoşulu"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Xəritəyə qoşulu"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"SAP(es-ey-pi)\'yə qoşuludur"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Fayl transfer serverinə qoşulmayıb"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Daxiletmə cihazına qoşuldu"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"İnternet girişi üçün cihaza birləşdi"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Lokal internet bağlantısı cihazla paylaşılır"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"İnternet girişi üçün istifadə edin"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Xəritə üçün istifadə et"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"SIM girişi üçün istifadə edin"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Media audio istifadə edin"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Telefon audiosu istifadə edin"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Fayl transferi üçün istifadə edin"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Daxiletmə üçün istifadə edin"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Birləşdir"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"CÜTLƏNDİR"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Ləğv et"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Qoşulan zaman kontaktlarınıza və çağrı tarixçəsinə giriş cütlənməsi."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ilə birləşdirmək alınmadı."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Yanlış PIN və ya parola görə <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ilə cütləşmək alınmadı."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ilə ünsiyyət qurula bilmir."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Cütləşdirmə <xliff:g id="DEVICE_NAME">%1$s</xliff:g> tərəfindən rədd edildi."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wifi sönülüdür."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wifi bağlantı kəsildi."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wifi bir xətdir."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wifi iki xətdir."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wifi üç xətdir."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Wifi siqnalı tamdır."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Silinmiş tətbiqlər"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Tətbiqləri və istifadəçiləri silin"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB Birləşmə"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Portativ hotspot"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth birləşmə"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Birləşmə"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Birləşmə və daşınan hotspot"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"İş profili"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Qonaq"</string>
+ <string name="unknown" msgid="1592123443519355854">"Naməlum"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"İstifadəçi: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Bəzi susmaya görələr təyin edilib"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Susmaya görələr təyin edilməyib."</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Mətndən-danışığa parametrləri"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Mətndən-nitqə çıxışı"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Nitq diapazonu"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Mətnin səsləndirilmə sürəti"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Dil"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Sistem dili işlədin"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Dil seçilməyib"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Danışılan oxunulan mətnə dil üçün spesifik səs ayarlayır"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Nümunə dinləyin"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Nitq sintezindən nümunə göstərin"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Səs datasını quraşdırın"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Nitq sintezi üçün səs datası quraşdırın"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Bu nitq sitnez mühərriki danışılan bütün mətni, həmçinin parollarınızı və kredir kart nömrələrinizi toplaya bilər. <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> mühərrikindən gəlir. Nitq sintez mühərriki istifadə olunsun?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Bu dil mətnin nitqə çıxışı üçün şəbəkə bağlantısı tələb edir."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Bu, bir nitq sintez nümunəsidir."</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Defolt dil statusu"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> tam dəstəklənir"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> şəbəkə bağlantısı tələb edir"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> dəstəklənmir"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Yoxlanılır..."</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g> üçün ayarlar"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Mühərrik parametrlərini başladın"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Tərcih olunmuş mühərrik"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Ümumi"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Çox yavaş"</item>
+ <item msgid="4795095314303559268">"Yavaş"</item>
+ <item msgid="8903157781070679765">"Normal"</item>
+ <item msgid="164347302621392996">"Sürətli"</item>
+ <item msgid="5794028588101562009">"Daha sürətli"</item>
+ <item msgid="7163942783888652942">"Çox sürətli"</item>
+ <item msgid="7831712693748700507">"Tez"</item>
+ <item msgid="5194774745031751806">"Çox tez"</item>
+ <item msgid="9085102246155045744">"Ən sürətli"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Profil Seçin"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Şəxsi"</string>
+ <string name="category_work" msgid="8699184680584175622">"İş"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-bg/arrays.xml b/packages/SettingsLib/res/values-bg/arrays.xml
new file mode 100644
index 0000000..a754787
--- /dev/null
+++ b/packages/SettingsLib/res/values-bg/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Сканира се..."</item>
+ <item msgid="8513729475867537913">"Установява се връзка…"</item>
+ <item msgid="515055375277271756">"Удостоверява се..."</item>
+ <item msgid="1943354004029184381">"Получава се IP адрес"</item>
+ <item msgid="4221763391123233270">"Установена е връзка"</item>
+ <item msgid="624838831631122137">"Прекъсната"</item>
+ <item msgid="7979680559596111948">"Изключва се..."</item>
+ <item msgid="1634960474403853625">"Изкл."</item>
+ <item msgid="746097431216080650">"Неуспешно"</item>
+ <item msgid="6367044185730295334">"Блокирано"</item>
+ <item msgid="503942654197908005">"Временно се избягва лоша връзка"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Сканира се..."</item>
+ <item msgid="355508996603873860">"Установява се връзка с/ъс <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="554971459996405634">"Удостоверява се с/ъс <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="7928343808033020343">"IP адресът от <xliff:g id="NETWORK_NAME">%1$s</xliff:g> се получава..."</item>
+ <item msgid="8937994881315223448">"Установена е връзка с/ъс<xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Прекъсната"</item>
+ <item msgid="7698638434317271902">"Изключва се от <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="197508606402264311">"Изкл."</item>
+ <item msgid="8578370891960825148">"Неуспешно"</item>
+ <item msgid="5660739516542454527">"Блокирано"</item>
+ <item msgid="1805837518286731242">"Временно се избягва лоша връзка"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
new file mode 100644
index 0000000..5e9eacd
--- /dev/null
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Не може да се сканира за мрежи"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Няма"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Запазено"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Деактивирани"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Неуспешно конфигуриране на IP адреса"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Неуспешна връзка с Wi-Fi"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Проблем при удостоверяването"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Извън обхват"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Не е открит достъп до интернет. Няма да се свърже отново автоматично."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Запазено от <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Установена е връзка чрез помощника за Wi-Fi"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Установена е връзка през „%1$s“"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Мрежата е достъпна през „%1$s“"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Установена е връзка – няма достъп до интернет"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Изкл."</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Изключва се..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Установява се връзка…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Установена е връзка"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Сдвояване..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Свързано (без телефона)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Свързано (без мултимедията)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Има връзка (няма достъп до съобщенията)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Свързано (без телефона или мултимедията)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Мултимедийно аудио"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Звук на телефона"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Прехвърляне на файл"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Входно устройство"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Достъп до интернет"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Споделяне на контакти"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Използване за споделяне на контакти"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Споделяне на връзката с интернет"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Достъп до съобщенията"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Достъп до SIM картата"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Установена е връзка с медийно аудио"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Връзка със звука на телефона"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Установена е връзка със сървър за трансфер на файлове"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Има връзка с MAP"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Установена е връзка със SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Няма връзка със сървър за трансфер на файлове"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Има връзка с устройството за въвеждане"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Има връзка с у-во за достъп до интернет"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Лок. връзка с интернет се споделя с у-вото"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Използване за достъп до интернет"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Да се използва за MAP"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Използване за достъп до SIM картата"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Употреба за медийно аудио"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Използване на телефон за аудио"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Използване на за пренос на файлове"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Да се използва за въвеждане"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Сдвояване"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"СДВОЯВАНЕ"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Отказ"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"При свързване сдвояването предоставя достъп до вашите контакти и история на обажданията."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Не можа да се сдвои с/ъс <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Не можа да се сдвои с/ъс <xliff:g id="DEVICE_NAME">%1$s</xliff:g> поради неправилен ПИН или код за достъп."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Не може да се свърже с/ъс <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Сдвояването е отхвърлено от <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi е изключен."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Няма връзка с Wi-Fi."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi е с една чертичка."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wi-Fi е с две чертички."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wi-Fi е с три чертички."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Сигналът за Wi-Fi е пълен."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android (ОС)"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Премахнати приложения"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Премахнати приложения и потребители"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"Тетъринг през USB"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Преносима точка за достъп"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Тетъринг през Bluetooth"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Тетъринг"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Тетъринг и пренос. точка за достъп"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Служебен потр. профил"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Гост"</string>
+ <string name="unknown" msgid="1592123443519355854">"Неизвестно"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Потребител: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Зададени са някои стандартни настройки"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Няма зададени стандартни настройки"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Настройки за синтезиран говор"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Синтезиран говор"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Скорост на речта"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Скорост, с която се изговаря текстът"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Език"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Използване на системния език"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Езикът не е избран"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Задава глас за конкретен език за изговорения текст"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Чуйте пример"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Възпроизвеждане на кратка демонстрация на синтезиране на реч"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Инсталиране на гласови данни"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Инсталиране на гласови данни, нужни за синтезиране на реч"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Тази машина за синтезиране на реч може да събира всички изговорено от вас, включително лични данни като пароли и номера на кредитни карти. Тя произлиза от машината <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Искате ли да я активирате?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Този език изисква работеща връзка с мрежата за синтезирания говор."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Това е пример за синтезиране на говор"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Състояние на основния език"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> се поддържа напълно"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"За <xliff:g id="LOCALE">%1$s</xliff:g> се изисква връзка с мрежа"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> не се поддържа"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Извършва се проверка…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Настройки за <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Стартиране на настройките на машината"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Предпочитана машина"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Общи"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Много бавна"</item>
+ <item msgid="4795095314303559268">"Бавна"</item>
+ <item msgid="8903157781070679765">"Нормална"</item>
+ <item msgid="164347302621392996">"Бърза"</item>
+ <item msgid="5794028588101562009">"По-бърза"</item>
+ <item msgid="7163942783888652942">"Много бърза"</item>
+ <item msgid="7831712693748700507">"Изключително бърза"</item>
+ <item msgid="5194774745031751806">"Свръхбърза"</item>
+ <item msgid="9085102246155045744">"Най-бърза"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Избиране на потр. профил"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Лични"</string>
+ <string name="category_work" msgid="8699184680584175622">"Служебни"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-bn-rBD/arrays.xml b/packages/SettingsLib/res/values-bn-rBD/arrays.xml
new file mode 100644
index 0000000..367e268
--- /dev/null
+++ b/packages/SettingsLib/res/values-bn-rBD/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"স্ক্যান করা হচ্ছে…"</item>
+ <item msgid="8513729475867537913">"সংযুক্ত হচ্ছে..."</item>
+ <item msgid="515055375277271756">"প্রমাণীকরণ হচ্ছে..."</item>
+ <item msgid="1943354004029184381">"IP ঠিকানা প্রাপ্ত করা হচ্ছে..."</item>
+ <item msgid="4221763391123233270">"সংযুক্ত হয়েছে"</item>
+ <item msgid="624838831631122137">"স্থগিত করা হয়েছে"</item>
+ <item msgid="7979680559596111948">"সংযোগ বিচ্ছিন্ন হচ্ছে..."</item>
+ <item msgid="1634960474403853625">"সংযোগ বিচ্ছিন্ন করা হয়েছে"</item>
+ <item msgid="746097431216080650">"অসফল"</item>
+ <item msgid="6367044185730295334">"ব্লক করা"</item>
+ <item msgid="503942654197908005">"সাময়িকরূপে দুর্বল সংযোগ এড়ানো হচ্ছে"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"স্ক্যান করা হচ্ছে…"</item>
+ <item msgid="355508996603873860">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> এর সাথে সংযুক্ত হচ্ছে…"</item>
+ <item msgid="554971459996405634">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> দিয়ে প্রমাণীকরণ করা হচ্ছে..."</item>
+ <item msgid="7928343808033020343">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> থেকে IP ঠিকানা জানা হচ্ছে…"</item>
+ <item msgid="8937994881315223448">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> তে সংযুক্ত হয়েছে"</item>
+ <item msgid="1330262655415760617">"স্থগিত করা হয়েছে"</item>
+ <item msgid="7698638434317271902">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> থেকে সংযোগ বিচ্ছিন্ন হচ্ছে…"</item>
+ <item msgid="197508606402264311">"সংযোগ বিচ্ছিন্ন করা হয়েছে"</item>
+ <item msgid="8578370891960825148">"অসফল"</item>
+ <item msgid="5660739516542454527">"অবরুদ্ধ"</item>
+ <item msgid="1805837518286731242">"সাময়িকরূপে দুর্বল সংযোগ এড়ানো হচ্ছে"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-bn-rBD/strings.xml b/packages/SettingsLib/res/values-bn-rBD/strings.xml
new file mode 100644
index 0000000..786e7d6
--- /dev/null
+++ b/packages/SettingsLib/res/values-bn-rBD/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"নেটওয়ার্কগুলির জন্য স্ক্যান করা যাবে না"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"কোনো কিছুই নয়"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"সংরক্ষিত"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"অক্ষম হয়েছে"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP কনফিগারেশনের ব্যর্থতা"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi সংযোগের ব্যর্থতা"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"প্রমাণীকরণ সমস্যা"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"পরিসরের মধ্যে নয়"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"কোনো ইন্টারনেট অ্যাক্সেস শনাক্ত হয়নি, স্বয়ংক্রিয়ভাবে পুনরায় সংযোগ স্থাপন করবে না৷"</string>
+ <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> দ্বারা সংরক্ষিত"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fi সহায়ক-এর মাধ্যমে সংযুক্ত হয়েছে"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s মাধ্যমে সংযুক্ত হয়েছে"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s এর মাধ্যমে উপলব্ধ"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"সংযুক্ত, ইন্টারনেট নেই"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"সংযোগ বিচ্ছিন্ন করা হয়েছে"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"সংযোগ বিচ্ছিন্ন হচ্ছে..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"সংযুক্ত হচ্ছে..."</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"সংযুক্ত হয়েছে"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"যুক্ত করা হচ্ছে..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"সংযুক্ত (কোনো ফোন নেই)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"সংযুক্ত (কোনো মিডিয়া নেই)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"সংযুক্ত (কোনো বার্তা অ্যাক্সেস নেই)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"সংযুক্ত (কোনো ফোন বা মিডিয়া নেই)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"মিডিয়া অডিও"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"ফোন অডিও"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"ফাইল স্থানান্তর"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"ইনপুট ডিভাইস"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"ইন্টারনেট অ্যাক্সেস"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"পরিচিতি ভাগ করা"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"পরিচিতি ভাগ করার কাজে ব্যবহার করুন"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"ইন্টারনেট সংযোগ ভাগ করা হচ্ছে"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"বার্তা অ্যাক্সেস"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM -এর অ্যাক্সেস"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"মিডিয়া অডিওতে সংযুক্ত রয়েছে"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"ফোন অডিওতে সংযুক্ত"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ফাইল স্থানান্তর সার্ভারের সঙ্গে সংযুক্ত"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"মানচিত্রে সংযুক্ত"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"SAP -তে সংযুক্ত হয়েছে"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"ফাইল স্থানান্তর সার্ভারের সঙ্গে সংযুক্ত নয়"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"ইনপুট ডিভাইসে সংযুক্ত"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"ইন্টারনেট অ্যাক্সেসের জন্য ডিভাইসে সংযুক্ত"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"ডিভাইসের সাথে স্থানীয় ইন্টারনেট সংযোগ ভাগ করছে"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"ইন্টারনেট অ্যাক্সেসের জন্য ব্যবহার করুন"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"মানচিত্রের জন্য ব্যবহার করুন"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"SIM -এর অ্যাক্সেসের জন্য ব্যবহার করুন"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"মিডিয়া অডিওয়ের জন্য ব্যবহার করুন"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"ফোন অডিওয়ের জন্য ব্যবহার করুন"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ফাইল স্থানান্তরের জন্য ব্যবহার করুন"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ইনপুটের জন্য ব্যবহার করুন"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"যুক্ত করুন"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"যুক্ত করুন"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"বাতিল করুন"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"যুক্ত করা থাকলে তা সংযুক্ত থাকাকালীন অবস্থায় আপনার পরিচিতিগুলি এবং কলের ইতিহাসকে অ্যাক্সেস করতে অনুমোদিত করে৷"</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> এর সাথে যুক্ত করা যায়নি।"</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"ভুল পিন বা পাস কী দেওয়ার কারণে <xliff:g id="DEVICE_NAME">%1$s</xliff:g> এর সঙ্গে যুক্ত করা যায়নি।"</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> এর সঙ্গে যোগাযোগ করতে পারবেন না।"</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"যুক্ত করা <xliff:g id="DEVICE_NAME">%1$s</xliff:g> প্রত্যাখ্যান করেছে।"</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wifi বন্ধ৷"</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wifi এর সংযোগ বিচ্ছিন্ন হয়েছে৷"</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wifi এ একটি দণ্ড৷"</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wifi এ দুইটি দণ্ড৷"</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wifi এ তিনটি দণ্ড৷"</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Wifi এ সম্পূর্ণ সিগন্যাল৷"</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"সরানো অ্যাপ্লিকেশানগুলি"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"সরানো অ্যাপ্লিকেশানগুলি এবং ব্যবহারকারীগণ"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB টেদারিং"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"পোর্টেবল হটস্পট"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth টেদারিং"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"টেদারিং"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"টেদারিং ও পোর্টেবল হটস্পট"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"কর্মস্থলের প্রোফাইল"</string>
+ <string name="user_guest" msgid="8475274842845401871">"অতিথি"</string>
+ <string name="unknown" msgid="1592123443519355854">"অজানা"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"ব্যবহারকারী: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"কিছু ডিফল্ট সেট করা রয়েছে"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"কোনো ডিফল্ট সেট করা নেই"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"পাঠ্য থেকে ভাষ্য আউটপুট সেটিংস"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"লেখিত-থেকে-ভাষ্য"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"ভাষ্য হার"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"যে গতিতে পাঠ্য উচ্চারিত হয়"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"ভাষা"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"সিস্টেমের ভাষা ব্যবহার করুন"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"ভাষা নির্বাচন করা নেই"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"কথ্য পাঠ্যের জন্য ভাষা-নির্দিষ্ট ভয়েস সেট করে"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"একটি উদাহরণ শুনুন"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"কথন সংশ্লেষণের উপর একটি ছোট ডেমো প্লে করুন"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"ভয়েস ডেটা ইনস্টল করুন"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"বিবৃতি সংশ্লেষণের জন্য প্রয়োজনীয় ভয়েস ডেটা ইনস্টল করুন"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"এই বিবৃতি সংশ্লেষণ ইঞ্জিন হয়তো পাসওয়ার্ড এবং ক্রেডিট কার্ড নম্বর সহ কথ্য সমস্ত পাঠ্য সংগ্রহ করতে সক্ষম হতে পারে। এটি <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> ইঞ্জিন থেকে আসে। এই বিবৃতি সংশ্লেষণ ইঞ্জিন সক্ষম করবেন?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"পাঠ্য থেকে ভাষ্য আউটপুটের জন্য এই ভাষার একটি কাজ করছে এমন নেটওয়ার্ক সংযোগ প্রয়োজন।"</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"এটি হল ভাষ্য সংশ্লেষণের একটি উদাহরণ"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"ডিফল্ট ভাষা স্থিতি"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> সম্পূর্ণ সমর্থিত"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> এর নেটওয়ার্ক সংযোগের প্রয়োজন"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> সমর্থিত নয়"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"পরীক্ষা করা হচ্ছে..."</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g> জন্য সেটিংস"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"ইঞ্জিন সেটিংস লঞ্চ করুন"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"পছন্দের ইঞ্জিন"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"সাধারণ"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"অত্যন্ত ধীরে"</item>
+ <item msgid="4795095314303559268">"ধীর"</item>
+ <item msgid="8903157781070679765">"স্বাভাবিক"</item>
+ <item msgid="164347302621392996">"দ্রুত"</item>
+ <item msgid="5794028588101562009">"আরো দ্রুত"</item>
+ <item msgid="7163942783888652942">"খুব দ্রুত"</item>
+ <item msgid="7831712693748700507">"দ্রুত"</item>
+ <item msgid="5194774745031751806">"খুব দ্রুত"</item>
+ <item msgid="9085102246155045744">"দ্রুততম"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"প্রোফাইল বেছে নিন"</string>
+ <string name="category_personal" msgid="1299663247844969448">"ব্যক্তিগত"</string>
+ <string name="category_work" msgid="8699184680584175622">"কর্মক্ষেত্র"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-ca/arrays.xml b/packages/SettingsLib/res/values-ca/arrays.xml
new file mode 100644
index 0000000..1bcbe6e
--- /dev/null
+++ b/packages/SettingsLib/res/values-ca/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"S\'està cercant..."</item>
+ <item msgid="8513729475867537913">"S\'està connectant…"</item>
+ <item msgid="515055375277271756">"S\'està autenticant…"</item>
+ <item msgid="1943354004029184381">"S\'està obtenint l\'adreça IP…"</item>
+ <item msgid="4221763391123233270">"Connectat"</item>
+ <item msgid="624838831631122137">"Suspesa"</item>
+ <item msgid="7979680559596111948">"S\'està desconnectant..."</item>
+ <item msgid="1634960474403853625">"Desconnectada"</item>
+ <item msgid="746097431216080650">"Incorrecte"</item>
+ <item msgid="6367044185730295334">"Bloquejada"</item>
+ <item msgid="503942654197908005">"S\'està evitant temporalment una connexió feble"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"S\'està cercant..."</item>
+ <item msgid="355508996603873860">"S\'està connectant a <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="554971459996405634">"S\'està autenticant amb <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="7928343808033020343">"S\'està obtenint l\'adreça IP de <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="8937994881315223448">"Connectat a <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Suspesa"</item>
+ <item msgid="7698638434317271902">"S\'està desconnectant de <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="197508606402264311">"Desconnectada"</item>
+ <item msgid="8578370891960825148">"Incorrecte"</item>
+ <item msgid="5660739516542454527">"Bloquejada"</item>
+ <item msgid="1805837518286731242">"S\'està evitant temporalment una connexió feble"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
new file mode 100644
index 0000000..28f196f
--- /dev/null
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"No es poden cercar xarxes"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Cap"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Desat"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Desactivat"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Error de configuració d\'IP"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Error de connexió Wi-Fi"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problema d\'autenticació"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Fora de l\'abast"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"No s\'ha detectat accés a Internet, no s\'hi tornarà a connectar automàticament."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Desat per <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Connectat mitjançant l\'assistent de Wi‑Fi"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Connectada mitjançant %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Disponible mitjançant %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Connectada, sense Internet"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Desconnectat"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"S\'està desconnectant..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"S\'està connectant…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Connectat"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"S\'està vinculant..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Connectats (sense telèfon)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Connectat (sense fitxers multimèdia)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Connectat (no hi ha accés als missatges)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Connectat (sense telèfon o disp. mult.)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Àudio multimèdia"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Àudio del telèfon"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Transferència del fitxer"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Dispositiu d\'entrada"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Accés a Internet"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Compartir contactes"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"S\'utilitza per compartir contactes."</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Connexió compartida a Internet"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Accés al missatge"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Accés a la SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Connectat a l\'àudio del mitjà"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Connectat a àudio del telèfon"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Connectat al servidor de transferència de fitxers"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Connectat a un mapa"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Connectat a SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"No està connectat al servidor de transferència de fitxers"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Connectat a dispositiu d\'entrada"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Connectat al dispositiu per a accés a Internet"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"S\'està compartint la connexió a Internet amb el dispositiu"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Utilitza\'l per a l\'accés a Internet"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Utilitza per al mapa"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Utilitza per a l\'accés a la SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Utilitza per a l\'àudio del mitjà"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Utilitza-ho per a l\'àudio del telèfon"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Utilitza per a la transferència de fitxers"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Utilitza per a entrada"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Vincula"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"VINCULA"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Cancel·la"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"La vinculació permet accedir als contactes i a l\'historial de trucades quan el dispositiu està connectat."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"No s\'ha pogut vincular amb <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"No s\'ha pogut vincular amb <xliff:g id="DEVICE_NAME">%1$s</xliff:g>, perquè el PIN o la contrasenya són incorrectes."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"No es pot comunicar amb <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Vinculació rebutjada per <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi desactivada."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi desconnectada."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Senyal Wi-Fi: una barra."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Senyal Wi-Fi: dues barres."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Senyal Wi-Fi: tres barres."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Senyal Wi-Fi: complet."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Aplicacions eliminades"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Aplicacions i usuaris eliminats"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"Ancoratge d\'USB"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Zona Wi-Fi portàtil"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Ancoratge de Bluetooth"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Ancoratge a xarxa"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Ancoratge a la xarxa i zona Wi-Fi"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Perfil professional"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Convidat"</string>
+ <string name="unknown" msgid="1592123443519355854">"Desconegut"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Usuari: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"S\'han definit alguns valors predeterminats"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"No s\'ha definit cap valor predeterminat"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Configuració de síntesi de veu"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Síntesi de veu"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Velocitat de veu"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Velocitat de lectura del text"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Idioma"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Utilitza l\'idioma del sistema"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"No has seleccionat cap idioma"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Defineix la llengua utilitzada per a la síntesi de veu"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Vull escoltar un exemple"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Reprodueix una breu demostració de síntesi de veu"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Instal·la dades de veu"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Instal·la les dades de veu necessàries per a la síntesi de veu"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Pot ser que aquest motor de síntesi de la parla pugui recopilar tot el text que es dirà en veu alta, incloses les dades personals, com ara les contrasenyes i els números de les targetes de crèdit. Ve del motor <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Voleu activar l\'ús d\'aquest motor de síntesi de la parla?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Aquest idioma requereix una connexió de xarxa activa per a la sortida de síntesi de veu."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Això és un exemple de síntesi de veu"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Estat de l\'idioma predeterminat"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> és totalment compatible"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"Es necessita una connexió de xarxa per a <xliff:g id="LOCALE">%1$s</xliff:g>"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> no és compatible"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"S\'està comprovant…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Configuració de: <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Obre la configuració del motor"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Motor preferit"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"General"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Molt lenta"</item>
+ <item msgid="4795095314303559268">"Lenta"</item>
+ <item msgid="8903157781070679765">"Normal"</item>
+ <item msgid="164347302621392996">"Ràpida"</item>
+ <item msgid="5794028588101562009">"Més ràpida"</item>
+ <item msgid="7163942783888652942">"Molt ràpida"</item>
+ <item msgid="7831712693748700507">"Veloç"</item>
+ <item msgid="5194774745031751806">"Molt veloç"</item>
+ <item msgid="9085102246155045744">"Màxima"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Triar un perfil"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Personal"</string>
+ <string name="category_work" msgid="8699184680584175622">"Feina"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-cs/arrays.xml b/packages/SettingsLib/res/values-cs/arrays.xml
new file mode 100644
index 0000000..d990f46
--- /dev/null
+++ b/packages/SettingsLib/res/values-cs/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Vyhledávání..."</item>
+ <item msgid="8513729475867537913">"Připojování..."</item>
+ <item msgid="515055375277271756">"Ověřování..."</item>
+ <item msgid="1943354004029184381">"Načítání IP adresy…"</item>
+ <item msgid="4221763391123233270">"Připojeno"</item>
+ <item msgid="624838831631122137">"Pozastaveno"</item>
+ <item msgid="7979680559596111948">"Odpojování..."</item>
+ <item msgid="1634960474403853625">"Odpojeno"</item>
+ <item msgid="746097431216080650">"Neúspěšné"</item>
+ <item msgid="6367044185730295334">"Blokováno"</item>
+ <item msgid="503942654197908005">"Dočasné předcházení slabému připojení"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Vyhledávání..."</item>
+ <item msgid="355508996603873860">"Připojování k síti <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="554971459996405634">"Ověřování v síti <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="7928343808033020343">"Získávání IP adresy ze sítě <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="8937994881315223448">"Připojeno k síti <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Pozastaveno"</item>
+ <item msgid="7698638434317271902">"Odpojování od sítě <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="197508606402264311">"Odpojeno"</item>
+ <item msgid="8578370891960825148">"Neúspěšné"</item>
+ <item msgid="5660739516542454527">"Blokováno"</item>
+ <item msgid="1805837518286731242">"Dočasné předcházení slabému připojení"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
new file mode 100644
index 0000000..c14278a
--- /dev/null
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nelze hledat sítě"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Žádné"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Uloženo"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Vypnuto"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Selhání konfigurace protokolu IP"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Selhání připojení Wi-Fi"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problém s ověřením"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Mimo dosah"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Nebyl zjištěn žádný přístup k internetu, připojení nebude automaticky obnoveno."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Uloženo uživatelem <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Připojeno pomocí asistenta připojení Wi-Fi"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Připojeno prostřednictvím %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Dostupné prostřednictvím %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Připojeno, není k dispozici internet"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Odpojeno"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Odpojování..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Připojování..."</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Připojeno"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Párování..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Připojeno (žádný telefon)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Připojeno (žádná média)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Připojeno (bez přístupu ke zprávám)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Připojeno (žádný telefon nebo média)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Zvuk médií"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Zvuk telefonu"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Přenos souborů"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Vstupní zařízení"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Přístup k internetu"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Sdílení kontaktů"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Použít ke sdílení kontaktů"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Sdílení internetového připojení"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Přístup ke zprávám"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Přístup k SIM kartě"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Připojeno ke zvukovému médiu"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Připojeno k náhlavní soupravě"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Připojeno k serveru pro přenos dat"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Připojeno k mapě"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Připojeno k SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Nepřipojeno k serveru pro přenos souborů"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Připojeno ke vstupnímu zařízení"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Připojeno k zařízení za účelem přístupu k internetu"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Místní internetové připojení je sdíleno se zařízením"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Použít pro přístup k internetu"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Použít pro mapu"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Používat pro přístup k SIM kartě"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Umožňuje připojení zvukového média"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Umožňuje připojení náhlavní soupravy"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Použít pro přenos souborů"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Použít pro vstup"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Párovat"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"PÁROVAT"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Zrušit"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Párováním připojenému zařízení udělíte přístup ke svým kontaktům a historii volání."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Nelze párovat se zařízením <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Nelze párovat se zařízením <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. Kód PIN nebo přístupový klíč je nesprávný."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Se zařízením <xliff:g id="DEVICE_NAME">%1$s</xliff:g> nelze navázat komunikaci."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Párování odmítnuto zařízením <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Síť Wi-Fi je vypnuta."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Síť Wi-Fi je odpojena."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi – jedna čárka."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wi-Fi – dvě čárky."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wi-Fi – tři čárky."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Wi-Fi – plný signál."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"OS Android"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Odebrané aplikace"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Odebrané aplikace a odebraní uživatelé"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"Připojení přes USB"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Přenosný hotspot"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Připojení přes Bluetooth"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Sdílené připojení"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Sdílené připojení a přenosný hotspot"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Pracovní profil"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Host"</string>
+ <string name="unknown" msgid="1592123443519355854">"Neznámé"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Uživatel: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Je nastaveno několik výchozích hodnot"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Nejsou nastaveny žádné výchozí hodnoty"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Nastavení převodu textu na řeč"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Převod textu na řeč"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Rychlost řeči"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Rychlost mluveného textu"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Jazyk"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Použít jazyk systému"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Nebyl vybrán jazyk"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Nastavení jazyka hlasu pro mluvený text"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Poslechnout příklad"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Přehrát krátkou ukázku syntézy řeči"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Nainstalovat hlasová data"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Nainstalovat hlasová data pro syntézu řeči"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Modul pro syntézu řeči může shromažďovat veškerý mluvený text, včetně osobních dat jako jsou hesla nebo čísla kreditních karet. Je založen na modulu <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Chcete modul pro syntézu řeči aktivovat?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Pro tento jazyk je k převodu textu na hlasový výstup potřeba připojení k síti."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Toto je příklad syntézy řeči"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Stav výchozího jazyka"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"Jazyk <xliff:g id="LOCALE">%1$s</xliff:g> je plně podporován"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"Jazyk <xliff:g id="LOCALE">%1$s</xliff:g> vyžaduje připojení k síti"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"Jazyk <xliff:g id="LOCALE">%1$s</xliff:g> není podporován"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Probíhá kontrola…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Spustit vyhledávač"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Preferovaný modul"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Obecné"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Velmi pomalá"</item>
+ <item msgid="4795095314303559268">"Pomalá"</item>
+ <item msgid="8903157781070679765">"Normální"</item>
+ <item msgid="164347302621392996">"Mírně rychlá"</item>
+ <item msgid="5794028588101562009">"Středně rychlá"</item>
+ <item msgid="7163942783888652942">"Rychlá"</item>
+ <item msgid="7831712693748700507">"Velmi rychlá"</item>
+ <item msgid="5194774745031751806">"Ultra rychlá"</item>
+ <item msgid="9085102246155045744">"Nejrychlejší"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Vyberte profil"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Osobní"</string>
+ <string name="category_work" msgid="8699184680584175622">"Pracovní"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-da/arrays.xml b/packages/SettingsLib/res/values-da/arrays.xml
new file mode 100644
index 0000000..a8c2a45
--- /dev/null
+++ b/packages/SettingsLib/res/values-da/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Scanner ..."</item>
+ <item msgid="8513729475867537913">"Opretter forbindelse..."</item>
+ <item msgid="515055375277271756">"Godkender..."</item>
+ <item msgid="1943354004029184381">"Henter IP-adresse…"</item>
+ <item msgid="4221763391123233270">"Tilsluttet"</item>
+ <item msgid="624838831631122137">"Sat på pause"</item>
+ <item msgid="7979680559596111948">"Afbryder ..."</item>
+ <item msgid="1634960474403853625">"Afbrudt"</item>
+ <item msgid="746097431216080650">"Mislykkedes"</item>
+ <item msgid="6367044185730295334">"Blokeret"</item>
+ <item msgid="503942654197908005">"Undgår midlertidigt dårlig forbindelse"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Scanner ..."</item>
+ <item msgid="355508996603873860">"Opretter forbindelse til <xliff:g id="NETWORK_NAME">%1$s</xliff:g> ..."</item>
+ <item msgid="554971459996405634">"Godkender med <xliff:g id="NETWORK_NAME">%1$s</xliff:g> ..."</item>
+ <item msgid="7928343808033020343">"Henter IP-adresse fra <xliff:g id="NETWORK_NAME">%1$s</xliff:g> ..."</item>
+ <item msgid="8937994881315223448">"Forbundet til <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Sat på pause"</item>
+ <item msgid="7698638434317271902">"Afbryder fra <xliff:g id="NETWORK_NAME">%1$s</xliff:g> ..."</item>
+ <item msgid="197508606402264311">"Afbrudt"</item>
+ <item msgid="8578370891960825148">"Mislykkedes"</item>
+ <item msgid="5660739516542454527">"Blokeret"</item>
+ <item msgid="1805837518286731242">"Undgår midlertidigt dårlig forbindelse"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
new file mode 100644
index 0000000..ed7d0db
--- /dev/null
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Der kan ikke søges efter netværk"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Ingen"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Gemt"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Deaktiveret"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-konfigurationsfejl"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi-forbindelsesfejl"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problem med godkendelse"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Ikke inden for rækkevidde"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Der blev ikke fundet nogen internetadgang. Forbindelsen bliver ikke automatisk genoprettet."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Gemt af <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Forbindelse via Wi-Fi-assistent"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Tilsluttet via %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Tilgængelig via %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Tilsluttet – intet internet"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Afbrudt"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Afbryder ..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Opretter forbindelse..."</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Tilsluttet"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Parrer..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Forbundet (ingen telefon)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Tilsluttet (intet medie)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Forbundet (ingen adgang til meddelelse)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Forbundet (ingen telefon eller medier)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Medielyd"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Telefonlyd"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Filoverførsel"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Inputenhed"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Internetadgang"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Deling af kontaktpersoner"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Brug til deling af kontaktpersoner"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Deling af internetforbindelse"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Adgang til meddelelse"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM-adgang"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Forbundet til medielyd"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Forbundet til telefonlyd"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Forbundet til filoverførselsserver"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Forbundet til kort"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Sluttet til SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Ikke forbundet til filoverførselsserver"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Forbundet til inputenhed"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Tilsluttet enhed/internet"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Deler lokal internetforbindelse med enhed"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Brug til internetadgang"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Brug til kort"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Brug til SIM-adgang"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Brug til medielyd"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Brug til telefonlyd"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Brug til filoverførsel"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Brug til input"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Par"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ACCEPTÉR PARRING"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Annuller"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Parring giver adgang til dine kontaktpersoner og din opkaldshistorik, når enhederne er forbundet."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Der kunne ikke parres med <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Der kunne ikke parres med <xliff:g id="DEVICE_NAME">%1$s</xliff:g> på grund af en forkert pinkode eller adgangsnøgle."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Der kan ikke kommunikeres med <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Parring afvist af <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi er slået fra."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi er afbrudt."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi har én bjælke."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wi-Fi har to bjælker."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wi-Fi har tre bjælker."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Wi-Fi har fuldt signal."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Fjernede apps"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Fjernede apps og brugere"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"Netdeling via USB"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Hotspot"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Netdeling via Bluetooth"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Netdeling"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Netdeling og hotspot"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Arbejdsprofil"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Gæst"</string>
+ <string name="unknown" msgid="1592123443519355854">"Ukendt"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Bruger: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Der er angivet nogle standarder"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Der er ikke angivet nogen standarder"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Indstillinger for oplæsning"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Oplæsning"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Talehastighed"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Oplæsningshastighed for tekst"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Sprog"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Brug systemsprog"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Der er ikke valgt sprog"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Angiver det sprog, der skal bruges til oplæsning af tekst"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Lyt til et eksempel"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Afspil en kort demonstration af talesyntese"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Installer stemmedata"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Installer de påkrævede stemmedata for talesyntese"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Denne funktion kan indsamle al den tekst, der læses op, inklusive personlige data såsom adgangskoder og kreditkortnumre. Den kommer fra <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>-maskinen. Vil du aktivere brug af denne talesyntesemaskine?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Oplæsning på dette sprog fungerer kun, når der findes en aktiv netværksforbindelse."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Dette er et eksempel på talesyntese."</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Status for standardsprog"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> er fuldt understøttet"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> kræver netværksforbindelse"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> understøttes ikke"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Kontrollerer…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Indstillinger for <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Åbn indstillinger for maskinen"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Foretrukken maskine"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Generelt"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Meget langsom"</item>
+ <item msgid="4795095314303559268">"Langsom"</item>
+ <item msgid="8903157781070679765">"Normal"</item>
+ <item msgid="164347302621392996">"Hurtig"</item>
+ <item msgid="5794028588101562009">"Hurtigere"</item>
+ <item msgid="7163942783888652942">"Meget hurtig"</item>
+ <item msgid="7831712693748700507">"Lynhurtig"</item>
+ <item msgid="5194774745031751806">"Ekstra lynhurtig"</item>
+ <item msgid="9085102246155045744">"Hurtigst"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Vælg profil"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Personlig"</string>
+ <string name="category_work" msgid="8699184680584175622">"Arbejde"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-de/arrays.xml b/packages/SettingsLib/res/values-de/arrays.xml
new file mode 100644
index 0000000..6772c7d
--- /dev/null
+++ b/packages/SettingsLib/res/values-de/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Scan läuft…"</item>
+ <item msgid="8513729475867537913">"Verbindung wird hergestellt..."</item>
+ <item msgid="515055375277271756">"Authentifizierung..."</item>
+ <item msgid="1943354004029184381">"IP-Adresse wird abgerufen..."</item>
+ <item msgid="4221763391123233270">"Verbunden"</item>
+ <item msgid="624838831631122137">"Angehalten"</item>
+ <item msgid="7979680559596111948">"Verbindung wird getrennt..."</item>
+ <item msgid="1634960474403853625">"Nicht verbunden"</item>
+ <item msgid="746097431216080650">"Nicht erfolgreich"</item>
+ <item msgid="6367044185730295334">"Blockiert"</item>
+ <item msgid="503942654197908005">"Schlechte Internetverbindung wird vorübergehend vermieden."</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Scan läuft…"</item>
+ <item msgid="355508996603873860">"Verbindung mit <xliff:g id="NETWORK_NAME">%1$s</xliff:g> wird hergestellt..."</item>
+ <item msgid="554971459996405634">"Authentifizierung mit <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="7928343808033020343">"IP-Adresse wird von <xliff:g id="NETWORK_NAME">%1$s</xliff:g> abgerufen..."</item>
+ <item msgid="8937994881315223448">"Verbunden mit <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Angehalten"</item>
+ <item msgid="7698638434317271902">"Verbindung mit <xliff:g id="NETWORK_NAME">%1$s</xliff:g> wird getrennt..."</item>
+ <item msgid="197508606402264311">"Nicht verbunden"</item>
+ <item msgid="8578370891960825148">"Nicht erfolgreich"</item>
+ <item msgid="5660739516542454527">"Blockiert"</item>
+ <item msgid="1805837518286731242">"Schlechte Internetverbindung wird vorübergehend vermieden."</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
new file mode 100644
index 0000000..6f38bb3
--- /dev/null
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Netzwerkscan nicht möglich"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Keine"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Gespeichert"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Deaktiviert"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-Konfigurationsfehler"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WLAN-Verbindungsfehler"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Authentifizierungsproblem"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Nicht in Reichweite"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Keine Internetverbindung erkannt, es kann nicht automatisch eine Verbindung hergestellt werden."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Gespeichert von <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Über WLAN-Assistenten verbunden"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Über %1$s verbunden"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Verfügbar über %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Verbunden, kein Internet"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Nicht verbunden"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Verbindung wird getrennt..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Verbindung wird hergestellt..."</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Verbunden"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Pairing läuft…"</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Verbunden (kein Telefon)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Verbunden (außer Audiomedien)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Verbunden (ohne Nachrichtenzugriff)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Verbunden (außer Telefon- und Audiomedien)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Media-Audio"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Telefon-Audio"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Dateiübertragung"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Eingabegerät"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Internetzugriff"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Kontaktfreigabe"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Für Kontaktfreigabe nutzen"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Freigabe der Internetverbindung"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Nachrichtenzugriff"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Zugriff auf SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Verbunden mit Audiosystem von Medien"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Verbunden mit Audiosystem des Telefons"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Mit Dateiübertragungsserver verbunden"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Mit Karte verbunden"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Mit SAP verbunden"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Nicht mit Dateiübertragungsserver verbunden"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Mit einem Eingabegerät verbunden"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Für Internetzugriff an Gerät angeschlossen"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Lokale Internetverbindung für Gerät freigeben"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Für Internetzugriff verwenden"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Für Karte verwenden"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Für SIM-Zugriff verwenden"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Für Audiosystem von Medien verwenden"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Für Audiosystem des Telefons verwenden"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Für Dateiübertragung verwenden"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Für Eingabe verwenden"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Pairing durchführen"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"Pairing durchführen"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Abbrechen"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Über das Pairing kann auf Ihre Kontakte und auf Ihren Anrufverlauf zugegriffen werden, wenn eine Verbindung besteht."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Pairing mit <xliff:g id="DEVICE_NAME">%1$s</xliff:g> war nicht möglich."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Pairing mit <xliff:g id="DEVICE_NAME">%1$s</xliff:g> war nicht möglich, weil die eingegebene PIN oder der Zugangscode falsch ist."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Kommunikation mit <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ist nicht möglich."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Verbindung wurde von <xliff:g id="DEVICE_NAME">%1$s</xliff:g> abgelehnt."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"WLAN: aus"</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"WLAN getrennt"</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"WLAN: ein Balken"</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"WLAN: zwei Balken"</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"WLAN: drei Balken"</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"WLAN: volle Signalstärke"</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Entfernte Apps"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Entfernte Apps und Nutzer"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB-Tethering"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Mobiler Hotspot"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth-Tethering"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Tethering"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Tethering & mobiler Hotspot"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Arbeitsprofil"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Gast"</string>
+ <string name="unknown" msgid="1592123443519355854">"Unbekannt"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Nutzer: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Einige Standardeinstellungen festgelegt"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Keine Standardeinstellungen festgelegt"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Text-in-Sprache"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Text-in-Sprache-Ausgabe"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Sprechgeschwindigkeit"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Geschwindigkeit, mit der der Text gesprochen wird"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Sprache"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Systemsprache verwenden"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Keine Sprache ausgewählt"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Legt die sprachspezifische Stimme für den gesprochenen Text fest"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Beispiel anhören"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Kurzes Beispiel der Sprachsynthese abspielen"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Sprachdaten installieren"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Sprachdaten für Sprachsynthese installieren"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Dieses Sprachsynthesemodul kann den gesamten gesprochenen Text erfassen, einschließlich personenbezogener Daten wie Passwörter und Kreditkartennummern. Es ist Teil der App \"<xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>\". Möchten Sie dieses Sprachsynthesemodul aktivieren?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Für diese Sprache ist zur Text-in-Sprache-Ausgabe eine aktive Netzwerkverbindung erforderlich."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Dies ist ein Beispiel für Sprachsynthese."</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Status der Standardsprache"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> wird vollständig unterstützt."</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> erfordert eine Netzwerkverbindung."</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> wird nicht unterstützt."</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Wird überprüft…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Einstellungen für <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Einstellungen der Suchmaschine starten"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Bevorzugtes Modul"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Allgemein"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Sehr langsam"</item>
+ <item msgid="4795095314303559268">"Langsam"</item>
+ <item msgid="8903157781070679765">"Mittel"</item>
+ <item msgid="164347302621392996">"Schnell"</item>
+ <item msgid="5794028588101562009">"Schneller"</item>
+ <item msgid="7163942783888652942">"Sehr schnell"</item>
+ <item msgid="7831712693748700507">"Schnell"</item>
+ <item msgid="5194774745031751806">"Sehr schnell"</item>
+ <item msgid="9085102246155045744">"Am schnellsten"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Profil auswählen"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Nutzer"</string>
+ <string name="category_work" msgid="8699184680584175622">"Geschäftlich"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-el/arrays.xml b/packages/SettingsLib/res/values-el/arrays.xml
new file mode 100644
index 0000000..f616547
--- /dev/null
+++ b/packages/SettingsLib/res/values-el/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Σάρωση..."</item>
+ <item msgid="8513729475867537913">"Σύνδεση..."</item>
+ <item msgid="515055375277271756">"Έλεγχος ταυτότητας..."</item>
+ <item msgid="1943354004029184381">"Λήψη διεύθυνσης IP…"</item>
+ <item msgid="4221763391123233270">"Συνδεδεμένο"</item>
+ <item msgid="624838831631122137">"Σε αναστολή"</item>
+ <item msgid="7979680559596111948">"Αποσύνδεση..."</item>
+ <item msgid="1634960474403853625">"Αποσυνδέθηκε"</item>
+ <item msgid="746097431216080650">"Ανεπιτυχής"</item>
+ <item msgid="6367044185730295334">"Αποκλεισμένο"</item>
+ <item msgid="503942654197908005">"Προσωρινή αποφυγή αδύναμης σύνδεσης"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Σάρωση..."</item>
+ <item msgid="355508996603873860">"Σύνδεση σε <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="554971459996405634">"Έλεγχος ταυτότητας με <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="7928343808033020343">"Λήψη διεύθυνσης IP από <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="8937994881315223448">"Συνδεδεμένο σε <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Σε αναστολή"</item>
+ <item msgid="7698638434317271902">"Αποσύνδεση από <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="197508606402264311">"Αποσυνδέθηκε"</item>
+ <item msgid="8578370891960825148">"Ανεπιτυχής"</item>
+ <item msgid="5660739516542454527">"Αποκλεισμένο"</item>
+ <item msgid="1805837518286731242">"Προσωρινή αποφυγή αδύναμης σύνδεσης"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
new file mode 100644
index 0000000..25ab5b8
--- /dev/null
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Δεν είναι δυνατή η σάρωση για δίκτυα"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Καμία"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Αποθηκευμένο"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Απενεργοποιημένο"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Αποτυχία διαμόρφωσης διεύθυνσης IP"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Αποτυχία σύνδεσης Wi-Fi"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Πρόβλημα ελέγχου ταυτότητας"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Εκτός εμβέλειας"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Δεν εντοπίστηκε καμία πρόσβαση στο διαδίκτυο, δεν θα γίνει αυτόματη επανασύνδεση."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Αποθηκεύτηκε από <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Σύνδεση μέσω βοηθού Wi‑Fi"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Συνδέθηκε μέσω %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Διαθέσιμο μέσω %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Συνδέθηκε, χωρίς διαδίκτυο"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Αποσυνδέθηκε"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Αποσύνδεση..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Σύνδεση..."</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Συνδεδεμένο"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Σύζευξη..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Συνδεδεμένο (όχι τηλ.)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Συνδεδεμένο (όχι μέσα)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Συνδεδεμένο (χωρίς πρόσβαση μηνύματος)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Συνδεδεμένο (χωρίς τηλέφωνο ή πολυμέσα)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Ήχος πολυμέσων"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Ήχος τηλεφώνου"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Μεταφορά αρχείου"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Συσκευή εισόδου"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Πρόσβαση στο Διαδίκτυο"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Κοινή χρήση επαφών"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Για κοινή χρήση επαφών"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Κοινή χρήση σύνδεσης στο Διαδίκτυο"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Πρόσβαση στο μήνυμα"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Πρόσβαση SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Συνδέθηκε σε ήχο πολυμέσων"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Συνδεδεμένο στον ήχο τηλεφώνου"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Συνδεδεμένο σε διακομιστή μεταφοράς αρχείων"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Συνδεδεμένο στο χάρτη"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Συνδέθηκε σε SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Δεν έχει συνδεθεί σε διακομιστή μεταφοράς αρχείων"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Συνδέθηκε σε συσκευή εισόδου"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Συνδέθηκε με συσκευή με πρόσβ. στο Διαδ."</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Κοιν. χρ. σύνδ. στο Διαδ. με συσκευή"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Χρήση για την πρόσβαση στο Διαδίκτυο"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Χρήση για χάρτη"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Χρήση για πρόσβαση στη SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Χρήση για ήχο πολυμέσων"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Χρήση για ήχο τηλεφώνου"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Χρήση για τη μεταφορά αρχείων"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Χρήση για είσοδο"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Σύζευξη"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ΣΥΖΕΥΞΗ"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Ακύρωση"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Η σύζευξη παρέχει πρόσβαση στις επαφές σας και το ιστορικό κλήσεων όταν συνδεθείτε."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Δεν ήταν δυνατή η σύζευξη με τη συσκευή <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Δεν ήταν δυνατή η σύζευξη με τη συσκευή <xliff:g id="DEVICE_NAME">%1$s</xliff:g> λόγω εσφαλμένου αριθμού PIN ή κλειδιού πρόσβασης."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Δεν είναι δυνατή η σύνδεση με τη συσκευή <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Η ζεύξη απορρίφθηκε από τη συσκευή <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi ανενεργό."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Το Wi-Fi έχει αποσυνδεθεί."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Μία γραμμή Wi-Fi."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Δύο γραμμές Wi-Fi."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Τρεις γραμμές Wi-Fi."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Άριστο σήμα Wi-Fi."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Λειτουργικό σύστημα Android"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Εφαρμογές που καταργήθηκαν"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Εφαρμογές και χρήστες που έχουν καταργηθεί"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"Πρόσδεση USB"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Φορητό σημείο πρόσβασης"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Πρόσδεση Bluetooth"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Πρόσδεση"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Πρόσ. και φορητό σημ. πρόσβ."</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Προφίλ εργασίας"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Επισκέπτης"</string>
+ <string name="unknown" msgid="1592123443519355854">"Άγνωστο"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Χρήστης: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Έχουν οριστεί κάποιες προεπιλογές"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Δεν έχουν οριστεί προεπιλογές"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Ρυθμίσεις μετατροπής κειμένου σε ομιλία"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Έξοδος μετατροπής κειμένου σε ομιλία"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Ταχύτητα λόγου"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Ταχύτητα με την οποία εκφωνείται το κείμενο"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Γλώσσα"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Χρήση γλώσσας συστήματος"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Δεν έχει επιλεγεί γλώσσα"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Ορίζει τη φωνή που σχετίζεται με τη γλώσσα για το κείμενο ομιλίας"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Ακούστε ένα παράδειγμα"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Αναπαραγωγή μιας σύντομης αναπαράστασης σύνθεσης λόγου"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Εγκατάσταση δεδομένων φωνής"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Εγκατάσταση δεδομένων ήχου που απαιτούνται για τη σύνθεση ήχου"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Αυτή η μηχανή σύνθεσης ομιλίας μπορεί να συγκεντρώσει όλο το κείμενο που θα εκφωνηθεί, συμπεριλαμβανομένων προσωπικών δεδομένων όπως οι κωδικοί πρόσβασης και οι αριθμοί πιστωτικών καρτών. Προέρχεται από τη μηχανή <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Να ενεργοποιηθεί η μηχανή σύνθεσης ομιλίας;"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Αυτή η γλώσσα απαιτεί μια ενεργή σύνδεση δικτύου για τη μετατροπή κειμένου σε ομιλία."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Αυτό είναι ένα παράδειγμα σύνθεσης λόγου"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Κατάσταση προεπιλεγμένης γλώσσας"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"Τα <xliff:g id="LOCALE">%1$s</xliff:g> υποστηρίζονται πλήρως"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"Τα <xliff:g id="LOCALE">%1$s</xliff:g> απαιτούν σύνδεση δικτύου"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"Δεν υποστηρίζονται τα <xliff:g id="LOCALE">%1$s</xliff:g>"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Έλεγχος…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Ρυθμίσεις για: <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Εκκίνηση ρυθμίσεων μηχανής"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Προτεινόμενη μηχανή"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Γενικά"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Πολύ αργή"</item>
+ <item msgid="4795095314303559268">"Αργή"</item>
+ <item msgid="8903157781070679765">"Κανονική"</item>
+ <item msgid="164347302621392996">"Γρήγορη"</item>
+ <item msgid="5794028588101562009">"Πιο γρήγορη"</item>
+ <item msgid="7163942783888652942">"Πολύ γρήγορη"</item>
+ <item msgid="7831712693748700507">"Ταχεία"</item>
+ <item msgid="5194774745031751806">"Εξαιρετικά ταχεία"</item>
+ <item msgid="9085102246155045744">"Ταχύτατη"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Επιλογή προφίλ"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Προσωπικό"</string>
+ <string name="category_work" msgid="8699184680584175622">"Εργασία"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-en-rAU/arrays.xml b/packages/SettingsLib/res/values-en-rAU/arrays.xml
new file mode 100644
index 0000000..8fa3918
--- /dev/null
+++ b/packages/SettingsLib/res/values-en-rAU/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Scanning…"</item>
+ <item msgid="8513729475867537913">"Connecting…"</item>
+ <item msgid="515055375277271756">"Authenticating…"</item>
+ <item msgid="1943354004029184381">"Obtaining IP address…"</item>
+ <item msgid="4221763391123233270">"Connected"</item>
+ <item msgid="624838831631122137">"Suspended"</item>
+ <item msgid="7979680559596111948">"Disconnecting…"</item>
+ <item msgid="1634960474403853625">"Disconnected"</item>
+ <item msgid="746097431216080650">"Unsuccessful"</item>
+ <item msgid="6367044185730295334">"Blocked"</item>
+ <item msgid="503942654197908005">"Temporarily avoiding poor connection"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Scanning…"</item>
+ <item msgid="355508996603873860">"Connecting to <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="554971459996405634">"Authenticating with <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="7928343808033020343">"Obtaining IP address from <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="8937994881315223448">"Connected to <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Suspended"</item>
+ <item msgid="7698638434317271902">"Disconnecting from <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="197508606402264311">"Disconnected"</item>
+ <item msgid="8578370891960825148">"Unsuccessful"</item>
+ <item msgid="5660739516542454527">"Blocked"</item>
+ <item msgid="1805837518286731242">"Temporarily avoiding poor connection"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
new file mode 100644
index 0000000..0e7ebd8
--- /dev/null
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Can\'t scan for networks"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"None"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Saved"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Disabled"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Configuration Failure"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi Connection Failure"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Authentication problem"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Not in range"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"No Internet Access Detected, won\'t automatically reconnect."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Saved by <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Connected via Wi‑Fi assistant"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Connected via %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Available via %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Connected, no Internet"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Disconnected"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Disconnecting…"</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Connecting…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Connected"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Pairing…"</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Connected (no phone)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Connected (no media)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Connected (no message access)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Connected (no phone or media)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Media audio"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Phone audio"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"File transfer"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Input device"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Internet access"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Contact sharing"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Use for contact sharing"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Internet connection sharing"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Message Access"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM Access"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Connected to media audio"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Connected to phone audio"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Connected to file-transfer server"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Connected to map"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Connected to SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Not connected to file-transfer server"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Connected to input device"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Connected to device for Internet access"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Sharing local Internet connection with device"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Use for Internet access"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Use for map"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Use for SIM access"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Use for media audio"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Use for phone audio"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Use for file transfer"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Use for input"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Pair"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"PAIR"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Cancel"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Pairing grants access to your contacts and call history when connected."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Couldn\'t pair with <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Couldn\'t pair with <xliff:g id="DEVICE_NAME">%1$s</xliff:g> because of an incorrect PIN or passkey."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Can\'t communicate with <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Pairing rejected by <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi off."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi disconnected."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi one bar."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wi-Fi two bars."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wi-Fi three bars."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Wi-Fi signal full."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Removed apps"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Removed apps and users"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB tethering"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Portable hotspot"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth tethering"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Tethering"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Tethering & portable hotspot"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Work profile"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Guest"</string>
+ <string name="unknown" msgid="1592123443519355854">"Unknown"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"User: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Some defaults set"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"No defaults set"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Text-to-speech settings"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Text-to-speech output"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Speech rate"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Speed at which the text is spoken"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Language"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Use system language"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Language not selected"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Sets the language-specific voice for the spoken text"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Listen to an example"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Play a short demonstration of speech synthesis"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Install voice data"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Install the voice data required for speech synthesis"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"This speech synthesis engine may be able to collect all the text that will be spoken, including personal data like passwords and credit card numbers. It comes from the <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> engine. Enable the use of this speech synthesis engine?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"This language requires a working network connection for text-to-speech output."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"This is an example of speech synthesis"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Default language status"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> is fully supported"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> requires network connection"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> is not supported"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Checking…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Settings for <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Launch engine settings"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Preferred engine"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"General"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Very slow"</item>
+ <item msgid="4795095314303559268">"Slow"</item>
+ <item msgid="8903157781070679765">"Normal"</item>
+ <item msgid="164347302621392996">"Fast"</item>
+ <item msgid="5794028588101562009">"Faster"</item>
+ <item msgid="7163942783888652942">"Very fast"</item>
+ <item msgid="7831712693748700507">"Rapid"</item>
+ <item msgid="5194774745031751806">"Very rapid"</item>
+ <item msgid="9085102246155045744">"Fastest"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Choose Profile"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Personal"</string>
+ <string name="category_work" msgid="8699184680584175622">"Work"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-en-rGB/arrays.xml b/packages/SettingsLib/res/values-en-rGB/arrays.xml
new file mode 100644
index 0000000..8fa3918
--- /dev/null
+++ b/packages/SettingsLib/res/values-en-rGB/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Scanning…"</item>
+ <item msgid="8513729475867537913">"Connecting…"</item>
+ <item msgid="515055375277271756">"Authenticating…"</item>
+ <item msgid="1943354004029184381">"Obtaining IP address…"</item>
+ <item msgid="4221763391123233270">"Connected"</item>
+ <item msgid="624838831631122137">"Suspended"</item>
+ <item msgid="7979680559596111948">"Disconnecting…"</item>
+ <item msgid="1634960474403853625">"Disconnected"</item>
+ <item msgid="746097431216080650">"Unsuccessful"</item>
+ <item msgid="6367044185730295334">"Blocked"</item>
+ <item msgid="503942654197908005">"Temporarily avoiding poor connection"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Scanning…"</item>
+ <item msgid="355508996603873860">"Connecting to <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="554971459996405634">"Authenticating with <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="7928343808033020343">"Obtaining IP address from <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="8937994881315223448">"Connected to <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Suspended"</item>
+ <item msgid="7698638434317271902">"Disconnecting from <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="197508606402264311">"Disconnected"</item>
+ <item msgid="8578370891960825148">"Unsuccessful"</item>
+ <item msgid="5660739516542454527">"Blocked"</item>
+ <item msgid="1805837518286731242">"Temporarily avoiding poor connection"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..0e7ebd8
--- /dev/null
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Can\'t scan for networks"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"None"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Saved"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Disabled"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Configuration Failure"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi Connection Failure"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Authentication problem"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Not in range"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"No Internet Access Detected, won\'t automatically reconnect."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Saved by <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Connected via Wi‑Fi assistant"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Connected via %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Available via %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Connected, no Internet"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Disconnected"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Disconnecting…"</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Connecting…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Connected"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Pairing…"</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Connected (no phone)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Connected (no media)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Connected (no message access)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Connected (no phone or media)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Media audio"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Phone audio"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"File transfer"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Input device"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Internet access"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Contact sharing"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Use for contact sharing"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Internet connection sharing"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Message Access"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM Access"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Connected to media audio"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Connected to phone audio"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Connected to file-transfer server"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Connected to map"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Connected to SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Not connected to file-transfer server"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Connected to input device"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Connected to device for Internet access"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Sharing local Internet connection with device"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Use for Internet access"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Use for map"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Use for SIM access"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Use for media audio"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Use for phone audio"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Use for file transfer"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Use for input"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Pair"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"PAIR"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Cancel"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Pairing grants access to your contacts and call history when connected."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Couldn\'t pair with <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Couldn\'t pair with <xliff:g id="DEVICE_NAME">%1$s</xliff:g> because of an incorrect PIN or passkey."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Can\'t communicate with <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Pairing rejected by <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi off."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi disconnected."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi one bar."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wi-Fi two bars."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wi-Fi three bars."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Wi-Fi signal full."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Removed apps"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Removed apps and users"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB tethering"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Portable hotspot"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth tethering"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Tethering"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Tethering & portable hotspot"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Work profile"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Guest"</string>
+ <string name="unknown" msgid="1592123443519355854">"Unknown"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"User: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Some defaults set"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"No defaults set"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Text-to-speech settings"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Text-to-speech output"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Speech rate"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Speed at which the text is spoken"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Language"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Use system language"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Language not selected"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Sets the language-specific voice for the spoken text"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Listen to an example"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Play a short demonstration of speech synthesis"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Install voice data"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Install the voice data required for speech synthesis"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"This speech synthesis engine may be able to collect all the text that will be spoken, including personal data like passwords and credit card numbers. It comes from the <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> engine. Enable the use of this speech synthesis engine?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"This language requires a working network connection for text-to-speech output."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"This is an example of speech synthesis"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Default language status"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> is fully supported"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> requires network connection"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> is not supported"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Checking…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Settings for <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Launch engine settings"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Preferred engine"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"General"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Very slow"</item>
+ <item msgid="4795095314303559268">"Slow"</item>
+ <item msgid="8903157781070679765">"Normal"</item>
+ <item msgid="164347302621392996">"Fast"</item>
+ <item msgid="5794028588101562009">"Faster"</item>
+ <item msgid="7163942783888652942">"Very fast"</item>
+ <item msgid="7831712693748700507">"Rapid"</item>
+ <item msgid="5194774745031751806">"Very rapid"</item>
+ <item msgid="9085102246155045744">"Fastest"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Choose Profile"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Personal"</string>
+ <string name="category_work" msgid="8699184680584175622">"Work"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-en-rIN/arrays.xml b/packages/SettingsLib/res/values-en-rIN/arrays.xml
new file mode 100644
index 0000000..8fa3918
--- /dev/null
+++ b/packages/SettingsLib/res/values-en-rIN/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Scanning…"</item>
+ <item msgid="8513729475867537913">"Connecting…"</item>
+ <item msgid="515055375277271756">"Authenticating…"</item>
+ <item msgid="1943354004029184381">"Obtaining IP address…"</item>
+ <item msgid="4221763391123233270">"Connected"</item>
+ <item msgid="624838831631122137">"Suspended"</item>
+ <item msgid="7979680559596111948">"Disconnecting…"</item>
+ <item msgid="1634960474403853625">"Disconnected"</item>
+ <item msgid="746097431216080650">"Unsuccessful"</item>
+ <item msgid="6367044185730295334">"Blocked"</item>
+ <item msgid="503942654197908005">"Temporarily avoiding poor connection"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Scanning…"</item>
+ <item msgid="355508996603873860">"Connecting to <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="554971459996405634">"Authenticating with <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="7928343808033020343">"Obtaining IP address from <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="8937994881315223448">"Connected to <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Suspended"</item>
+ <item msgid="7698638434317271902">"Disconnecting from <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="197508606402264311">"Disconnected"</item>
+ <item msgid="8578370891960825148">"Unsuccessful"</item>
+ <item msgid="5660739516542454527">"Blocked"</item>
+ <item msgid="1805837518286731242">"Temporarily avoiding poor connection"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
new file mode 100644
index 0000000..0e7ebd8
--- /dev/null
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Can\'t scan for networks"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"None"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Saved"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Disabled"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Configuration Failure"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi Connection Failure"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Authentication problem"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Not in range"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"No Internet Access Detected, won\'t automatically reconnect."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Saved by <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Connected via Wi‑Fi assistant"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Connected via %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Available via %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Connected, no Internet"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Disconnected"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Disconnecting…"</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Connecting…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Connected"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Pairing…"</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Connected (no phone)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Connected (no media)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Connected (no message access)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Connected (no phone or media)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Media audio"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Phone audio"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"File transfer"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Input device"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Internet access"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Contact sharing"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Use for contact sharing"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Internet connection sharing"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Message Access"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM Access"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Connected to media audio"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Connected to phone audio"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Connected to file-transfer server"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Connected to map"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Connected to SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Not connected to file-transfer server"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Connected to input device"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Connected to device for Internet access"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Sharing local Internet connection with device"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Use for Internet access"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Use for map"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Use for SIM access"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Use for media audio"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Use for phone audio"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Use for file transfer"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Use for input"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Pair"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"PAIR"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Cancel"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Pairing grants access to your contacts and call history when connected."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Couldn\'t pair with <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Couldn\'t pair with <xliff:g id="DEVICE_NAME">%1$s</xliff:g> because of an incorrect PIN or passkey."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Can\'t communicate with <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Pairing rejected by <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi off."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi disconnected."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi one bar."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wi-Fi two bars."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wi-Fi three bars."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Wi-Fi signal full."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Removed apps"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Removed apps and users"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB tethering"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Portable hotspot"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth tethering"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Tethering"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Tethering & portable hotspot"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Work profile"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Guest"</string>
+ <string name="unknown" msgid="1592123443519355854">"Unknown"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"User: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Some defaults set"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"No defaults set"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Text-to-speech settings"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Text-to-speech output"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Speech rate"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Speed at which the text is spoken"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Language"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Use system language"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Language not selected"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Sets the language-specific voice for the spoken text"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Listen to an example"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Play a short demonstration of speech synthesis"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Install voice data"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Install the voice data required for speech synthesis"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"This speech synthesis engine may be able to collect all the text that will be spoken, including personal data like passwords and credit card numbers. It comes from the <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> engine. Enable the use of this speech synthesis engine?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"This language requires a working network connection for text-to-speech output."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"This is an example of speech synthesis"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Default language status"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> is fully supported"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> requires network connection"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> is not supported"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Checking…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Settings for <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Launch engine settings"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Preferred engine"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"General"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Very slow"</item>
+ <item msgid="4795095314303559268">"Slow"</item>
+ <item msgid="8903157781070679765">"Normal"</item>
+ <item msgid="164347302621392996">"Fast"</item>
+ <item msgid="5794028588101562009">"Faster"</item>
+ <item msgid="7163942783888652942">"Very fast"</item>
+ <item msgid="7831712693748700507">"Rapid"</item>
+ <item msgid="5194774745031751806">"Very rapid"</item>
+ <item msgid="9085102246155045744">"Fastest"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Choose Profile"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Personal"</string>
+ <string name="category_work" msgid="8699184680584175622">"Work"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-es-rUS/arrays.xml b/packages/SettingsLib/res/values-es-rUS/arrays.xml
new file mode 100644
index 0000000..5947056
--- /dev/null
+++ b/packages/SettingsLib/res/values-es-rUS/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Explorando..."</item>
+ <item msgid="8513729475867537913">"Conectando…"</item>
+ <item msgid="515055375277271756">"Autenticando…"</item>
+ <item msgid="1943354004029184381">"Obteniendo la dirección IP…"</item>
+ <item msgid="4221763391123233270">"Conectado"</item>
+ <item msgid="624838831631122137">"Suspendido"</item>
+ <item msgid="7979680559596111948">"Desconectando…"</item>
+ <item msgid="1634960474403853625">"Desconectado"</item>
+ <item msgid="746097431216080650">"Incorrecto"</item>
+ <item msgid="6367044185730295334">"Bloqueada"</item>
+ <item msgid="503942654197908005">"Desactivando mala conexión temporalmente"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Explorando..."</item>
+ <item msgid="355508996603873860">"Conectando a <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="554971459996405634">"Autenticando con <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="7928343808033020343">"Obteniendo dirección IP de <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="8937994881315223448">"Conectado a <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Suspendido"</item>
+ <item msgid="7698638434317271902">"Desconectando de <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="197508606402264311">"Desconectado"</item>
+ <item msgid="8578370891960825148">"Incorrecto"</item>
+ <item msgid="5660739516542454527">"Bloqueada"</item>
+ <item msgid="1805837518286731242">"Desactivando mala conexión temporalmente"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..013d576
--- /dev/null
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"No se pueden buscar las redes."</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Ninguna"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Guardada"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Inhabilitada"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Error de configuración IP"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Error de conexión Wi-Fi"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problema de autenticación"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Fuera de alcance"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"No se detectó el acceso a Internet. No se volverá a conectar de forma automática."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Guardadas por <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Conexión por asistente de Wi-Fi"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Conexión 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">"Conectado a Wi-Fi, sin conexión a Internet"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Desconectado"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Desconectando…"</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Conectando…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Conectado"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Vinculando..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Conectado (sin teléfono)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Conectado (sin audio multimedia)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Conectado (sin acceso a mensajes)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Conectado (sin tel. ni audio multimedia)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Audio multimedia"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Audio del dispositivo"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Transferencia de archivos"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Dispositivo de entrada"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Acceso a Internet"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Compartir contactos"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Utilizar para compartir contactos"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Compartir conexión a Internet"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Acceso a mensajes"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Acceso SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Conectado al audio multimedia"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Conectado al audio del dispositivo"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Conectado al servidor de transferencia de archivo"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Conectado al mapa"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Conexión a SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"No conectado al servidor de transferencia de archivo"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Conectado a dispositivo de entrada"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Conectado a un dispositivo para acceder a Internet"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Conexión a Internet local compartida con dispositivo"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Utilizar para acceso a Internet"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Usar para mapa"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Usar para acceder a la SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Utilizar para el audio multimedia"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Utilizar para el audio del dispositivo"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Utilizar para la transferencia de archivos"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Utilizar para entrada"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Vincular"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"SINCRONIZAR"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Cancelar"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"La sincronización te permite acceder a los contactos y al historial de llamadas cuando el dispositivo está conectado."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"No se pudo sincronizar con <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"No se pudo sincronizar con <xliff:g id="DEVICE_NAME">%1$s</xliff:g> debido a que el PIN o la clave de acceso son incorrectos."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"No se puede establecer la comunicación con <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Vínculo rechazado por <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi inhabilitado"</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi desconectado"</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Una barra de Wi-Fi"</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Dos barras de Wi-Fi"</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Tres barras de Wi-Fi"</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Señal de Wi-Fi excelente"</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"SO Android"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Aplicaciones eliminadas"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Aplicaciones y usuarios eliminados"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"Anclaje a red USB"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Zona Wi-Fi portátil"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Anclaje a red Bluetooth"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Anclaje a red"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Anclaje a red y zona portátil"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Perfil de trabajo"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Invitado"</string>
+ <string name="unknown" msgid="1592123443519355854">"Desconocido"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Usuario: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Hay algunas configuraciones predeterminadas establecidas."</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"No hay configuraciones predeterminadas establecidas."</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Configuración de texto a voz"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Salida de texto a voz"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Velocidad de voz"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Velocidad en la que se habla el texto"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Idioma"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Usar el idioma del sistema"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Idioma no seleccionado"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Establece la voz específica de un idioma para el texto hablado"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Escuchar un ejemplo"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Reproducir una pequeña demostración de síntesis de voz"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Instalar datos de voz"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Instalar los datos de voz necesarios para la síntesis de voz"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Es posible que este motor de síntesis de voz pueda recopilar todo el texto que se hablará, incluidos los datos personales como las contraseñas y los números de tarjetas de crédito. Proviene del motor <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. ¿Deseas activar el uso de este motor de síntesis de voz?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Este idioma necesita una conexión de red en funcionamiento para la salida de texto a voz."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Ejemplo de síntesis de voz"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Estado del idioma predeterminado"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"El idioma <xliff:g id="LOCALE">%1$s</xliff:g> es totalmente compatible."</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"El idioma <xliff:g id="LOCALE">%1$s</xliff:g> requiere una conexión de red."</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"El idioma <xliff:g id="LOCALE">%1$s</xliff:g> no es compatible."</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Comprobando…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Configuración de <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Iniciar configuración de motor"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Motor preferido"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"General"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Muy lenta"</item>
+ <item msgid="4795095314303559268">"Lenta"</item>
+ <item msgid="8903157781070679765">"Normal"</item>
+ <item msgid="164347302621392996">"Ligera"</item>
+ <item msgid="5794028588101562009">"Muy ligera"</item>
+ <item msgid="7163942783888652942">"A velocidad muy alta"</item>
+ <item msgid="7831712693748700507">"Rápida"</item>
+ <item msgid="5194774745031751806">"Muy rápida"</item>
+ <item msgid="9085102246155045744">"A velocidad máxima"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Elegir perfil"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Personal"</string>
+ <string name="category_work" msgid="8699184680584175622">"Trabajo"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-es/arrays.xml b/packages/SettingsLib/res/values-es/arrays.xml
new file mode 100644
index 0000000..ba51a81
--- /dev/null
+++ b/packages/SettingsLib/res/values-es/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Buscando..."</item>
+ <item msgid="8513729475867537913">"Estableciendo conexión..."</item>
+ <item msgid="515055375277271756">"Autenticando..."</item>
+ <item msgid="1943354004029184381">"Obteniendo dirección IP…"</item>
+ <item msgid="4221763391123233270">"Conexión establecida"</item>
+ <item msgid="624838831631122137">"Suspendida"</item>
+ <item msgid="7979680559596111948">"Desconectando..."</item>
+ <item msgid="1634960474403853625">"Desconectada"</item>
+ <item msgid="746097431216080650">"Con error"</item>
+ <item msgid="6367044185730295334">"Bloqueada"</item>
+ <item msgid="503942654197908005">"Inhabilitando conexión inestable temporalmente..."</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Buscando..."</item>
+ <item msgid="355508996603873860">"Estableciendo conexión con <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="554971459996405634">"Autenticando con <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="7928343808033020343">"Obteniendo dirección IP de <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="8937994881315223448">"Conexión establecida con <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Suspendida"</item>
+ <item msgid="7698638434317271902">"Desconectando de <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="197508606402264311">"Desconectada"</item>
+ <item msgid="8578370891960825148">"Con error"</item>
+ <item msgid="5660739516542454527">"Bloqueada"</item>
+ <item msgid="1805837518286731242">"Inhabilitando conexión inestable temporalmente..."</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
new file mode 100644
index 0000000..317b37a
--- /dev/null
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"No se puede buscar redes."</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Ninguna"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Guardado"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Inhabilitado"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Error de configuración de IP"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Error de conexión Wi-Fi"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Error de autenticación"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Fuera de rango"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"No se ha detectado acceso a Internet, no se volverá a conectar automáticamente."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Guardadas por <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Conectado a través de asistente Wi‑Fi"</string>
+ <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_disconnecting" msgid="8913264760027764974">"Desconectando…"</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Estableciendo conexión…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Conectado"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Vinculando…"</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Conectado (sin teléfono)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Conectado (sin audio multimedia)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Conectado (sin acceso a mensajes)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Conectado (sin teléfono ni multimedia)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Audio multimedia"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Audio del teléfono"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Transferencia de archivos"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Dispositivo de entrada"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Acceso a Internet"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Compartir contactos"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Usar para compartir contactos"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Compartir conexión a Internet"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Acceso a mensajes"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Acceso a tarjeta SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Conectado al audio del medio"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Conectado al audio del teléfono"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Conectado con el servidor de transferencia de archivos"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Conectado a mapa"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Conectado a SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Sin conexión con el servidor de transferencia de archivos"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Conectado a dispositivo de entrada"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Conectado para acceso a Internet"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Compartiendo conexión a Internet con el dispositivo"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Usar para acceder a Internet"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Usar para mapa"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Utilizar para acceso a tarjeta SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Utilizar para audio de medio"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Utilizar para audio del teléfono"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Uso de la transferencia de archivos"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Usar para entrada"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Vincular"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"VINCULAR"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Cancelar"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"La vinculación permite acceder a tus contactos y al historial de llamadas cuando el dispositivo está conectado."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"No se ha podido vincular con <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"No se ha podido vincular con <xliff:g id="DEVICE_NAME">%1$s</xliff:g> porque la clave de acceso o el PIN son incorrectos."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"No se puede establecer comunicación con <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Vinculación rechazada por <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi desactivado."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi desconectado."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Una barra de Wi-Fi."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Dos barras de Wi-Fi."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Tres barras de Wi-Fi."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Señal de Wi-Fi al máximo."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"SO Android"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Aplicaciones eliminadas"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Usuarios y aplicaciones eliminados"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"Compartir por USB"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Zona Wi-Fi portátil"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Compartir por Bluetooth"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Compartir Internet"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Compartir Internet y zona Wi-Fi"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Perfil de trabajo"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Invitado"</string>
+ <string name="unknown" msgid="1592123443519355854">"Desconocido"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Usuario: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Se han establecido algunos valores predeterminados"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"No se han establecido valores predeterminados"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Configuración síntesis voz"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Síntesis de voz"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Velocidad de la voz"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Velocidad a la que se lee el texto"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Idioma"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Usar idioma del sistema"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Idioma no seleccionado"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Establecer la voz del idioma específico para el texto hablado"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Escuchar un ejemplo"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Reproducir una breve demostración de síntesis de voz"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Instalar archivos de voz"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Instalar los archivos de datos de voz necesarios para la síntesis de voz"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Es posible que este motor de síntesis de voz recopile todo el texto hablado, incluidos datos personales, como contraseñas y números de tarjeta de crédito. Procede del motor <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. ¿Quieres habilitar el uso de este motor de síntesis de voz?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Este idioma requiere una conexión de red activa para la salida de síntesis de voz."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Este es un ejemplo de síntesis de voz"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Estado del idioma predeterminado"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"El <xliff:g id="LOCALE">%1$s</xliff:g> se admite completamente"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"El idioma <xliff:g id="LOCALE">%1$s</xliff:g> requiere conexión a Internet"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"No se admite el <xliff:g id="LOCALE">%1$s</xliff:g>"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Comprobando..."</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Ajustes de <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Iniciar configuración de motor"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Motor preferido"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"General"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Muy lenta"</item>
+ <item msgid="4795095314303559268">"Lenta"</item>
+ <item msgid="8903157781070679765">"Normal"</item>
+ <item msgid="164347302621392996">"Rápida"</item>
+ <item msgid="5794028588101562009">"Más rápida"</item>
+ <item msgid="7163942783888652942">"Muy rápida"</item>
+ <item msgid="7831712693748700507">"Superrápida"</item>
+ <item msgid="5194774745031751806">"Hiperrrápida"</item>
+ <item msgid="9085102246155045744">"La más rápida"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Seleccionar perfil"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Personal"</string>
+ <string name="category_work" msgid="8699184680584175622">"Trabajo"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-et-rEE/arrays.xml b/packages/SettingsLib/res/values-et-rEE/arrays.xml
new file mode 100644
index 0000000..2a8f69b
--- /dev/null
+++ b/packages/SettingsLib/res/values-et-rEE/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Skannimine…"</item>
+ <item msgid="8513729475867537913">"Ühenduse loomine…"</item>
+ <item msgid="515055375277271756">"Autentimine…"</item>
+ <item msgid="1943354004029184381">"IP-aadressi hankimine…"</item>
+ <item msgid="4221763391123233270">"Ühendatud"</item>
+ <item msgid="624838831631122137">"Peatatud"</item>
+ <item msgid="7979680559596111948">"Ühenduse katkestamine…"</item>
+ <item msgid="1634960474403853625">"Ühendus katkestatud"</item>
+ <item msgid="746097431216080650">"Ebaõnnestus"</item>
+ <item msgid="6367044185730295334">"Blokeeritud"</item>
+ <item msgid="503942654197908005">"Kehva ühenduse ajutine vältimine"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Skannimine…"</item>
+ <item msgid="355508996603873860">"Ühendamine võrguga <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="554971459996405634">"Autentimine võrguga <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="7928343808033020343">"IP-aadressi hankimine võrgust <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="8937994881315223448">"Ühendatud võrguga <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Peatatud"</item>
+ <item msgid="7698638434317271902">"Ühenduse katkestamine võrguga <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="197508606402264311">"Ühendus katkestatud"</item>
+ <item msgid="8578370891960825148">"Ebaõnnestus"</item>
+ <item msgid="5660739516542454527">"Blokeeritud"</item>
+ <item msgid="1805837518286731242">"Kehva ühenduse ajutine vältimine"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-et-rEE/strings.xml b/packages/SettingsLib/res/values-et-rEE/strings.xml
new file mode 100644
index 0000000..366c336
--- /dev/null
+++ b/packages/SettingsLib/res/values-et-rEE/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Võrke ei saa kontrollida"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Puudub"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Salvestatud"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Keelatud"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP seadistamise ebaõnnestumine"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi-ühenduse viga"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Autentimise probleem"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Pole vahemikus"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Interneti-ühendust ei tuvastatud, seadet ei ühendata automaatselt."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Salvestas: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Ühendatud WiFi-abi kaudu"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Ühendatud üksuse %1$s kaudu"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Saadaval üksuse %1$s kaudu"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Ühendatud, Interneti-ühendus puudub"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Ühendus katkestatud"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Ühenduse katkestamine ..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Ühendamine ..."</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Ühendatud"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Sidumine ..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Ühendatud (telefoni pole)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Ühendatud (meediat pole)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Ühendatud (sõnumita juurdepääs)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Ühendatud (pole telefoni ega meediat)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Meedia heli"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Telefoni heli"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Failiedastus"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Sisendseade"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Internetti juurdepääs"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Kontakti jagamine"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Kasutamine kontaktide jagamiseks"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Interneti-ühenduse jagamine"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Juurdepääs sõnumile"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM-kaardi juurdepääs"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Ühendatud meediumiheliga"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Ühendatud telefoniheliga"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Ühendatud failiedastuse serveriga"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Ühendatud kaardiga"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Ühendatud SAP-iga"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Ei ole failiedastuse serveriga ühendatud"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Ühendatud sisendseade"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Ühendatud seadmega Internetti juurdepääsuks"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Kohaliku Interneti-ühenduse jagamine seadmega"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Interneti-juurdepääsuks kasutamine"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Kasuta kaardi jaoks"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"SIM-kaardi juurdepääsuks kasutamine"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Kasuta meediumiheli jaoks"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Kasuta telefoniheli jaoks"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Kasutage failide edastamiseks"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Kasutage sisendi jaoks"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Seo"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"SEO"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Tühista"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Sidumine annab ühenduse ajal juurdepääsu kontaktidele ja kõneajaloole."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Ei saanud seadmega <xliff:g id="DEVICE_NAME">%1$s</xliff:g> siduda."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Ei saanud seadmega <xliff:g id="DEVICE_NAME">%1$s</xliff:g> siduda vale PIN-koodi või parooli tõttu."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Seadmega <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ei saa sidet luua."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> hülgas sidumise."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"WiFi on välja lülitatud."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"WiFi-ühendus on katkestatud."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"WiFi: üks pulk."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"WiFi: kaks pulka."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"WiFi: kolm pulka."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"WiFi-signaal on tugev."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Eemaldatud rakendused"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Eemaldatud rakendused ja kasutajad"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB jagamine"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Kantav tööpunkt"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetoothi jagamine"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Jagamine"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Jagam. ja kant. kuumkoht"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Tööprofiil"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Külaline"</string>
+ <string name="unknown" msgid="1592123443519355854">"Tundmatu"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Kasutaja: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Määratud on mõned vaikevalikud"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Ühtegi vaikevalikut pole määratud"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Kõnesünteesi seaded"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Kõnesünteesi väljund"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Kõnekiirus"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Teksti rääkimise kiirus"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Keel"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Süsteemi keele kasutamine"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Keelt pole valitud"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Määrab räägitud teksti keelespetsiifilise hääle"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Kuulake näidet"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Esita lühike kõnesünteesi demo"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Installi hääleandmed"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Installi kõnesünteesi jaoks vajalikud hääleandmed"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"See kõnesünteesimootor võib koguda kogu kõneldud teksti, sh isiklikke andmeid, nagu paroolid ja krediitkaardinumbrid. Selle aluseks on mootor <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Kas lubada kõnesünteesimootori kasutamine?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Selle keele puhul on kõnesünteesi väljundi jaoks vaja toimivat võrguühendust."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"See on kõnesünteesi näide"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Vaikekeele olek"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> on täielikult toetatud"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> vajab võrguühendust"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> pole toetatud"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Kontrollimine ..."</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Seaded: <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Käivita mootori seaded"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Eelistatud mootor"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Üldine"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Väga aeglane"</item>
+ <item msgid="4795095314303559268">"Aeglane"</item>
+ <item msgid="8903157781070679765">"Tavaline"</item>
+ <item msgid="164347302621392996">"Kiire"</item>
+ <item msgid="5794028588101562009">"Kiirem"</item>
+ <item msgid="7163942783888652942">"Väga kiire"</item>
+ <item msgid="7831712693748700507">"Tormakas"</item>
+ <item msgid="5194774745031751806">"Väga tormakas"</item>
+ <item msgid="9085102246155045744">"Kõige kiirem"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Profiili valimine"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Isiklik"</string>
+ <string name="category_work" msgid="8699184680584175622">"Töö"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-eu-rES/arrays.xml b/packages/SettingsLib/res/values-eu-rES/arrays.xml
new file mode 100644
index 0000000..7746f01
--- /dev/null
+++ b/packages/SettingsLib/res/values-eu-rES/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Bilatzen…"</item>
+ <item msgid="8513729475867537913">"Konektatzen…"</item>
+ <item msgid="515055375277271756">"Autentifikatzen…"</item>
+ <item msgid="1943354004029184381">"IP helbidea lortzen…"</item>
+ <item msgid="4221763391123233270">"Konektatuta"</item>
+ <item msgid="624838831631122137">"Etenda"</item>
+ <item msgid="7979680559596111948">"Deskonektatzen…"</item>
+ <item msgid="1634960474403853625">"Deskonektatuta"</item>
+ <item msgid="746097431216080650">"Ezin izan da konektatu"</item>
+ <item msgid="6367044185730295334">"Blokeatuta"</item>
+ <item msgid="503942654197908005">"Konexio ahula aldi baterako saihesten"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Bilatzen…"</item>
+ <item msgid="355508996603873860">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> sarera konektatzen…"</item>
+ <item msgid="554971459996405634">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> sarearekin autentifikatzen…"</item>
+ <item msgid="7928343808033020343">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> sarearen IP helbidea lortzen…"</item>
+ <item msgid="8937994881315223448">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> sarera konektatuta"</item>
+ <item msgid="1330262655415760617">"Etenda"</item>
+ <item msgid="7698638434317271902">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> saretik deskonektatzen…"</item>
+ <item msgid="197508606402264311">"Deskonektatuta"</item>
+ <item msgid="8578370891960825148">"Ezin izan da konektatu"</item>
+ <item msgid="5660739516542454527">"Blokeatuta"</item>
+ <item msgid="1805837518286731242">"Konexio ahula aldi baterako saihesten"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-eu-rES/strings.xml b/packages/SettingsLib/res/values-eu-rES/strings.xml
new file mode 100644
index 0000000..68591cf
--- /dev/null
+++ b/packages/SettingsLib/res/values-eu-rES/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Ezin dira sareak bilatu"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Bat ere ez"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Gordeta"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Desgaituta"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Ezin izan da konfiguratu IP helbidea"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Ezin izan da konektatu Wi-Fi sarera"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Autentifikazio-arazoa"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Urrunegi"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Ez da hauteman Interneterako sarbiderik. Ez da automatikoki berriro konektatuko."</string>
+ <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> aplikazioak gorde du"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fi laguntzailearen bidez konektatuta"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s bidez konektatuta"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s bidez erabilgarri"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Konektatuta, ez dago Interneteko konexiorik"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Deskonektatuta"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Deskonektatzen…"</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Konektatzen…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Konektatuta"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Parekatzen…"</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Konektatuta (ez dago telefonorik)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Konektatuta (ez dago euskarririk)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Konektatuta (mezuetarako sarbiderik ez)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Konektatuta (ez dago telef./euskarririk)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Euskarriaren audioa"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Telefonoaren audioa"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Fitxategi-transferentzia"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Sarrerako gailua"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Interneterako sarbidea"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Kontaktuak partekatzea"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Erabili kontaktuak partekatzeko"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Interneteko konexioa partekatzea"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Mezuetarako sarbidea"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM txartelerako sarbidea"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Euskarriaren audiora konektatuta"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Telefonoaren audiora konektatuta"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Fitxategi-transferentziako zerbitzarira konektatuta"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Mapara konektatuta"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"SAP sarbide-puntura konektatuta"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Fitxategi-transferentziako zerbitzarira konektatu gabe"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Sarrerako gailura konektatuta"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Gailura konektatuta Interneteko sarbiderako"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Tokiko Interneteko konexioa gailu batekin partekatzea"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Erabili Interneteko sarbiderako"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Erabili maparako"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Erabili SIM txartelerako sarbiderako"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Erabili euskarriaren audiorako"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Erabili telefonoaren audiorako"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Erabili fitxategi-transferentziarako"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Erabili idazketarako"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Parekatu"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"PAREKATU"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Utzi"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Gailuak parekatzen badituzu, batetik besteko kontaktuak eta deien historia atzitu ahal izango dituzu."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Ezin izan da <xliff:g id="DEVICE_NAME">%1$s</xliff:g> gailuarekin parekatu."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Ezin izan da <xliff:g id="DEVICE_NAME">%1$s</xliff:g> gailuarekin parekatu PIN edo pasakode okerra idatzi delako."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Ezin da <xliff:g id="DEVICE_NAME">%1$s</xliff:g> gailuarekin komunikatu."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> gailuak bikotetzea ukatu du."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Desaktibatuta dago Wi-Fi konexioa."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Deskonektatu egin da Wi-Fi konexioa."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi sarearen barra bat."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wi-Fi sarearen bi barra."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wi-Fi sarearen hiru barra."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Wi-Fi sarearen seinalea osoa."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android sistema eragilea"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Kendutako aplikazioak"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Kendutako aplikazioak eta erabiltzaileak"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"Konexioa partekatzea (USB)"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Sare publiko eramangarria"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Konexioa partekatzea (Bluetooth)"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Konexioa partekatzea"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Konexioa partekatzea eta sare publikoak"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Laneko profila"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Gonbidatua"</string>
+ <string name="unknown" msgid="1592123443519355854">"Ezezaguna"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Erabiltzailea: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Hobespen lehenetsi batzuk ezarrita daude"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Ez dago hobespen lehenetsirik ezarrita"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Testua ahots bihurtzeko eginbidearen ezarpenak"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Testua ahots bihurtzeko eginbidearen irteera"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Hizketa-abiadura"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Testua zer abiaduran esaten den"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Hizkuntza"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Erabili sistemaren hizkuntza"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Ez da hizkuntza hautatu"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Esandako testuaren ahotsa hizkuntzaren arabera ezartzen du"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Entzun adibide bat"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Erreproduzitu hizketa-sintesiaren demostrazio laburra"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Instalatu ahotsaren datuak"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Instalatu hizketaren sintesirako behar diren ahots-datuak"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Hizketaren sintesi-motorrak idazten duzun testu guztia bil dezake, pasahitzak eta kreditu-txarteleko zenbakiak bezalako datu pertsonalak barne. <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> motorrak egin du eskaera. Hizketaren sintesi-motor hori erabili nahi duzu?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Testua ahots bihurtzeko eginbidea erabiltzeko, hizkuntza honek sareko konexioa behar du."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Hizketa-sintesiaren adibide bat da hau"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Hizkuntza lehenetsiaren egoera"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> guztiz onartzen da."</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> eskualde-ezarpenak sareko konexioa behar du"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"Ez da <xliff:g id="LOCALE">%1$s</xliff:g> onartzen"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Egiaztatzen…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g> motorraren ezarpenak"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Abiarazi motorraren ezarpenak"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Motor hobetsia"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Orokorra"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Oso motela"</item>
+ <item msgid="4795095314303559268">"Motela"</item>
+ <item msgid="8903157781070679765">"Normala"</item>
+ <item msgid="164347302621392996">"Bizkorra"</item>
+ <item msgid="5794028588101562009">"Bizkorragoa"</item>
+ <item msgid="7163942783888652942">"Oso bizkorra"</item>
+ <item msgid="7831712693748700507">"Bizkorra"</item>
+ <item msgid="5194774745031751806">"Oso bizkorra"</item>
+ <item msgid="9085102246155045744">"Bizkorrena"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Aukeratu profila"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Pertsonalak"</string>
+ <string name="category_work" msgid="8699184680584175622">"Lanekoak"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-fa/arrays.xml b/packages/SettingsLib/res/values-fa/arrays.xml
new file mode 100644
index 0000000..1781c79
--- /dev/null
+++ b/packages/SettingsLib/res/values-fa/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"اسکن کردن..."</item>
+ <item msgid="8513729475867537913">"در حال اتصال…"</item>
+ <item msgid="515055375277271756">"در حال تأیید اعتبار..."</item>
+ <item msgid="1943354004029184381">"در حال دریافت آدرس IP..."</item>
+ <item msgid="4221763391123233270">"متصل"</item>
+ <item msgid="624838831631122137">"معلق"</item>
+ <item msgid="7979680559596111948">"در حال قطع اتصال..."</item>
+ <item msgid="1634960474403853625">"اتصال قطع شد"</item>
+ <item msgid="746097431216080650">"ناموفق"</item>
+ <item msgid="6367044185730295334">"مسدود شده"</item>
+ <item msgid="503942654197908005">"جلوگیری موقت از اتصال ضعیف"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"اسکن کردن..."</item>
+ <item msgid="355508996603873860">"در حال اتصال به <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="554971459996405634">"در حال تأیید اعتبار با <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="7928343808033020343">"در حال دریافت آدرس IP از <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="8937994881315223448">"متصل شد به <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"معلق"</item>
+ <item msgid="7698638434317271902">"اتصال قطع شد از <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="197508606402264311">"اتصال قطع شد"</item>
+ <item msgid="8578370891960825148">"ناموفق"</item>
+ <item msgid="5660739516542454527">"مسدود شده"</item>
+ <item msgid="1805837518286731242">"جلوگیری موقت از اتصال ضعیف"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
new file mode 100644
index 0000000..fa9983a
--- /dev/null
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"اسکن شبکهها امکانپذیر نیست"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"هیچکدام"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"ذخیرهشده"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"غیرفعال شد"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"پیکربندی IP انجام نشد"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"اتصال Wi-Fi برقرار نشد"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"مشکل تأیید اعتبار"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"در محدوده نیست"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"دسترسی به اینترنت شناسایی نشد، به صورت خودکار وصل نمیشود."</string>
+ <string name="saved_network" msgid="4352716707126620811">"ذخیرهشده توسط <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"متصل شده از طریق دستیار Wi-Fi"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"متصل از طریق %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"دردسترس از طریق %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"متصل، بدون اینترنت"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"اتصال قطع شد"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"در حال قطع اتصال..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"در حال اتصال…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"متصل"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"در حال مرتبطسازی..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"متصل شد (بدون تلفن)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"متصل شد (بدون رسانه)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"متصل (عدم دسترسی به پیام)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"متصل شد (بدون تلفن یا رسانه)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"رسانه صوتی"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"صدای تلفن"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"انتقال فایل"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"دستگاه ورودی"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"دسترسی به اینترنت"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"اشتراکگذاری مخاطب"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"استفاده برای اشتراکگذاری مخاطب"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"اشتراکگذاری اتصال اینترنت"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"دسترسی به پیام"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"دسترسی سیمکارت"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"به رسانه صوتی متصل شد"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"به تلفن صوتی متصل شد"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"به سرور انتقال فایل متصل شد"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"به نقشه متصل شد"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"متصل به SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"به سرور انتقال فایل متصل نیست"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"به دستگاه ورودی متصل شد"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"برای دسترسی به اینترنت، به دستگاه متصل شد"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"اشتراکگذاری اتصال اینترنت محلی با دستگاه"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"استفاده برای دسترسی به اینترنت"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"استفاده برای نقشه"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"استفاده برای دسترسی سیمکارت"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"استفاده برای رسانه صوتی"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"استفاده برای تلفن صوتی"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"استفاده برای انتقال فایل"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"استفاده برای چاپ"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"مرتبطسازی"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"مرتبطسازی"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"لغو"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"وقتی وصل باشید، مرتبطسازی اجازه دسترسی به مخاطبین و سابقه تماستان را فراهم میکند."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"با <xliff:g id="DEVICE_NAME">%1$s</xliff:g> مرتبطسازی نشد."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"به خاطر یک پین یا کلیدواژه نادرست، مرتبطسازی با <xliff:g id="DEVICE_NAME">%1$s</xliff:g> انجام نشد."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"ارتباط با <xliff:g id="DEVICE_NAME">%1$s</xliff:g> امکانپذیر نیست."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> مرتبطسازی را رد کرد."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi‑Fi خاموش است."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi قطع شد."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"یک نوار برای Wi‑Fi."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"دو نوار برای Wi‑Fi."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"سه نوار برای Wi‑Fi."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"قدرت سیگنال Wi‑Fi کامل است."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"سیستم عامل Android"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"برنامههای حذف شده"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"برنامهها و کاربران حذف شده"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"اتصال داده با سیم USB"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"نقطه اتصال قابل حمل"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"اتصال اینترنت با تلفن همراه بلوتوث"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"اتصال به اینترنت با تلفن همراه"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"تترینگ و نقطه اتصال قابل حمل"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"نمایه کاری"</string>
+ <string name="user_guest" msgid="8475274842845401871">"مهمان"</string>
+ <string name="unknown" msgid="1592123443519355854">"ناشناس"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"کاربر: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"بعضی پیشفرضها تنظیم شدهاند"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"هیچ پیشفرضی تنظیم نشده است"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"تنظیمات متن به گفتار"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"خروجی تبدیل متن به گفتار"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"سرعت گفتار"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"سرعتی که متن خوانده میشود"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"زبان"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"استفاده از زبان سیستم"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"زبان انتخاب نشده است"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"صدای خاص یک زبان را برای متن گفتاری تنظیم میکند"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"به نمونهای گوش کنید"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"قسمت کوتاهی از ترکیب گفتار پخش شود"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"نصب دادههای صوتی"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"نصب دادههای صوتی مورد نیاز برای ترکیب گفتار"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"این موتور ترکیب صوتی ممکن است بتواند تمام متن گفته شده را جمعآوری کند، از جمله اطلاعات شخصی مانند رمزهای ورود و شمارههای کارت اعتباری. این کارها توسط موتور<xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> انجام میشود. استفاده از این موتور ترکیب صوتی را فعال میکنید؟"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"این زبان به یک اتصال شبکه فعال برای خروجی نوشتار به گفتار نیاز دارد."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"این نمونهای از ترکیب گفتار است"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"وضعیت زبان پیشفرض"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> کاملاً پشتیبانی میشود"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> به اتصال شبکه نیاز دارد"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> پشتیبانی نمیشود"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"در حال بررسی…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"تنظیمات برای <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"راهاندازی تنظیمات موتور"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"موتور ترجیحی"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"کلی"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"بسیار آهسته"</item>
+ <item msgid="4795095314303559268">"آهسته"</item>
+ <item msgid="8903157781070679765">"معمولی"</item>
+ <item msgid="164347302621392996">"سریع"</item>
+ <item msgid="5794028588101562009">"سریعتر"</item>
+ <item msgid="7163942783888652942">"خیلی سریع"</item>
+ <item msgid="7831712693748700507">"تند"</item>
+ <item msgid="5194774745031751806">"خیلی تند"</item>
+ <item msgid="9085102246155045744">"سریعترین"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"انتخاب نمایه"</string>
+ <string name="category_personal" msgid="1299663247844969448">"شخصی"</string>
+ <string name="category_work" msgid="8699184680584175622">"محل کار"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-fi/arrays.xml b/packages/SettingsLib/res/values-fi/arrays.xml
new file mode 100644
index 0000000..76cf49b
--- /dev/null
+++ b/packages/SettingsLib/res/values-fi/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Etsitään..."</item>
+ <item msgid="8513729475867537913">"Yhdistetään…"</item>
+ <item msgid="515055375277271756">"Varmennetaan…"</item>
+ <item msgid="1943354004029184381">"Haetaan IP-osoitetta..."</item>
+ <item msgid="4221763391123233270">"Yhdistetty"</item>
+ <item msgid="624838831631122137">"Pidätetty"</item>
+ <item msgid="7979680559596111948">"Katkaistaan yhteyttä..."</item>
+ <item msgid="1634960474403853625">"Yhteys katkaistu"</item>
+ <item msgid="746097431216080650">"Epäonnistui"</item>
+ <item msgid="6367044185730295334">"Estetty"</item>
+ <item msgid="503942654197908005">"Vältetään huonoa yhteyttä tilapäisesti"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Etsitään..."</item>
+ <item msgid="355508996603873860">"Yhdistetään verkkoon <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="554971459996405634">"Varmennetaan verkkoon <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="7928343808033020343">"Haetaan IP-osoitetta verkosta <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="8937994881315223448">"Yhdistetty verkkoon <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Pidätetty"</item>
+ <item msgid="7698638434317271902">"Katkaistaan yhteys verkosta <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="197508606402264311">"Yhteys katkaistu"</item>
+ <item msgid="8578370891960825148">"Epäonnistui"</item>
+ <item msgid="5660739516542454527">"Estetty"</item>
+ <item msgid="1805837518286731242">"Vältetään huonoa yhteyttä tilapäisesti"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
new file mode 100644
index 0000000..2037885
--- /dev/null
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Verkkoja ei voi etsiä."</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Ei mitään"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Tallennettu"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Pois käytöstä"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-kokoonpanovirhe"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi-yhteysvirhe"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Todennusvirhe"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Ei kantoalueella"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Internetyhteyttä ei havaittu, yhteyttä ei muodosteta automaattisesti uudelleen."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Tallentaja: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Yhteys muodostettu Wi‑Fi-apurin kautta"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Yhdistetty seuraavan kautta: %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Käytettävissä seuraavan kautta: %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Yhdistetty, ei internetyhteyttä."</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Yhteys katkaistu"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Katkaistaan yhteyttä..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Yhdistetään…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Yhdistetty"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Laiteparia muodostetaan..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Yhdistetty (ei puhelimen ääntä)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Yhdistetty (ei median ääntä)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Yhdistetty (ei MAP)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Yhdistetty (ei puhelimen/median ääntä)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Median ääni"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Puhelimen ääni"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Tiedostonsiirto"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Syöttölaite"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Internetyhteys"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Yhteystietojen jakaminen"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Käytä yhteystietojen jakamiseen"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Internetyhteyden jakaminen"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"MAP"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM-kortin käyttö"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Yhdistetty median ääneen"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Yhdistetty puhelimen ääneen"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Yhdistetty tiedostonsiirtopalvelimeen"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"MAP-yhteys"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"SAP-yhteys on muodostettu."</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Ei yhdistetty tiedostonsiirtopalvelimeen"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Yhdistetty syöttölaitteeseen"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Yhdistetty laitteen internetyhteyteen"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Paikallinen internetyhteys jaetaan laitteen kanssa"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Käytä internetyhteyteen"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Käytä MAP-profiilille"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Käytetään SIM-kortin käyttöoikeuden määrittämiseen."</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Käytä median äänille"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Käytä puhelimen äänille"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Käytä tiedostojen siirtoon"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Käytä syöttöön"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Muodosta laitepari"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"MUODOSTA LAITEPARI"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Peruuta"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Laiteparin muodostaminen mahdollistaa yhteystietojen ja soittohistorian käyttämisen yhteyden aikana."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Laiteparin muodostaminen laitteeseen <xliff:g id="DEVICE_NAME">%1$s</xliff:g> epäonnistui."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Laiteparia laitteen <xliff:g id="DEVICE_NAME">%1$s</xliff:g> kanssa ei voitu muodostaa, koska PIN-koodi tai avain oli virheellinen."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Ei yhteyttä laitteeseen <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Laite <xliff:g id="DEVICE_NAME">%1$s</xliff:g> torjui laitepariyhteyden."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi pois käytöstä"</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Ei Wi-Fi-yhteyttä"</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi-signaali – yksi palkki"</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wi-Fi-signaali – kaksi palkkia"</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wi-Fi-signaali – kolme palkkia"</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Vahva Wi-Fi-signaali"</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android-käyttöjärjestelmä"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Poistetut sovellukset"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Poistetut sovellukset ja käyttäjät"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"Jaettu yhteys USB:n kautta"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Kannettava yhteyspiste"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Jaettu Bluetooth-yhteys"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Jaettu yhteys"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Jaettu yhteys ja kannettava yhteyspiste"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Työprofiili"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Vieras"</string>
+ <string name="unknown" msgid="1592123443519355854">"Tuntematon"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Käyttäjä: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Joitakin oletuksia on asetettu."</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Oletuksia ei asetettu."</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Tekstistä puheeksi -asetukset"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Tekstistä puheeksi -toisto"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Puheen nopeus"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Tekstin puhumisnopeus"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Kieli"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Käytä järjestelmän kieltä"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Kieltä ei ole valittu"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Asettaa puhutulle tekstille kielikohtaisen äänen"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Kuuntele esimerkki"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Toista lyhyt esittely puhesynteesistä"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Asenna äänitiedot"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Asenna puhesynteesiin tarvittavat äänitiedot"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Tämä puhesynteesimoottori saattaa kerätä kaiken puhutun tekstin, mukaan lukien henkilökohtaiset tiedot kuten salasanat ja luottokorttinumerot. Se on lähtöisin moottorista <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Haluatko ottaa tämän puhesynteesimoottorin käyttöön?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Tämä kieli vaatii verkkoyhteyden, jotta tekstistä puheeksi muuntaminen toimii."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Tämä on esimerkki puhesynteesistä."</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Oletuskieliasetus"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"Kieltä <xliff:g id="LOCALE">%1$s</xliff:g> tuetaan"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"Kieli <xliff:g id="LOCALE">%1$s</xliff:g> tarvitsee yhteyden verkostoon"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"Kieltä <xliff:g id="LOCALE">%1$s</xliff:g> ei tueta"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Tarkistetaan…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Asetukset: <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Käynnistä moottorin asetukset"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Ensisijainen kone"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Yleiset"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Hyvin hidas"</item>
+ <item msgid="4795095314303559268">"Hidas"</item>
+ <item msgid="8903157781070679765">"Normaali"</item>
+ <item msgid="164347302621392996">"Nopea"</item>
+ <item msgid="5794028588101562009">"Nopeampi"</item>
+ <item msgid="7163942783888652942">"Hyvin nopea"</item>
+ <item msgid="7831712693748700507">"Nopea"</item>
+ <item msgid="5194774745031751806">"Erittäin nopea"</item>
+ <item msgid="9085102246155045744">"Nopein"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Valitse profiili"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Henkilökohtainen"</string>
+ <string name="category_work" msgid="8699184680584175622">"Työ"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-fr-rCA/arrays.xml b/packages/SettingsLib/res/values-fr-rCA/arrays.xml
new file mode 100644
index 0000000..991d824
--- /dev/null
+++ b/packages/SettingsLib/res/values-fr-rCA/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Recherche…"</item>
+ <item msgid="8513729475867537913">"Connexion en cours…"</item>
+ <item msgid="515055375277271756">"Authentification en cours…"</item>
+ <item msgid="1943354004029184381">"Récupération de l\'adresse IP…"</item>
+ <item msgid="4221763391123233270">"Connecté"</item>
+ <item msgid="624838831631122137">"Suspendu"</item>
+ <item msgid="7979680559596111948">"Déconnexion…"</item>
+ <item msgid="1634960474403853625">"Déconnecté"</item>
+ <item msgid="746097431216080650">"Échec"</item>
+ <item msgid="6367044185730295334">"Bloqué"</item>
+ <item msgid="503942654197908005">"Mauvaise connexion évitée momentanément"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Recherche…"</item>
+ <item msgid="355508996603873860">"Connexion à <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="554971459996405634">"Authentification avec <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="7928343808033020343">"Obtention de l\'adresse IP à partir de <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="8937994881315223448">"Connecté à <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Suspendu"</item>
+ <item msgid="7698638434317271902">"Déconnexion de <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="197508606402264311">"Déconnecté"</item>
+ <item msgid="8578370891960825148">"Échec"</item>
+ <item msgid="5660739516542454527">"Bloqué"</item>
+ <item msgid="1805837518286731242">"Mauvaise connexion évitée momentanément"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000..da22cf9
--- /dev/null
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Impossible de rechercher des réseaux."</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Aucun"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Enregistré"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Désactivés"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Échec de configuration de l\'adresse IP"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Échec de connexion Wi-Fi"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problème d\'authentification"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Hors de portée"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Aucun accès à Internet détecté, reconnexion automatique impossible"</string>
+ <string name="saved_network" msgid="4352716707126620811">"Enregistrés par <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Connecté à l\'aide de l\'assistant Wi-Fi"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Connecté par %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Accessible par %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Connecté, aucun accès à Internet"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Déconnecté"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Déconnexion…"</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Connexion en cours…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Connecté"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Association…"</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Connecté (sans audio pour appels tél.)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Connecté (sans audio contenu mutimédia)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Connecté (sans accès aux messages)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Connecté (sans audio tel./multimédia)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Paramètres audio du support"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Paramètres audio du téléphone"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Transfert de fichier"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Périphérique d\'entrée"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Accès Internet"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Partage de contact"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Utiliser pour le partage de contacts"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Partage de connexion Internet"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Accès aux messages"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Accès à la carte SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Connecté aux paramètres audio du média"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Connecté à l\'audio du téléphone"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Connexion au serveur de transfert de fichiers"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Connecté à la carte"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Connecté au point d\'accès au service"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Connexion au serveur de transfert de fichiers non établie"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Connecté au périphérique d\'entrée"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Connecté à l\'appareil pour accès Internet"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Connexion Internet locale partagée"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Utiliser pour l\'accès à Internet"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Utiliser pour la carte"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Utiliser pour l\'accès à la carte SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Utiliser pour les paramètres audio du support"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Utiliser pour les paramètres audio du téléphone"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Utiliser pour le transfert de fichiers"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Utiliser comme entrée"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Associer"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ASSOCIER"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Annuler"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"L\'association vous permet d\'accéder à vos contacts et à l\'historique des appels lorsque vous êtes connecté."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Impossible d\'associer à <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Impossible d\'établir l\'association avec <xliff:g id="DEVICE_NAME">%1$s</xliff:g> en raison d\'un NIP ou d\'une clé d\'accès incorrects."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Impossible d\'établir la communication avec <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Association refusée par <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi désactivé."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi déconnecté."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi : une barre."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wi-Fi : deux barres."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wi-Fi : trois barres."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Wi-Fi : signal complet."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Système d\'exploitation Android"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Applications supprimées"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Applications et utilisateurs supprimés"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"Partage de connexion par USB"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Point d\'accès Wi-Fi mobile"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Via Bluetooth"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Partage de connexion"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Partage de connexion et point d\'accès mobile"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Profil professionnel"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Invité"</string>
+ <string name="unknown" msgid="1592123443519355854">"Inconnu"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Utilisateur : <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Certaines préférences par défaut définies"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Aucune préférence par défaut définie"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Synthèse vocale"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Sortie de la synthèse vocale"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Cadence"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Vitesse à laquelle le texte est énoncé"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Langue"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Utiliser la langue du système"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Langue non sélectionnée"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Définit la langue utilisée par la syntèse vocale"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Écouter un échantillon"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Lire une courte démonstration de la synthèse vocale"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Installer les données vocales"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Installer les données nécessaires à la synthèse vocale"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Ce moteur de synthèse vocale est susceptible d\'enregistrer tout ce que vous direz, y compris des données personnelles telles que les mots de passe et les numéros de carte de paiement. Il provient du moteur <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Voulez-vous l\'activer?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Une connexion réseau est nécessaire pour la synthèse vocale dans cette langue."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Voici un échantillon de synthèse vocale"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"État de la langue par défaut"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"Les paramètres régionaux <xliff:g id="LOCALE">%1$s</xliff:g> sont compatibles"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"Les paramètres régionaux <xliff:g id="LOCALE">%1$s</xliff:g> nécessitent une connexion réseau"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"Les paramètres régionaux <xliff:g id="LOCALE">%1$s</xliff:g> ne sont pas compatibles"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Vérification en cours…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Paramètres de <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Lancer les paramètres du moteur"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Moteur préféré"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Général"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Très lente"</item>
+ <item msgid="4795095314303559268">"Lente"</item>
+ <item msgid="8903157781070679765">"Normale"</item>
+ <item msgid="164347302621392996">"Rapide"</item>
+ <item msgid="5794028588101562009">"Plus rapide"</item>
+ <item msgid="7163942783888652942">"Très rapide"</item>
+ <item msgid="7831712693748700507">"Rapide"</item>
+ <item msgid="5194774745031751806">"Très rapide"</item>
+ <item msgid="9085102246155045744">"La plus rapide"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Sélectionnez un profil"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Personnel"</string>
+ <string name="category_work" msgid="8699184680584175622">"Travail"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-fr/arrays.xml b/packages/SettingsLib/res/values-fr/arrays.xml
new file mode 100644
index 0000000..050793b
--- /dev/null
+++ b/packages/SettingsLib/res/values-fr/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Recherche…"</item>
+ <item msgid="8513729475867537913">"Connexion…"</item>
+ <item msgid="515055375277271756">"Authentification..."</item>
+ <item msgid="1943354004029184381">"Récupération de l\'adresse IP…"</item>
+ <item msgid="4221763391123233270">"Connecté"</item>
+ <item msgid="624838831631122137">"Interrompu"</item>
+ <item msgid="7979680559596111948">"Déconnexion…"</item>
+ <item msgid="1634960474403853625">"Déconnecté"</item>
+ <item msgid="746097431216080650">"Échec"</item>
+ <item msgid="6367044185730295334">"Bloqué"</item>
+ <item msgid="503942654197908005">"Mauvaise connexion évitée momentanément"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Recherche…"</item>
+ <item msgid="355508996603873860">"Connexion à <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="554971459996405634">"Authentification avec <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="7928343808033020343">"Récupération de l\'adresse IP à partir de <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="8937994881315223448">"Connecté à <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Interrompu"</item>
+ <item msgid="7698638434317271902">"Déconnexion de <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="197508606402264311">"Déconnecté"</item>
+ <item msgid="8578370891960825148">"Échec"</item>
+ <item msgid="5660739516542454527">"Bloqué"</item>
+ <item msgid="1805837518286731242">"Mauvaise connexion évitée momentanément"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
new file mode 100644
index 0000000..9609ff9
--- /dev/null
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Impossible de rechercher des réseaux."</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Aucune"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Enregistré"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Désactivé"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Échec de configuration de l\'adresse IP"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Échec de la connexion Wi-Fi"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problème d\'authentification."</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Hors de portée"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Aucun accès à Internet détecté, reconnexion automatique impossible"</string>
+ <string name="saved_network" msgid="4352716707126620811">"Enregistré par <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Connecté via l\'assistant Wi‑Fi"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Connecté via %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Disponible via %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Connecté, aucun accès à Internet"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Déconnecté"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Déconnexion…"</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Connexion…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Connecté"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Association…"</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Connecté (sans audio pour appels tél.)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Connecté (sans audio contenu mutimédia)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Connecté (sans accès aux messages)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Connecté (sans audio tel./multimédia)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Multimédia"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Appels et notifications"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Transfert de fichier"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Périphérique d\'entrée"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Accès Internet"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Partage de contacts"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Utiliser pour le partage de contacts"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Partage de connexion Internet"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Accès aux messages"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Accès à la carte SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Connecté aux paramètres audio du média"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Connecté aux paramètres audio du téléphone"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Connexion au serveur de transfert de fichiers"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Connecté à la carte"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Connecté au point d\'accès au service"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Connexion au serveur de transfert de fichiers non établie"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Connecté au périphérique d\'entrée"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Connecté à l\'appareil pour accès Internet"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Connexion Internet locale partagée"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Utiliser pour l\'accès à Internet"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Utiliser pour la carte"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Utiliser pour l\'accès à la carte SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Utiliser pour les paramètres audio du média"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Utiliser pour les paramètres audio du téléphone"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Utiliser pour le transfert de fichiers"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Utiliser comme entrée"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Associer"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ASSOCIER"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Annuler"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"L\'association vous permet d\'accéder à vos contacts et à l\'historique des appels lorsque vous êtes connecté."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Impossible d\'associer à <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Impossible d\'établir l\'association avec <xliff:g id="DEVICE_NAME">%1$s</xliff:g> en raison d\'un code ou d\'une clé d\'accès incorrects."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Impossible d\'établir la communication avec <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Association refusée par <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi désactivé"</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi déconnecté"</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Signal Wi-Fi faible"</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Signal Wi-Fi moyen"</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Signal Wi-Fi bon"</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Signal Wi-Fi excellent"</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Plate-forme Android"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Applications supprimées"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Applications et utilisateurs supprimés"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"Partage connexion Bluetooth par USB"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Point d\'accès Wi-Fi mobile"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Partage connexion Bluetooth"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Partage de connexion"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Partage de connexion"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Profil professionnel"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Invité"</string>
+ <string name="unknown" msgid="1592123443519355854">"Inconnu"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Utilisateur : <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Certains paramètres par défaut définis"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Aucun paramètre par défaut défini"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Paramètres de la synthèse vocale"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Synthèse vocale"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Cadence"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Vitesse à laquelle le texte est énoncé"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Langue"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Utiliser la langue du système"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Langue non sélectionnée"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Définir la langue utilisée par la synthèse vocale"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Écouter un échantillon"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Lire une courte démonstration de la synthèse vocale"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Installer les données vocales"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Installer les données nécessaires à la synthèse vocale"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Ce moteur de synthèse vocale est susceptible de collecter tout ce qui sera lu, y compris les données personnelles comme les mots de passe et les numéros de carte de paiement. Il provient du moteur <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Voulez-vous activer son utilisation ?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Une connexion réseau est nécessaire pour la synthèse vocale dans cette langue."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Voici un échantillon de synthèse vocale."</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"État de la langue par défaut"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"Les paramètres régionaux <xliff:g id="LOCALE">%1$s</xliff:g> sont compatibles."</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"Les paramètres régionaux <xliff:g id="LOCALE">%1$s</xliff:g> nécessitent une connexion réseau."</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"Les paramètres régionaux <xliff:g id="LOCALE">%1$s</xliff:g> ne sont pas compatibles."</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Vérification…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Paramètres de <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Lancer les paramètres du moteur"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Moteur préféré"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Paramètres généraux"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Très lente"</item>
+ <item msgid="4795095314303559268">"Lente"</item>
+ <item msgid="8903157781070679765">"Normale"</item>
+ <item msgid="164347302621392996">"Rapide"</item>
+ <item msgid="5794028588101562009">"Plus rapide"</item>
+ <item msgid="7163942783888652942">"Très rapide"</item>
+ <item msgid="7831712693748700507">"Rapide"</item>
+ <item msgid="5194774745031751806">"Très rapide"</item>
+ <item msgid="9085102246155045744">"La plus rapide"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Sélectionner un profil"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Personnel"</string>
+ <string name="category_work" msgid="8699184680584175622">"Professionnel"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-gl-rES/arrays.xml b/packages/SettingsLib/res/values-gl-rES/arrays.xml
new file mode 100644
index 0000000..8122efb
--- /dev/null
+++ b/packages/SettingsLib/res/values-gl-rES/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Buscando..."</item>
+ <item msgid="8513729475867537913">"Conectando..."</item>
+ <item msgid="515055375277271756">"Autenticando…"</item>
+ <item msgid="1943354004029184381">"Obtendo enderezo IP..."</item>
+ <item msgid="4221763391123233270">"Conectada"</item>
+ <item msgid="624838831631122137">"Suspendida"</item>
+ <item msgid="7979680559596111948">"Desconectando..."</item>
+ <item msgid="1634960474403853625">"Desconectada"</item>
+ <item msgid="746097431216080650">"Incorrecta"</item>
+ <item msgid="6367044185730295334">"Bloqueada"</item>
+ <item msgid="503942654197908005">"Evitando conexión deficiente temporalmente"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Buscando..."</item>
+ <item msgid="355508996603873860">"Conectando con <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="554971459996405634">"Autenticando con <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="7928343808033020343">"Obtendo enderezo IP de <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="8937994881315223448">"Conectado a <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Suspendida"</item>
+ <item msgid="7698638434317271902">"Desconectando de <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="197508606402264311">"Desconectada"</item>
+ <item msgid="8578370891960825148">"Incorrecta"</item>
+ <item msgid="5660739516542454527">"Bloqueada"</item>
+ <item msgid="1805837518286731242">"Evitando conexión deficiente temporalmente"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-gl-rES/strings.xml b/packages/SettingsLib/res/values-gl-rES/strings.xml
new file mode 100644
index 0000000..ffede7e
--- /dev/null
+++ b/packages/SettingsLib/res/values-gl-rES/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Non se poden explorar redes"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Ningunha"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Gardada"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Desactivadas"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Erro na configuración de IP"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Erro na conexión wifi"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problema de autenticación"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Non está dentro da zona de cobertura"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Non se detectou acceso a Internet e non se volverá conectar automaticamente."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Redes gardadas por <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Conectado ao asistente de wifi"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Conectado a través de %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Dispoñible a través de %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Conectado, pero sen Internet"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Desconectado"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Desconectando..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Conectando..."</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Conectado"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Sincronizando..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Conectado (ningún teléfono)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Conectado (sen ficheiros multimedia)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Conectado (sen acceso ás mensaxes)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Conectado (ningún teléfono nin soporte)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Audio multimedia"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Audio do teléfono"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Transferencia de ficheiros"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Dispositivo de entrada"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Acceso a Internet"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Compartir contactos"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Utilizar para compartir contactos"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Uso compartido da conexión a Internet"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Acceso ás mensaxes"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Acceso á SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Conectado ao audio multimedia"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Conectado ao audio do teléfono"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Conectado ao servidor de transferencia de ficheiros"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Conectado ao mapa"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Conectado a SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Non conectado co servidor de transferencia de ficheiros"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Conectado ao dispositivo de entrada"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Conectado ao dispositivo para acceder a Internet"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Compartindo conexión a Internet co dispositivo"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Utilízase para o acceso a Internet"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Usar para o mapa"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Usar para acceso á SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Utilízase para audio multimedia"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Utilízase para o audio do teléfono"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Utilízase para a transferencia de ficheiros"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Utilízase para a entrada"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Sincronizar"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"SINCRONIZAR"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Cancelar"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"A sincronización garante acceso aos teus contactos e ao historial de chamadas ao estar conectado"</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Non se puido sincronizar con <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Non se puido sincronizar con <xliff:g id="DEVICE_NAME">%1$s</xliff:g> debido a que se introduciu un contrasinal ou PIN incorrecto."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Non se pode comunicar con <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Sincronización rexeitada por <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wifi desactivada."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wifi desconectada."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Unha barra de wifi."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Dúas barras de wifi."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Tres barras de wifi."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Sinal completo de wifi."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"SO Android"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Aplicacións eliminadas"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Aplicacións e usuarios eliminados"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"Ancoraxe de USB"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Zona interactiva portátil"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Ancoraxe de Bluetooth"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Ancoraxe á rede"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Ancoraxe á rede e zona wifi"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Perfil do traballo"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Convidado"</string>
+ <string name="unknown" msgid="1592123443519355854">"Descoñecida"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Usuario: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Definíronse algúns valores predeterminados"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Non se definiu ningún valor predeterminado"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Configuración da síntese de voz"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Síntese de voz"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Velocidade da fala"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Velocidade á que se di o texto"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Idioma"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Utilizar idioma do sistema"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Idioma non seleccionado"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Define a voz específica do idioma para o texto falado"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Escoitar un exemplo"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Reproduce unha breve demostración da síntese de voz"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Instalar datos de voz"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Instala os datos de voz necesarios para a síntese de voz"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"É posible que este motor de síntese de voz poida recompilar todo o texto falado, incluídos datos persoais como contrasinais e números de tarxetas de crédito. Provén do motor <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Queres activar o uso deste motor de síntese de voz?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Este idioma precisa dispoñer dunha conexión de rede que funcione para emitir a síntese de voz."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Este é un exemplo da síntese de voz"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Estado do idioma predeterminado"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> é completamente compatible"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> require unha conexión de rede"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"Non se admite <xliff:g id="LOCALE">%1$s</xliff:g>"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Comprobando..."</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Configuración de <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Iniciar configuración do motor"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Motor preferido"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Xeral"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Moi lento"</item>
+ <item msgid="4795095314303559268">"Lento"</item>
+ <item msgid="8903157781070679765">"Normal"</item>
+ <item msgid="164347302621392996">"Rápido"</item>
+ <item msgid="5794028588101562009">"Máis rápido"</item>
+ <item msgid="7163942783888652942">"Moi rápido"</item>
+ <item msgid="7831712693748700507">"Rápido"</item>
+ <item msgid="5194774745031751806">"Moi rápido"</item>
+ <item msgid="9085102246155045744">"O máis rápido"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Seleccionar perfil"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Persoal"</string>
+ <string name="category_work" msgid="8699184680584175622">"Traballo"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-gu-rIN/arrays.xml b/packages/SettingsLib/res/values-gu-rIN/arrays.xml
new file mode 100644
index 0000000..0c9e673
--- /dev/null
+++ b/packages/SettingsLib/res/values-gu-rIN/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"સ્કેન કરી રહ્યું છે..."</item>
+ <item msgid="8513729475867537913">"કનેક્ટ થઈ રહ્યું છે…"</item>
+ <item msgid="515055375277271756">"પ્રમાણિત કરી રહ્યું છે..."</item>
+ <item msgid="1943354004029184381">"IP સરનામું મેળવી રહ્યું છે..."</item>
+ <item msgid="4221763391123233270">"કનેક્ટ કર્યું"</item>
+ <item msgid="624838831631122137">"સસ્પેન્ડ કરેલ"</item>
+ <item msgid="7979680559596111948">"ડિસ્કનેક્ટ થઈ રહ્યું છે..."</item>
+ <item msgid="1634960474403853625">"ડિસ્કનેક્ટ કર્યું"</item>
+ <item msgid="746097431216080650">"અસફળ"</item>
+ <item msgid="6367044185730295334">"અવરોધિત"</item>
+ <item msgid="503942654197908005">"નબળા કનેક્શનને અસ્થાયી રૂપે ટાળી રહ્યું છે"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"સ્કેન કરી રહ્યું છે..."</item>
+ <item msgid="355508996603873860">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> પર કનેક્ટ થઈ રહ્યું છે..."</item>
+ <item msgid="554971459996405634">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> સાથે પ્રમાણીકૃત થઈ રહ્યું છે…"</item>
+ <item msgid="7928343808033020343">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> તરફથી IP સરનામું મેળવી રહ્યું છે..."</item>
+ <item msgid="8937994881315223448">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> સાથે કનેક્ટ થયાં"</item>
+ <item msgid="1330262655415760617">"સસ્પેન્ડ કરેલ"</item>
+ <item msgid="7698638434317271902">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> થી ડિસ્કનેક્ટ થઈ રહ્યાં છે…"</item>
+ <item msgid="197508606402264311">"ડિસ્કનેક્ટ કર્યું"</item>
+ <item msgid="8578370891960825148">"અસફળ"</item>
+ <item msgid="5660739516542454527">"અવરોધિત"</item>
+ <item msgid="1805837518286731242">"અસ્થાયી રૂપે નબળા કનેક્શનને ટાળી રહ્યું છે"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-gu-rIN/strings.xml b/packages/SettingsLib/res/values-gu-rIN/strings.xml
new file mode 100644
index 0000000..d515899
--- /dev/null
+++ b/packages/SettingsLib/res/values-gu-rIN/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"નેટવર્ક્સ માટે સ્કૅન કરી શકતા નથી"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"કોઈ નહીં"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"સાચવેલા"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"અક્ષમ કર્યો"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP કન્ફિગરેશન નિષ્ફળ"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi કનેક્શન નિષ્ફળ"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"પ્રમાણીકરણ સમસ્યા"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"રેન્જમાં નથી"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"કોઈ ઇન્ટરનેટ અૅક્સેસ શોધાયું નથી, આપમેળે ફરીથી કનેક્ટ કરશે નહીં."</string>
+ <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> દ્વારા સચવાયું"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Wi-Fi સહાયક દ્વારા કનેક્ટ થયું"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s દ્વારા કનેક્ટ થયેલ"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s દ્વારા ઉપલબ્ધ"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"કનેક્ટ કર્યું, કોઈ ઇન્ટરનેટ નથી"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"ડિસ્કનેક્ટ કર્યું"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"ડિસ્કનેક્ટ થઈ રહ્યું છે..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"કનેક્ટ થઈ રહ્યું છે…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"કનેક્ટ કર્યું"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"જોડી કરી રહ્યું છે…"</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"કનેક્ટ કર્યું (કોઇ ફોન નથી)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"કનેક્ટ કર્યું (મીડિયા નથી)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"કનેક્ટ કર્યું (કોઇ સંદેશ ઍક્સેસ નથી)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"કનેક્ટ કરેલ (કોઈ ફોન અથવા મીડિયા નથી)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"મીડિયા ઑડિઓ"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"ફોન ઑડિઓ"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"ફાઇલ સ્થાનાંતરણ"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"ઇનપુટ ઉપકરણ"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"ઇન્ટરનેટ ઍક્સેસ"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"સંપર્ક શેરિંગ"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"સંપર્ક શેરિંગ માટે ઉપયોગ કરો"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"ઇન્ટરનેટ કનેક્શન શેરિંગ"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"સંદેશ ઍક્સેસ"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM ઍક્સેસ"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"મીડિયા ઑડિઓ સાથે કનેક્ટ કર્યુ"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"ફોન ઑડિઓ સાથે કનેક્ટ થયાં"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ફાઇલ સ્થાનાંતરણ સેવાથી કનેક્ટ થયાં"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"નકશા સાથે કનેક્ટ થયું"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"SAP થી કનેક્ટ કરેલ"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"ફાઇલ સ્થાનાંતરણ સેવાથી કનેક્ટ થયેલ નથી"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"ઇનપુટ ઉપકરણ સાથે કનેક્ટ થયાં"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"ઇન્ટરનેટ ઍક્સેસ માટે ઉપકરણથી કનેક્ટેડ છે"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"ઉપકરણ સાથે સ્થાનિક ઇન્ટરનેટ કનેક્શન શેર કરે છે"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"ઇન્ટરનેટ ઍક્સેસ માટે ઉપયોગ કરો"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"નકશા માટે વાપરો"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"SIM ઍક્સેસ માટે ઉપયોગ કરો"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"મીડિયા ઑડિઓ માટે ઉપયોગ કરો"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"ફોન ઑડિઓ માટે ઉપયોગ કરો"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ફાઇલ સ્થાનાંતર માટે ઉપયોગ કરો"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ઇનપુટ માટે ઉપયોગ કરો"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"જોડી"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"જોડી કરો"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"રદ કરો"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"જોડી કરવી એ કનેક્ટ કરેલ હોય ત્યારે તમારા સંપર્કો અને કૉલ ઇતિહાસની અૅક્સેસ આપે છે."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> સાથે જોડી કરી શક્યાં નહીં."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"એક ખોટા PIN અથવા પાસકીને કારણે <xliff:g id="DEVICE_NAME">%1$s</xliff:g> સાથે જોડી બનાવી શકાઈ નથી."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> સાથે સંચાર કરી શકાતો નથી."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> દ્વારા જોડી કરવાનું નકાર્યું."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wifi બંધ."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wifi ડિસ્કનેક્ટ થયું."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wifi એક બાર."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wifi બે બાર."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wifi ત્રણ બાર."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"પૂર્ણ Wifi સિગ્નલ."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"દૂર કરેલી એપ્લિકેશનો"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"દૂર કરેલી એપ્લિકેશનો અને વપરાશકર્તાઓ"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB ટિથરિંગ"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"પોર્ટેબલ હોટસ્પોટ"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth ટિથરિંગ"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"ટિથરિંગ"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"ટિથરિંગ અને પોર્ટેબલ હોટસ્પોટ"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"કાર્ય પ્રોફાઇલ"</string>
+ <string name="user_guest" msgid="8475274842845401871">"અતિથિ"</string>
+ <string name="unknown" msgid="1592123443519355854">"અજાણ્યું"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"વપરાશકર્તા: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"કેટલાંક ડિફોલ્ટ્સ સેટ કરેલ છે"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"કોઇ ડિફોલ્ટ્સ સેટ કરેલ નથી"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"ટેક્સ્ટ-ટુ-સ્પીચ સેટિંગ્સ"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"ટેક્સ્ટ ટુ સ્પીચ આઉટપુટ"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"વાણી દર"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"ટેક્સ્ટ બોલાયેલ છે તે ઝડપ"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"ભાષા"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"સિસ્ટમ ભાષાનો ઉપયોગ કરો"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"ભાષા પસંદ કરેલ નથી"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"બોલાયેલ ટેક્સ્ટ માટે ભાષા-વિશિષ્ટ વૉઇસ સેટ કરે છે"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"એક ઉદાહરણ સાંભળો"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"વાણી સંશ્લેષણનું એક ટૂંકુ પ્રદર્શન ચલાવો"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"વૉઇસ ડેટા ઇન્સ્ટોલ કરો"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"વાણી સંશ્લેષણ માટે જરૂરી વૉઇસ ડેટા ઇન્સ્ટોલ કરો"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"આ વાણી સંશ્લેષણ એન્જિન પાસવર્ડ્સ અને ક્રેડિટ કાર્ડ નંબર જેવી વ્યક્તિગત માહિતી સહિત બોલવામાં આવશે તે તમામ ટેક્સ્ટને એકત્રિત કરવા માટે સક્ષમ હોઈ શકે છે. તે <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> એન્જિન પરથી આવે છે. આ વાણી સંશ્લેષણ એન્જિનના ઉપયોગને સક્ષમ કરીએ?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"આ ભાષાને ટેક્સ્ટ ટુ સ્પીચ આઉટપુટ માટે ચાલુ નેટવર્કની આવશ્યકતા છે."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"આ વાણી સંશ્લેષણનું એક ઉદાહરણ છે"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"ડીફોલ્ટ ભાષા સ્થિતિ"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> સંપૂર્ણપણે સમર્થિત છે"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> નેટવર્ક કનેક્શનની આવશ્યકતા છે"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> સમર્થિત નથી"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"તપાસી રહ્યું છે..."</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g> માટેની સેટિંગ્સ"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"એન્જિન સેટિંગ્સ લોંચ કરો"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"મનપસંદ એન્જિન"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"સામાન્ય"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"ખૂબ જ ધીમી"</item>
+ <item msgid="4795095314303559268">"ધીમી"</item>
+ <item msgid="8903157781070679765">"સામાન્ય"</item>
+ <item msgid="164347302621392996">"ઝડપી"</item>
+ <item msgid="5794028588101562009">"વધુ ઝડપી"</item>
+ <item msgid="7163942783888652942">"ખૂબ ઝડપી"</item>
+ <item msgid="7831712693748700507">"તીવ્ર"</item>
+ <item msgid="5194774745031751806">"ખૂબ જ તીવ્ર"</item>
+ <item msgid="9085102246155045744">"સૌથી ઝડપી"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"પ્રોફાઇલ પસંદ કરો"</string>
+ <string name="category_personal" msgid="1299663247844969448">"વ્યક્તિગત"</string>
+ <string name="category_work" msgid="8699184680584175622">"કાર્યાલય"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-hi/arrays.xml b/packages/SettingsLib/res/values-hi/arrays.xml
new file mode 100644
index 0000000..ec1c57f
--- /dev/null
+++ b/packages/SettingsLib/res/values-hi/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"स्कैन कर रहा है…"</item>
+ <item msgid="8513729475867537913">"कनेक्ट हो रहा है..."</item>
+ <item msgid="515055375277271756">"प्रमाणीकरण कर रहा है…"</item>
+ <item msgid="1943354004029184381">"IP पता प्राप्त कर रहा है…"</item>
+ <item msgid="4221763391123233270">"कनेक्ट किया गया"</item>
+ <item msgid="624838831631122137">"निलंबित"</item>
+ <item msgid="7979680559596111948">"डिस्कनेक्ट हो रहा है..."</item>
+ <item msgid="1634960474403853625">"डिस्कनेक्ट किया गया"</item>
+ <item msgid="746097431216080650">"असफल"</item>
+ <item msgid="6367044185730295334">"अवरोधित"</item>
+ <item msgid="503942654197908005">"खराब कनेक्शन को अस्थायी रूप से अनदेखा कर रहा है"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"स्कैन कर रहा है…"</item>
+ <item msgid="355508996603873860">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> से कनेक्ट कर रहा है…"</item>
+ <item msgid="554971459996405634">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> के साथ प्रमाणीकरण कर रहा है…"</item>
+ <item msgid="7928343808033020343">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> से IP पता प्राप्त कर रहा है…"</item>
+ <item msgid="8937994881315223448">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> से कनेक्ट किया गया"</item>
+ <item msgid="1330262655415760617">"निलंबित"</item>
+ <item msgid="7698638434317271902">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> से डिस्कनेक्ट कर रहा है…"</item>
+ <item msgid="197508606402264311">"डिस्कनेक्ट किया गया"</item>
+ <item msgid="8578370891960825148">"असफल"</item>
+ <item msgid="5660739516542454527">"अवरोधित"</item>
+ <item msgid="1805837518286731242">"खराब कनेक्शन को अस्थायी रूप से अनदेखा कर रहा है"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
new file mode 100644
index 0000000..abcbdbd
--- /dev/null
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"नेटवर्क के लिए स्कैन नहीं कर सकता"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"कोई नहीं"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"सहेजा गया"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"अक्षम"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP कॉन्फ़िगरेशन की विफलता"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"वाईफ़ाई कनेक्शन विफलता"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"प्रमाणीकरण समस्या"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"रेंज में नहीं"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"किसी इंटरनेट कनेक्शन का पता नहीं चला, अपने आप पुन: कनेक्ट नहीं हो सकता."</string>
+ <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> के द्वारा सहेजा गया"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"वाई-फ़ाई सहायक के द्वारा कनेक्ट है"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s के द्वारा उपलब्ध"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s के द्वारा उपलब्ध"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"कनेक्ट किया गया, इंटरनेट नहीं"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"डिस्कनेक्ट किया गया"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"डिस्कनेक्ट हो रहा है..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"कनेक्ट हो रहा है..."</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"कनेक्ट किया गया"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"युग्मित कर रहा है…"</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"कनेक्ट है (फ़ोन नहीं)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"कनेक्ट है (मीडिया नहीं)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"कनेक्ट किया गया (कोई संदेश एक्सेस नहीं)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"कनेक्ट है (फ़ोन या मीडिया नहीं)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"मीडिया ऑडियो"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"फ़ोन ऑडियो"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"फ़ाइल स्थानांतरण"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"इनपुट डिवाइस"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"इंटरनेट पहुंच"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"संपर्क साझाकरण"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"संपर्क साझाकरण के लिए उपयोग करें"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"इंटरनेट कनेक्शन साझाकरण"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"संदेश एक्सेस"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"सिम ऐक्सेस"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"मीडिया ऑडियो से कनेक्ट किया गया"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"फ़ोन ऑडियो से कनेक्ट किया गया"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"फ़ाइल स्थानांतरण सर्वर से कनेक्ट किया गया"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"मानचित्र से कनेक्ट किया गया"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"SAP से कनेक्ट किया गया"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"फ़ाइल स्थानांतरण सर्वर से कनेक्ट नहीं किया गया"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"इनपुट डिवाइस से कनेक्ट किया गया"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"इंटरनेट पहुंच के लिए डिवाइस से कनेक्ट"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"डिवाइस से स्थानीय इंटरनेट कनेक्शन शेयर किया जा रहा है"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"इंटरनेट पहुंच के लिए उपयोग करें"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"मानचित्र के लिए उपयोग करें"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"सिम ऐक्सेस के लिए उपयोग करें"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"मीडिया ऑडियो के लिए उपयोग करें"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"फ़ोन ऑडियो के लिए उपयोग करें"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"फ़ाइल स्थानांतरण के लिए उपयोग करें"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"इनपुट के लिए उपयोग करें"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"युग्म बनाएं"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"युग्मित करें"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"अभी नहीं"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"कनेक्ट रहने पर, पेयरिंग आपको अपने संपर्कों और कॉल इतिहास की एक्सेस प्रदान करता है."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> के साथ युग्मित नहीं हो सका."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"गलत पिन या पासकी के कारण <xliff:g id="DEVICE_NAME">%1$s</xliff:g> के साथ युग्मित नहीं हो सका."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> से संचार नहीं कर सकता."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> द्वारा युग्मन अस्वीकृत किया गया."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"वाई-फ़ाई बंद है."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"वाई-फ़ाई डिस्कनेक्ट है."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"वाई-फ़ाई का एक बार है."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"वाई-फ़ाई की दो पट्टी मिल रही हैं."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"वाई-फ़ाई की एक पट्टी मिल रही है."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"पूरे वाई-फ़ाई सिग्नल मिल रहे हैं."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"निकाले गए ऐप्स"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"ऐप्स और उपयोगकर्ताओं को निकालें"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB टेदरिंग"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"पोर्टेबल हॉटस्पॉट"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ब्लूटूथ टेदरिंग"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"टेदरिंग"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"टेदरिंग और पोर्टेबल हॉटस्पॉट"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"कार्य प्रोफ़ाइल"</string>
+ <string name="user_guest" msgid="8475274842845401871">"अतिथि"</string>
+ <string name="unknown" msgid="1592123443519355854">"अज्ञात"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"उपयोगकर्ता: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"कुछ डिफ़ॉल्ट सेट हैं"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"कोई डिफ़ॉल्ट सेट नहीं है"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"लेख से बोली सेटिंग"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"लेख को सुनें"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"बोली दर"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"बोलने की गति तय करें"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"भाषा"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"सिस्टम भाषा का उपयोग करें"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"भाषा नहीं चुनी गई है"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"बोले गए लेख के लिए भाषा-विशिष्ट ध्वनि सेट करता है"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"एक उदाहरण सुनें"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"बोली संश्लेषण का एक संक्षिप्त प्रदर्शन चलाएं"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"ध्वनि डेटा इंस्टॉल करें"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"बोली-संश्लेषण के लिए आवश्यक ध्वनि डेटा इंस्टॉल करें"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"यह बोली संश्लेषण इंजन पासवर्ड और क्रेडिट कार्ड नंबर जैसे निजी डेटा समेत आपके द्वारा बोले जाने वाले सभी लेख को एकत्र कर सकता है. यह <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> इंजन से आता है. इस बोली संश्लेषण इंजन के उपयोग को सक्षम करें?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"लेख-से-बोली आउटपुट के लिए इस भाषा को क्रियाशील नेटवर्क कनेक्शन की आवश्यकता है."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"यह बोली संश्लेषण का एक उदाहरण है"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"डिफ़ॉल्ट भाषा स्थिति"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> पूरी तरह से समर्थित है"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> के लिए नेटवर्क कनेक्शन की आवश्यकता है"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> समर्थित नहीं है"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"जांच की जा रही है..."</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g> के लिए सेटिंग"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"इंजन सेटिंग लॉन्च करें"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"पसंदीदा इंजन"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"सामान्य"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"अत्यधिक धीमा"</item>
+ <item msgid="4795095314303559268">"धीमा"</item>
+ <item msgid="8903157781070679765">"सामान्य"</item>
+ <item msgid="164347302621392996">"तेज़"</item>
+ <item msgid="5794028588101562009">"अधिक तेज़"</item>
+ <item msgid="7163942783888652942">"अत्यधिक तेज़"</item>
+ <item msgid="7831712693748700507">"त्वरित"</item>
+ <item msgid="5194774745031751806">"अत्यधिक तीव्र"</item>
+ <item msgid="9085102246155045744">"सबसे तेज़"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"प्रोफ़ाइल चुनें"</string>
+ <string name="category_personal" msgid="1299663247844969448">"व्यक्तिगत"</string>
+ <string name="category_work" msgid="8699184680584175622">"कार्यालय"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-hr/arrays.xml b/packages/SettingsLib/res/values-hr/arrays.xml
new file mode 100644
index 0000000..b7a0d51
--- /dev/null
+++ b/packages/SettingsLib/res/values-hr/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Skeniranje..."</item>
+ <item msgid="8513729475867537913">"Povezivanje…"</item>
+ <item msgid="515055375277271756">"Autentikacija…"</item>
+ <item msgid="1943354004029184381">"Dohvaćanje IP adrese…"</item>
+ <item msgid="4221763391123233270">"Povezan"</item>
+ <item msgid="624838831631122137">"Obustavljeno"</item>
+ <item msgid="7979680559596111948">"Isključivanje…"</item>
+ <item msgid="1634960474403853625">"Niste povezani"</item>
+ <item msgid="746097431216080650">"Neuspješno"</item>
+ <item msgid="6367044185730295334">"Blokirano"</item>
+ <item msgid="503942654197908005">"Privremeno izbjegavanje loše veze"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Skeniranje..."</item>
+ <item msgid="355508996603873860">"Povezivanje na mrežu <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="554971459996405634">"Autentikacija s mrežom <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="7928343808033020343">"Dohvaćanje IP adrese s mreže <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="8937994881315223448">"Povezano s mrežom <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Obustavljeno"</item>
+ <item msgid="7698638434317271902">"Isključivanje iz mreže <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="197508606402264311">"Niste povezani"</item>
+ <item msgid="8578370891960825148">"Neuspješno"</item>
+ <item msgid="5660739516542454527">"Blokirano"</item>
+ <item msgid="1805837518286731242">"Privremeno izbjegavanje loše veze"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
new file mode 100644
index 0000000..7516959
--- /dev/null
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Skeniranje mreža nije moguće"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Nema"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Spremljeno"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Onemogućeno"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Konfiguracija IP-a nije uspjela"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Povezivanje s Wi-Fi-jem nije uspjelo"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problem u autentifikaciji"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Nije u rasponu"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Pristup internetu nije otkriven. Nema automatskog ponovnog povezivanja."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Spremljeno: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Povezani putem pomoćnika za Wi-Fi"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Povezano putem %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Dostupno putem %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Povezano, bez interneta"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Niste povezani"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Isključivanje…"</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Povezivanje…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Povezan"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Uparivanje…"</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Povezano (bez telefona)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Povezano (bez medija)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Povezano (bez pristupa porukama)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Povezano (bez telefona ili medija)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Medijski zvuk"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Zvuk telefona"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Prijenos datoteke"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Ulazni uređaj"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Pristup internetu"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Dijeljenje kontakata"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Upotrijebi za dijeljenje kontakata"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Dijeljenje internetske veze"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Pristup porukama"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Pristup SIM-u"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Povezano s medijskim zvukom"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Povezano sa telefonskim zvukom"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Povezano s poslužiteljem za prijenos datoteka"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Povezano s kartom"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Povezano sa SAP-om"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Niste povezani s poslužiteljem za prijenos datoteka"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Povezano s ulaznim uređajem"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Povezano s uređajem za pristup internetu"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Dijeljenje lokalne internetske veze s uređajem"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Upotrijebi za pristup internetu"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Upotreba za kartu"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Upotrijebi za pristup SIM-u"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Koristi za medijski zvuk"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Koristi za telefonski zvuk"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Koristi za prijenos datoteke"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Upotrijebi za ulaz"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Upari"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"UPARI"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Odustani"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Uparivanje omogućuje pristup vašim kontaktima i povijesti poziva dok ste povezani."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Uparivanje s uređajem <xliff:g id="DEVICE_NAME">%1$s</xliff:g> nije bilo moguće."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Uparivanje s uređajem <xliff:g id="DEVICE_NAME">%1$s</xliff:g> nije bilo moguće zbog netočnog PIN-a ili zaporke."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Komunikacija s uređajem <xliff:g id="DEVICE_NAME">%1$s</xliff:g> nije moguća."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Uparivanje odbio uređaj <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi je isključen."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi je isključen."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi signal ima jedan stupac."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wi-Fi signal ima dva stupca."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wi-Fi signal ima tri stupca."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Wi-Fi signal je pun."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Uklonjene aplikacije"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Uklonjene aplikacije i korisnici"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB dijeljenje veze"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Prijen. pristupna točka"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Dijeljenje Bluetoothom veze"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Dijeljenje veze"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Dijeljenje veze i žarišna točka"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Radni profil"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Gost"</string>
+ <string name="unknown" msgid="1592123443519355854">"Nepoznato"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Korisnik: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Postavljene su neke zadane postavke"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Nema zadanih postavki"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Postavke za tekst u govor"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Pretvaranje teksta u govor"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Brzina govora"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Brzina kojom se izgovara tekst"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Jezik"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"upotrijebi jezik sustava"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Jezik nije odabran"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Postavlja jezik govora"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Poslušajte primjer"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Poslušajte primjer sinteze zvuka"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Instaliraj glasovne podatke"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Instaliraj glasovne podatke potrebne za sintezu govora"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Alat za sintezu govora može prikupljati izgovoreni sadržaj, uključujući osobne podatke kao što su zaporke i brojevi kreditnih kartica. To omogućuje alat <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Želite li omogućiti alat za sintezu govora?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Da bi se dobili izlazni podaci pretvaranja teksta u govor za taj jezik, potrebna je aktivna veza s mrežom."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Ovo je primjer sinteze govora"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Status zadanog jezika"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> je u potpunosti podržan"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> zahtijeva mrežnu vezu"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> nije podržan"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Provjeravanje…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Postavke za <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Postavke pokretanja alata"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Željeni alat"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Opće"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Vrlo sporo"</item>
+ <item msgid="4795095314303559268">"Sporo"</item>
+ <item msgid="8903157781070679765">"Uobičajeno"</item>
+ <item msgid="164347302621392996">"Brzo"</item>
+ <item msgid="5794028588101562009">"Brže"</item>
+ <item msgid="7163942783888652942">"Vrlo brzo"</item>
+ <item msgid="7831712693748700507">"Ubrzano"</item>
+ <item msgid="5194774745031751806">"Vrlo ubrzano"</item>
+ <item msgid="9085102246155045744">"Najbrže"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Odabir profila"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Osobno"</string>
+ <string name="category_work" msgid="8699184680584175622">"Posao"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-hu/arrays.xml b/packages/SettingsLib/res/values-hu/arrays.xml
new file mode 100644
index 0000000..681489e
--- /dev/null
+++ b/packages/SettingsLib/res/values-hu/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Keresés..."</item>
+ <item msgid="8513729475867537913">"Kapcsolódás…"</item>
+ <item msgid="515055375277271756">"Hitelesítés..."</item>
+ <item msgid="1943354004029184381">"IP-cím lekérése..."</item>
+ <item msgid="4221763391123233270">"Csatlakozva"</item>
+ <item msgid="624838831631122137">"Felfüggesztve"</item>
+ <item msgid="7979680559596111948">"Kapcsolat bontása..."</item>
+ <item msgid="1634960474403853625">"Nincs kapcsolat"</item>
+ <item msgid="746097431216080650">"Sikertelen"</item>
+ <item msgid="6367044185730295334">"Letiltva"</item>
+ <item msgid="503942654197908005">"A rossz minőségű kapcsolatok átmeneti elkerülése"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Keresés..."</item>
+ <item msgid="355508996603873860">"Csatlakozás a(z) <xliff:g id="NETWORK_NAME">%1$s</xliff:g> hálózathoz..."</item>
+ <item msgid="554971459996405634">"Hitelesítés a(z) <xliff:g id="NETWORK_NAME">%1$s</xliff:g> hálózaton..."</item>
+ <item msgid="7928343808033020343">"IP-cím lekérése a(z) <xliff:g id="NETWORK_NAME">%1$s</xliff:g> hálózattól..."</item>
+ <item msgid="8937994881315223448">"Kapcsolódva a(z) <xliff:g id="NETWORK_NAME">%1$s</xliff:g> hálózathoz"</item>
+ <item msgid="1330262655415760617">"Felfüggesztve"</item>
+ <item msgid="7698638434317271902">"Kapcsolat bontása <xliff:g id="NETWORK_NAME">%1$s</xliff:g> hálózattal..."</item>
+ <item msgid="197508606402264311">"Nincs kapcsolat"</item>
+ <item msgid="8578370891960825148">"Sikertelen"</item>
+ <item msgid="5660739516542454527">"Letiltva"</item>
+ <item msgid="1805837518286731242">"A rossz minőségű kapcsolatok átmeneti elkerülése"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
new file mode 100644
index 0000000..77c944d
--- /dev/null
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nem lehet beolvasni a hálózatokat"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Nincs"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Mentve"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Letiltva"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-konfigurációs hiba"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi-kapcsolati hiba"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Azonosítási probléma"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Hatókörön kívül"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Nincs érzékelhető internet-hozzáférés, ezért nem kapcsolódik újra automatikusan."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Mentette: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Csatlakozva Wi‑Fi-segéddel"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Csatlakozva a következőn keresztül: %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Elérhető a következőn keresztül: %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Csatlakozva, nincs internetelérés"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Szétkapcsolva"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Szétkapcsolás..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Csatlakozás…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Csatlakozva"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Párosítás..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Csatlakoztatva (nincs telefon)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Csatlakoztatva (nincs hordozó)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Csatlakoztatva (nincs üzenet-hozzáférés)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Csatlakoztatva (nincs telefon vagy hordozó)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Média audió"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Telefon hangja"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Fájlátvitel"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Beviteli eszköz"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Internetelérés"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Névjegyek megosztása"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Használja a névjegyek megosztására"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Internetkapcsolat megosztása"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Üzenet-hozzáférés"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM-elérés"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Csatlakoztatva az eszköz hangjához"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Csatlakoztatva a telefon hangjához"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Csatlakozva a fájlküldő szerverhez"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Csatlakoztatva a térképhez"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Csatlakozva az SAP-hoz"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Nincs csatlakozva a fájlküldő szerverhez"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Beviteli eszköz csatlakoztatva"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Eszközhöz csatlakozik az interneteléréshez"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Helyi internetkapcsolat megosztva az eszközzel"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Használat internetelérésre"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Használat a térképhez"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Használat SIM-elérésre"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Felhasználás az eszköz hangjához"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Felhasználás a telefon hangjához"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Felhasználás fájlátvitelre"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Használat beviteli eszközként"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Párosítás"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"PÁROSÍTÁS"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Mégse"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"A párosítás hozzáférést biztosít a névjegyekhez és híváselőzményekhez összekapcsolt állapotban."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Nem lehet párosítani a(z) <xliff:g id="DEVICE_NAME">%1$s</xliff:g> eszközzel."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"A párosítás sikertelen volt a(z) <xliff:g id="DEVICE_NAME">%1$s</xliff:g> eszközzel hibás PIN-kód vagy jelszó miatt."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Nem lehet kommunikálni a(z) <xliff:g id="DEVICE_NAME">%1$s</xliff:g> eszközzel."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"A(z) <xliff:g id="DEVICE_NAME">%1$s</xliff:g> eszköz elutasította a párosítást."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi kikapcsolva."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Nincs Wi-Fi-kapcsolat."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi-jel: egy sáv."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wi-Fi-jel: két sáv."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wi-Fi-jel: három sáv."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Wi-Fi-jel: teljes."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Eltávolított alkalmazások"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Eltávolított alkalmazások és felhasználók"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB-megosztás"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Hordozható hotspot"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth megosztása"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Megosztás"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Megosztás és hotspot"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Munkaprofil"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Vendég"</string>
+ <string name="unknown" msgid="1592123443519355854">"Ismeretlen"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Felhasználó: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Van néhány alapbeállítás"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Nincs alapbeállítás"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Szöveg-beszéd átalakítás beállításai"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Szövegfelolvasás"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Beszéd sebessége"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"A szöveg kimondásának sebessége"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Nyelv"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"A rendszer nyelvének használata"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Nincs nyelv kiválasztva"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Beállítja a beszélt szöveg nyelvspecifikus hangját"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Példa meghallgatása"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Beszédszintézis rövid bemutatása"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Hangadatok telepítése"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"A beszédszintetizáláshoz szükséges hangadatok telepítése"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Ez a beviteli módszer alkalmas lehet az elhangzott szövegek -- köztük az olyan személyes adatok, mint a jelszavak és a hitelkártyaszámok -- összegyűjtésére. A(z) <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> motor kapcsolta be. Engedélyezi ennek a beszédszintetizáló motornak a használatát?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Ez a nyelv működő hálózati kapcsolatot igényel a szövegfelolvasáshoz."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Ez egy példa a beszédszintézisre"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Alapértelmezett nyelv állapota"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"A(z) <xliff:g id="LOCALE">%1$s</xliff:g> nyelv teljes támogatást élvez"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"A(z) <xliff:g id="LOCALE">%1$s</xliff:g> nyelv használatához hálózati kapcsolatra van szükség"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"A(z) <xliff:g id="LOCALE">%1$s</xliff:g> nyelvet nem támogatja a rendszer"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Ellenőrzés…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"A(z) <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g> beállításai"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Keresőmotor beállításainak indítása"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Preferált motor"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Általános"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Nagyon lassú"</item>
+ <item msgid="4795095314303559268">"Lassú"</item>
+ <item msgid="8903157781070679765">"Normál"</item>
+ <item msgid="164347302621392996">"Gyors"</item>
+ <item msgid="5794028588101562009">"Gyorsabb"</item>
+ <item msgid="7163942783888652942">"Nagyon gyors"</item>
+ <item msgid="7831712693748700507">"Igen gyors"</item>
+ <item msgid="5194774745031751806">"Rendkívül gyors"</item>
+ <item msgid="9085102246155045744">"Leggyorsabb"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Profil kiválasztása"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Személyes"</string>
+ <string name="category_work" msgid="8699184680584175622">"Munkahelyi"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-hy-rAM/arrays.xml b/packages/SettingsLib/res/values-hy-rAM/arrays.xml
new file mode 100644
index 0000000..483e9d3
--- /dev/null
+++ b/packages/SettingsLib/res/values-hy-rAM/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Սկանավորում…"</item>
+ <item msgid="8513729475867537913">"Միանում է..."</item>
+ <item msgid="515055375277271756">"Նույնականացում…"</item>
+ <item msgid="1943354004029184381">"IP հասցեն գտնվում է...."</item>
+ <item msgid="4221763391123233270">"Միացված է"</item>
+ <item msgid="624838831631122137">"Կասեցված է"</item>
+ <item msgid="7979680559596111948">"Անջատվում է…"</item>
+ <item msgid="1634960474403853625">"Անջատված է"</item>
+ <item msgid="746097431216080650">"Անհաջող"</item>
+ <item msgid="6367044185730295334">"Արգելափակված"</item>
+ <item msgid="503942654197908005">"Ժամանակավորապես խուսափելով թույլ կապից"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Սկանավորում...."</item>
+ <item msgid="355508996603873860">"Միանում է <xliff:g id="NETWORK_NAME">%1$s</xliff:g>-ին…"</item>
+ <item msgid="554971459996405634">"Նույնականացում <xliff:g id="NETWORK_NAME">%1$s</xliff:g>-ի հետ…"</item>
+ <item msgid="7928343808033020343">"IP հասցեի ստացում <xliff:g id="NETWORK_NAME">%1$s</xliff:g>-ից…"</item>
+ <item msgid="8937994881315223448">"Միացված է <xliff:g id="NETWORK_NAME">%1$s</xliff:g>-ին"</item>
+ <item msgid="1330262655415760617">"Անջատված"</item>
+ <item msgid="7698638434317271902">"Անջատվում է <xliff:g id="NETWORK_NAME">%1$s</xliff:g>-ից…"</item>
+ <item msgid="197508606402264311">"Անջատված"</item>
+ <item msgid="8578370891960825148">"Անհաջող"</item>
+ <item msgid="5660739516542454527">"Արգելափակված"</item>
+ <item msgid="1805837518286731242">"Վատ ցանցից ժամանակավոր խուսափում"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-hy-rAM/strings.xml b/packages/SettingsLib/res/values-hy-rAM/strings.xml
new file mode 100644
index 0000000..ae8bab4
--- /dev/null
+++ b/packages/SettingsLib/res/values-hy-rAM/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Հնարավոր չէ սկանավորել ցանցերը"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Ոչ մեկը"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Պահված է"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Անջատված"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP կարգավորման ձախողում"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi կապի ձախողում"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Նույնականացման խնդիր"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Ընդգրկույթից դուրս է"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Ինտերնետի հասանելիություն չկա. ավտոմատ կերպով կրկին չի միանա:"</string>
+ <string name="saved_network" msgid="4352716707126620811">"Պահել է հետևյալ օգտվողը՝ <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Կապակցված է Wi‑Fi Օգնականի միջոցով"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Կապակցված է %1$s-ի միջոցով"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Հասանելի է %1$s-ի միջոցով"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Կապակցված է առանց համացանցի"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Անջատված է"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Անջատվում է..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Միանում է..."</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Միացված է"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Զուգավորում..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Միացված (առանց հեռախոսի)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Միացված է (առանց մեդիա)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Միացված է (հաղորդագրությանը մուտք չկա)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Միացված է (առանց հեռախոսի և մեդիայի)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Մեդիա աուդիո"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Հեռախոսի աուդիո"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Ֆայլերի փոխանցում"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Ներմուծման սարք"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Ինտերնետի մուտք"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Կոնտակտի համօգտագործում"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Օգտագործել կոնտակտի համօգտագործման համար"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Ինտերնետ կապի տարածում"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Մուտք հաղորդագրություն"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM քարտի հասանելիություն"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Միացված է մեդիա աուդիոյին"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Միացված է հեռախոսի ձայնային տվյալներին"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Միացված է ֆայլերի փոխանցման սերվերին"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Միացված է քարտեզին"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Կապակցված է SAP-ին"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Ֆայլերը փոխանցող սերվերի հետ կապ չկա"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Միացված է մուտքային սարքին"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Կապակցված է սարքին` ինտերնետ մուտք գործելու համար"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Տեղային ինտերնետ կապի տարածում սարքի հետ"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Օգտագործել ինտերնետ մուտք գործելու համար"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Օգտագործել քարտեզի համար"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"SIM քարտի օգտագործում"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Օգտագործել մեդիա աուդիոյի համար"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Օգտագործել հեռախոսի աուդիոյի համար"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Օգտագործել ֆայլի փոխանցման համար"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Օգտագործել ներմուծման համար"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Զուգավորել"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"Զուգավորել"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Չեղարկել"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Զուգավորում է մուտքի թույլտվությունը դեպի ձեր կոնտակտները և զանգերի պատմությունը, երբ միացված է:"</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Չհաջողվեց զուգավորել <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-ի հետ:"</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Հնարավոր չեղավ զուգավորվել <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-ի հետ սխալ PIN-ի կամ անցաբառի պատճառով:."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Հնարավոր չէ կապ հաստատել <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-ի հետ:"</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Զուգավորումը մերժվեց <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-ի կողմից:"</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi-ն անջատված է:"</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi-ը կապակցված չէ:"</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi-ի ուժգնությունը՝ մեկ գիծ:"</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wi-Fi-ի ուժգնությունը՝ երկու գիծ:"</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wi-Fi-ի ուժգնությունը՝ երեք գիծ:"</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Wi-Fi-ի ազդանշանը ուժեղ է:"</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Հեռացված ծրագրեր"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Հեռացված հավելվածներն ու օգտվողները"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB միացում"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Դյուրակիր թեժ կետ"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth-ը կապվում է"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Միացում"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Միացում և շարժական թեժ կետ"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Աշխատանքային պրոֆիլ"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Հյուր"</string>
+ <string name="unknown" msgid="1592123443519355854">"Անհայտ"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Օգտվող՝ <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Որոշ կանխադրված կարգավորումներ կան"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Կանխադրված կարգավորումներ չկան"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Տեքստից-խոսք կարգավորումներ"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Գրվածքից խոսք ելք"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Խոսքի գնահատական"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Տեքստի արտասանման արագությունը"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Լեզու"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Օգտագործել համակարգի լեզուն"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Լեզուն ընտրված չէ"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Կարգավորում է լեզվին բնորոշ ձայնը արտասանվող տեքստի համար"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Լսել օրինակը"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Կարճ հնչեցնել խոսքի սինթեզը"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Տեղադրել ձայնային տվյալները"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Տեղադրել անհրաժեշտ ձայնային տվյալները` խոսքը սինթեզելու համար"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Այս խոսքային սինթեզի գործիքը կարող է հավաքագրել այն ամենը, ինչ արտասանված է, այդ թվում` անձնական տվյալներ, ինչպիսիք են գաղտնաբառն ու բանկային քարտի համարները: Սկզբնաբյուրը <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> շարժիչն է: Միացնե՞լ խոսքի սինթեզի շարժիչի օգտագործումը:"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Այս լեզուն պահանջում է աշխատող ցանցային կապ գրվածքից խոսք ելքի համար:"</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Սա խոսքային սինթեզի մի նմուշ է:"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Լռելյայն լեզվի կարգավիճակը"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g>-ը լիովին աջակցվում է"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g>-ը պահանջում է ցանցային կապ"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g>-ը չի աջակցվում"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Ստուգվում է…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Կարգավորումներ <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>-ի համար"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Գործարկման շարժիչի կարգավորումներ"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Նախընտրած շարժիչը"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Ընդհանուր"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Շատ դանդաղ"</item>
+ <item msgid="4795095314303559268">"Դանդաղ"</item>
+ <item msgid="8903157781070679765">"Սովորական"</item>
+ <item msgid="164347302621392996">"Արագ"</item>
+ <item msgid="5794028588101562009">"Ավելի արագ"</item>
+ <item msgid="7163942783888652942">"Շատ արագ"</item>
+ <item msgid="7831712693748700507">"Սրընթաց"</item>
+ <item msgid="5194774745031751806">"Չափազանց արագ"</item>
+ <item msgid="9085102246155045744">"Ամենաարագ"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Ընտրել պրոֆիլ"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Անձնական"</string>
+ <string name="category_work" msgid="8699184680584175622">"Աշխատանքային"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-in/arrays.xml b/packages/SettingsLib/res/values-in/arrays.xml
new file mode 100644
index 0000000..c2488df
--- /dev/null
+++ b/packages/SettingsLib/res/values-in/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Memindai..."</item>
+ <item msgid="8513729475867537913">"Menyambung…"</item>
+ <item msgid="515055375277271756">"Mengautentikasi…"</item>
+ <item msgid="1943354004029184381">"Mendapatkan alamat IP…"</item>
+ <item msgid="4221763391123233270">"Terhubung"</item>
+ <item msgid="624838831631122137">"Ditangguhkan"</item>
+ <item msgid="7979680559596111948">"Memutus sambungan..."</item>
+ <item msgid="1634960474403853625">"Sambungan terputus"</item>
+ <item msgid="746097431216080650">"Gagal"</item>
+ <item msgid="6367044185730295334">"Dicekal"</item>
+ <item msgid="503942654197908005">"Menghindari sambungan buruk untuk sementara"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Memindai..."</item>
+ <item msgid="355508996603873860">"Menyambung ke <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="554971459996405634">"Mengautentikasi dengan <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="7928343808033020343">"Mendapatkan alamat IP dari <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="8937994881315223448">"Tersambung ke <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Ditangguhkan"</item>
+ <item msgid="7698638434317271902">"Diputus dari <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="197508606402264311">"Sambungan terputus"</item>
+ <item msgid="8578370891960825148">"Gagal"</item>
+ <item msgid="5660739516542454527">"Dicekal"</item>
+ <item msgid="1805837518286731242">"Menghindari sambungan buruk untuk sementara"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
new file mode 100644
index 0000000..3033afa
--- /dev/null
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Tidak dapat memindai jaringan"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Tidak Ada"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Disimpan"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Nonaktif"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Kegagalan Konfigurasi IP"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Kegagalan Sambungan Wi-Fi"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Masalah autentikasi"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Tidak dalam jangkauan"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Akses Internet Tidak Terdeteksi, tidak akan menyambung ulang secara otomatis."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Disimpan oleh <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Terhubung melalui Asisten Wi-Fi"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Terhubung melalui %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Tersedia melalui %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Tersambung, tidak ada internet"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Sambungan terputus"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Memutus sambungan..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Menyambung…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Terhubung"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Menyandingkan..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Terhubung (bukan telepon)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Terhubung (kecuali media)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Tersambung (tidak ada akses pesan)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Terhubung (bukan telepon atau media)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Audio media"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Audio telepon"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Transfer file"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Perangkat masukan"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Akses Internet"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Berbagi kontak"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Gunakan untuk berbagi kontak"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Berbagi sambungan internet"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Akses Pesan"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Akses SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Tersambung ke media audio"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Tersambung ke audio ponsel"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Sambungkan ke server transfer file"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Tersambung ke peta"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Terhubung ke SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Tidak tersambung kepada server transfer file"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Terhubung ke perangkat masukan"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Tersambung ke perangkat untuk akses Internet"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Berbagi sambungan Internet lokal dengan perangkat"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Digunakan untuk akses internet"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Gunakan untuk peta"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Gunakan untuk akses SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Gunakan untuk audio media"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Gunakan untuk audio ponsel"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Gunakan untuk transfer file"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Gunakan untuk masukan"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Sandingkan"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"SANDINGKAN"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Batal"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Penyandingan memberi akses ke kontak dan riwayat panggilan saat tersambung"</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Tidak dapat menyandingkan dengan <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Tidak dapat menyandingkan dengan <xliff:g id="DEVICE_NAME">%1$s</xliff:g> karena PIN atau kode sandi salah."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Tidak dapat berkomunikasi dengan <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Penyandingan ditolak oleh <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi tidak aktif."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi tidak tersambung."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi satu baris."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wi-Fi dua baris"</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wi-Fi tiga baris."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Sinyal Wi-Fi penuh."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"OS Android"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Aplikasi dihapus"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Aplikasi dan pengguna yang dihapus"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"Penambatan USB"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Hotspot portabel"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Penambatan bluetooth"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Menambatkan"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Menambatkan & hotspot portabel"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Profil kerja"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Tamu"</string>
+ <string name="unknown" msgid="1592123443519355854">"Tidak diketahui"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Pengguna: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Beberapa setelan default"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Tidak ada setelan default"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Setelan text-to-speech"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Keluaran text-to-speech"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Laju bicara"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Kecepatan teks diucapkan"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Bahasa"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Gunakan bahasa sistem"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Bahasa tidak dipilih"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Menyetel suara spesifik bahasa untuk teks lisan"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Dengarkan contoh"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Putar demonstrasi singkat dari sintesis suara"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Pasang data suara"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Pasang data suara yang dibutuhkan untuk sintesis suara"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Mesin sintesis suara ini mungkin dapat mengumpulkan semua teks yang akan diucapkan, termasuk di antaranya data pribadi seperti sandi dan nomor kartu kredit. Berasal dari <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> aplikasi. Gunakan metode masukan ini?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Bahasa ini perlu sambungan jaringan yang bekerja untuk keluaran text-to-speech."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Ini adalah contoh sintesis suara"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Status bahasa default"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> didukung sepenuhnya"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> membutuhkan sambungan jaringan"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> tidak didukung"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Memeriksa…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Setelan untuk <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Luncurkan setelan mesin"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Mesin yang dipilih"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Umum"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Sangat lambat"</item>
+ <item msgid="4795095314303559268">"Lambat"</item>
+ <item msgid="8903157781070679765">"Biasa"</item>
+ <item msgid="164347302621392996">"Cepat"</item>
+ <item msgid="5794028588101562009">"Lebih cepat"</item>
+ <item msgid="7163942783888652942">"Sangat cepat"</item>
+ <item msgid="7831712693748700507">"Cepat sekali"</item>
+ <item msgid="5194774745031751806">"Sangat cepat sekali"</item>
+ <item msgid="9085102246155045744">"Tercepat"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Pilih Profil"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Pribadi"</string>
+ <string name="category_work" msgid="8699184680584175622">"Kantor"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-is-rIS/arrays.xml b/packages/SettingsLib/res/values-is-rIS/arrays.xml
new file mode 100644
index 0000000..540956f
--- /dev/null
+++ b/packages/SettingsLib/res/values-is-rIS/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Leitar…"</item>
+ <item msgid="8513729475867537913">"Tengist…"</item>
+ <item msgid="515055375277271756">"Auðkennir…"</item>
+ <item msgid="1943354004029184381">"Sækir IP-tölu…"</item>
+ <item msgid="4221763391123233270">"Tengt"</item>
+ <item msgid="624838831631122137">"Lokað"</item>
+ <item msgid="7979680559596111948">"Aftengist…"</item>
+ <item msgid="1634960474403853625">"Aftengt"</item>
+ <item msgid="746097431216080650">"Mistókst"</item>
+ <item msgid="6367044185730295334">"Læst"</item>
+ <item msgid="503942654197908005">"Forðast lélega tengingu tímabundið"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Leitar…"</item>
+ <item msgid="355508996603873860">"Tengist <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="554971459996405634">"Auðkennir á <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="7928343808033020343">"Sækir IP-tölu frá <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="8937994881315223448">"Tengt við <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Lokað"</item>
+ <item msgid="7698638434317271902">"Aftengist <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="197508606402264311">"Aftengt"</item>
+ <item msgid="8578370891960825148">"Mistókst"</item>
+ <item msgid="5660739516542454527">"Læst"</item>
+ <item msgid="1805837518286731242">"Forðast lélega tengingu tímabundið"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-is-rIS/strings.xml b/packages/SettingsLib/res/values-is-rIS/strings.xml
new file mode 100644
index 0000000..21f80f4
--- /dev/null
+++ b/packages/SettingsLib/res/values-is-rIS/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Ekki er hægt að leita að netum"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Ekkert"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Vistað"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Óvirkt"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-stillingarvilla"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi-tengingarvilla"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Vandamál við auðkenningu"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Ekkert samband"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Enginn netaðgangur fannst; endurtengist ekki sjálfkrafa."</string>
+ <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> vistaði"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Tengt í gegnum Wi-Fi aðstoð"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Tengt í gegnum %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Í boði í gegnum %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Tengt, enginn internetaðgangur"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Aftengt"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Aftengist…"</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Tengist…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Tengt"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Parar…"</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Tengt (ekki sími)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Tengt (ekki efnisspilun)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Tengt (enginn skilaboðaaðgangur)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Tengt (ekki sími eða efnisspilun)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Hljóð efnis"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Hljóð síma"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Skráaflutningur"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Inntakstæki"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Internetaðgangur"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Deiling tengiliða"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Nota til að deila tengiliðum"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Deiling nettengingar"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Skilaboðaaðgangur"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Aðgangur að SIM-korti"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Tengt við hljóðspilun efnis"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Tengt við hljóð símans"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Tengt við skráaflutningsþjón"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Tengt við kort"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Tengt við SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Ekki tengt við skráaflutningsþjón."</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Tengt við inntakstæki"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Tengt við tæki til að fá netaðgang"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Internettengingu deilt með tæki"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Nota fyrir netaðgang"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Nota fyrir kort"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Nota fyrir aðgang að SIM-korti"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Nota fyrir hljóð efnisspilunar"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Nota fyrir hljóð símans"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Nota við skráaflutning"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Nota fyrir inntak"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Para"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"PARA"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Hætta við"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Pörun veitir aðgang að tengiliðunum þínum og símtalaferli þegar tengingu hefur verið komið á."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Ekki var hægt að para við <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Ekki tókst að para við <xliff:g id="DEVICE_NAME">%1$s</xliff:g> þar sem PIN-númer eða aðgangslykill er rangur."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Ekki er hægt að eiga samskipti við <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> hafnaði pörun."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Slökkt á Wi-Fi."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi ótengt."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi: Eitt strik."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wi-Fi: Tvö strik."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wi-Fi: Þrjú strik."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Fullur Wi-Fi sendistyrkur."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android stýrikerfið"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Fjarlægð forrit"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Fjarlægð forrit og notendur"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB-tjóðrun"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Heitur reitur"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth-tjóðrun"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Tjóðrun"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Tjóðrun og heitur reitur"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Vinnusnið"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Gestur"</string>
+ <string name="unknown" msgid="1592123443519355854">"Óþekkt"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Notandi: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Einhver sjálfgildi valin"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Engin sjálfgildi valin"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Stillingar upplesturs"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Úttak upplesturs"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Talhraði"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Hraði talaðs texta"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Tungumál"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Nota tungumál kerfis"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Tungumál ekki valið"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Velur rödd hvers tungumáls fyrir lesinn texta"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Hlusta á dæmi"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Spila stutta kynningu á talgervli"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Setja upp raddgögn"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Setja upp nauðsynleg raddgögn fyrir talgervil"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Þessi talgervilsvél getur hugsanlega skráð allan texta sem lesin er upp, þ. á m. persónuupplýsingar á borð við aðgangsorð og kreditkortanúmer. Hún kemur frá vélinni <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Viltu gera þessa talgervilsvél virka?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Þetta tungumál þarf nettengingu til að hægt sé að nota talgervil."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Þetta er dæmi um talgervil"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Staða sjálfgefins tungumáls"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> er með fullan stuðning"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> krefst nettengingar"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> er ekki studd"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Athugar..."</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Stillingar fyrir <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Stillingar vélarræsingar"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Valin vél"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Almennt"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Mjög hægt"</item>
+ <item msgid="4795095314303559268">"Hægt"</item>
+ <item msgid="8903157781070679765">"Venjulegt"</item>
+ <item msgid="164347302621392996">"Hratt"</item>
+ <item msgid="5794028588101562009">"Hraðar"</item>
+ <item msgid="7163942783888652942">"Mjög hratt"</item>
+ <item msgid="7831712693748700507">"Leifturhratt"</item>
+ <item msgid="5194774745031751806">"Næsthraðast"</item>
+ <item msgid="9085102246155045744">"Hraðast"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Veldu snið"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Persónulegt"</string>
+ <string name="category_work" msgid="8699184680584175622">"Vinna"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-it/arrays.xml b/packages/SettingsLib/res/values-it/arrays.xml
new file mode 100644
index 0000000..719adbc
--- /dev/null
+++ b/packages/SettingsLib/res/values-it/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Ricerca..."</item>
+ <item msgid="8513729475867537913">"Connessione..."</item>
+ <item msgid="515055375277271756">"Autenticazione..."</item>
+ <item msgid="1943354004029184381">"Acquisizione indirizzo IP..."</item>
+ <item msgid="4221763391123233270">"Connessa"</item>
+ <item msgid="624838831631122137">"Sospesa"</item>
+ <item msgid="7979680559596111948">"Disconnessione..."</item>
+ <item msgid="1634960474403853625">"Disconnessa"</item>
+ <item msgid="746097431216080650">"Operazione non riuscita"</item>
+ <item msgid="6367044185730295334">"Bloccato"</item>
+ <item msgid="503942654197908005">"Al momento vengono evitate connessioni deboli"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Ricerca..."</item>
+ <item msgid="355508996603873860">"Connessione a <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="554971459996405634">"Autenticazione con <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="7928343808033020343">"Acquisizione indirizzo IP da <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="8937994881315223448">"Connessa a <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Sospesa"</item>
+ <item msgid="7698638434317271902">"Disconnessione da <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="197508606402264311">"Disconnessa"</item>
+ <item msgid="8578370891960825148">"Operazione non riuscita"</item>
+ <item msgid="5660739516542454527">"Bloccato"</item>
+ <item msgid="1805837518286731242">"Al momento vengono evitate connessioni deboli"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
new file mode 100644
index 0000000..c2ad84e
--- /dev/null
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Impossibile cercare reti"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Nessuna"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Salvata"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Disattivata"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Errore configurazione IP"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Errore connessione Wi-Fi"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problema di autenticazione"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Fuori portata"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Nessun accesso a Internet rilevato, non verrà eseguita la riconnessione automatica."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Salvata da <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Connesso tramite assistente Wi‑Fi"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Collegato tramite %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Disponibile tramite %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Connesso senza Internet"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Disconnesso"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Disconnessione..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Connessione..."</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Connesso"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Accoppiamento..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Collegato (telefono escluso)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Collegato (contenuti multimed. esclusi)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Connesso (nessun accesso ai messaggi)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Collegato (telef. o conten. mult. esclusi)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Audio multimediale"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Audio telefono"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Trasferimento file"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Dispositivo di input"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Accesso Internet"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Condivisione contatti"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Usa per condivisione contatti"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Condivisione connessione Internet"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Accesso ai messaggi"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Accesso alla SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Collegato ad audio media"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Collegato ad audio telefono"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Collegato al server di trasferimento file"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Connesso alla mappa"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Collegato al SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Non collegato al server di trasferimento file"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Connesso a dispositivo di input"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Connesso a dispositivo per accesso Internet"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Connessione Internet locale condivisa con dispositivo"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Usa per accesso Internet"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Utilizza per la mappa"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Utilizza per accesso SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Usa per audio media"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Usa per audio telefono"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Usa per trasferimento file"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Utilizza per l\'input"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Accoppia"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ACCOPPIA"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Annulla"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"L\'accoppiamento consente l\'accesso ai tuoi contatti e alla cronologia chiamate quando i dispositivi sono connessi."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Impossibile eseguire l\'accoppiamento con <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Impossibile eseguire l\'accoppiamento con <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. La passkey o il PIN è errato."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Impossibile comunicare con <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Accoppiamento rifiutato da <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi non attivo."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Rete Wi-Fi scollegata."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi: una barra."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wi-Fi: due barre."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wi-Fi: tre barre."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Segnale Wi-Fi completo."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Sistema operativo Android"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Applicazioni rimosse"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"App e utenti rimossi"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"Tethering USB"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Hotspot portatile"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Tethering Bluetooth"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Tethering"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Tethering/hotspot portatile"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Profilo di lavoro"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Ospite"</string>
+ <string name="unknown" msgid="1592123443519355854">"Sconosciuta"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Utente: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Alcune opzioni predefinite impostate"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Nessuna applicazione predefinita impostata"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Impostazioni di sintesi vocale"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Output sintesi vocale"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Velocità voce"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Velocità di pronuncia del testo"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Lingua"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Usa lingua di sistema"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Lingua non selezionata"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Imposta la voce specifica della lingua per il testo parlato"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Ascolta un esempio"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Riproduci una breve dimostrazione della sintesi vocale"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Installa dati vocali"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Installa i dati vocali richiesti per la sintesi vocale"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Questo motore di sintesi vocale potrebbe essere in grado di raccogliere tutto il testo pronunciato, inclusi dati personali come password e numeri di carte di credito. Deriva dal motore <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Abilitare l\'uso di questo motore di sintesi vocale?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Questa lingua richiede una connessione di rete funzionante per l\'output di sintesi vocale."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Questo è un esempio di sintesi vocale"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Stato della lingua predefinita"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> completamente supportato"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> richiede connessione di rete"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> non supportato"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Verifica…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Impostazioni per <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Avvia impostazioni del motore"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Motore preferito"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Generali"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Molto lenta"</item>
+ <item msgid="4795095314303559268">"Lenta"</item>
+ <item msgid="8903157781070679765">"Normale"</item>
+ <item msgid="164347302621392996">"Veloce"</item>
+ <item msgid="5794028588101562009">"Più veloce"</item>
+ <item msgid="7163942783888652942">"Molto veloce"</item>
+ <item msgid="7831712693748700507">"Rapida"</item>
+ <item msgid="5194774745031751806">"Molto rapida"</item>
+ <item msgid="9085102246155045744">"Massima velocità"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Scegli profilo"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Personali"</string>
+ <string name="category_work" msgid="8699184680584175622">"Lavoro"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-iw/arrays.xml b/packages/SettingsLib/res/values-iw/arrays.xml
new file mode 100644
index 0000000..d5f3d9d
--- /dev/null
+++ b/packages/SettingsLib/res/values-iw/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"סורק..."</item>
+ <item msgid="8513729475867537913">"מתחבר ..."</item>
+ <item msgid="515055375277271756">"מאמת…"</item>
+ <item msgid="1943354004029184381">"משיג כתובת IP…"</item>
+ <item msgid="4221763391123233270">"מחובר"</item>
+ <item msgid="624838831631122137">"בהשעיה"</item>
+ <item msgid="7979680559596111948">"מתנתק..."</item>
+ <item msgid="1634960474403853625">"מנותק"</item>
+ <item msgid="746097431216080650">"נכשל"</item>
+ <item msgid="6367044185730295334">"חסומה"</item>
+ <item msgid="503942654197908005">"נמנע זמנית מחיבור חלש"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"סורק..."</item>
+ <item msgid="355508996603873860">"מתחבר אל <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="554971459996405634">"מאמת עם <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="7928343808033020343">"משיג כתובת IP מ-<xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="8937994881315223448">"מחובר אל <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"בהשעיה"</item>
+ <item msgid="7698638434317271902">"מתנתק מרשת <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="197508606402264311">"מנותק"</item>
+ <item msgid="8578370891960825148">"נכשל"</item>
+ <item msgid="5660739516542454527">"חסומה"</item>
+ <item msgid="1805837518286731242">"נמנע זמנית מחיבור חלש"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
new file mode 100644
index 0000000..b025a0d
--- /dev/null
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"לא ניתן לסרוק לאיתור רשתות"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"ללא"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"נשמר"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"מושבת"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"כשל בתצורת IP"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"כשל בחיבור Wi-Fi"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"בעיית אימות"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"מחוץ לטווח"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"אין גישה לאינטרנט. לא יתבצע חיבור מחדש באופן אוטומטי."</string>
+ <string name="saved_network" msgid="4352716707126620811">"נשמר על ידי <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"מחובר באמצעות אסיסטנט ה-Wi-Fi"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"מחובר דרך %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"זמינה דרך %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"מחובר. אין אינטרנט"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"מנותק"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"מתנתק..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"מתחבר ..."</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"מחובר"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"מבצע התאמה..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"מחובר (ללא טלפון)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"מחובר (ללא מדיה)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"מחובר (אין גישה להודעות)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"מחובר (ללא טלפון או מדיה)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"אודיו של מדיה"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"אודיו של טלפון"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"העברת קבצים"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"מכשיר קלט"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"גישה לאינטרנט"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"שיתוף אנשי קשר"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"השתמש עבור שיתוף אנשי קשר"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"שיתוף חיבור לאינטרנט"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"גישה להודעות"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"גישה ל-SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"מחובר לאודיו של מדיה"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"מחובר לאודיו של הטלפון"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"מחובר לשרת העברת קבצים"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"מחובר למפה"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"מחובר ל-SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"לא מחובר לשרת העברת קבצים"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"מחובר למכשיר קלט"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"מחובר למכשיר לצורך גישה לאינטרנט"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"משתף חיבור אינטרנט מקומי עם מכשיר"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"השתמש עבור גישה לאינטרנט"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"שימוש עבור מפה"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"השתמש לגישה של SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"השתמש עבור אודיו של מדיה"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"השתמש עבור האודיו של הטלפון"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"השתמש להעברת קבצים"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"השתמש לקלט"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"התאם"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"התאם"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"ביטול"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"לאחר החיבור, התאמה מספקת גישה לאנשי הקשר ולהיסטוריית השיחות שלך."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"לא ניתן לבצע התאמה עם <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"לא ניתן להתאים את <xliff:g id="DEVICE_NAME">%1$s</xliff:g> בשל קוד PIN או סיסמה שגויים."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"לא ניתן לתקשר עם <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"ההתאמה נדחתה על ידי <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi כבוי."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi מנותק."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"פס אחד של Wi-Fi."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"שני פסים של Wi-Fi."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"שלושה פסים של Wi-Fi."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"אות Wi-Fi מלא."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"אפליקציות שהוסרו"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"אפליקציות ומשתמשים שהוסרו"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"שיתוף אינטרנט דרך USB"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"נקודה לשיתוף אינטרנט"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"שיתוף אינטרנט דרך Bluetooth"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"שיתוף אינטרנט בין ניידים"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"נקודה לשיתוף אינטרנט"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"פרופיל עבודה"</string>
+ <string name="user_guest" msgid="8475274842845401871">"אורח"</string>
+ <string name="unknown" msgid="1592123443519355854">"לא ידוע"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"משתמש: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"הוגדרו כמה ברירת מחדל"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"לא הוגדרו ברירות מחדל"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"הגדרות טקסט לדיבור"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"פלט טקסט לדיבור"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"קצב דיבור"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"המהירות שבה הטקסט נאמר"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"שפה"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"שימוש בשפת המערכת"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"לא נבחרה שפה"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"מגדיר קול ספציפי לשפה עבור הטקסט הנאמר"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"האזן לדוגמה"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"הפעל הדגמה קצרה של סינתזת דיבור"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"התקן נתוני קול"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"התקן את הנתונים הקוליים הדרושים לסינתזת דיבור"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"ייתכן שמנוע סינתזת דיבור זה יכול לאסוף את כל הטקסט המדובר, כולל נתונים אישיים כגון סיסמאות ומספרי כרטיסי אשראי. הוא מגיע מהמנוע <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. להפוך את השימוש במנוע סינתזת דיבור זה לפעיל?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"שפה זו דורשת חיבור רשת פעיל עבור פלט טקסט לדיבור."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"זוהי דוגמה לסינתזת דיבור"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"ברירת המחדל לסטטוס שפה"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> נתמכת באופן מלא"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> מצריכה חיבור לרשת"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> אינה נתמכת"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"בודק…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"הגדרות עבור <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"השק הגדרות מנוע"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"מנוע מועדף"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"כללי"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"איטי מאוד"</item>
+ <item msgid="4795095314303559268">"איטי"</item>
+ <item msgid="8903157781070679765">"רגיל"</item>
+ <item msgid="164347302621392996">"מהיר"</item>
+ <item msgid="5794028588101562009">"מהיר יותר"</item>
+ <item msgid="7163942783888652942">"מהיר מאוד"</item>
+ <item msgid="7831712693748700507">"מהיר במיוחד"</item>
+ <item msgid="5194774745031751806">"יותר מהיר ממהיר במיוחד"</item>
+ <item msgid="9085102246155045744">"הכי מהיר"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"בחר פרופיל"</string>
+ <string name="category_personal" msgid="1299663247844969448">"אישי"</string>
+ <string name="category_work" msgid="8699184680584175622">"עבודה"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-ja/arrays.xml b/packages/SettingsLib/res/values-ja/arrays.xml
new file mode 100644
index 0000000..f9f2ae7
--- /dev/null
+++ b/packages/SettingsLib/res/values-ja/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"スキャン中..."</item>
+ <item msgid="8513729475867537913">"接続中..."</item>
+ <item msgid="515055375277271756">"認証中..."</item>
+ <item msgid="1943354004029184381">"IPアドレスを取得中..."</item>
+ <item msgid="4221763391123233270">"接続済み"</item>
+ <item msgid="624838831631122137">"保留中"</item>
+ <item msgid="7979680559596111948">"切断中..."</item>
+ <item msgid="1634960474403853625">"切断されました"</item>
+ <item msgid="746097431216080650">"失敗しました"</item>
+ <item msgid="6367044185730295334">"ブロック中"</item>
+ <item msgid="503942654197908005">"接続不良により一時的に無効"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"スキャン中..."</item>
+ <item msgid="355508996603873860">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>に接続中..."</item>
+ <item msgid="554971459996405634">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>による認証中..."</item>
+ <item msgid="7928343808033020343">"IPアドレスを<xliff:g id="NETWORK_NAME">%1$s</xliff:g>から取得中..."</item>
+ <item msgid="8937994881315223448">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>に接続しました"</item>
+ <item msgid="1330262655415760617">"保留中"</item>
+ <item msgid="7698638434317271902">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>から切断中..."</item>
+ <item msgid="197508606402264311">"切断されました"</item>
+ <item msgid="8578370891960825148">"失敗しました"</item>
+ <item msgid="5660739516542454527">"ブロック中"</item>
+ <item msgid="1805837518286731242">"接続不良により一時的に無効"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
new file mode 100644
index 0000000..6a44854
--- /dev/null
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"ネットワークをスキャンできません"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"なし"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"保存済み"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"無効"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP設定エラー"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi接続エラー"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"認証に問題"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"圏外"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"インターネットアクセスを検出できないため、自動的に再接続されません。"</string>
+ <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g>で保存"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fiアシスタント経由で接続"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s経由で接続"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s経由で使用可能"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"接続済み、インターネットは利用できません"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"切断"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"切断中..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"接続中..."</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"接続"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"ペアとして設定中..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"接続済み(電話を除く)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"接続済み(メディアを除く)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"接続済み(メッセージへのアクセスなし)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"接続済み(電話/メディアを除く)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"メディアの音声"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"電話の音声"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"ファイル転送"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"入力デバイス"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"インターネットアクセス"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"連絡先の共有"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"連絡先の共有に使用"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"インターネット接続の共有"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"メッセージへのアクセス"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIMアクセス"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"メディアの音声に接続"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"携帯電話の音声に接続"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ファイル転送サーバーに接続"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"地図に接続済み"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"SAPに接続"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"ファイル転送サーバーに接続しない"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"入力デバイスに接続されています"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"インターネットアクセス用に接続"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"ローカルインターネット接続をデバイスと共有"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"インターネットアクセスに使用する"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"地図に使用"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"SIMアクセスに使用"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"メディアの音声に使用"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"携帯電話の音声に使用"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ファイル転送に使用"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"入力に使用"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"ペア設定する"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ペア設定する"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"キャンセル"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"ペア設定により、接続時に連絡先や通話履歴へのアクセスが許可されます。"</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>をペアに設定できません。"</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"PINまたはパスキーが正しくないため、<xliff:g id="DEVICE_NAME">%1$s</xliff:g>をペアに設定できませんでした。"</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>と通信できません。"</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"ペア設定が<xliff:g id="DEVICE_NAME">%1$s</xliff:g>に拒否されました。"</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-FiはOFFです。"</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fiが切断されました。"</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fiはレベル1です。"</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wi-Fiはレベル2です。"</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wi-Fiはレベル3です。"</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Wi-Fiの電波はフルです。"</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"削除したアプリケーション"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"削除されたアプリとユーザー"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USBテザリング"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ポータブルアクセスポイント"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetoothテザリング"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"テザリング"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"テザリングとポータブルアクセスポイント"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"仕事用プロファイル"</string>
+ <string name="user_guest" msgid="8475274842845401871">"ゲスト"</string>
+ <string name="unknown" msgid="1592123443519355854">"不明"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"ユーザー: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"一部デフォルトを設定"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"既定の設定なし"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"テキスト読み上げの設定"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"テキスト読み上げの出力"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"音声の速度"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"テキストの読み上げ速度"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"言語"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"システムの言語を使用"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"言語が選択されていません"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"テキスト読み上げに使用する言語固有の音声を設定する"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"サンプルを再生"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"音声合成の短いサンプルを再生する"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"音声データをインストール"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"音声合成に必要な音声データをインストールする"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"この音声合成エンジンを使用すると、読み上げるすべてのテキストの収集をエンジン(<xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>)に許可することになります。これにはパスワードやクレジットカード番号などの個人情報も含まれます。この音声合成エンジンを有効にしますか?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"この言語でテキスト読み上げを出力するには、ネットワーク接続が正常に機能している必要があります。"</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"これは音声合成のサンプルです"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"デフォルトの言語のステータス"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g>は完全サポート対象です"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g>ではネットワーク接続が必要です"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g>はサポート対象ではありません"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"確認しています…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>の設定"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"エンジン設定を起動"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"優先するエンジン"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"全般"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"非常に遅い"</item>
+ <item msgid="4795095314303559268">"遅い"</item>
+ <item msgid="8903157781070679765">"標準"</item>
+ <item msgid="164347302621392996">"速い"</item>
+ <item msgid="5794028588101562009">"より速い"</item>
+ <item msgid="7163942783888652942">"非常に速い"</item>
+ <item msgid="7831712693748700507">"高速"</item>
+ <item msgid="5194774745031751806">"非常に高速"</item>
+ <item msgid="9085102246155045744">"最高速"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"プロファイルの選択"</string>
+ <string name="category_personal" msgid="1299663247844969448">"個人用"</string>
+ <string name="category_work" msgid="8699184680584175622">"仕事用"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-ka-rGE/arrays.xml b/packages/SettingsLib/res/values-ka-rGE/arrays.xml
new file mode 100644
index 0000000..1185d8e
--- /dev/null
+++ b/packages/SettingsLib/res/values-ka-rGE/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"სკანირება…"</item>
+ <item msgid="8513729475867537913">"დაკავშირება…"</item>
+ <item msgid="515055375277271756">"ავტორიზაცია…"</item>
+ <item msgid="1943354004029184381">"IP მისამართის მიღება…"</item>
+ <item msgid="4221763391123233270">"დაკავშირებულია"</item>
+ <item msgid="624838831631122137">"შეწყვეტილია"</item>
+ <item msgid="7979680559596111948">"კავშირის გაწყვეტა…"</item>
+ <item msgid="1634960474403853625">"არ არის ხაზზე"</item>
+ <item msgid="746097431216080650">"ვერ მოხერხდა"</item>
+ <item msgid="6367044185730295334">"დაბლოკილი"</item>
+ <item msgid="503942654197908005">"ცუდი კავშირის დროებით თავიდან აცილება"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"სკანირება…"</item>
+ <item msgid="355508996603873860">"მიმდინარეობს დაკავშირება <xliff:g id="NETWORK_NAME">%1$s</xliff:g>-თან…"</item>
+ <item msgid="554971459996405634">"ავთენტიფიკაცია <xliff:g id="NETWORK_NAME">%1$s</xliff:g>-ში…"</item>
+ <item msgid="7928343808033020343">"IP მისამართის მოპოვება <xliff:g id="NETWORK_NAME">%1$s</xliff:g>-დან…"</item>
+ <item msgid="8937994881315223448">"დაკავშირებულია <xliff:g id="NETWORK_NAME">%1$s</xliff:g>-თან"</item>
+ <item msgid="1330262655415760617">"შეჩერებული"</item>
+ <item msgid="7698638434317271902">"კავშირის გაწყვეტა <xliff:g id="NETWORK_NAME">%1$s</xliff:g>-თან…"</item>
+ <item msgid="197508606402264311">"კავშირი გაწყვეტილია"</item>
+ <item msgid="8578370891960825148">"ვერ მოხერხდა"</item>
+ <item msgid="5660739516542454527">"დაბლოკილი"</item>
+ <item msgid="1805837518286731242">"ცუდი კავშირის დროებით თავიდან აცილება"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-ka-rGE/strings.xml b/packages/SettingsLib/res/values-ka-rGE/strings.xml
new file mode 100644
index 0000000..da5aa46
--- /dev/null
+++ b/packages/SettingsLib/res/values-ka-rGE/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"ქსელების სკანირება არა არის შესაძლებელი"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"არცერთი"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"დამახსოვრებულია"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"გამორთულია"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP კონფიგურაციის შეფერხება"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi კავშირის შეფერხება"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"ავთენტიკაციის პრობლემა"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"არ არის დიაპაზონში"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"ინტერნეტთან წვდომის ამოცნობა ვერ მოხერხდა. ავტომატურად ხელახლა დაკავშირება არ განხორციელდება."</string>
+ <string name="saved_network" msgid="4352716707126620811">"შენახული <xliff:g id="NAME">%1$s</xliff:g>-ის მიერ"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"დაკავშირებულია Wi-Fi თანაშემწით"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s-ით დაკავშირებული"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"ხელმისაწვდომია %1$s-ით"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"დაკავშირებულია, ინტერნეტის გარეშე"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"კავშირი გაწყვეტილია"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"მიმდინარეობს გათიშვა…"</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"მიმდინარეობს დაკავშირება…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"შეერთებულია"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"დაწყვილება…"</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"დაკავშირებულია (ტელეფონის გარდა)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"მიერთებულია (მედიის გარდა)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"დაკავშირებულია (შეტყობინებაზე წვდომა არ არის)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"დაკავშირება (გარდა ტელეფონისა და მედიისა)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"მედია აუდიო"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"ტელეფონის აუდიო"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"ფაილების გადაცემა"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"შეყვანის მოწყობილობა"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"ინტერნეტზე წვდომა"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"კონტაქტის გაზიარება"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"კონტაქტის გაზიარებისთვის გამოყენება"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"ინტერნეტ კავშირის გაზიარება"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"შეტყობინებებზე წვდომა"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM წვდომა"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"დაკავშირებულია აუდიო მულტიმედიურ სისტემასთან"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"დაკავშირებულია ტელეფონის აუდიო მოწყობილობასთან"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"დაკავშირებულია ფაილების გადაცემის სერვერთან"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"რუკასთან დაკავშირებული"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"SAP-თან დაკავშირებული"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"არ არის დაკავშირებული ფაილების ტრანსფერის სერვერთან"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"დაკავშირებულია შეყვანის მოწყობილობასთან"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"დაკავშირებულია მოწყობილობასთან ინტერნეტთან წვდომისთვის"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"ლოკალური კავშირის გაზიარება მოწყობილობასთან"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"გამოიყენე ინტერნეტთან წვდომისთვის"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"რუკაზე გამოყენება"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"გამოყენება SIM წვდომისთვის"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"მედია აუდიოსთვის გამოყენება"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"გამოიყენეთ ტელეფონის აუდიომოწყობილობაში"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ფაილების ტრანსფერისათვის გამოყენება"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"შეტანისთვის გამოყენება"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"დაწყვილება"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"დაწყვილება"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"გაუქმება"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"დაწყვილების შემთხვევაში დაკავშირებულ რეჟიმში ყოფნისას მიენიჭება თქვენს კონტაქტებზე და ზარის ისტორიაზე წვდომის ნებართვა."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"ვერ დაწყვილდა <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-თან."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>-თან დაწყვილება ვერ მოხერხდა, რადგან PIN ან გასაღები არასწორია."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"შეუძლებელია <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-თან კომუნიკაცია."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"დაწყვილება უარყოფილია <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-ის მიერ."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"WiFi გამორთულია."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"WiFi არ არის დაკავშირებული."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"WiFi სიგნალი ერთ ზოლზეა."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"WiFi სიგნალი ორ ზოლზეა."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"WiFi სიგნალი სამ ზოლზეა."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"WiFi სიგნალი სრულია."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"აპების წაშლა"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"წაშლილი აპები და მომხმარებლები"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB მოდემი"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"პორტატული უსადენო ქსელი"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth-მოდემი"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"მოდემის რეჟიმი"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"მოდემი და პორტატული უსადენო ქსელი"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"სამუშაო პროფილი"</string>
+ <string name="user_guest" msgid="8475274842845401871">"სტუმარი"</string>
+ <string name="unknown" msgid="1592123443519355854">"უცნობი"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"მომხმარებელი: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"ზოგიერთი ნაგულისხმევი პარამეტრი დაყენებულია"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"ნაგულისხმევი პარამეტრები არ არის დაყენებული."</string>
+ <string name="tts_settings" msgid="8186971894801348327">"ტექსტის გახმოვანების პარამეტრები"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"მეტყველების სინთეზი"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"მეტყველების ტემპი"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"ტექსტის თხრობის სიჩქარე"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"ენა"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"სისტემის ენის გამოყენება"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"ენა არჩეული არ არის"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"ენის შესაფერი ხმის დაყენება"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"მოუსმინეთ მაგალითს"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"სინთეზირებული მეტყველების მოკლე ნაწყვეტის აღწარმოება"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"ხმის მონაცემების დაყენება"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Install the voice data required for speech synthesis"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"მეტყველების სინთეზის მოცემულ სისტემას შეუძლია ჩაიწეროს წარმოთქმული სიტყვები, მათ შორის პირადი მონაცემები, როგორიცაა პაროლები და საკრედიტო ბარათების ნომრები. ამას ახორციელებს <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> სისტემა. გამოვიყენოთ მეტყველების სინთეზიზს მოცემული სისტემა?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"ეს ენა ტექსტის ამოკითხვისთვის მოითხოვს აქტიურ ინტერნეტ კავშირს."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"ეს არის მეტყველების სინთეზის მაგალითი."</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"ენის ნაგულისხმევი სტატუსი"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> სრულად მხარდაჭერილია"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> ითხოვს ქსელის კავშირს"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> არ არის მხარდაჭერილი"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"შემოწმება..."</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g> პარამეტრები"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"ძრავის პარამეტრების გაშვება"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"რჩეული ძრავი"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"ზოგადი"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"ძალიან ნელი"</item>
+ <item msgid="4795095314303559268">"ნელი"</item>
+ <item msgid="8903157781070679765">"ჩვეულებრივი"</item>
+ <item msgid="164347302621392996">"სწრაფი"</item>
+ <item msgid="5794028588101562009">"უფრო სწრაფი"</item>
+ <item msgid="7163942783888652942">"ძალიან სწრაფი"</item>
+ <item msgid="7831712693748700507">"ჩქარი"</item>
+ <item msgid="5194774745031751806">"ძალიან ჩქარი"</item>
+ <item msgid="9085102246155045744">"უსწრაფესი"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"პროფილის არჩევა"</string>
+ <string name="category_personal" msgid="1299663247844969448">"პირადი"</string>
+ <string name="category_work" msgid="8699184680584175622">"სამსახური"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-kk-rKZ/arrays.xml b/packages/SettingsLib/res/values-kk-rKZ/arrays.xml
new file mode 100644
index 0000000..6555efe1
--- /dev/null
+++ b/packages/SettingsLib/res/values-kk-rKZ/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Тексеруде…"</item>
+ <item msgid="8513729475867537913">"Қосылуда..."</item>
+ <item msgid="515055375277271756">"Растауда…"</item>
+ <item msgid="1943354004029184381">"IP мекенжайына қол жеткізуде…"</item>
+ <item msgid="4221763391123233270">"Қосылған"</item>
+ <item msgid="624838831631122137">"Уақытша тоқтатылды"</item>
+ <item msgid="7979680559596111948">"Ажыратуда…"</item>
+ <item msgid="1634960474403853625">"Ажыратылған"</item>
+ <item msgid="746097431216080650">"Сәтсіз"</item>
+ <item msgid="6367044185730295334">"Бөгелген"</item>
+ <item msgid="503942654197908005">"Нашар байланысты уақытша қолданбау"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Тексеруде…"</item>
+ <item msgid="355508996603873860">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> қосылуда…"</item>
+ <item msgid="554971459996405634">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> арқылы расталуда…"</item>
+ <item msgid="7928343808033020343">"IP мекенжайын <xliff:g id="NETWORK_NAME">%1$s</xliff:g> арқылы алуда…"</item>
+ <item msgid="8937994881315223448">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> жалғанған"</item>
+ <item msgid="1330262655415760617">"Уақытша тоқтатылды"</item>
+ <item msgid="7698638434317271902">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> байланысынан ажыратылуда…"</item>
+ <item msgid="197508606402264311">"Ажыратылған"</item>
+ <item msgid="8578370891960825148">"Сәтсіз"</item>
+ <item msgid="5660739516542454527">"Бөгелген"</item>
+ <item msgid="1805837518286731242">"Нашар байланысты уақытша қолданбау"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-kk-rKZ/strings.xml b/packages/SettingsLib/res/values-kk-rKZ/strings.xml
new file mode 100644
index 0000000..179e4af
--- /dev/null
+++ b/packages/SettingsLib/res/values-kk-rKZ/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Желілерді шолу мүмкін емес"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Ешқандай"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Сақталды"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Өшірілген"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP конфигурациясының қатесі"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi байланысының қатесі"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Растау мәселесі"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Аумақта жоқ"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Интернетке қатынас анықталмады, автоматты түрде қайта қосылу орындалмайды."</string>
+ <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> сақтаған"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fi көмекшісі арқылы қосылу орындалды"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s арқылы қосылған"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s арқылы қолжетімді"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Қосылған, интернет жоқ"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Ажыратылған"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Ажыратылуда…"</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Жалғауда..."</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Жалғанған"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Жұптауда..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Жалғанған (телефон жоқ)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Жалғанған (медиа жоқ)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Жалғанған (хабарлар қол жетімсіз)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Жалғанған (телефон және медиа жоқ)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Meдиа аудиосы"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Телефон аудиосы"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Файл жіберу"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Кіріс құрылғысы"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Интернетке қосылу"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Контактіні бөлісу"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Контактіні бөлісу үшін пайдалану"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Интернет байланысын ортақ қолдану"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Хабарға кіру"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM картасына кіру"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Медиа аудиосына жалғанған"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Телефон аудиосына қосылған"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Файл жіберу серверіне жалғанған"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Картаға жалғанған"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"SAP жүйесіне қосылған"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Файл жіберу серверіне жалғанбаған"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Кіріс құрылғысына косылған"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Құрылғыға интернетке кіру үшін жалғанған"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Жергілікті интернет байланысын құрылғымен ортақ пайдалану"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Интернетке кіру үшін қолдану"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Карта үшін қолдану"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"SIM картасына кіру үшін пайдалану"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Медиа аудиосы үшін қолдану"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Телефон аудиосы үшін қолдану"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Файлды жіберу үшін қолдану"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Кіріс үшін қолдану"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Жұптау"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ЖҰПТАУ"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Бас тарту"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Жұптасқан кезде, контактілеріңіз бен қоңыраулар тарихын көру мүмкіндігі беріледі."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> жұпталу орындалмады."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> құрылғысымен жұптала алмады, себебі PIN немесе кілтсөз дұрыс емес."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> құрылғысымен қатынаса алмайды"</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> құрылғысы жұпталудан бас тартты."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi өшірулі."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi ажыратылған."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi сигналы — бір жолақ."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wi-Fi сигналы — екі жолақ."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wi-Fi сигналы — үш жолақ."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Wi-Fi сигналы толық."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android операциялық жүйесі"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Алынған қолданбалар"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Алынған қолданбалар және пайдаланушылар"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB модем режимі"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Алынбалы хот-спот"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth модем"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Тетеринг"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Тетеринг және алынбалы хотспот"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Жұмыс профилі"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Қонақ"</string>
+ <string name="unknown" msgid="1592123443519355854">"Белгісіз"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Пайдаланушы: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Кейбір әдепкі параметрлер орнатылған"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Әдепкі параметрлер орнатылмаған"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Мәтіннен-сөйлеуге параметрлері"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Мәтінді тілге айналдыру"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Сөйлеу жылдамдығы"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Мәтіннің оқылу жылдамдығы"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Тіл"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Жүйелік тілді пайдалану"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Тіл таңдалған жоқ"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Сөйлеу мәтіні үшін тілге тән дауыс орнатады"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Үлгіні тыңдау"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Сөйлеу синтезінің қысқа көрсетілімін ойнату"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Дауыс дерекқорын орнату"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Тілді синтездеуге қажетті дауыс дерегін орнату"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Бұл сөйлеуді синтездеу қозғалтқышы айтылған барлық мәтіндерді, кілтсөз және кредит карта нөмірлері сияқты жеке ақпаратты қоса, жинауы мүмкін. Ол <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> қозғалқышы арқылы жадбықталған. Осы сөйлеуді синтездеу қозғалтқышын пайдалану қосылсын ба?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Бұл тіл мәтіннен-сөйлеуге шығысы үшін қосылып тұрған желі байланысын қажет етеді."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Бұл сөйлеу синтезінің мысалы"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Бастапқы тіл күйі"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> тілінің қолдауы бар"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> желі байланысын қажет етеді"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> үшін қолдау ұсынылмаған"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Тексерілуде…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g> параметрлері"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Қозғалтқыш параметрлерін қосу"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Қалаулы қозғалтқыш"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Жалпы"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Өте баяу"</item>
+ <item msgid="4795095314303559268">"Баяу"</item>
+ <item msgid="8903157781070679765">"Қалыпты"</item>
+ <item msgid="164347302621392996">"Жылдам"</item>
+ <item msgid="5794028588101562009">"Жылдамырақ"</item>
+ <item msgid="7163942783888652942">"Өте жылдам"</item>
+ <item msgid="7831712693748700507">"Тез"</item>
+ <item msgid="5194774745031751806">"Өте тез"</item>
+ <item msgid="9085102246155045744">"Ең тез"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Профильді таңдау"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Жеке"</string>
+ <string name="category_work" msgid="8699184680584175622">"Жұмыс"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-km-rKH/arrays.xml b/packages/SettingsLib/res/values-km-rKH/arrays.xml
new file mode 100644
index 0000000..3c65bf3
--- /dev/null
+++ b/packages/SettingsLib/res/values-km-rKH/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"កំពុងវិភាគរក…"</item>
+ <item msgid="8513729475867537913">"កំពុងតភ្ជាប់…"</item>
+ <item msgid="515055375277271756">"កំពុងផ្ទៀងផ្ទាត់..."</item>
+ <item msgid="1943354004029184381">"កំពុងទទួលអាសយដ្ឋាន IP…"</item>
+ <item msgid="4221763391123233270">"បានតភ្ជាប់"</item>
+ <item msgid="624838831631122137">"បានពន្យារពេល"</item>
+ <item msgid="7979680559596111948">"កំពុងផ្ដាច់…"</item>
+ <item msgid="1634960474403853625">"បានផ្ដាច់"</item>
+ <item msgid="746097431216080650">"មិនជោគជ័យ"</item>
+ <item msgid="6367044185730295334">"បានទប់ស្កាត់"</item>
+ <item msgid="503942654197908005">"ជៀសវាងការតភ្ជាប់ដែលខ្សោយជាបណ្ដោះអាសន្ន"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"កំពុងវិភាគរក…"</item>
+ <item msgid="355508996603873860">"កំពុងតភ្ជាប់ទៅ <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="554971459996405634">"កំពុងផ្ទៀងផ្ទាត់ជាមួយ <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="7928343808033020343">"កំពុងយកអាសយដ្ឋាន IP ពី <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="8937994881315223448">"បានតភ្ជាប់ទៅ <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"បានផ្អាក"</item>
+ <item msgid="7698638434317271902">"កំពុងផ្ដាច់ពីរ <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="197508606402264311">"បានផ្ដាច់"</item>
+ <item msgid="8578370891960825148">"មិនជោគជ័យ"</item>
+ <item msgid="5660739516542454527">"បានទប់ស្កាត់"</item>
+ <item msgid="1805837518286731242">"ជៀសវាងការតភ្ជាប់ខ្សោយជាបណ្ដោះអាសន្ន"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-km-rKH/strings.xml b/packages/SettingsLib/res/values-km-rKH/strings.xml
new file mode 100644
index 0000000..dd13d21
--- /dev/null
+++ b/packages/SettingsLib/res/values-km-rKH/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"មិនអាចវិភាគរកបណ្ដាញ"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"គ្មាន"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"បានរក្សាទុក"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"បានបិទ"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"ការកំណត់រចនាសម្ព័ន្ធ IP បរាជ័យ"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"ការភ្ជាប់ WiFi បរាជ័យ"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"បញ្ហាក្នុងការផ្ទៀងផ្ទាត់"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"នៅក្រៅតំបន់"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"រកមិនឃើញការចូលដំណើរការអ៊ីនធឺណិត, នឹងមិនភ្ជាប់ឡើងវិញដោយស្វ័យប្រវត្តិទេ។"</string>
+ <string name="saved_network" msgid="4352716707126620811">"បានរក្សាទុកដោយ <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"បានភ្ជាប់តាមរយៈជំនួយការ Wi‑Fi"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"បានភ្ជាប់តាមរយៈ %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"មានតាមរយៈ %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"បានភ្ជាប់ ប៉ុន្តែគ្មានអ៊ីនធឺណិតទេ"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"បានផ្ដាច់"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"កំពុងផ្ដាច់…"</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"កំពុងតភ្ជាប់…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"បានតភ្ជាប់"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"កំពុងផ្គូផ្គង..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"បានតភ្ជាប់ (គ្មានទូរស័ព្ទ)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"បានតភ្ជាប់ (គ្មានមេឌៀ)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"បានភ្ជាប់ (គ្មានការចូលដំណើរការសារ)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"បានតភ្ជាប់ (គ្មានទូរស័ព្ទ ឬមេឌៀ)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"សំឡេងមេឌៀ"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"សំឡេងទូរស័ព្ទ"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"ផ្ទេរឯកសារ"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"ឧបករណ៍បញ្ចូល"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"ចូលអ៊ីនធឺណិត"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"ការចែករំលែកទំនាក់ទំនង"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"ប្រើសម្រាប់ការចែករំលែកទំនាក់ទំនង"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"ចែករំលែកការតភ្ជាប់អ៊ីនធឺណិត"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"ចូលដំណើរការសារ"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"ការចូលដំណើរការស៊ីម"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"បានភ្ជាប់ទៅអូឌីយ៉ូមេឌៀ"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"តភ្ជាប់ទៅអូឌីយ៉ូទូរស័ព្ទ"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"បានតភ្ជាប់ទៅម៉ាស៊ីនមេផ្ទេរឯកសារ"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"បានភ្ជាប់ទៅផែនទី"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"បានភ្ជាប់ទៅ SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"មិនបានតភ្ជាប់ទៅម៉ាស៊ីនមេផ្ទេរឯកសារ"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"បានតភ្ជាប់ទៅឧបករណ៍បញ្ចូល"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"បានតភ្ជាប់ឧបករណ៍សម្រាប់ចូលអ៊ីនធឺណិត"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"ចែករំលែកការតភ្ជាប់អ៊ីនធឺណិតមូលដ្ឋានជាមួយឧបករណ៍"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"ប្រើសម្រាប់ចូលអ៊ីនធឺណិត"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"ប្រើសម្រាប់ផែនទី"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"ប្រើសម្រាប់ចូលដំណើរការស៊ីម"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"ប្រើសម្រាប់សំឡេងមេឌៀ"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"ប្រើសម្រាប់អូឌីយ៉ូទូរស័ព្ទ"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ប្រើសម្រាប់ផ្ទេរឯកសារ"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ប្រើសម្រាប់បញ្ចូល"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"ផ្គូផ្គង"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ផ្គូផ្គង"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"បោះបង់"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"ការផ្គូផ្គងដើម្បីចូលដំណើរការទំនាក់ទំនង និងប្រវត្តិហៅរបស់អ្នក ពេលបានតភ្ជាប់។"</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"មិនអាចផ្គូផ្គងជាមួយ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ។"</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"មិនអាចផ្គូផ្គងជាមួយ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ព្រោះកូដ PIN ឬលេខកូដមិនត្រឹមត្រូវ។"</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"មិនអាចទាក់ទងជាមួយ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ។"</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"ការផ្គូផ្គងបានបដិសេធដោយ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ។"</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"បានបិទ Wifi"</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"បានផ្តាច់ Wifi"</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wifi មួយកាំ"</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wifi ពីរកាំ"</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wifi បីកាំ"</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"សេវា Wifi ពេញ"</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"ប្រព័ន្ធប្រតិបត្តិការ Android"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"កម្មវិធីដែលបានលុប"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"បានលុបកម្មវិធី និងអ្នកប្រើ"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"ការភ្ជាប់យូអេសប៊ី"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ហតស្ពតចល័ត"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ការភ្ជាប់ប៊្លូធូស"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"ការភ្ជាប់"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"ការភ្ជាប់ & ហតស្ពតចល័ត"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"ប្រវត្តិរូបការងារ"</string>
+ <string name="user_guest" msgid="8475274842845401871">"ភ្ញៀវ"</string>
+ <string name="unknown" msgid="1592123443519355854">"មិនស្គាល់"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"អ្នកប្រើ៖ <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"លំនាំដើមមួយចំនួនត្រូវបានកំណត់"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"គ្មានការកំណត់លំនាំដើម"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"ការកំណត់អត្ថបទទៅជាកានិយាយ"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"លទ្ធផលអត្ថបទទៅការនិយាយ"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"អត្រានិយាយ"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"ល្បឿនពេលអានអត្ថបទ"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"ភាសា"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"ប្រើភាសាប្រព័ន្ធ"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"មិនបានជ្រើសភាសា"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"កំណត់សំឡេងភាសាជាក់លាក់សម្រាប់អត្ថបទបាននិយាយ"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"ស្ដាប់ឧទាហរណ៍"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"ចាក់ការបង្ហាញខ្លីនៃការសំយោគការនិយាយ"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"ដំឡើងទិន្នន័យសំឡេង"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"ដំឡើងទិន្នន័យសំឡេងដែលទាមទារសម្រាប់ការសំយោគសំដី"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"ម៉ាស៊ីនសំយោគការនិយាយអាចប្រមូលអត្ថបទទាំងអស់ដែលនឹងត្រូវនិយាយ រួមមានទិន្នន័យផ្ទាល់ខ្លួន ដូចជាពាក្យសម្ងាត់ និងលេខកាតឥណទាន។ វាបានមកពីម៉ាស៊ីន <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> ។បើកការប្រើម៉ាស៊ីនសំយោគការនិយាយនេះ?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"ភាសានេះទាមទារការតភ្ជាប់បណ្ដាញដែលដំណើរការសម្រាប់លទ្ធផលអត្ថបទត្រូវនិយាយ។"</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"នេះជាឧទាហរណ៍នៃការសំយោគការនិយាយ។"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"ស្ថានភាពភាសាលំនាំដើម"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"បានគាំទ្រពេញលេញ <xliff:g id="LOCALE">%1$s</xliff:g>"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> ទាមទារការភ្ជាប់បណ្ដាញ"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"មិនគាំទ្រ <xliff:g id="LOCALE">%1$s</xliff:g>"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"កំពុងពិនិត្យ..."</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"ការកំណត់សម្រាប់ <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"ចាប់ផ្ដើមការកំណត់ម៉ាស៊ីនផ្សេង"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"ម៉ាស៊ីនដែលពេញចិត្ត"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"ទូទៅ"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"យឺតខ្លាំង"</item>
+ <item msgid="4795095314303559268">"យឺត"</item>
+ <item msgid="8903157781070679765">"ធម្មតា"</item>
+ <item msgid="164347302621392996">"លឿន"</item>
+ <item msgid="5794028588101562009">"លឿនជាង"</item>
+ <item msgid="7163942783888652942">"លឿនខ្លាំង"</item>
+ <item msgid="7831712693748700507">"រហ័ស"</item>
+ <item msgid="5194774745031751806">"រហ័សខ្លាំង"</item>
+ <item msgid="9085102246155045744">"លឿនបំផុត"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"ជ្រើសប្រវត្តិរូប"</string>
+ <string name="category_personal" msgid="1299663247844969448">"ផ្ទាល់ខ្លួន"</string>
+ <string name="category_work" msgid="8699184680584175622">"កន្លែងធ្វើការ"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-kn-rIN/arrays.xml b/packages/SettingsLib/res/values-kn-rIN/arrays.xml
new file mode 100644
index 0000000..7e42fb4
--- /dev/null
+++ b/packages/SettingsLib/res/values-kn-rIN/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"ಸ್ಕ್ಯಾನ್ ಮಾಡಲಾಗುತ್ತಿದೆ…"</item>
+ <item msgid="8513729475867537913">"ಸಂಪರ್ಕಪಡಿಸಲಾಗುತ್ತಿದೆ..."</item>
+ <item msgid="515055375277271756">"ಪ್ರಮಾಣೀಕರಿಸಲಾಗುತ್ತಿದೆ..."</item>
+ <item msgid="1943354004029184381">"IP ವಿಳಾಸವನ್ನು ಪಡೆಯಲಾಗುತ್ತಿದೆ…"</item>
+ <item msgid="4221763391123233270">"ಸಂಪರ್ಕಗೊಂಡಿದೆ"</item>
+ <item msgid="624838831631122137">"ತಡೆಹಿಡಿಯಲಾಗಿದೆ"</item>
+ <item msgid="7979680559596111948">"ಸಂಪರ್ಕ ಕಡಿತಗೊಳಿಸಲಾಗುತ್ತಿದೆ..."</item>
+ <item msgid="1634960474403853625">"ಸಂಪರ್ಕ ಕಡಿತಗೊಳಿಸಲಾಗಿದೆ"</item>
+ <item msgid="746097431216080650">"ಯಶಸ್ವಿಯಾಗಲಿಲ್ಲ"</item>
+ <item msgid="6367044185730295334">"ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ"</item>
+ <item msgid="503942654197908005">"ಕಳಪೆ ಸಂಪರ್ಕವನ್ನು ತಾತ್ಕಾಲಿಕವಾಗಿ ತಡೆಗಟ್ಟಲಾಗುತ್ತಿದೆ"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"ಸ್ಕ್ಯಾನ್ ಮಾಡಲಾಗುತ್ತಿದೆ…"</item>
+ <item msgid="355508996603873860">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> ಗೆ ಸಂಪರ್ಕಪಡಿಸಲಾಗುತ್ತಿದೆ…"</item>
+ <item msgid="554971459996405634">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> ಜೊತೆ ಪ್ರಮಾಣೀಕರಿಸಲಾಗುತ್ತಿದೆ…"</item>
+ <item msgid="7928343808033020343">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> ನಿಂದ IP ವಿಳಾಸವನ್ನು ಪಡೆಯಲಾಗುತ್ತಿದೆ…"</item>
+ <item msgid="8937994881315223448">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> ಗೆ ಸಂಪರ್ಕಪಡಿಸಲಾಗಿದೆ"</item>
+ <item msgid="1330262655415760617">"ತಡೆಹಿಡಿಯಲಾಗಿದೆ"</item>
+ <item msgid="7698638434317271902">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> ನಿಂದ ಸಂಪರ್ಕ ಕಡಿತಗೊಳಿಸಲಾಗುತ್ತಿದೆ…"</item>
+ <item msgid="197508606402264311">"ಸಂಪರ್ಕ ಕಡಿತಗೊಳಿಸಲಾಗಿದೆ"</item>
+ <item msgid="8578370891960825148">"ಯಶಸ್ವಿಯಾಗಲಿಲ್ಲ"</item>
+ <item msgid="5660739516542454527">"ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ"</item>
+ <item msgid="1805837518286731242">"ಕಳಪೆ ಸಂಪರ್ಕವನ್ನು ತಾತ್ಕಾಲಿಕವಾಗಿ ತಡೆಗಟ್ಟಲಾಗುತ್ತಿದೆ"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-kn-rIN/strings.xml b/packages/SettingsLib/res/values-kn-rIN/strings.xml
new file mode 100644
index 0000000..a3586b8
--- /dev/null
+++ b/packages/SettingsLib/res/values-kn-rIN/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"ನೆಟ್ವರ್ಕ್ಗಳಿಗಾಗಿ ಸ್ಕ್ಯಾನ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"ಯಾವುದೂ ಇಲ್ಲ"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"ಉಳಿಸಲಾಗಿದೆ"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP ಕಾನ್ಫಿಗರೇಶನ್ ವಿಫಲತೆ"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi ಸಂಪರ್ಕ ವಿಫಲತೆ"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"ಪ್ರಮಾಣೀಕರಣ ಸಮಸ್ಯೆ"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"ವ್ಯಾಪ್ತಿಯಲ್ಲಿಲ್ಲ"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಪ್ರವೇಶ ಪತ್ತೆಯಾಗಿಲ್ಲ, ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಮರುಸಂಪರ್ಕಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
+ <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> ರಿಂದ ಉಳಿಸಲಾಗಿದೆ"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fi ಸಹಾಯಕದ ಮೂಲಕ ಸಂಪರ್ಕಿತಗೊಳಿಸಲಾಗಿದೆ"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s ಮೂಲಕ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s ಮೂಲಕ ಲಭ್ಯವಿದೆ"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"ಸಂಪರ್ಕಪಡಿಸಲಾಗಿದೆ, ಇಂಟರ್ನೆಟ್ ಇಲ್ಲ"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"ಸಂಪರ್ಕ ಕಡಿತಗೊಳಿಸಲಾಗಿದೆ"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"ಸಂಪರ್ಕ ಕಡಿತಗೊಳಿಸಲಾಗುತ್ತಿದೆ..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"ಸಂಪರ್ಕಗೊಳಿಸಲಾಗುತ್ತಿದೆ..."</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"ಜೋಡಿಸಲಾಗುತ್ತಿದೆ..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"ಸಂಪರ್ಕಗೊಂಡಿದೆ (ಫೋನ್ ಇಲ್ಲ)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"ಸಂಪರ್ಕಗೊಂಡಿದೆ (ಮಾಧ್ಯಮವಿಲ್ಲ)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"ಸಂಪರ್ಕಪಡಿಸಲಾಗಿದೆ (ಯಾವುದೇ ಸಂದೇಶ ಪ್ರವೇಶವಿಲ್ಲ)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"ಸಂಪರ್ಕಗೊಂಡಿದೆ (ಫೋನ್ ಅಥವಾ ಮಾಧ್ಯಮವಿಲ್ಲ)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"ಮಾಧ್ಯಮ ಆಡಿಯೋ"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"ಫೋನ್ ಆಡಿಯೋ"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"ಫೈಲ್ ವರ್ಗಾವಣೆ"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"ಇನ್ಪುಟ್ ಸಾಧನ"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"ಇಂಟರ್ನೆಟ್ ಪ್ರವೇಶ"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"ಸಂಪರ್ಕ ಹಂಚಿಕೆ"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"ಸಂಪರ್ಕ ಹಂಚಿಕೆಗಾಗಿ ಬಳಸಿ"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"ಇಂಟರ್ನೆಟ್ ಸಂಪರ್ಕ ಹಂಚಿಕೊಳ್ಳುವಿಕೆ"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"ಸಂದೇಶ ಪ್ರವೇಶಿಸುವಿಕೆ"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"ಸಿಮ್ ಪ್ರವೇಶ"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"ಮಾಧ್ಯಮ ಆಡಿಯೋಗೆ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"ಫೋನ್ ಆಡಿಯೋಗೆ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ಫೈಲ್ ವರ್ಗಾವಣೆ ಸರ್ವರ್ಗೆ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"ನಕ್ಷೆಗೆ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"SAP ಗೆ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"ಫೈಲ್ ವರ್ಗಾವಣೆ ಸರ್ವರ್ಗೆ ಸಂಪರ್ಕಗೊಳ್ಳಲಿಲ್ಲ"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"ಇನ್ಪುಟ್ ಸಾಧನಕ್ಕೆ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"ಇಂಟರ್ನೆಟ್ ಪ್ರವೇಶಕ್ಕಾಗಿ ಸಾಧನಕ್ಕೆ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"ಸಾಧನದ ಜೊತೆಗೆ ಸ್ಥಳೀಯ ಇಂಟರ್ನೆಟ್ ಸಂಪರ್ಕವನ್ನು ಹಂಚಿಕೊಳ್ಳಲಾಗುತ್ತಿದೆ"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"ಇಂಟರ್ನೆಟ್ ಪ್ರವೇಶಕ್ಕಾಗಿ ಬಳಸಿ"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"ನಕ್ಷೆಗಾಗಿ ಬಳಸಿ"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"ಸಿಮ್ ಪ್ರವೇಶಕ್ಕೆ ಬಳಸಿ"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"ಮಾಧ್ಯಮ ಆಡಿಯೋ ಬಳಸು"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"ಫೋನ್ ಆಡಿಯೋಗಾಗಿ ಬಳಕೆ"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ಫೈಲ್ ವರ್ಗಾವಣೆಗಾಗಿ ಬಳಸು"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ಇನ್ಪುಟ್ಗಾಗಿ ಬಳಸು"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"ಜೋಡಿ"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ಜೋಡಿ ಮಾಡು"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"ರದ್ದುಮಾಡು"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"ಸಂಪರ್ಕಪಡಿಸಿದಾಗ, ಜೋಡಿಸುವಿಕೆಯು ನಿಮ್ಮ ಸಂಪರ್ಕಗಳು ಮತ್ತು ಕರೆ ಇತಿಹಾಸಕ್ಕೆ ಪ್ರವೇಶವನ್ನು ಅನುಮತಿಸುತ್ತದೆ."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ಜೊತೆಗೆ ಜೋಡಣೆ ಮಾಡಲಾಗಲಿಲ್ಲ."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"ತಪ್ಪಾಗಿರುವ ಪಿನ್ ಅಥವಾ ಪಾಸ್ಕೀ ಕಾರಣದಿಂದಾಗಿ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ಜೊತೆಗೆ ಜೋಡಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ಜೊತೆಗೆ ಸಂವಹನ ನಡೆಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"ಜೋಡಿಸುವಿಕೆಯನ್ನು <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ತಿರಸ್ಕರಿಸಿದೆ"</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"ವೈಫೈ ಆಫ್."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"ವೈಫೈ ಸಂಪರ್ಕ ಕಡಿತಗೊಂಡಿದೆ."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"ವೈಫೈ ಒಂದು ಪಟ್ಟಿ."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"ವೈಫೈ ಎರಡು ಪಟ್ಟಿಗಳು."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"ವೈಫೈ ಮೂರು ಪಟ್ಟಿಗಳು."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"ವೈಫೈ ಸಿಗ್ನಲ್ ಪೂರ್ತಿ ಇದೆ."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"ತೆಗೆದುಹಾಕಲಾದ ಅಪ್ಲಿಕೇಶನ್ಗಳು"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"ಅಪ್ಲಿಕೇಶನ್ಗಳು ಮತ್ತು ಬಳಕೆದಾರರನ್ನು ತೆಗೆದುಹಾಕಲಾಗಿದೆ"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB ಟೆಥರಿಂಗ್"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ಪೋರ್ಟಬಲ್ ಹಾಟ್ಸ್ಪಾಟ್"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ಬ್ಲೂಟೂತ್ ಟೆಥರಿಂಗ್"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"ಟೆಥರಿಂಗ್"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"ಟೆಥರಿಂಗ್ & ಪೋರ್ಟಬಲ್ ಹಾಟ್ಸ್ಪಾಟ್"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"ಕೆಲಸದ ಪ್ರೊಫೈಲ್"</string>
+ <string name="user_guest" msgid="8475274842845401871">"ಅತಿಥಿ"</string>
+ <string name="unknown" msgid="1592123443519355854">"ಅಜ್ಞಾತ"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"ಬಳಕೆದಾರ: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"ಕೆಲವು ಡೀಫಾಲ್ಟ್ಗಳನ್ನು ಹೊಂದಿಸಲಾಗಿದೆ"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"ಡೀಫಾಲ್ಟ್ಗಳನ್ನು ಹೊಂದಿಸಲಾಗಿಲ್ಲ"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"ಪಠ್ಯದಿಂದ ಧ್ವನಿಗೆ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"ಧ್ವನಿಗೆ-ಪಠ್ಯದ ಔಟ್ಪುಟ್"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"ಧ್ವನಿಯ ದರ"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"ಪಠ್ಯವನ್ನು ಹೇಳಿದ ವೇಗ"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"ಭಾಷೆ"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"ಸಿಸ್ಟಂ ಭಾಷೆಯನ್ನು ಬಳಸು"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"ಭಾಷೆಯನ್ನು ಆಯ್ಕೆಮಾಡಲಾಗಿಲ್ಲ"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"ಮಾತಿನ ಪಠ್ಯಕ್ಕೆ ಭಾಷಾ-ನಿರ್ದಿಷ್ಟ ಧ್ವನಿಯನ್ನು ಹೊಂದಿಸುತ್ತದೆ"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"ಉದಾಹರಣೆಯೊಂದನ್ನು ಆಲಿಸಿ"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"ಧ್ವನಿ ಸಮನ್ವಯದ ಕಿರು ಪ್ರಾತ್ಯಕ್ಷಿಕೆಯನ್ನು ಪ್ಲೇ ಮಾಡು"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"ಧ್ವನಿ ಡೇಟಾವನ್ನು ಸ್ಥಾಪಿಸಿ"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"ಧ್ವನಿ ಸಮನ್ವಯಕ್ಕಾಗಿ ಅಗತ್ಯವಿರುವ ಧ್ವನಿ ಡೇಟಾ ಸ್ಥಾಪಿಸಿ"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"ಪಾಸ್ವರ್ಡ್ಗಳು ಮತ್ತು ಕ್ರೆಡಿಟ್ ಕಾರ್ಡ್ ಸಂಖ್ಯೆಗಳಂತಹ ವೈಯಕ್ತಿಕ ಡೇಟಾ ಒಳಗೊಂಡಂತೆ ಮಾತನಾಡುವ ಎಲ್ಲ ಪಠ್ಯವನ್ನು ಸಂಗ್ರಹಿಸಲು ಈ ಧ್ವನಿ ಸಮನ್ವಯ ಎಂಜಿನ್ಗೆ ಸಾಧ್ಯವಾಗಬಹುದು. ಇದು <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> ಎಂಜಿನ್ನಿಂದ ಬರುತ್ತದೆ. ಈ ಧ್ವನಿ ಸಮನ್ವಯ ಎಂಜಿನ್ ಬಳಕೆಯನ್ನು ಸಕ್ರಿಯಗೊಳಿಸುವುದೇ?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"ಪಠ್ಯದಿಂದ ಧ್ವನಿ ಔಟ್ಪುಟ್ಗಾಗಿ ಈ ಭಾಷೆಗೆ ಕಾರ್ಯನಿರತವಾದ ನೆಟ್ವರ್ಕ್ನ ಅಗತ್ಯವಿದೆ."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"ಇದು ಧ್ವನಿ ಸಮನ್ವಯದ ಒಂದು ಉದಾಹರಣೆಯಾಗಿದೆ"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"ಡೀಫಾಲ್ಟ್ ಭಾಷೆಯ ಸ್ಥಿತಿ"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> ಸಂಪೂರ್ಣವಾಗಿ ಬೆಂಬಲಿತವಾಗಿದೆ"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> ನೆಟ್ವರ್ಕ್ ಸಂಪರ್ಕದ ಅಗತ್ಯವಿದೆ"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> ಬೆಂಬಲಿತವಾಗಿಲ್ಲ"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"ಪರಿಶೀಲಿಸಲಾಗುತ್ತಿದೆ..."</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g> ಗಾಗಿ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"ಎಂಜಿನ್ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಪ್ರಾರಂಭಿಸು"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"ಪ್ರಾಶಸ್ತ್ಯದ ಎಂಜಿನ್"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"ಸಾಮಾನ್ಯ"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"ತುಂಬಾ ನಿಧಾನ"</item>
+ <item msgid="4795095314303559268">"ನಿಧಾನ"</item>
+ <item msgid="8903157781070679765">"ಸಾಮಾನ್ಯ"</item>
+ <item msgid="164347302621392996">"ವೇಗ"</item>
+ <item msgid="5794028588101562009">"ಕ್ಷಿಪ್ರ"</item>
+ <item msgid="7163942783888652942">"ಅತ್ಯಂತ ವೇಗ"</item>
+ <item msgid="7831712693748700507">"ತ್ವರಿತ"</item>
+ <item msgid="5194774745031751806">"ಅತ್ಯಂತ ತ್ವರಿತ"</item>
+ <item msgid="9085102246155045744">"ಅತಿ ಕ್ಷಿಪ್ರ"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"ಪ್ರೊಫೈಲ್ ಆಯ್ಕೆಮಾಡಿ"</string>
+ <string name="category_personal" msgid="1299663247844969448">"ವೈಯಕ್ತಿಕ"</string>
+ <string name="category_work" msgid="8699184680584175622">"ಕೆಲಸದ ಸ್ಥಳ"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-ko/arrays.xml b/packages/SettingsLib/res/values-ko/arrays.xml
new file mode 100644
index 0000000..0fcb70d
--- /dev/null
+++ b/packages/SettingsLib/res/values-ko/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"검색 중..."</item>
+ <item msgid="8513729475867537913">"연결 중…"</item>
+ <item msgid="515055375277271756">"인증하는 중..."</item>
+ <item msgid="1943354004029184381">"IP 주소를 가져오는 중..."</item>
+ <item msgid="4221763391123233270">"연결됨"</item>
+ <item msgid="624838831631122137">"일시 정지됨"</item>
+ <item msgid="7979680559596111948">"연결을 끊는 중…"</item>
+ <item msgid="1634960474403853625">"연결 끊김"</item>
+ <item msgid="746097431216080650">"실패"</item>
+ <item msgid="6367044185730295334">"차단됨"</item>
+ <item msgid="503942654197908005">"연결 불량 일시적으로 방지"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"검색 중..."</item>
+ <item msgid="355508996603873860">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>에 연결 중..."</item>
+ <item msgid="554971459996405634">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>에서 인증하는 중..."</item>
+ <item msgid="7928343808033020343">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>에서 IP 주소를 가져오는 중..."</item>
+ <item msgid="8937994881315223448">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>에 연결됨"</item>
+ <item msgid="1330262655415760617">"일시 정지됨"</item>
+ <item msgid="7698638434317271902">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>에서 연결을 끊는 중..."</item>
+ <item msgid="197508606402264311">"연결 끊김"</item>
+ <item msgid="8578370891960825148">"실패"</item>
+ <item msgid="5660739516542454527">"차단됨"</item>
+ <item msgid="1805837518286731242">"연결 상태 불량 일시적으로 방지"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
new file mode 100644
index 0000000..745fb5b
--- /dev/null
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"네트워크를 검색할 수 없습니다."</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"없음"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"저장됨"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"사용 중지됨"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP 설정 실패"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi 연결 실패"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"인증 문제"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"범위 내에 없음"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"감지된 인터넷 액세스가 없으며 자동으로 다시 연결되지 않습니다."</string>
+ <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g>(으)로 저장됨"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fi 도우미를 통해 연결됨"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s을(를) 통해 연결됨"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s을(를) 통해 사용 가능"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"인터넷을 사용하지 않고 연결됨"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"연결 끊김"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"연결을 끊는 중…"</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"연결 중…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"연결됨"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"페어링 중..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"연결됨(전화 없음)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"연결됨(미디어 없음)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"연결됨(메시지 액세스 없음)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"연결됨(전화 또는 미디어 없음)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"미디어 오디오"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"휴대폰 오디오"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"파일 전송"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"입력 장치"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"인터넷 액세스"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"연락처 공유"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"연락처 공유용"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"인터넷 연결 공유"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"메시지 액세스"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM 액세스"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"미디어 오디오에 연결됨"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"휴대전화 오디오에 연결됨"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"파일 전송 서버에 연결됨"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"지도에 연결됨"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"SAP에 연결됨"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"파일 전송 서버에 연결되지 않았습니다."</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"입력 장치에 연결됨"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"인터넷 액세스를 위해 기기에 연결됨"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"로컬 인터넷 연결을 기기와 공유 중"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"인터넷 액세스에 사용"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"지도에 사용"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"SIM 액세스에 사용"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"미디어 오디오에 사용"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"휴대전화 오디오에 사용"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"파일 전송에 사용"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"입력에 사용"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"페어링"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"페어링"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"취소"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"페어링하면 연결 시 주소록 및 통화 기록에 액세스할 수 있습니다."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>와(과) 페어링하지 못했습니다."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"PIN 또는 패스키가 잘못되어 <xliff:g id="DEVICE_NAME">%1$s</xliff:g>와(과) 페어링하지 못했습니다."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>와(과) 통신할 수 없습니다."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>에서 페어링을 거부했습니다."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi가 꺼져 있습니다."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi 연결이 끊어졌습니다."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi 신호 막대가 한 개입니다."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wi-Fi 신호 막대가 두 개입니다."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wi-Fi 신호 막대가 세 개입니다."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Wi-Fi 신호가 강합니다."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"삭제된 앱"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"삭제된 앱 및 사용자"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB 테더링"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"휴대용 핫스팟"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"블루투스 테더링"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"테더링"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"테더링 및 휴대용 핫스팟"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"직장 프로필"</string>
+ <string name="user_guest" msgid="8475274842845401871">"손님"</string>
+ <string name="unknown" msgid="1592123443519355854">"알 수 없음"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"사용자: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"일부 기본값이 설정됨"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"기본값이 설정되지 않음"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"TTS 설정"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"TTS 출력"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"말하는 속도"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"텍스트를 읽어주는 속도"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"언어"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"시스템 언어 사용"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"언어가 선택되지 않음"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"발음된 텍스트에 대해 언어별로 음성 설정"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"예제 듣기"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"짧은 음성 합성 데모 재생"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"음성 데이터 설치"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"음성 합성을 사용하려면 음성 데이터를 설치해야 함"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"이 음성 합성 엔진은 비밀번호와 신용카드 번호 등의 개인 데이터를 비롯한 모든 음성 데이터를 수집할 수 있습니다. 이것은 <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> 엔진의 기능입니다. 음성 합성 엔진을 사용하도록 설정하시겠습니까?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"이 언어로 TTS(Text-to-speech)를 출력하려면 네트워크 연결이 작동해야 합니다."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"음성 합성의 예입니다."</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"기본 언어 상태"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g>은(는) 완전하게 지원됩니다."</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g>은(는) 네트워크 연결이 필요합니다."</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g>은(는) 지원되지 않습니다."</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"확인 중…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>에 대한 설정"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"엔진 설정 실행"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"기본 엔진"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"기본설정"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"매우 느리게"</item>
+ <item msgid="4795095314303559268">"느리게"</item>
+ <item msgid="8903157781070679765">"보통"</item>
+ <item msgid="164347302621392996">"빠르게"</item>
+ <item msgid="5794028588101562009">"더 빠르게"</item>
+ <item msgid="7163942783888652942">"매우 빠르게"</item>
+ <item msgid="7831712693748700507">"상당히 빠르게"</item>
+ <item msgid="5194774745031751806">"매우 빠르게"</item>
+ <item msgid="9085102246155045744">"가장 빠르게"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"프로필 선택"</string>
+ <string name="category_personal" msgid="1299663247844969448">"개인"</string>
+ <string name="category_work" msgid="8699184680584175622">"직장"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-ky-rKG/arrays.xml b/packages/SettingsLib/res/values-ky-rKG/arrays.xml
new file mode 100644
index 0000000..207dea1
--- /dev/null
+++ b/packages/SettingsLib/res/values-ky-rKG/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Скандалууда…"</item>
+ <item msgid="8513729475867537913">"Туташууда…"</item>
+ <item msgid="515055375277271756">"Аныктыгы текшерилүүдө…"</item>
+ <item msgid="1943354004029184381">"IP дареги алынууда…"</item>
+ <item msgid="4221763391123233270">"Туташып турат"</item>
+ <item msgid="624838831631122137">"Убактылуу токтотулду"</item>
+ <item msgid="7979680559596111948">"Ажыратылууда…"</item>
+ <item msgid="1634960474403853625">"Ажыратылды"</item>
+ <item msgid="746097431216080650">"Ийгиликсиз"</item>
+ <item msgid="6367044185730295334">"Бөгөттөлгөн"</item>
+ <item msgid="503942654197908005">"Начар байланыштан убактылуу баш тартууда"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Скандалууда…"</item>
+ <item msgid="355508996603873860">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> тармагына туташууда…"</item>
+ <item msgid="554971459996405634">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> менен аныктыгы текшерилүүдө…"</item>
+ <item msgid="7928343808033020343">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> тармагынан IP дареги алынууда…"</item>
+ <item msgid="8937994881315223448">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> тармагына туташты"</item>
+ <item msgid="1330262655415760617">"Убактылуу токтотулду"</item>
+ <item msgid="7698638434317271902">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> тармагынан ажыратылууда…"</item>
+ <item msgid="197508606402264311">"Ажыратылды"</item>
+ <item msgid="8578370891960825148">"Ийгиликсиз"</item>
+ <item msgid="5660739516542454527">"Бөгөттөлгөн"</item>
+ <item msgid="1805837518286731242">"Начар байланыштан убактылуу баш тартууда"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-ky-rKG/strings.xml b/packages/SettingsLib/res/values-ky-rKG/strings.xml
new file mode 100644
index 0000000..946f55b
--- /dev/null
+++ b/packages/SettingsLib/res/values-ky-rKG/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Түйүндөрдү издөө мүмкүн эмес"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Жок"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Сакталды"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Өчүрүлгөн"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP конфигурациясы бузулду"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi туташуусу бузулду"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Аутентификация маселеси бар"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Тейлөө аймагында эмес"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Интернетке кирүү мүмкүнчүлүгү табылган жок, андыктан автоматтык түрдө кайра туташпайт."</string>
+ <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> тарабынан сакталды"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fi жардамчысы аркылуу туташып турат"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s аркылуу жеткиликтүү"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s аркылуу жеткиликтүү"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Туташып турат, Интернет жок"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Ажыратылды"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Ажыратылууда…"</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Туташууда…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Туташып турат"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Жупташтырылууда…"</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Туташып турат (телефониясыз)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Туташып турат (медиасыз)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Байланышта (билдирүү алмашуу жок)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Туташып турат (телефониясыз же медиасыз)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Аудио"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Телефон"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Файл алмашуу"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Киргизүү түзмөгү"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Интернетке мүмкүнчүлүк алуу"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Байланышты бөлүшүү"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Байланышты бөлүшүү үчүн колдонуу"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Интернет байланышын бөлүшүү"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Билдирүү алмашуу"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM картаны пайдалануу мүмкүнчүлүгү"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Медиа аудиого туташты"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Телефон аудиосуна туташты"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Файл өткөрүү серверине туташты"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"MAP\'ка байланышты"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"SAP\'ка туташып турат"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Файл өткөрүү серверине туташкан жок"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Киргизүү түзмөгүнө туташты"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Интернетке мүмкүнчүлүк алуу үчүн түзмөккө туташты"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Жергиликтүү Интернет туташуусу түзмөк менен бөлүшүлүүдө"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Интернетке мүмкүнчүлүк алуу үчүн колдонулсун"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"MAP үчүн колдонуу"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"SIM картаны пайдалануу үчүн колдонуу"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Медиа аудио үчүн колдонуу"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Телефон аудиосу үчүн колдонулсун"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Файл өткөрүү үчүн колдонулсун"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Киргизүү үчүн колдонулсун"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Жупташтыруу"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ЖУПТАШТЫРУУ"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Баш тартуу"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Жупташканда байланыштарыңыз менен чалуу таржымалыңызды пайдалана аласыз."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> менен жупташуу мүмкүн эмес."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"PIN же код туура эмес болгондуктан <xliff:g id="DEVICE_NAME">%1$s</xliff:g> туташуу мүмкүн эмес."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> менен байланышуу мүмкүн эмес."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Жупташтырууну <xliff:g id="DEVICE_NAME">%1$s</xliff:g> четке какты."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wifi өчүк."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wifi туташуусу жок."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wifi: бир таякча."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wifi: эки таякча."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wifi: үч таякча."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Wifi: күчтүү сигнал."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Алынып салынган колдонмолор"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Өчүрүлгөн колдонмолор жана колдонуучулар"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB жалгаштыруу"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Ташыма кошулуу чекити"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth жалгаштыруу"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Жалгаштыруу"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Жалгаштыруу жана ташыма чекит"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Жумуш профили"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Конок"</string>
+ <string name="unknown" msgid="1592123443519355854">"Белгисиз"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Колдонуучу: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Айрым демейкилер коюлду"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Демейкилер коюлган жок"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Кеп синтезаторунун жөндөөлөрү"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Текстти-оозекилөө"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Кеп ылдамдыгы"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Текст айтылчу ылдамдык"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Тил"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Тутум тилин колдонуу"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Тил тандалган жок"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Айтылган текст боюнча тилге тиешелүү үндү коёт"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Үлгүнү угуу"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Кепти синтездөөнүн кыскача көргөзмөсүн ойнотуу"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Үн дайындарын орнотуу"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Кеп синтезине керектүү үн дайындарын орнотуңуз"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Бул кепти синтездөө каражаты бардык айтыла турган текстти, анын ичинде сырсөздөр жана насыя карточкасынын номери сыяктуу жеке маалыматты, топтошу мүмкүн. Ал <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> каражатынан алынат. Бул кепти синтездөө каражаты колдонулсунбу?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Бул тилде текстти-оозекилөө үчүн иштеп турган интернет-байланыш керек."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Бул айтылганды синтездөөнүн мисалы"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Абалкы тилдин абалы"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> толук колдоого алынган"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> желеге туташууну талап кылат"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> колдоого алынган эмес"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Текшерилүүдө…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g> жөндөөлөрдү"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Жарак тууралоолорун ачуу"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Тандалган жарак"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Жалпы"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Өтө жай"</item>
+ <item msgid="4795095314303559268">"Жай"</item>
+ <item msgid="8903157781070679765">"Орточо"</item>
+ <item msgid="164347302621392996">"Ылдам"</item>
+ <item msgid="5794028588101562009">"Ылдамыраак"</item>
+ <item msgid="7163942783888652942">"Абдан ылдам"</item>
+ <item msgid="7831712693748700507">"Тез"</item>
+ <item msgid="5194774745031751806">"Өтө тез"</item>
+ <item msgid="9085102246155045744">"Эң ылдам"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Профиль тандоо"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Жеке"</string>
+ <string name="category_work" msgid="8699184680584175622">"Жумуш"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-lo-rLA/arrays.xml b/packages/SettingsLib/res/values-lo-rLA/arrays.xml
new file mode 100644
index 0000000..799a6ec
--- /dev/null
+++ b/packages/SettingsLib/res/values-lo-rLA/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"ກຳລັງສະແກນ..."</item>
+ <item msgid="8513729475867537913">"ກຳລັງເຊື່ອມຕໍ່..."</item>
+ <item msgid="515055375277271756">"ກຳລັງພິສູດຢືນຢັນ..."</item>
+ <item msgid="1943354004029184381">"ກຳລັງຂໍທີ່ຢູ່ IP..."</item>
+ <item msgid="4221763391123233270">"ເຊື່ອມຕໍ່ແລ້ວ"</item>
+ <item msgid="624838831631122137">"ຖືກລະງັບໄວ້"</item>
+ <item msgid="7979680559596111948">"ກຳລັງຕັດການເຊື່ອມຕໍ່..."</item>
+ <item msgid="1634960474403853625">"ບໍ່ໄດ້ເຊື່ອມຕໍ່"</item>
+ <item msgid="746097431216080650">"ບໍ່ສຳເລັດ"</item>
+ <item msgid="6367044185730295334">"ບລັອກແລ້ວ"</item>
+ <item msgid="503942654197908005">"ຫຼີກເວັ້ນການເຊື່ອມຕໍ່ຊົ່ວຄາວຫາກສັນຍານບໍ່ແຮງພໍ"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"ກຳລັງຊອກຫາສັນຍານ..."</item>
+ <item msgid="355508996603873860">"ກຳລັງເຊື່ອມຕໍ່ <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="554971459996405634">"ກຳລັງກວດສອບສິດທິກັບ <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="7928343808033020343">"ກຳລັງຂໍທີ່ຢູ່ IP ຈາກ <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="8937994881315223448">"ເຊື່ອມຕໍ່ກັບ <xliff:g id="NETWORK_NAME">%1$s</xliff:g> ແລ້ວ"</item>
+ <item msgid="1330262655415760617">"ຖືກລະງັບແລ້ວ"</item>
+ <item msgid="7698638434317271902">"ກຳລັງຕັດການເຊື່ອມຕໍ່ຈາກ <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="197508606402264311">"ປິດການເຊື່ອມຕໍ່ແລ້ວ"</item>
+ <item msgid="8578370891960825148">"ບໍ່ສຳເລັດ"</item>
+ <item msgid="5660739516542454527">"ປິດກັ້ນແລ້ວ"</item>
+ <item msgid="1805837518286731242">"ຫຼີກເວັ້ນການເຊື່ອມຕໍ່ເຄືອຂ່າຍສັນຍານອ່ອນຊົ່ວຄາວ"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-lo-rLA/strings.xml b/packages/SettingsLib/res/values-lo-rLA/strings.xml
new file mode 100644
index 0000000..894d4d9
--- /dev/null
+++ b/packages/SettingsLib/res/values-lo-rLA/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"ບໍ່ສາມາດກວດຫາເຄືອຂ່າຍໄດ້"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"ບໍ່ໃຊ້"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"ບັນທຶກແລ້ວ"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"ປິດການນຳໃຊ້"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"ການຕັ້ງຄ່າ IP ລົ້ມເຫຼວ"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"ການເຊື່ອມຕໍ່ WiFi ລົ້ມເຫຼວ"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"ບັນຫາການພິສູດຢືນຢັນ"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"ບໍ່ຢູ່ໃນໄລຍະທີ່ເຊື່ອມຕໍ່ໄດ້"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"ບໍ່ພົບການເຊື່ອມຕໍ່ອິນເຕີເນັດ, ຈະບໍ່ຖືກເຊື່ອມຕໍ່ໃໝ່ໂດຍອັດຕະໂນມັດ."</string>
+ <string name="saved_network" msgid="4352716707126620811">"ບັນທຶກໂດຍ <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"ເຊື່ອມຕໍ່ຜ່ານ Wi‑Fi ຕົວຊ່ວຍແລ້ວ"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"ເຊື່ອມຕໍ່ຜ່ານ %1$s ແລ້ວ"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"ມີໃຫ້ຜ່ານ %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"ເຊື່ອມຕໍ່ແລ້ວ, ບໍ່ມີອິນເຕີເນັດ"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"ຕັດການເຊື່ອມຕໍ່ແລ້ວ"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"ກຳລັງຢຸດການເຊື່ອມຕໍ່..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"ກຳລັງເຊື່ອມຕໍ່..."</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"ເຊື່ອມຕໍ່ແລ້ວ"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"ກຳລັງຈັບຄູ່..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"ເຊື່ອມຕໍ່ແລ້ວ (ບໍ່ມີໂທລະສັບ)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"ເຊື່ອມຕໍ່ແລ້ວ (ບໍ່ມີສື່)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"ເຊື່ອມຕໍ່ (ບໍ່ມີການເຂົ້າເຖິງຂໍ້ຄວາມ)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"ເຊື່ອມຕໍ່ແລ້ວ (ບໍ່ມີໂທລະສັບ ຫຼືສື່)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"ສຽງ"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"ສຽງໂທລະສັບ"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"ການໂອນຍ້າຍໄຟລ໌"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"ອຸປະກອນປ້ອນຂໍ້ມູນ"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"ການເຂົ້າເຖິງອິນເຕີເນັດ"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"ການແບ່ງປັນລາຍຊື່ຜູ່ຕິດຕໍ່"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"ໃຊ້ສຳລັບການແບ່ງປັນລາຍຊື່ຜູ່ຕິດຕໍ່"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"ການແບ່ງປັນການເຊື່ອມຕໍ່ອິນເຕີເນັດ"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"ການເຂົ້າເຖິງຂໍ້ຄວາມ"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"ການເຂົ້າເຖິງ SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"ເຊື່ອມຕໍ່ກັບສື່ດ້ານສຽງແລ້ວ"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"ເຊື່ອມຕໍ່ກັບສຽງໂທລະສັບແລ້ວ"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ເຊື່ອມຕໍ່ກັບເຊີບເວີໂອນຍ້າຍໄຟລ໌ແລ້ວ"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"ເຊື່ອມຕໍ່ກັບແຜນທີ່ແລ້ວ"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"ເຊື່ອມຕໍ່ກັບ SAP ແລ້ວ"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"ບໍ່ໄດ້ເຊື່ອມຕໍ່ຫາເຊີບເວີໂອນຍ້າຍໄຟລ໌"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"ເຊື່ອມຕໍ່ກັບອຸປະກອນປ້ອນຂໍ້ມູນແລ້ວ"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"ເຊື່ອມຕໍ່ກັບອຸປະກອນເພື່ອເຂົ້າເຖິງອິນເຕີເນັດແລ້ວ"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"ກຳລັງແບ່ງປັນການເຊື່ອມຕໍ່ອິນເຕີເນັດກັບອຸປະກອນ"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"ໃຊ້ເພື່ອເຂົ້າອິນເຕີເນັດ"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"ໃຊ້ສຳລັບແຜນທີ່"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"ການໃຊ້ສໍາລັບການເຂົ້າເຖິງ SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"ໃຊ້ສຳລັບສື່ດ້ານສຽງ"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"ໃຊ້ສຳລັບລະບົບສຽງຂອງໂທລະສັບ"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ໃຊ້ເພື່ອໂອນຍ້າຍໄຟລ໌"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ໃຊ້ສຳລັບການປ້ອນຂໍ້ມູນ"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"ຈັບຄູ່"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ຈັບຄູ່"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"ຍົກເລີກ"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"ການຈັບຄູ່ຈະອະນຸຍາດໃຫ້ເຂົ້າເຖິງລາຍຊື່ຜູ່ຕິດຕໍ່ ແລະ ປະຫວັດການໂທຂອງທ່ານທຸກໆເທື່ອທີ່ເຊື່ອມຕໍ່ກັນ."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"ບໍ່ສາມາດຈັບຄູ່ກັບ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ໄດ້."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"ບໍ່ສາມາດຈັບຄູ່ກັບ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ໄດ້ ເພາະ PIN ຫຼື passkey ບໍ່ຖືກຕ້ອງ."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"ບໍ່ສາມາດຕິດຕໍ່ສື່ສານກັບ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ໄດ້."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"ການຈັບຄູ່ຖືກປະຕິເສດໂດຍ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"WiFi ປິດຢູ່."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"ຕັດການເຊື່ອມຕໍ່ Wi-Fi ແລ້ວ."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"ສັນຍານ Wi-Fi ນຶ່ງຂີດ."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"ສັນຍານ Wi-Fi ສອງຂີດ."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wifi ສາມຂີດ."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"ສັນຍານ Wi-Fi ເຕັມ"</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"ແອັບຯທີ່ຖືກລຶບອອກແລ້ວ"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"ລຶບແອັບຯ ແລະຜູ່ໃຊ້ແລ້ວ"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"ການປ່ອຍສັນຍານຜ່ານ USB"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ຮັອດສະປອດເຄື່ອນທີ່"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ປ່ອຍສັນຍານຜ່ານ Bluetooth"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"ການປ່ອຍສັນຍານ"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"ການປ່ອຍສັນຍານ & ຮັອດສະປອດເຄື່ອນທີ່"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກ"</string>
+ <string name="user_guest" msgid="8475274842845401871">"ແຂກ"</string>
+ <string name="unknown" msgid="1592123443519355854">"ບໍ່ຮູ້ຈັກ"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"ຜູ່ໃຊ້: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"ຕັ້ງບາງຄ່າເລີ່ມຕົ້ນແລ້ວ"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"ບໍ່ໄດ້ຕັ້ງຄ່າເລີ່ມຕົ້ນເທື່ອ"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"ການຕັ້ງຄ່າການປ່ຽນຂໍ້ຄວາມເປັນສຽງເວົ້າ"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"ການປ່ຽນຂໍ້ຄວາມເປັນສຽງ"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"ອັດຕາການເວົ້າ"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"ຄວາມໄວໃນການເວົ້າຂໍ້ຄວາມ"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"ພາສາ"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"ໃຊ້ພາສາຂອງລະບົບ"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"ບໍ່ໄດ້ເລືອກພາສາ"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"ຕັ້ງຄ່າສຽງໃນພາສາທີ່ລະບຸສຳລັບຂໍ້ຄວາມທີ່ເວົ້າ"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"ຟັງຕົວຢ່າງ"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"ສະແດງການສາທິດສັ້ນໆກ່ຽວກັບການສັງເຄາະສຽງ"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"ຕິດຕັ້ງຂໍ້ມູນສຽງ"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"ຕິດຕັ້ງຂໍ້ມູນສຽງທີ່ຕ້ອງໃຊ້ສຳລັບການວິເຄາະສຽງ"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"ລະບົບການສັງເຄາະສຽງນີ້ ອາດເກັບກຳຂໍ້ຄວາມທຸກຢ່າງທີ່ຖືກປ່ຽນເປັນສຽງເວົ້າ ຮວມທັງຂໍ້ມູນສ່ວນໂຕເຊັ່ນ: ລະຫັດຜ່ານ ແລະໝາຍເລກບັດເຄຣດິດນຳ. ມັນມາຈາກລະບົບ <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. ເປີດການນຳໃຊ້ລະບົບສັງເຄາະສຽງໂຕນີ້ບໍ່?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"ພາສານີ້ຕ້ອງໃຊ້ການເຊື່ອມຕໍ່ອິນເຕີເນັດ ເພື່ອການປ່ຽນຂໍ້ຄວາມເປັນສຽງເວົ້າ."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"ນີ້ແມ່ນຕົວຢ່າງການສັງເຄາະສຽງ"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"ສະຖານະພາສາເລີ່ມຕົ້ນ"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"ຮອງຮັບ <xliff:g id="LOCALE">%1$s</xliff:g> ເຕັມຮູບແບບ"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> ຕ້ອງການການເຊື່ອມຕໍ່ອິນເຕີເນັດ"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"ບໍ່ຮອງຮັບ <xliff:g id="LOCALE">%1$s</xliff:g>"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"ກຳລັງກວດສອບ..."</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"ການຕັ້ງຄ່າສຳລັບ <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"ເປີດການຕັ້ງຄ່າລະບົບສະເຄາະສຽງ"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"ລະບົບທີ່ຕ້ອງການ"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"ທົ່ວໄປ"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"ຊ້າຫຼາຍ"</item>
+ <item msgid="4795095314303559268">"ຊ້າ"</item>
+ <item msgid="8903157781070679765">"ປົກກະຕິ"</item>
+ <item msgid="164347302621392996">"ໄວ"</item>
+ <item msgid="5794028588101562009">"ໄວກວ່າ"</item>
+ <item msgid="7163942783888652942">"ໄວຫຼາຍ"</item>
+ <item msgid="7831712693748700507">"ໄວ"</item>
+ <item msgid="5194774745031751806">"ໄວສຸດໆ"</item>
+ <item msgid="9085102246155045744">"ໄວທີ່ສຸດ"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"ເລືອກໂປຣໄຟລ໌"</string>
+ <string name="category_personal" msgid="1299663247844969448">"ສ່ວນໂຕ"</string>
+ <string name="category_work" msgid="8699184680584175622">"ບ່ອນເຮັດວຽກ"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-lt/arrays.xml b/packages/SettingsLib/res/values-lt/arrays.xml
new file mode 100644
index 0000000..8b16385
--- /dev/null
+++ b/packages/SettingsLib/res/values-lt/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Nuskaitoma..."</item>
+ <item msgid="8513729475867537913">"Prisijungiama..."</item>
+ <item msgid="515055375277271756">"Nustatoma tapatybė..."</item>
+ <item msgid="1943354004029184381">"Gaunamas IP adresas..."</item>
+ <item msgid="4221763391123233270">"Prisijungta"</item>
+ <item msgid="624838831631122137">"Pristabdyta"</item>
+ <item msgid="7979680559596111948">"Atjungiama..."</item>
+ <item msgid="1634960474403853625">"Atsijungęs (-usi)"</item>
+ <item msgid="746097431216080650">"Nesėkminga"</item>
+ <item msgid="6367044185730295334">"Užblokuota"</item>
+ <item msgid="503942654197908005">"Laikinai vengiama prasto ryšio"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Nuskaitoma..."</item>
+ <item msgid="355508996603873860">"Prijungiama prie <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="554971459996405634">"Nustatoma tapatybė su <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="7928343808033020343">"Gaunamas IP adresas iš <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="8937994881315223448">"Prijungta prie <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Pristabdyta"</item>
+ <item msgid="7698638434317271902">"Atjungiama nuo <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="197508606402264311">"Atsijungęs (-usi)"</item>
+ <item msgid="8578370891960825148">"Nesėkminga"</item>
+ <item msgid="5660739516542454527">"Užblokuota"</item>
+ <item msgid="1805837518286731242">"Laikinai vengiama prasto ryšio"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
new file mode 100644
index 0000000..132ad1f
--- /dev/null
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nepavyksta nuskaityti tinklų"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Nėra"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Išsaugotas"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Neleidžiama"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP konfigūracijos triktis"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"„Wi-Fi“ ryšio triktis"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Autentifikavimo problema"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Ne diapazone"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Neaptikta jokia prieiga prie interneto, nebus automatiškai iš naujo prisijungta."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Išsaugojo <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Prisijungta naudojant „Wi‑Fi“ pagelbiklį"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Prisijungta naudojant „%1$s“"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Pasiekiama naudojant „%1$s“"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Prisijungta, nėra interneto"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Atsijungęs (-usi)"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Atjungiama..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Prisijungiama..."</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Prisijungta"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Susiejama..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Prijungta (be telefono)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Prijungta (be laikmenos)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Prisijungta (be prieigos prie pranešimų)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Prijungta (be telefono ar laikmenos)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Laikmenos garsas"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Telefono garsas"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Failo perkėlimas"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Įvesties įrenginys"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Prieiga prie interneto"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Kontaktų bendrinimas"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Naudoti kontaktams bendrinti"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Interneto ryšio bendrinimas"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Prieiga prie pranešimų"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM prieiga"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Prijungta prie medijos garso įrašo"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Prijungta prie telefono garso"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Prijungta prie failų perkėlimo serverio"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Prisijungta prie žemėlapio"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Prisijungta prie SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Neprijungta prie failų perkėlimo serverio"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Prisijungta prie įvesties įrenginio."</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Pr. prie įr., kad gaut. pr. prie int."</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Bendr. vt. int. ryš. su įr."</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Naudoti interneto prieigai"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Naudoti žemėlapyje"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Naudoti SIM prieigai"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Naudoti medijos garsui"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Naudoti telefono garso įrašui"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Naudoti failų perkėlimui"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Naudoti įvedant"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Susieti"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"SUSIETI"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Atšaukti"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Susiejus suteikiama prieiga prie kontaktų ir skambučių istorijos, esant prisijungus."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Nepavyko susieti su „<xliff:g id="DEVICE_NAME">%1$s</xliff:g>“."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Nepavyko susieti su „<xliff:g id="DEVICE_NAME">%1$s</xliff:g>“ dėl netinkamo PIN kodo ar prieigos rakto."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Nepavyksta užmegzti ryšio su „<xliff:g id="DEVICE_NAME">%1$s</xliff:g>“."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Susiejimą atmetė <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"„Wi-Fi“ išjungtas."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"„Wi-Fi“ atjungtas."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Viena „Wi-Fi“ signalo juosta."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Dvi „Wi-Fi“ signalo juostos."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Trys „Wi-Fi“ signalo juostos."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Stiprus „Wi-Fi“ signalas."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"„Android“ OS"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Pašalintos programos"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Pašalintos programos ir naudotojai"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB susiejimas"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Perkeliama aktyvioji sritis"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"„Bluetooth“ susiejimas"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Susiejimas"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Susiej. ir perk. vieš. int. pr. tašk."</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Darbo profilis"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Svečias"</string>
+ <string name="unknown" msgid="1592123443519355854">"Nežinomas"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Naudotojas: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Nustatyti kai kurie numatytieji nustatymai"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Nenustatyta jokių numatytųjų nustatymų"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"„Teksto į kalbą“ nustatymai"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"„Teksto į kalbą“ išvestis"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Kalbėjimo greitis"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Greitis, kuriuo sakomas tekstas"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Kalba"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Naudoti sistemos kalbą"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Kalba nepasirinkta"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Nustato konkrečiai kalbai būdingą sakomo teksto balsą"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Klausytis pavyzdžio"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Paleisti trumpą kalbos sintezės demonstraciją"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Įdiegti balso duomenis"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Įdiegti balso duomenis, reikalingus kalbos sintezei"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Šis kalbos sintezės variklis gali rinkti visą įvedamą tekstą, įskaitant asmeninius duomenis, pvz., kredito kortelės numerius, išskyrus slaptažodžius. Tai vyksta dėl variklio <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Įgalinti šio kalbos sintezės variklio naudojimą?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Pasirinkus šią kalbą, teksto vertimo kalba išvesčiai būtinas veikiantis tinklo ryšys."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Tai yra kalbos sintezės pavyzdys"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Numatytoji kalbos būsena"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> visiškai palaikoma"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> būtinas tinklo ryšys"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> nepalaikoma"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Tikrinama…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"„<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>“ nustatymai"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Paleisti variklio nustatymus"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Pageidaujamas variklis"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Bendra"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Labai lėtas"</item>
+ <item msgid="4795095314303559268">"Lėtas"</item>
+ <item msgid="8903157781070679765">"Įprastas"</item>
+ <item msgid="164347302621392996">"Greitas"</item>
+ <item msgid="5794028588101562009">"Greitesnis"</item>
+ <item msgid="7163942783888652942">"Labai greitas"</item>
+ <item msgid="7831712693748700507">"Spartus"</item>
+ <item msgid="5194774745031751806">"Labai spartus"</item>
+ <item msgid="9085102246155045744">"Greičiausias"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Profilio pasirinkimas"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Asmeninės"</string>
+ <string name="category_work" msgid="8699184680584175622">"Darbo"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-lv/arrays.xml b/packages/SettingsLib/res/values-lv/arrays.xml
new file mode 100644
index 0000000..40ba222
--- /dev/null
+++ b/packages/SettingsLib/res/values-lv/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Notiek skenēšana..."</item>
+ <item msgid="8513729475867537913">"Notiek savienojuma izveide…"</item>
+ <item msgid="515055375277271756">"Notiek autentificēšana..."</item>
+ <item msgid="1943354004029184381">"Notiek IP adreses iegūšana…"</item>
+ <item msgid="4221763391123233270">"Izveidots savienojums"</item>
+ <item msgid="624838831631122137">"Atlikts"</item>
+ <item msgid="7979680559596111948">"Notiek atvienošana..."</item>
+ <item msgid="1634960474403853625">"Atvienots"</item>
+ <item msgid="746097431216080650">"Neizdevās"</item>
+ <item msgid="6367044185730295334">"Bloķēts"</item>
+ <item msgid="503942654197908005">"Pagaidām netiek izmantots vājš savienojums."</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Notiek skenēšana..."</item>
+ <item msgid="355508996603873860">"Notiek savienojuma izveide ar <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="554971459996405634">"Notiek autentificēšana ar <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="7928343808033020343">"Notiek IP adreses iegūšana no <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="8937994881315223448">"Savienots ar <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Atlikts"</item>
+ <item msgid="7698638434317271902">"Notiek atvienošana no <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="197508606402264311">"Atvienots"</item>
+ <item msgid="8578370891960825148">"Neizdevās"</item>
+ <item msgid="5660739516542454527">"Bloķēts"</item>
+ <item msgid="1805837518286731242">"Pagaidām netiek izmantots vājš savienojums."</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
new file mode 100644
index 0000000..f3dd7e7
--- /dev/null
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nevar skenēt tīklus"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Nav"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Saglabāts"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Atspējots"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP konfigurācijas kļūme"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi savienojuma kļūme"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Autentificēšanas problēma"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Nav diapazona ietvaros"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Nevar noteikt interneta savienojumu. Savienojums netiks izveidots vēlreiz automātiski."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Saglabāja: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Izveidots savienojums ar Wi‑Fi palīgu"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Savienots, izmantojot %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Pieejams, izmantojot %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Savienots, nav piekļuves internetam"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Atvienots"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Notiek atvienošana..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Notiek savienojuma izveide…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Izveidots savienojums"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Notiek pāra izveide..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Savienojums ir izveidots (nav tālruņa)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Sav. ir izveidots (nav multivides)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Savienots (nav piekļuves ziņojumam)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Sav. ir izveidots (nav tel. vai multiv.)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Multivides audio"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Tālruņa audio"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Failu pārsūtīšana"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Ievades ierīce"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Interneta piekļuve"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Kontaktpersonas informācijas kopīgošana"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Izmantot kontaktpersonas informācijas kopīgošanai"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Interneta savienojuma koplietošana"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Piekļuve ziņojumam"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Piekļuve SIM kartei"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Savienots ar multivides audio"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Savienots ar tālruņa audio"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Savienots ar failu pārsūtīšanas serveri"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Izveidots savienojums ar karti"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Savienots ar SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Nav savienots ar failu pārsūtīšanas serveri"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Izveidots savienojums ar ievades ierīci"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Savien. ar ier., lai nodr. int. piekļ."</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Tiek kopliet. lok. intern. savien. ar ierīci"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Izmantot, lai piekļūtu internetam"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Lietot kartei"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Izmantot, lai piekļūtu SIM kartei"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Izmantot multivides skaņai"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Izmantot tālruņa skaņai"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Izmantot faila pārsūtīšanai"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Izmantot ievadei"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Izveidot pāri"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"SAVIENOT PĀRĪ"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Atcelt"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Veicot savienošanu pārī, šī ierīce savienojuma laikā varēs piekļūt jūsu kontaktpersonām un zvanu vēsturei."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Nevarēja savienot pārī ar ierīci <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Nevarēja savienot pārī ar ierīci <xliff:g id="DEVICE_NAME">%1$s</xliff:g>, jo tika ievadīts nepareizs PIN kods vai nepareiza ieejas atslēga."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Nevar sazināties ar ierīci <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> noraidīja pāra izveidi."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi savienojums izslēgts"</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi savienojums pārtraukts"</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi: viena josla"</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wi-Fi: divas joslas"</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wi-Fi: trīs joslas"</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Pilna piekļuve Wi-Fi signālam"</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Noņemtās lietotnes"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Noņemtās lietotnes un lietotāji"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB saistīšana"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Pārnēsājams tīklājs"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth saistīšana"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Saistīšana"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Piesaiste un pārn. tīklājs"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Darba profils"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Viesis"</string>
+ <string name="unknown" msgid="1592123443519355854">"Nezināms"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Lietotājs: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Iestatīti daži noklusējumi"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Nav iestatīti noklusējumi"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Teksta-runas iestatījumi"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Teksta-runas izvade"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Runas ātrums"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Teksta ierunāšanas ātrums"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Valoda"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Izmantot sistēmas valodu"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Nav atlasīta valoda."</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Runātajam tekstam iestata valodai raksturīgu balsi"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Noklausīties piemēru"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Atskaņot īsu runas sintēzes demonstrāciju"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Instalēt balss datus"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Instalēt runas sintēzei nepieciešamos balss datus"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Lietojot šo runas sintēzes programmu, var tikt apkopots viss ierunātais teksts, tostarp tāda personīgā informācija kā paroles un kredītkaršu numuri. Tā ir no <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> programmas. Vai iespējot šīs runas sintēzes programmas lietošanu?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Lai izmantotu teksta pārveidošanu runā šajā valodā, ir nepieciešams aktīvs tīkla savienojums."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Šis ir runas sintēzes piemērs."</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Noklusējuma valodas statuss"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"Lokalizācija <xliff:g id="LOCALE">%1$s</xliff:g> tiek pilnībā atbalstīta"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"Lokalizācijai <xliff:g id="LOCALE">%1$s</xliff:g> nepieciešams tīkla savienojums."</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"Lokalizācija <xliff:g id="LOCALE">%1$s</xliff:g> netiek atbalstīta"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Notiek pārbaude..."</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Programmas <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g> iestatījumi"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Programmas iestatījumu palaišana"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Vēlamā programma"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Vispārīgi"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Ļoti lēni"</item>
+ <item msgid="4795095314303559268">"Lēni"</item>
+ <item msgid="8903157781070679765">"Normāli"</item>
+ <item msgid="164347302621392996">"Ātri"</item>
+ <item msgid="5794028588101562009">"Ātrāk"</item>
+ <item msgid="7163942783888652942">"Ļoti ātri"</item>
+ <item msgid="7831712693748700507">"Raiti"</item>
+ <item msgid="5194774745031751806">"Ļoti raiti"</item>
+ <item msgid="9085102246155045744">"Visātrāk"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Profila izvēlēšanās"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Privāts"</string>
+ <string name="category_work" msgid="8699184680584175622">"Darba"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-mk-rMK/arrays.xml b/packages/SettingsLib/res/values-mk-rMK/arrays.xml
new file mode 100644
index 0000000..5c51a7d
--- /dev/null
+++ b/packages/SettingsLib/res/values-mk-rMK/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Скенирање..."</item>
+ <item msgid="8513729475867537913">"Се поврзува..."</item>
+ <item msgid="515055375277271756">"Автентицирање..."</item>
+ <item msgid="1943354004029184381">"Добивање ИП адреса..."</item>
+ <item msgid="4221763391123233270">"Поврзана"</item>
+ <item msgid="624838831631122137">"Суспендирана"</item>
+ <item msgid="7979680559596111948">"Се исклучува..."</item>
+ <item msgid="1634960474403853625">"Исклучено"</item>
+ <item msgid="746097431216080650">"Неуспешна"</item>
+ <item msgid="6367044185730295334">"Блокирана"</item>
+ <item msgid="503942654197908005">"Привремено избегнува лоша врска"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Скенирање..."</item>
+ <item msgid="355508996603873860">"Поврзување на <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="554971459996405634">"Автентикација со <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="7928343808033020343">"Добивање ИП адреса од <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="8937994881315223448">"Поврзано на <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Суспендирана"</item>
+ <item msgid="7698638434317271902">"Исклучување од <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="197508606402264311">"Исклучено"</item>
+ <item msgid="8578370891960825148">"Неуспешна"</item>
+ <item msgid="5660739516542454527">"Блокирано"</item>
+ <item msgid="1805837518286731242">"Привремено избегнува лоша врска"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-mk-rMK/strings.xml b/packages/SettingsLib/res/values-mk-rMK/strings.xml
new file mode 100644
index 0000000..a6f45d2
--- /dev/null
+++ b/packages/SettingsLib/res/values-mk-rMK/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Не може да скенира за мрежи"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Ниедна"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Зачувано"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Оневозможено"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Конфигурирањето ИП не успеа"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Поврзувањето преку Wi-Fi не успеа"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Проблем со автентикација"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Надвор од опсег"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Не е откриен пристап до интернет, нема автоматски повторно да се поврзете."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Зачувано од <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Поврзано преку помошник за Wi-Fismile"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Поврзано преку %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Достапно преку %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Поврзана, нема интернет"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Исклучено"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Се исклучува..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Се поврзува..."</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Поврзани"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Поврзување..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Поврзани (без телефон)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Поврзани (без медиуми)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Поврзано (без порака за пристап)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Поврзан (без телефон или медиуми)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Аудио на медиуми"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Аудио на телефон"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Пренос на датотека"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Влезен уред"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Пристап на интернет"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Споделување контакти"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Користи за споделување контакти"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Споделување конекција на интернет"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Порака за пристап"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Пристап до СИМ"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Поврзан со аудио на медиуми"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Поврзан со аудио на телефон"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Поврзан со сервер за пренос на датотеки"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Поврзано со карта"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Поврзано со SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Не е поврзан со сервер за пренос на датотеки"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Поврзан со влезен уред"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Поврзан со уред за пристап на интернет"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Споделување локална конекција на интернет со уред"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Користи за пристап на интернет"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Користи за карта"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Користете се пристап до СИМ"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Користи за аудио на медиуми"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Користи за аудио на телефон"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Користи за пренос на датотеки"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Користи за внес"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Спари"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"СПАРИ"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Откажи"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Кога е поврзано, спарувањето одобрува пристап до контактите и историјата на повиците."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Не можеше да се спари со <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Не можеше да се спари со <xliff:g id="DEVICE_NAME">%1$s</xliff:g> поради погрешен ПИН или лозинка."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Не може да комуницира со <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Спарувањето е одбиено од <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi е исклучено."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi е исклучено."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Една црта на Wi-Fi."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Две црти на Wi-Fi."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Три црти на Wi-Fi."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Полн сигнал на Wi-Fi."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Оперативен систем Android"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Отстранети апликации"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Отстранети апликации и корисници"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"Поврзување со УСБ"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Преносл. точка на пристап"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Поврзување со Bluetooth"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Поврзување"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Поврзување и пренослива точка на пристап"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Работен профил"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Гостин"</string>
+ <string name="unknown" msgid="1592123443519355854">"Непознато"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Корисник: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Поставени се некои стандардни вредности"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Нема поставено стандардни вредности"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Подесувања на текст-во-говор"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Излез текст-во-говор"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Брзина на говор"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Брзина со која се кажува текстот"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Јазик"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Користете системски јазик"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Јазикот не е избран"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Поставува глас специфичен за јазикот за говорниот текст"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Слушни пример"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Пушти краток приказ на синтеза на говор"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Инсталирај гласовни податоци"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Инсталирај ги гласовните податоци потребни за синтеза на говор"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Овој софтвер за синтеза на говор може да го собере сиот текст што ќе се говори, вклучувајќи и лични податоци како што се лозинки и броеви на кредитни картички. Тоа го прави апликацијата <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Употреби го овој софтвер за синтеза на говор?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"За овој јазик е потребно поврување со мрежа што функционира на излез за текст-во-говор."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Ова е пример на синтеза на говор"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Статус на стандарден јазик"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> е целосно поддржан"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> бара мрежно поврзување"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> не е поддржано"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Се проверува..."</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Подесувања на <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Стартувај подесувања на софтвер"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Претпочитан софтвер"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Општо"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Многу бавно"</item>
+ <item msgid="4795095314303559268">"Бавно"</item>
+ <item msgid="8903157781070679765">"Нормално"</item>
+ <item msgid="164347302621392996">"Брзо"</item>
+ <item msgid="5794028588101562009">"Побрзо"</item>
+ <item msgid="7163942783888652942">"Многу брзо"</item>
+ <item msgid="7831712693748700507">"Рапидно"</item>
+ <item msgid="5194774745031751806">"Многу рапидно"</item>
+ <item msgid="9085102246155045744">"Најбрзо"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Изберете профил"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Лични"</string>
+ <string name="category_work" msgid="8699184680584175622">"Работа"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-ml-rIN/arrays.xml b/packages/SettingsLib/res/values-ml-rIN/arrays.xml
new file mode 100644
index 0000000..4d3261a
--- /dev/null
+++ b/packages/SettingsLib/res/values-ml-rIN/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"സ്കാൻചെയ്യുന്നു..."</item>
+ <item msgid="8513729475867537913">"കണക്റ്റുചെയ്യുന്നു..."</item>
+ <item msgid="515055375277271756">"പ്രാമാണീകരിക്കുന്നു..."</item>
+ <item msgid="1943354004029184381">"IP വിലാസം നേടുന്നു..."</item>
+ <item msgid="4221763391123233270">"കണക്റ്റുചെയ്തു"</item>
+ <item msgid="624838831631122137">"താൽക്കാലികമായി നിർത്തി"</item>
+ <item msgid="7979680559596111948">"വിച്ഛേദിക്കുന്നു..."</item>
+ <item msgid="1634960474403853625">"വിച്ഛേദിച്ചു"</item>
+ <item msgid="746097431216080650">"പരാജയപ്പെട്ടു"</item>
+ <item msgid="6367044185730295334">"തടഞ്ഞിരിക്കുന്നു"</item>
+ <item msgid="503942654197908005">"മോശം കണക്ഷൻ താൽക്കാലികമായി ഒഴിവാക്കുന്നു"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"സ്കാൻചെയ്യുന്നു..."</item>
+ <item msgid="355508996603873860">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> എന്നതിലേക്ക് കണക്റ്റുചെയ്യുന്നു..."</item>
+ <item msgid="554971459996405634">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> മുഖേന പ്രമാണീകരിക്കുന്നു..."</item>
+ <item msgid="7928343808033020343">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> എന്നതിൽ നിന്ന് IP വിലാസം നേടുന്നു..."</item>
+ <item msgid="8937994881315223448">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> എന്നതിൽ കണക്റ്റുചെയ്തു"</item>
+ <item msgid="1330262655415760617">"താൽക്കാലികമായി നിർത്തി"</item>
+ <item msgid="7698638434317271902">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> എന്നതിൽ നിന്ന് വിച്ഛേദിക്കുന്നു..."</item>
+ <item msgid="197508606402264311">"വിച്ഛേദിച്ചു"</item>
+ <item msgid="8578370891960825148">"പരാജയപ്പെട്ടു"</item>
+ <item msgid="5660739516542454527">"തടഞ്ഞിരിക്കുന്നു"</item>
+ <item msgid="1805837518286731242">"മോശം കണക്ഷൻ താൽക്കാലികമായി ഒഴിവാക്കുന്നു"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-ml-rIN/strings.xml b/packages/SettingsLib/res/values-ml-rIN/strings.xml
new file mode 100644
index 0000000..16e33d1
--- /dev/null
+++ b/packages/SettingsLib/res/values-ml-rIN/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"നെറ്റ്വർക്കുകൾക്കായി സ്കാൻ ചെയ്യാനായില്ല"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"ഒന്നുമില്ല"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"സംരക്ഷിച്ചു"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"പ്രവർത്തനരഹിതമാക്കി"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP കോൺഫിഗറേഷൻ പരാജയം"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi കണക്ഷൻ പരാജയം"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"ആധികാരികമാക്കുന്നതിലെ പ്രശ്നം"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"പരിധിയിലില്ല"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"ഇന്റർനെറ്റ് ആക്സസ്സൊന്നും കണ്ടെത്താത്തതിനാൽ സ്വയം വീണ്ടും കണക്റ്റുചെയ്യില്ല."</string>
+ <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> സംരക്ഷിച്ചത്"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"വൈഫൈ അസിസ്റ്റന്റ് മുഖേന കണക്റ്റുചെയ്തു"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s വഴി ബന്ധിപ്പിച്ചു"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s വഴി ലഭ്യം"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"കണക്റ്റുചെയ്തിരിക്കുന്നു, ഇന്റർനെറ്റില്ല"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"വിച്ഛേദിച്ചു"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"വിച്ഛേദിക്കുന്നു..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"കണക്റ്റുചെയ്യുന്നു..."</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"കണക്റ്റുചെയ്തു"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"ജോടിയാക്കുന്നു…"</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"കണക്റ്റുചെയ്തു (ഫോൺ ഇല്ല)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"കണക്റ്റുചെയ്തു (മീഡിയ ഇല്ല)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"കണക്റ്റുചെയ്തു (സന്ദേശ ആക്സസ്സില്ല)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"കണക്റ്റുചെയ്തു (ഫോണോ മീഡിയയോ അല്ല)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"മീഡിയ ഓഡിയോ"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"ഫോൺ ഓഡിയോ"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"ഫയൽ കൈമാറൽ"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"ഇൻപുട്ട് ഉപകരണം"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"ഇന്റർനെറ്റ് ആക്സസ്സ്"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"കോൺടാക്റ്റ് പങ്കിടൽ"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"കോൺടാക്റ്റ് പങ്കിടലിനായി ഉപയോഗിക്കുക"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"ഇന്റർനെറ്റ് കണക്ഷൻ പങ്കിടൽ"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"ആക്സസ്സ് നിയന്ത്രിക്കുക"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM ആക്സസ്"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"മീഡിയ ഓഡിയോയിലേക്ക് കണക്റ്റുചെയ്തു"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"ഫോൺ ഓഡിയോയിൽ കണക്റ്റുചെയ്തു"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ഫയൽ കൈമാറ്റ സെർവറിലേക്ക് കണക്റ്റുചെയ്തു"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"മാപ്പിലേക്ക് കണക്റ്റുചെയ്തു"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"SAP-യിലേക്ക് ബന്ധിപ്പിച്ചു"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"ഫയൽ കൈമാറ്റ സെർവറിൽ കണക്റ്റുചെയ്തിട്ടില്ല"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"ഇൻപുട്ട് ഉപകരണത്തിൽ കണക്റ്റുചെയ്തു"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"ഇന്റർനെറ്റ് ആക്സസ്സിനായി ഉപകരണത്തിൽ കണക്റ്റുചെയ്തു"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"ഉപകരണവുമായി പ്രദേശിക ഇന്റർനെറ്റ് കണക്ഷൻ പങ്കിടുന്നു"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"ഇന്റർനെറ്റ് ആക്സസ്സിനായി ഉപയോഗിക്കുന്നു"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"മാപ്പിനായി ഉപയോഗിക്കുക"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"SIM ആക്സസിന് ഉപയോഗിക്കുക"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"മീഡിയ ഓഡിയോയ്ക്കായി ഉപയോഗിക്കുക"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"ഫോൺ ഓഡിയോയ്ക്കായി ഉപയോഗിക്കുക"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ഫയൽ കൈമാറ്റത്തിനായി ഉപയോഗിക്കുന്നു"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ഇൻപുട്ടിനായി ഉപയോഗിക്കുക"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"ജോടിയാക്കുക"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ജോടിയാക്കുക"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"റദ്ദാക്കുക"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"കണക്റ്റുചെയ്തിരിക്കുമ്പോൾ, ജോടിയാക്കുന്നത് നിങ്ങളുടെ കോൺടാക്റ്റുകളിലേക്കും കോൾ ചരിത്രത്തിലേക്കും ആക്സസ്സ് അനുവദിക്കുന്നു."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> എന്നതുമായി ജോടിയാക്കാനായില്ല."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"ഒരു തെറ്റായ പിൻ അല്ലെങ്കിൽ പാസ്കീ കാരണം <xliff:g id="DEVICE_NAME">%1$s</xliff:g> എന്നതുമായി ജോടിയാക്കാനായില്ല."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> എന്നതുമായി ആശയവിനിമയം നടത്താനായില്ല."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>, ജോടിയാക്കൽ നിരസിച്ചു."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"വൈഫൈ ഓഫാണ്."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"വൈഫൈ വിച്ഛേദിച്ചു."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"വൈഫൈ സിഗ്നൽ ഒരു ബാർ."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"വൈഫൈ സിഗ്നൽ രണ്ട് ബാറുകൾ."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"വൈഫൈ സിഗ്നൽ മൂന്ന് ബാറുകൾ."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"വൈഫൈ മികച്ച സിഗ്നൽ."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"നീക്കംചെയ്ത അപ്ലിക്കേഷനുകൾ"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"നീക്കംചെയ്ത അപ്ലിക്കേഷനുകളും ഉപയോക്താക്കളും"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB ടെതറിംഗ്"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"പോർട്ടബിൾ ഹോട്ട്സ്പോട്ട്"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ബ്ലൂടൂത്ത് ടെതറിംഗ്"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"ടെതറിംഗ്"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"ടെതറിംഗും പോർട്ടബിൾ ഹോട്ട്സ്പോട്ടും"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"ഔദ്യോഗിക പ്രൊഫൈൽ"</string>
+ <string name="user_guest" msgid="8475274842845401871">"അതിഥി"</string>
+ <string name="unknown" msgid="1592123443519355854">"അജ്ഞാതം"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"ഉപയോക്താവ്: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"സ്ഥിരമായ ചിലവ സജ്ജീകരിച്ചു"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"സ്ഥിരമായൊന്നും സജ്ജീകരിച്ചിട്ടില്ല"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"ടെക്സ്റ്റ്-ടു-സ്പീച്ച് ക്രമീകരണങ്ങൾ"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"ടെക്സ്റ്റ്-ടു-സ്പീച്ച് ഔട്ട്പുട്ട്"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"വായന നിരക്ക്"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"വാചകം പറയുന്ന വേഗത"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"ഭാഷ"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"സിസ്റ്റം ഭാഷ ഉപയോഗിക്കുക"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"ഭാഷ തിരഞ്ഞെടുത്തിട്ടില്ല"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"സംഭാഷണ വാചകത്തിന് ഭാഷാ-നിർദ്ദിഷ്ട ശബ്ദം സജ്ജമാക്കുന്നു"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"ഒരു ഉദാഹരണം കേൾക്കുക"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"സംഭാഷണത്തിന്റെ ഒരു ഹ്രസ്വ പ്രകടനം പ്ലേ ചെയ്യുക"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"വോയ്സ് ഡാറ്റ ഇൻസ്റ്റാളുചെയ്യുക"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"സംഭാഷണ സിന്തസിസ്സിന് ആവശ്യമായ വോയ്സ് ഡാറ്റ ഇൻസ്റ്റാൾ ചെയ്യുക"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"പാസ്വേഡുകൾ, ക്രെഡിറ്റ് കാർഡ് നമ്പറുകൾ എന്നിവ പോലുള്ള വ്യക്തിഗതമായ ഡാറ്റയുൾപ്പെടെ നിങ്ങൾ സംസാരിക്കുന്ന എല്ലാ വാചകവും ഈ സംഭാഷണ സിന്തസിസ് എഞ്ചിന് ശേഖരിക്കാനായേക്കും. ഇത് <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> എഞ്ചിനിൽ നിന്നും വരുന്നു. ഈ സംഭാഷണ സിന്തസിസ് എഞ്ചിൻ ഉപയോഗിക്കുന്നത് പ്രവർത്തനക്ഷമമാക്കണോ?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"ടെക്സ്റ്റ്-ടു-സ്പീച്ച് ഔട്ട്പുട്ടിനായി ഈ ഭാഷയ്ക്ക് പ്രവർത്തിക്കുന്ന ഒരു നെറ്റ്വർക്ക് കണക്ഷൻ ആവശ്യമാണ്."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"സംഭാഷണം ഒരുമിച്ചുചേർക്കുന്നതിനുള്ള ഒരുദാഹരണമാണിത്"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"സ്ഥിര ഭാഷാ സ്ഥിതിവിവരക്കണക്കുകൾ"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> ഭാഷയെ പൂർണ്ണമായും പിന്തുണയ്ക്കുന്നു"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> ഭാഷയ്ക്ക് നെറ്റ്വർക്ക് കണക്ഷൻ ആവശ്യമാണ്"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> ഭാഷയെ പിന്തുണയ്ക്കുന്നില്ല"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"പരിശോധിക്കുന്നു…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g> എന്നതിനായുള്ള ക്രമീകരണങ്ങൾ"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"എഞ്ചിൻ ക്രമീകരണങ്ങൾ സമാരംഭിക്കുക"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"തിരഞ്ഞെടുത്ത എഞ്ചിൻ"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"പൊതുവായ കാര്യങ്ങൾ"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"വളരെ കുറഞ്ഞ വേഗത്തിൽ"</item>
+ <item msgid="4795095314303559268">"കുറഞ്ഞ വേഗത്തിൽ"</item>
+ <item msgid="8903157781070679765">"സാധാരണ വേഗത്തിൽ"</item>
+ <item msgid="164347302621392996">"വേഗത്തിൽ"</item>
+ <item msgid="5794028588101562009">"കൂടുതൽ വേഗത്തിൽ"</item>
+ <item msgid="7163942783888652942">"വളരെ വേഗത്തിൽ"</item>
+ <item msgid="7831712693748700507">"ശീഘ്രം"</item>
+ <item msgid="5194774745031751806">"വളരെ ശീഘ്രം"</item>
+ <item msgid="9085102246155045744">"ഏറ്റവും വേഗത്തിൽ"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"പ്രൊഫൈൽ തിരഞ്ഞെടുക്കുക"</string>
+ <string name="category_personal" msgid="1299663247844969448">"വ്യക്തിഗതം"</string>
+ <string name="category_work" msgid="8699184680584175622">"ഔദ്യോഗികം"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-mn-rMN/arrays.xml b/packages/SettingsLib/res/values-mn-rMN/arrays.xml
new file mode 100644
index 0000000..2849b51
--- /dev/null
+++ b/packages/SettingsLib/res/values-mn-rMN/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Скан хийж байна…"</item>
+ <item msgid="8513729475867537913">"Холбогдож байна..."</item>
+ <item msgid="515055375277271756">"Гэрчлэж байна"</item>
+ <item msgid="1943354004029184381">"IP хаягийг авч байна…"</item>
+ <item msgid="4221763391123233270">"Холбогдсон"</item>
+ <item msgid="624838831631122137">"Түр хаасан"</item>
+ <item msgid="7979680559596111948">"Салгаж байна…"</item>
+ <item msgid="1634960474403853625">"Салгагдсан"</item>
+ <item msgid="746097431216080650">"Амжилтгүй"</item>
+ <item msgid="6367044185730295334">"Хориглогдсон"</item>
+ <item msgid="503942654197908005">"Муу холболтоос түр зайлсхийж байна"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Скан хийж байна…"</item>
+ <item msgid="355508996603873860">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> руу холбогдож байна…"</item>
+ <item msgid="554971459996405634">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>-тай гэрчилж байна…"</item>
+ <item msgid="7928343808033020343">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>-с IP хаягийг авч байна…"</item>
+ <item msgid="8937994881315223448">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> руу холбогдсон"</item>
+ <item msgid="1330262655415760617">"Түр хаасан"</item>
+ <item msgid="7698638434317271902">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>-с салгагдаж байна…"</item>
+ <item msgid="197508606402264311">"Салгагдсан"</item>
+ <item msgid="8578370891960825148">"Амжилтгүй"</item>
+ <item msgid="5660739516542454527">"Хориглогдсон"</item>
+ <item msgid="1805837518286731242">"Муу холболтоос түр зайлсхийж байна"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-mn-rMN/strings.xml b/packages/SettingsLib/res/values-mn-rMN/strings.xml
new file mode 100644
index 0000000..53c71a4
--- /dev/null
+++ b/packages/SettingsLib/res/values-mn-rMN/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Сүлжээнүүдийг скан хийх боломжгүй"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Байхгүй"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Хадгалагдсан"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Идэвхгүйжүүлсэн"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP тохируулга амжилтгүй"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi холболт амжилтгүй"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Гэрчлэлийн асуудал"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Хүрээнд байхгүй"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Интернэт холболт илэрсэнгүй, автоматаар дахин холболт хийгдэхгүй"</string>
+ <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> хадгалсан"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Wi-Fi туслагчаар дамжуулан холбогдлоо"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s-р холбогдсон"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s-р боломжтой"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Холбогдсон, интернэт байхгүй байна"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Салгагдсан"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Салгаж байна…"</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Холбогдож байна..."</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Холбогдсон"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Хослуулж байна…"</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Холбогдсон (утас байхгүй)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Холбогдсон (медиа байхгүй)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Холбогдсон (зурвас хандалт байхгүй)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Холбогдсон (утас буюу медиа байхгүй)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Медиа аудио"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Утасны аудио"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Файл дамжуулалт"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Оруулах төхөөрөмж"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Интернэт хандалт"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Харилцагч хуваалцах"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Харилцагч хуваалцахад ашиглах"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Интернэт холболтыг хуваалцах"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Зурвас хандалт"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM Хандалт"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Медиа аудиод холбогдсон"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Утасны аудид холбогдсон"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Файл дамжуулах серверт холбогдсон"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Газрын зурагтай холбогдсон"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"SAP-д холбогдсон"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Файл дамжуулах серверт холбогдоогүй"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Оруулах төхөөрөмжтэй холбогдсон"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Интернетэд хандахын тулд төхөөрөмжтэй холбогдсон"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Төхөөрөмжтэй локал Интернет холболтыг хуваалцаж байна"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Интернет хандалтанд ашиглах"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Газрын зурагт ашиглах"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"SIM хандалтад ашиглах"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Медиа аудиод ашиглах"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Утасны аудиод ашиглах"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Файл дамжуулахад ашиглах"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Оруулахад ашиглах"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Хослуулах"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ХОСЛУУЛАХ"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Цуцлах"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Хослуулснаар холбогдсон үед таны харилцагчид болон дуудлагын түүхэд хандах боломжтой."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>-тай хослуулж чадсангүй."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Буруу PIN эсхүл дамжих түлхүүрээс шалтгаалан <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-тай хослуулж чадсангүй."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>-тай холбоо барих боломжгүй."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Хослуулахаас <xliff:g id="DEVICE_NAME">%1$s</xliff:g> татгалзсан."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wifi унтраалттай байна."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wifi холбогдоогүй байна."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wifi сүлжээний дохио нэг баганатай байна."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wifi сүлжээний дохио хоёр баганатай байна."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wifi сүлжээний дохио гурван баганатай байна."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Wifi-н дохио дүүрэн байна."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Андройд OS"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Арилгасан апп-ууд"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Арилгасан апп-ууд болон хэрэглэгчид"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB модем болгох"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Зөөврийн сүлжээний цэг"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Блютүүт модем болгох"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Модем болгох"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Модем болгох & зөөврийн сүлжээний цэг"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Ажлын профайл"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Зочин"</string>
+ <string name="unknown" msgid="1592123443519355854">"Тодорхойгүй"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Хэрэглэгч: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Зарим үндсэн тохиргоонуудыг суулгасан"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Ямар ч үндсэн тохиргоог суулгаагүй байна"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Текст-ярианы тохиргоо"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Текст-яриа гаргах"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Ярианы түвшин"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Текстийг унших хурд"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Хэл"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Системийн хэлийг ашиглах"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Хэл сонгогдоогүй байна"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Ярианы текстүүдэд тухайн хэлэнд зориулсан хоолойг тохируулна"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Жишээ сонсох"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Яриа үүсгэх талаар богинохон жишээг тоглуулах"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Хоолойн өгөгдлийг суулгах"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Яриа үүсгэхэд шаардлагатай дууны өгөгдлийг суулгах"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Энэ яриа үүсгүүр нь нууц үг, зээлийн картын дугаар гэх мэт таны хувийн мэдээллийг оруулан унших бүх текстийг цуглуулах боломжтой. Үүнийг <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> үүсгүүрээс нийлүүлдэг. Энэ яриа үүсгүүрийн ашиглалтыг идэвхжүүлэх үү?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Энэ хэл нь текстээс дуунд хөрвүүлэхэд ажлын сүлжээний холболтыг шаарддаг."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Энэ бол яриа үүсгэх жишээ юм."</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Үндсэн хэлний статус"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> бүрэн дэмжигдсэн"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> сүлжээнд хrequires network connection"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> дэмжигдээгүй байна"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Шалгаж байна..."</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>-н тохиргоо"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Үүсгүүрийн тохиргоог ажиллуулах"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Давуу үүсгүүр"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Ерөнхий"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Маш удаан"</item>
+ <item msgid="4795095314303559268">"Удаан"</item>
+ <item msgid="8903157781070679765">"Хэвийн"</item>
+ <item msgid="164347302621392996">"Хурдан"</item>
+ <item msgid="5794028588101562009">"Илүү хурдан"</item>
+ <item msgid="7163942783888652942">"Маш хурдан"</item>
+ <item msgid="7831712693748700507">"Түргэн"</item>
+ <item msgid="5194774745031751806">"Маш түргэн"</item>
+ <item msgid="9085102246155045744">"Хамгийн хурдан"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Профайлаа сонгоно уу"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Хувийн"</string>
+ <string name="category_work" msgid="8699184680584175622">"Ажил"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-mr-rIN/arrays.xml b/packages/SettingsLib/res/values-mr-rIN/arrays.xml
new file mode 100644
index 0000000..db21ccc
--- /dev/null
+++ b/packages/SettingsLib/res/values-mr-rIN/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"स्कॅन करत आहे…"</item>
+ <item msgid="8513729475867537913">"कनेक्ट करत आहे..."</item>
+ <item msgid="515055375277271756">"प्रमाणीकरण करत आहे…"</item>
+ <item msgid="1943354004029184381">"IP पत्ता प्राप्त करत आहे…"</item>
+ <item msgid="4221763391123233270">"कनेक्ट केले"</item>
+ <item msgid="624838831631122137">"निलंबित"</item>
+ <item msgid="7979680559596111948">"डिस्कनेक्ट करत आहे..."</item>
+ <item msgid="1634960474403853625">"डिस्कनेक्ट केले"</item>
+ <item msgid="746097431216080650">"अयशस्वी"</item>
+ <item msgid="6367044185730295334">"अवरोधित"</item>
+ <item msgid="503942654197908005">"तात्पुरते खराब कनेक्शन टाळत आहे"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"स्कॅन करत आहे…"</item>
+ <item msgid="355508996603873860">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> वर कनेक्ट करत आहे…"</item>
+ <item msgid="554971459996405634">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> सह प्रमाणीकरण करत आहे…"</item>
+ <item msgid="7928343808033020343">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> वरून IP पत्ता प्राप्त करत आहे…"</item>
+ <item msgid="8937994881315223448">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> वर कनेक्ट केले आहे"</item>
+ <item msgid="1330262655415760617">"निलंबित"</item>
+ <item msgid="7698638434317271902">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> वरून डिस्कनेक्ट करत आहे…"</item>
+ <item msgid="197508606402264311">"डिस्कनेक्ट केले"</item>
+ <item msgid="8578370891960825148">"अयशस्वी"</item>
+ <item msgid="5660739516542454527">"अवरोधित"</item>
+ <item msgid="1805837518286731242">"तात्पुरते खराब कनेक्शन टाळत आहे"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-mr-rIN/strings.xml b/packages/SettingsLib/res/values-mr-rIN/strings.xml
new file mode 100644
index 0000000..04d9d78
--- /dev/null
+++ b/packages/SettingsLib/res/values-mr-rIN/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"नेटवर्कसाठी स्कॅन करू शकत नाही"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"काहीही नाही"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"जतन केले"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"अक्षम"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP कॉन्फिगरेशन अयशस्वी"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi कनेक्शन अयशस्वी"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"प्रमाणीकरण समस्या"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"परिक्षेत्रामध्ये नाही"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"कोणताही इंटरनेट प्रवेश आढळला नाही, स्वयंचलितपणे रीकनेक्ट करणार नाही."</string>
+ <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> द्वारे जतन केले"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fi सहाय्यक द्वारे कनेक्ट केले"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s द्वारे कनेक्ट केले"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s द्वारे उपलब्ध"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"कनेक्ट केले, इंटरनेट नाही"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"डिस्कनेक्ट केले"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"डिस्कनेक्ट करत आहे..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"कनेक्ट करीत आहे..."</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"कनेक्ट केले"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"जोडत आहे…"</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"कनेक्ट केले (फोन नाही)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"कनेक्ट केले (मीडिया नाही)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"कनेक्ट केलेले आहे (कोणत्याही संदेशामध्ये प्रवेश नाही)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"कनेक्ट केले (फोन किंवा मीडिया नाही)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"मीडिया ऑडिओ"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"फोन ऑडिओ"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"फाइल स्थानांतरण"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"इनपुट डिव्हाइस"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"इंटरनेट प्रवेश"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"संपर्क सामायिकरण"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"संपर्क सामायिकरणासाठी वापरा"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"इंटरनेट कनेक्शन सामायिकरण"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"संदेशात प्रवेश"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"सिम प्रवेश"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"मीडिया ऑडिओवर कनेक्ट केले"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"फोन ऑडिओ वर कनेक्ट केले"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"फाईल स्थानांतर सर्व्हरवर कनेक्ट केले"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"नकाशाशी कनेक्ट केले"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"SAP शी कनेक्ट केले"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"फाइल स्थानांतर सर्व्हरशी कनेक्ट केले नाही"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"इनपुट डिव्हाइसवर कनेक्ट केले"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"इंटरनेट प्रवेशासाठी डिव्हाइसवर कनेक्ट केले"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"डिव्हाइससह स्थानिक इंटरनेट कनेक्शन सामायिक करत आहे"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"इंटरनेट प्रवेशासाठी वापरा"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"नकाशासाठी वापरा"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"SIM प्रवेशासाठी वापरा"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"मीडिया ऑडिओसाठी वापरा"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"फोन ऑडिओसाठी वापरा"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"फाईल स्थानांतरणासाठी वापरा"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"इनपुट साठी वापरा"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"जोडा"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"जोडा"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"रद्द करा"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"कनेक्ट केल्यावर जोडणी आपले संपर्क आणि कॉल इतिहास यावरील प्रवेशास मंजूरी देते."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> शी जोडू शकलो नाही."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"अयोग्य पिन किंवा पासकीमुळे <xliff:g id="DEVICE_NAME">%1$s</xliff:g> सह जोडू शकलो नाही."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> शी संप्रेषण करू शकत नाही."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> द्वारे जोडणी नाकारली."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"वाय फाय बंद."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"वाय फाय डिस्कनेक्ट झाले."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"वाय फाय एक बार."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"वाय फाय दोन बार."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"वाय फाय तीन बार."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"वाय फाय सिग्नल संपूर्ण आहे."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"काढलेले अॅप्स"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"काढलेले अॅप्स आणि वापरकर्ते"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB टेदरिंग"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"पोर्टेबल हॉटस्पॉट"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ब्लूटुथ टेदरिंग"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"टिथरिंग"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"टेदरिंग आणि पोर्टेबल हॉटस्पॉट"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"कार्य प्रोफाईल"</string>
+ <string name="user_guest" msgid="8475274842845401871">"अतिथी"</string>
+ <string name="unknown" msgid="1592123443519355854">"अज्ञात"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"वापरकर्ता: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"काही डीफॉल्ट सेट केले"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"कोणतेही डीफॉल्ट सेट केले नाही"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"टेक्स्ट-टू-स्पीच सेटिंग्ज"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"टेक्स्ट-टू-स्पीच आउटपुट"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"बोलण्याचा रेट"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"ज्या गतीने मजकूर बोलला जातो ती"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"भाषा"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"सिस्टम भाषा वापरा"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"भाषा निवडलेली नाही"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"बोललेल्या मजकुरासाठी भाषा-विशिष्ट आवाज सेट करते"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"उदाहरण ऐका"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"उच्चार संश्लेषणाचे एक छोटेसे प्रात्यक्षिक प्ले करा"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"व्हॉइस डेटा स्थापित करा"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"उच्चार संश्लेषणासाठी आवश्यक आवाज डेटा स्थापित करा"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"हे उच्चार संश्लेषण इंजिन संकेतशब्द आणि क्रेडिट कार्ड नंबर यासारख्या वैयक्तिक मजकुरासह, बोलला जाणारा सर्व मजकूर संकलित करण्यात सक्षम होऊ शकते. हे <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> इंजिनवरून येते. या उच्चार संश्लेषण इंजिनचा वापर सक्षम करायचा?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"या भाषेस टेक्स्ट टू स्पीचसाठी एका नेटवर्क कनेक्शनची आवश्यकता आहे."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"हे उच्चार संश्लेषणाचे एक उदाहरण आहे"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"डीफॉल्ट भाषा स्थिती"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> पूर्णपणे समर्थित आहे"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> ला नेटवर्क कनेक्शनची आवश्यकता आहे"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> समर्थित नाही"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"तपासत आहे..."</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g> साठी सेटिंग्ज"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"इंजिन सेटिंग्ज लाँच करा"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"प्राधान्यकृत इंजिन"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"सामान्य"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"खूप धीमे"</item>
+ <item msgid="4795095314303559268">"धीमे"</item>
+ <item msgid="8903157781070679765">"सामान्य"</item>
+ <item msgid="164347302621392996">"थोडे जलद"</item>
+ <item msgid="5794028588101562009">"जलद"</item>
+ <item msgid="7163942783888652942">"खूप जलद"</item>
+ <item msgid="7831712693748700507">"शीघ्र"</item>
+ <item msgid="5194774745031751806">"अतिशीघ्र"</item>
+ <item msgid="9085102246155045744">"जलद"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"प्रोफाईल निवडा"</string>
+ <string name="category_personal" msgid="1299663247844969448">"वैयक्तिक"</string>
+ <string name="category_work" msgid="8699184680584175622">"कार्य"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-ms-rMY/arrays.xml b/packages/SettingsLib/res/values-ms-rMY/arrays.xml
new file mode 100644
index 0000000..15cd7ba
--- /dev/null
+++ b/packages/SettingsLib/res/values-ms-rMY/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Mengimbas..."</item>
+ <item msgid="8513729475867537913">"Menyambung..."</item>
+ <item msgid="515055375277271756">"Mengesahkan..."</item>
+ <item msgid="1943354004029184381">"Mendapatkan alamat IP..."</item>
+ <item msgid="4221763391123233270">"Disambungkan"</item>
+ <item msgid="624838831631122137">"Digantung"</item>
+ <item msgid="7979680559596111948">"Memutuskan sambungan..."</item>
+ <item msgid="1634960474403853625">"Diputuskan sambungan"</item>
+ <item msgid="746097431216080650">"Tidak berjaya"</item>
+ <item msgid="6367044185730295334">"Disekat"</item>
+ <item msgid="503942654197908005">"Mengelakkan sambungan lemah buat sementara"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Mengimbas..."</item>
+ <item msgid="355508996603873860">"Menyambung kepada <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="554971459996405634">"Mengesahkan dengan <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="7928343808033020343">"Mendapatkan alamat IP dari <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="8937994881315223448">"Disambungkan kepada <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Digantung"</item>
+ <item msgid="7698638434317271902">"Memutuskan sambungan dari <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="197508606402264311">"Diputuskan sambungan"</item>
+ <item msgid="8578370891960825148">"Tidak berjaya"</item>
+ <item msgid="5660739516542454527">"Disekat"</item>
+ <item msgid="1805837518286731242">"Mengelakkan sambungan lemah buat sementara"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-ms-rMY/strings.xml b/packages/SettingsLib/res/values-ms-rMY/strings.xml
new file mode 100644
index 0000000..e3ceddb
--- /dev/null
+++ b/packages/SettingsLib/res/values-ms-rMY/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Tidak boleh mengimbas untuk rangkaian"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Tiada"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Disimpan"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Dinyahdayakan"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Kegagalan Konfigurasi IP"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Kegagalan Sambungan WiFi"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Masalah pengesahan"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Tidak dalam liputan"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Tiada Akses Internet Dikesan, tidak akan menyambung secara automatik."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Diselamatkan oleh <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Disambungkan melalui Pembantu Wi-Fi"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Disambungkan melalui %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Tersedia melalui %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Disambungkan, tiada Internet"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Diputuskan sambungan"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Memutuskan sambungan..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Menyambung..."</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Bersambung"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Memasangkan..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Disambungkan (tiada telefon)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Disambungkan (tiada media)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Disambungkan (tiada akses mesej)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Disambungkan (tiada telefon atau media)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Audio media"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Audio telefon"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Pemindahan fail"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Peranti input"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Akses Internet"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Perkongsian kenalan"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Gunakan untuk perkongsian kenalan"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Perkongsian sambungan Internet"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Akses Mesej"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Akses SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Disambungkan ke audio media"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Disambungkan ke audio telefon"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Bersambung ke pelayan pemindahan fail"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Disambungkan ke peta"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Disambungkan ke SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Tidak bersambung kepada pelayan pemindahan fail"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Disambungkan ke peranti input"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Sbg ke pranti utk aks Int"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Kgsi sbgn Int dgn peranti"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Gunakan untuk akses Internet"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Gunakan untuk peta"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Gunakan untuk akses SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Gunakan untuk audio media"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Gunakan untuk audio telefon"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Gunakan untuk pemindahan fail"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Gunakan untuk input"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Jadikan pasangan"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"JADIKAN PASANGAN"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Batal"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Berpasangan memberi anda akses kepada kenalan dan sejarah panggilan apabila disambungkan."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Tidak dapat berpasangan dengan <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Tidak dapat berpasangan dengan <xliff:g id="DEVICE_NAME">%1$s</xliff:g> kerana PIN atau kunci laluan yang salah."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Tidak boleh berkomunikasi dengan <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Pasangan ditolak oleh <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi dimatikan."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi diputuskan sambungannya."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi satu bar."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wi-Fi dua bar."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wi-Fi tiga bar."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Isyarat Wi-Fi penuh."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"OS Android"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Apl dialih keluar"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Apl dan pengguna yang dialih keluar"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"Penambatan USB"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Titik panas mudah alih"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Penambatan Bluetooth"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Penambatan"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Liputan tambatan & mudah alih"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Profil kerja"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Tetamu"</string>
+ <string name="unknown" msgid="1592123443519355854">"Tidak diketahui"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Pengguna: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Beberapa lalai ditetapkan"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Tiada lalai ditetapkan"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Tetapan teks kepada pertuturan"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Output teks ke pertuturan"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Kadar pertuturan"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Kelajuan pertuturan teks"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Bahasa"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Gunakan bahasa sistem"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Bahasa tidak dipilih"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Menetapkan suara khusus bahasa untuk teks pertuturan"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Dengar contoh"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Mainkan tunjuk cara singkat sintesis pertuturan"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Pasang data suara"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Pasang data suara yang diperlukan untuk sintesis pertuturan"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Enjin sintesis pertuturan ini mungkin boleh mengumpul semua teks yang akan dituturkan, termasuk data peribadi seperti kata laluan dan nombor kad kredit. Ia datang daripada enjin <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Dayakan penggunaan enjin sintesis pertuturan ini?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Bahasa ini memerlukan sambungan rangkaian yang berfungsi untuk output teks ke pertuturan."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Ini ialah contoh sintesis pertuturan."</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Status bahasa tetapan asal"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> disokong sepenuhnya"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> memerlukan sambungan rangkaian"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> tidak disokong"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Menyemak…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Tetapan untuk <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Lancarkan tetapan enjin"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Enjin pilihan"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Umum"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Sangat perlahan"</item>
+ <item msgid="4795095314303559268">"Perlahan"</item>
+ <item msgid="8903157781070679765">"Biasa"</item>
+ <item msgid="164347302621392996">"Laju"</item>
+ <item msgid="5794028588101562009">"Lebih laju"</item>
+ <item msgid="7163942783888652942">"Sangat laju"</item>
+ <item msgid="7831712693748700507">"Pantas"</item>
+ <item msgid="5194774745031751806">"Sangat pantas"</item>
+ <item msgid="9085102246155045744">"Paling laju"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Pilih Profil"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Peribadi"</string>
+ <string name="category_work" msgid="8699184680584175622">"Tempat Kerja"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-my-rMM/arrays.xml b/packages/SettingsLib/res/values-my-rMM/arrays.xml
new file mode 100644
index 0000000..82f2ded
--- /dev/null
+++ b/packages/SettingsLib/res/values-my-rMM/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"စကင်ပြုလုပ်နေပြီ"</item>
+ <item msgid="8513729475867537913">"ချိတ်ဆက်နေသည်"</item>
+ <item msgid="515055375277271756">"စစ်မှန်ကြောင်းအတည်ပြုနေသည်"</item>
+ <item msgid="1943354004029184381">"အိုင်ပီလိပ်စာရယူနေသည်"</item>
+ <item msgid="4221763391123233270">"ဆက်သွယ်ထားပြီး"</item>
+ <item msgid="624838831631122137">"ဆိုင်းငံ့ထားသည်"</item>
+ <item msgid="7979680559596111948">"အဆက်အသွယ်ဖြတ်တောက်နေသည်"</item>
+ <item msgid="1634960474403853625">"ချိတ်ဆက်မှုပြတ်တောက်သည်"</item>
+ <item msgid="746097431216080650">"မအောင်မြင်ပါ"</item>
+ <item msgid="6367044185730295334">"ပိတ်ထားသည်"</item>
+ <item msgid="503942654197908005">"နှေးကွေးသောဆက်သွယ်မှုကို ယာယီရှောင်ရှားထားသည်"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"စကင်ပြုလုပ်နေပြီ"</item>
+ <item msgid="355508996603873860">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> နှင့် ဆက်သွယ်နေပါသည်"</item>
+ <item msgid="554971459996405634">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>နှင့်စစ်မှန်ကြောင်းအတည်ပြုနေသည်"</item>
+ <item msgid="7928343808033020343">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> မှ IPလိပ်စာရယူနေသည်"</item>
+ <item msgid="8937994881315223448">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> နှင့် ဆက်သွယ်ထားပြီး"</item>
+ <item msgid="1330262655415760617">"ဆိုင်းငံ့ထားသည်"</item>
+ <item msgid="7698638434317271902">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>မှ ဆက်သွယ်မှုဖြတ်တောက်သွားသည်"</item>
+ <item msgid="197508606402264311">"ချိတ်ဆက်မှုပြတ်တောက်သည်"</item>
+ <item msgid="8578370891960825148">"မအောင်မြင်ပါ"</item>
+ <item msgid="5660739516542454527">"ပိတ်ထားသည်"</item>
+ <item msgid="1805837518286731242">"နှေးကွေးသောဆက်သွယ်မှုကို ယာယီရှောင်ရှားထားသည်"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-my-rMM/strings.xml b/packages/SettingsLib/res/values-my-rMM/strings.xml
new file mode 100644
index 0000000..99c27c8
--- /dev/null
+++ b/packages/SettingsLib/res/values-my-rMM/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"ကွန်ယက်များကို စကင်မလုပ်နိုင်ပါ"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"တစ်ခုမှမဟုတ်ပါ"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"သိမ်းဆည်းပြီး"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"သုံးမရအောင် ပိတ်ထားသည်"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP ပြုပြင်ခြင်း မအောင်မြင်ပါ"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi ချိတ်ဆက်မှု မအောင်မြင်ပါ"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"စစ်မှန်ကြောင်းအတည်ပြုရန်၌ ပြသနာရှိခြင်း"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"စက်ကွင်းထဲတွင် မဟုတ်ပါ"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"မည်သည့် အင်တာနက်မျှမရှိပါ၊ အလိုအလျောက် ပြန်လည်မချိတ်ဆက်ပါ။"</string>
+ <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> မှသိမ်းဆည်းခဲ့သည်"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"ကြိုးမဲ့ကူညီသူမှတဆင့် ချိတ်ဆက်၏"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s မှတစ်ဆင့် ချိတ်ဆက်ထားသည်"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s မှတစ်ဆင့်ရနိုင်သည်"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"ချိတ်ဆက်ထားသည်၊ အင်တာနက်မရှိ"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"ချိတ်ဆက်မှုပြတ်တောက်သည်"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"အဆက်အသွယ်ဖြတ်တောက်သည်"</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"ချိတ်ဆက်နေသည်"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"ချိတ်ဆက်ထားပြီး"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"တွဲချိတ်ပါ"</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"ချိတ်ဆက်ထားပြီး (ဖုန်းမရှိ)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"ချိတ်ဆက်ထားပြီး (မီဒီယာမရှိ)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"ချိတ်ဆက်မိသည် (သတင်းရယူမှုမရှိ)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"ချိတ်ဆက်ပြီး (ဖုန်း သို့ မီဒီယာမဟုတ်ပါ)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"မီဒီယာ အသံ"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"ဖုန်းအသံ"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"ဖိုင်လွဲပြောင်းခြင်း"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"ထည့်သွင်းသော စက်"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"အင်တာနက်ချိတ်ဆက်ခြင်း"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"အဆက်အသွယ်ကို မျှဝေရန်"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"အဆက်အသွယ်ကို မျှဝေရန် အတွက် သုံးရန်"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"အင်တာနက်ဆက်သွယ်မှု မျှဝေခြင်း"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"သတင်းရယူမှု"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM အသုံးပြုခြင်း"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"မီဒီယာအသံအား ချိတ်ဆက်ရန်"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"ဖုန်းအသံအား ချိတ်ဆက်ရန်"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ဖိုင်လွှဲပြောင်းမည့်ဆာဗာနှင့် ချိတ်ဆက်ထားပြီး"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"မြေပုံနှင့် ချိတ်ဆက်ရန်"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"SAP သို့ချိတ်ဆက်ထားသည်"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"ဖိုင်လွှဲပြောင်းမည့်ဆာဗာနှင့် ချိတ်ဆက်မထားပါ"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"ထည့်သွင်းထားသောစက်ကို ချိတ်ဆက်မည်"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"အင်တာနက်ဆက်သွယ်မှုရရန် စက်နှင်ချိတ်ဆက်မည်"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"စက်နှင့် ပုံမှန်အင်တာနက်ဆက်သွယ်မှုအား မျှဝေစေရန်"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"အင်တာနက်ချိတ်ဆက်ရန်အသုံးပြုသည်"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"မြေပုံအတွက်သုံးရန်"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"SIM အသုံးပြုမှုအတွက် အသုံးပြုမည်"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"မီဒီယာအသံအတွက်အသုံးပြုရန်"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"ဖုန်းအသံအားအသုံးပြုရန်"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ဖိုင်လွဲပြောင်းရန်အတွက်အသုံးပြုရန်"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ထည့်သွင်းရန်အသုံးပြုသည်"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"အတူတွဲပါ"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ချိတ်တွဲရန်"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"ထားတော့"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"ချိတ်တွဲမှုက ချိတ်ဆက်ထားလျှင် သင်၏ အဆက်အသွယ်များ နှင့် ခေါ်ဆိုမှု မှတ်တမ်းကို ရယူခွင့် ပြုသည်။"</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>နှင့် တွဲချိတ်မရပါ"</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"ပင်နံပါတ် သို့မဟုတ် ဖြတ်သန်းခွင့်ကီးမမှန်ကန်သောကြောင့်<xliff:g id="DEVICE_NAME">%1$s</xliff:g>နှင့် တွဲချိတ်မရပါ။"</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>နှင့်ဆက်သွယ်မရပါ"</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>နှင့်တွဲချိတ်ရန် ပယ်ချခံရသည်"</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"ဝိုင်ဖိုင် မရှိ"</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"ဝိုင်ဖိုင် ချိတ်ဆက်ထားမှု မရှိပါ"</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"ဝိုင်ဖိုင် ၁ ဘားရှိ"</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"ဝိုင်ဖိုင် ၂ ဘား"</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"ဝိုင်ဖိုင် ၃ ဘား"</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"ဝိုင်ဖိုင် အပြည့်ရှိ"</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android စနစ်"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"ဖယ်ရှားထားသော အပ်ပလီကေးရှင်းများ"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"ဖယ်ရှားထားသော အပလီကေးရှင်းနှင့် သုံးစွဲသူများ"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USBမှတဆင့်ချိတ်ဆက်ခြင်း"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ရွေ့လျားနိုင်သောဟော့စပေါ့"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ဘလူးတုသ်တဆင့်ပြန်ချိတ်ဆက်"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"တဆင့်ပြန်လည်ချိတ်ဆက်ခြင်း"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"တဆင့်ချိတ်ဆက်ခြင်း၊ ဟော့စပေါ့"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"အလုပ် ပရိုဖိုင်"</string>
+ <string name="user_guest" msgid="8475274842845401871">"ဧည့်သည်"</string>
+ <string name="unknown" msgid="1592123443519355854">"အကြောင်းအရာ မသိရှိ"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"သုံးစွဲသူ၊ <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"တချို့အားပုံမှတ်အဖြစ်သတ်မှတ်"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"ပုံမှန်သတ်မှတ်ထားခြင်းမရှိ"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"စာသားမှစကားပြောပြောင်း ဆက်တင်များ"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"စာသားမှ အသံထွက်စေခြင်း"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"စကားပြောနှုန်း"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"စာတမ်းအားပြောဆိုသော အမြန်နှုန်း"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"ဘာသာစကား"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"စနစ်၏ ဘာသာစကားကို အသုံးပြုရန်"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"ဘာသာစကား မရွေးချယ်ထားပါ။"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"ဘာသာစကားကိုသတ်မှတ်မည်။ ပြောဆိုမည့်စာသားအတွက် သီးခြားအသံ"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"နမူနာကို နားထောင်မည်"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"အသံပေါင်းစပ်ခြင်းအတွက် တိုတောင်းသောသရုပ်ပြမှုကို ပြခြင်း"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"အသံဒေတာများကို ထည့်သွင်းခြင်း"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"စကားသံပေါင်းစပ်မှုအတွက်လိုအပ်သောအသံဒေတာအား ထည့်သွင်းမည်"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"ဤစကားသံပေါင်းစပ်အင်ဂျင်အားအသုံးပြုရာရာတွင် သင့်ကိုယ်ရေးအချက်အလက်များဖြစ်သော စကားဝှက်များနှင့် ကရက်ဒစ်ကဒ်နံပါတ်စသည်တို့အပါအဝင် သင်ပြောဆိုသောစာသားများအားလုံးကို ရယူသွားမည်ဖြစ်သည်။ <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>အင်ဂျင်မှ လာပါသည်။ ဤစကားသံပေါင်းစပ်အင်ဂျင်ကို အသုံးပြုမည်လား?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"ဤဘာသာစကားသည် စာသားမှ အသံထွက်ရန် အလုပ်လုပ်သော ကွန်ရက်ချိတ်ဆက်မှု လိုအပ်သည်။"</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"ဤသည်မှာ အသံတုလုပ်ခြင်း ၏ နမူနာတစ်ခုဖြစ်သည်။"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"လက်ရှိဘာသာစကားအခြေအနေ"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> သည်အပြည့်အ၀ အထောက်အကူပြုသည်။"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g>သည် ကွန်ရက်ဆက်သွယ်မှုလိုအပ်သည်"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> အတွက် မရသေးပါ"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"စစ်ဆေးနေပါသည်…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>အတွက် ဆက်တင်များ"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"အင်ဂျင်ဆက်တင်များကိုဖွင့်ခြင်း"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"ဦးစားပေးအင်ဂျင်"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"ယေဘုယျ"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"အလွန်နှေး"</item>
+ <item msgid="4795095314303559268">"နှေး"</item>
+ <item msgid="8903157781070679765">"ပုံမှန်"</item>
+ <item msgid="164347302621392996">"မြန်"</item>
+ <item msgid="5794028588101562009">"ပိုမြန်"</item>
+ <item msgid="7163942783888652942">"အလွန်မြန်"</item>
+ <item msgid="7831712693748700507">"သွက်သွက်"</item>
+ <item msgid="5194774745031751806">"အရမ်းသွက်"</item>
+ <item msgid="9085102246155045744">"အမြန်ဆုံး"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"ပရိုဖိုင်ရွေးရန်"</string>
+ <string name="category_personal" msgid="1299663247844969448">"ကိုယ်ရေး"</string>
+ <string name="category_work" msgid="8699184680584175622">"အလုပ်အကိုင်"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-nb/arrays.xml b/packages/SettingsLib/res/values-nb/arrays.xml
new file mode 100644
index 0000000..af7c0b1
--- /dev/null
+++ b/packages/SettingsLib/res/values-nb/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Skanner ..."</item>
+ <item msgid="8513729475867537913">"Kobler til …"</item>
+ <item msgid="515055375277271756">"Autentiserer ..."</item>
+ <item msgid="1943354004029184381">"Henter IP-adresse …"</item>
+ <item msgid="4221763391123233270">"Tilkoblet"</item>
+ <item msgid="624838831631122137">"Avsluttet"</item>
+ <item msgid="7979680559596111948">"Kobler fra ..."</item>
+ <item msgid="1634960474403853625">"Frakoblet"</item>
+ <item msgid="746097431216080650">"Mislyktes"</item>
+ <item msgid="6367044185730295334">"Blokkert"</item>
+ <item msgid="503942654197908005">"Unngår dårlig tilkobling midlertidig"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Skanner ..."</item>
+ <item msgid="355508996603873860">"Kobler til <xliff:g id="NETWORK_NAME">%1$s</xliff:g> …"</item>
+ <item msgid="554971459996405634">"Autentiserer med <xliff:g id="NETWORK_NAME">%1$s</xliff:g> …"</item>
+ <item msgid="7928343808033020343">"Henter IP-adresse fra <xliff:g id="NETWORK_NAME">%1$s</xliff:g> …"</item>
+ <item msgid="8937994881315223448">"Koblet til <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Avsluttet"</item>
+ <item msgid="7698638434317271902">"Kobler fra <xliff:g id="NETWORK_NAME">%1$s</xliff:g> …"</item>
+ <item msgid="197508606402264311">"Frakoblet"</item>
+ <item msgid="8578370891960825148">"Mislyktes"</item>
+ <item msgid="5660739516542454527">"Blokkert"</item>
+ <item msgid="1805837518286731242">"Unngår dårlig tilkobling midlertidig"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
new file mode 100644
index 0000000..413165a
--- /dev/null
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Kan ikke søke etter nettverk"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Ingen"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Lagret"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Slått av"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-konfigurasjonsfeil"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi-tilkoblingsfeil"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Autentiseringsproblem"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Utenfor område"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Ingen Internett-tilgang ble funnet. Kan ikke koble til på nytt automatisk."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Lagret av <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Koblet til via en Wi-Fi-assistent"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Tilkoblet via %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Tilgjengelig via %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Tilkoblet – ingen Internett-forbindelse"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Frakoblet"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Kobler fra…"</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Kobler til…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Tilkoblet"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Sammenkobles …"</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Tilkobling (ingen telefon)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Tilkoblet (ingen medier)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Tilkoblet (ingen meldingstilgang)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Tilkoblet (ingen telefon eller media)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Medielyd"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Telefonlyd"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Filoverføring"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Inndataenhet"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Internett-tilgang"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Kontaktdeling"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Bruk til kontaktdeling"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Deling av Internett-tilkobling"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Meldingstilgang"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Tilgang til SIM-kortet"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Koblet til medielyd"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Koblet til telefonlyd"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Koblet til tjener for filoverføring"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Koblet til kart"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Koblet til SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Ikke koblet til tjener for filoverføring"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Koblet til inndataenhet"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Koblet til enhet for Internett-tilgang"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Deler lokal Internett-tilkobling med enhet"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Bruk for Internett-tilgang"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Bruk for kart"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Bruk for tilgang til SIM-kortet"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Bruk for medielyd"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Bruk for telefonlyd"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Bruk til filoverføring"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Bruk for inndata"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Sammenkoble"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"KOBLE"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Avbryt"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Med sammenkobling får den andre enheten tilgang til kontaktene og anropsloggen din når den er tilkoblet."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Kan ikke koble til <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Kan ikke koble til <xliff:g id="DEVICE_NAME">%1$s</xliff:g> på grunn av feil personlig kode eller passord."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Kan ikke kommunisere med <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> avslo paring."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi er av."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi er frakoblet."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi-signal med én stolpe."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wi-Fi-signal med to stolper."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wi-Fi-signal med tre stolper."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Wi-Fi-signalet er ved full styrke."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android-operativsystem"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Fjernede apper"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Fjernede apper og brukere"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB-internettdeling"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Flyttbar trådløs sone"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth-internettdeling"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Internettdeling"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Nettdeling og trådløs sone"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Jobbprofil"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Gjest"</string>
+ <string name="unknown" msgid="1592123443519355854">"Ukjent"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Bruker:<xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Noen standardvalg er angitt"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Ingen standardvalg er angitt"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Talesyntese-kontroller"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Tekst-til-tale"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Talehastighet"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Hvor raskt teksten leses"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Språk"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Bruk systemspråk"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Språk er ikke valgt"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Velger språkspesifikk stemme"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Lytt til et eksempel"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Spill en kort demonstrasjon av talesyntesen"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Installer stemmedata"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Installer nødvendige stemmedata for talesyntese"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Denne talesyntesemotoren kan samle inn all uttalt tekst, herunder personlige opplysninger som for eksempel passord og kredittkortnumre. Den er basert på motoren <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Vil du slå på denne talesyntesemotoren?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Dette språket krever en fungerende nettverkstilkobling for tekst-til-tale."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Dette er et eksempel på talesyntese."</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Status for standardspråk"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> støttes fullt ut"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> krever nettverkstilkobling"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> støttes ikke"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Kontrollerer …"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Innstillinger for <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Innstillinger for kjøring av motor"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Foretrukket motor"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Generelt"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Veldig langsom"</item>
+ <item msgid="4795095314303559268">"Langsom"</item>
+ <item msgid="8903157781070679765">"Vanlig"</item>
+ <item msgid="164347302621392996">"Rask"</item>
+ <item msgid="5794028588101562009">"Litt raskere"</item>
+ <item msgid="7163942783888652942">"Veldig rask"</item>
+ <item msgid="7831712693748700507">"Hurtig"</item>
+ <item msgid="5194774745031751806">"Veldig hurtig"</item>
+ <item msgid="9085102246155045744">"Raskest"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Velg profil"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Personlig"</string>
+ <string name="category_work" msgid="8699184680584175622">"Jobb"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-ne-rNP/arrays.xml b/packages/SettingsLib/res/values-ne-rNP/arrays.xml
new file mode 100644
index 0000000..7cb9ceb
--- /dev/null
+++ b/packages/SettingsLib/res/values-ne-rNP/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"स्क्यान गरिँदै..."</item>
+ <item msgid="8513729475867537913">"जडान हुँदै..."</item>
+ <item msgid="515055375277271756">"प्रमाणित गर्दै ..."</item>
+ <item msgid="1943354004029184381">"IP ठेगाना पत्ता लगाउँदै ..."</item>
+ <item msgid="4221763391123233270">"जडान गरिएको"</item>
+ <item msgid="624838831631122137">"निलम्बित"</item>
+ <item msgid="7979680559596111948">"विच्छेदन गर्दै..."</item>
+ <item msgid="1634960474403853625">"विच्छेदन भएको"</item>
+ <item msgid="746097431216080650">"असफल"</item>
+ <item msgid="6367044185730295334">"रोक्का गरियो"</item>
+ <item msgid="503942654197908005">"अस्थायी रूपमा कमजोर जडान बेवास्ता गर्दै"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"स्क्यान गर्दै..."</item>
+ <item msgid="355508996603873860">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>सँग जडान हुँदै..."</item>
+ <item msgid="554971459996405634">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>को साथ प्रमाणित गर्दै…"</item>
+ <item msgid="7928343808033020343">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>बाट IP ठेगाना प्राप्त गर्दै…"</item>
+ <item msgid="8937994881315223448">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>सँग जडित"</item>
+ <item msgid="1330262655415760617">"निलम्बित"</item>
+ <item msgid="7698638434317271902">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>बाट विच्छेदन गर्दै..."</item>
+ <item msgid="197508606402264311">"विच्छेदन भएको"</item>
+ <item msgid="8578370891960825148">"असफल"</item>
+ <item msgid="5660739516542454527">"रोकियो"</item>
+ <item msgid="1805837518286731242">"अस्थायी रूपमा कमजोर जडान हटाइँदै"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-ne-rNP/strings.xml b/packages/SettingsLib/res/values-ne-rNP/strings.xml
new file mode 100644
index 0000000..eedbdec
--- /dev/null
+++ b/packages/SettingsLib/res/values-ne-rNP/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"सञ्जालका लागि स्क्यान गर्न सक्दैन"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"कुनै पनि होइन"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"बचत गरियो"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"असक्षम पारियो"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP विन्यास असफल"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"वाईफाई जडान असफल"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"प्रमाणीकरण समस्या"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"दायराभित्र छैन"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"कुनै इन्टरनेट पहुँच पाईएन, स्वचालित रूपमा पुन: जडान छैन।"</string>
+ <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> द्वारा सुरक्षित गरियो"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Wi-Fi सहायक द्वारा जोडिएको"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s मार्फत जडित"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s मार्फत उपलब्ध"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"जडित, इन्टरनेट चलेको छैन"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"विच्छेदन गरियो"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"जडान हटाइँदै ..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"जडान हुँदै..."</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"जडान गरिएको"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"जोडा बाँध्दै..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"जडान (कुनै फोन छैन)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"जडित (कुनै पनि मिडिया छैन)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"जडित छ (सन्देशमा पहुँच छैन)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"जडित (फोन वा मिडिया छैन)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"मिडिया अडियो"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"फोन अडियो"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"फाइल स्थानान्तरण"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"इनपुट उपकरण"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"इन्टरनेट पहुँच"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"सम्पर्क साझेदारी"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"सम्पर्क साझेदारीका लागि प्रयोग"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"इन्टरनेट जडान साझेदारी गर्दै"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"सन्देश पहुँच"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM पहुँच"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"मिडिया अडियोसँग जडित"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"फोन अडियोमा जडान गरियो"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"फाइल ट्रान्सफर सर्भरमा जडान गरियो"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"नक्सासँग जडित"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"SAP मा जडित"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"फाइल ट्रान्सफर सर्भरसँग जडान गरिएको छैन"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"इनपुट उपकरणसँग जोडिएको छ"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"इन्टरनेट पहुँचका लागि उपकरणसँग जडित"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"उपकरणसँग स्थानीय इन्टरनेट जडान साझेदारी गर्दै"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"इन्टर्नेट पहुँचका लागि प्रयोग गर्नुहोस्"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"नक्साको लागि प्रयोग गर्नुहोस्"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"SIM मा पहुँचका लागि प्रयोग गर्नुहोस्"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"मिडिया अडियोका लागि प्रयोग गर्नुहोस्"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"फोन अडियोको लागि प्रयोग गर्नुहोस्"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"फाइल ट्रान्सफरका लागि प्रयोग गर्नुहोस्"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"इनपुटको लागि प्रयोग गर्नुहोस्"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"जोडी"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"जोडी"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"रद्द गर्नुहोस्"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"जब जडान हुन्छ जोडी अनुदानले तपाईँको सम्पर्कहरू पहुँच गर्छ र इतिहास सम्झाउँछ।"</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>सँग जोडा मिलाउन सकेन"</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>गलत PIN वा पासकिका कारण सँग जोडा बाँध्न सक्दैन।"</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> सँग कुराकानी हुन सक्दैन।"</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> द्वारा जोडा बाँध्ने कार्य अस्वीकृत"</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi बन्द।"</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi जडान विच्छेद भयो।"</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi एक पट्टि।"</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wi-Fi दुई पट्टि।"</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wi-Fi तीन बारहरू।"</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"पूर्ण Wi-Fi सिंग्नल।"</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"एन्ड्रोइड OS"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"हटाइएका अनुप्रयोगहरू"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"अनुप्रयोगहरू र प्रयोगकर्ताहरू हटाइयो।"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB टेथर गर्दै"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"पोर्टेबल हटस्पट"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ब्लुटुथ टेथर गर्दै"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"टेदर गर्दै"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"टेदर गर्ने र पोर्टेबल हटस्पट"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"कार्य प्रोफाइल"</string>
+ <string name="user_guest" msgid="8475274842845401871">"अतिथि"</string>
+ <string name="unknown" msgid="1592123443519355854">"अज्ञात"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"प्रयोगकर्ता: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"केही पूर्वनिर्धारितहरू सेट गरिएका छन्"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"कुनै पूर्वनिर्धारित सेट गरिएको छैन"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"पाठ-वाचन सेटिङहरू"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"पाठ-बाट-वाणी उत्पादन"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"वाणी दर"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"पाठ वाचन हुने गति"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"भाषा"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"प्रणाली भाषा प्रयोग गर्नुहोस्"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"भाषा चयन गरिएको छैन"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"बोली पाठका लागि भाषा-विशेष आवाज सेट गर्दछ"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"एउटा उदाहरणलाई सुन्नुहोस्"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"वाणी संश्लेषणको एउटा छोटो प्रदर्शन बजाउनुहोस्"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"आवाज डेटा स्थापना गर्नुहोस्"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"वाणी संश्लेषणका लागि आवश्यक आवाज डेटा स्थापना गर्नुहोस्"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"यो वाणी संश्लेषण इन्जिनले पासवर्ड र क्रेडिट कार्ड नम्बर जस्ता निजी डेटासहित बोलिने सबै पाठ जम्मा गर्न सक्षम हुन सक्छ। यो <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> इन्जिनबाट आउँछ। यो वाणी संश्लेषण इन्जिनको उपयोग सक्षम गर्नुहुन्छ?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"पाठ वाचकको आउटपुटका लागि यस भाषालाई काम गरिरहेको सञ्जाल जडान आवाश्यक पर्छ।"</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"यो वाणी संश्लेषणको एउटा उदाहरण हो।"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"पूर्वनिर्धारित भाषाको वस्तुस्थिति"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> पूर्ण रूपले समर्थित छ"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> नेटवर्क जडान चाहिन्छ"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> समर्थित छैन"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"जाँच गर्दै..."</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>को लागि सेटिङ गर्दै"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"इन्जिन सेटिङहरू सुरुवात गर्नुहोस्"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"रुचाइएको इन्जिन"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"सामान्य"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"निकै बिस्तारै"</item>
+ <item msgid="4795095314303559268">"ढिलो"</item>
+ <item msgid="8903157781070679765">"सामान्य"</item>
+ <item msgid="164347302621392996">"छिटो"</item>
+ <item msgid="5794028588101562009">"थप छिटो"</item>
+ <item msgid="7163942783888652942">"ज्यादै छिटो"</item>
+ <item msgid="7831712693748700507">"तीव्र"</item>
+ <item msgid="5194774745031751806">"धेरै तीव्र"</item>
+ <item msgid="9085102246155045744">"सबभन्दा छिटो"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"प्रोफाइल रोज्नुहोस्"</string>
+ <string name="category_personal" msgid="1299663247844969448">"व्यक्तिगत"</string>
+ <string name="category_work" msgid="8699184680584175622">"काम"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-nl/arrays.xml b/packages/SettingsLib/res/values-nl/arrays.xml
new file mode 100644
index 0000000..ab72b67
--- /dev/null
+++ b/packages/SettingsLib/res/values-nl/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Scannen..."</item>
+ <item msgid="8513729475867537913">"Verbinding maken..."</item>
+ <item msgid="515055375277271756">"Verifiëren..."</item>
+ <item msgid="1943354004029184381">"IP-adres ophalen…"</item>
+ <item msgid="4221763391123233270">"Verbonden"</item>
+ <item msgid="624838831631122137">"Opgeschort"</item>
+ <item msgid="7979680559596111948">"Verbinding verbreken..."</item>
+ <item msgid="1634960474403853625">"Verbinding verbroken"</item>
+ <item msgid="746097431216080650">"Mislukt"</item>
+ <item msgid="6367044185730295334">"Geblokkeerd"</item>
+ <item msgid="503942654197908005">"Slechte verbinding tijdelijk vermijden"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Scannen..."</item>
+ <item msgid="355508996603873860">"Verbinding maken met <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="554971459996405634">"Verifiëren met <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="7928343808033020343">"IP-adres ophalen van <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="8937994881315223448">"Verbonden met <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Opgeschort"</item>
+ <item msgid="7698638434317271902">"Verbinding verbreken met <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="197508606402264311">"Verbinding verbroken"</item>
+ <item msgid="8578370891960825148">"Mislukt"</item>
+ <item msgid="5660739516542454527">"Geblokkeerd"</item>
+ <item msgid="1805837518286731242">"Slechte verbinding tijdelijk vermijden"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
new file mode 100644
index 0000000..d2edbb7
--- /dev/null
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Kan niet scannen naar netwerken"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Geen"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Opgeslagen"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Uitgeschakeld"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-configuratie mislukt"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wifi-verbinding mislukt"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Authenticatieprobleem"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Niet binnen bereik"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Geen internettoegang gevonden. Er wordt niet automatisch opnieuw verbinding gemaakt."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Opgeslagen door <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Verbonden via wifi-assistent"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Verbonden via %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Beschikbaar via %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Verbonden, geen internet"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Verbinding verbroken"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Verbinding verbreken..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Verbinding maken..."</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Verbonden"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Koppelen..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Gekoppeld (geen telefoon)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Gekoppeld (geen media)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Verbonden (geen toegang tot berichten)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Gekoppeld (geen telefoon of media)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Media-audio"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Telefoonaudio"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Bestandsoverdracht"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Invoerapparaat"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Internettoegang"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Contacten delen"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Gebruiken voor contacten delen"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Internetverbinding delen"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Toegang tot berichten"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Sim-toegang"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Verbonden met audio van medium"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Verbonden met audio van telefoon"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Verbonden met server voor bestandsoverdracht"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Verbonden met kaart"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Verbonden via SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Niet verbonden met server voor bestandsoverdracht"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Verbonden met invoerapparaat"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Verbonden met apparaat voor internet"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Lokale internetverbinding delen met apparaat"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Gebruik voor internettoegang"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Gebruiken voor kaart"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Gebruiken voor sim-toegang"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Gebruiken voor audio van medium"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Gebruiken voor audio van telefoon"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Gebruiken voor bestandsoverdracht"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Gebruiken voor invoer"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Koppelen"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"KOPPELEN"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Annuleren"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Koppelen verleent toegang tot je contacten en oproepgeschiedenis wanneer de apparaten zijn verbonden."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Kan niet koppelen aan <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Kan niet koppelen aan <xliff:g id="DEVICE_NAME">%1$s</xliff:g> vanwege een onjuiste pincode of toegangscode."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Kan niet communiceren met <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Koppeling geweigerd door <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wifi: uitgeschakeld."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wifi-verbinding verbroken."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wifi: één streepje."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wifi: twee streepjes."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wifi: drie streepjes."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Wifii-signaal is op volledige sterkte."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android-besturingssysteem"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Verwijderde apps"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Verwijderde apps en gebruikers"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB-tethering"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Draagbare hotspot"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth-tethering"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Tethering"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Tethering en draagbare hotspot"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Werkprofiel"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Gast"</string>
+ <string name="unknown" msgid="1592123443519355854">"Onbekend"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Gebruiker: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Enkele standaardwaarden ingesteld"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Geen standaardwaarden ingesteld"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Instellingen tekst-naar-spraak"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Spraakuitvoer"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Spreeksnelheid"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Snelheid waarmee de tekst wordt gesproken"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Taal"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Systeemtaal gebruiken"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Taal niet geselecteerd"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"De taalspecifieke stem voor de gesproken tekst instellen"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Luisteren naar een voorbeeld"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Een korte demonstratie van spraaksynthese afspelen"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Spraakgegevens installeren"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"De stemgegevens voor spraaksynthese installeren"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Deze engine voor spraaksynthese kan mogelijk alle tekst verzamelen die wordt gesproken, waaronder persoonlijke gegevens zoals wachtwoorden en creditcardnummers. Deze engine is afkomstig van de <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>-engine. Het gebruik van deze engine voor spraaksynthese inschakelen?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Deze taal heeft een werkende netwerkverbinding nodig voor tekst-naar-spraak-uitvoer."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Dit is een voorbeeld van spraaksynthese"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Status van standaardtaal"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> wordt volledig ondersteund"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> heeft een netwerkverbinding nodig"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> wordt niet ondersteund"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Controleren…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Instellingen voor <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Engine-instellingen openen"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Voorkeursengine"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Algemeen"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Zeer langzaam"</item>
+ <item msgid="4795095314303559268">"Langzaam"</item>
+ <item msgid="8903157781070679765">"Normaal"</item>
+ <item msgid="164347302621392996">"Snel"</item>
+ <item msgid="5794028588101562009">"Sneller"</item>
+ <item msgid="7163942783888652942">"Zeer snel"</item>
+ <item msgid="7831712693748700507">"Vlug"</item>
+ <item msgid="5194774745031751806">"Zeer vlug"</item>
+ <item msgid="9085102246155045744">"Snelst"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Profiel kiezen"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Persoonlijk"</string>
+ <string name="category_work" msgid="8699184680584175622">"Werk"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-pa-rIN/arrays.xml b/packages/SettingsLib/res/values-pa-rIN/arrays.xml
new file mode 100644
index 0000000..a03f17e
--- /dev/null
+++ b/packages/SettingsLib/res/values-pa-rIN/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"ਸਕੈਨ ਕਰ ਰਿਹਾ ਹੈ..."</item>
+ <item msgid="8513729475867537913">"ਕਨੈਕਟ ਕਰ ਰਿਹਾ ਹੈ…"</item>
+ <item msgid="515055375277271756">"ਪ੍ਰਮਾਣਿਤ ਕਰ ਰਿਹਾ ਹੈ…"</item>
+ <item msgid="1943354004029184381">"IP ਪਤਾ ਪ੍ਰਾਪਤ ਕਰ ਰਿਹਾ ਹੈ..."</item>
+ <item msgid="4221763391123233270">"ਕਨੈਕਟ ਕੀਤਾ"</item>
+ <item msgid="624838831631122137">"ਮੁਅੱਤਲ ਕੀਤਾ"</item>
+ <item msgid="7979680559596111948">"ਡਿਸਕਨੈਕਟ ਕਰ ਰਿਹਾ ਹੈ..."</item>
+ <item msgid="1634960474403853625">"ਡਿਸਕਨੈਕਟ ਕੀਤਾ"</item>
+ <item msgid="746097431216080650">"ਅਸਫਲ"</item>
+ <item msgid="6367044185730295334">"ਬਲੌਕ ਕੀਤਾ"</item>
+ <item msgid="503942654197908005">"ਅਸਥਾਈ ਤੌਰ ਤੇ ਖ਼ਰਾਬ ਕਨੈਕਸ਼ਨ ਤੋਂ ਬਚਣ ਲਈ"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"ਸਕੈਨ ਕਰ ਰਿਹਾ ਹੈ..."</item>
+ <item msgid="355508996603873860">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਕਰ ਰਿਹਾ ਹੈ…"</item>
+ <item msgid="554971459996405634">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> ਨਾਲ ਪ੍ਰਮਾਣਿਤ ਕਰ ਰਿਹਾ ਹੈ…"</item>
+ <item msgid="7928343808033020343">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> ਤੋਂ IP ਪਤਾ ਪ੍ਰਾਪਤ ਕਰ ਰਿਹਾ ਹੈ…"</item>
+ <item msgid="8937994881315223448">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ"</item>
+ <item msgid="1330262655415760617">"ਮੁਅੱਤਲ ਕੀਤਾ"</item>
+ <item msgid="7698638434317271902">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> ਤੋਂ ਡਿਸਕਨੈਕਟ ਕਰ ਰਿਹਾ ਹੈ…"</item>
+ <item msgid="197508606402264311">"ਡਿਸਕਨੈਕਟ ਕੀਤਾ"</item>
+ <item msgid="8578370891960825148">"ਅਸਫਲ"</item>
+ <item msgid="5660739516542454527">"ਬਲੌਕ ਕੀਤਾ"</item>
+ <item msgid="1805837518286731242">"ਅਸਥਾਈ ਤੌਰ ਤੇ ਖ਼ਰਾਬ ਕਨੈਕਸ਼ਨ ਤੋਂ ਬਚਣ ਲਈ"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-pa-rIN/strings.xml b/packages/SettingsLib/res/values-pa-rIN/strings.xml
new file mode 100644
index 0000000..921d48f
--- /dev/null
+++ b/packages/SettingsLib/res/values-pa-rIN/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"ਨੈਟਵਰਕਾਂ ਲਈ ਸਕੈਨ ਨਹੀਂ ਕਰ ਸਕਦਾ"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"ਕੋਈ ਨਹੀਂ"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"ਸੁਰੱਖਿਅਤ ਕੀਤਾ"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"ਅਸਮਰੱਥ ਬਣਾਇਆ"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP ਕੌਂਫਿਗਰੇਸ਼ਨ ਅਸਫਲਤਾ"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi ਕਨੈਕਸ਼ਨ ਅਸਫਲਤਾ"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"ਪ੍ਰਮਾਣੀਕਰਨ ਸਮੱਸਿਆ"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"ਰੇਂਜ ਵਿੱਚ ਨਹੀਂ ਹੈ"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"ਕੋਈ ਇੰਟਰਨੈਟ ਪਹੁੰਚ ਨਹੀਂ ਮਿਲੀ, ਆਟੋਮੈਟਿਕਲੀ ਰੀਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ ਜਾਏਗਾ।"</string>
+ <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> ਵੱਲੋਂ ਸੁਰੱਖਿਅਤ ਕੀਤਾ"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fi ਸਹਾਇਕ ਰਾਹੀਂ ਕਨੈਕਟ ਕੀਤਾ"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s ਰਾਹੀਂ ਕਨੈਕਟ ਕੀਤਾ"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s ਰਾਹੀਂ ਉਪਲਬਧ"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"ਕਨੈਕਟ ਕੀਤਾ, ਕੋਈ ਇੰਟਰਨੈਟ ਨਹੀਂ"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"ਡਿਸਕਨੈਕਟ ਕੀਤਾ"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"ਡਿਸਕਨੈਕਟ ਕਰ ਰਿਹਾ ਹੈ..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"ਕਨੈਕਟ ਕਰ ਰਿਹਾ ਹੈ…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"ਕਨੈਕਟ ਕੀਤਾ"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"ਪੇਅਰ ਕਰ ਰਿਹਾ ਹੈ…"</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"ਕਨੈਕਟ ਕੀਤਾ (ਕੋਈ ਫੋਨ ਨਹੀਂ)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"ਕਨੈਕਟ ਕੀਤਾ (ਕੋਈ ਮੀਡੀਆ ਨਹੀਂ)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"ਕਨੈਕਟ ਕੀਤਾ (ਕੋਈ ਸੁਨੇਹਾ ਪਹੁੰਚ ਨਹੀਂ)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"ਕਨੈਕਟ ਕੀਤਾ (ਕੋਈ ਫੋਨ ਜਾਂ ਮੀਡੀਆ ਨਹੀਂ)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"ਮੀਡੀਆ ਔਡੀਓ"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"ਫੋਨ ਔਡੀਓ"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"ਫਾਈਲ ਟ੍ਰਾਂਸਫਰ"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"ਇਨਪੁਟ ਡਿਵਾਈਸ"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"ਇੰਟਰਨੈਟ ਪਹੁੰਚ"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"ਸੰਪਰਕ ਸ਼ੇਅਰਿੰਗ"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"ਸੰਪਰਕ ਸ਼ੇਅਰਿੰਗ ਲਈ ਵਰਤੋ"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"ਇੰਟਰਨੈਟ ਕਨੈਕਸ਼ਨ ਸ਼ੇਅਰਿੰਗ"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"ਸੁਨੇਹਾ ਪਹੁੰਚ"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM ਪਹੁੰਚ"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"ਮੀਡੀਆ ਔਡੀਓ ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"ਫੋਨ ਔਡੀਓ ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ਫਾਈਲ ਟ੍ਰਾਂਸਫਰ ਸਰਵਰ ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"ਨਕਸ਼ੇ ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"SAP ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"ਫਾਈਲ ਟ੍ਰਾਂਸਫਰ ਸਰਵਰ ਨਾਲ ਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"ਇਨਪੁਟ ਡਿਵਾਈਸ ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"ਇੰਟਰਨੈਟ ਪਹੁੰਚ ਲਈ ਡਿਵਾਈਸ ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"ਡਿਵਾਈਸ ਨਾਲ ਸਥਾਨਕ ਇੰਟਰਨੈਟ ਕਨੈਕਸ਼ਨ ਸ਼ੇਅਰ ਕਰ ਰਿਹਾ ਹੈ"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"ਇੰਟਰਨੈਟ ਪਹੁੰਚ ਲਈ ਵਰਤੋ"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"ਨਕਸ਼ੇ ਲਈ ਵਰਤੋ"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"SIM ਪਹੁੰਚ ਲਈ ਵਰਤੋ"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"ਮੀਡੀਆ ਔਡੀਓ ਲਈ ਵਰਤੋ"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"ਫੋਨ ਔਡੀਓ ਲਈ ਵਰਤੋ"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ਫਾਈਲ ਟ੍ਰਾਂਸਫਰ ਲਈ ਵਰਤੋ"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ਇਨਪੁਟ ਲਈ ਵਰਤੋ"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"ਪੇਅਰ ਕਰੋ"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ਪੇਅਰ ਕਰੋ"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"ਰੱਦ ਕਰੋ"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"ਪੇਅਰ ਕਰਨਾ ਕਨੈਕਟ ਕੀਤੇ ਜਾਣ ਤੇ ਤੁਹਾਡੇ ਸੰਪਰਕਾਂ ਅਤੇ ਕਾਲ ਇਤਿਹਾਸ ਤੱਕ ਪਹੁੰਚ ਦੀ ਅਨੁਮਤੀ ਦਿੰਦਾ ਹੈ।"</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ਨਾਲ ਪੇਅਰ ਨਹੀਂ ਕਰ ਸਕਿਆ।"</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"ਇੱਕ ਗ਼ਲਤ PIN ਜਾਂ ਪਾਸਕੁੰਜੀ ਦੇ ਕਾਰਨ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ਨਾਲ ਪੇਅਰ ਨਹੀਂ ਕਰ ਸਕਿਆ।"</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ਨਾਲ ਸੰਚਾਰ ਨਹੀਂ ਕਰ ਸਕਦਾ।"</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"ਪੇਅਰਿੰਗ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ਵੱਲੋਂ ਰੱਦ ਕੀਤੀ ਗਈ।"</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wifi ਬੰਦ।"</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wifi ਡਿਸਕਨੈਕਟ ਕੀਤਾ।"</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wifi ਇੱਕ ਬਾਰ।"</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wifi ਦੋ ਬਾਰ।"</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wifi ਤਿੰਨ ਬਾਰ।"</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Wifi ਸਿਗਨਲ ਪੂਰਾ।"</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"ਹਟਾਏ ਗਏ ਐਪਸ"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"ਹਟਾਏ ਗਏ ਐਪਸ ਅਤੇ ਉਪਭੋਗਤਾ"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB ਟੀਥਰਿੰਗ"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ਪੋਰਟੇਬਲ ਹੌਟਸਪੌਟ"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth ਟੀਥਰਿੰਗ"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"ਟੀਥਰਿੰਗ"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"ਟੀਥਰਿੰਗ & ਪੋਰਟੇਬਲ ਹੌਟਸਪੌਟ"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"ਕੰਮ ਪ੍ਰੋਫਾਈਲ"</string>
+ <string name="user_guest" msgid="8475274842845401871">"ਮਹਿਮਾਨ"</string>
+ <string name="unknown" msgid="1592123443519355854">"ਅਗਿਆਤ"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"ਉਪਭੋਗਤਾ: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"ਕੁਝ ਡਿਫੌਲਟਸ ਸੈਟ ਕੀਤੇ"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"ਕੋਈ ਡਿਫੌਲਟਸ ਸੈਟ ਨਹੀਂ ਕੀਤੇ"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"ਟੈਕਸਟ-ਟੂ-ਸਪੀਚ ਸੈਟਿੰਗਾਂ"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"ਟੈਕਸਟ-ਟੂ-ਸਪੀਚ ਆਉਟਪੁਟ"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"ਸਪੀਚ ਰੇਟ"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"ਸਪੀਡ ਜਿਸਤੇ ਟੈਕਸਟ ਬੋਲਿਆ ਜਾਂਦਾ ਹੈ"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"ਭਾਸ਼ਾ"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"ਸਿਸਟਮ ਭਾਸ਼ਾ ਵਰਤੋ"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"ਭਾਸ਼ਾ ਨਹੀਂ ਚੁਣੀ"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"ਬੋਲੇ ਗਏ ਟੈਕਸਟ ਲਈ ਭਾਸ਼ਾ-ਵਿਸ਼ੇਸ਼ ਵੌਇਸ ਸੈਟ ਕਰਦਾ ਹੈ"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"ਇੱਕ ਉਦਾਹਰਨ ਲਈ ਸੁਣੋ"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"ਸਪੀਚ ਸਿੰਥੈਸਿਸ ਦਾ ਇੱਕ ਛੋਟਾ ਪ੍ਰਦਰਸ਼ਨ ਪਲੇ ਕਰੋ"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"ਵੌਇਸ ਡਾਟਾ ਇੰਸਟੌਲ ਕਰੋ"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"ਸਪੀਚ ਸਿੰਥੈਸਿਸ ਲਈ ਲੁੜੀਂਦਾ ਵੌਇਸ ਡਾਟਾ ਇੰਸਟੌਲ ਕਰੋ"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"ਇਹ ਸਪੀਚ ਸਿੰਥੈਸਿਸ ਇੰਜਣ ਉਹ ਸਾਰਾ ਟੈਕਸਟ ਇਕੱਤਰ ਕਰਨ ਵਿੱਚ ਸਮਰੱਥ ਹੋ ਸਕਦਾ ਹੈ, ਜੋ ਬੋਲਿਆ ਜਾਏਗਾ, ਨਿੱਜੀ ਡਾਟਾ ਸਮੇਤ ਜਿਵੇਂ ਪਾਸਵਰਡ ਅਤੇ ਕ੍ਰੈਡਿਟ ਕਾਰਡ ਨੰਬਰ। ਇਹ <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> ਇੰਜਣ ਤੋਂ ਆਉਂਦਾ ਹੈ। ਕੀ ਇਸ ਸਪੀਚ ਸਿੰਥੈਸਿਸ ਇੰਜਣ ਦੀ ਵਰਤੋਂ ਕਰਨੀ ਹੈ?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"ਇਸ ਭਾਸ਼ਾ ਲਈ ਟੈਕਸਟ-ਟੂ-ਸਪੀਚ ਆਉਟਪੁਟ ਲਈ ਇੱਕ ਚਾਲੂ ਨੈਟਵਰਕ ਕਨੈਕਸ਼ਨ ਦੀ ਲੋੜ ਹੈ।"</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"ਇਹ ਸਪੀਚ ਸਿੰਥੈਸਿਸ ਦਾ ਇੱਕ ਉਦਾਹਰਨ ਹੈ"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"ਡਿਫੌਲਟ ਭਾਸ਼ਾ ਸਥਿਤੀ"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> ਪੂਰੀ ਤਰ੍ਹਾਂ ਸਮਰਥਿਤ ਹੈ"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> ਲਈ ਨੈਟਵਰਕ ਕਨੈਕਸ਼ਨ ਲੁੜੀਂਦਾ ਹੈ"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> ਸਮਰਥਿਤ ਨਹੀਂ ਹੈ"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"ਜਾਂਚ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ..."</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g> ਲਈ ਸੈਟਿੰਗਾਂ"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"ਇੰਜਨ ਸੈਟਿੰਗਾਂ ਲੌਂਚ ਕਰੋ"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"ਤਰਜੀਹੀ ਇੰਜਣ"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"ਸਧਾਰਨ"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"ਬਹੁਤ ਹੌਲੀ"</item>
+ <item msgid="4795095314303559268">"ਹੌਲੀ"</item>
+ <item msgid="8903157781070679765">"ਸਧਾਰਨ"</item>
+ <item msgid="164347302621392996">"ਤੇਜ਼"</item>
+ <item msgid="5794028588101562009">"ਵੱਧ ਤੇਜ਼"</item>
+ <item msgid="7163942783888652942">"ਬਹੁਤ ਤੇਜ਼"</item>
+ <item msgid="7831712693748700507">"ਤੇਜ਼"</item>
+ <item msgid="5194774745031751806">"ਬਹੁਤ ਤੇਜ਼"</item>
+ <item msgid="9085102246155045744">"ਸਭ ਤੋਂ ਵੱਧ ਤੇਜ਼"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"ਪ੍ਰੋਫਾਈਲ ਚੁਣੋ"</string>
+ <string name="category_personal" msgid="1299663247844969448">"ਨਿੱਜੀ"</string>
+ <string name="category_work" msgid="8699184680584175622">"ਦਫ਼ਤਰ"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-pl/arrays.xml b/packages/SettingsLib/res/values-pl/arrays.xml
new file mode 100644
index 0000000..d2cbe24
--- /dev/null
+++ b/packages/SettingsLib/res/values-pl/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Trwa skanowanie..."</item>
+ <item msgid="8513729475867537913">"Trwa łączenie..."</item>
+ <item msgid="515055375277271756">"Trwa uwierzytelnianie..."</item>
+ <item msgid="1943354004029184381">"Uzyskiwanie adresu IP..."</item>
+ <item msgid="4221763391123233270">"Połączono"</item>
+ <item msgid="624838831631122137">"Zawieszona"</item>
+ <item msgid="7979680559596111948">"Trwa rozłączanie..."</item>
+ <item msgid="1634960474403853625">"Rozłączona"</item>
+ <item msgid="746097431216080650">"Niepowodzenie"</item>
+ <item msgid="6367044185730295334">"Zablokowana"</item>
+ <item msgid="503942654197908005">"Tymczasowo, by uniknąć połączenia o niskiej jakości"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Trwa skanowanie..."</item>
+ <item msgid="355508996603873860">"Trwa łączenie z siecią <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="554971459996405634">"Trwa uwierzytelnianie w sieci <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="7928343808033020343">"Uzyskiwanie adresu IP z sieci <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="8937994881315223448">"Połączono z siecią <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Zawieszona"</item>
+ <item msgid="7698638434317271902">"Trwa rozłączanie z siecią <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="197508606402264311">"Rozłączona"</item>
+ <item msgid="8578370891960825148">"Niepowodzenie"</item>
+ <item msgid="5660739516542454527">"Zablokowana"</item>
+ <item msgid="1805837518286731242">"Tymczasowo, by uniknąć połączenia o niskiej jakości"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
new file mode 100644
index 0000000..8fa5abf
--- /dev/null
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nie można wyszukać sieci."</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Brak"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Zapisana"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Wyłączona"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Błąd konfiguracji IP"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Błąd połączenia Wi-Fi"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problem z uwierzytelnianiem"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Poza zasięgiem"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Nie wykryto dostępu do internetu. Nie można automatycznie przywrócić połączenia."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Zapisane przez: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Połączono przez Asystenta Wi‑Fi"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Połączono przez %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Dostępne przez %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Połączono, brak internetu"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Rozłączona"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Rozłączanie..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Łączenie..."</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Połączony"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Parowanie..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Połączono (bez telefonu)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Połączono (bez multimediów)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Połączono (brak dostępu do wiadomości)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Połączono (bez telefonu ani multimediów)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Dźwięk multimediów"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Dźwięk telefonu"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Przesyłanie pliku"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Urządzenie wejściowe"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Dostęp do internetu"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Udostępnianie kontaktów"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Używaj do udostępniania kontaktów"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Udostępnianie połączenia internetowego"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Dostęp do wiadomości"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Dostęp do karty SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Połączono z funkcją audio multimediów"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Połączono z funkcją audio telefonu"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Połączono z serwerem transferu plików"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Połączono z mapą"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Połączono z PDU"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Brak połączenia z serwerem transferu plików"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Podłączono do urządzenia wejściowego"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Połączone w celu dostępu do internetu"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Udostępnianie połączenia internetowego"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Użyj na potrzeby dostępu do internetu"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Używaj dla mapy"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Używaj, by uzyskać dostęp do karty SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Użyj dla funkcji audio multimediów"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Użyj dla funkcji audio telefonu"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Użyj do transferu plików"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Użyj do wprowadzania"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Sparuj"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"SPARUJ"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Anuluj"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Parowanie spowoduje przyznanie dostępu do historii połączeń i Twoich kontaktów w trakcie połączenia."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Nie można sparować z urządzeniem <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Nie można sparować z urządzeniem <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ze względu na błędny kod PIN lub klucz."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Nie można skomunikować się z urządzeniem <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Powiązanie odrzucone przez urządzenie <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi wyłączone."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi odłączone."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi: jeden pasek."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wi-Fi: dwa paski."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wi-Fi: trzy paski."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Wi-Fi: pełna moc sygnału."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"System operacyjny Android"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Usunięte aplikacje"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Usunięte aplikacje i użytkownicy"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"Tethering przez USB"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Przenośny punkt dostępu"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Tethering przez Bluetooth"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Tethering"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Tethering i punkt dostępu"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Profil do pracy"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Gość"</string>
+ <string name="unknown" msgid="1592123443519355854">"Nieznana"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Użytkownik: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Część ustawień domyślnych"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Brak ustawień domyślnych"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Zamiana tekstu na mowę"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Zamiana tekstu na mowę"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Szybkość mowy"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Szybkość czytania tekstu"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Język"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Użyj języka systemu"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Nie wybrano języka"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Ustawia zależny od języka głos dla czytanych tekstów"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Posłuchaj przykładu"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Odtwórz krótką prezentację syntezy mowy"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Zainstaluj dane głosowe"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Zainstaluj dane głosowe wymagane do syntezy mowy"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Ten mechanizm syntezy mowy może gromadzić cały odczytywany tekst, w tym dane osobiste w postaci haseł i numerów kart kredytowych. Nazwa mechanizmu to <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Włączyć ten mechanizm syntezy mowy?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Do przetwarzania tekstu na mowę w tym języku wymagane jest działające połączenie sieciowe."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"To jest przykład syntezy mowy"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Stan domyślnego języka"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"Język <xliff:g id="LOCALE">%1$s</xliff:g> jest w pełni obsługiwany"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"Język <xliff:g id="LOCALE">%1$s</xliff:g> wymaga połączenia z siecią"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"Język <xliff:g id="LOCALE">%1$s</xliff:g> nie jest obsługiwany"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Sprawdzam…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Ustawienia <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Otwórz ustawienia mechanizmu"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Preferowany mechanizm"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Ogólne"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Bardzo wolno"</item>
+ <item msgid="4795095314303559268">"Powoli"</item>
+ <item msgid="8903157781070679765">"Normalnie"</item>
+ <item msgid="164347302621392996">"Szybko"</item>
+ <item msgid="5794028588101562009">"Trochę szybciej"</item>
+ <item msgid="7163942783888652942">"Szybciej"</item>
+ <item msgid="7831712693748700507">"Jeszcze szybciej"</item>
+ <item msgid="5194774745031751806">"Bardzo szybko"</item>
+ <item msgid="9085102246155045744">"Najszybciej"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Wybierz profil"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Osobiste"</string>
+ <string name="category_work" msgid="8699184680584175622">"Praca"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-pt-rBR/arrays.xml b/packages/SettingsLib/res/values-pt-rBR/arrays.xml
new file mode 100644
index 0000000..cea70da
--- /dev/null
+++ b/packages/SettingsLib/res/values-pt-rBR/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Procurando…"</item>
+ <item msgid="8513729475867537913">"Conectando..."</item>
+ <item msgid="515055375277271756">"Autenticando..."</item>
+ <item msgid="1943354004029184381">"Obtendo endereço IP…"</item>
+ <item msgid="4221763391123233270">"Conectado"</item>
+ <item msgid="624838831631122137">"Suspenso"</item>
+ <item msgid="7979680559596111948">"Desconectando…"</item>
+ <item msgid="1634960474403853625">"Desconectado"</item>
+ <item msgid="746097431216080650">"Falha"</item>
+ <item msgid="6367044185730295334">"Bloqueado"</item>
+ <item msgid="503942654197908005">"Temporariamente evitando uma conexão ruim"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Procurando…"</item>
+ <item msgid="355508996603873860">"Conectando-se a <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="554971459996405634">"Autenticando com a <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="7928343808033020343">"Obtendo endereço IP da <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="8937994881315223448">"Conectado a <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Suspenso"</item>
+ <item msgid="7698638434317271902">"Desconectando da <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="197508606402264311">"Desconectado"</item>
+ <item msgid="8578370891960825148">"Falha"</item>
+ <item msgid="5660739516542454527">"Bloqueado"</item>
+ <item msgid="1805837518286731242">"Temporariamente evitando uma conexão ruim"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
new file mode 100644
index 0000000..4dd6961
--- /dev/null
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Não é possível verificar a existência de redes"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Nenhuma"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Salva"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Desativado"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Falha de configuração de IP"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Falha de conexão Wi-Fi"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problema de autenticação"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Fora do alcance"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Nenhum acesso à Internet detectado. O dispositivo não conectará automaticamente."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Salvas por <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Conectado via assistente de Wi‑Fi"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Conectado via %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Disponível via %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Conectada, sem Internet"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Desconectado"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Desconectando…"</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Conectando..."</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Conectado"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Pareando…"</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Conectado (sem telefone)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Conectado (sem mídia)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Conectado (sem acesso a mensagens)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Conectado (sem telefone ou mídia)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Áudio da mídia"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Áudio do telefone"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Transferência de arquivo"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Dispositivo de entrada"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Acesso à Internet"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Compartilhamento de contatos"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Usar para compartilhamento de contatos"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Compartilhamento de conexão à Internet"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Acesso a mensagens"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Acesso SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Conectado ao áudio da mídia"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Conectado ao áudio do telefone"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Conectado ao servidor de transferência de arquivo"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Conectado ao mapa"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Conectado a SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Não está conectado ao servidor de transferência de arquivo"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Conectado ao dispositivo de entrada"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Conectado ao dispositivo para acesso à Internet"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Compart. conexão local de Intern. com disp."</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Usar para acesso à Internet"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Usar para mapa"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Use para acesso SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Usar para áudio de mídia"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Usar para áudio do telefone"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Usado para transferência de arquivo"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Usar para entrada"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Parear"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"PAREAR"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Cancelar"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"O pareamento dá acesso a seus contatos e ao histórico de chamadas quando estiver conectado."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Não foi possível parear com <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Não foi possível parear com <xliff:g id="DEVICE_NAME">%1$s</xliff:g> por causa de um PIN ou senha incorretos."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Não é possível se comunicar com <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Emparelhamento rejeitado por <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi desligado."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi desconectado"</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Uma barra de Wi-Fi."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Duas barras de Wi-Fi."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Três barras de Wi-Fi."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Sinal Wi-Fi cheio."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Sistema operacional Android"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Apps removidos"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Apps e usuários removidos"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"Tethering USB"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Ponto de acesso portátil"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Tethering Bluetooth"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Tethering"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Tethering e acesso portátil"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Perfil de trabalho"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Convidado"</string>
+ <string name="unknown" msgid="1592123443519355854">"Desconhecido"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Usuário: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Alguns padrões definidos"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Nenhum padrão definido"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Configurações da conversão de texto em voz"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Conversão de texto em voz"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Taxa de fala"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Velocidade em que o texto é falado"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Idioma"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Usar idioma do sistema"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Idioma não selecionado"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Define a voz específica do idioma para o texto falado"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Ouça um exemplo"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Reproduzir uma rápida demonstração da voz sintetizada"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Instalar dados de voz"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"É necessário instalar os dados de voz para a sintetização da fala"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Este mecanismo de síntese de fala pode coletar todo texto que será falado, inclusive dados pessoais como senhas e números de cartão de crédito. Ele é proveniente do mecanismo <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Deseja ativar o uso desse mecanismo de síntese de fala?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Este idioma requer uma conexão de rede ativa para a conversão de texto em voz."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Este é um exemplo de sintetização de voz."</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Status de idioma padrão"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> é totalmente suportada"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> requer conexão de rede"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> não é suportado"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Verificando..."</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Configurações para <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Iniciar configurações do mecanismo"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Mecanismo preferencial"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Gerais"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Muito devagar"</item>
+ <item msgid="4795095314303559268">"Devagar"</item>
+ <item msgid="8903157781070679765">"Normal"</item>
+ <item msgid="164347302621392996">"Rápida"</item>
+ <item msgid="5794028588101562009">"Mais rápida"</item>
+ <item msgid="7163942783888652942">"Muito rápida"</item>
+ <item msgid="7831712693748700507">"Rápida"</item>
+ <item msgid="5194774745031751806">"Muito rápida"</item>
+ <item msgid="9085102246155045744">"Super-rápida"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Escolher perfil"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Pessoal"</string>
+ <string name="category_work" msgid="8699184680584175622">"Trabalho"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-pt-rPT/arrays.xml b/packages/SettingsLib/res/values-pt-rPT/arrays.xml
new file mode 100644
index 0000000..37bd52a
--- /dev/null
+++ b/packages/SettingsLib/res/values-pt-rPT/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"A procurar..."</item>
+ <item msgid="8513729475867537913">"A ligar..."</item>
+ <item msgid="515055375277271756">"A autenticar..."</item>
+ <item msgid="1943354004029184381">"A obter endereço IP..."</item>
+ <item msgid="4221763391123233270">"Ligado"</item>
+ <item msgid="624838831631122137">"Suspenso"</item>
+ <item msgid="7979680559596111948">"A desligar..."</item>
+ <item msgid="1634960474403853625">"Desligado"</item>
+ <item msgid="746097431216080650">"Sem sucesso"</item>
+ <item msgid="6367044185730295334">"Bloqueado"</item>
+ <item msgid="503942654197908005">"A evitar temporariamente uma ligação fraca"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"A procurar..."</item>
+ <item msgid="355508996603873860">"A ligar a <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="554971459996405634">"A autenticar com <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="7928343808033020343">"A obter endereço IP de <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="8937994881315223448">"Ligado a <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Suspenso"</item>
+ <item msgid="7698638434317271902">"A desligar de <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="197508606402264311">"Desligado"</item>
+ <item msgid="8578370891960825148">"Sem sucesso"</item>
+ <item msgid="5660739516542454527">"Bloqueado"</item>
+ <item msgid="1805837518286731242">"A evitar temporariamente uma ligação fraca"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..1a54edb
--- /dev/null
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Não é possível verificar redes"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Nenhuma"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Guardada"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Desativado"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Falha de configuração de IP"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Falha de ligação Wi-Fi"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problema de autenticação"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Fora do alcance"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Nenhum acesso à Internet detetado; não será efetuada uma nova ligação automaticamente."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Guardada por <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Ligado através do Assistente de Wi‑Fi"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Ligado através de %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Disponível através de %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Ligado, sem Internet"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Desligado"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"A desligar..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"A ligar..."</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Ligado"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"A emparelhar..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Ligado (sem telefone)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Ligado (sem multimédia)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Ligado (sem acesso a mensagens)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Ligado (sem telefone ou multimédia)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Áudio de multimédia"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Áudio do telemóvel"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Transferência do ficheiro"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Dispositivo de entrada"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Acesso à internet"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Partilha de contactos"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Utilizar para a partilha de contactos"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Partilha da ligação à internet"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Acesso a mensagens"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Acesso ao SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Ligado ao áudio de multimédia"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Ligado ao áudio do telefone"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Ligado ao servidor de transferência de ficheiros"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Ligado ao mapa"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Ligado ao SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Não ligado ao servidor de transferência de ficheiros"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Ligado a um dispositivo de entrada"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Ligado ao aparelho para acesso à internet"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"A partilhar a ligação à internet local com o aparelho"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Utilizar para acesso à internet"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Utilizar para o mapa"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Utilizar para acesso ao SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Utilizar para áudio de multimédia"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Utilizar para áudio do telefone"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Utilizar para transferência de ficheiros"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Utilizar para entrada"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Par"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"SINCRONIZAR"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Cancelar"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"A sincronização concede acesso aos seus contactos e ao histórico de chamadas quando tem uma ligação estabelecida."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Não foi possível sincronizar com <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Não foi possível sincronizar com <xliff:g id="DEVICE_NAME">%1$s</xliff:g> devido a PIN ou chave de acesso incorreto."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Não é possível comunicar com <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Emparelhamento rejeitado por <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi desativado."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi desligado."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Uma barra de Wi-Fi."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Duas barras de Wi-Fi."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Três barras de Wi-Fi."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Sinal de Wi-Fi completo."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"SO Android"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Aplicações removidas"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Aplicações e utilizadores removidos"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"Ligação USB"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Hotspot portátil"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Ligação Bluetooth"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Ligação ponto a ponto"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Partilha de Internet"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Perfil de trabalho"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Convidado"</string>
+ <string name="unknown" msgid="1592123443519355854">"Desconhecido"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Utilizador: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Algumas predefinições definidas"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Nenhuma predefinição definida"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Definições de texto para voz"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Saída de texto para voz"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Taxa de voz"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Velocidade a que o texto é falado"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Idioma"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Utilizar idioma do sistema"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Idioma não selecionado"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Define a voz do idioma específico para o texto lido"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Ouvir um exemplo"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Reproduzir uma breve demonstração de síntese de voz"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Instalar dados de voz"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Instalar os dados de voz necessários para a síntese de voz"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Este motor de síntese de discurso pode permitir a recolha de todo o texto que será falado, incluindo dados pessoais, como palavras-passe e números de cartão de crédito. O serviço é fornecido com o motor <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Permitir a utilização deste motor de síntese de discurso?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Este idioma requer uma ligação de rede ativa para uma saída de síntese de voz."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Exemplo de síntese de voz."</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Estado do idioma predefinido"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> é totalmente suportado"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> necessita de ligação de rede"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> não é suportado"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"A verificar…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Definições do <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Iniciar as definições do motor"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Motor preferido"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Geral"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Muito lenta"</item>
+ <item msgid="4795095314303559268">"Lenta"</item>
+ <item msgid="8903157781070679765">"Normal"</item>
+ <item msgid="164347302621392996">"Rápida"</item>
+ <item msgid="5794028588101562009">"Mais rápida"</item>
+ <item msgid="7163942783888652942">"Muito rápida"</item>
+ <item msgid="7831712693748700507">"Acelerada"</item>
+ <item msgid="5194774745031751806">"Muito acelerada"</item>
+ <item msgid="9085102246155045744">"A mais rápida"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Escolher perfil"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Pessoal"</string>
+ <string name="category_work" msgid="8699184680584175622">"Trabalho"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-pt/arrays.xml b/packages/SettingsLib/res/values-pt/arrays.xml
new file mode 100644
index 0000000..cea70da
--- /dev/null
+++ b/packages/SettingsLib/res/values-pt/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Procurando…"</item>
+ <item msgid="8513729475867537913">"Conectando..."</item>
+ <item msgid="515055375277271756">"Autenticando..."</item>
+ <item msgid="1943354004029184381">"Obtendo endereço IP…"</item>
+ <item msgid="4221763391123233270">"Conectado"</item>
+ <item msgid="624838831631122137">"Suspenso"</item>
+ <item msgid="7979680559596111948">"Desconectando…"</item>
+ <item msgid="1634960474403853625">"Desconectado"</item>
+ <item msgid="746097431216080650">"Falha"</item>
+ <item msgid="6367044185730295334">"Bloqueado"</item>
+ <item msgid="503942654197908005">"Temporariamente evitando uma conexão ruim"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Procurando…"</item>
+ <item msgid="355508996603873860">"Conectando-se a <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="554971459996405634">"Autenticando com a <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="7928343808033020343">"Obtendo endereço IP da <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="8937994881315223448">"Conectado a <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Suspenso"</item>
+ <item msgid="7698638434317271902">"Desconectando da <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="197508606402264311">"Desconectado"</item>
+ <item msgid="8578370891960825148">"Falha"</item>
+ <item msgid="5660739516542454527">"Bloqueado"</item>
+ <item msgid="1805837518286731242">"Temporariamente evitando uma conexão ruim"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
new file mode 100644
index 0000000..4dd6961
--- /dev/null
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Não é possível verificar a existência de redes"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Nenhuma"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Salva"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Desativado"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Falha de configuração de IP"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Falha de conexão Wi-Fi"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problema de autenticação"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Fora do alcance"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Nenhum acesso à Internet detectado. O dispositivo não conectará automaticamente."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Salvas por <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Conectado via assistente de Wi‑Fi"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Conectado via %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Disponível via %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Conectada, sem Internet"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Desconectado"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Desconectando…"</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Conectando..."</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Conectado"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Pareando…"</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Conectado (sem telefone)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Conectado (sem mídia)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Conectado (sem acesso a mensagens)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Conectado (sem telefone ou mídia)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Áudio da mídia"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Áudio do telefone"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Transferência de arquivo"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Dispositivo de entrada"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Acesso à Internet"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Compartilhamento de contatos"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Usar para compartilhamento de contatos"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Compartilhamento de conexão à Internet"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Acesso a mensagens"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Acesso SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Conectado ao áudio da mídia"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Conectado ao áudio do telefone"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Conectado ao servidor de transferência de arquivo"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Conectado ao mapa"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Conectado a SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Não está conectado ao servidor de transferência de arquivo"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Conectado ao dispositivo de entrada"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Conectado ao dispositivo para acesso à Internet"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Compart. conexão local de Intern. com disp."</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Usar para acesso à Internet"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Usar para mapa"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Use para acesso SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Usar para áudio de mídia"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Usar para áudio do telefone"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Usado para transferência de arquivo"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Usar para entrada"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Parear"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"PAREAR"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Cancelar"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"O pareamento dá acesso a seus contatos e ao histórico de chamadas quando estiver conectado."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Não foi possível parear com <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Não foi possível parear com <xliff:g id="DEVICE_NAME">%1$s</xliff:g> por causa de um PIN ou senha incorretos."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Não é possível se comunicar com <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Emparelhamento rejeitado por <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi desligado."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi desconectado"</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Uma barra de Wi-Fi."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Duas barras de Wi-Fi."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Três barras de Wi-Fi."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Sinal Wi-Fi cheio."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Sistema operacional Android"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Apps removidos"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Apps e usuários removidos"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"Tethering USB"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Ponto de acesso portátil"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Tethering Bluetooth"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Tethering"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Tethering e acesso portátil"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Perfil de trabalho"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Convidado"</string>
+ <string name="unknown" msgid="1592123443519355854">"Desconhecido"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Usuário: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Alguns padrões definidos"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Nenhum padrão definido"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Configurações da conversão de texto em voz"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Conversão de texto em voz"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Taxa de fala"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Velocidade em que o texto é falado"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Idioma"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Usar idioma do sistema"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Idioma não selecionado"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Define a voz específica do idioma para o texto falado"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Ouça um exemplo"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Reproduzir uma rápida demonstração da voz sintetizada"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Instalar dados de voz"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"É necessário instalar os dados de voz para a sintetização da fala"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Este mecanismo de síntese de fala pode coletar todo texto que será falado, inclusive dados pessoais como senhas e números de cartão de crédito. Ele é proveniente do mecanismo <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Deseja ativar o uso desse mecanismo de síntese de fala?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Este idioma requer uma conexão de rede ativa para a conversão de texto em voz."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Este é um exemplo de sintetização de voz."</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Status de idioma padrão"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> é totalmente suportada"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> requer conexão de rede"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> não é suportado"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Verificando..."</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Configurações para <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Iniciar configurações do mecanismo"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Mecanismo preferencial"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Gerais"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Muito devagar"</item>
+ <item msgid="4795095314303559268">"Devagar"</item>
+ <item msgid="8903157781070679765">"Normal"</item>
+ <item msgid="164347302621392996">"Rápida"</item>
+ <item msgid="5794028588101562009">"Mais rápida"</item>
+ <item msgid="7163942783888652942">"Muito rápida"</item>
+ <item msgid="7831712693748700507">"Rápida"</item>
+ <item msgid="5194774745031751806">"Muito rápida"</item>
+ <item msgid="9085102246155045744">"Super-rápida"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Escolher perfil"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Pessoal"</string>
+ <string name="category_work" msgid="8699184680584175622">"Trabalho"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-ro/arrays.xml b/packages/SettingsLib/res/values-ro/arrays.xml
new file mode 100644
index 0000000..c4b3b34
--- /dev/null
+++ b/packages/SettingsLib/res/values-ro/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"În curs de scanare..."</item>
+ <item msgid="8513729475867537913">"Se conectează..."</item>
+ <item msgid="515055375277271756">"În curs de autentificare…"</item>
+ <item msgid="1943354004029184381">"Se obține adresa IP..."</item>
+ <item msgid="4221763391123233270">"Conectată"</item>
+ <item msgid="624838831631122137">"Suspendată"</item>
+ <item msgid="7979680559596111948">"În curs de deconectare..."</item>
+ <item msgid="1634960474403853625">"Deconectată"</item>
+ <item msgid="746097431216080650">"Nereușit"</item>
+ <item msgid="6367044185730295334">"Blocat"</item>
+ <item msgid="503942654197908005">"Evitarea temporară a conexiunii slabe"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"În curs de scanare..."</item>
+ <item msgid="355508996603873860">"Se conectează la <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="554971459996405634">"Se autentifică cu <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="7928343808033020343">"Se obține adresa IP de la <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="8937994881315223448">"Conectat la <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Suspendată"</item>
+ <item msgid="7698638434317271902">"În curs de deconectare de la <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="197508606402264311">"Deconectată"</item>
+ <item msgid="8578370891960825148">"Nereușit"</item>
+ <item msgid="5660739516542454527">"Blocat"</item>
+ <item msgid="1805837518286731242">"Evitarea temporară a conexiunii slabe"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
new file mode 100644
index 0000000..262fe97
--- /dev/null
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nu se poate scana pentru rețele"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Niciuna"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Salvată"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Dezactivată"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Eroare de configurație IP"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Eroare de conexiune Wi-Fi"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problemă la autentificare"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"În afara ariei de acoperire"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Nu s-a detectat acces la internet, nu se va efectua reconectarea automată."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Salvată de <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Conexiune realizată printr-un asistent Wi-Fi"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Conectată prin %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Disponibilă prin %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Conectată, fără internet"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Deconectat"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"În curs de deconectare..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Se conectează..."</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Conectat"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Se conectează…"</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Conectat (fără telefon)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Conectat (fără conținut media)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Conectat (fără acces la mesaje)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Conectat (fără telefon sau conț. media)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Conținut media audio"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Componenta audio a telefonului"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Transfer de fișiere"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Dispozitiv de intrare"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Acces internet"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Acces la Agendă"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Utilizați pentru a permite accesul la Agendă"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Distribuirea conexiunii la internet"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Acces la mesaje"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Acces la SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Conectat la profilul pentru conținut media audio"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Conectat la componenta audio a telefonului"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Conectat la serverul de transfer de fișiere"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Conectat la hartă"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Conectat la SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Neconectat la serverul de transfer de fișiere"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Conectat la dispozitivul de intrare"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Conectat la dispoz. pt. acces internet"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Se permite dispoz. acces la internet local"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Utilizați pentru acces internet"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Utilizați pentru hartă"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Folosiți pentru acces la SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Utilizați pentru profilul pentru conținut media audio"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Utilizați pentru componenta audio a telefonului"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Utilizați pentru transferul de fișiere"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Utilizați pentru introducere date"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Asociați"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"CONECTAȚI"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Anulați"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Asocierea dispozitivelor vă permite accesul la persoanele de contact și la istoricul apelurilor când dispozitivul este conectat."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Nu s-a putut împerechea cu <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Nu s-a putut împerechea cu <xliff:g id="DEVICE_NAME">%1$s</xliff:g> din cauza unui cod PIN sau al unei chei de acces incorecte."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Nu se poate comunica cu <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Împerechere respinsă de <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi dezactivat."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi deconectat."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Semnal Wi-Fi: o bară."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Semnal Wi-Fi: două bare."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Semnal Wi-Fi: trei bare."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Semnal Wi-Fi: complet."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Sistem de operare Android"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Aplicații eliminate"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Aplicaţii și utilizatori eliminaţi"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"Tethering prin USB"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Hotspot portabil"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Tethering prin Bluetooth"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Tethering"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Tethering și hotspot portabil"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Profil de serviciu"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Invitat"</string>
+ <string name="unknown" msgid="1592123443519355854">"Necunoscut"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Utilizator: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Unele valori prestabilite sunt configurate"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Nu este configurată nicio valoare prestabilită"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Setări text în vorbire"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Transformare text în vorbire"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Ritmul vorbirii"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Viteza cu care este vorbit textul"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Limbă"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Utilizaţi limba sistemului"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Nu ați selectat limba"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Setează vocea caracteristică limbii pentru textul vorbit"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Ascultați un exemplu"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Redați o demonstrație scurtă a sintetizării vorbirii"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Instalați date vocale"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Instalați datele vocale necesare pentru sintetizarea vorbirii"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Acest motor de sintetizare a vorbirii poate culege în întregime textul vorbit, inclusiv datele personale cum ar fi parolele și numerele cărților de credit. Metoda provine de la motorul <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Permiteți utilizarea acestui motor de sintetizare a vorbirii?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Pentru rezultatul transformării textului în vorbire pentru această limbă este necesară o conexiune de rețea care să funcţioneze."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Acesta este un exemplu de sintetizare a vorbirii"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Starea limbii prestabilite"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> este acceptată integral"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> necesită conexiune la rețea"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> nu este acceptată"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Se verifică…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Setări pentru <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Lansați setările motorului"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Motor preferat"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Preferințe generale"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Foarte încet"</item>
+ <item msgid="4795095314303559268">"Încet"</item>
+ <item msgid="8903157781070679765">"Normal"</item>
+ <item msgid="164347302621392996">"Repede"</item>
+ <item msgid="5794028588101562009">"Mai repede"</item>
+ <item msgid="7163942783888652942">"Foarte repede"</item>
+ <item msgid="7831712693748700507">"Rapid"</item>
+ <item msgid="5194774745031751806">"Foarte rapid"</item>
+ <item msgid="9085102246155045744">"Cel mai repede"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Alegeți un profil"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Personal"</string>
+ <string name="category_work" msgid="8699184680584175622">"Serviciu"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-ru/arrays.xml b/packages/SettingsLib/res/values-ru/arrays.xml
new file mode 100644
index 0000000..5f50648
--- /dev/null
+++ b/packages/SettingsLib/res/values-ru/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Поиск..."</item>
+ <item msgid="8513729475867537913">"Подключение..."</item>
+ <item msgid="515055375277271756">"Аутентификация..."</item>
+ <item msgid="1943354004029184381">"Получение IP-адреса..."</item>
+ <item msgid="4221763391123233270">"Подключено"</item>
+ <item msgid="624838831631122137">"Приостановлено"</item>
+ <item msgid="7979680559596111948">"Отключение..."</item>
+ <item msgid="1634960474403853625">"Нет подключения"</item>
+ <item msgid="746097431216080650">"Сбой"</item>
+ <item msgid="6367044185730295334">"Заблокировано"</item>
+ <item msgid="503942654197908005">"Временно избегать плохого соединения"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Поиск..."</item>
+ <item msgid="355508996603873860">"Соединение с сетью <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="554971459996405634">"Аутентификация в сети <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="7928343808033020343">"Получение IP-адреса от <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="8937994881315223448">"Подключено к <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Приостановлено"</item>
+ <item msgid="7698638434317271902">"Отключение от <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="197508606402264311">"Нет подключения"</item>
+ <item msgid="8578370891960825148">"Сбой"</item>
+ <item msgid="5660739516542454527">"Заблокировано"</item>
+ <item msgid="1805837518286731242">"Временно избегать плохого соединения"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
new file mode 100644
index 0000000..31b70ad
--- /dev/null
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Не удалось начать поиск сетей."</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Нет"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Сохранено"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Отключено"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Ошибка IP-конфигурации"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Ошибка подключения Wi-Fi"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Ошибка аутентификации"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Недоступна"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Подключение к Интернету отсутствует и не будет восстановлено автоматически."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Кто сохранил: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Установлено подключение через Ассистента Wi-Fi"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Подключено к %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Доступно через %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Подключено, без Интернета"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Отключено"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Отключение..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Подключение..."</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Подключено"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Сопряжение..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Подключено (кроме HSP/HFP)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Подключено (кроме A2DP)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Подключено (нет доступа к сообщениям)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Подключено (кроме HSP/HFP/A2DP)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Профиль A2DP"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Профиль HSP/HFP"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Профиль OPP"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Профиль HID"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Интернет-доступ"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Обмен контактами"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Использовать для обмена контактами"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Профиль PAN"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Доступ к сообщениям"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Доступ к SIM-карте"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Подключено к мультимедийному аудиоустройству"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Подключено к аудиоустройству телефона"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Установлено подключение к серверу передачи файлов"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Доступ к сообщениям"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Подключено к точке доступа"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Нет подключения к серверу передачи файлов"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Подключено к устройству ввода"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Используется интернет-подключение другого устройства"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Устройство работает в режиме модема"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Использовать для доступа к Интернету"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Использовать для доступа к сообщениям"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Использовать для доступа к SIM-карте"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Использовать для мультимедийного аудиоустройства"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Использовать для аудиоустройства телефона"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Используется для передачи файлов"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Использовать для ввода"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Подключить"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ПОДКЛЮЧИТЬ"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Отмена"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Сопряжение обеспечивает доступ к вашим контактам и журналу звонков при подключении."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Не удалось подключиться к устройству \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\"."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Не удалось подключиться к устройству \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\", так как введен неверный PIN-код или пароль."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Не удается установить соединение с устройством \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\"."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> не разрешает сопряжение."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi выключен"</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi отключен"</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi: одно деление"</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wi-Fi: два деления"</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wi-Fi: три деления"</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Wi-Fi: надежный сигнал"</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"ОС Android"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Удаленные приложения"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Удаленные приложения и пользователи"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB-модем"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Точка доступа Wi-Fi"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth-модем"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Режим модема"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Режим модема"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Рабочий профиль"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Гость"</string>
+ <string name="unknown" msgid="1592123443519355854">"Неизвестно"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Пользователь: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Установлены некоторые настройки по умолчанию"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Настройки по умолчанию не установлены"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Настройки синтеза речи"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Синтез речи"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Скорость речи"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Скорость чтения текста"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Язык"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Язык системы"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Язык не выбран"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Выбор языка для чтения текста"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Прослушать пример"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Воспроизвести краткую демонстрацию синтезированной речи"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Установка голосовых данных"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Установить голосовые данные, необходимые для синтеза речи"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Данная система синтеза речи может записывать произносимые слова, включая личные данные, такие как пароли и номера кредитных карт. Это осуществляет система <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Разрешить использование этой системы синтеза речи?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Чтобы включить синтезатор речи для этого языка, необходимо подключение к Интернету."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Это пример синтеза речи."</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Язык по умолчанию"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> поддерживается"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> поддерживается только при подключении к сети"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> не поддерживается"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Проверка…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Настройки синтеза речи"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Система по умолчанию"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Общие"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Очень медленная"</item>
+ <item msgid="4795095314303559268">"Медленная"</item>
+ <item msgid="8903157781070679765">"Обычная"</item>
+ <item msgid="164347302621392996">"Умеренно быстрая"</item>
+ <item msgid="5794028588101562009">"Быстрая"</item>
+ <item msgid="7163942783888652942">"Очень быстрая"</item>
+ <item msgid="7831712693748700507">"Ускоренная"</item>
+ <item msgid="5194774745031751806">"Сильно ускоренная"</item>
+ <item msgid="9085102246155045744">"Максимальная"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Выберите профиль"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Личные данные"</string>
+ <string name="category_work" msgid="8699184680584175622">"Работа"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-si-rLK/arrays.xml b/packages/SettingsLib/res/values-si-rLK/arrays.xml
new file mode 100644
index 0000000..225ea36
--- /dev/null
+++ b/packages/SettingsLib/res/values-si-rLK/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"පරිලෝකනය කරමින්…"</item>
+ <item msgid="8513729475867537913">"සම්බන්ධ වෙමින්…"</item>
+ <item msgid="515055375277271756">"සත්යාපනය වෙමින්…"</item>
+ <item msgid="1943354004029184381">"IP ලිපිනය ලබාගනිමින්…"</item>
+ <item msgid="4221763391123233270">"සම්බන්ධිතයි"</item>
+ <item msgid="624838831631122137">"අත්හිටුවන ලදි"</item>
+ <item msgid="7979680559596111948">"විසන්ධි වෙමින්…"</item>
+ <item msgid="1634960474403853625">"විසන්ධි වුණි"</item>
+ <item msgid="746097431216080650">"අසාර්ථකයි"</item>
+ <item msgid="6367044185730295334">"අවහිර කරන ලදි"</item>
+ <item msgid="503942654197908005">"දුර්වල සම්බන්ධතාවය තාවකාලිකව මඟහරිමින්"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"පරිලෝකනය කරමින්…"</item>
+ <item msgid="355508996603873860">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> වෙත සම්බන්ධ වෙමින්…"</item>
+ <item msgid="554971459996405634">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> සමග සත්යාපනය කරමින්…"</item>
+ <item msgid="7928343808033020343">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> මගින් IP ලිපිනය ලබා ගනිමින්"</item>
+ <item msgid="8937994881315223448">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> වෙත සම්බන්ධ වුණි"</item>
+ <item msgid="1330262655415760617">"අත්හිටුවන ලදි"</item>
+ <item msgid="7698638434317271902">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> වෙතින් විසන්ධි වෙමින්…"</item>
+ <item msgid="197508606402264311">"විසන්ධි විය"</item>
+ <item msgid="8578370891960825148">"අසාර්ථකයි"</item>
+ <item msgid="5660739516542454527">"අවහිර කරන ලදි"</item>
+ <item msgid="1805837518286731242">"දුර්වල සම්බන්ධතාවය තාවකාලිකව මඟහරිමින්"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-si-rLK/strings.xml b/packages/SettingsLib/res/values-si-rLK/strings.xml
new file mode 100644
index 0000000..6ff81b7
--- /dev/null
+++ b/packages/SettingsLib/res/values-si-rLK/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"ජාල සඳහා පරිලෝකනය කළ නොහැක"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"කිසිවක් නැත"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"සුරකින ලදි"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"අබලයි"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP වින්යාස කිරීම අසාර්ථකයි"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi සම්බන්ධතාව අසාර්ථකයි"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"සත්යාපනයේ ගැටලුවකි"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"පරාසයේ නැත"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"අන්තර්ජාල ප්රවේශය අනාවරණය වුයේ නැත, ස්වයංක්රිය නැවත සම්බන්ධ වීම වූ නැත"</string>
+ <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> විසින් සුරකින ලදී"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fi සහායක හරහා සම්බන්ධ කරන ලදි"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s හරහා සම්බන්ධ විය"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s හරහා ලබා ගැනීමට හැකිය"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"සම්බන්ධයි, අන්තර්ජාලය නැත"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"විසන්ධි වුණි"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"විසන්ධි වෙමින්…"</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"සම්බන්ධ වෙමින්…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"සම්බන්ධිතයි"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"යුගල කරමින්…"</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"සම්බන්ධයි (දුරකථන නැත)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"සම්බන්ධිතයි (මාධ්යයක් නොමැත)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"සම්බන්ධිතයි (පණිවිඩ ප්රවේශ නොමැත)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"සම්බන්ධිතයි (දුරකතනයක් හෝ මාධ්යයක් නැත)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"මාධ්ය ශ්රව්ය"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"දුරකථන ශ්රව්ය"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"ගොනු හුවමාරුව"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"ආදාන උපාංගය"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"අන්තර්ජාල ප්රවේශය"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"සම්බන්ධතා බෙදාගැනීම"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"සම්බන්ධතා බෙදාගැනීම සඳහා භාවිතා කිරීම"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"අන්තර්ජාල සම්බන්ධතා බෙදාගැනීම"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"පණිවිඩ ප්රවේශය"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM ප්රවේශය"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"මාධ්ය ශ්රව්යට සම්බන්ධ විය"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"දුරකතනයේ ශ්රව්යට සම්බන්ධ විය"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ගොනු හුවමාරු සේවාදායකය සමග සම්බන්ධ විය"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"සිතියම වෙත සම්බන්ධිතයි"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"SAP වෙත සම්බන්ධ විය"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"ගොනු හුවමාරු සේවාදායකය වෙත සම්බන්ධ වී නොමැත"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"ආදාන උපාංග වෙත සම්බන්ධිතයි"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"අන්තර්ජාල ප්රවේශය සඳහා උපාංගය වෙත සම්බන්ධ වුණි"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"මෙම උපාංගය සමඟ පෙදෙසි අන්තර්ජාල සම්බන්ධතාවය බෙදාගනිමින්"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"අන්තර්ජාල ප්රවේශය සඳහා භාවිතා කරන්න"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"සිතියම සඳහා භාවිතා කරන්න"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"SIM ප්රවේශය සඳහා භාවිත කරන්න"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"මාධ්ය ශ්රව්ය සඳහා භාවිතා කරන්න"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"දුරකථන ශ්රව්ය සඳහා භාවිතා කෙරේ"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ගොනු හුවමාරුව සඳහා භාවිතා කරන්න"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ආදානය සඳහා භාවිතා කරන්න"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"යුගල කරන්න"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"යුගල කරන්න"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"අවලංගු කරන්න"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"සමබන්ධ වන විට ඔබගේ සම්බන්ධතා සහ ඇමතුම් ඉතිහාසයට යුගළ කිරීමට ප්රවේශය දෙන්න."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> සමඟ යුගල කළ නොහැකි විය."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"වැරදි PIN එකක් හෝ පාස් යතුරක් නිසා <xliff:g id="DEVICE_NAME">%1$s</xliff:g> සමඟ යුගල කිරීමට නොහැකිය."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> සමඟ සන්නිවේදනය කළ නොහැක."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> විසින් යුගල කිරීම ප්රතික්ෂේප කරන ලදි."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wifi අක්රියයි."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wifi සම්බන්ධ කර නොමැත."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wifi තීරු එකයි."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wifi තීරු දෙකයි."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"WiFi තීරු තුනයි."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Wifi සංඥාව පිරී ඇත."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"ඉවත් කළ යෙදුම්"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"යෙදුම් සහ පරිශීලකයින් ඉවත් කරන ලදි"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB ටෙදරින්"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ජංගම හොට්ස්පොට්"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"බ්ලූටූත් ටෙදරින්"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"ටෙදරින්"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"ටෙදරින් සහ සුවහනීය හොට්ස්පොට්"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"කාර්යාල පැතිකඩ"</string>
+ <string name="user_guest" msgid="8475274842845401871">"අමුත්තා"</string>
+ <string name="unknown" msgid="1592123443519355854">"නොදනී"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"පරිශීලකයා: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"ඇතැම් පෙරනිමියන් සකස් කර ඇත"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"පෙරනිමියන් සකසා නොමැත"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"පෙළ-සිට-කථාවට සැකසුම්"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"පෙළ-සිට-කථන ප්රතිදානය"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"කථන ශීඝ්රතාව"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"පෙළ කථා කරනා වේගය"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"භාෂාව"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"පද්ධති භාෂාව භාවිතා කරන්න"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"භාෂාව තෝරා ගෙන නැත"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"කථා කරන පෙළ සඳහා භාෂා-විශේෂිත හඬ සකසන්න"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"උදාහරණයකට සවන් දෙන්න"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"කථන සංස්ලෙෂණයේ කුඩා ආදර්ශනයක් ධාවනය කරන්න"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"හඬ දත්ත ස්ථාපනය කරන්න"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"කථන සංකලනයට අවශ්ය හඬ දත්ත ස්ථාපනය කරන්න"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"මෙම කථා සංස්ලේෂණ යන්ත්රයට කථා කරන සියළුම පෙළ එකතු කර ගත හැකිය, මුරපද සහ ණයපත් අංකද ඇතුලත්ව. එය පැමිණ ඇත්තේ <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> යන්ත්රයෙනි. මෙම කථා සංස්ලේෂණ යන්ත්රයෙහි භාවිතය සබල කරන්නද?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"මෙම භාෂාවට පෙළ-සිට-කථනය ප්රතිදානය සඳහා වැඩ කරන ජාල සම්බන්ධතාවයක් අවශ්යයි."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"මෙය කථන සංස්ලේෂණයට උදාහරණයකි"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"සුපුරුදු භාෂා තත්වය"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> මුළුමනින්ම සහාය දක්වයි"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> සඳහා ජාල සම්බන්ධතාවයක් අවශ්යයි"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> සහාය නොදක්වයි"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"පරික්ෂා කරමින්..."</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g> සඳහා සැකසුම්"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"එන්ජිම් සැකසීම් දියත් කරන්න"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"වරණ එන්ජිම"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"සාමාන්ය"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"ඉතා මන්දගාමී"</item>
+ <item msgid="4795095314303559268">"මන්දගාමී"</item>
+ <item msgid="8903157781070679765">"සාමාන්ය"</item>
+ <item msgid="164347302621392996">"වේගවත්"</item>
+ <item msgid="5794028588101562009">"වේශවත්"</item>
+ <item msgid="7163942783888652942">"ඉතා වේගවත්"</item>
+ <item msgid="7831712693748700507">"ශීඝ්ර"</item>
+ <item msgid="5194774745031751806">"ඉතා ශීඝ්ර"</item>
+ <item msgid="9085102246155045744">"ඉතාම වේගවත්"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"පැතිකඩ තෝරන්න"</string>
+ <string name="category_personal" msgid="1299663247844969448">"පෞද්ගලික"</string>
+ <string name="category_work" msgid="8699184680584175622">"කාර්යාලය"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-sk/arrays.xml b/packages/SettingsLib/res/values-sk/arrays.xml
new file mode 100644
index 0000000..6cab133
--- /dev/null
+++ b/packages/SettingsLib/res/values-sk/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Prebieha vyhľadávanie..."</item>
+ <item msgid="8513729475867537913">"Prebieha pripájanie…"</item>
+ <item msgid="515055375277271756">"Prebieha overovanie…"</item>
+ <item msgid="1943354004029184381">"Získava sa adresa IP…"</item>
+ <item msgid="4221763391123233270">"Pripojené"</item>
+ <item msgid="624838831631122137">"Pozastavená"</item>
+ <item msgid="7979680559596111948">"Prebieha odpájanie..."</item>
+ <item msgid="1634960474403853625">"Odpojený"</item>
+ <item msgid="746097431216080650">"Neúspešné"</item>
+ <item msgid="6367044185730295334">"Blokované"</item>
+ <item msgid="503942654197908005">"Dočasne bolo zabránené slabému pripojeniu"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Prebieha vyhľadávanie..."</item>
+ <item msgid="355508996603873860">"Prebieha pripájanie k sieti <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="554971459996405634">"Prebieha overovanie v sieti <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="7928343808033020343">"Získava sa adresa IP zo siete <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="8937994881315223448">"Pripojené k sieti <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Pozastavená"</item>
+ <item msgid="7698638434317271902">"Prebieha odpájanie od siete <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="197508606402264311">"Odpojený"</item>
+ <item msgid="8578370891960825148">"Neúspešné"</item>
+ <item msgid="5660739516542454527">"Blokované"</item>
+ <item msgid="1805837518286731242">"Dočasne bolo zabránené slabému pripojeniu"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
new file mode 100644
index 0000000..a499dc5
--- /dev/null
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Siete sa nedajú vyhľadávať"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Žiadne"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Uložené"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Zakázané"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Zlyhanie konfigurácie adresy IP"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Zlyhanie pripojenia Wi-Fi"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problém s overením totožnosti"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Mimo dosah"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Nenašiel sa žiadny prístup k internetu, preto nedôjde k automatickému opätovnému pripojeniu"</string>
+ <string name="saved_network" msgid="4352716707126620811">"Uložil(a) <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Pripojené pomocou Asistenta Wi-Fi"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Pripojené prostredníctvom %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"K dispozícii prostredníctvom %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Pripojené, žiadny internet"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Odpojený"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Prebieha odpájanie..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Prebieha pripájanie…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Pripojené"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Párovanie..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Pripojené (bez telefónu)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Pripojené (bez média)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Pripojené (bez prístupu ku správam)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Pripojené (bez telefónu alebo média)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Zvuk medií"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Zvuk telefónu"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Prenos súborov"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Vstupné zariadenie"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Prístup na Internet"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Zdieľanie kontaktov"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Použiť na zdieľanie kontaktov"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Zdieľanie pripojenia na Internet"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Prístup ku správam"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Prístup k SIM karte"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Pripojené ku zvukovému médiu"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Pripojené ku zvuku telefónu"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Pripojené na server pre prenos údajov"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Pripojené k mape"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Pripojené k systému SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Nepripojené k serveru pre prenos súborov"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Pripojené na vstupné zariadenie"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Pripoj. k zariad. s príst. na Internet"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Zdieľa miestne internet. pripoj. so zariad"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Použiť na prístup k Internetu"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Použiť pre mapu"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Použiť na pristupovanie k SIM karte"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Umožňuje pripojenie zvukového média"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Použiť pre zvuk telefónu"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Použiť na prenos súborov"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Použiť pre vstup"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Párovať"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"PÁROVAŤ"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Zrušiť"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Párovaním udelíte zariadeniam po pripojení prístup k svojim kontaktom a histórii hovorov."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Nepodarilo sa spárovať so zariadením <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Nepodarilo sa spárovať so zariadením <xliff:g id="DEVICE_NAME">%1$s</xliff:g>, pretože ste zadali nesprávny kód PIN alebo prístupový kľúč."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"So zariadením <xliff:g id="DEVICE_NAME">%1$s</xliff:g> nie je možné komunikovať."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Párovanie odmietnuté zariadením <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Sieť Wi-Fi je vypnutá."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Sieť Wi-Fi je odpojená."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Jedna čiarka signálu Wi-Fi."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Dve čiarky signálu Wi-Fi."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Tri čiarky signálu Wi-Fi."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Plný signál Wi-Fi."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"OS Android"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Odstránené aplikácie"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Odstránené aplikácie a používatelia"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"Zdieľané pripojenie cez USB"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Prenosný prístupový bod"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Pripojenie cez Bluetooth"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Zdieľanie dát. pripojenia"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Zdieľané pripojenie a prenosný hotspot"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Pracovný profil"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Hosť"</string>
+ <string name="unknown" msgid="1592123443519355854">"Neznáme"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Používateľ: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Predvolená pre niektoré akcie"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Nie je predvolená pre žiadne akcie"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Nastavenia prevodu textu na reč"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Výstup prevodu textu na reč"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Rýchlosť reči"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Rýchlosť hovoreného textu"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Jazyk"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Používať jazyk systému"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Nebol vybratý jazyk"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Nastavenia jazyka hlasu pre hovorený text"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Vypočuť príklad"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Prehrať krátku ukážku syntézy reči"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Nainštalovať hlasové dáta"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Nainštalovať hlasové dáta potrebné na syntézu reči"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Nástroj na syntézu reči môže zhromažďovať všetok hovorený text, vrátane osobných údajov, ako sú heslá alebo čísla kreditných kariet. Je založený na nástroji <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Chcete aktivovať nástroj na syntézu reči?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"V prípade tohto jazyka je na prevod textu na rečový výstup potrebné pripojenie k sieti."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Toto je príklad syntézy reči"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Stav predvoleného jazyka"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"Jazyk <xliff:g id="LOCALE">%1$s</xliff:g> je plne podporovaný"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"Jazyk <xliff:g id="LOCALE">%1$s</xliff:g> vyžaduje pripojenie k sieti"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"Jazyk <xliff:g id="LOCALE">%1$s</xliff:g> nie je podporovaný"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Kontroluje sa..."</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Spustiť nastavenia nástroja"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Preferovaný nástroj"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Všeobecné"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Veľmi pomaly"</item>
+ <item msgid="4795095314303559268">"Pomaly"</item>
+ <item msgid="8903157781070679765">"Normálne"</item>
+ <item msgid="164347302621392996">"Rýchlo"</item>
+ <item msgid="5794028588101562009">"Rýchlejšie"</item>
+ <item msgid="7163942783888652942">"Veľmi rýchlo"</item>
+ <item msgid="7831712693748700507">"Svižne"</item>
+ <item msgid="5194774745031751806">"Veľmi rýchlo"</item>
+ <item msgid="9085102246155045744">"Najrýchlejšie"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Výber profilu"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Osobné"</string>
+ <string name="category_work" msgid="8699184680584175622">"Práca"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-sl/arrays.xml b/packages/SettingsLib/res/values-sl/arrays.xml
new file mode 100644
index 0000000..fef1fdd
--- /dev/null
+++ b/packages/SettingsLib/res/values-sl/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Iskanje …"</item>
+ <item msgid="8513729475867537913">"Vzpostavljanje povezave ..."</item>
+ <item msgid="515055375277271756">"Preverjanje pristnosti ..."</item>
+ <item msgid="1943354004029184381">"Pridobivanje naslova IP …"</item>
+ <item msgid="4221763391123233270">"Povezava je vzpostavljena"</item>
+ <item msgid="624838831631122137">"Odloženo"</item>
+ <item msgid="7979680559596111948">"Prekinjanje povezave ..."</item>
+ <item msgid="1634960474403853625">"Prekinjena povezava"</item>
+ <item msgid="746097431216080650">"Ni uspelo"</item>
+ <item msgid="6367044185730295334">"Blokirano"</item>
+ <item msgid="503942654197908005">"Začasno izogibanje slabi povezavi"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Iskanje …"</item>
+ <item msgid="355508996603873860">"Vzpostavljanje povezave z omrežjem <xliff:g id="NETWORK_NAME">%1$s</xliff:g> …"</item>
+ <item msgid="554971459996405634">"Preverjanje pristnosti v omrežju <xliff:g id="NETWORK_NAME">%1$s</xliff:g> …"</item>
+ <item msgid="7928343808033020343">"Pridobivanje naslova IP od <xliff:g id="NETWORK_NAME">%1$s</xliff:g> …"</item>
+ <item msgid="8937994881315223448">"Povezava z omrežjem <xliff:g id="NETWORK_NAME">%1$s</xliff:g> je vzpostavljena"</item>
+ <item msgid="1330262655415760617">"Začasno ustavljeno"</item>
+ <item msgid="7698638434317271902">"Prekinjanje povezave z omrežjem <xliff:g id="NETWORK_NAME">%1$s</xliff:g> …"</item>
+ <item msgid="197508606402264311">"Prekinjena povezava"</item>
+ <item msgid="8578370891960825148">"Ni uspelo"</item>
+ <item msgid="5660739516542454527">"Blokirano"</item>
+ <item msgid="1805837518286731242">"Začasno izogibanje slabi povezavi"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
new file mode 100644
index 0000000..b2d047b
--- /dev/null
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Ni mogoče iskati omrežij"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Brez"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Shranjeno"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Onemogočeno"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Konfiguracija IP-ja ni uspela"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Povezava prek Wi-Fi-ja ni uspela"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Težava s preverjanjem pristnosti"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Ni v obsegu"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Ni zaznanega dostopa do interneta; samodejna vnovična vzpostavitev povezave se ne bo izvedla."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Shranil(-a): <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Povezava vzpostavljena prek pomočnika za Wi-Fi"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Vzpostavljena povezava prek: %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Na voljo prek: %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Vzpostavljena povezava, brez interneta"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Prekinjena povezava"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Prekinjanje povezave ..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Vzpostavljanje povezave ..."</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Povezava je vzpostavljena"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Seznanjanje ..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Povezava vzpostavljena (brez telefona)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Povezava vzpostavljena (brez predstavnosti)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Povezava vzp. (ni dostopa do sporočil)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Povezava vzpostavljena (brez telefona ali predstavnosti)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Zvok predstavnosti"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Zvok telefona"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Prenos datoteke"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Vnosna naprava"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Internetni dostop"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Dajanje stikov v skupno rabo"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Uporabi za dajanje stikov v skupno rabo"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Skupna raba internetne povezave"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Dostop do sporočil"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Dostop do kartice SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Povezan s profilom za predstavnostni zvok"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Povezava s profilom za zvok telefona vzpostavljena"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Povezava s strežnikom za prenos datotek je vzpostavljena"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Povezava je vzpostavljena z zemljevidom"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Vzpostavljena povezava s profilom SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Povezava s strežnikom za prenos datotek ni vzpostavljena"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Povezava z vnosno napravo je vzpostavljena"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Povezava z napravo za internetni dostop"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Skupna raba lok. internetne povezave z napravo"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Uporabi za dostop do interneta"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Uporabi za zemljevid"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Uporablja se za dostop do kartice SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Uporabi za zvok predstavnosti"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Uporabi za zvok telefona"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Uporabi za prenos datotek"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Uporabi za vnos"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Seznani"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"SEZNANI"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Prekliči"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Seznanjanje pri vzpostavljeni povezavi omogoči dostop do vaših stikov in zgodovine klicev."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Ni bilo mogoče vzpostaviti povezave z napravo <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Zaradi nepravilne kode PIN ali gesla ni mogoče vzpostaviti povezave z napravo <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Z napravo <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ni mogoče vzpostaviti povezave."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Naprava <xliff:g id="DEVICE_NAME">%1$s</xliff:g> je zavrnila seznanitev."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi je izklopljen."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Povezava Wi-Fi je prekinjena."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Ena črtica signala Wi-Fi."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Dve črtici signala Wi-Fi."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Tri črtice signala Wi-Fi."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Poln signal Wi-Fi."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"OS Android"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Odstranjene aplikacije"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Odstranjene aplikacije in uporabniki"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"Internet prek USB-ja"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Prenosna dostopna točka"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Internet prek Bluetootha"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Internet prek mob. napr."</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Modem/prenosna dost. točka"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Delovni profil"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Gost"</string>
+ <string name="unknown" msgid="1592123443519355854">"Neznano"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Uporabnik: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Nastavljene so nekatere privzete nastavitve"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Ni privzetih nastavitev"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Nastavitve pretvorbe besedila v govor"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Besedilo v govor"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Hitrost govora"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Hitrost govorjenega besedila"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Jezik"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Uporabi sistemski jezik"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Jezik ni izbran"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Nastavite glas, odvisen od jezika, za govorjeno besedilo"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Poslušaj primer"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Predvajaj kratko predstavitev sinteze govora"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Namesti glasovne podatke"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Namestite govorne datoteke, ki jih potrebujete za sintezo govora"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Ta mehanizem za sintezo govora bo morda lahko zbiral izgovorjeno besedilo, vključno z osebnimi podatki, kot so gesla in številke kreditnih kartic. Omogoča ga mehanizem <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Ali želite omogočiti uporabo tega mehanizma za sintezo govora?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Za pretvorbo besedila v govor potrebuje ta jezik delujočo omrežno povezavo."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"To je primer sinteze govora."</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Stanje privzetega jezika"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"Jezik <xliff:g id="LOCALE">%1$s</xliff:g> je v celoti podprt"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> zahteva omrežno povezavo"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"Jezik <xliff:g id="LOCALE">%1$s</xliff:g> ni podprt"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Preverjanje ..."</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Nastavitve za <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Zagon nastavitev mehanizma"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Prednostni mehanizem"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Splošno"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Zelo počasi"</item>
+ <item msgid="4795095314303559268">"Počasi"</item>
+ <item msgid="8903157781070679765">"Običajno"</item>
+ <item msgid="164347302621392996">"Hitro"</item>
+ <item msgid="5794028588101562009">"Hitreje"</item>
+ <item msgid="7163942783888652942">"Zelo hitro"</item>
+ <item msgid="7831712693748700507">"Naglo"</item>
+ <item msgid="5194774745031751806">"Zelo naglo"</item>
+ <item msgid="9085102246155045744">"Najhitreje"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Izbira profila"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Osebno"</string>
+ <string name="category_work" msgid="8699184680584175622">"Služba"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-sq-rAL/arrays.xml b/packages/SettingsLib/res/values-sq-rAL/arrays.xml
new file mode 100644
index 0000000..5aceb09
--- /dev/null
+++ b/packages/SettingsLib/res/values-sq-rAL/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Po skanon..."</item>
+ <item msgid="8513729475867537913">"Po lidhet..."</item>
+ <item msgid="515055375277271756">"Po vërteton…"</item>
+ <item msgid="1943354004029184381">"Po merr adresën IP…"</item>
+ <item msgid="4221763391123233270">"I lidhur"</item>
+ <item msgid="624838831631122137">"I pezulluar"</item>
+ <item msgid="7979680559596111948">"Po shkëputet..."</item>
+ <item msgid="1634960474403853625">"I shkëputur"</item>
+ <item msgid="746097431216080650">"I pasuksesshëm"</item>
+ <item msgid="6367044185730295334">"I bllokuar"</item>
+ <item msgid="503942654197908005">"Po shmang përkohësisht lidhje të dobët"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Po skanon..."</item>
+ <item msgid="355508996603873860">"Po lidhet me <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="554971459996405634">"Po kryen vërtetimin me <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="7928343808033020343">"Po merr adresën IP nga <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="8937994881315223448">"I lidhur me <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"I pezulluar"</item>
+ <item msgid="7698638434317271902">"I shkëputur nga <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="197508606402264311">"I shkëputur"</item>
+ <item msgid="8578370891960825148">"I pasuksesshëm"</item>
+ <item msgid="5660739516542454527">"I bllokuar"</item>
+ <item msgid="1805837518286731242">"Përkohësisht duke shmangur një lidhje të dobët"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-sq-rAL/strings.xml b/packages/SettingsLib/res/values-sq-rAL/strings.xml
new file mode 100644
index 0000000..87390b4
--- /dev/null
+++ b/packages/SettingsLib/res/values-sq-rAL/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nuk mund të skanojë për rrjete"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Asnjë"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"U ruajt"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Të çaktivizuara"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Dështim në konfigurimin e IP-së"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Dështim i lidhjes WiFi"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problem me vërtetimin"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Nuk është brenda rrezes"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Nuk u diktua qasje në internet. Lidhja nuk do të realizohet automatikisht."</string>
+ <string name="saved_network" msgid="4352716707126620811">"E ruajtur nga <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"I lidhur nëpërmjet ndihmësit të Wi‑Fi"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"E lidhur përmes %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"E mundshme përmes %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"U lidh, nuk ka internet"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Shkëputur"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Po shkëputet..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Po lidhet..."</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"U lidh"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Po çiftohet..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"U lidh (pa telefon)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"U lidh (nuk ka media)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"U lidh (pa qasje te mesazhet)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"I lidhur (pa telefon apo media)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Audioja e klipit \"media\""</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Audioja e telefonit"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Transferimi i skedarëve"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Pajisja e hyrjes"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Qasja në internet"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Ndarja e kontakteve"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Përdore për ndarjen e kontakteve"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Ndarja e lidhjes së internetit"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Qasja në mesazhe"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Qasje në kartën SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"U lidh me audion e medias"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"U lidh me audion e telefonit"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"U lidh me serverin e transferimit të skedarëve"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"U lidh me hartën"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Lidhur me SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Nuk u lidh me serverin e transferimit të skedarëve"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"U lidh me pajisjen e hyrjes"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Lidhur me pajisjen për qasje në internet"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Po ndan lidhjen lokale të internetit me pajisjen"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Përdor për qasje në internet"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Përdore për hartën"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Përdor për qasje në kartën SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Përdor për audion e medias"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Përdor për audion e telefonit"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Përdor për transferimin e skedarëve"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Përdore për hyrjen"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Çifto"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ÇIFTO"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Anulo"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Çiftimi lejon qasjen te kontaktet dhe historiku yt i telefonatave."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Nuk mundi të çiftohej me <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Nuk mundi të çiftohej me <xliff:g id="DEVICE_NAME">%1$s</xliff:g> për shkak të një kodi PIN ose një kodi të pasaktë."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Nuk mund të komunikohet me <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Çiftimi u refuzua nga <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi është çaktivizuar."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi është i shkëputur."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi ka një vijë."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wi-Fi ka dy vija."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wi-Fi: tre vija."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Wi-Fi ka sinjal të plotë."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Sistemi operativ Android"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Aplikacionet e hequra"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Aplikacionet dhe përdoruesit e hequr"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"Ndarje përmes USB-së"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Zona e qasjes e lëvizshme"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Ndarje interneti përmes Bluetooth-it"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Ndarja e internetit"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Zonë qasjeje dhe ndarjeje interneti"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Profili i punës"</string>
+ <string name="user_guest" msgid="8475274842845401871">"I ftuar"</string>
+ <string name="unknown" msgid="1592123443519355854">"I panjohur"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Përdoruesi: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Disa caktime me parazgjedhje"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Nuk janë caktuar parazgjedhje"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Cilësimet \"tekst-në-ligjërim\""</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Dalja \"tekst-në-ligjërim\""</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Shpejtësia e të folurit"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Shpejtësia me të cilën thuhet teksti"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Gjuha"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Përdor gjuhën e sistemit"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Nuk është përzgjedhur gjuha"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Cakton zërin specifik të gjuhës për tekstin e folur"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Dëgjo një shembull"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Luaj një demonstrim të shkurtër të sintezës së të folurit"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Instalo të dhënat e zërit"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Instalo të dhënat e zërit që kërkohen për sintezën e të folurit"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Ky motor i sintezës së të folurit mund të mbledhë të gjithë tekstin që do të flitet, duke përfshirë të dhëna personale si fjalëkalime dhe numra kartash krediti. Ai vjen nga motori <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Të aktivizohet përdorimi i këtij motori të sintezës së të folurit?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Kjo gjuhë kërkon një lidhje funksionale interneti për daljen \"tekst-në-ligjërim\"."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Ky është një shembull i sintezës së të folurit"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Statusi i gjuhës së parazgjedhur"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> mbështetet plotësisht"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> kërkon lidhje interneti"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> nuk mbështetet"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Po kontrollon..."</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Cilësimet për \"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>\""</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Hap cilësimet e motorit"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Motori i preferuar"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Të përgjithshme"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Shumë e ulët"</item>
+ <item msgid="4795095314303559268">"E ngadaltë"</item>
+ <item msgid="8903157781070679765">"Normale"</item>
+ <item msgid="164347302621392996">"E shpejtë"</item>
+ <item msgid="5794028588101562009">"Më e shpejtë"</item>
+ <item msgid="7163942783888652942">"Shumë e shpejtë"</item>
+ <item msgid="7831712693748700507">"E shpejtë"</item>
+ <item msgid="5194774745031751806">"Shumë e shpejtë"</item>
+ <item msgid="9085102246155045744">"Më e shpejta"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Zgjidh profilin"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Personale"</string>
+ <string name="category_work" msgid="8699184680584175622">"Punë"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-sr/arrays.xml b/packages/SettingsLib/res/values-sr/arrays.xml
new file mode 100644
index 0000000..cc60251
--- /dev/null
+++ b/packages/SettingsLib/res/values-sr/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Скенирање..."</item>
+ <item msgid="8513729475867537913">"Повезивање…"</item>
+ <item msgid="515055375277271756">"Потврђује се аутентичност..."</item>
+ <item msgid="1943354004029184381">"Преузимање IP адресе..."</item>
+ <item msgid="4221763391123233270">"Повезано"</item>
+ <item msgid="624838831631122137">"Обустављено"</item>
+ <item msgid="7979680559596111948">"Прекидање везе..."</item>
+ <item msgid="1634960474403853625">"Веза је прекинута"</item>
+ <item msgid="746097431216080650">"Неуспешно"</item>
+ <item msgid="6367044185730295334">"Блокирано"</item>
+ <item msgid="503942654197908005">"Привремено избегавање лоше везе"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Скенирање..."</item>
+ <item msgid="355508996603873860">"Повезивање са мрежом <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="554971459996405634">"Проверавање идентитета мреже <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="7928343808033020343">"Добијање IP адресе од мреже <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="8937994881315223448">"Повезано са мрежом <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Обустављено"</item>
+ <item msgid="7698638434317271902">"Прекидање везе са мрежом <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="197508606402264311">"Веза је прекинута"</item>
+ <item msgid="8578370891960825148">"Неуспешно"</item>
+ <item msgid="5660739516542454527">"Блокирано"</item>
+ <item msgid="1805837518286731242">"Привремено избегавање лоше везе"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
new file mode 100644
index 0000000..4ed4739
--- /dev/null
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Није могуће скенирати мреже"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Нема"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Сачувано"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Онемогућено"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP конфигурација је отказала"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi веза је отказала"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Проблем са потврдом аутентичности"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Није у опсегу"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Приступ интернету није откривен, аутоматско повезивање није могуће."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Сачувао/ла је <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Повезано преко Wi‑Fi помоћника"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Веза је успостављена преко приступне тачке %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Доступна је преко приступне тачке %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Веза је успостављена, нема интернета"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Веза је прекинута"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Прекидање везе..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Повезивање…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Повезано"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Упаривање..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Повезано (без телефона)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Повезано (без медија)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Повезано је (нема приступа порукама)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Повезано (без телефона или медија)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Звук медија"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Звук телефона"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Пренос датотеке"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Улазни уређај"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Приступ Интернету"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Дељење контаката"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Користите за дељење контаката"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Дељење интернет везе"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Приступ порукама"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Приступ SIM картици"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Повезано са звуком медија"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Повезано са звуком телефона"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Повезано са сервером за пренос датотека"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Повезано је са мапом"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Веза са тачком приступа услугама је успостављена"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Није повезано са сервером за пренос датотека"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Повезан са улазним уређајем"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Повез. са уређ. ради приступа Интернету"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Локална интернет веза се дели са уређајем"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Користи за приступ Интернету"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Користи се за мапу"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Користи за приступ SIM картици"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Коришћење за звук медија"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Коришћење за аудио телефона"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Коришћење за пренос датотека"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Користи за улаз"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Упари"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"УПАРИ"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Откажи"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Упаривање омогућава приступ контактима и историји позива након повезивања."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Упаривање са уређајем <xliff:g id="DEVICE_NAME">%1$s</xliff:g> није могуће."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Упаривање са уређајем <xliff:g id="DEVICE_NAME">%1$s</xliff:g> није могуће због нетачног PIN-а или приступног кода."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Није могуће комуницирати са уређајем <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> је одбио/ла упаривање"</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi је искључен."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi веза је прекинута."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi сигнал има једну црту."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wi-Fi сигнал има две црте."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wi-Fi сигнал има три црте."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Wi-Fi сигнал је најјачи."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android ОС"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Уклоњене апликације"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Уклоњене апликације и корисници"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB Интернет повезивање"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Преносни хотспот"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth привезивање"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Повезивање са интернетом"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Повезивање и преносни хотспот"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Профил за посао"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Гост"</string>
+ <string name="unknown" msgid="1592123443519355854">"Непознато"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Корисник: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Подешене су неке подразумеване вредности"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Нису подешене подразумеване вредности"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Подешавања преласка из текста у говор"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Излаз за претварање текста у говор"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Брзина говора"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Брзина изговарања текста"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Језик"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Користи језик система"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Језик није изабран"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Подешава глас специфичан за језик намењен говорном тексту"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Послушај пример"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Пуштање кратке демонстрације синтезе говора"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Инсталирај гласовне податке"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Инсталирање говорних података потребних за синтезу говора"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Ова технологија за синтезу говора можда може да прикупља сав текст који ће бити изговорен, укључујући личне податке као што су лозинке и бројеви кредитних картица. То потиче из технологије <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Желите ли да омогућите коришћење ове технологије за синтезу говора?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"За овај језик је потребна исправна мрежна веза за претварање текста у говор."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Ово је пример синтезе говора"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Статус подразумеваног језика"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> је подржан у потпуности"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> захтева везу са мрежом"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> није подржан"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Проверава се..."</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Подешавања за <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Покрени подешавања машине"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Жељена машина"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Опште"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Веома споро"</item>
+ <item msgid="4795095314303559268">"Споро"</item>
+ <item msgid="8903157781070679765">"Нормално"</item>
+ <item msgid="164347302621392996">"Брзо"</item>
+ <item msgid="5794028588101562009">"Брже"</item>
+ <item msgid="7163942783888652942">"Веома брзо"</item>
+ <item msgid="7831712693748700507">"Убрзано"</item>
+ <item msgid="5194774745031751806">"Веома убрзано"</item>
+ <item msgid="9085102246155045744">"Најбрже"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Изаберите профил"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Лично"</string>
+ <string name="category_work" msgid="8699184680584175622">"Посао"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-sv/arrays.xml b/packages/SettingsLib/res/values-sv/arrays.xml
new file mode 100644
index 0000000..02b352c
--- /dev/null
+++ b/packages/SettingsLib/res/values-sv/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Skannar…"</item>
+ <item msgid="8513729475867537913">"Ansluter…"</item>
+ <item msgid="515055375277271756">"Autentiserar…"</item>
+ <item msgid="1943354004029184381">"Erhåller IP-adress…"</item>
+ <item msgid="4221763391123233270">"Ansluten"</item>
+ <item msgid="624838831631122137">"Pausad"</item>
+ <item msgid="7979680559596111948">"Kopplar ifrån…"</item>
+ <item msgid="1634960474403853625">"Frånkopplad"</item>
+ <item msgid="746097431216080650">"Misslyckades"</item>
+ <item msgid="6367044185730295334">"Blockerat"</item>
+ <item msgid="503942654197908005">"Undviker just nu dålig anslutning"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Skannar…"</item>
+ <item msgid="355508996603873860">"Ansluter till <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="554971459996405634">"Autentiserar med <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="7928343808033020343">"Hämtar IP-adress från <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="8937994881315223448">"Ansluten till: <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Pausad"</item>
+ <item msgid="7698638434317271902">"Kopplar ifrån <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="197508606402264311">"Frånkopplad"</item>
+ <item msgid="8578370891960825148">"Misslyckades"</item>
+ <item msgid="5660739516542454527">"Blockerat"</item>
+ <item msgid="1805837518286731242">"Undviker just nu dålig anslutning"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
new file mode 100644
index 0000000..1db99a59
--- /dev/null
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Det går inte att söka efter nätverk"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Ingen"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Sparat"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Inaktiverad"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-konfigurationsfel"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi-anslutningsfel"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Autentiseringsproblem"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Utom räckhåll"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Ingen internetåtkomst hittades. Det går inte att återansluta automatiskt."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Sparades av <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Ansluten via Wi-Fi-assistent"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Anslutet via %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Tillgängligt via %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Ansluten, inget internet"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Kopplas ifrån"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Kopplar ifrån…"</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Ansluter…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Ansluten"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Parkoppling…"</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Ansluten (ingen telefon)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Ansluten (inga media)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Ansluten (ingen meddelandeåtkomst)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Ansluten (ingen telefon och inga media)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Medialjud"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Telefonljud"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Filöverföring"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Indataenhet"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Internetåtkomst"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Kontaktdelning"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Använd för kontaktdelning"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Delning av Internetanslutning"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Meddelandeåtkomst"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM-åtkomst"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Ansluten till medialjud"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Ansluten till telefonens ljud"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Ansluten till filöverföringsserver"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Ansluten till MAP"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Ansluten till SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Inte ansluten till filöverföringsserver"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Ansluten till indataenhet"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Ansluten för Internetåtkomst"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Dela lokal Internetanslutning med enhet"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Använd för Internetåtkomst"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Använd för MAP"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Använd för SIM-åtkomst"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Använd för medialjud"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Använd för telefonens ljud"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Använd för filöverföring"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Använd för inmatning"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Parkoppling"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"KOPPLA"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Avbryt"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Om du kopplar enheten får du tillgång till dina kontakter och din samtalshistorik när du är ansluten."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Det gick inte att koppla till <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Det gick inte att koppla till <xliff:g id="DEVICE_NAME">%1$s</xliff:g> på grund av en felaktig PIN-kod eller nyckel."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Det går inte att kommunicera med <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Parkoppling avvisad av <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi är inaktiverat."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Ingen Wi-Fi-anslutning."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi: en stapel."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wi-Fi: två staplar."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wi-Fi: tre staplar."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Full signalstyrka för Wi-Fi."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Operativsystemet Android"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Borttagna appar"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Borttagna appar och användare"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"Internetdelning via USB"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Mobil surfpunkt"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Delning via Bluetooth"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Internetdelning"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Internetdelning och surfpunkt"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Arbetsprofil"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Gäst"</string>
+ <string name="unknown" msgid="1592123443519355854">"Okänd"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Användare: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Några standardinställningar har angetts"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Inga standardinställningar har angetts"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Text-till-tal-inställningar"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Text-till-tal"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Talhastighet"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Talhastighet för texten"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Språk"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Använd systemspråk"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Inget språk valt"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Ange språkspecifik röst för den talade texten"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Lyssna på ett exempel"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Spela upp en kort demonstration av talsyntes"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Installera röstdata"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Installera de ljuddata som krävs för talsyntes"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Talsyntesmotorn kan samla in all text som ska talas, inklusive personlig information som lösenord och kreditkortsnummer. Den kommer från <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>-motorn. Vill du använda den här talsyntesmotorn?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Det krävs en fungerande nätverksanslutning för att text-till-tal ska fungera för det här språket."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Detta är ett exempel på talsyntes"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Status för standardspråk"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> stöds"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> kräver nätverksanslutning"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> stöds inte"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Kontrollerar …"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Inställningar för <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Öppna inställningar för sökmotor"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Prioriterad sökmotor"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Allmänt"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Mycket långsamt"</item>
+ <item msgid="4795095314303559268">"Långsamt"</item>
+ <item msgid="8903157781070679765">"Normalt"</item>
+ <item msgid="164347302621392996">"Snabbt"</item>
+ <item msgid="5794028588101562009">"Snabbare"</item>
+ <item msgid="7163942783888652942">"Mycket snabbt"</item>
+ <item msgid="7831712693748700507">"Supersnabbt"</item>
+ <item msgid="5194774745031751806">"Turbosnabbt"</item>
+ <item msgid="9085102246155045744">"Snabbast"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Välj profil"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Personligt"</string>
+ <string name="category_work" msgid="8699184680584175622">"Arbetet"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-sw/arrays.xml b/packages/SettingsLib/res/values-sw/arrays.xml
new file mode 100644
index 0000000..cbd906d
--- /dev/null
+++ b/packages/SettingsLib/res/values-sw/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Inasafisha..."</item>
+ <item msgid="8513729475867537913">"Inaunganisha…"</item>
+ <item msgid="515055375277271756">"Inathibitisha..."</item>
+ <item msgid="1943354004029184381">"Inapata anwani ya Ip..."</item>
+ <item msgid="4221763391123233270">"Umeunganishwa"</item>
+ <item msgid="624838831631122137">"Imesimamishwa"</item>
+ <item msgid="7979680559596111948">"Inakatisha muunganisho..."</item>
+ <item msgid="1634960474403853625">"Muunganisho Umekatika"</item>
+ <item msgid="746097431216080650">"Haijafanikiwa"</item>
+ <item msgid="6367044185730295334">"Imezuiwa"</item>
+ <item msgid="503942654197908005">"Inaepuka kwa muda muunganisho mbovu"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Inasafisha..."</item>
+ <item msgid="355508996603873860">"Inaunganisha kwa <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="554971459996405634">"Uhalalishaji kwa <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="7928343808033020343">"Inamiliki anwani ya IP kutoka <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="8937994881315223448">" Umeunganishwa kwa<xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Imesimamishwa"</item>
+ <item msgid="7698638434317271902">"inakatisha muunganisho kutoka <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="197508606402264311">"Muunganisho Umekatika"</item>
+ <item msgid="8578370891960825148">"Haijafanikiwa"</item>
+ <item msgid="5660739516542454527">"Imezuiwa"</item>
+ <item msgid="1805837518286731242">"Inaepuka kwa muda muunganisho mbovu"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
new file mode 100644
index 0000000..e4740ac
--- /dev/null
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Haiwezi kutambaza mitandao"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Hamna"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Imehifadhiwa"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Imelemazwa"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Haikuweza Kusanidi IP"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Haikuweza Kuunganisha kwenye WiFi"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Tatizo la uthibitishaji"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Haiko karibu"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Hakuna Ufikiaji kwa Intaneti Uliogunduliwa, haitaweza kuunganisha kiotomatiki."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Ilihifadhiwa na <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Imeunganishwa kupitia Kisaidizi cha Wi-Fi"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Imeunganishwa kupitia %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Inapatikana kupitia %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Imeunganishwa, hakuna Intaneti"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Imetenganishwa"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Inatenganisha..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Inaunganisha…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Umeunganishwa"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Inaoanisha..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Imeunganishwa (hakuna simu)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Imeunganishwa(hakuna vyombo vya habari)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Imeunganishwa (hakuna ufikiaji kwa ujumbe)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Imeunganishwa(hakuna simu au vyombo vya habari)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Media ya sauti"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Sauti ya simu"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Uhamishaji wa faili"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Kifaa cha kuingiza"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Ufikivu wa mtandao"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Kushiriki anwani"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Tumia kwa kushiriki anwani"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Kushiriki muunganisho wa tovuti"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Ufikiaji wa Ujumbe"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Ufikiaji wa SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Imeunganishwa kwenye sikika ya njia ya mawasiliano"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Imeunganishwa kwenye sauti ya simu"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Imeunganishwa kwenye seva ya kuhamisha faili"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Imeunganishwa kwenye ramani"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Imeunganishwa kwenye SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Haijaunganishwa kwenye seva ya kuhamisha faili"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Umeunganishwa kwa kifaa cha kuingiza"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Umeunganishwa kwa kifaa cha ufikia Mtandao"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Kushiriki muunganisho wa mtandao wa nyumbani na kifaa"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Tumia kwa ufikiaji mtandao"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Tumia kwa ramani"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Tumia kwa ufikiaji wa SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Tumia kwa sauti ya media"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Tumia kwa sauti ya simu"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Tumia kwa hali faili"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Tumia kwa kuingiza"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Oanisha"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"OANISHA"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Ghairi"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Kuoanisha hutoa ruhusa ya kufikiwa kwa unaowasiliana nao na rekodi ya simu zilizopigwa unapounganishwa."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Haikuwezakulinganisha na <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Haikuweza kulingana na <xliff:g id="DEVICE_NAME">%1$s</xliff:g> kwa sababu ya PIN isiyo sahihi au msimbo ya kuingia."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Haiwezi kuanzisha mawasiliano na <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Ulinganishaji umekataliwa na <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wifi imezimwa."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wifi imeondolewa."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Kipima mtandao kimoja cha Wifi."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Vipima mtandao viwili vya Wifi."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Vipima mtandao vitatu vya Wifi."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Nguvu kamili ya mtandao wa Wifi."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"OS ya Android"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Programu zilizoondolewa"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Watumiaji na programu ziilizoondolewa"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"Shiriki intaneti kwa USB"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Intaneti ya kusambazwa"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Shiriki intaneti kwa Bluetooth"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Inazuia"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Kushiriki na kusambaza intaneti"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Wasifu wa kazi"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Aliyealikwa"</string>
+ <string name="unknown" msgid="1592123443519355854">"Haijulikani"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Mtumiaji: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Baadhi ya chaguo-msingi zimewekwa"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Hakuna chaguo-misingi zilizowekwa"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Mipangilio ya maandishi kwa hotuba"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Kubadilisha maandishi hadi usemi"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Kiwango cha usemaji"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Kasi ya kutamkwa kwa maneno"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Lugha"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Tumia lugha ya mfumo"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Lugha haijachaguliwa"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Huweka sauti maalum ya lugha inayolingana na yanayozungumzwa"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Sikiliza mfano"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Cheza onyesho fupi la usanisi usemaji"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Sakinisha data ya sauti"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Sakinisha data ya sauti inayohitajika kuunganisha usemi"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Hotuba hii inawezesha injini huenda ikaweza kukusanya maandishi ambayo yatazungumziwa, ikijumlisha data ya kibinafsi ya nenosiri na namba ya kaddi ya mkopo. Inatoka kwa injini ya <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> Wezesha matumizi ya hotuba hii iliyowezeshwa ya injini?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Lugha hii inahitaji muunganisho wa mtandao unaofanya kazi kwa towe ya maandishi hadi sauti."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Huu ni mfano wa usanisi usemaji"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Hali ya lugha chaguo-msingi"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> inaweza kutumiwa kikamilifu"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> inahitaji muunganisho wa mtandao"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> haiwezi kutumiwa"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Inakagua..."</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Mipangilio ya <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Zindua mipangilio ya injini"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Injini inayofaa"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Kwa ujumla"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Polepole sana"</item>
+ <item msgid="4795095314303559268">"Polepole"</item>
+ <item msgid="8903157781070679765">"Kawaida"</item>
+ <item msgid="164347302621392996">"Haraka"</item>
+ <item msgid="5794028588101562009">"Haraka kiasi"</item>
+ <item msgid="7163942783888652942">"Haraka sana"</item>
+ <item msgid="7831712693748700507">"Kasi"</item>
+ <item msgid="5194774745031751806">"Kasi sana"</item>
+ <item msgid="9085102246155045744">"Kasi zaidi"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Chagua Wasifu"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Ya Kibinafsi"</string>
+ <string name="category_work" msgid="8699184680584175622">"Kazini"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-ta-rIN/arrays.xml b/packages/SettingsLib/res/values-ta-rIN/arrays.xml
new file mode 100644
index 0000000..c554fea
--- /dev/null
+++ b/packages/SettingsLib/res/values-ta-rIN/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"ஸ்கேன் செய்கிறது…"</item>
+ <item msgid="8513729475867537913">"இணைக்கிறது..."</item>
+ <item msgid="515055375277271756">"அங்கீகரிக்கிறது..."</item>
+ <item msgid="1943354004029184381">"IP முகவரியைப் பெறுகிறது…"</item>
+ <item msgid="4221763391123233270">"இணைக்கப்பட்டது"</item>
+ <item msgid="624838831631122137">"இடைநீக்கப்பட்டது"</item>
+ <item msgid="7979680559596111948">"துண்டிக்கிறது..."</item>
+ <item msgid="1634960474403853625">"தொடர்பு துண்டிக்கப்பட்டது"</item>
+ <item msgid="746097431216080650">"தோல்வி"</item>
+ <item msgid="6367044185730295334">"தடுக்கப்பட்டது"</item>
+ <item msgid="503942654197908005">"வேகம் குறைந்த இணைப்பைத் தற்காலிகமாகத் தவிர்க்கிறது"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"ஸ்கேன் செய்கிறது…"</item>
+ <item msgid="355508996603873860">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> இல் இணைக்கிறது…"</item>
+ <item msgid="554971459996405634">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> உடன் அங்கீகரிக்கிறது…"</item>
+ <item msgid="7928343808033020343">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> இலிருந்து IP முகவரியைப் பெறுகிறது…"</item>
+ <item msgid="8937994881315223448">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> உடன் இணைக்கப்பட்டது"</item>
+ <item msgid="1330262655415760617">"இடைநீக்கப்பட்டது"</item>
+ <item msgid="7698638434317271902">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> இலிருந்து தொடர்பு துண்டிக்கப்படுகிறது…"</item>
+ <item msgid="197508606402264311">"தொடர்பு துண்டிக்கப்பட்டது"</item>
+ <item msgid="8578370891960825148">"தோல்வி"</item>
+ <item msgid="5660739516542454527">"தடுக்கப்பட்டது"</item>
+ <item msgid="1805837518286731242">"வேகம் குறைந்த இணைப்பைத் தற்காலிகமாகத் தவிர்க்கிறது"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-ta-rIN/strings.xml b/packages/SettingsLib/res/values-ta-rIN/strings.xml
new file mode 100644
index 0000000..aaafa68
--- /dev/null
+++ b/packages/SettingsLib/res/values-ta-rIN/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"நெட்வொர்க்குகளுக்கு ஸ்கேன் செய்யப்படவில்லை"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"ஏதுமில்லை"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"சேமிக்கப்பட்டது"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"முடக்கப்பட்டது"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP உள்ளமைவில் தோல்வி"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"வைஃபை இணைப்பில் தோல்வி"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"அங்கீகரிப்புச் சிக்கல்"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"தொடர்பு எல்லையில் இல்லை"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"இணைய அணுகல் இல்லை, மீண்டும் தானாக இணையாது."</string>
+ <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> சேமித்தது"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"வைஃபை அசிஸ்டண்ட் மூலம் இணைக்கப்பட்டது"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s வழியாக இணைக்கப்பட்டது"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s வழியாகக் கிடைக்கிறது"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"இணைக்கப்பட்டது, இணையம் இல்லை"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"தொடர்பு துண்டிக்கப்பட்டது"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"துண்டிக்கிறது..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"இணைக்கிறது..."</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"இணைக்கப்பட்டது"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"இணைக்கிறது..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"இணைக்கப்பட்டது (மொபைல் இல்லை)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"இணைக்கப்பட்டது (மீடியா இல்லை)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"இணைக்கப்பட்டது (செய்திக்கான அணுகல் இல்லை)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"இணைக்கப்பட்டது (மொபைல் அல்லது மீடியாவுடன் அல்ல)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"மீடியா ஆடியோ"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"மொபைல் ஆடியோ"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"கோப்பு இடமாற்றம்"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"உள்ளீட்டுச் சாதனம்"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"இணைய அணுகல்"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"தொடர்புப் பகிர்தல்"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"தொடர்புப் பகிர்தலுக்குப் பயன்படுத்து"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"இணைய இணைப்பு பகிர்தல்"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"செய்திக்கான அணுகல்"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"சிம் அணுகல்"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"மீடியா ஆடியோவுடன் இணைக்கப்பட்டது"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"மொபைல் ஆடியோவுடன் இணைக்கப்பட்டது"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"கோப்பைப் பரிமாற்றும் சேவையகத்துடன் இணைக்கப்பட்டது"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"வரைபடத்துடன் இணைக்கப்பட்டது"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"SAP உடன் இணைக்கப்பட்டது"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"கோப்பு இடமாற்றும் சேவையகத்துடன் இணைக்கப்படவில்லை"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"உள்ளீட்டுச் சாதனத்துடன் இணைக்கப்பட்டது"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"இணைய அணுகலுக்காகச் சாதனம் இணைக்கப்பட்டது"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"சாதனத்துடன் அக இணைய இணைப்பைப் பகிர்கிறது"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"இணைய அணுகலுக்காகப் பயன்படுத்து"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"வரைபடத்திற்குப் பயன்படுத்து"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"சிம் அணுகலுக்குப் பயன்படுத்தும்"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"மீடியாவின் ஆடியோவிற்குப் பயன்படுத்து"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"மொபைல் ஆடியோவைப் பயன்படுத்து"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"கோப்பு பரிமாற்றத்திற்காகப் பயன்படுத்து"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"உள்ளீட்டுக்குப் பயன்படுத்து"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"இணை"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"இணை"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"ரத்துசெய்"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"இணைத்தலானது உங்கள் தொடர்புகள், அழைப்பு வரலாறுக்கான அணுகலை வழங்குகிறது."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> உடன் இணைய முடியவில்லை."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"தவறான பின் அல்லது கடவுச்சொல் காரணமாக <xliff:g id="DEVICE_NAME">%1$s</xliff:g> உடன் இணைக்க முடியவில்லை."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> உடன் இணைக்க முடியவில்லை."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> இணைப்பதை நிராகரித்தது."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"வைஃபை முடக்கப்பட்டது."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"வைஃபை துண்டிக்கப்பட்டது."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"வைஃபை சிக்னல்: ஒரு கோடு."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"வைஃபை சிக்னல்: இரண்டு கோடுகள்."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"வைஃபை சிக்னல்: மூன்று கோடுகள்."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"வைஃபை சிக்னல் முழுமையாக உள்ளது."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"அகற்றப்பட்ட பயன்பாடுகள்"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"அகற்றப்பட்ட பயன்பாடுகள் மற்றும் பயனர்கள்"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB டெதெரிங்"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"போர்ட்டபிள் ஹாட்ஸ்பாட்"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"புளூடூத் டெதெரிங்"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"டெதெரிங்"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"டெதெரிங் & போர்டபிள் ஹாட்ஸ்பாட்"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"பணி சுயவிவரம்"</string>
+ <string name="user_guest" msgid="8475274842845401871">"அழைக்கப்பட்டவர்"</string>
+ <string name="unknown" msgid="1592123443519355854">"அறியப்படாத"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"பயனர்: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"சில இயல்புநிலைகள் அமைக்கப்பட்டன"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"இயல்புநிலைகள் எதுவும் அமைக்கப்படவில்லை"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"உரை வடிவத்திலிருந்து பேச்சுக்கான அமைப்பு"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"உரையிலிருந்து பேச்சாக மாற்றுதல்"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"பேச்சு வீதம்"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"பேசப்படும் உரையின் வேகம்"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"மொழி"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"முறைமையின் மொழியைப் பயன்படுத்து"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"மொழி தேர்ந்தெடுக்கப்படவில்லை"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"பேசப்படும் உரைக்கு மொழி சார்ந்த குரலை அமைக்கிறது"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"எடுத்துக்காட்டைக் கவனிக்கவும்"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"பேச்சு இணைப்பாக்கத்தின் சிறிய செயல்விளக்கத்தை இயக்கு"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"குரல் தரவை நிறுவு"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"பேச்சு இணைப்பாக்கத்திற்குத் தேவையான குரல் தரவை நிறுவவும்"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"இந்தப் பேச்சு இணைப்பாக்கல் இன்ஜின் ஆனது, கடவுச்சொற்கள் மற்றும் கிரெடிட் கார்டு எண்கள் போன்ற தனிப்பட்ட தகவல் உள்பட பேசப்படும் எல்லா உரையையும் சேகரிக்கலாம். இது <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> இன்ஜினிலிருந்து வந்துள்ளது. இந்தப் பேச்சு இணைப்பாக்கல் இன்ஜினை இயக்கவா?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"உரை வடிவத்திலிருந்து பேச்சு வெளியீட்டிற்காக, இந்த மொழிக்கு செயலில் உள்ள நெட்வொர்க் இணைப்பு தேவை."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"இது பேச்சு இணைப்பாக்கத்திற்கான எடுத்துக்காட்டாகும்"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"இயல்பு மொழியின் நிலை"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> முழுமையாக ஆதரிக்கப்படுகிறது"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> க்கு நெட்வொர்க் இணைப்பு அவசியமாகும்"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> ஆதரிக்கப்படவில்லை"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"சரிபார்க்கிறது..."</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g> க்கான அமைப்பு"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"இன்ஜின் அமைப்புகளைத் தொடங்கு"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"விருப்பத்தேர்வு"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"பொதுவானவை"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"மிகவும் மெதுவாக"</item>
+ <item msgid="4795095314303559268">"மெதுவாக"</item>
+ <item msgid="8903157781070679765">"இயல்பு"</item>
+ <item msgid="164347302621392996">"வேகமாக"</item>
+ <item msgid="5794028588101562009">"மிக வேகமாக"</item>
+ <item msgid="7163942783888652942">"அதிவேகமாக"</item>
+ <item msgid="7831712693748700507">"அதிக வேகமாக"</item>
+ <item msgid="5194774745031751806">"மிக அதிக வேகமாக"</item>
+ <item msgid="9085102246155045744">"அதிகபட்ச வேகம்"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"சுயவிவரத்தைத் தேர்வுசெய்யவும்"</string>
+ <string name="category_personal" msgid="1299663247844969448">"தனிப்பட்டவை"</string>
+ <string name="category_work" msgid="8699184680584175622">"பணியிடம்"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-te-rIN/arrays.xml b/packages/SettingsLib/res/values-te-rIN/arrays.xml
new file mode 100644
index 0000000..e21a5f4
--- /dev/null
+++ b/packages/SettingsLib/res/values-te-rIN/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"స్కాన్ చేస్తోంది…"</item>
+ <item msgid="8513729475867537913">"కనెక్ట్ చేస్తోంది..."</item>
+ <item msgid="515055375277271756">"ప్రామాణీకరిస్తోంది…"</item>
+ <item msgid="1943354004029184381">"IP చిరునామాను పొందుతోంది…"</item>
+ <item msgid="4221763391123233270">"కనెక్ట్ చేయబడింది"</item>
+ <item msgid="624838831631122137">"తాత్కాలికంగా రద్దు చేయబడింది"</item>
+ <item msgid="7979680559596111948">"డిస్కనెక్ట్ చేస్తోంది..."</item>
+ <item msgid="1634960474403853625">"డిస్కనెక్ట్ చేయబడింది"</item>
+ <item msgid="746097431216080650">"విఫలమైంది"</item>
+ <item msgid="6367044185730295334">"బ్లాక్ చేయబడింది"</item>
+ <item msgid="503942654197908005">"బలహీన కనెక్షన్ను తాత్కాలికంగా నివారిస్తోంది"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"స్కాన్ చేస్తోంది…"</item>
+ <item msgid="355508996603873860">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>కి కనెక్ట్ చేస్తోంది…"</item>
+ <item msgid="554971459996405634">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>తో ప్రామాణీకరిస్తోంది…"</item>
+ <item msgid="7928343808033020343">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> నుండి IP చిరునామాను పొందుతోంది…"</item>
+ <item msgid="8937994881315223448">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>కి కనెక్ట్ చేయబడింది"</item>
+ <item msgid="1330262655415760617">"తాత్కాలికంగా రద్దు చేయబడింది"</item>
+ <item msgid="7698638434317271902">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> నుండి డిస్కనెక్ట్ చేస్తోంది…"</item>
+ <item msgid="197508606402264311">"డిస్కనెక్ట్ చేయబడింది"</item>
+ <item msgid="8578370891960825148">"విఫలమైంది"</item>
+ <item msgid="5660739516542454527">"బ్లాక్ చేయబడింది"</item>
+ <item msgid="1805837518286731242">"బలహీన కనెక్షన్ను తాత్కాలికంగా నివారిస్తోంది"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-te-rIN/strings.xml b/packages/SettingsLib/res/values-te-rIN/strings.xml
new file mode 100644
index 0000000..4b3570e
--- /dev/null
+++ b/packages/SettingsLib/res/values-te-rIN/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"నెట్వర్క్ల కోసం స్కాన్ చేయడం సాధ్యపడదు"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"ఏదీ లేదు"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"సేవ్ చేయబడింది"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"నిలిపివేయబడింది"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP కాన్ఫిగరేషన్ వైఫల్యం"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi కనెక్షన్ వైఫల్యం"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"ప్రామాణీకరణ సమస్య"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"పరిధిలో లేదు"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"ఇంటర్నెట్ ప్రాప్యత కనుగొనబడలేదు, స్వయంచాలకంగా మళ్లీ కనెక్ట్ చేయబడదు."</string>
+ <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> ద్వారా సేవ్ చేయబడింది"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fi సహాయకం ద్వారా కనెక్ట్ చేయబడింది"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s ద్వారా కనెక్ట్ చేయబడింది"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s ద్వారా అందుబాటులో ఉంది"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"కనెక్ట్ చేయబడింది, ఇంటర్నెట్ లేదు"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"డిస్కనెక్ట్ చేయబడింది"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"డిస్కనెక్ట్ చేస్తోంది..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"కనెక్ట్ చేస్తోంది..."</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"కనెక్ట్ చేయబడింది"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"జత చేస్తోంది..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"కనెక్ట్ చేయబడింది (ఫోన్ కాదు)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"కనెక్ట్ చేయబడింది (మీడియా కాదు)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"కనెక్ట్ చేయబడింది (సందేశ ప్రాప్యత లేదు)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"కనెక్ట్ చేయబడింది (ఫోన్ లేదా మీడియా కాకుండా)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"మీడియా ఆడియో"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"ఫోన్ ఆడియో"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"ఫైల్ బదిలీ"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"ఇన్పుట్ పరికరం"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"ఇంటర్నెట్ ప్రాప్యత"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"పరిచయ భాగస్వామ్యం"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"పరిచయ భాగస్వామ్యం కోసం ఉపయోగించు"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"ఇంటర్నెట్ కనెక్షన్ భాగస్వామ్యం"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"సందేశ ప్రాప్యత"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM ప్రాప్యత"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"మీడియా ఆడియోకు కనెక్ట్ చేయబడింది"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"ఫోన్ ఆడియోకు కనెక్ట్ చేయబడింది"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ఫైల్ బదిలీ సర్వర్కు కనెక్ట్ చేయబడింది"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"మ్యాప్కు కనెక్ట్ చేయబడింది"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"SAPకి కనెక్ట్ చేయబడింది"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"ఫైల్ బదిలీ సర్వర్కు కనెక్ట్ చేయబడలేదు"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"ఇన్పుట్ పరికరానికి కనెక్ట్ చేయబడింది"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"ఇంటర్నెట్ ప్రాప్యత కోసం పరికరానికి కనెక్ట్ చేయబడింది"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"స్థానిక ఇంటర్నెట్ కనెక్షన్ను పరికరంతో భాగస్వామ్యం చేయడం"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"ఇంటర్నెట్ ప్రాప్యత కోసం ఉపయోగించు"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"మ్యాప్ కోసం ఉపయోగించు"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"SIM ప్రాప్యత కోసం ఉపయోగించబడుతుంది"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"మీడియా ఆడియో కోసం ఉపయోగించు"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"ఫోన్ ఆడియో కోసం ఉపయోగించు"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ఫైల్ బదిలీ కోసం ఉపయోగించు"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ఇన్పుట్ కోసం ఉపయోగించు"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"జత చేయి"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"జత చేయి"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"రద్దు చేయి"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"జత చేయడం వలన కనెక్ట్ చేయబడినప్పుడు మీ పరిచయాలకు మరియు కాల్ చరిత్రకు ప్రాప్యతను మంజూరు చేస్తుంది."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>తో జత చేయడం సాధ్యపడలేదు."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"పిన్ లేదా పాస్కీ చెల్లని కారణంగా <xliff:g id="DEVICE_NAME">%1$s</xliff:g>తో జత చేయడం సాధ్యపడలేదు."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>తో కమ్యూనికేట్ చేయడం సాధ్యపడదు."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> జత చేయడాన్ని తిరస్కరించింది."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wifi ఆఫ్లో ఉంది."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wifi డిస్కనెక్ట్ చేయబడింది."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wifi సిగ్నల్ ఒక బార్ ఉంది."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wifi సిగ్నల్ రెండు బార్లు ఉంది."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wifi సిగ్నల్ మూడు బార్లు ఉంది."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Wifi సిగ్నల్ పూర్తిగా ఉంది."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"తీసివేయబడిన అనువర్తనాలు"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"తీసివేయబడిన అనువర్తనాలు మరియు వినియోగదారులు"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB టీథరింగ్"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"పోర్టబుల్ హాట్స్పాట్"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"బ్లూటూత్ టీథరింగ్"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"టీథరింగ్"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"టీథరింగ్ & పోర్టబుల్ హాట్స్పాట్"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"కార్యాలయ ప్రొఫైల్"</string>
+ <string name="user_guest" msgid="8475274842845401871">"అతిథి"</string>
+ <string name="unknown" msgid="1592123443519355854">"తెలియదు"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"వినియోగదారు: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"కొన్ని డిఫాల్ట్లు సెట్ చేయబడ్డాయి"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"డిఫాల్ట్లు ఏవీ సెట్ చేయబడలేదు"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"వచనం నుండి ప్రసంగం సెట్టింగ్లు"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"వచనం నుండి ప్రసంగం అవుట్పుట్"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"ప్రసంగం రేట్"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"వచనాన్ని చదివి వినిపించాల్సిన వేగం"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"భాష"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"సిస్టమ్ భాషను ఉపయోగించు"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"భాష ఎంచుకోబడలేదు"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"చదవి వినిపించబడే వచనం కోసం భాష-నిర్దిష్ట వాయిస్ను సెట్ చేస్తుంది"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"ఒక ఉదాహరణ వినండి"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"ప్రసంగ సమన్వయం గురించి సంక్షిప్త ప్రదర్శనను ప్లే చేయి"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"వాయిస్ డేటాను ఇన్స్టాల్ చేయి"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"ప్రసంగ సమన్వయం కోసం అవసరమైన వాయిస్ డేటాను ఇన్స్టాల్ చేయండి"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"ఈ ప్రసంగ సమన్వయ ఇంజిన్ చదివి వినిపించబడే మొత్తం వచనాన్ని అలాగే పాస్వర్డలు మరియు క్రెడిట్ కార్డు నంబర్ల వంటి వ్యక్తిగత డేటాను సేకరించగలదు. ఇది <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> ఇంజిన్లో అందించబడుతుంది. ఈ ప్రసంగ సమన్వయ ఇంజిన్ యొక్క వినియోగాన్ని ప్రారంభించాలా?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"వచనం నుండి ప్రసంగం అవుట్పుట్ కోసం ఈ భాషకు పని చేస్తున్న నెట్వర్క్ కనెక్షన్ కావాలి."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"ఇది ప్రసంగ సమన్వయానికి ఉదాహరణ"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"డిఫాల్ట్ భాష స్థితి"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g>కి పూర్తి మద్దతు ఉంది"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g>కి నెట్వర్క్ కనెక్షన్ అవసరం"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g>కు మద్దతు లేదు"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"తనిఖీ చేస్తోంది..."</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g> కోసం సెట్టింగ్లు"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"ఇంజిన్ సెట్టింగ్లను ప్రారంభించండి"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"ప్రాధాన్య ఇంజిన్"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"సాధారణం"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"చాలా నెమ్మది"</item>
+ <item msgid="4795095314303559268">"నెమ్మది"</item>
+ <item msgid="8903157781070679765">"సాధారణం"</item>
+ <item msgid="164347302621392996">"వేగవంతం"</item>
+ <item msgid="5794028588101562009">"అధిక వేగవంతం"</item>
+ <item msgid="7163942783888652942">"చాలా వేగవంతం"</item>
+ <item msgid="7831712693748700507">"అధిక వేగం"</item>
+ <item msgid="5194774745031751806">"అత్యంత వేగం"</item>
+ <item msgid="9085102246155045744">"అత్యంత వేగవంతం"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"ప్రొఫైల్ను ఎంచుకోండి"</string>
+ <string name="category_personal" msgid="1299663247844969448">"వ్యక్తిగతం"</string>
+ <string name="category_work" msgid="8699184680584175622">"కార్యాలయం"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-th/arrays.xml b/packages/SettingsLib/res/values-th/arrays.xml
new file mode 100644
index 0000000..190a37d
--- /dev/null
+++ b/packages/SettingsLib/res/values-th/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"กำลังสแกน..."</item>
+ <item msgid="8513729475867537913">"กำลังเชื่อมต่อ…"</item>
+ <item msgid="515055375277271756">"กำลังตรวจสอบความถูกต้อง…"</item>
+ <item msgid="1943354004029184381">"กำลังรับที่อยู่ IP…"</item>
+ <item msgid="4221763391123233270">"เชื่อมต่อแล้ว"</item>
+ <item msgid="624838831631122137">"ระงับไว้"</item>
+ <item msgid="7979680559596111948">"กำลังตัดการเชื่อมต่อ..."</item>
+ <item msgid="1634960474403853625">"ตัดการเชื่อมต่อ"</item>
+ <item msgid="746097431216080650">"ไม่สำเร็จ"</item>
+ <item msgid="6367044185730295334">"ถูกบล็อก"</item>
+ <item msgid="503942654197908005">"หลีกเลี่ยงการเชื่อมต่อกับสัญญาณที่ไม่แรงพอชั่วคราว"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"กำลังสแกน..."</item>
+ <item msgid="355508996603873860">"กำลังเชื่อมต่อไปยัง <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="554971459996405634">"กำลังตรวจสอบสิทธิ์กับ<xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="7928343808033020343">"กำลังรับที่อยู่ IP จาก <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="8937994881315223448">"เชื่อมต่อกับ <xliff:g id="NETWORK_NAME">%1$s</xliff:g> แล้ว"</item>
+ <item msgid="1330262655415760617">"ระงับไว้"</item>
+ <item msgid="7698638434317271902">"กำลังตัดการเชื่อมต่อจาก <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="197508606402264311">"ตัดการเชื่อมต่อ"</item>
+ <item msgid="8578370891960825148">"ไม่สำเร็จ"</item>
+ <item msgid="5660739516542454527">"ถูกบล็อก"</item>
+ <item msgid="1805837518286731242">"หลีกเลี่ยงการเชื่อมต่อกับสัญญาณที่ไม่แรงพอชั่วคราว"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
new file mode 100644
index 0000000..029962e
--- /dev/null
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"ไม่สามารถสแกนหาเครือข่าย"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"ไม่มี"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"บันทึกแล้ว"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"ปิดอยู่"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"การกำหนดค่า IP ล้มเหลว"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"การเชื่อมต่อ Wi-Fi ล้มเหลว"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"ปัญหาในการตรวจสอบสิทธิ์"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"ไม่อยู่ในพื้นที่ให้บริการ"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"ไม่พบการเข้าถึงอินเทอร์เน็ต ระบบจะไม่เชื่อมต่อใหม่โดยอัตโนมัติ"</string>
+ <string name="saved_network" msgid="4352716707126620811">"บันทึกโดย <xliff:g id="NAME">%1$s</xliff:g> แล้ว"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"เชื่อมต่อผ่านตัวช่วย Wi-Fi อยู่"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"เชื่อมต่อผ่าน %1$s แล้ว"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"พร้อมใช้งานผ่านทาง %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"เชื่อมต่อแล้ว ไม่พบอินเทอร์เน็ต"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"ตัดการเชื่อมต่อ"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"กำลังตัดการเชื่อมต่อ..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"กำลังเชื่อมต่อ…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"เชื่อมต่อแล้ว"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"กำลังกำหนดค่าอุปกรณ์ให้ตรงกัน..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"เชื่อมต่อแล้ว (ยกเว้นเสียงโทรศัพท์)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"เชื่อมต่อแล้ว (ยกเว้นเสียงสื่อ)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"เชื่อมต่อแล้ว (ไม่มีการเข้าถึงข้อความ)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"เชื่อมต่อ (ยกเว้นเสียงโทรศัพท์หรือสื่อ)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"เสียงสื่อ"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"เสียงโทรศัพท์"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"การถ่ายโอนไฟล์"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"อุปกรณ์อินพุต"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"การเข้าถึงอินเทอร์เน็ต"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"การแชร์รายชื่อผู้ติดต่อ"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"ใช้สำหรับการแชร์รายชื่อผู้ติดต่อ"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"การแชร์การเชื่อมต่ออินเทอร์เน็ต"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"การเข้าถึงข้อความ"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"การเข้าถึงซิม"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"เชื่อมต่อกับระบบเสียงของสื่อแล้ว"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"เชื่อมต่อกับระบบเสียงของโทรศัพท์แล้ว"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"เชื่อมต่อกับเซิร์ฟเวอร์สำหรับโอนไฟล์แล้ว"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"เชื่อมต่อกับแผนที่แล้ว"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"เชื่อมต่อ SAP แล้ว"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"ไม่ได้เชื่อมต่อกับเซิร์ฟเวอร์สำหรับโอนไฟล์"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"เชื่อมต่อกับอุปกรณ์อินพุตแล้ว"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"เชื่อมต่อกับอุปกรณ์สำหรับการเข้าถึงอินเทอร์เน็ต"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"กำลังแชร์อินเทอร์เน็ตกับอุปกรณ์"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"ใช้การเข้าถึงอินเทอร์เน็ต"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"ใช้สำหรับแผนที่"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"ใช้สำหรับการเข้าถึงซิม"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"ใช้สำหรับระบบเสียงของสื่อ"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"ใช้สำหรับระบบเสียงของโทรศัพท์"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ใช้สำหรับการโอนไฟล์"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ใช้สำหรับการป้อนข้อมูล"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"กำหนดค่าอุปกรณ์ให้ตรงกัน"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"จับคู่อุปกรณ์"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"ยกเลิก"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"การจับคู่อุปกรณ์จะให้สิทธิ์การเข้าถึงที่อยู่ติดต่อและประวัติการโทรเมื่อเชื่อมต่อแล้ว"</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"ไม่สามารถจับคู่กับ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"ไม่สามารถจับคู่กับ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ได้เพราะ PIN หรือรหัสผ่านไม่ถูกต้อง"</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"ไม่สามารถเชื่อมต่อกับ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"การกำหนดค่าอุปกรณ์ให้ตรงกันถูกปฏิเสธโดย <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi ปิดอยู่"</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"ไม่ได้เชื่อมต่อ Wi-Fi"</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"สัญญาณ Wi-Fi 1 ขีด"</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"สัญญาณ Wi-Fi 2 ขีด"</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"สัญญาณ Wi-Fi 3 ขีด"</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"สัญญาณ Wi-Fi เต็ม"</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"ระบบปฏิบัติการของ Android"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"แอปพลิเคชันที่นำออก"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"แอปพลิเคชันและผู้ใช้ที่นำออก"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"ปล่อยสัญญาณผ่าน USB"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ฮอตสปอตแบบพกพาได้"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ปล่อยสัญญาณบลูทูธ"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"การปล่อยสัญญาณ"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"การปล่อยสัญญาณและฮอตสปอต"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"โปรไฟล์งาน"</string>
+ <string name="user_guest" msgid="8475274842845401871">"ผู้เข้าร่วม"</string>
+ <string name="unknown" msgid="1592123443519355854">"ไม่ทราบ"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"ผู้ใช้: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"ตั้งค่าเริ่มต้นไว้บางส่วน"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"ไม่ได้ตั้งค่าเริ่มต้น"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"การตั้งค่าการอ่านออกเสียงข้อความ"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"เอาต์พุตการอ่านออกเสียง"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"ความเร็วของคำพูด"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"ความเร็วในการพูดข้อความ"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"ภาษา"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"ใช้ภาษาของระบบ"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"ไม่ได้เลือกภาษา"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"ตั้งค่าเสียงในภาษาที่ระบุสำหรับข้อความที่ได้ยิน"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"ฟังตัวอย่าง"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"เล่นการสาธิตสั้นๆ เกี่ยวกับการสังเคราะห์เสียง"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"ติดตั้งข้อมูลเสียง"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"ติดตั้งข้อมูลเสียงที่ต้องใช้สำหรับการสังเคราะห์เสียง"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"โปรแกรมสังเคราะห์เสียงนี้อาจเก็บข้อความทั้งหมดที่ได้ยิน รวมถึงข้อมูลส่วนบุคคล เช่น รหัสผ่านและหมายเลขบัตรเครดิต โปรแกรมนี้มาจากโปรแกรม <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> คุณต้องการเปิดใช้งานโปรแกรมสังเคราะห์เสียงนี้หรือไม่"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"ภาษานี้ต้องใช้การเชื่อมต่อเครือข่ายซึ่งใช้การได้สำหรับการแปลงข้อความเป็นเสียงพูด"</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"นี่เป็นตัวอย่างการสังเคราะห์เสียง"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"สถานะของภาษาเริ่มต้น"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"สนับสนุน<xliff:g id="LOCALE">%1$s</xliff:g>อย่างสมบูรณ์"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"ต้องมีการเชื่อมต่อเครือข่ายสำหรับ<xliff:g id="LOCALE">%1$s</xliff:g>"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"ไม่สนับสนุน<xliff:g id="LOCALE">%1$s</xliff:g>"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"กำลังตรวจสอบ…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"การตั้งค่าสำหรับ <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"เปิดการตั้งค่าเครื่องมือ"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"เครื่องมือที่ต้องการ"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"ทั่วไป"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"ช้ามาก"</item>
+ <item msgid="4795095314303559268">"ช้า"</item>
+ <item msgid="8903157781070679765">"ปกติ"</item>
+ <item msgid="164347302621392996">"เร็ว"</item>
+ <item msgid="5794028588101562009">"เร็วขึ้น"</item>
+ <item msgid="7163942783888652942">"เร็วมาก"</item>
+ <item msgid="7831712693748700507">"เร็ว"</item>
+ <item msgid="5194774745031751806">"เร็วมาก"</item>
+ <item msgid="9085102246155045744">"เร็วที่สุด"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"เลือกโปรไฟล์"</string>
+ <string name="category_personal" msgid="1299663247844969448">"ส่วนตัว"</string>
+ <string name="category_work" msgid="8699184680584175622">"ที่ทำงาน"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-tl/arrays.xml b/packages/SettingsLib/res/values-tl/arrays.xml
new file mode 100644
index 0000000..35c9724
--- /dev/null
+++ b/packages/SettingsLib/res/values-tl/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Nag-i-scan…"</item>
+ <item msgid="8513729475867537913">"Kumukonekta…"</item>
+ <item msgid="515055375277271756">"Pinapatunayan…"</item>
+ <item msgid="1943354004029184381">"Kumukuha ng IP address…"</item>
+ <item msgid="4221763391123233270">"Konektado"</item>
+ <item msgid="624838831631122137">"Suspendido"</item>
+ <item msgid="7979680559596111948">"Nadidiskonekta…"</item>
+ <item msgid="1634960474403853625">"Hindi nakakonekta"</item>
+ <item msgid="746097431216080650">"Hindi tagumpay"</item>
+ <item msgid="6367044185730295334">"Naka-block"</item>
+ <item msgid="503942654197908005">"Pansamantalang iniiwasan ang mabagal na koneksyon"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Nag-ii-scan…"</item>
+ <item msgid="355508996603873860">"Kumukonekta sa <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="554971459996405634">"Nagpapatotoo sa <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="7928343808033020343">"Pagkuha ng IP address mula sa <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="8937994881315223448">"Nakakonekta sa <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Suspendido"</item>
+ <item msgid="7698638434317271902">"Inaalis sa pagkakakonekta mula sa <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="197508606402264311">"Hindi nakakonekta"</item>
+ <item msgid="8578370891960825148">"Hindi tagumpay"</item>
+ <item msgid="5660739516542454527">"Naka-block"</item>
+ <item msgid="1805837518286731242">"Pansamantalang iniiwasan ang mabagal na koneksyon"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
new file mode 100644
index 0000000..49f10a9
--- /dev/null
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Hindi makapag-scan ng mga network"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Wala"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Na-save"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Hindi Pinagana"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Pagkabigo ng Configuration ng IP"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Pagkabigo ng Koneksyon sa WiFi"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problema sa pagpapatotoo"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Wala sa sakop"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Walang Natukoy na Access sa Internet, hindi awtomatikong muling kumonekta."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Na-save ni <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Nakakonekta sa pamamagitan ng Wi‑Fi assistant"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Nakakonekta sa pamamagitan ng %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Available sa pamamagitan ng %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Nakakonekta, walang Internet"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Hindi nakakonekta"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Nadidiskonekta..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Kumukonekta…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Konektado"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Pinapares…"</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Nakakonekta (walang telepono)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Nakakonekta (walang media)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Nakakonekta (walang access sa mensahe)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Nakakonekta (walang telepono o media)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Audio ng media"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Audio ng telepono"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Paglilipat ng file"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Device sa pag-input"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Access sa internet"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Pagbabahagi ng contact"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Gamitin para sa pagbabahagi ng contact"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Pagbabahagi ng koneksyon sa internet"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Access sa Mensahe"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Access sa SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Konektado sa media audio"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Nakakonekta sa audio ng telepono"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Nakakonekta sa server sa paglilipat ng file"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Nakakonekta sa mapa"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Nakakonekta sa SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Hindi konektado sa server ng paglipat ng file"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Nakakonekta sa device ng input"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Konektado sa device sa Internet access"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Pagbahagi lokal koneksyon sa Internet sa device"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Gamitin para sa pag-access sa Internet"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Gamitin para sa mapa"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Gamitin para sa pag-access sa SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Gamitin para sa media audio"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Ginagamit para sa audio ng telepono"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Ginagamit para sa paglilipat ng file"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Gamitin para sa input"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Pares"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"IPARES"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Kanselahin"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Nagbibigay ang pagpapares ng access sa iyong mga contact at history ng tawag kapag nakakonekta."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Hindi maipares sa <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Hindi maipares sa <xliff:g id="DEVICE_NAME">%1$s</xliff:g> dahil sa maling PIN o passkey."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Hindi magawang makipag-ugnay sa <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Tinanggihan ng <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ang pagpapares."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Naka-off ang Wifi."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Nakadiskonekta ang Wifi."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"May isang bar ang Wifi."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"May dalawang bar ang Wifi."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"May tatlong bar ang Wifi."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Puno ang signal ng Wifi."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Mga inalis na app"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Mga inalis na apps at user"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"Pag-tether sa USB"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Portable na hotspot"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Pag-tether ng Bluetooth"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Nagte-tether"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Pag-tether at portable hotspot"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Profile sa trabaho"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Bisita"</string>
+ <string name="unknown" msgid="1592123443519355854">"Hindi Kilala"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"User: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"May nakatakdang ilang default"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Walang nakatakdang mga default"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Mga setting ng text-to-speech"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Output ng text-to-speech"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Rate ng pagsasalita"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Bilis ng pagsambit sa teksto"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Wika"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Gamitin ang wika ng system"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Walang napiling wika"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Itinatakda ang boses na partikular sa wika para sa sinasambit na teksto"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Makinig sa isang halimbawa"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Mag-play ng maikling pagpapakita ng speech synthesis"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"I-install ang data ng boses"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"I-install ang data ng boses na kinakailangan para sa speech synthesis"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Maaaring makolekta ng speech synthesis engine na ito ang lahat ng teksto na sasabihin, kabilang ang personal na data tulad ng mga password at mga numero ng credit card. Nanggagaling ito mula sa <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> engine. Paganahin ang paggamit ng speech synthesis engine na ito?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Nangangailangan ang wikang ito ng gumaganang koneksyon sa network para sa text-to-speech na output."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Isa itong halimbawa ng speech synthesis"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Status ng default na wika"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"Ganap na sinusuportahan ang <xliff:g id="LOCALE">%1$s</xliff:g>"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"Kailangan ng <xliff:g id="LOCALE">%1$s</xliff:g> ng koneksyon sa network"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"Hindi sinusuportahan ang <xliff:g id="LOCALE">%1$s</xliff:g>"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Sinusuri…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Mga setting para sa <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Ilunsad ang mga setting ng engine"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Ginustong engine"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Pangkalahatan"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Napakabagal"</item>
+ <item msgid="4795095314303559268">"Mabagal"</item>
+ <item msgid="8903157781070679765">"Normal"</item>
+ <item msgid="164347302621392996">"Mabilis"</item>
+ <item msgid="5794028588101562009">"Mas Mabilis"</item>
+ <item msgid="7163942783888652942">"Napakabilis"</item>
+ <item msgid="7831712693748700507">"Matulin"</item>
+ <item msgid="5194774745031751806">"Napakatulin"</item>
+ <item msgid="9085102246155045744">"Pinakamabilis"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Pumili ng Profile"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Personal"</string>
+ <string name="category_work" msgid="8699184680584175622">"Trabaho"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-tr/arrays.xml b/packages/SettingsLib/res/values-tr/arrays.xml
new file mode 100644
index 0000000..49d6839
--- /dev/null
+++ b/packages/SettingsLib/res/values-tr/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Taranıyor…"</item>
+ <item msgid="8513729475867537913">"Bağlanıyor…"</item>
+ <item msgid="515055375277271756">"Kimlik doğrulanıyor…"</item>
+ <item msgid="1943354004029184381">"IP adresi alınıyor..."</item>
+ <item msgid="4221763391123233270">"Bağlandı"</item>
+ <item msgid="624838831631122137">"Askıya alındı"</item>
+ <item msgid="7979680559596111948">"Bağlantı kesiliyor…"</item>
+ <item msgid="1634960474403853625">"Bağlantı kesildi"</item>
+ <item msgid="746097431216080650">"Başarısız"</item>
+ <item msgid="6367044185730295334">"Engellendi"</item>
+ <item msgid="503942654197908005">"Kötü bağlantıdan geçici olarak kaçınılıyor"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Taranıyor…"</item>
+ <item msgid="355508996603873860">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> ağına bağlanılıyor…"</item>
+ <item msgid="554971459996405634">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> üzerinde kimlik doğrulanıyor…"</item>
+ <item msgid="7928343808033020343">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> ağından IP adresi alınıyor…"</item>
+ <item msgid="8937994881315223448">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> ağına bağlandı"</item>
+ <item msgid="1330262655415760617">"Askıya alındı"</item>
+ <item msgid="7698638434317271902">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> bağlantısı kesiliyor…"</item>
+ <item msgid="197508606402264311">"Bağlantı kesildi"</item>
+ <item msgid="8578370891960825148">"Başarısız"</item>
+ <item msgid="5660739516542454527">"Engellendi"</item>
+ <item msgid="1805837518286731242">"Kötü bağlantıdan geçici olarak kaçınılıyor"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
new file mode 100644
index 0000000..db40231
--- /dev/null
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Ağlar taranamıyor"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Yok"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Kaydedildi"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Devre dışı"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Yapılandırması Hatası"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Kablosuz Bağlantı Hatası"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Kimlik doğrulama sorunu"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Kapsama alanı dışında"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"İnternet Erişimi algılanmadı, otomatik olarak tekrar bağlanmayacak."</string>
+ <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> tarafından kaydedildi"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Kablosuz bağlantı yardımcısıyla bağlandı"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s üzerinden bağlı"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s üzerinden kullanılabilir"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Bağlı, İnternet yok"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Bağlantı kesildi"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Bağlantı kesiliyor…"</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Bağlanıyor…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Bağlandı"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Eşleştiriliyor…"</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Bağlandı (telefon yok)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Bağlandı (medya yok)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Bağlı (mesaj erişimi yok)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Bağlandı (telefon veya medya yok)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Medya sesi"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Telefon sesi"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Dosya aktarımı"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Giriş cihazı"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"İnternet erişimi"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Kişi paylaşma"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Kişi paylaşmak için kullan"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"İnternet bağlantısı paylaşımı"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Mesaj Erişimi"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM Erişimi"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Medya sesine bağlanıldı"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Telefon sesine bağlandı"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Dosya aktarım sunucusuna bağlandı"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"MAP\'ye bağlı"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"SAP\'ye bağlı"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Dosya aktarım sunucusuna bağlanmadı"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Giriş cihazına bağlı"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"İnternet erişimi için cihaza bağlandı"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Cihazla yerel Intrnt bağlantısını paylaşıyor"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"İnternet erişimi için kullan"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"MAP için kullan"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"SIM erişimi için kullan"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Medya sesi için kullan"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Telefon sesi için kullan"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Dosya aktarımı için kullan"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Giriş için kullan"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Eşleştir"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"EŞLEŞTİR"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"İptal"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Eşleme işlemi, bağlantı kurulduğunda kişilerinize ve çağrı geçmişine erişim izni verir."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ile eşleştirilemedi."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"PIN veya parola yanlış olduğundan <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ile eşleştirilemedi"</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ile iletişim kurulamıyor."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Eşleştirme <xliff:g id="DEVICE_NAME">%1$s</xliff:g> tarafından reddedildi."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Kablosuz kapalı."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Kablosuz bağlantı kesildi."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Kablosuz sinyal gücü tek çubuk."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Kablosuz sinyal gücü iki çubuk."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Kablosuz sinyal gücü üç çubuk."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Kablosuz sinyal gücü tam."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android İS"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Kaldırılan uygulamalar"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Kaldırılmış kullanıcılar ve uygulamalar"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB tethering"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Taşınabilir hotspot"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth tethering"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Tethering"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Tethering ve taşnblr hotspot"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"İş profili"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Misafir"</string>
+ <string name="unknown" msgid="1592123443519355854">"Bilinmiyor"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Kullanıcı: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Bazı varsayılan tercihler ayarlandı"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Hiçbir varsayılan tercih ayarlanmadı"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Metin-konuşma ayarları"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Metin-konuşma çıktısı"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Konuşma hızı"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Metnin konuşulduğu hız"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Dil"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Sistemin dilini kullan"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Dil seçilmedi"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Dil için tanımlı sesi, konuşulan metin için ayarlar"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Bir örnek dinleyin"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Konuşma sentezinin kısa bir sunumunu çal"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Ses verilerini yükle"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Konuşma sentezi için gereken ses verilerini yükle"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Bu konuşma sentezi motoru, şifreler ve kredi kartı numaraları gibi kişisel veriler de dahil konuşulan tüm metni toplayabilir. <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> motorundan gelmektedir. Bu konuşma sentezi motorunun kullanımı etkinleştirilsin mi?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Bu dil, metin-konuşma çıktısı için bir ağ bağlantısı gerektirir."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Bu bir konuşma sentezi örneğidir"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Varsayılan dil durumu"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> tamamen destekleniyor"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> ağ bağlantısı gerektiriyor"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> desteklenmiyor"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Kontrol ediliyor…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g> ayarları"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Motor ayarlarını başlat"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Tercih edilen motor"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Genel"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Çok yavaş"</item>
+ <item msgid="4795095314303559268">"Yavaş"</item>
+ <item msgid="8903157781070679765">"Normal"</item>
+ <item msgid="164347302621392996">"Hızlı"</item>
+ <item msgid="5794028588101562009">"Daha hızlı"</item>
+ <item msgid="7163942783888652942">"Çok hızlı"</item>
+ <item msgid="7831712693748700507">"Seri"</item>
+ <item msgid="5194774745031751806">"Çok seri"</item>
+ <item msgid="9085102246155045744">"En hızlı"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Profil Seçin"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Kişisel"</string>
+ <string name="category_work" msgid="8699184680584175622">"İş"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-uk/arrays.xml b/packages/SettingsLib/res/values-uk/arrays.xml
new file mode 100644
index 0000000..a5608b4
--- /dev/null
+++ b/packages/SettingsLib/res/values-uk/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Сканув-ня..."</item>
+ <item msgid="8513729475867537913">"Підключення…"</item>
+ <item msgid="515055375277271756">"Автентифікація…"</item>
+ <item msgid="1943354004029184381">"Отримання ІР-адреси…"</item>
+ <item msgid="4221763391123233270">"Підключено"</item>
+ <item msgid="624838831631122137">"Припинено"</item>
+ <item msgid="7979680559596111948">"Відключення..."</item>
+ <item msgid="1634960474403853625">"Роз’єднано"</item>
+ <item msgid="746097431216080650">"Помилка"</item>
+ <item msgid="6367044185730295334">"Заблоковано"</item>
+ <item msgid="503942654197908005">"Тимчасове уникнення слабкого з’єднання"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Сканув-ня..."</item>
+ <item msgid="355508996603873860">"Підключення до <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="554971459996405634">"Автентифікація з <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="7928343808033020343">"Отримання ІР-адреси від <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="8937994881315223448">"Підключено до <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Призупинено"</item>
+ <item msgid="7698638434317271902">"Відключення від <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="197508606402264311">"Роз’єднано"</item>
+ <item msgid="8578370891960825148">"Помилка"</item>
+ <item msgid="5660739516542454527">"Заблоковано"</item>
+ <item msgid="1805837518286731242">"Тимчасове уникнення слабкого з’єднання"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
new file mode 100644
index 0000000..696ea39
--- /dev/null
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Неможливо здійснити сканування мереж"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Немає"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Збережено"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Вимкнено"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Помилка конфігурації IP-адреси"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Помилка з’єднання Wi-Fi"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Проблема з автентифікацією"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Не в діапазоні"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Немає доступу до Інтернету. Спроба під’єднання не здійснюватиметься автоматично."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Збережено додатком <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Під’єднано через Диспетчер Wi-Fi-з’єднання"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Під’єднано через %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Доступ через %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Під’єднано, але немає доступу до Інтернету"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Роз’єднано"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Відключення..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Підключення…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Підключено"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Підключення…"</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Під’єднано (без телефону)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Під’єднано (без медіа-файлів)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Під’єднано (без доступу до повідомлень)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Під’єднано (без телефону чи медіа)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Звук медіа-файлів"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Звук телефону"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Передавання файлів"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Пристрій введення"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Доступ до Інтернету"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Надсилання контактів"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Використовувати для надсилання контактів"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Надання доступу до Інтернету"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Доступ до повідомлень"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Доступ до SIM-карти"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Підключено до аудіоджерела"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Підключено до звуку телеф."</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Підключ. до сервера передачі файлів"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Під’єднано до карти"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Під’єднано до точки доступу"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Не підключ. до сервера передачі файлів"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Підключено до пристрою введ."</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Під’єдн. до пристр. для дост.до Інтерн."</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Доступ до локал.з’єдн. з Інтерн. ч-з пристрій"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Використовувати для доступу до Інтернету"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Використовувати для карти"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Використовувати для доступу до SIM-карти"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Викор. для аудіоджер."</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Викор. для звуку тел."</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Викор. для перед. файлів"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Викор. для введ."</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Підключити"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ПІДКЛЮЧИТИСЯ"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Скасувати"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Якщо ви під’єднаєте інший пристрій, він матиме доступ до ваших контактів та історії дзвінків."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Не вдалося створити пару з пристроєм <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Не вдалося створити пару з пристроєм <xliff:g id="DEVICE_NAME">%1$s</xliff:g> через неправильний PIN-код чи ключ доступу."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Неможливо зв’язатися з пристроєм <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Створ. пари відхилено <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi вимкнено."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi від’єднано."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Одна смужка сигналу Wi-Fi."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Дві смужки сигналу Wi-Fi."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Три смужки сигналу Wi-Fi."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Максимальний сигнал Wi-Fi."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"ОС Android"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Видалені програми"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Видалені програми та користувачі"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"Прив\'язка USB"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Порт. точка дост."</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Прив\'язка Bluetooth"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Прив\'язка"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Режим модема"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Робочий профіль"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Гість"</string>
+ <string name="unknown" msgid="1592123443519355854">"Невідомо"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Користувач: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Налаштовано деякі значення за умовчанням"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Немає значень за умовчанням"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Налашт. синтезу мовлення"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Вивід синтезу мовлення з тексту"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Темп мовлення"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Швидкість відтворення тексту"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Мова"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Використовувати мову системи"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Мову не вибрано"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Устан. голос для відтворюваного тексту залежно від мови"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Прослухайте приклад"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Відтворити коротку демонстрацію синтезу мови"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Встановлення голосових даних"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Установіть потрібні голосові дані для синтезу мовлення"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Ця система синтезу мовлення може збирати всі відтворювані тексти, зокрема особисті дані, такі як паролі та номери кредитних карток. Вона походить від системи <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Увімкнути використання цієї системи синтезу мовлення?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Для виходу синтезу мовлення цією мовою потрібно мати з’єднання з Інтернетом."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Це приклад синтезу мовлення"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Статус мови за умовчанням"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g>: підтримується повністю"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g>: потрібне з’єднання з мережею"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g>: не підтримується"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Перевірка…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Запускати налаштування системи"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Бажана система"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Загальні"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Дуже повільно"</item>
+ <item msgid="4795095314303559268">"Повільно"</item>
+ <item msgid="8903157781070679765">"Звичайно"</item>
+ <item msgid="164347302621392996">"Швидко"</item>
+ <item msgid="5794028588101562009">"Швидше"</item>
+ <item msgid="7163942783888652942">"Дуже швидко"</item>
+ <item msgid="7831712693748700507">"Надзвичайно швидко"</item>
+ <item msgid="5194774745031751806">"Украй швидко"</item>
+ <item msgid="9085102246155045744">"Найшвидше"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Вибрати профіль"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Особисте"</string>
+ <string name="category_work" msgid="8699184680584175622">"Робота"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-ur-rPK/arrays.xml b/packages/SettingsLib/res/values-ur-rPK/arrays.xml
new file mode 100644
index 0000000..c58a4a7
--- /dev/null
+++ b/packages/SettingsLib/res/values-ur-rPK/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"اسکین کر رہا ہے…"</item>
+ <item msgid="8513729475867537913">"مربوط ہو رہا ہے…"</item>
+ <item msgid="515055375277271756">"توثیق ہو رہی ہے…"</item>
+ <item msgid="1943354004029184381">"IP پتہ حاصل کر رہا ہے…"</item>
+ <item msgid="4221763391123233270">"مربوط ہو گیا"</item>
+ <item msgid="624838831631122137">"معطل شدہ"</item>
+ <item msgid="7979680559596111948">"منقطع کیا جارہا ہے…"</item>
+ <item msgid="1634960474403853625">"منقطع ہو گیا"</item>
+ <item msgid="746097431216080650">"ناکام"</item>
+ <item msgid="6367044185730295334">"مسدود"</item>
+ <item msgid="503942654197908005">"عارضی طور پر خراب کنکشن سے اجتناب کر رہا ہے"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"اسکین کر رہا ہے…"</item>
+ <item msgid="355508996603873860">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> سے مربوط ہو رہا ہے…"</item>
+ <item msgid="554971459996405634">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> کے ساتھ توثیق ہو رہی ہے…"</item>
+ <item msgid="7928343808033020343">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> سے IP پتہ حاصل کر رہا ہے…"</item>
+ <item msgid="8937994881315223448">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> سے مربوط ہو گیا"</item>
+ <item msgid="1330262655415760617">"معطل شدہ"</item>
+ <item msgid="7698638434317271902">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> سے منقطع ہو رہا ہے…"</item>
+ <item msgid="197508606402264311">"منقطع ہو گیا"</item>
+ <item msgid="8578370891960825148">"ناکام"</item>
+ <item msgid="5660739516542454527">"مسدود ہے"</item>
+ <item msgid="1805837518286731242">"عارضی طور پر خراب کنکشن سے اجتناب کر رہا ہے"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-ur-rPK/strings.xml b/packages/SettingsLib/res/values-ur-rPK/strings.xml
new file mode 100644
index 0000000..31b76ed
--- /dev/null
+++ b/packages/SettingsLib/res/values-ur-rPK/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"نیٹ ورکس کیلئے اسکین نہيں کر سکتے ہیں"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"کوئی نہیں"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"محفوظ کردیا گیا"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"غیر فعال"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP کنفیگریشن کی ناکامی"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi کنکشن کی ناکامی"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"توثیق کا مسئلہ"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"رینج میں نہیں ہے"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"انٹرنیٹ تک کسی رسائی کا پتہ نہیں چلا، خود بخود دوبارہ منسلک نہیں ہوگا۔"</string>
+ <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> کی جانب سے محفوظ کردہ"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fi اسسٹنٹ کے ذریعے منسلک ہے"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"منسلک بذریعہ %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"دستیاب بذریعہ %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"منسلک، انٹرنیٹ نہیں ہے"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"منقطع"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"منقطع کیا جارہا ہے…"</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"مربوط ہو رہا ہے…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"مربوط"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"جوڑا بنایا جا رہا ہے…"</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"مربوط (کوئی فون نہیں ہے)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"مربوط (کوئی میڈیا نہیں ہے)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"مربوط (کسی پیغام تک رسائی نہیں ہے)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"مربوط (کوئی فون یا میڈیا نہیں ہے)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"میڈيا آڈیو"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"فون آڈیو"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"فائل کی منتقلی"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"ان پٹ آلہ"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"انٹرنیٹ تک رسائی"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"رابطہ کا اشتراک"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"رابطہ کے اشتراک کیلئے استعمال کریں"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"انٹرنیٹ کنکشن کا اشتراک کرنا"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"پیغام تک رسائی"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM رسائی"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"میڈیا آڈیو سے مربوط"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"فون آڈیو سے مربوط"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"فائل منتقلی سرور سے مربوط ہو گیا ہے"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"نقشہ سے مربوط ہوگیا"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"SAP سے منسلک"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"فائل منتقلی سرور سے مربوط نہیں ہے"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"ان پٹ آلہ سے مربوط"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"انٹرنیٹ رسائی کیلئے آلہ سے مربوط ہو گیا"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"آلہ کے ساتھ مقامی انٹرنیٹ کنکشن کا اشتراک کر رہا ہے"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"انٹرنیٹ رسائی کیلئے استعمال"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"نقشہ کیلئے استعمال کریں"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"SIM رسائی کے لئے استعمال کریں"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"میڈیا آڈیو کیلئے استعمال کریں"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"فون آڈیو کیلئے استعمال کریں"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"فائل منتقل کرنے کیلئے استعمال کریں"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ان پٹ کیلئے استعمال"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"جوڑا بنائیں"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"جوڑا بنائیں"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"منسوخ کریں"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"منسلک ہونے پر جوڑا بنانے سے آپ کے رابطوں اور کال کی سرگزشت تک رسائی حاصل ہو جاتی ہے۔"</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> کے ساتھ جوڑا نہیں بنا سکا۔"</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"غلط PIN یا پاس کلید کی وجہ سے <xliff:g id="DEVICE_NAME">%1$s</xliff:g> کے ساتھ جوڑا نہیں بنا سکا۔"</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> کے ساتھ مواصلت نہیں ہو سکتی۔"</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> نے جوڑا بنانے کو مسترد کر دیا۔"</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wifi آف ہے۔"</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wifi غیر منسلک ہو گیا۔"</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wifi ایک بار۔"</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wifi دو بارز۔"</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wifi تین بارز۔"</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Wifi سگنل پورا ہے۔"</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"ہٹائی گئی ایپس"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"ہٹائی گئی ایپس اور صارفین"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB مربوط کرنا"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"پورٹیبل ہاٹ اسپاٹ"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"بلوٹوتھ مربوط کرنا"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"مربوط کرنا"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"مربوط کرنا اور پورٹیبل ہاٹ اسپاٹ"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"دفتر کا پروفائل"</string>
+ <string name="user_guest" msgid="8475274842845401871">"مہمان"</string>
+ <string name="unknown" msgid="1592123443519355854">"نامعلوم"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"صارف: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"کچھ ڈیفالٹس سیٹ ہیں"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"کوئی ڈیفالٹس سیٹ نہیں ہیں"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"ٹیکسٹ ٹو اسپیچ کی ترتیبات"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"ٹیکسٹ ٹو اسپیچ آؤٹ پٹ"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"اسپیچ کی شرح"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"متن بولے جانے کی رفتار"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"زبان"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"سسٹم کی زبان استعمال کریں"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"زبان منتخب نہیں کی گئی"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"زبانی متن کیلئے زبان کیلئے مخصوص آواز کو سیٹ کرتا ہے"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"ایک مثال سنیں"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"اسپیچ سنتھیسس کا ایک مختصر مظاہرہ چلائیں"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"صوتی ڈیٹا انسٹال کریں"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"اسپیچ سنتھیسس کیلئے درکار صوتی ڈیٹا انسٹال کریں"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"یہ اسپیچ سنتھیسس انجن آپ کے ٹائپ کردہ سبھی متن کو جمع کر سکتا ہے، بشمول ذاتی ڈیٹا جیسے پاس ورڈز اور کریڈٹ کارڈ نمبرز۔ یہ <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> انجن سے آتا ہے۔ اس اسپیچ سنتھیسس انجن کا استعمال فعال کریں؟"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"اس زبان کو ٹیکسٹ ٹو اسپیچ کے آؤٹ پٹ کیلئے ایک کام کرنے والے کنکشن کی ضرورت ہے۔"</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"یہ تقریر کی ترکیب کی ایک مثال ہے"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"ڈیفالٹ زبان کا اسٹیٹس"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> مکمل طور پر تعاون یافتہ ہے"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> کو نیٹ ورک کنکشن کی ضرورت ہے"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> تعاون یافتہ نہیں ہے"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"چیک کیا جا رہا ہے…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g> کیلئے ترتیبات"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"انجن کی ترتیبات شروع کریں"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"ترجیحی انجن"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"عمومی"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"بہت سست"</item>
+ <item msgid="4795095314303559268">"سست"</item>
+ <item msgid="8903157781070679765">"عام"</item>
+ <item msgid="164347302621392996">"تیز"</item>
+ <item msgid="5794028588101562009">"تیز تر"</item>
+ <item msgid="7163942783888652942">"بہت تیز"</item>
+ <item msgid="7831712693748700507">"تیز"</item>
+ <item msgid="5194774745031751806">"کافی تیز"</item>
+ <item msgid="9085102246155045744">"تیز ترین"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"پروفائل منتخب کریں"</string>
+ <string name="category_personal" msgid="1299663247844969448">"ذاتی"</string>
+ <string name="category_work" msgid="8699184680584175622">"دفتر"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-uz-rUZ/arrays.xml b/packages/SettingsLib/res/values-uz-rUZ/arrays.xml
new file mode 100644
index 0000000..f2cae2c
--- /dev/null
+++ b/packages/SettingsLib/res/values-uz-rUZ/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Tekshirib chiqilmoqda…"</item>
+ <item msgid="8513729475867537913">"Ulanmoqda…"</item>
+ <item msgid="515055375277271756">"Tasdiqdan o‘tilmoqda…"</item>
+ <item msgid="1943354004029184381">"IP manzil o‘zlashtirilmoqda…"</item>
+ <item msgid="4221763391123233270">"Ulangan"</item>
+ <item msgid="624838831631122137">"Muzlatildi"</item>
+ <item msgid="7979680559596111948">"Uzilmoqda…"</item>
+ <item msgid="1634960474403853625">"Uzilgan"</item>
+ <item msgid="746097431216080650">"Muvaffaqiyatsiz"</item>
+ <item msgid="6367044185730295334">"To‘sib qo‘yilgan"</item>
+ <item msgid="503942654197908005">"Sifatsiz ulanishdan vaqtinchalik chetlashish"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Tekshirilmoqda…"</item>
+ <item msgid="355508996603873860">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> tarmog‘iga ulanilmoqda…"</item>
+ <item msgid="554971459996405634">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> bilan aloqa o‘rnatilyapti…"</item>
+ <item msgid="7928343808033020343">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> IP manzil beryapti…"</item>
+ <item msgid="8937994881315223448">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> tarmog‘iga ulanildi"</item>
+ <item msgid="1330262655415760617">"Muzlatildi"</item>
+ <item msgid="7698638434317271902">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> tarmog‘idan uzilmoqda…"</item>
+ <item msgid="197508606402264311">"Uzildi"</item>
+ <item msgid="8578370891960825148">"Muvaffaqiyatsiz"</item>
+ <item msgid="5660739516542454527">"To‘sib qo‘yilgan"</item>
+ <item msgid="1805837518286731242">"Sifatsiz ulanishdan vaqtinchalik chetlashish"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-uz-rUZ/strings.xml b/packages/SettingsLib/res/values-uz-rUZ/strings.xml
new file mode 100644
index 0000000..705194c
--- /dev/null
+++ b/packages/SettingsLib/res/values-uz-rUZ/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Tarmoqlarni tekshirib chiqishni iloji bo‘lmadi"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Yo‘q"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Saqlandi"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"O‘chiq"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP manzilini sozlab bo‘lmadi"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi ulanishini o‘rnatib bo‘lmadi"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Tasdiqdan o‘tishda muammo"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Aloqada emas"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Internetga ulanish aniqlanmadi, avtomatik ravishda qayta ulana olmaydi."</string>
+ <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> tomonidan saqlangan"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fi yordamchisi orqali ulangan"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s orqali ulangan"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s orqali ishlaydi"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Ulangan, lekin internet aloqasi yo‘q"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Uzildi"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Uzilyapti…"</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Ulanmoqda…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Ulandi"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Biriktirilmoqda"</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Ulandi (telefondan tashqari)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Ulanildi (mediadan tashqari)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Ulangan (xabarlarga kirib bo‘lmaydi)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Ulangan (telefon yoki media qurilma emas)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Media audio"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Telefon audiosi"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Fayl o‘tkazish"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Kiritish qurilmasi"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Internetga kirish"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Kontaktlarni ulashish"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Kontaktlarni ulashish uchun ishlatilsin"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Internet aloqasi ulashmasi"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Xabarga kirish"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM-kartaga kirish"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Audio qurilmasiga ulangan"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Telefon karnayiga ulanildi"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Fayl almashinish serveriga ulanildi"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Xaritaga ulangan"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Ulanish nuqtasiga ulandi"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Fayl uzatish serveriga ulanmagan"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Kiritish qurilmasiga ulanildi"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Internet manbai qurilmasiga ulanildi"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Qurilmaga mahaliy internet aloqani tarqatish"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Internet manbai sifatida foydalanish"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Xaritada foydalanish"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"SIM-kartaga kirish uchun foydalanish"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Audio qurilma uchun foydalanish"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Dok’dan karnay sifatida foydalanish"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Fayl almashinish uchun foydalanish"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Kiritish qurilmasi sifatida foydalanish"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Biriktirish"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ULANISH"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Bekor qilish"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Agar ulanishga ruxsat bersangiz, ulangan vaqtda kontakt va qo‘ng‘iroqlaringiz tarixiga kirishi mumkin."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> bilan biriktirib bo‘lmadi."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> qurilmasiga ulanib bo‘lmadi, chunki PIN-kod yoki parol noto‘g‘ri kiritildi."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Quyidagi qurilma javob bermayapti: <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> biriktirish so‘rovini rad qildi."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi o‘chiq."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi o‘chirilgan."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi: bitta ustun"</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wi-Fi: ikkita ustun"</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wi-Fi: uchta ustun"</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Wi-Fi: signal to‘liq"</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"O‘chirilgan ilovalar"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"O‘chirib yuborilgan ilovalar va foydalanuvchilar"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB modem"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Ixcham hotspot"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth modem"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Modem"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Modem rejimi"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Ishchi profil"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Mehmon"</string>
+ <string name="unknown" msgid="1592123443519355854">"Noma‘lum"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Foydalanuvchi: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Ba’zi birlamchi sozlamalar o‘rnatilgan"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Birlamchi sozlamalar o‘rnatilmagan"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Nutq sintezi sozlamalari"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Nutq sintezi"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Nutq tezligi"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Matnni o‘qish tezligi"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Til"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Tizim tili"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Til tanlanmagan"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Og‘zaki matn uchun tilga moslangan ovoz o‘rnatadi"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Namunani eshitish"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Qisqa nutq sintezi namunasini eshitish"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Ovoz ma’lumotlarini o‘rnatish"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Nutq sintezi uchun kerakli ovoz ma’lumotlarini o‘rnatish"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Ushbu nutq sintezi mexanizmi barcha yozgan matnlaringizni to‘plab olishi mumkin, jumladan kredit karta raqamlari va parollar kabi shaxsiy ma‘lumotlarni ham. U <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> mexanizmi bilan o‘rnatiladi. Ushbu nutq sintezi mexanizmidan foydalanilsinmi?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Bu til matnni nutqga o‘girish uchun faol Internet ulanishini talab qiladi."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Bu nutq sintezining misoli"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Birlamchi til"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> – to‘liq qo‘llab-quvvatlanadi"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> tili tarmoqqa ulanishi lozim"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> – qo‘llab-quvvatlanmaydi"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Tekshirilmoqda…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g> sozlamalari"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Mexanizm sozlamalarini ishga tushirish"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Standart tizim"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Umumiy"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Juda sekin"</item>
+ <item msgid="4795095314303559268">"Sekin"</item>
+ <item msgid="8903157781070679765">"O‘rtacha"</item>
+ <item msgid="164347302621392996">"Tez"</item>
+ <item msgid="5794028588101562009">"Tezroq"</item>
+ <item msgid="7163942783888652942">"Juda tez"</item>
+ <item msgid="7831712693748700507">"Tez"</item>
+ <item msgid="5194774745031751806">"Juda tez"</item>
+ <item msgid="9085102246155045744">"Eng tez"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Profil tanlash"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Shaxsiy"</string>
+ <string name="category_work" msgid="8699184680584175622">"Ish"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-vi/arrays.xml b/packages/SettingsLib/res/values-vi/arrays.xml
new file mode 100644
index 0000000..26d32a9
--- /dev/null
+++ b/packages/SettingsLib/res/values-vi/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Đang quét…"</item>
+ <item msgid="8513729475867537913">"Đang kết nối…"</item>
+ <item msgid="515055375277271756">"Đang xác thực…"</item>
+ <item msgid="1943354004029184381">"Đang lấy địa chỉ IP…"</item>
+ <item msgid="4221763391123233270">"Đã kết nối"</item>
+ <item msgid="624838831631122137">"Bị tạm ngưng"</item>
+ <item msgid="7979680559596111948">"Đang ngắt kết nối…"</item>
+ <item msgid="1634960474403853625">"Đã ngắt kết nối"</item>
+ <item msgid="746097431216080650">"Không thành công"</item>
+ <item msgid="6367044185730295334">"Bị chặn"</item>
+ <item msgid="503942654197908005">"Tạm thời tránh kết nối kém"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Đang quét…"</item>
+ <item msgid="355508996603873860">"Đang kết nối tới <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="554971459996405634">"Đang xác thực với <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="7928343808033020343">"Đang lấy địa chỉ IP từ <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="8937994881315223448">"Đã kết nối tới <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Bị tạm ngưng"</item>
+ <item msgid="7698638434317271902">"Đăng ngắt kết nối khỏi <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="197508606402264311">"Đã ngắt kết nối"</item>
+ <item msgid="8578370891960825148">"Không thành công"</item>
+ <item msgid="5660739516542454527">"Bị chặn"</item>
+ <item msgid="1805837518286731242">"Tạm thời tránh kết nối kém"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
new file mode 100644
index 0000000..c3db6c5
--- /dev/null
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Không thể dò tìm mạng"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Không"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Đã lưu"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Đã tắt"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Lỗi cấu hình IP"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Lỗi kết nối WiFi"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Sự cố xác thực"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Ngoài vùng phủ sóng"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Không phát hiện thấy truy cập Internet nào, mạng sẽ không được tự động kết nối lại."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Được lưu bởi <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Được kết nối qua trình hỗ trợ Wi‑Fi"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Được kết nối qua %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Có sẵn qua %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Đã kết nối, không có Internet"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Đã ngắt kết nối"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Đang ngắt kết nối…"</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Đang kết nối…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Đã kết nối"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Đang ghép nối…"</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Đã kết nối (không có điện thoại)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Đã kết nối (không có phương tiện)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Đã kết nối (không truy cập tin nhắn)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Đã k.nối (kg có ĐT hoặc p.tiện nào)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Âm thanh của phương tiện"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Âm thanh điện thoại"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Chuyển tệp"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Thiết bị đầu vào"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Truy cập Internet"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Chia sẻ liên hệ"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Sử dụng để chia sẻ liên hệ"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Chia sẻ kết nối internet"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Truy cập tin nhắn"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Quyền truy cập SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Đã kết nối với âm thanh phương tiện"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Đã kết nối với âm thanh điện thoại"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Đã kết nối với máy chủ chuyển tệp"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Đã kết nối với bản đồ"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Được kết nối với SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Chưa kết nối với máy chủ chuyển tệp"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Đã kết nối với thiết bị nhập"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Đã kết nối với thiết bị"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Chia sẻ kết nối Internet"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Sử dụng để truy cập Internet"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Sử dụng cho bản đồ"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Sử dụng để truy cập SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Sử dụng cho âm thanh phương tiện"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Sử dụng cho âm thanh điện thoại"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Sử dụng để chuyển tệp"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Sử dụng để nhập"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Ghép nối"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"GHÉP NỐI"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Hủy"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Ghép nối giúp bạn có thể truy cập danh bạ và nhật ký cuộc gọi của mình khi được kết nối."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Không thể ghép nối với <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Không thể ghép nối với <xliff:g id="DEVICE_NAME">%1$s</xliff:g> do mã PIN hoặc mã xác nhận không đúng."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Không thể kết nối với <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Ghép nối bị <xliff:g id="DEVICE_NAME">%1$s</xliff:g> từ chối."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Đã tắt Wi-Fi."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Đã ngắt kết nối Wi-Fi."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Tín hiệu Wi-Fi một vạch."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Tín hiệu Wi-Fi hai vạch."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Tín hiệu Wi-Fi ba vạch."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Tín hiệu Wi-Fi đủ."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Hệ điều hành Android"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Ứng dụng đã xóa"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Ứng dụng và người dùng bị xóa"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"Truy cập Internet qua USB"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Điểm phát sóng di động"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Truy cập Internet qua Bluetooth"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Đang dùng làm điểm truy cập Internet"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"USB Internet & điểm truy cập di động"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Hồ sơ công việc"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Khách"</string>
+ <string name="unknown" msgid="1592123443519355854">"Không xác định"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Người dùng: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Đã đặt một số ứng dụng chạy mặc định"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Chưa đặt mặc định"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Cài đặt chuyển văn bản thành giọng nói"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Đầu ra v.bản thành giọng nói"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Tốc độ nói"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Tốc độ đọc văn bản"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Ngôn ngữ"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Sử dụng ngôn ngữ hệ thống"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Không thể chọn ngôn ngữ"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Đặt giọng nói ngôn ngữ cụ thể cho văn bản nói"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Nghe ví dụ"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Phát minh hoạ ngắn về tổng hợp giọng nói"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Cài đặt dữ liệu thoại"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Cài đặt dữ liệu thoại bắt buộc cho tổng hợp tiếng nói"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Công cụ tổng hợp tiếng nói này có thể thu thập tất cả nội dụng sẽ được nói, bao gồm dữ liệu cá nhân như mật khẩu và số thẻ tín dụng. Công cụ này xuất phát từ công cụ <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Cho phép sử dụng công cụ tổng hợp tiếng nói này?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Ngôn ngữ này yêu cầu phải có kết nối mạng hoạt động để có thể phát âm thanh được chuyển từ văn bản sang giọng nói."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Đây là một ví dụ về tổng hợp tiếng nói"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Trạng thái ngôn ngữ mặc định"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> được hỗ trợ đầy đủ"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> yêu cầu kết nối mạng"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> không được hỗ trợ"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Đang kiểm tra…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Cài đặt cho <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Cài đặt chạy công cụ"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Công cụ ưu tiên"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Chung"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Rất chậm"</item>
+ <item msgid="4795095314303559268">"Chậm"</item>
+ <item msgid="8903157781070679765">"Bình thường"</item>
+ <item msgid="164347302621392996">"Nhanh"</item>
+ <item msgid="5794028588101562009">"Nhanh hơn"</item>
+ <item msgid="7163942783888652942">"Rất nhanh"</item>
+ <item msgid="7831712693748700507">"Nhanh"</item>
+ <item msgid="5194774745031751806">"Rất nhanh"</item>
+ <item msgid="9085102246155045744">"Nhanh nhất"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Chọn hồ sơ"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Cá nhân"</string>
+ <string name="category_work" msgid="8699184680584175622">"Cơ quan"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-zh-rCN/arrays.xml b/packages/SettingsLib/res/values-zh-rCN/arrays.xml
new file mode 100644
index 0000000..b7d99a1
--- /dev/null
+++ b/packages/SettingsLib/res/values-zh-rCN/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"正在扫描..."</item>
+ <item msgid="8513729475867537913">"正在连接..."</item>
+ <item msgid="515055375277271756">"正在进行身份验证..."</item>
+ <item msgid="1943354004029184381">"正在获取IP地址..."</item>
+ <item msgid="4221763391123233270">"已连接"</item>
+ <item msgid="624838831631122137">"已暂停"</item>
+ <item msgid="7979680559596111948">"正在断开连接..."</item>
+ <item msgid="1634960474403853625">"已断开连接"</item>
+ <item msgid="746097431216080650">"失败"</item>
+ <item msgid="6367044185730295334">"已停用"</item>
+ <item msgid="503942654197908005">"暂时关闭(网络状况不佳)"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"正在扫描..."</item>
+ <item msgid="355508996603873860">"正在连接到 <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="554971459996405634">"正在通过 <xliff:g id="NETWORK_NAME">%1$s</xliff:g> 进行身份验证..."</item>
+ <item msgid="7928343808033020343">"正在从<xliff:g id="NETWORK_NAME">%1$s</xliff:g>获取IP地址..."</item>
+ <item msgid="8937994881315223448">"已连接到 <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"已暂停"</item>
+ <item msgid="7698638434317271902">"正在断开与 <xliff:g id="NETWORK_NAME">%1$s</xliff:g> 的连接..."</item>
+ <item msgid="197508606402264311">"已断开连接"</item>
+ <item msgid="8578370891960825148">"失败"</item>
+ <item msgid="5660739516542454527">"已停用"</item>
+ <item msgid="1805837518286731242">"暂时关闭(网络状况不佳)"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..e7c6d14
--- /dev/null
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"无法扫描网络"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"无"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"已保存"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"已停用"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP 配置失败"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WLAN 连接失败"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"身份验证出现问题"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"不在范围内"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"未检测到任何互联网连接,因此不会自动重新连接。"</string>
+ <string name="saved_network" msgid="4352716707126620811">"已通过<xliff:g id="NAME">%1$s</xliff:g>保存"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"已连接(通过 WLAN 助手)"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"已通过%1$s连接"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"可通过%1$s连接"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"已连接,但无法访问互联网"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"已断开连接"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"正在断开连接..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"正在连接..."</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"已连接"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"正在配对..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"已连接(无手机)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"已连接(无媒体)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"已连接(无消息权限)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"已连接(没有手机或媒体信号)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"媒体音频"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"手机音频"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"文件传输"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"输入设备"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"互联网连接"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"共享联系人"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"用于共享联系人"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"共享互联网连接"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"消息权限"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM 卡存取权限"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"已连接到媒体音频"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"已连接到手机音频"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"已连接到文件传输服务器"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"已连接到地图"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"已连接到 SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"未连接到文件传输服务器"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"已连接到输入设备"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"经由其他设备连接到互联网"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"与其他设备共享该设备的互联网连接"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"用于连接互联网"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"用于地图"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"用于存取 SIM 卡"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"用于媒体音频"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"用于手机音频"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"用于文件传输"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"用于输入"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"配对"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"配对"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"取消"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"配对之后,所配对的设备将可以在建立连接后访问您的通讯录和通话记录。"</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"无法与“<xliff:g id="DEVICE_NAME">%1$s</xliff:g>”进行配对。"</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"PIN码或配对密钥不正确,无法与<xliff:g id="DEVICE_NAME">%1$s</xliff:g>配对。"</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"无法与“<xliff:g id="DEVICE_NAME">%1$s</xliff:g>”进行通信。"</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> 已拒绝配对。"</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"WLAN 已关闭。"</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"WLAN 连接已断开。"</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"WLAN 信号强度为一格。"</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"WLAN 信号强度为两格。"</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"WLAN 信号强度为三格。"</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"WLAN 信号满格。"</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android 操作系统"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"已删除的应用"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"已删除的应用和用户"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB网络共享"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"便携式热点"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"蓝牙网络共享"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"网络共享"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"网络共享与便携式热点"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"工作资料"</string>
+ <string name="user_guest" msgid="8475274842845401871">"访客"</string>
+ <string name="unknown" msgid="1592123443519355854">"未知"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"用户:<xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"已设置部分默认选项"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"未设置任何默认选项"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"文字转语音设置"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"文字转语音 (TTS) 输出"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"语速"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"文字转换成语音后的播放速度"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"语言"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"使用系统语言"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"未选择语言"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"设置文字转语音功能要使用的语言"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"收听示例"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"播放简短的语音合成示例"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"安装语音数据包"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"安装语音合成所需的数据包"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"此语音合成引擎能够收集语音中出现的所有信息,包括密码和信用卡号码之类的个人数据。此功能由 <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> 引擎提供。是否启用此语音合成引擎?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"您必须连接到网络才能使用文字转语音功能输出这种语言。"</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"这是语音合成示例"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"默认语言状态"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"完全支持<xliff:g id="LOCALE">%1$s</xliff:g>"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"只有在连接到网络的情况下,才支持<xliff:g id="LOCALE">%1$s</xliff:g>"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"不支持<xliff:g id="LOCALE">%1$s</xliff:g>"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"正在检查…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"“<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>”的设置"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"进行引擎设置"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"首选引擎"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"常规"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"很慢"</item>
+ <item msgid="4795095314303559268">"慢"</item>
+ <item msgid="8903157781070679765">"正常"</item>
+ <item msgid="164347302621392996">"快"</item>
+ <item msgid="5794028588101562009">"较快"</item>
+ <item msgid="7163942783888652942">"非常快"</item>
+ <item msgid="7831712693748700507">"迅速"</item>
+ <item msgid="5194774745031751806">"很迅速"</item>
+ <item msgid="9085102246155045744">"最快"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"选择个人资料"</string>
+ <string name="category_personal" msgid="1299663247844969448">"个人"</string>
+ <string name="category_work" msgid="8699184680584175622">"工作"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-zh-rHK/arrays.xml b/packages/SettingsLib/res/values-zh-rHK/arrays.xml
new file mode 100644
index 0000000..b8a1ebb
--- /dev/null
+++ b/packages/SettingsLib/res/values-zh-rHK/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"掃瞄中…"</item>
+ <item msgid="8513729475867537913">"正在連線..."</item>
+ <item msgid="515055375277271756">"正在驗證…"</item>
+ <item msgid="1943354004029184381">"正在取得 IP 位址…"</item>
+ <item msgid="4221763391123233270">"已連線"</item>
+ <item msgid="624838831631122137">"已暫停"</item>
+ <item msgid="7979680559596111948">"正在中斷連線…"</item>
+ <item msgid="1634960474403853625">"已中斷連線"</item>
+ <item msgid="746097431216080650">"失敗"</item>
+ <item msgid="6367044185730295334">"已封鎖"</item>
+ <item msgid="503942654197908005">"目前正暫時避開欠佳的連線"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"掃瞄中…"</item>
+ <item msgid="355508996603873860">"正在連線到 <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="554971459996405634">"正在取得 <xliff:g id="NETWORK_NAME">%1$s</xliff:g> 的授權…"</item>
+ <item msgid="7928343808033020343">"正在從 <xliff:g id="NETWORK_NAME">%1$s</xliff:g> 取得 IP 位址…"</item>
+ <item msgid="8937994881315223448">"已連線到 <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"已暫停"</item>
+ <item msgid="7698638434317271902">"正在中斷 <xliff:g id="NETWORK_NAME">%1$s</xliff:g> 連線…"</item>
+ <item msgid="197508606402264311">"已中斷連線"</item>
+ <item msgid="8578370891960825148">"失敗"</item>
+ <item msgid="5660739516542454527">"已封鎖"</item>
+ <item msgid="1805837518286731242">"目前正暫時避開欠佳的連線"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
new file mode 100644
index 0000000..170bc0f
--- /dev/null
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"無法掃瞄網絡"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"無"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"已儲存"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"已停用"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP 設定失敗"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi 連線失敗"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"驗證問題"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"超出可用範圍"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"未能偵測到互聯網連線,因此不會自動重新連線。"</string>
+ <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> 的儲存"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"已透過 Wi-Fi 小幫手連線"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"已透過 %1$s 連線"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"可透過 %1$s 連線"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"已連線,沒有互聯網"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"已中斷連線"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"正在中斷連線..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"正在連線..."</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"已連結"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"正在配對..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"已連線(無手機)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"已連線 (無媒體)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"已連結 (無訊息存取權)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"已連線 (無手機或媒體)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"媒體音效"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"手機音效"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"檔案傳輸"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"輸入裝置"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"互聯網連線"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"聯絡人共用"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"用於聯絡人共用"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"互聯網連線分享"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"訊息存取權"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM 卡存取"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"已連接媒體音頻裝置"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"已連接手機耳機"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"已連線至檔案傳輸伺服器"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"已連結地圖"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"已連接 SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"未連線至檔案傳輸伺服器"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"已連線至輸入裝置"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"已連線至裝置並取得互聯網連線"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"與裝置分享本地互聯網連線"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"用於上網"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"用於地圖"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"用來存取 SIM 卡"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"用於媒體音效"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"用於手機音效"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"用於傳輸檔案"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"用於輸入"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"配對"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"配對"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"取消"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"一旦配對成功,即可存取您的通訊錄和通話記錄。"</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"無法與 <xliff:g id="DEVICE_NAME">%1$s</xliff:g> 配對。"</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"無法與 <xliff:g id="DEVICE_NAME">%1$s</xliff:g> 配對,因為 PIN 碼或密鑰不正確。"</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"無法與 <xliff:g id="DEVICE_NAME">%1$s</xliff:g> 通訊。"</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」拒絕配對要求。"</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi 已關閉。"</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi 連線已中斷。"</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi 訊號一格。"</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wi-Fi 訊號兩格。"</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wi-Fi 訊號三格。"</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Wi-Fi 訊號滿格。"</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android 作業系統"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"已移除的應用程式"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"已移除的應用程式和使用者"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB 網絡共享"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"可攜式熱點"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"藍牙網絡共享"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"網絡共享"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"網絡共享和可攜式熱點"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"公司檔案"</string>
+ <string name="user_guest" msgid="8475274842845401871">"訪客"</string>
+ <string name="unknown" msgid="1592123443519355854">"未知"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"使用者:<xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"已設定部分預設值"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"未設定預設值"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"文字轉語音設定"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"文字轉語音輸出"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"語音速率"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"文字轉語音的播放速度"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"語言"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"使用系統語言"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"未選取語言"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"設定朗讀文字所使用的語音語言"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"聆聽例子"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"播放簡短的語音合成例子"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"安裝語音資料"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"安裝語音合成所需的語音資料"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"這個語音合成引擎可能會收集您輸入的所有語音,包括密碼和信用卡號等個人資料。這個引擎來自「<xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>」引擎,是否要使用這個語音合成引擎?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"您需要操作正常的網絡連線,才能使用文字轉語音功能輸出這種語言。"</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"這是語音合成例子"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"預設語言狀態"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"全面支援<xliff:g id="LOCALE">%1$s</xliff:g>"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g>需要網絡連線"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"不支援<xliff:g id="LOCALE">%1$s</xliff:g>"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"正在檢查…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g> 設定"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"啟動引擎設定"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"首選引擎"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"一般設定"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"非常慢"</item>
+ <item msgid="4795095314303559268">"慢"</item>
+ <item msgid="8903157781070679765">"正常"</item>
+ <item msgid="164347302621392996">"快"</item>
+ <item msgid="5794028588101562009">"較快"</item>
+ <item msgid="7163942783888652942">"很快"</item>
+ <item msgid="7831712693748700507">"急速"</item>
+ <item msgid="5194774745031751806">"極快"</item>
+ <item msgid="9085102246155045744">"最快"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"選擇設定檔"</string>
+ <string name="category_personal" msgid="1299663247844969448">"個人"</string>
+ <string name="category_work" msgid="8699184680584175622">"公司"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-zh-rTW/arrays.xml b/packages/SettingsLib/res/values-zh-rTW/arrays.xml
new file mode 100644
index 0000000..ac1a5ad
--- /dev/null
+++ b/packages/SettingsLib/res/values-zh-rTW/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"掃描中…"</item>
+ <item msgid="8513729475867537913">"連線中…"</item>
+ <item msgid="515055375277271756">"驗證中…"</item>
+ <item msgid="1943354004029184381">"正在取得 IP 位址…"</item>
+ <item msgid="4221763391123233270">"已連線"</item>
+ <item msgid="624838831631122137">"已暫停"</item>
+ <item msgid="7979680559596111948">"中斷連線中…"</item>
+ <item msgid="1634960474403853625">"已中斷連線"</item>
+ <item msgid="746097431216080650">"失敗"</item>
+ <item msgid="6367044185730295334">"已封鎖"</item>
+ <item msgid="503942654197908005">"目前正暫時避開品質不佳的連線"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"掃描中…"</item>
+ <item msgid="355508996603873860">"正在連線到 <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="554971459996405634">"正在取得 <xliff:g id="NETWORK_NAME">%1$s</xliff:g> 的授權…"</item>
+ <item msgid="7928343808033020343">"正在由 <xliff:g id="NETWORK_NAME">%1$s</xliff:g> 取得 IP 位址…"</item>
+ <item msgid="8937994881315223448">"已連線到 <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"已暫停"</item>
+ <item msgid="7698638434317271902">"正在中斷 <xliff:g id="NETWORK_NAME">%1$s</xliff:g> 連線…"</item>
+ <item msgid="197508606402264311">"已中斷連線"</item>
+ <item msgid="8578370891960825148">"失敗"</item>
+ <item msgid="5660739516542454527">"已封鎖"</item>
+ <item msgid="1805837518286731242">"目前正暫時避開品質不佳的連線"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..8a00d8c
--- /dev/null
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"無法掃描網路"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"無"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"已儲存"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"已停用"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP 設定失敗"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi 連線失敗"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"驗證問題"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"不在有效範圍內"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"未偵測到可用的網際網路連線,系統無法為您自動重新連線。"</string>
+ <string name="saved_network" msgid="4352716707126620811">"由<xliff:g id="NAME">%1$s</xliff:g>儲存"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"已透過 Wi‑Fi 小幫手連線"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"已透過 %1$s 連線"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"可透過 %1$s 使用"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"已連線,沒有網際網路"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"已中斷連線"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"正在中斷連線…"</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"連線中…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"已連線"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"配對中…"</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"已連線 (無手機音訊)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"已連線 (無媒體音訊)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"已連線 (無訊息存取權)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"已連線 (無手機或媒體音訊)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"媒體音訊"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"手機音訊"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"檔案傳輸"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"輸入裝置"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"網際網路連線"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"聯絡人共用"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"用於聯絡人共用"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"網際網路連線分享"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"訊息存取權"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM 卡存取權"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"連接至媒體音訊"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"連接至電話音訊"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"已連線到檔案傳輸伺服器"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"已連線至地圖"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"已連線到 SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"不要連線到檔案傳輸伺服器"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"已連線到輸入裝置"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"已連線至裝置並取得網際網路存取權"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"與裝置分享本地網際網路連線"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"用於網際網路連線"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"地圖使用偏好"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"用於 SIM 卡存取權"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"用於媒體音訊"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"用於電話音訊"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"用於傳輸檔案"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"用於輸入"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"配對"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"配對"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"取消"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"配對完成後,所配對的裝置即可在連線後存取您的聯絡人和通話紀錄。"</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"無法與 <xliff:g id="DEVICE_NAME">%1$s</xliff:g> 配對。"</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"無法與 <xliff:g id="DEVICE_NAME">%1$s</xliff:g> 配對,因為 PIN 或密碼金鑰不正確。"</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"無法與 <xliff:g id="DEVICE_NAME">%1$s</xliff:g> 通訊。"</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」拒絕配對要求。"</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"已關閉 Wi-Fi。"</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi 連線已中斷。"</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi 訊號強度一格。"</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wi-Fi 訊號強度兩格。"</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wi-Fi 訊號強度三格。"</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Wi-Fi 訊號強度滿格。"</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Android 作業系統"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"移除的應用程式"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"已移除的應用程式和使用者"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB 網路共用"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"可攜式無線基地台"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"藍牙網路共用"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"網路共用"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"網路共用與可攜式無線基地台"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"工作設定檔"</string>
+ <string name="user_guest" msgid="8475274842845401871">"訪客"</string>
+ <string name="unknown" msgid="1592123443519355854">"不明"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"使用者:<xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"已設定部分預設值"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"未設定預設值"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"文字轉語音設定"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"文字轉語音輸出"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"語音速率"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"文字轉語音的播放速度"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"語言"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"使用系統設定"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"未選取語言"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"設定文字轉語音功能要用的語言"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"聆聽範例"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"播放簡短的語音合成範例"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"安裝語音資料"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"安裝語音合成所需的語音資料"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"此語音合成引擎可能會收集您輸入的所有語音,包括密碼和信用卡號等個人資料。此引擎來自「<xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>」引擎,是否要使用此語音合成引擎?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"您必須連上網路才能使用文字轉語音輸出功能。"</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"您目前聽到的是我們的語音合成範例"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"預設語言狀態"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"完整支援<xliff:g id="LOCALE">%1$s</xliff:g>"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"需要網路連線才支援<xliff:g id="LOCALE">%1$s</xliff:g>"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"不支援<xliff:g id="LOCALE">%1$s</xliff:g>"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"檢查中…"</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g> 設定"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"啟動引擎設定"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"偏好的引擎"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"一般"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"很慢"</item>
+ <item msgid="4795095314303559268">"慢"</item>
+ <item msgid="8903157781070679765">"一般"</item>
+ <item msgid="164347302621392996">"快"</item>
+ <item msgid="5794028588101562009">"較快"</item>
+ <item msgid="7163942783888652942">"很快"</item>
+ <item msgid="7831712693748700507">"非常快"</item>
+ <item msgid="5194774745031751806">"極快"</item>
+ <item msgid="9085102246155045744">"最快"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"選擇設定檔"</string>
+ <string name="category_personal" msgid="1299663247844969448">"個人"</string>
+ <string name="category_work" msgid="8699184680584175622">"公司"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-zu/arrays.xml b/packages/SettingsLib/res/values-zu/arrays.xml
new file mode 100644
index 0000000..3404563
--- /dev/null
+++ b/packages/SettingsLib/res/values-zu/arrays.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wifi_status">
+ <item msgid="1922181315419294640"></item>
+ <item msgid="8934131797783724664">"Iyathwebula..."</item>
+ <item msgid="8513729475867537913">"Iyaxhuma kwi-inthanethi..."</item>
+ <item msgid="515055375277271756">"Iyaqinisekisa..."</item>
+ <item msgid="1943354004029184381">"Ithola ikheli le-IP..."</item>
+ <item msgid="4221763391123233270">"Ixhunyiwe kwi-inthanethi"</item>
+ <item msgid="624838831631122137">"Kumiswe okwesikhashana"</item>
+ <item msgid="7979680559596111948">"Inqamula uxhumano kwi-inthanethi..."</item>
+ <item msgid="1634960474403853625">"Ayixhunyiwe"</item>
+ <item msgid="746097431216080650">"Akuphumelelanga"</item>
+ <item msgid="6367044185730295334">"Uvinjelwe"</item>
+ <item msgid="503942654197908005">"Okwesikhashana ivikela ukuxhumana okungaqinile"</item>
+ </string-array>
+ <string-array name="wifi_status_with_ssid">
+ <item msgid="7714855332363650812"></item>
+ <item msgid="8878186979715711006">"Iyathwebula..."</item>
+ <item msgid="355508996603873860">"Ixhuma ku-<xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="554971459996405634">"Iqinisekisa nge <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="7928343808033020343">"Ithola ikheli le-IP kusuka <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="8937994881315223448">"Ixhumeke ku-<xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Kumiswe okwesikhashana"</item>
+ <item msgid="7698638434317271902">"Inqamula kusuka <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="197508606402264311">"Ayixhunyiwe kwi-inthanethi"</item>
+ <item msgid="8578370891960825148">"Akuphumelelanga"</item>
+ <item msgid="5660739516542454527">"Uvinjelwe"</item>
+ <item msgid="1805837518286731242">"Okwesikhashana ivikela ukuxhumana okungaqinile"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
new file mode 100644
index 0000000..4575ac1
--- /dev/null
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Ayikwazi ukuhlola amanethiwekhi"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Lutho"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Kulondoloziwe"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Akusebenzi"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Ukwehluleka kokulungiswa kwe-IP"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Ukwehlulekla koxhumo le-WiFi"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Inkinga yokufakazela ubuqiniso"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Ayikho ebubanzini"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Ukufinyeela okungekhona kwe-inthanethi kutholakele, ngeke kuxhumeke ngokuzenzakalelayo."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Kulondolozwe ngu-<xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Ixhunywe ngomsizi we-Wi-FI"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Kuxhumeke nge-%1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Iyatholakala nge-%1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Kuxhumekile, ayikho i-inthanethi"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Ayixhunyiwe"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Inqamula uxhumano kwi-inthanethi..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Iyaxhuma..."</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Ixhunyiwe"</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Iyabhangqa..."</string>
+ <string name="bluetooth_connected_no_headset" msgid="2866994875046035609">"Ixhunyiwe (ayikho ifoni)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="4576188601581440337">"Ixhunyiwe (ayikho imidiya)"</string>
+ <string name="bluetooth_connected_no_map" msgid="6504436917057479986">"Kuxhunyiwe (akukho ukufinyelela umlayezo)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="9195757766755553810">"Ixhunyiwe (ayikho ifoni noma imidiya)"</string>
+ <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Umsindo wemidiya"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Umsindo wefoni"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Dlulisa ifayela"</string>
+ <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Idivaysi yokufakwayo"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Ukufinyelela i-Inthanethi"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Ukwabelana kokuxhumana"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Sebenzisela ukwabelana kokuxhumana"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Ukwabelana ngoxhumano lwe-Inthanethi"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Ukufinyelela umlayezo"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Ukufinyelela kwe-SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Ixhume emsindweni wemidiya"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Ixhunywe kumsindo wefoni"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Ixhunywe kwiseva yokudlulisa ifayela"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Ixhumeke kumephu"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Kuxhumeke ku-SAP"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Ayixhunyiwe kwiseva sokudlulisa ifayela"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Ixhunywe kwidivaysi yokufakwayo"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Ixhunyiwe kwidivaysi yokufinyelela kwi-Inthanethi"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Yabelana noxhumano lwe-Inthanethi kanye nedivaysi"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Sebenzisa ukufinyelela i-Inthanethi"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Sebenzisela imephu"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Ukusebenzisa kokufinyelela kwe-SIM"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Sebenzisela umsindo wemidiya"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Sebenziselwa umsindo wefoni"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Sebenziselwa ukudlulisa ifayela"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Isetshenziselwa okufakwayo"</string>
+ <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Bhangqa"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"BHANQA"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Khansela"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Ukubhanqa kunika ukufinyelela koxhumana nabo nomlando wekholi uma uxhumekile."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Ayikwazanga ukuhlangana ne <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Ayikwazanga ukumataniswa ne <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ngenxa ye-PIN noma isihluthulelo sokungena okungafanele."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Ayaikwazi ukuxhumana ne- <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Ukubhangqa kunqatshelwe i-<xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"I-Wifi ivaliwe."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"I-Wifi inqanyuliwe."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Ibha elilodwa le-Wifi."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Amabha amabili we-Wifi."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Amabha amathathu we-Wifi."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Isiginali ye-Wifi igcwele."</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"I-Android OS"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Izinhlelo zokusebenza zisusiwe"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Izinhelo zokusebenza nabasebenzisi abasusiwe"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"Imodemu nge-USB"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"I-hotspot ephathekayo"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Imodemu nge-Bluetooth"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Ukusebenzisa njengemodemu"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Ukusebenzisa njengemodemu & i-hotspot ephathekayo"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Iphrofayela yomsebenzi"</string>
+ <string name="user_guest" msgid="8475274842845401871">"Isivakashi"</string>
+ <string name="unknown" msgid="1592123443519355854">"Akwaziwa"</string>
+ <string name="running_process_item_user_label" msgid="3129887865552025943">"Umsebenzisi: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Okunye okuzenzakalelayo okusethiwe"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Akukho okuzenzakalelayo okusethiwe"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Izilungiselelo zombhalo-kuya-kunkulumo"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Umbhalo-uya-kokukhishwa ngokukhuluma"</string>
+ <string name="tts_default_rate_title" msgid="6030550998379310088">"Ukukala izwi"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Isivinini leso umbhalo okhulunywe ngaso"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Ulimi"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Sebenzisa ulimi lwesistimu"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Ulimi alukhethwanga"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Ihlela izwi lolimi oluthize lombhalo okhulunyiwe"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Lalela isibonelo"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Dlala umboniso omfushane wamazwi"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Faka idatha yezwi"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Faka idatha yezwi edingekayo yamazwi ngamanye"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Lenjini yokuqaphela amagama ingase ikwazi ukuqoqa wonke umbhalo ozokhulunywa, kuhlanganise idatha yomuntu siqu njengamaphasiwedi nezinombolo zekhadi lesikoloto. Iphuma <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> kwinjini. Vumela ukusebenzisa lenjini yokuqaphela amagama?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Lolu limi ludinga ukuxhumana okusebenzayo kwenethiwekhi ekukhishweni kokuthunyelwe njengombhalo kunkulumo."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Lesi isibonelo sezwi"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Isimo solimi oluzenzakalelayo"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"I-<xliff:g id="LOCALE">%1$s</xliff:g> isekelwe ngokugcwele"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"I-<xliff:g id="LOCALE">%1$s</xliff:g> idinga ukuxhumeka kwenethiwekhi"</string>
+ <string name="tts_status_not_supported" msgid="4491154212762472495">"I-<xliff:g id="LOCALE">%1$s</xliff:g> ayisekelwe"</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"Iyahlola..."</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Izilungiselelo ze-<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Faka izilungiselelo zenjini"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Injini eyintandokazi"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Okuvamile"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Phansi kakhulu"</item>
+ <item msgid="4795095314303559268">"Phansi"</item>
+ <item msgid="8903157781070679765">"Okujwayelekile"</item>
+ <item msgid="164347302621392996">"Sheshayo"</item>
+ <item msgid="5794028588101562009">"Ngokushesha"</item>
+ <item msgid="7163942783888652942">"Kushesha kakhulu"</item>
+ <item msgid="7831712693748700507">"Esheshisayo"</item>
+ <item msgid="5194774745031751806">"Esheshisa kakhulu"</item>
+ <item msgid="9085102246155045744">"Esheshisa kakhulukhulu"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Khetha iphrofayela"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Okomuntu siqu"</string>
+ <string name="category_work" msgid="8699184680584175622">"Umsebenzi"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index 3ad8f21..d7c78f6 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -23,4 +23,12 @@
<dimen name="disappear_y_translation">-32dp</dimen>
<dimen name="circle_avatar_size">40dp</dimen>
+
+ <!-- Height of a user icon view -->
+ <dimen name="user_icon_view_height">56dp</dimen>
+ <!-- User spinner -->
+ <dimen name="user_spinner_height">72dp</dimen>
+ <dimen name="user_spinner_padding">4dp</dimen>
+ <dimen name="user_spinner_padding_sides">20dp</dimen>
+ <dimen name="user_spinner_item_height">56dp</dimen>
</resources>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 9b1f103..9e48849 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -225,4 +225,127 @@
<!-- [CHAR LIMIT=NONE] Label of a running process that represents another user -->
<string name="running_process_item_user_label">User: <xliff:g id="user_name">%1$s</xliff:g></string>
+
+ <!-- Launch defaults preference summary with some set [CHAR LIMIT=40] -->
+ <string name="launch_defaults_some">Some defaults set</string>
+ <!-- Launch defaults preference summary with none set [CHAR LIMIT=40] -->
+ <string name="launch_defaults_none">No defaults set</string>
+
+ <!-- Text-To-Speech (TTS) settings --><skip />
+ <!-- Name of the TTS package as listed by the package manager. -->
+ <string name="tts_settings">Text-to-speech settings</string>
+ <!-- TTS option item name in the main settings screen -->
+ <string name="tts_settings_title">Text-to-speech output</string>
+ <!-- On main TTS Settings screen, in default settings section, setting default speech rate for synthesized voice -->
+ <string name="tts_default_rate_title">Speech rate</string>
+ <!-- On main TTS Settings screen, summary for default speech rate for synthesized voice -->
+ <string name="tts_default_rate_summary">Speed at which the text is spoken</string>
+ <!-- On main TTS Settings screen, in default settings section, setting default language for synthesized voice -->
+ <string name="tts_default_lang_title">Language</string>
+ <!-- Entry in the TTS engine language/locale picker, when selected will try to default to the system language [CHAR LIMIT=50] -->
+ <string name="tts_lang_use_system">Use system language</string>
+ <!-- On main TTS Settings screen, language summary if it can't default to system language [CHAR LIMIT=50] -->
+ <string name="tts_lang_not_selected">Language not selected</string>
+ <!-- On main TTS Settings screen, summary for default language for synthesized voice -->
+ <string name="tts_default_lang_summary">Sets the language-specific voice for the spoken text</string>
+ <!-- On main TTS Settings screen, triggers playback of an example of speech synthesis -->
+ <string name="tts_play_example_title">Listen to an example</string>
+ <!-- On main TTS Settings screen, summary for triggering playback of an example of speech synthesis -->
+ <string name="tts_play_example_summary">Play a short demonstration of speech synthesis</string>
+ <!-- On main TTS Settings screen, click to install required speech synthesis data -->
+ <string name="tts_install_data_title">Install voice data</string>
+ <!-- On main TTS Settings screen, summary for click to install required speech synthesis data -->
+ <string name="tts_install_data_summary">Install the voice data required for speech synthesis</string>
+ <!-- Warning message about security implications of enabling a TTS engine, displayed as a dialog
+ message when the user selects to enable an engine. -->
+ <string name="tts_engine_security_warning">This speech synthesis engine may be able to collect
+ all the text that will be spoken, including personal data like passwords and credit
+ card numbers. It comes from the <xliff:g id="tts_plugin_engine_name">%s</xliff:g> engine.
+ Enable the use of this speech synthesis engine?</string>
+ <!-- Warning message about required internet conectivity for TTS synthesis, displayed as a dialog
+ message when the user selects to play an example for network only locale and there's no internet connectivity. -->
+ <string name="tts_engine_network_required">This language requires a working network connection for text-to-speech output.</string>
+ <!-- Text spoken by the TTS engine as an example if the engine doesn't provide sample text [CHAR LIMIT=100] -->
+ <string name="tts_default_sample_string">This is an example of speech synthesis</string>
+ <!-- On main TTS Settings screen, title of a field explaining current TTS engine status for
+ current default language [CHAR LIMIT=50] -->
+ <string name="tts_status_title">Default language status</string>
+ <!-- On main TTS Settings screen, current TTS engine status for the current default language,
+ selected language is fully supported by the engine [CHAR LIMIT=150]-->
+ <string name="tts_status_ok"><xliff:g id="locale" example="English (United States)">%1$s</xliff:g> is fully supported</string>
+ <!-- On main TTS Settings screen, current TTS engine status for the current default language,
+ selected language is supported by the engine only if there's a working network connection [CHAR LIMIT=150] -->
+ <string name="tts_status_requires_network"><xliff:g id="locale" example="English (United States)">%1$s</xliff:g> requires network connection</string>
+ <!-- On main TTS Settings screen, current TTS engine status for the current default language,
+ selected language is not supported by the engine [CHAR LIMIT=150] -->
+ <string name="tts_status_not_supported"><xliff:g id="locale" example="English (United States)">%1$s</xliff:g> is not supported</string>
+ <!-- On main TTS Settings screen, current TTS engine status for the current default language,
+ tts engine is queried for status [CHAR LIMIT=150] -->
+ <string name="tts_status_checking">Checking…</string>
+ <!-- Title for a preference in the main TTS settings screen, which
+ launches the settings screen for a given TTS engine when clicked
+ [CHAR LIMIT=30]-->
+ <string name="tts_engine_settings_title">Settings for <xliff:g id="tts_engine_name">%s</xliff:g></string>
+ <!-- [CHAR LIMIT=150] Text for screen readers / accessibility programs for
+ the image that launches the TTS engine settings when clicked. -->
+ <string name="tts_engine_settings_button">Launch engine settings</string>
+ <!-- [CHAR LIMIT=50] The text for the settings section that users to set a
+ preferred text to speech engine -->
+ <string name="tts_engine_preference_section_title">Preferred engine</string>
+ <!-- [CHAR LIMIT=50] Title of the settings section that displays general preferences
+ that are applicable to all engines, such as the speech rate -->
+ <string name="tts_general_section_title">General</string>
+
+ <!-- Default speech rate choices -->
+ <string-array name="tts_rate_entries">
+ <item>Very slow</item>
+ <item>Slow</item>
+ <item>Normal</item>
+ <item>Fast</item>
+ <item>Faster</item>
+ <item>Very fast</item>
+ <item>Rapid</item>
+ <item>Very rapid</item>
+ <item>Fastest</item>
+ </string-array>
+ <!-- Do not translate. -->
+ <string-array name="tts_rate_values">
+ <item>60</item>
+ <item>80</item>
+ <item>100</item>
+ <item>150</item>
+ <item>200</item>
+ <item>250</item>
+ <item>300</item>
+ <item>350</item>
+ <item>400</item>
+ </string-array>
+
+ <!-- Do not translate. -->
+ <string-array name="tts_demo_strings" translatable="false">
+ <item>This is an example of speech synthesis in English.</item>
+ <item>Voici un échantillon de synthèse vocale en français.</item>
+ <item>Dies ist ein Beispiel für Sprachsynthese in Deutsch.</item>
+ <item>Questo è un esempio di sintesi vocale in italiano.</item>
+ <item>Este es un ejemplo de síntesis de voz en español.</item>
+ <item>이것은 한국어 음성 합성의 예입니다.</item>
+ </string-array>
+ <!-- Do not translate. -->
+ <string-array name="tts_demo_string_langs" translatable="false">
+ <item>eng</item>
+ <item>fra</item>
+ <item>deu</item>
+ <item>ita</item>
+ <item>spa</item>
+ <item>kor</item>
+ </string-array>
+
+ <!-- Title for profile selection dialog [CHAR LIMIT=30] -->
+ <string name="choose_profile">Choose Profile</string>
+
+ <!-- Header for items under the personal user [CHAR LIMIT=30] -->
+ <string name="category_personal">Personal</string>
+ <!-- Header for items under the work user [CHAR LIMIT=30] -->
+ <string name="category_work">Work</string>
+
</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
new file mode 100644
index 0000000..99bd4ad
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
@@ -0,0 +1,154 @@
+/*
+ * 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.accessibility;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.view.accessibility.AccessibilityManager;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+
+public class AccessibilityUtils {
+ public static final char ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR = ':';
+
+ final static TextUtils.SimpleStringSplitter sStringColonSplitter =
+ new TextUtils.SimpleStringSplitter(ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR);
+
+ /**
+ * @return the set of enabled accessibility services. If there are not services
+ * it returned the unmodifiable {@link Collections#emptySet()}.
+ */
+ public static Set<ComponentName> getEnabledServicesFromSettings(Context context) {
+ final String enabledServicesSetting = Settings.Secure.getString(
+ context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
+ if (enabledServicesSetting == null) {
+ return Collections.emptySet();
+ }
+
+ final Set<ComponentName> enabledServices = new HashSet<>();
+ final TextUtils.SimpleStringSplitter colonSplitter = sStringColonSplitter;
+ colonSplitter.setString(enabledServicesSetting);
+
+ while (colonSplitter.hasNext()) {
+ final String componentNameString = colonSplitter.next();
+ final ComponentName enabledService = ComponentName.unflattenFromString(
+ componentNameString);
+ if (enabledService != null) {
+ enabledServices.add(enabledService);
+ }
+ }
+
+ return enabledServices;
+ }
+
+ /**
+ * @return a localized version of the text resource specified by resId
+ */
+ public static CharSequence getTextForLocale(Context context, Locale locale, int resId) {
+ final Resources res = context.getResources();
+ final Configuration config = new Configuration(res.getConfiguration());
+ config.setLocale(locale);
+ final Context langContext = context.createConfigurationContext(config);
+ return langContext.getText(resId);
+ }
+
+ public static void setAccessibilityServiceState(Context context, ComponentName toggledService,
+ boolean enabled) {
+ // Parse the enabled services.
+ Set<ComponentName> enabledServices = AccessibilityUtils.getEnabledServicesFromSettings(
+ context);
+
+ if (enabledServices.isEmpty()) {
+ enabledServices = new ArraySet<>(1);
+ }
+
+ // Determine enabled services and accessibility state.
+ boolean accessibilityEnabled = false;
+ if (enabled) {
+ enabledServices.add(toggledService);
+ // Enabling at least one service enables accessibility.
+ accessibilityEnabled = true;
+ } else {
+ enabledServices.remove(toggledService);
+ // Check how many enabled and installed services are present.
+ Set<ComponentName> installedServices = getInstalledServices(context);
+ for (ComponentName enabledService : enabledServices) {
+ if (installedServices.contains(enabledService)) {
+ // Disabling the last service disables accessibility.
+ accessibilityEnabled = true;
+ break;
+ }
+ }
+ }
+
+ // Update the enabled services setting.
+ StringBuilder enabledServicesBuilder = new StringBuilder();
+ // Keep the enabled services even if they are not installed since we
+ // have no way to know whether the application restore process has
+ // completed. In general the system should be responsible for the
+ // clean up not settings.
+ for (ComponentName enabledService : enabledServices) {
+ enabledServicesBuilder.append(enabledService.flattenToString());
+ enabledServicesBuilder.append(
+ AccessibilityUtils.ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR);
+ }
+ final int enabledServicesBuilderLength = enabledServicesBuilder.length();
+ if (enabledServicesBuilderLength > 0) {
+ enabledServicesBuilder.deleteCharAt(enabledServicesBuilderLength - 1);
+ }
+ Settings.Secure.putString(context.getContentResolver(),
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+ enabledServicesBuilder.toString());
+
+ // Update accessibility enabled.
+ Settings.Secure.putInt(context.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_ENABLED, accessibilityEnabled ? 1 : 0);
+ }
+
+ private static Set<ComponentName> getInstalledServices(Context context) {
+ final Set<ComponentName> installedServices = new HashSet<>();
+ installedServices.clear();
+
+ final List<AccessibilityServiceInfo> installedServiceInfos =
+ AccessibilityManager.getInstance(context)
+ .getInstalledAccessibilityServiceList();
+ if (installedServiceInfos == null) {
+ return installedServices;
+ }
+
+ for (final AccessibilityServiceInfo info : installedServiceInfos) {
+ final ResolveInfo resolveInfo = info.getResolveInfo();
+ final ComponentName installedService = new ComponentName(
+ resolveInfo.serviceInfo.packageName,
+ resolveInfo.serviceInfo.name);
+ installedServices.add(installedService);
+ }
+ return installedServices;
+ }
+
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
new file mode 100644
index 0000000..344bf62
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
@@ -0,0 +1,71 @@
+/*
+ * 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.applications;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.hardware.usb.IUsbManager;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.android.settingslib.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AppUtils {
+ private static final String TAG = "AppUtils";
+
+ public static CharSequence getLaunchByDefaultSummary(ApplicationsState.AppEntry appEntry,
+ IUsbManager usbManager, PackageManager pm, Context context) {
+ String packageName = appEntry.info.packageName;
+ boolean hasPreferred = hasPreferredActivities(pm, packageName)
+ || hasUsbDefaults(usbManager, packageName);
+ int status = pm.getIntentVerificationStatus(packageName, UserHandle.myUserId());
+ // consider a visible current link-handling state to be any explicitly designated behavior
+ boolean hasDomainURLsPreference =
+ status != PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+ return context.getString(hasPreferred || hasDomainURLsPreference
+ ? R.string.launch_defaults_some
+ : R.string.launch_defaults_none);
+ }
+
+ public static boolean hasUsbDefaults(IUsbManager usbManager, String packageName) {
+ try {
+ if (usbManager != null) {
+ return usbManager.hasDefaults(packageName, UserHandle.myUserId());
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "mUsbManager.hasDefaults", e);
+ }
+ return false;
+ }
+
+ public static boolean hasPreferredActivities(PackageManager pm, String packageName) {
+ // Get list of preferred activities
+ List<ComponentName> prefActList = new ArrayList<>();
+ // Intent list cannot be null. so pass empty list
+ List<IntentFilter> intentList = new ArrayList<>();
+ pm.getPreferredActivities(intentList, prefActList, packageName);
+ Log.d(TAG, "Have " + prefActList.size() + " number of activities in preferred list");
+ return prefActList.size() > 0;
+ }
+
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
index 0380e21..f935f31 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
@@ -19,6 +19,7 @@
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
+import android.bluetooth.le.BluetoothLeScanner;
import android.content.Context;
import android.os.ParcelUuid;
import android.util.Log;
@@ -106,6 +107,10 @@
return mAdapter.getScanMode();
}
+ public BluetoothLeScanner getBluetoothLeScanner() {
+ return mAdapter.getBluetoothLeScanner();
+ }
+
public int getState() {
return mAdapter.getState();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java b/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
new file mode 100644
index 0000000..0f322cf
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
@@ -0,0 +1,123 @@
+/**
+ * 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.drawer;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DashboardCategory implements Parcelable {
+
+ /**
+ * Title of the category that is shown to the user.
+ */
+ public CharSequence title;
+
+ /**
+ * Key used for placing external tiles.
+ */
+ public String key;
+
+ /**
+ * Used to control display order.
+ */
+ public int priority;
+
+ /**
+ * List of the category's children
+ */
+ public List<DashboardTile> tiles = new ArrayList<DashboardTile>();
+
+
+ public DashboardCategory() {
+ // Empty
+ }
+
+ public void addTile(DashboardTile tile) {
+ tiles.add(tile);
+ }
+
+ public void addTile(int n, DashboardTile tile) {
+ tiles.add(n, tile);
+ }
+
+ public void removeTile(DashboardTile tile) {
+ tiles.remove(tile);
+ }
+
+ public void removeTile(int n) {
+ tiles.remove(n);
+ }
+
+ public int getTilesCount() {
+ return tiles.size();
+ }
+
+ public DashboardTile getTile(int n) {
+ return tiles.get(n);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ TextUtils.writeToParcel(title, dest, flags);
+ dest.writeString(key);
+ dest.writeInt(priority);
+
+ final int count = tiles.size();
+ dest.writeInt(count);
+
+ for (int n = 0; n < count; n++) {
+ DashboardTile tile = tiles.get(n);
+ tile.writeToParcel(dest, flags);
+ }
+ }
+
+ public void readFromParcel(Parcel in) {
+ title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ key = in.readString();
+ priority = in.readInt();
+
+ final int count = in.readInt();
+
+ for (int n = 0; n < count; n++) {
+ DashboardTile tile = DashboardTile.CREATOR.createFromParcel(in);
+ tiles.add(tile);
+ }
+ }
+
+ DashboardCategory(Parcel in) {
+ readFromParcel(in);
+ }
+
+ public static final Creator<DashboardCategory> CREATOR = new Creator<DashboardCategory>() {
+ public DashboardCategory createFromParcel(Parcel source) {
+ return new DashboardCategory(source);
+ }
+
+ public DashboardCategory[] newArray(int size) {
+ return new DashboardCategory[size];
+ }
+ };
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardTile.java b/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardTile.java
new file mode 100644
index 0000000..6fef134
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardTile.java
@@ -0,0 +1,142 @@
+/**
+ * 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.drawer;
+
+import android.content.Intent;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.UserHandle;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+
+/**
+ * Description of a single dashboard tile that the user can select.
+ */
+public class DashboardTile implements Parcelable {
+
+ /**
+ * Title of the tile that is shown to the user.
+ * @attr ref android.R.styleable#PreferenceHeader_title
+ */
+ public CharSequence title;
+
+ /**
+ * Optional summary describing what this tile controls.
+ * @attr ref android.R.styleable#PreferenceHeader_summary
+ */
+ public CharSequence summary;
+
+ /**
+ * Optional icon to show for this tile.
+ * @attr ref android.R.styleable#PreferenceHeader_icon
+ */
+ public Icon icon;
+
+ /**
+ * Intent to launch when the preference is selected.
+ */
+ public Intent intent;
+
+ /**
+ * Optional list of user handles which the intent should be launched on.
+ */
+ public ArrayList<UserHandle> userHandle = new ArrayList<>();
+
+ /**
+ * Optional additional data for use by subclasses of the activity
+ */
+ public Bundle extras;
+
+ /**
+ * Category in which the tile should be placed.
+ */
+ public String category;
+
+ /**
+ * Priority of the intent filter that created this tile, used for display ordering.
+ */
+ public int priority;
+
+ public DashboardTile() {
+ // Empty
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ TextUtils.writeToParcel(title, dest, flags);
+ TextUtils.writeToParcel(summary, dest, flags);
+ if (icon != null) {
+ dest.writeByte((byte) 1);
+ icon.writeToParcel(dest, flags);
+ } else {
+ dest.writeByte((byte) 0);
+ }
+ if (intent != null) {
+ dest.writeByte((byte) 1);
+ intent.writeToParcel(dest, flags);
+ } else {
+ dest.writeByte((byte) 0);
+ }
+ final int N = userHandle.size();
+ dest.writeInt(N);
+ for (int i = 0; i < N; i++) {
+ userHandle.get(i).writeToParcel(dest, flags);
+ }
+ dest.writeBundle(extras);
+ dest.writeString(category);
+ dest.writeInt(priority);
+ }
+
+ public void readFromParcel(Parcel in) {
+ title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ summary = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ if (in.readByte() != 0) {
+ icon = Icon.CREATOR.createFromParcel(in);
+ }
+ if (in.readByte() != 0) {
+ intent = Intent.CREATOR.createFromParcel(in);
+ }
+ final int N = in.readInt();
+ for (int i = 0; i < N; i++) {
+ userHandle.add(UserHandle.CREATOR.createFromParcel(in));
+ }
+ extras = in.readBundle();
+ category = in.readString();
+ priority = in.readInt();
+ }
+
+ DashboardTile(Parcel in) {
+ readFromParcel(in);
+ }
+
+ public static final Creator<DashboardTile> CREATOR = new Creator<DashboardTile>() {
+ public DashboardTile createFromParcel(Parcel source) {
+ return new DashboardTile(source);
+ }
+ public DashboardTile[] newArray(int size) {
+ return new DashboardTile[size];
+ }
+ };
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/ProfileSelectDialog.java b/packages/SettingsLib/src/com/android/settingslib/drawer/ProfileSelectDialog.java
new file mode 100644
index 0000000..793e877d
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/ProfileSelectDialog.java
@@ -0,0 +1,67 @@
+/*
+ * 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.drawer;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.FragmentManager;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+public class ProfileSelectDialog extends DialogFragment implements OnClickListener {
+
+ private static final String ARG_SELECTED_TILE = "selectedTile";
+
+ private DashboardTile mSelectedTile;
+
+ public static void show(FragmentManager manager, DashboardTile tile) {
+ ProfileSelectDialog dialog = new ProfileSelectDialog();
+ Bundle args = new Bundle();
+ args.putParcelable(ARG_SELECTED_TILE, tile);
+ dialog.setArguments(args);
+ dialog.show(manager, "select_profile");
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mSelectedTile = getArguments().getParcelable(ARG_SELECTED_TILE);
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ Context context = getActivity();
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ UserAdapter adapter = UserAdapter.createUserAdapter(UserManager.get(context), context,
+ mSelectedTile.userHandle);
+ builder.setTitle(com.android.settingslib.R.string.choose_profile)
+ .setAdapter(adapter, this);
+
+ return builder.create();
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ UserHandle user = mSelectedTile.userHandle.get(which);
+ getActivity().startActivityAsUser(mSelectedTile.intent, user);
+ ((SettingsDrawerActivity) getActivity()).onProfileTileOpen();
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
new file mode 100644
index 0000000..f5fc698
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
@@ -0,0 +1,159 @@
+/**
+ * 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.drawer;
+
+import android.annotation.LayoutRes;
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.v4.widget.DrawerLayout;
+import android.util.Pair;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.AdapterView;
+import android.widget.ListView;
+import android.widget.Toolbar;
+import com.android.settingslib.R;
+
+import java.util.HashMap;
+import java.util.List;
+
+public class SettingsDrawerActivity extends Activity {
+
+ private SettingsDrawerAdapter mDrawerAdapter;
+ // Hold on to a cache of tiles to avoid loading the info multiple times.
+ private final HashMap<Pair<String, String>, DashboardTile> mTileCache = new HashMap<>();
+ private List<DashboardCategory> mDashboardCategories;
+ private DrawerLayout mDrawerLayout;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ super.setContentView(R.layout.settings_with_drawer);
+ mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
+ if (mDrawerLayout == null) {
+ return;
+ }
+ Toolbar toolbar = (Toolbar) findViewById(R.id.action_bar);
+ setActionBar(toolbar);
+ mDrawerAdapter = new SettingsDrawerAdapter(this);
+ ListView listView = (ListView) findViewById(R.id.left_drawer);
+ listView.setAdapter(mDrawerAdapter);
+ listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ public void onItemClick(android.widget.AdapterView<?> parent, View view, int position,
+ long id) {
+ onTileClicked(mDrawerAdapter.getTile(position));
+ };
+ });
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (mDrawerLayout != null && item.getItemId() == android.R.id.home
+ && mDrawerAdapter.getCount() != 0) {
+ openDrawer();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ updateDrawer();
+ }
+
+ public void openDrawer() {
+ if (mDrawerLayout != null) {
+ mDrawerLayout.openDrawer(Gravity.START);
+ }
+ }
+
+ public void closeDrawer() {
+ if (mDrawerLayout != null) {
+ mDrawerLayout.closeDrawers();
+ }
+ }
+
+ @Override
+ public void setContentView(@LayoutRes int layoutResID) {
+ LayoutInflater.from(this).inflate(layoutResID,
+ (ViewGroup) findViewById(R.id.content_frame));
+ }
+
+ @Override
+ public void setContentView(View view) {
+ ((ViewGroup) findViewById(R.id.content_frame)).addView(view);
+ }
+
+ @Override
+ public void setContentView(View view, ViewGroup.LayoutParams params) {
+ ((ViewGroup) findViewById(R.id.content_frame)).addView(view, params);
+ }
+
+ public void updateDrawer() {
+ if (mDrawerLayout == null) {
+ return;
+ }
+ // TODO: Do this in the background with some loading.
+ mDrawerAdapter.updateCategories();
+ if (mDrawerAdapter.getCount() != 0) {
+ mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
+ getActionBar().setHomeAsUpIndicator(R.drawable.ic_menu);
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+ } else {
+ mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
+ }
+ }
+
+ public List<DashboardCategory> getDashboardCategories(boolean force) {
+ if (force) {
+ mDashboardCategories = TileUtils.getCategories(this, mTileCache);
+ }
+ return mDashboardCategories;
+ }
+
+ public boolean openTile(DashboardTile tile) {
+ closeDrawer();
+ int numUserHandles = tile.userHandle.size();
+ if (numUserHandles > 1) {
+ ProfileSelectDialog.show(getFragmentManager(), tile);
+ return false;
+ } else if (numUserHandles == 1) {
+ startActivityAsUser(tile.intent, tile.userHandle.get(0));
+ } else {
+ startActivity(tile.intent);
+ }
+ return true;
+ }
+
+ protected void onTileClicked(DashboardTile tile) {
+ if (openTile(tile)) {
+ finish();
+ }
+ }
+
+ public void onProfileTileOpen() {
+ finish();
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerAdapter.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerAdapter.java
new file mode 100644
index 0000000..fef716c
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerAdapter.java
@@ -0,0 +1,106 @@
+/*
+ * 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.drawer;
+
+import android.graphics.drawable.Icon;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.settingslib.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class SettingsDrawerAdapter extends BaseAdapter {
+
+ private final ArrayList<Item> mItems = new ArrayList<>();
+ private final SettingsDrawerActivity mActivity;
+
+ public SettingsDrawerAdapter(SettingsDrawerActivity activity) {
+ mActivity = activity;
+ }
+
+ void updateCategories() {
+ List<DashboardCategory> categories = mActivity.getDashboardCategories(true);
+ mItems.clear();
+ for (int i = 0; i < categories.size(); i++) {
+ Item category = new Item();
+ category.icon = null;
+ DashboardCategory dashboardCategory = categories.get(i);
+ category.label = dashboardCategory.title;
+ mItems.add(category);
+ for (int j = 0; j < dashboardCategory.tiles.size(); j++) {
+ Item tile = new Item();
+ DashboardTile dashboardTile = dashboardCategory.tiles.get(j);
+ tile.label = dashboardTile.title;
+ tile.icon = dashboardTile.icon;
+ tile.tile = dashboardTile;
+ mItems.add(tile);
+ }
+ }
+ notifyDataSetChanged();
+ }
+
+ public DashboardTile getTile(int position) {
+ return mItems.get(position).tile;
+ }
+
+ @Override
+ public int getCount() {
+ return mItems.size();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return mItems.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ return mItems.get(position).icon != null;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ Item item = mItems.get(position);
+ boolean isTile = item.icon != null;
+ if (convertView == null || (isTile != (convertView.getId() == R.id.tile_item))) {
+ convertView = LayoutInflater.from(mActivity).inflate(isTile ? R.layout.drawer_item
+ : R.layout.drawer_category,
+ parent, false);
+ }
+ if (isTile) {
+ ((ImageView) convertView.findViewById(android.R.id.icon)).setImageIcon(item.icon);
+ }
+ ((TextView) convertView.findViewById(android.R.id.title)).setText(item.label);
+ return convertView;
+ }
+
+ private static class Item {
+ public Icon icon;
+ public CharSequence label;
+ public DashboardTile tile;
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
new file mode 100644
index 0000000..18e8d31
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
@@ -0,0 +1,312 @@
+/*
+ * 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.drawer;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pair;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class TileUtils {
+
+ private static final boolean DEBUG = false;
+
+ private static final String LOG_TAG = "TileUtils";
+
+ /**
+ * Settings will search for system activities of this action and add them as a top level
+ * settings tile using the following parameters.
+ *
+ * <p>A category must be specified in the meta-data for the activity named
+ * {@link #EXTRA_CATEGORY_KEY}
+ *
+ * <p>The title may be defined by meta-data named {@link #META_DATA_PREFERENCE_TITLE}
+ * otherwise the label for the activity will be used.
+ *
+ * <p>The icon may be defined by meta-data named {@link #META_DATA_PREFERENCE_ICON}
+ * otherwise the icon for the activity will be used.
+ *
+ * <p>A summary my be defined by meta-data named {@link #META_DATA_PREFERENCE_SUMMARY}
+ */
+ private static final String EXTRA_SETTINGS_ACTION =
+ "com.android.settings.action.EXTRA_SETTINGS";
+
+ /**
+ * Same as #EXTRA_SETTINGS_ACTION but used for the platform Settings activities.
+ */
+ private static final String SETTINGS_ACTION =
+ "com.android.settings.action.SETTINGS";
+
+ private static final String OPERATOR_SETTINGS =
+ "com.android.settings.OPERATOR_APPLICATION_SETTING";
+
+ private static final String OPERATOR_DEFAULT_CATEGORY =
+ "com.android.settings.category.wireless";
+
+ private static final String MANUFACTURER_SETTINGS =
+ "com.android.settings.MANUFACTURER_APPLICATION_SETTING";
+
+ private static final String MANUFACTURER_DEFAULT_CATEGORY =
+ "com.android.settings.category.device";
+
+ /**
+ * The key used to get the category from metadata of activities of action
+ * {@link #EXTRA_SETTINGS_ACTION}
+ * The value must be one of:
+ * <li>com.android.settings.category.wireless</li>
+ * <li>com.android.settings.category.device</li>
+ * <li>com.android.settings.category.personal</li>
+ * <li>com.android.settings.category.system</li>
+ */
+ private static final String EXTRA_CATEGORY_KEY = "com.android.settings.category";
+
+ /**
+ * Name of the meta-data item that should be set in the AndroidManifest.xml
+ * to specify the icon that should be displayed for the preference.
+ */
+ public static final String META_DATA_PREFERENCE_ICON = "com.android.settings.icon";
+
+ /**
+ * Name of the meta-data item that should be set in the AndroidManifest.xml
+ * to specify the title that should be displayed for the preference.
+ */
+ public static final String META_DATA_PREFERENCE_TITLE = "com.android.settings.title";
+
+ /**
+ * Name of the meta-data item that should be set in the AndroidManifest.xml
+ * to specify the summary text that should be displayed for the preference.
+ */
+ public static final String META_DATA_PREFERENCE_SUMMARY = "com.android.settings.summary";
+
+ private static final String SETTING_PKG = "com.android.settings";
+
+ public static List<DashboardCategory> getCategories(Context context) {
+ return getCategories(context, new HashMap<Pair<String, String>, DashboardTile>());
+ }
+
+ public static List<DashboardCategory> getCategories(Context context,
+ HashMap<Pair<String, String>, DashboardTile> cache) {
+ ArrayList<DashboardTile> tiles = new ArrayList<>();
+ UserManager userManager = UserManager.get(context);
+ for (UserHandle user : userManager.getUserProfiles()) {
+ // TODO: Needs much optimization, too many PM queries going on here.
+ if (user.getIdentifier() == ActivityManager.getCurrentUser()) {
+ // Only add Settings for this user.
+ getTilesForAction(context, user, SETTINGS_ACTION, cache, null, tiles, true);
+ getTilesForAction(context, user, OPERATOR_SETTINGS, cache,
+ OPERATOR_DEFAULT_CATEGORY, tiles, false);
+ getTilesForAction(context, user, MANUFACTURER_SETTINGS, cache,
+ MANUFACTURER_DEFAULT_CATEGORY, tiles, false);
+ }
+ getTilesForAction(context, user, EXTRA_SETTINGS_ACTION, cache, null, tiles, false);
+ }
+ HashMap<String, DashboardCategory> categoryMap = new HashMap<>();
+ for (DashboardTile tile : tiles) {
+ DashboardCategory category = categoryMap.get(tile.category);
+ if (category == null) {
+ category = createCategory(context, tile.category);
+ if (category == null) {
+ Log.w(LOG_TAG, "Couldn't find category " + tile.category);
+ continue;
+ }
+ categoryMap.put(category.key, category);
+ }
+ category.addTile(tile);
+ }
+ ArrayList<DashboardCategory> categories = new ArrayList<>(categoryMap.values());
+ for (DashboardCategory category : categories) {
+ Collections.sort(category.tiles, TILE_COMPARATOR);
+ }
+ Collections.sort(categories, CATEGORY_COMPARATOR);
+ return categories;
+ }
+
+ private static DashboardCategory createCategory(Context context, String categoryKey) {
+ DashboardCategory category = new DashboardCategory();
+ category.key = categoryKey;
+ PackageManager pm = context.getPackageManager();
+ List<ResolveInfo> results = pm.queryIntentActivities(new Intent(categoryKey), 0);
+ if (results.size() == 0) {
+ return null;
+ }
+ for (ResolveInfo resolved : results) {
+ if (!resolved.system) {
+ // Do not allow any app to add to settings, only system ones.
+ continue;
+ }
+ category.title = resolved.activityInfo.loadLabel(pm);
+ category.priority = SETTING_PKG.equals(
+ resolved.activityInfo.applicationInfo.packageName) ? resolved.priority : 0;
+ if (DEBUG) Log.d(LOG_TAG, "Adding category " + category.title);
+ }
+
+ return category;
+ }
+
+ private static void getTilesForAction(Context context,
+ UserHandle user, String action, Map<Pair<String, String>, DashboardTile> addedCache,
+ String defaultCategory, ArrayList<DashboardTile> outTiles, boolean requireSettings) {
+ PackageManager pm = context.getPackageManager();
+ Intent intent = new Intent(action);
+ List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent,
+ PackageManager.GET_META_DATA, user.getIdentifier());
+ for (ResolveInfo resolved : results) {
+ if (requireSettings) {
+ if (!SETTING_PKG.equals(resolved.activityInfo.applicationInfo.packageName)) {
+ continue;
+ }
+ } else {
+ if (!resolved.system) {
+ // Do not allow any app to add to settings, only system ones.
+ continue;
+ }
+ }
+ ActivityInfo activityInfo = resolved.activityInfo;
+ Bundle metaData = activityInfo.metaData;
+ String categoryKey = defaultCategory;
+ if (((metaData == null) || !metaData.containsKey(EXTRA_CATEGORY_KEY))
+ && categoryKey == null) {
+ Log.w(LOG_TAG, "Found " + resolved.activityInfo.name + " for action "
+ + action + " missing metadata "
+ + (metaData == null ? "" : EXTRA_CATEGORY_KEY));
+ continue;
+ } else {
+ categoryKey = metaData.getString(EXTRA_CATEGORY_KEY);
+ }
+ Pair<String, String> key = new Pair<String, String>(activityInfo.packageName,
+ activityInfo.name);
+ DashboardTile tile = addedCache.get(key);
+ if (tile == null) {
+ tile = new DashboardTile();
+ tile.intent = new Intent().setClassName(
+ activityInfo.packageName, activityInfo.name);
+ tile.category = categoryKey;
+ tile.priority = requireSettings ? resolved.priority : 0;
+ updateTileData(context, tile);
+ if (DEBUG) Log.d(LOG_TAG, "Adding tile " + tile.title);
+
+ addedCache.put(key, tile);
+ }
+ if (!tile.userHandle.contains(user)) {
+ tile.userHandle.add(user);
+ }
+ if (!outTiles.contains(tile)) {
+ outTiles.add(tile);
+ }
+ }
+ }
+
+ private static DashboardCategory getCategory(List<DashboardCategory> target,
+ String categoryKey) {
+ for (DashboardCategory category : target) {
+ if (categoryKey.equals(category.key)) {
+ return category;
+ }
+ }
+ return null;
+ }
+
+ private static boolean updateTileData(Context context, DashboardTile tile) {
+ Intent intent = tile.intent;
+ if (intent != null) {
+ // Find the activity that is in the system image
+ PackageManager pm = context.getPackageManager();
+ List<ResolveInfo> list = tile.userHandle.size() != 0
+ ? pm.queryIntentActivitiesAsUser(intent, PackageManager.GET_META_DATA,
+ tile.userHandle.get(0).getIdentifier())
+ : pm.queryIntentActivities(intent, PackageManager.GET_META_DATA);
+ int listSize = list.size();
+ for (int i = 0; i < listSize; i++) {
+ ResolveInfo resolveInfo = list.get(i);
+ if (resolveInfo.activityInfo.applicationInfo.isSystemApp()) {
+ int icon = 0;
+ CharSequence title = null;
+ String summary = null;
+
+ // Get the activity's meta-data
+ try {
+ Resources res = pm.getResourcesForApplication(
+ resolveInfo.activityInfo.packageName);
+ Bundle metaData = resolveInfo.activityInfo.metaData;
+
+ if (res != null && metaData != null) {
+ if (metaData.containsKey(META_DATA_PREFERENCE_ICON)) {
+ icon = metaData.getInt(META_DATA_PREFERENCE_ICON);
+ }
+ if (metaData.containsKey(META_DATA_PREFERENCE_TITLE)) {
+ title = metaData.getString(META_DATA_PREFERENCE_TITLE);
+ }
+ if (metaData.containsKey(META_DATA_PREFERENCE_SUMMARY)) {
+ summary = metaData.getString(META_DATA_PREFERENCE_SUMMARY);
+ }
+ }
+ } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
+ if (DEBUG) Log.d(LOG_TAG, "Couldn't find info", e);
+ }
+
+ // Set the preference title to the activity's label if no
+ // meta-data is found
+ if (TextUtils.isEmpty(title)) {
+ title = resolveInfo.loadLabel(pm).toString();
+ }
+ if (icon == 0) {
+ icon = resolveInfo.activityInfo.icon;
+ }
+
+ // Set icon, title and summary for the preference
+ tile.icon = Icon.createWithResource(resolveInfo.activityInfo.packageName, icon);
+ tile.title = title;
+ tile.summary = summary;
+ // Replace the intent with this specific activity
+ tile.intent = new Intent().setClassName(resolveInfo.activityInfo.packageName,
+ resolveInfo.activityInfo.name);
+
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private static final Comparator<DashboardTile> TILE_COMPARATOR =
+ new Comparator<DashboardTile>() {
+ @Override
+ public int compare(DashboardTile lhs, DashboardTile rhs) {
+ return rhs.priority - lhs.priority;
+ }
+ };
+
+ private static final Comparator<DashboardCategory> CATEGORY_COMPARATOR =
+ new Comparator<DashboardCategory>() {
+ @Override
+ public int compare(DashboardCategory lhs, DashboardCategory rhs) {
+ return rhs.priority - lhs.priority;
+ }
+ };
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java b/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java
new file mode 100644
index 0000000..f9fa805f
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.drawer;
+
+import android.app.ActivityManager;
+
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.database.DataSetObserver;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.ListAdapter;
+import android.widget.SpinnerAdapter;
+import android.widget.TextView;
+import com.android.internal.util.UserIcons;
+import com.android.settingslib.drawable.CircleFramedDrawable;
+
+import com.android.settingslib.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Adapter for a spinner that shows a list of users.
+ */
+public class UserAdapter implements SpinnerAdapter, ListAdapter {
+ /** Holder for user details */
+ public static class UserDetails {
+ private final UserHandle mUserHandle;
+ private final String mName;
+ private final Drawable mIcon;
+
+ public UserDetails(UserHandle userHandle, UserManager um, Context context) {
+ mUserHandle = userHandle;
+ UserInfo userInfo = um.getUserInfo(mUserHandle.getIdentifier());
+ Drawable icon;
+ if (userInfo.isManagedProfile()) {
+ mName = context.getString(R.string.managed_user_title);
+ icon = context.getDrawable(
+ com.android.internal.R.drawable.ic_corp_icon);
+ } else {
+ mName = userInfo.name;
+ final int userId = userInfo.id;
+ if (um.getUserIcon(userId) != null) {
+ icon = new BitmapDrawable(context.getResources(), um.getUserIcon(userId));
+ } else {
+ icon = UserIcons.getDefaultUserIcon(userId, /* light= */ false);
+ }
+ }
+ this.mIcon = encircle(context, icon);
+ }
+
+ private static Drawable encircle(Context context, Drawable icon) {
+ return CircleFramedDrawable.getInstance(context, UserIcons.convertToBitmap(icon));
+ }
+ }
+ private ArrayList<UserDetails> data;
+ private final LayoutInflater mInflater;
+
+ public UserAdapter(Context context, ArrayList<UserDetails> users) {
+ if (users == null) {
+ throw new IllegalArgumentException("A list of user details must be provided");
+ }
+ this.data = users;
+ mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ }
+
+ public UserHandle getUserHandle(int position) {
+ if (position < 0 || position >= data.size()) {
+ return null;
+ }
+ return data.get(position).mUserHandle;
+ }
+
+ @Override
+ public View getDropDownView(int position, View convertView, ViewGroup parent) {
+ final View row = convertView != null ? convertView : createUser(parent);
+
+ UserDetails user = data.get(position);
+ ((ImageView) row.findViewById(android.R.id.icon)).setImageDrawable(user.mIcon);
+ ((TextView) row.findViewById(android.R.id.title)).setText(getTitle(user));
+ return row;
+ }
+
+ private int getTitle(UserDetails user) {
+ int userHandle = user.mUserHandle.getIdentifier();
+ if (userHandle == UserHandle.USER_CURRENT
+ || userHandle == ActivityManager.getCurrentUser()) {
+ return R.string.category_personal;
+ } else {
+ return R.string.category_work;
+ }
+ }
+
+ private View createUser(ViewGroup parent) {
+ return mInflater.inflate(R.layout.user_preference, parent, false);
+ }
+
+ @Override
+ public void registerDataSetObserver(DataSetObserver observer) {
+ // We don't support observers
+ }
+
+ @Override
+ public void unregisterDataSetObserver(DataSetObserver observer) {
+ // We don't support observers
+ }
+
+ @Override
+ public int getCount() {
+ return data.size();
+ }
+
+ @Override
+ public UserDetails getItem(int position) {
+ return data.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return data.get(position).mUserHandle.getIdentifier();
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return false;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ return getDropDownView(position, convertView, parent);
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ return 0;
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return 1;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return data.isEmpty();
+ }
+
+ @Override
+ public boolean areAllItemsEnabled() {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ return true;
+ }
+
+ /**
+ * Creates a {@link UserAdapter} if there is more than one profile on the device.
+ *
+ * <p> The adapter can be used to populate a spinner that switches between the Settings
+ * app on the different profiles.
+ *
+ * @return a {@link UserAdapter} or null if there is only one profile.
+ */
+ public static UserAdapter createUserSpinnerAdapter(UserManager userManager,
+ Context context) {
+ List<UserHandle> userProfiles = userManager.getUserProfiles();
+ if (userProfiles.size() < 2) {
+ return null;
+ }
+
+ UserHandle myUserHandle = new UserHandle(UserHandle.myUserId());
+ // The first option should be the current profile
+ userProfiles.remove(myUserHandle);
+ userProfiles.add(0, myUserHandle);
+
+ return createUserAdapter(userManager, context, userProfiles);
+ }
+
+ public static UserAdapter createUserAdapter(UserManager userManager,
+ Context context, List<UserHandle> userProfiles) {
+ ArrayList<UserDetails> userDetails = new ArrayList<UserDetails>(userProfiles.size());
+ final int count = userProfiles.size();
+ for (int i = 0; i < count; i++) {
+ userDetails.add(new UserDetails(userProfiles.get(i), userManager, context));
+ }
+ return new UserAdapter(context, userDetails);
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java
new file mode 100644
index 0000000..f03e94d
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java
@@ -0,0 +1,185 @@
+/*
+ * 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.location;
+
+import android.app.AppGlobals;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Retrieves the information of applications which accessed location recently.
+ */
+public class RecentLocationApps {
+ private static final String TAG = RecentLocationApps.class.getSimpleName();
+ private static final String ANDROID_SYSTEM_PACKAGE_NAME = "android";
+
+ private static final int RECENT_TIME_INTERVAL_MILLIS = 15 * 60 * 1000;
+
+ private static final int[] LOCATION_OPS = new int[] {
+ AppOpsManager.OP_MONITOR_LOCATION,
+ AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION,
+ };
+
+ private final PackageManager mPackageManager;
+ private final Context mContext;
+
+ public RecentLocationApps(Context context) {
+ mContext = context;
+ mPackageManager = context.getPackageManager();
+ }
+
+ /**
+ * Fills a list of applications which queried location recently within specified time.
+ */
+ public List<Request> getAppList() {
+ // Retrieve a location usage list from AppOps
+ AppOpsManager aoManager =
+ (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+ List<AppOpsManager.PackageOps> appOps = aoManager.getPackagesForOps(LOCATION_OPS);
+
+ // Process the AppOps list and generate a preference list.
+ ArrayList<Request> requests = new ArrayList<>(appOps.size());
+ final long now = System.currentTimeMillis();
+ final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ final List<UserHandle> profiles = um.getUserProfiles();
+
+ final int appOpsN = appOps.size();
+ for (int i = 0; i < appOpsN; ++i) {
+ AppOpsManager.PackageOps ops = appOps.get(i);
+ // Don't show the Android System in the list - it's not actionable for the user.
+ // Also don't show apps belonging to background users except managed users.
+ String packageName = ops.getPackageName();
+ int uid = ops.getUid();
+ int userId = UserHandle.getUserId(uid);
+ boolean isAndroidOs =
+ (uid == Process.SYSTEM_UID) && ANDROID_SYSTEM_PACKAGE_NAME.equals(packageName);
+ if (isAndroidOs || !profiles.contains(new UserHandle(userId))) {
+ continue;
+ }
+ Request request = getRequestFromOps(now, ops);
+ if (request != null) {
+ requests.add(request);
+ }
+ }
+
+ return requests;
+ }
+
+ /**
+ * Creates a Request entry for the given PackageOps.
+ *
+ * This method examines the time interval of the PackageOps first. If the PackageOps is older
+ * than the designated interval, this method ignores the PackageOps object and returns null.
+ * When the PackageOps is fresh enough, this method returns a Request object for the package
+ */
+ private Request getRequestFromOps(long now,
+ AppOpsManager.PackageOps ops) {
+ String packageName = ops.getPackageName();
+ List<AppOpsManager.OpEntry> entries = ops.getOps();
+ boolean highBattery = false;
+ boolean normalBattery = false;
+ // Earliest time for a location request to end and still be shown in list.
+ long recentLocationCutoffTime = now - RECENT_TIME_INTERVAL_MILLIS;
+ for (AppOpsManager.OpEntry entry : entries) {
+ if (entry.isRunning() || entry.getTime() >= recentLocationCutoffTime) {
+ switch (entry.getOp()) {
+ case AppOpsManager.OP_MONITOR_LOCATION:
+ normalBattery = true;
+ break;
+ case AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION:
+ highBattery = true;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (!highBattery && !normalBattery) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, packageName + " hadn't used location within the time interval.");
+ }
+ return null;
+ }
+
+ // The package is fresh enough, continue.
+
+ int uid = ops.getUid();
+ int userId = UserHandle.getUserId(uid);
+
+ Request request = null;
+ try {
+ IPackageManager ipm = AppGlobals.getPackageManager();
+ ApplicationInfo appInfo =
+ ipm.getApplicationInfo(packageName, PackageManager.GET_META_DATA, userId);
+ if (appInfo == null) {
+ Log.w(TAG, "Null application info retrieved for package " + packageName
+ + ", userId " + userId);
+ return null;
+ }
+
+ final UserHandle userHandle = new UserHandle(userId);
+ Drawable appIcon = mPackageManager.getApplicationIcon(appInfo);
+ Drawable icon = mPackageManager.getUserBadgedIcon(appIcon, userHandle);
+ CharSequence appLabel = mPackageManager.getApplicationLabel(appInfo);
+ CharSequence badgedAppLabel = mPackageManager.getUserBadgedLabel(appLabel, userHandle);
+ if (appLabel.toString().contentEquals(badgedAppLabel)) {
+ // If badged label is not different from original then no need for it as
+ // a separate content description.
+ badgedAppLabel = null;
+ }
+ request = new Request(packageName, userHandle, icon, appLabel, highBattery,
+ badgedAppLabel);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Error while retrieving application info for package " + packageName
+ + ", userId " + userId, e);
+ }
+
+ return request;
+ }
+
+ public static class Request {
+ public final String packageName;
+ public final UserHandle userHandle;
+ public final Drawable icon;
+ public final CharSequence label;
+ public final boolean isHighBattery;
+ public final CharSequence contentDescription;
+
+ private Request(String packageName, UserHandle userHandle, Drawable icon,
+ CharSequence label, boolean isHighBattery, CharSequence contentDescription) {
+ this.packageName = packageName;
+ this.userHandle = userHandle;
+ this.icon = icon;
+ this.label = label;
+ this.isHighBattery = isHighBattery;
+ this.contentDescription = contentDescription;
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index d7dfdf8..e28b2e5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -258,6 +258,9 @@
mScanId++;
final List<ScanResult> newResults = mWifiManager.getScanResults();
for (ScanResult newResult : newResults) {
+ if (newResult.SSID == null || newResult.SSID.isEmpty()) {
+ continue;
+ }
mScanResultCache.put(newResult.BSSID, newResult);
mSeenBssids.put(newResult.BSSID, mScanId);
}
diff --git a/packages/SettingsProvider/AndroidManifest.xml b/packages/SettingsProvider/AndroidManifest.xml
index 71aefad..ba991fb 100644
--- a/packages/SettingsProvider/AndroidManifest.xml
+++ b/packages/SettingsProvider/AndroidManifest.xml
@@ -8,11 +8,12 @@
android:process="system"
android:backupAgent="SettingsBackupAgent"
android:killAfterRestore="false"
- android:icon="@mipmap/ic_launcher_settings">
-
- <!-- todo add: android:neverEncrypt="true" -->
+ android:icon="@mipmap/ic_launcher_settings"
+ android:forceDeviceEncrypted="true"
+ android:encryptionAware="true">
- <provider android:name="SettingsProvider" android:authorities="settings"
+ <provider android:name="SettingsProvider"
+ android:authorities="settings"
android:multiprocess="false"
android:exported="true"
android:singleUser="true"
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 95d7772..dde9709 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -392,12 +392,9 @@
if (DEBUG_PERSISTENCE) {
Slog.i(LOG_TAG, "[PERSIST END]");
}
-
- // Any error while writing is fatal.
} catch (Throwable t) {
Slog.wtf(LOG_TAG, "Failed to write settings, restoring backup", t);
destination.failWrite(out);
- throw new IllegalStateException("Failed to write settings, restoring backup", t);
} finally {
IoUtils.closeQuietly(out);
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 05591cc..8c39ee6 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -108,7 +108,9 @@
<uses-permission android:name="android.permission.REGISTER_SIM_SUBSCRIPTION" />
<uses-permission android:name="android.permission.GET_APP_OPS_STATS" />
- <application android:label="@string/app_label">
+ <application android:label="@string/app_label"
+ android:forceDeviceEncrypted="true"
+ android:encryptionAware="true">
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.android.shell"
diff --git a/packages/Shell/res/values-sv/strings.xml b/packages/Shell/res/values-sv/strings.xml
index 47ee2f7..8b72938 100644
--- a/packages/Shell/res/values-sv/strings.xml
+++ b/packages/Shell/res/values-sv/strings.xml
@@ -18,7 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="3701846017049540910">"Skal"</string>
<string name="bugreport_finished_title" msgid="2293711546892863898">"Felrapporten har skapats"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Dra till vänster om du vill dela felrapporten"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Svep åt vänster om du vill dela felrapporten"</string>
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Tryck om du vill dela felrapporten"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Felrapporter innehåller data från systemets olika loggfiler, inklusive personliga och privata uppgifter. Dela bara felrapporter med personer du litar på."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Visa det här meddelandet nästa gång"</string>
diff --git a/packages/Shell/src/com/android/shell/BugreportReceiver.java b/packages/Shell/src/com/android/shell/BugreportReceiver.java
index d83b516..e90a3b5 100644
--- a/packages/Shell/src/com/android/shell/BugreportReceiver.java
+++ b/packages/Shell/src/com/android/shell/BugreportReceiver.java
@@ -25,6 +25,7 @@
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
+import android.content.ClipData;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
@@ -41,14 +42,11 @@
import libcore.io.Streams;
import java.io.BufferedOutputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.io.OutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import java.util.ArrayList;
@@ -107,11 +105,6 @@
*/
private void triggerLocalNotification(final Context context, final File bugreportFile,
final File screenshotFile) {
- // Files are kept on private storage, so turn into Uris that we can
- // grant temporary permissions for.
- final Uri bugreportUri = FileProvider.getUriForFile(context, AUTHORITY, bugreportFile);
- final Uri screenshotUri = FileProvider.getUriForFile(context, AUTHORITY, screenshotFile);
-
boolean isPlainText = bugreportFile.getName().toLowerCase().endsWith(".txt");
if (!isPlainText) {
// Already zipped, send it right away.
@@ -133,12 +126,23 @@
*/
private static Intent buildSendIntent(Context context, Uri bugreportUri, Uri screenshotUri) {
final Intent intent = new Intent(Intent.ACTION_SEND_MULTIPLE);
+ final String mimeType = "application/vnd.android.bugreport";
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addCategory(Intent.CATEGORY_DEFAULT);
- intent.setType("application/vnd.android.bugreport");
+ intent.setType(mimeType);
intent.putExtra(Intent.EXTRA_SUBJECT, bugreportUri.getLastPathSegment());
- intent.putExtra(Intent.EXTRA_TEXT, SystemProperties.get("ro.build.description"));
+
+ // EXTRA_TEXT should be an ArrayList, but some clients are expecting a single String.
+ // So, to avoid an exception on Intent.migrateExtraStreamToClipData(), we need to manually
+ // create the ClipData object with the attachments URIs.
+ String messageBody = String.format("Build info: %s\nSerial number:%s",
+ SystemProperties.get("ro.build.description"), SystemProperties.get("ro.serialno"));
+ intent.putExtra(Intent.EXTRA_TEXT, messageBody);
+ final ClipData clipData = new ClipData(null, new String[] { mimeType },
+ new ClipData.Item(null, null, null, bugreportUri));
+ clipData.addItem(new ClipData.Item(null, null, null, screenshotUri));
+ intent.setClipData(clipData);
final ArrayList<Uri> attachments = Lists.newArrayList(bugreportUri, screenshotUri);
intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, attachments);
diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk
index 314b3c4..0cc2a67 100644
--- a/packages/SystemUI/Android.mk
+++ b/packages/SystemUI/Android.mk
@@ -3,7 +3,7 @@
LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-proto-files-under,src) \
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-proto-files-under,src) $(call all-Iaidl-files-under, src) \
src/com/android/systemui/EventLogTags.logtags
LOCAL_STATIC_JAVA_LIBRARIES := Keyguard
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 8fc7ad0..6fda2c6 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -127,6 +127,9 @@
<!-- Assist -->
<uses-permission android:name="android.permission.ACCESS_VOICE_INTERACTION_SERVICE" />
+ <!-- Listen for keyboard attachment / detachment -->
+ <uses-permission android:name="android.permission.TABLET_MODE" />
+
<!-- Self permission for internal broadcasts. -->
<permission android:name="com.android.systemui.permission.SELF"
android:protectionLevel="signature" />
@@ -142,7 +145,9 @@
android:icon="@drawable/icon"
android:process="com.android.systemui"
android:supportsRtl="true"
- android:theme="@style/systemui_theme">
+ android:theme="@style/systemui_theme"
+ android:forceDeviceEncrypted="true"
+ android:encryptionAware="true">
<!-- Keep theme in sync with SystemUIApplication.onCreate().
Setting the theme on the application does not affect views inflated by services.
The application theme is set again from onCreate to take effect for those views. -->
@@ -196,6 +201,11 @@
android:value="com.android.settings.category.system" />
</activity>
+ <!-- Service used by secondary users to register themselves with the system user. -->
+ <service android:name=".recents.RecentsSystemUserService"
+ android:exported="false"
+ android:permission="com.android.systemui.permission.SELF" />
+
<!-- Alternate Recents -->
<activity android:name=".recents.RecentsActivity"
android:label="@string/accessibility_desc_recent_apps"
@@ -206,23 +216,13 @@
android:resumeWhilePausing="true"
android:screenOrientation="behind"
android:resizeableActivity="true"
+ android:configChanges="orientation|screenSize|smallestScreenSize"
android:theme="@style/RecentsTheme.Wallpaper">
<intent-filter>
<action android:name="com.android.systemui.recents.TOGGLE_RECENTS" />
</intent-filter>
</activity>
- <receiver android:name=".recents.RecentsUserEventProxyReceiver"
- android:exported="false">
- <intent-filter>
- <action android:name="com.android.systemui.recents.action.SHOW_RECENTS_FOR_USER" />
- <action android:name="com.android.systemui.recents.action.HIDE_RECENTS_FOR_USER" />
- <action android:name="com.android.systemui.recents.action.TOGGLE_RECENTS_FOR_USER" />
- <action android:name="com.android.systemui.recents.action.PRELOAD_RECENTS_FOR_USER" />
- <action android:name="com.android.systemui.recents.action.CONFIG_CHANGED_FOR_USER" />
- </intent-filter>
- </receiver>
-
<!-- Callback for dismissing screenshot notification after a share target is picked -->
<receiver android:name=".screenshot.GlobalScreenshot$TargetChosenReceiver"
android:process=":screenshot"
diff --git a/packages/SystemUI/res/drawable/docked_divider_handle.xml b/packages/SystemUI/res/drawable/docked_divider_handle.xml
new file mode 100644
index 0000000..ea32548
--- /dev/null
+++ b/packages/SystemUI/res/drawable/docked_divider_handle.xml
@@ -0,0 +1,22 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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">
+ <size android:height="@dimen/docked_divider_handle_height"
+ android:width="@dimen/docked_divider_handle_width" />
+ <corners radius="1dp" />
+ <solid android:color="@color/docked_divider_handle" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_collapse_children.xml b/packages/SystemUI/res/drawable/ic_collapse_children.xml
deleted file mode 100644
index b0ce1e6..0000000
--- a/packages/SystemUI/res/drawable/ic_collapse_children.xml
+++ /dev/null
@@ -1,19 +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
- -->
-
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:insetTop="2dp"
- android:drawable="@drawable/ic_expand_less"/>
diff --git a/packages/SystemUI/res/drawable/ic_expand_children.xml b/packages/SystemUI/res/drawable/ic_expand_children.xml
deleted file mode 100644
index 1762be4..0000000
--- a/packages/SystemUI/res/drawable/ic_expand_children.xml
+++ /dev/null
@@ -1,18 +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
- -->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:insetTop="2dp"
- android:drawable="@drawable/ic_expand_more"/>
diff --git a/packages/SystemUI/res/drawable/notification_expand_more.xml b/packages/SystemUI/res/drawable/notification_expand_more.xml
new file mode 100644
index 0000000..5aa7937
--- /dev/null
+++ b/packages/SystemUI/res/drawable/notification_expand_more.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="22.0dp"
+ android:height="22.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M16.600000,8.600000l-4.600000,4.599999 -4.600000,-4.599999 -1.400000,1.400000 6.000000,6.000000 6.000000,-6.000000z"
+ android:fillColor="#FF000000"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/docked_stack_divider.xml b/packages/SystemUI/res/layout/docked_stack_divider.xml
new file mode 100644
index 0000000..5f42856
--- /dev/null
+++ b/packages/SystemUI/res/layout/docked_stack_divider.xml
@@ -0,0 +1,35 @@
+<?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.
+-->
+
+<com.android.systemui.stackdivider.DividerView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent">
+
+ <View
+ style="@style/DockedDividerBackground"
+ android:id="@+id/docked_divider_background"
+ android:background="@color/docked_divider_background"/>
+
+ <ImageButton
+ style="@style/DockedDividerHandle"
+ android:id="@+id/docked_divider_handle"
+ android:layout_height="48dp"
+ android:layout_width="48dp"
+ android:background="@null"
+ android:src="@drawable/docked_divider_handle"/>
+
+</com.android.systemui.stackdivider.DividerView>
diff --git a/packages/SystemUI/res/layout/hybrid_notification.xml b/packages/SystemUI/res/layout/hybrid_notification.xml
new file mode 100644
index 0000000..9e64d2c
--- /dev/null
+++ b/packages/SystemUI/res/layout/hybrid_notification.xml
@@ -0,0 +1,39 @@
+<?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
+ -->
+
+<com.android.systemui.statusbar.notification.HybridNotificationView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_max_height">
+ <TextView
+ android:id="@+id/notification_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:singleLine="true"
+ android:textAppearance="@*android:style/TextAppearance.Material.Notification.Title"
+ android:paddingEnd="4dp"
+ />
+ <TextView
+ android:id="@+id/notification_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:textAppearance="@*android:style/TextAppearance.Material.Notification"
+ android:singleLine="true"
+ />
+</com.android.systemui.statusbar.notification.HybridNotificationView>
diff --git a/packages/SystemUI/res/layout/notification_collapse_button.xml b/packages/SystemUI/res/layout/notification_collapse_button.xml
deleted file mode 100644
index 3ec5f63..0000000
--- a/packages/SystemUI/res/layout/notification_collapse_button.xml
+++ /dev/null
@@ -1,33 +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
- -->
-<FrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="@dimen/notification_bottom_decor_height">
- <TextView
- android:id="@+id/notification_expand_button"
- android:layout_width="wrap_content"
- android:layout_height="@dimen/notification_bottom_decor_height"
- android:background="@null"
- android:layout_gravity="top|center_horizontal"
- android:gravity="center_vertical|center_horizontal"
- android:textAppearance="@*android:style/TextAppearance.Material.Notification.Title"
- android:text="@string/notification_collapse_button_text"
- android:drawableEnd="@drawable/ic_collapse_children"
- android:drawablePadding="1dp"
- />
-</FrameLayout>
diff --git a/packages/SystemUI/res/layout/notification_expand_button.xml b/packages/SystemUI/res/layout/notification_expand_button.xml
deleted file mode 100644
index 3c478f7..0000000
--- a/packages/SystemUI/res/layout/notification_expand_button.xml
+++ /dev/null
@@ -1,40 +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
- -->
-
-<FrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="@dimen/notification_bottom_decor_height">
- <TextView
- android:id="@+id/notification_expand_button"
- android:layout_width="wrap_content"
- android:layout_height="@dimen/notification_bottom_decor_height"
- android:background="@null"
- android:layout_gravity="top|center_horizontal"
- android:gravity="center_vertical|center_horizontal"
- android:textAppearance="@*android:style/TextAppearance.Material.Notification.Title"
- android:text="@string/notification_expand_button_text"
- android:drawableEnd="@drawable/ic_expand_children"
- android:drawablePadding="1dp"
- />
- <com.android.systemui.statusbar.AlphaOptimizedView
- android:id="@+id/notification_expand_divider"
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:layout_gravity="top|center"
- android:background="@*android:drawable/notification_template_divider" />
-</FrameLayout>
diff --git a/packages/SystemUI/res/layout/notification_header.xml b/packages/SystemUI/res/layout/notification_header.xml
new file mode 100644
index 0000000..3475d00
--- /dev/null
+++ b/packages/SystemUI/res/layout/notification_header.xml
@@ -0,0 +1,83 @@
+<?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
+ -->
+
+<com.android.systemui.statusbar.notification.NotificationHeaderView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_header_height"
+ android:clipChildren="false">
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/notification_header_height"
+ android:layout_gravity="start"
+ android:gravity="center_vertical"
+ android:paddingStart="@dimen/notification_content_margin_start">
+ <ImageView
+ android:id="@+id/header_notification_icon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_marginEnd="4dp"
+ />
+ <TextView
+ android:id="@+id/number_of_children"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@*android:style/TextAppearance.Material.Notification"
+ android:layout_marginEnd="2dp"
+ android:layout_marginStart="1dp"
+ />
+ <TextView
+ android:id="@+id/app_name_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.Material.Notification.HeaderTitle"
+ android:layout_marginStart="4dp"
+ android:layout_marginEnd="8dp"
+ />
+ <TextView
+ android:id="@+id/app_title_sub_text_divider"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.Material.Notification.HeaderTitle"
+ android:layout_marginEnd="8dp"
+ android:text="@string/notification_header_divider_symbol"/>
+ <TextView
+ android:id="@+id/title_sub_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.Material.Notification.HeaderTitle"
+ android:layout_marginEnd="8dp" />
+ <TextView
+ android:id="@+id/post_time"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="end|center_vertical"
+ android:textAppearance="@*android:style/TextAppearance.Material.Notification.Time"
+ android:paddingEnd="8dp"
+ />
+ </LinearLayout>
+ <ImageButton
+ android:id="@+id/notification_expand_button"
+ android:background="@null"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:layout_gravity="end|center_vertical"
+ android:src="@drawable/notification_expand_more"
+ android:layout_marginEnd="8dp"
+ />
+</com.android.systemui.statusbar.notification.NotificationHeaderView>
diff --git a/packages/SystemUI/res/layout/qs_add_tile_layout.xml b/packages/SystemUI/res/layout/qs_add_tile_layout.xml
new file mode 100644
index 0000000..962b00e
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_add_tile_layout.xml
@@ -0,0 +1,43 @@
+<?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.
+-->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:paddingTop="20dp"
+ android:paddingStart="7dp"
+ android:paddingEnd="7dp">
+ <LinearLayout
+ android:layout_height="wrap_content"
+ android:layout_width="80dp"
+ android:orientation="vertical">
+ <ImageView
+ android:id="@+id/tile_icon"
+ android:layout_gravity="center"
+ android:layout_width="@dimen/qs_tile_icon_size"
+ android:layout_height="@dimen/qs_tile_icon_size" />
+ <TextView
+ android:id="@+id/tile_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center|bottom"
+ android:paddingTop="10dp"
+ android:gravity="center"
+ android:textSize="@dimen/qs_tile_text_size" />
+ </LinearLayout>
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout/qs_add_tiles_list.xml b/packages/SystemUI/res/layout/qs_add_tiles_list.xml
new file mode 100644
index 0000000..312c207
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_add_tiles_list.xml
@@ -0,0 +1,32 @@
+<?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.
+-->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <ListView
+ android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ <TextView
+ android:paddingTop="10dp"
+ android:id="@+id/empty_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="@string/no_tiles_add" />
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout/qs_customize_panel.xml b/packages/SystemUI/res/layout/qs_customize_panel.xml
index 59fed5b..f430fa5 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel.xml
@@ -19,14 +19,13 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
- android:paddingBottom="@dimen/navigation_bar_size"
android:background="@drawable/qs_customizer_background"
android:gravity="center_horizontal">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="?android:attr/colorPrimary">
+ android:background="@drawable/notification_header_bg">
<LinearLayout
android:id="@+id/drag_buttons"
@@ -72,8 +71,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:navigationContentDescription="@*android:string/action_bar_up_description"
- style="?android:attr/toolbarStyle"
- android:background="?android:attr/colorPrimary" />
+ android:background="@drawable/notification_header_bg"
+ style="?android:attr/toolbarStyle" />
</FrameLayout>
<com.android.systemui.tuner.AutoScrollView
@@ -105,4 +104,10 @@
android:elevation="@dimen/fab_elevation"
android:background="@drawable/fab_background" />
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/navigation_bar_size"
+ android:layout_gravity="bottom"
+ android:background="#ff000000" />
+
</com.android.systemui.qs.customize.QSCustomizer>
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 d778c98..cc35c51c 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-** Copyright 2015, The Android Open Source Project
+** Copyright 2012, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
@@ -23,6 +23,8 @@
android:layout_width="@dimen/notification_panel_width"
android:layout_height="@dimen/status_bar_header_height"
android:layout_gravity="@integer/notification_panel_layout_gravity"
+ android:paddingStart="@dimen/notification_side_padding"
+ android:paddingEnd="@dimen/notification_side_padding"
android:baselineAligned="false"
android:elevation="4dp"
android:background="@drawable/notification_header_bg"
@@ -30,54 +32,24 @@
android:focusable="true"
>
+ <com.android.systemui.qs.QuickQSPanel
+ android:id="@+id/quick_qs_panel"
+ android:background="#0000"
+ android:layout_width="144dp"
+ android:layout_height="match_parent"
+ android:layout_alignParentEnd="true"
+ android:layout_marginEnd="12dp" />
+
<LinearLayout
android:id="@+id/expanded_group"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:paddingEnd="12dp"
- android:orientation="horizontal">
-
- <com.android.systemui.statusbar.AlphaOptimizedButton android:id="@+id/alarm_status"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="4dp"
- android:drawablePadding="6dp"
- android:drawableStart="@drawable/ic_access_alarms_small"
- android:textColor="#64ffffff"
- android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date"
- android:paddingEnd="6dp"
- android:paddingStart="6dp"
- android:paddingTop="16dp"
- android:paddingBottom="16dp"
- android:background="?android:attr/selectableItemBackground"
- android:visibility="gone"
- />
-
- <com.android.systemui.statusbar.policy.DateView android:id="@+id/date"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="16dp"
- android:singleLine="true"
- android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date"
- android:layout_below="@id/clock"
- systemui:datePattern="EEE"
- android:gravity="center_vertical"
- android:textSize="20sp"
- android:paddingTop="16dp"
- android:layout_marginBottom="@dimen/clock_collapsed_bottom_margin" />
-
- <include layout="@layout/split_clock_view"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_marginStart="16dp"
- android:layout_marginTop="16dp"
- android:id="@+id/clock"
- />
-
- <Space
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1" />
+ android:gravity="center"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:orientation="horizontal"
+ android:layout_alignParentEnd="true"
+ android:layout_marginEnd="10dp">
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
android:id="@+id/settings_button_container"
@@ -103,8 +75,58 @@
android:src="@drawable/tuner" />
</com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
+
+ <ImageView
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:padding="12dp"
+ android:src="@drawable/ic_expand_less"
+ android:tint="@android:color/white" />
</LinearLayout>
+ <FrameLayout
+ android:id="@+id/date_group"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/clock_collapsed_bottom_margin"
+ android:layout_alignParentBottom="true">
+ <com.android.systemui.statusbar.policy.DateView android:id="@+id/date_collapsed"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dp"
+ android:singleLine="true"
+ android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date"
+ android:layout_below="@id/clock"
+ systemui:datePattern="@string/abbrev_wday_month_day_no_year_alarm"
+ />
+ </FrameLayout>
+
+ <include layout="@layout/split_clock_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dp"
+ android:layout_above="@id/date_group"
+ android:id="@+id/clock"
+ />
+
+ <com.android.systemui.statusbar.AlphaOptimizedButton android:id="@+id/alarm_status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_toEndOf="@id/date_group"
+ android:layout_marginBottom="4dp"
+ android:drawablePadding="6dp"
+ android:drawableStart="@drawable/ic_access_alarms_small"
+ android:textColor="#64ffffff"
+ android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date"
+ android:paddingEnd="6dp"
+ android:paddingStart="6dp"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp"
+ android:background="?android:attr/selectableItemBackground"
+ android:visibility="gone"
+ />
+
<include
android:id="@+id/qs_detail_header"
layout="@layout/qs_detail_header"
@@ -113,12 +135,6 @@
android:layout_alignParentBottom="true"
/>
- <com.android.systemui.qs.QuickQSPanel
- android:id="@+id/quick_qs_panel"
- android:background="#0000"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
<com.android.systemui.statusbar.AlphaOptimizedImageView
android:id="@+id/qs_detail_header_progress"
android:src="@drawable/indeterminate_anim"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index a64dbbd..bfa13e2 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -102,8 +102,7 @@
android:id="@+id/status_bar_header"
android:layout_width="@dimen/notification_panel_width"
android:layout_height="@dimen/status_bar_header_height"
- android:layout_marginStart="@dimen/notification_side_padding"
- android:layout_marginEnd="@dimen/notification_side_padding" />
+ android:layout_gravity="@integer/notification_panel_layout_gravity" />
<com.android.systemui.statusbar.AlphaOptimizedView
android:id="@+id/qs_navbar_scrim"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded_header.xml b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
index dd75dbf..5eca471 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
@@ -23,6 +23,8 @@
android:layout_width="@dimen/notification_panel_width"
android:layout_height="@dimen/status_bar_header_height"
android:layout_gravity="@integer/notification_panel_layout_gravity"
+ android:paddingStart="@dimen/notification_side_padding"
+ android:paddingEnd="@dimen/notification_side_padding"
android:baselineAligned="false"
android:elevation="4dp"
android:background="@drawable/notification_header_bg"
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index ea7ce96..b9088ec 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -61,11 +61,11 @@
/>
<ViewStub
- android:layout="@layout/notification_expand_button"
- android:id="@+id/more_button_stub"
- android:inflatedId="@+id/notification_more_button_container"
+ android:layout="@layout/notification_header"
+ android:id="@+id/notification_header_stub"
+ android:inflatedId="@+id/notification_header"
android:layout_width="match_parent"
- android:layout_height="@dimen/notification_bottom_decor_height"
+ android:layout_height="@dimen/notification_header_height"
/>
<ViewStub
diff --git a/packages/SystemUI/res/layout/tile_listing.xml b/packages/SystemUI/res/layout/tile_listing.xml
new file mode 100644
index 0000000..9ab62ca
--- /dev/null
+++ b/packages/SystemUI/res/layout/tile_listing.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ** Copyright 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.
+ -->
+
+ <LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/qs_background_primary"
+ android:paddingBottom="20dp"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/notification_header_bg"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:paddingTop="10dp"
+ android:paddingBottom="10dp"
+ android:orientation="horizontal">
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="36dp"
+ android:layout_height="36dp" />
+ <TextView
+ android:id="@android:id/title"
+ android:paddingStart="10dp"
+ android:textColor="@android:color/white"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical" />
+ </LinearLayout>
+
+ <GridLayout
+ android:id="@+id/tile_grid"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:columnCount="4" />
+
+ </LinearLayout>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index c7c7f1a..01d5e05 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Sien hulle voordat jy ontsluit"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Nee dankie"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Stel op"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Sien alles"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Versteek alles"</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">"Beëindig nou"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Vou uit"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Vou in"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Skerm is vasgespeld"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Dit hou dit in sig totdat jy dit ontspeld. Raak en hou Terug en Oorsig op dieselfde tyd om te ontspeld."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Dit hou dit in sig totdat jy ontspeld. Raak en hou Oorsig om te ontspeld."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Dit hou dit in sig totdat jy dit ontspeld. Raak en hou Terug om dit te ontspeld."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Het dit"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Nee, dankie"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Versteek <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Weier"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> is die volumedialoog"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Raak om die oorspronklike terug te stel."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Jy gebruik tans jou werkprofiel"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Stelsel-UI-ontvanger"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Wys persentasie van ingebedde battery"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Wys horlosiesekondes op die statusbalk. Sal batterylewe dalk beïnvloed."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Herrangskik Kitsinstellings"</string>
<string name="show_brightness" msgid="6613930842805942519">"Wys helderheid in Kitsinstellings"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Gebruik verdeling in Kitsinstellings"</string>
<string name="experimental" msgid="6198182315536726162">"Eksperimenteel"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Skakel Bluetooth aan?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Jy moet Bluetooth aanskakel om jou sleutelbord aan jou tablet te koppel."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Skakel aan"</string>
</resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index f49f5bc..8999d7c 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"ከመክፈትዎ በፊት ይመልከቷቸው"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"አይ፣ አመሰግናለሁ"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"አዋቅር"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"ሁሉንም ይመልከቱ"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"ሁሉንም ደብቅ"</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">"አሁን ጨርስ"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"አስፋ"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"ሰብስብ"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"ማያ ገጽ ተሰክቷል"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"ይህ እስከሚነቅሉት ድረስ ድረስ በዕይታ ውስጥ እንዲቆይ ያደርገዋል። ለመንቀል በተመሳሳይ ጊዜ ተመለስን እና አጠቃላይ ዕይታን አንድ ላይ ነክተው ይያዙ።"</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"ይህ እስከሚነቅሉት ድረስ በዕይታ ውስጥ ያቆየዋል። እንዲነቀል ለማድረግ አጠቃላይ ዕይታን ነካ አድርገው ይያዙት።"</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"ይህ እስከሚነቅሉት ድረስ በዕይታ ውስጥ ያቆየዋል። እንዲነቀል ለማድረግ ተመለስን ነካ አድርገው ይያዙት።"</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"ገባኝ"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"አይ፣ አመሰግናለሁ"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> ይደበቅ?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"ከልክል"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> የድምጽ መጠን መገናኛው ነው"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"የመጀመሪያውን ወደነበረበት ለመመለስ ይንኩ።"</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">"፣"</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"የስራ መገለጫዎን እየተጠቀሙ ነው"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"የስርዓት በይነገጽ መቃኛ"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"የተቀላቀለ የባትሪ አጠቃቀም መቶኛ አሳይ"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"የሰዓት ሰከንዶችን በሁኔታ አሞሌ ውስጥ አሳይ። በባትሪ ዕድሜ ላይ ተጽዕኖ ሊኖርው ይችል ይሆናል።"</string>
<string name="qs_rearrange" msgid="8060918697551068765">"ፈጣን ቅንብሮችን ዳግም ያደራጁ"</string>
<string name="show_brightness" msgid="6613930842805942519">"በፈጣን ቅንብሮች ውስጥ ብሩህነትን አሳይ"</string>
- <string name="qs_paging" msgid="7020133150248666132">"በፈጣን ቅንብሮች ውስጥ ምልክት መጥሪያን ይጠቀሙ"</string>
<string name="experimental" msgid="6198182315536726162">"የሙከራ"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"ብሉቱዝ ይብራ?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"የቁልፍ ሰሌዳዎን ከእርስዎ ጡባዊ ጋር ለማገናኘት በመጀመሪያ ብሉቱዝን ማብራት አለብዎት።"</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"አብራ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index aaf20cc..f677972 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -390,15 +390,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"الاطلاع عليها قبل إلغاء القفل"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"لا، شكرًا"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"إعداد"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"عرض الكل"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"إخفاء الكل"</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">"إنهاء الآن"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"توسيع"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"تصغير"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"تم تثبيت الشاشة"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"يساعد هذا على استمرار العرض حتى يتم إلغاء التثبيت. ويمكنك لمس \"رجوع\" و\"عرض عام\" مع الاستمرار في وقت واحد لإلغاء التثبيت."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"يساعد هذا على استمرار العرض حتى يتم إلغاء التثبيت. ويمكنك لمس \"عرض عام\" مع الاستمرار في وقت واحد لإلغاء التثبيت."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"يساعد هذا على استمرار العرض حتى يتم إلغاء التثبيت. ويمكنك لمس \"رجوع\" مع الاستمرار لإلغاء التثبيت."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"حسنًا"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"لا، شكرًا"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"هل تريد إخفاء <xliff:g id="TILE_LABEL">%1$s</xliff:g>؟"</string>
@@ -409,6 +406,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"رفض"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> هو مربع حوار مستوى الصوت"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"المس لاستعادة الإعداد الأصلي."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">"،"</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"أنت تستخدم ملفك الشخصي للعمل"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"أداة ضبط واجهة مستخدم النظام"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"عرض نسبة البطارية المدمجة"</string>
@@ -443,6 +441,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"عرض ثواني الساعة في شريط الحالة. قد يؤثر ذلك في عمر البطارية."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"إعادة ترتيب الإعدادات السريعة"</string>
<string name="show_brightness" msgid="6613930842805942519">"عرض السطوع في الإعدادات السريعة"</string>
- <string name="qs_paging" msgid="7020133150248666132">"استخدام ترقيم الصفحات في الإعدادات السريعة"</string>
<string name="experimental" msgid="6198182315536726162">"إعدادات تجريبية"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"تشغيل البلوتوث؟"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"لتوصيل لوحة المفاتيح بالجهاز اللوحي، يلزمك تشغيل بلوتوث أولاً."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"تشغيل"</string>
</resources>
diff --git a/packages/SystemUI/res/values-az-rAZ/strings.xml b/packages/SystemUI/res/values-az-rAZ/strings.xml
index 0efc82b..fb92d6b 100644
--- a/packages/SystemUI/res/values-az-rAZ/strings.xml
+++ b/packages/SystemUI/res/values-az-rAZ/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Kiliddən çıxarmadan öncə onları görün"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Yox, çox sağ olun"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Quraşdırın"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Hamısına baxın"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Hamısını gizlədin"</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">"İndi qurtarın"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Genişləndirin"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Yığcamlaşdırın"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Ekrana sancaq taxıldı"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Sancaq götürülənə qədər bu, görünəcək. Sancağı götürmək üçün Geri və İcmal düymələrinə eyni vaxtda toxunun və saxlayın."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Sancaq götürülənə qədər bu, görünəcək. Sancağı götürmək üçün Geri və İcmal düymələrinə toxunun və saxlayın."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Sancaq götürülənə qədər bu görünəcək. Sancağı götürmək üçün Geri düyməsinə toxunun və saxlayın."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Anladım!"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Yox, çox sağ olun"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> gizlədilsin?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Rədd et"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> proqramı səs səviyyəsi dialoqudur"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Orijinalı bərpa etmək üçün toxun."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"İş profilinizi istifadə edirsiniz"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Daxil batareya faizini göstərin"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Saatın saniyəsini status panelində göstərin. Batareyaya təsir edə bilər."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Sürətli Ayarları yenidən tənzimləyin"</string>
<string name="show_brightness" msgid="6613930842805942519">"Sürətli ayarlarda parlaqlılığı göstərin"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Sürətli Ayarlarda səhifə nömrələməsindən istifadə edin"</string>
<string name="experimental" msgid="6198182315536726162">"Eksperimental"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Bluetooth aktivləşsin?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Tabletinizlə klaviaturaya bağlanmaq üçün ilk olaraq Bluetooth\'u aktivləşdirməlisiniz."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Aktivləşdirin"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 6805779..b7f96a1 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Вижте известията, преди да отключите"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Няма нужда"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Настройване"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Преглед на всичко"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Скриване на всичко"</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">"Прекратяване сега"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Разгъване"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Свиване"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Екранът е фиксиран"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Така екранът ще се показва, докато не го освободите. За да направите това, докоснете и задръжте бутона за връщане назад и този за общ преглед едновременно."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Така екранът ще се показва, докато не го освободите. За да направите това, докоснете и задръжте бутона за общ преглед."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Така екранът ще се показва, докато не го освободите с докосване и задържане на бутона за връщане назад."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Разбрах"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Не, благодаря"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Да се скрие ли „<xliff:g id="TILE_LABEL">%1$s</xliff:g>“?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Отказване"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> изпълнява ролята на диалоговия прозорец за силата на звука"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Докоснете, за да възстановите оригинала."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Използвате служебния си потребителски профил"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Тунер на системния потребителски интерфейс"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Показване на процента на вградената батерия"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Показване на секундите на часовника в лентата на състоянието. Може да се отрази на живота на батерията."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Пренареждане на бързите настройки"</string>
<string name="show_brightness" msgid="6613930842805942519">"Показване на яркостта от бързите настройки"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Използване на разделянето на страници от бързите настройки"</string>
<string name="experimental" msgid="6198182315536726162">"Експериментални"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Да се включи ли Bluetooth?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"За да свържете клавиатурата с таблета си, първо трябва да включите Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Включване"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bn-rBD/strings.xml b/packages/SystemUI/res/values-bn-rBD/strings.xml
index c0ffe2c..6a93724 100644
--- a/packages/SystemUI/res/values-bn-rBD/strings.xml
+++ b/packages/SystemUI/res/values-bn-rBD/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"আপনি আনলক করার আগে ওগুলো দেখুন"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"না থাক"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"সেট আপ"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"সবগুলি দেখুন"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"সবগুলি লুকান"</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">"এখন সমাপ্ত করুন"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"প্রসারিত করুন"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"সঙ্কুচিত করুন"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"স্ক্রীন পিন করা হয়েছে"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"এটি আপনার আনপিন না করা পর্যন্ত এটিকে দর্শনে রাখে৷ আনপিন করতে একই সময়ে ফিরুন এবং ওভারভিউ এ স্পর্শ করে ধরে রাখুন৷"</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"এটি আপনার আনপিন না করা পর্যন্ত এটিকে দর্শনে রাখে৷ আনপিন করতে ওভারভিউ এ স্পর্শ করে ধরে রাখুন৷"</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"এটি আপনি আনপিন না করা পর্যন্ত এটিকে প্রদর্শিত করবে৷ আনপিন করতে \'ফিরুন\' এ স্পর্শ করে ধরে রাখুন৷"</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"বুঝেছি"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"না থাক"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> লুকাবেন?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"প্রত্যাখ্যান করুন"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> হল ভলিউম ডায়লগ"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"আসলটি পুনঃস্থাপন করতে স্পর্শ করুন৷"</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"আপনি আপনার কাজের প্রোফাইল ব্যবহার করছেন"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"সিস্টেম UI টিউনার"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"এম্বেড করা ব্যাটারির শতকরা হার দেখায়"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"স্থিতি দন্ডে ঘড়ির সেকেন্ড দেখায়৷ ব্যাটারি লাইফকে প্রভাবিত করতে পারে৷"</string>
<string name="qs_rearrange" msgid="8060918697551068765">"দ্রুত সেটিংসে পুনরায় সাজান"</string>
<string name="show_brightness" msgid="6613930842805942519">"দ্রুত সেটিংসে উজ্জ্বলতা দেখান"</string>
- <string name="qs_paging" msgid="7020133150248666132">"দ্রুত সেটিংসে পেজিং ব্যবহার করুন"</string>
<string name="experimental" msgid="6198182315536726162">"পরীক্ষামূলক"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Bluetooth চালু করবেন?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"আপনার ট্যাবলেটের সাথে আপনার কীবোর্ড সংযুক্ত করতে, আপনাকে প্রথমে Bluetooth চালু করতে হবে।"</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"চালু করুন"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 2a7c326..0da503a 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -150,9 +150,7 @@
<string name="accessibility_airplane_mode" msgid="834748999790763092">"Mode d\'avió."</string>
<string name="accessibility_no_sims" msgid="3957997018324995781">"No hi ha cap targeta SIM."</string>
<string name="accessibility_carrier_network_change_mode" msgid="4017301580441304305">"S\'està canviant la xarxa de l\'operador de telefonia mòbil."</string>
- <!-- String.format failed for translation -->
- <!-- no translation found for accessibility_battery_level (7451474187113371965) -->
- <skip />
+ <string name="accessibility_battery_level" msgid="7451474187113371965">"<xliff:g id="NUMBER">%d</xliff:g> per cent de bateria."</string>
<string name="accessibility_settings_button" msgid="799583911231893380">"Configuració del sistema."</string>
<string name="accessibility_notifications_button" msgid="4498000369779421892">"Notificacions."</string>
<string name="accessibility_remove_notification" msgid="3603099514902182350">"Esborra la notificació."</string>
@@ -388,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Mostra-les abans de desbloquejar"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"No"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Configura"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Mostra-les totes"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Amaga-les totes"</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">"Finalitza ara"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Amplia"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Replega"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"La pantalla està fixada"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Continuarà a la visualització fins que n\'anul·lis la fixació. Per fer-ho, toca i mantén premuts els botons Enrere i Visió general a la vegada."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Continuarà a la visualització fins que n\'anul·lis la fixació. Per fer-ho, toca i mantén premut el botó Visió general."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Continuarà a la visualització fins que n\'anul·lis la fixació. Per fer-ho, toca i mantén premut el botó Enrere."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"D\'acord"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"No, gràcies"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Vols amagar <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -407,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Denega"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> és el diàleg de volum"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Toca per restaurar l\'original."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Estàs utilitzant el perfil professional"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Personalitzador d\'interfície d\'usuari"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Mostra el percentatge de la bateria inserit"</string>
@@ -439,8 +435,10 @@
<string name="activity_not_found" msgid="348423244327799974">"L\'aplicació no està instal·lada al dispositiu"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Mostra els segons del rellotge"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Mostra els segons del rellotge a la barra d\'estat. Això pot afectar la durada de la bateria."</string>
- <string name="qs_rearrange" msgid="8060918697551068765">"Torna a ordenar la Configuració ràpida"</string>
- <string name="show_brightness" msgid="6613930842805942519">"Mostra la brillantor a la Configuració ràpida"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Utilitza la paginació a la Configuració ràpida"</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Reorganitza Configuració ràpida"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Mostra la brillantor a Configuració ràpida"</string>
<string name="experimental" msgid="6198182315536726162">"Experimental"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Vols activar el Bluetooth?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Per connectar el teclat amb la tauleta, primer has d\'activar el Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Activa"</string>
</resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 18fd994..c9e8a83 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -152,9 +152,7 @@
<string name="accessibility_airplane_mode" msgid="834748999790763092">"Režim Letadlo."</string>
<string name="accessibility_no_sims" msgid="3957997018324995781">"Není vložena SIM karta"</string>
<string name="accessibility_carrier_network_change_mode" msgid="4017301580441304305">"Probíhá změna sítě operátora."</string>
- <!-- String.format failed for translation -->
- <!-- no translation found for accessibility_battery_level (7451474187113371965) -->
- <skip />
+ <string name="accessibility_battery_level" msgid="7451474187113371965">"Stav baterie: <xliff:g id="NUMBER">%d</xliff:g> procent."</string>
<string name="accessibility_settings_button" msgid="799583911231893380">"Systémová nastavení."</string>
<string name="accessibility_notifications_button" msgid="4498000369779421892">"Oznámení."</string>
<string name="accessibility_remove_notification" msgid="3603099514902182350">"Vymazat oznámení."</string>
@@ -390,15 +388,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Můžete si je přečíst před odemčením obrazovky."</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Ne, děkuji"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Nastavit"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Zobrazit vše"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Skrýt vše"</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">"Ukončit"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Rozbalit"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Sbalit"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Obrazovka je připnuta"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Obsah bude připnut v zobrazení, dokud jej neuvolníte. Chcete-li jej uvolnit, stiskněte a podržte současně tlačítka Zpět a Přehled."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Obsah bude připnut v zobrazení, dokud jej neuvolníte. Uvolníte jej stisknutím a podržením tlačítka Přehled."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Obsah bude připnut v zobrazení, dokud jej neuvolníte. Uvolníte jej stisknutím a podržením tlačítka Zpět."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Rozumím"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Ne, děkuji"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Skrýt <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -409,6 +404,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Odmítnout"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> je dialog hlasitosti"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Klepnutím obnovíte originál."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Používáte pracovní profil"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Nástroj na ladění uživatelského rozhraní systému"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Zobrazovat vložené procento nabití baterie"</string>
@@ -443,6 +439,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Na stavovém řádku se bude zobrazovat sekundová ručička. Může být ovlivněna výdrž baterie."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Změnit uspořádání Rychlého nastavení"</string>
<string name="show_brightness" msgid="6613930842805942519">"Zobrazit jas v Rychlém nastavení"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Použít v Rychlém nastavení stránkování"</string>
<string name="experimental" msgid="6198182315536726162">"Experimentální"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Zapnout Bluetooth?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Chcete-li klávesnici připojit k tabletu, nejdříve musíte zapnout Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Zapnout"</string>
</resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 5a2dd33..a9931ce 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -85,13 +85,13 @@
<string name="accessibility_search_light" msgid="1103867596330271848">"Søg"</string>
<string name="accessibility_camera_button" msgid="8064671582820358152">"Kamera"</string>
<string name="accessibility_phone_button" msgid="6738112589538563574">"Telefon"</string>
- <string name="accessibility_voice_assist_button" msgid="487611083884852965">"Voice Assist"</string>
+ <string name="accessibility_voice_assist_button" msgid="487611083884852965">"Taleassistent"</string>
<string name="accessibility_unlock_button" msgid="128158454631118828">"Lås op"</string>
<string name="accessibility_unlock_button_fingerprint" msgid="8214125623493923751">"Knap til oplåsning. Venter på fingeraftryk"</string>
<string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"Lås op uden at bruge dit fingeraftryk"</string>
<string name="unlock_label" msgid="8779712358041029439">"lås op"</string>
<string name="phone_label" msgid="2320074140205331708">"åbn telefon"</string>
- <string name="voice_assist_label" msgid="3956854378310019854">"åbn voice assist"</string>
+ <string name="voice_assist_label" msgid="3956854378310019854">"åbn taleassistent"</string>
<string name="camera_label" msgid="7261107956054836961">"åbn kamera"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Vælg nyt opgavelayout"</string>
<string name="cancel" msgid="6442560571259935130">"Annuller"</string>
@@ -315,7 +315,7 @@
<string name="notification_tap_again" msgid="8524949573675922138">"Tryk igen for at åbne"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Stryg opad for at låse op"</string>
<string name="phone_hint" msgid="4872890986869209950">"Stryg fra telefonikonet"</string>
- <string name="voice_hint" msgid="8939888732119726665">"Stryg fra ikonet for voice assist"</string>
+ <string name="voice_hint" msgid="8939888732119726665">"Stryg fra mikrofonikonet"</string>
<string name="camera_hint" msgid="7939688436797157483">"Stryg fra kameraikonet"</string>
<string name="interruption_level_none_with_warning" msgid="5114872171614161084">"Helt lydløs. Denne handling slukker også skærmlæsere."</string>
<string name="interruption_level_none" msgid="6000083681244492992">"Total stilhed"</string>
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Se dem, før du låser op"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Nej tak"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Konfigurer"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Se alle"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Skjul alle"</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">"Afslut nu"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Udvid"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Skjul"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Skærmen er fastgjort"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Dette fastholder den i visningen, indtil du frigør den. Tryk på Tilbage og Oversigt på samme tid, og hold dem nede for at frigøre denne skærm."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Dette fastholder den i visningen, indtil du frigør den. Tryk på Oversigt, og hold den nede for at frigøre denne skærm."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Dette fastholder skærmen i visningen, indtil du frigør den. Tryk på Tilbage, og hold fingeren nede for at frigøre skærmen."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"OK, det er forstået"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Nej tak"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Vil du skjule <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Afvis"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> er dialogboksen for lydstyrke"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Tryk for at gendanne originalen."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Du bruger din arbejdsprofil"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Vis procent for det indbyggede batteri"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Vis sekunder i statuslinjen. Dette kan påvirke batteriets levetid."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Omarranger Hurtige indstillinger"</string>
<string name="show_brightness" msgid="6613930842805942519">"Vis lysstyrke i Hurtige indstillinger"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Brug sidelayout i Hurtige indstillinger"</string>
<string name="experimental" msgid="6198182315536726162">"Eksperimentel"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Vil du slå Bluetooth til?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Bluetooth skal være slået til, før du kan knytte dit tastatur til din tablet."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Slå til"</string>
</resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index a8143a4..8a7fa5e 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -150,9 +150,7 @@
<string name="accessibility_airplane_mode" msgid="834748999790763092">"Flugmodus"</string>
<string name="accessibility_no_sims" msgid="3957997018324995781">"Keine SIM-Karte"</string>
<string name="accessibility_carrier_network_change_mode" msgid="4017301580441304305">"Netzwerk des Mobilfunkanbieters wird gewechselt"</string>
- <!-- String.format failed for translation -->
- <!-- no translation found for accessibility_battery_level (7451474187113371965) -->
- <skip />
+ <string name="accessibility_battery_level" msgid="7451474187113371965">"Akku bei <xliff:g id="NUMBER">%d</xliff:g> Prozent."</string>
<string name="accessibility_settings_button" msgid="799583911231893380">"Systemeinstellungen"</string>
<string name="accessibility_notifications_button" msgid="4498000369779421892">"Benachrichtigungen"</string>
<string name="accessibility_remove_notification" msgid="3603099514902182350">"Benachrichtigung löschen"</string>
@@ -388,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Vor dem Entsperren anzeigen"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Nein danke"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Einrichten"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Alle anzeigen"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Alle ausblenden"</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">"Jetzt beenden"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Maximieren"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Minimieren"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Bildschirm ist fixiert"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Der Bildschirm wird solange angezeigt, bis Sie die Fixierung aufheben. Berühren und halten Sie \"Zurück\" und \"Übersicht\" gleichzeitig, um die Fixierung aufzuheben."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Der Bildschirm wird solange angezeigt, bis Sie die Fixierung aufheben. Berühren und halten Sie \"Übersicht\", wenn Sie die Fixierung aufheben möchten."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Der Bildschirm wird solange angezeigt, bis Sie die Fixierung aufheben. Berühren und halten Sie \"Zurück\", wenn Sie die Fixierung aufheben möchten."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"OK"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Nein danke"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> ausblenden?"</string>
@@ -407,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Ablehnen"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> regelt die Lautstärke."</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Zum Wiederherstellen des Originals hier tippen"</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">"u."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Sie verwenden Ihr Arbeitsprofil."</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Eingebettete Akku-Prozentzahl anzeigen"</string>
@@ -441,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Uhrsekunden in der Statusleiste anzeigen. Kann sich auf die Akkulaufzeit auswirken."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Schnelleinstellungen neu anordnen"</string>
<string name="show_brightness" msgid="6613930842805942519">"Helligkeit in den Schnelleinstellungen anzeigen"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Seitenlayout in den Schnelleinstellungen verwenden"</string>
<string name="experimental" msgid="6198182315536726162">"Experimentell"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Bluetooth aktivieren?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Zum Verbinden von Tastatur und Tablet muss Bluetooth aktiviert sein."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Aktivieren"</string>
</resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 6031d3b..47eb46f 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -150,9 +150,7 @@
<string name="accessibility_airplane_mode" msgid="834748999790763092">"Λειτουργία πτήσης."</string>
<string name="accessibility_no_sims" msgid="3957997018324995781">"Δεν υπάρχει κάρτα SIM."</string>
<string name="accessibility_carrier_network_change_mode" msgid="4017301580441304305">"Αλλαγή δικτύου εταιρείας κινητής τηλεφωνίας."</string>
- <!-- String.format failed for translation -->
- <!-- no translation found for accessibility_battery_level (7451474187113371965) -->
- <skip />
+ <string name="accessibility_battery_level" msgid="7451474187113371965">"Μπαταρία <xliff:g id="NUMBER">%d</xliff:g> τοις εκατό."</string>
<string name="accessibility_settings_button" msgid="799583911231893380">"Ρυθμίσεις συστήματος."</string>
<string name="accessibility_notifications_button" msgid="4498000369779421892">"Ειδοποιήσεις."</string>
<string name="accessibility_remove_notification" msgid="3603099514902182350">"Εκκαθάριση ειδοποίησης."</string>
@@ -388,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Εμφάνιση πριν το ξεκλείδωμα"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Όχι"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Ρύθμιση"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Εμφάνιση όλων"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Aπόκρυψη όλων"</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">"Τερματισμός τώρα"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Ανάπτυξη"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Σύμπτυξη"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Η οθόνη καρφιτσώθηκε"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Με αυτόν τον τρόπο παραμένει σε προβολή έως ότου την ξεκαρφιτσώσετε. Αγγίξτε παρατεταμένα \"Επιστροφή\" και \"Επισκόπηση\" ταυτόχρονα για ξεκαρφίτσωμα."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Με αυτόν τον τρόπο παραμένει σε προβολή έως ότου την ξεκαρφιτσώσετε. Αγγίξτε παρατεταμένα \"Επισκόπηση\" για ξεκαρφίτσωμα."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Με αυτόν τον τρόπο παραμένει σε προβολή έως ότου την ξεκαρφιτσώσετε. Αγγίξτε παρατεταμένα το στοιχείο επιστροφής για να την ξεκαρφιτσώσετε."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Το κατάλαβα"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Όχι"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Απόκρυψη <xliff:g id="TILE_LABEL">%1$s</xliff:g>;"</string>
@@ -407,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Απόρριψη"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> αποτελεί το παράθυρο διαλόγου ελέγχου έντασης"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Αγγίξτε για επαναφορά αρχικού."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Χρησιμοποιείτε το προφίλ εργασίας σας"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Εμφάνιση ποσοστού ενσωματωμένης μπαταρίας"</string>
@@ -441,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Εμφάνιση δευτερολέπτων ρολογιού στη γραμμή κατάστασης. Ενδέχεται να επηρεάσει τη διάρκεια ζωής της μπαταρίας."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Αναδιάταξη Γρήγορων ρυθμίσεων"</string>
<string name="show_brightness" msgid="6613930842805942519">"Εμφάνιση φωτεινότητας στις Γρήγορες ρυθμίσεις"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Χρήση τηλεειδοποίησης στις Γρήγορες ρυθμίσεις"</string>
<string name="experimental" msgid="6198182315536726162">"Σε πειραματικό στάδιο"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Ενεργοποίηση Bluetooth;"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Για να συνδέσετε το πληκτρολόγιο με το tablet σας, θα πρέπει πρώτα να ενεργοποιήσετε το Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Ενεργοποίηση"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index bef7661..f257c7f 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"See them before you unlock"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"No, thanks"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Setup"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"See all"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Hide all"</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">"End now"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Expand"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Collapse"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Screen is pinned"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"This keeps it in view until you unpin. Touch and hold Back and Overview at the same time to unpin."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"This keeps it in view until you unpin. Touch and hold Overview to unpin."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"This keeps it in view until you unpin. Touch and hold Back to unpin."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Got it"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"No, thanks"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Hide <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Deny"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> is the volume dialogue"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Touch to restore the original."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"You\'re using your work profile"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Show embedded battery percentage"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Show clock seconds in the status bar. May impact battery life."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Rearrange Quick Settings"</string>
<string name="show_brightness" msgid="6613930842805942519">"Show brightness in Quick Settings"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Use paging in Quick Settings"</string>
<string name="experimental" msgid="6198182315536726162">"Experimental"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Turn on Bluetooth?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"To connect your keyboard with your tablet, you first have to turn on Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Turn on"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index bef7661..f257c7f 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"See them before you unlock"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"No, thanks"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Setup"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"See all"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Hide all"</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">"End now"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Expand"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Collapse"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Screen is pinned"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"This keeps it in view until you unpin. Touch and hold Back and Overview at the same time to unpin."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"This keeps it in view until you unpin. Touch and hold Overview to unpin."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"This keeps it in view until you unpin. Touch and hold Back to unpin."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Got it"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"No, thanks"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Hide <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Deny"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> is the volume dialogue"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Touch to restore the original."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"You\'re using your work profile"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Show embedded battery percentage"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Show clock seconds in the status bar. May impact battery life."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Rearrange Quick Settings"</string>
<string name="show_brightness" msgid="6613930842805942519">"Show brightness in Quick Settings"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Use paging in Quick Settings"</string>
<string name="experimental" msgid="6198182315536726162">"Experimental"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Turn on Bluetooth?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"To connect your keyboard with your tablet, you first have to turn on Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Turn on"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index bef7661..f257c7f 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"See them before you unlock"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"No, thanks"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Setup"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"See all"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Hide all"</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">"End now"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Expand"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Collapse"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Screen is pinned"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"This keeps it in view until you unpin. Touch and hold Back and Overview at the same time to unpin."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"This keeps it in view until you unpin. Touch and hold Overview to unpin."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"This keeps it in view until you unpin. Touch and hold Back to unpin."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Got it"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"No, thanks"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Hide <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Deny"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> is the volume dialogue"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Touch to restore the original."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"You\'re using your work profile"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Show embedded battery percentage"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Show clock seconds in the status bar. May impact battery life."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Rearrange Quick Settings"</string>
<string name="show_brightness" msgid="6613930842805942519">"Show brightness in Quick Settings"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Use paging in Quick Settings"</string>
<string name="experimental" msgid="6198182315536726162">"Experimental"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Turn on Bluetooth?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"To connect your keyboard with your tablet, you first have to turn on Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Turn on"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en/donottranslate.xml b/packages/SystemUI/res/values-en/donottranslate.xml
deleted file mode 100644
index 9f04e1f..0000000
--- a/packages/SystemUI/res/values-en/donottranslate.xml
+++ /dev/null
@@ -1,23 +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
- -->
-
-<resources>
- <!-- DO NOT TRANSLATE - temporary hack to show the speed-less label if no translation is available -->
- <item type="string" name="keyguard_indication_charging_time_fast_if_translated">@string/keyguard_indication_charging_time_fast</item>
- <!-- DO NOT TRANSLATE - temporary hack to show the speed-less label if no translation is available -->
- <item type="string" name="keyguard_indication_charging_time_slowly_if_translated">@string/keyguard_indication_charging_time_slowly</item>
-</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 1590265..608f73c 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -150,9 +150,7 @@
<string name="accessibility_airplane_mode" msgid="834748999790763092">"Modo avión"</string>
<string name="accessibility_no_sims" msgid="3957997018324995781">"Sin tarjeta SIM"</string>
<string name="accessibility_carrier_network_change_mode" msgid="4017301580441304305">"Cambio de proveedor de red"</string>
- <!-- String.format failed for translation -->
- <!-- no translation found for accessibility_battery_level (7451474187113371965) -->
- <skip />
+ <string name="accessibility_battery_level" msgid="7451474187113371965">"Batería <xliff:g id="NUMBER">%d</xliff:g> por ciento"</string>
<string name="accessibility_settings_button" msgid="799583911231893380">"Configuración del sistema"</string>
<string name="accessibility_notifications_button" msgid="4498000369779421892">"Notificaciones"</string>
<string name="accessibility_remove_notification" msgid="3603099514902182350">"Eliminar notificación"</string>
@@ -388,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Ver antes de desbloquear"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"No"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Configurar"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Ver todas"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Ocultar todas"</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">"Finalizar ahora"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Expandir"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Contraer"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Pantalla fija"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Esta función mantiene fija la vista de la pantalla hasta que la desactivas. Mantén presionados los botones Atrás y Recientes al mismo tiempo para anular la fijación."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Esta función mantiene fija la vista de la pantalla hasta que la desactivas. Mantén presionado el botón Recientes para anular la fijación."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Esta función mantiene la pantalla visible hasta que dejes de fijarla. Mantén presionado el botón Atrás para dejar de fijar."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Entendido"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"No, gracias"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"¿Ocultar <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -407,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Rechazar"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> es el cuadro de diálogo de volumen."</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Toca para restaurar el original."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Estás usando tu perfil de trabajo"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Sintonizador de IU del sistema"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Mostrar porcentaje de la batería integrada"</string>
@@ -441,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Muestra los segundos del reloj en la barra de estado. Puede afectar la duración de la batería."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Reorganizar la Configuración rápida"</string>
<string name="show_brightness" msgid="6613930842805942519">"Mostrar el brillo en la Configuración rápida"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Usar la paginación en la Configuración rápida"</string>
<string name="experimental" msgid="6198182315536726162">"Experimental"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"¿Activar Bluetooth?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Para conectar el teclado con la tablet, primero debes activar Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Activar"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 082e762..aa4fd56 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Ver antes de desbloquear"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"No, gracias"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Configurar"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Ver todas"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Ocultar todas"</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">"Finalizar ahora"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Mostrar"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Ocultar"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Pantalla fijada"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"La pantalla se mantendrá visible hasta que dejes de fijarla. Para ello, mantén pulsados los botones de retroceso e información general."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"La pantalla se mantendrá visible hasta que dejes de fijarla. Para ello, mantén pulsado el botón de información general."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"La pantalla se mantendrá visible hasta que dejes de fijarla. Para dejar de fijarla, mantén pulsado el botón de retroceso."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Entendido"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"No, gracias"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"¿Ocultar <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Rechazar"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> es el cuadro de diálogo de volumen"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Toca para restaurar la versión original."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">"y"</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Estás usando tu perfil de trabajo"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Configurador de IU del sistema"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Mostrar porcentaje de batería insertado"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Muestra los segundos del reloj en la barra de estado. Puede afectar a la duración de la batería."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Reorganizar Ajustes rápidos"</string>
<string name="show_brightness" msgid="6613930842805942519">"Mostrar brillo en Ajustes rápidos"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Utilizar paginación en Ajustes rápidos"</string>
<string name="experimental" msgid="6198182315536726162">"Experimental"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"¿Activar Bluetooth?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Para poder conectar tu teclado a tu tablet, debes activar el Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Activar"</string>
</resources>
diff --git a/packages/SystemUI/res/values-et-rEE/strings.xml b/packages/SystemUI/res/values-et-rEE/strings.xml
index 4add147..655ee5b 100644
--- a/packages/SystemUI/res/values-et-rEE/strings.xml
+++ b/packages/SystemUI/res/values-et-rEE/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Näete neid enne avamist"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Tänan, ei"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Seadistus"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Kuva kõik"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Peida kõik"</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">"Lõpeta nüüd"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Laiendamine"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Ahendamine"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Ekraan on kinnitatud"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"See hoiab selle kuval, kuni selle vabastate. Vabastamiseks puudutage ning hoidke korraga all nuppe Tagasi ja Ülevaade."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"See hoiab selle kuval, kuni selle vabastate. Vabastamiseks puudutage ja hoidke all nuppu Ülevaade."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"See hoiab selle kuval, kuni selle vabastate. Vabastamiseks puudutage pikalt nuppu Tagasi."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Selge"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Tänan, ei"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Kas peita <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Keela"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> on helitugevuse dialoog"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Originaali taastamiseks puudutage."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Kasutate oma tööprofiili"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Süsteemi kasutajaliidese tuuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Kuva lisatud akutaseme protsent"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Olekuribal kella sekundite kuvamine. See võib mõjutada aku kasutusaega."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Korralda kiirseaded ümber"</string>
<string name="show_brightness" msgid="6613930842805942519">"Kuva kiirseadetes heledus"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Kasuta kiirseadetes lehe paigutust"</string>
<string name="experimental" msgid="6198182315536726162">"Eksperimentaalne"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Kas lülitada Bluetooth sisse?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Klaviatuuri ühendamiseks tahvelarvutiga peate esmalt Bluetoothi sisse lülitama."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Lülita sisse"</string>
</resources>
diff --git a/packages/SystemUI/res/values-eu-rES/strings.xml b/packages/SystemUI/res/values-eu-rES/strings.xml
index 4f5c9f2..5ac93c6 100644
--- a/packages/SystemUI/res/values-eu-rES/strings.xml
+++ b/packages/SystemUI/res/values-eu-rES/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Ikusi desblokeatu baino lehen"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Ez, eskerrik asko"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Konfiguratu"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Ikusi guztiak"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Ezkutatu guztiak"</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">"Amaitu"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Zabaldu"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Tolestu"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Pantaila ainguratuta dago"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Horrela, ikusgai mantenduko da aingura kendu arte. Aingura kentzeko, eduki ukituta aldi berean \"Atzera\" eta \"Ikuspegi orokorra\" botoiak."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Horrela, ikusgai mantenduko da, aingura kendu arte. Aingura kentzeko, eduki ukituta \"Ikuspegi orokorra\" botoia."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Horrela, ikusgai egongo da aingura kendu arte. Aingura kentzeko, eduki ukituta \"Atzera\" botoia."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Ados"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Ez, eskerrik asko"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> ezkutatu nahi duzu?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Ukatu"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> da bolumenaren leihoa"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Ukitu jatorrizkora leheneratzeko"</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Work profila erabiltzen ari zara"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Sistemako erabiltzaile-interfazearen konfiguratzailea"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Erakutsi txertatutako bateriaren ehunekoa"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Erakutsi erlojuko segundoak egoera-barran. Baliteke bateria gehiago erabiltzea."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Berrantolatu ezarpen bizkorrak"</string>
<string name="show_brightness" msgid="6613930842805942519">"Erakutsi distira Ezarpen bizkorretan"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Erabili orriak pasatzeko diseinu berria Ezarpen bizkorretan"</string>
<string name="experimental" msgid="6198182315536726162">"Esperimentala"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Bluetooth eginbidea aktibatu nahi duzu?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Teklatua tabletara konektatzeko, Bluetooth eginbidea aktibatu behar duzu."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Aktibatu"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 6fa7e1c..837b727 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -67,12 +67,12 @@
<string name="usb_debugging_secondary_user_message" msgid="8572228137833020196">"کاربری که درحالحاضر در این دستگاه وارد سیستم شده نمیتواند اشکالزدای USB را روشن کند. برای استفاده از این ویژگی، لطفاً به کاربر «سرپرست» تغییر حالت دهید."</string>
<string name="compat_mode_on" msgid="6623839244840638213">"بزرگنمایی برای پر کردن صفحه"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"گسترده کردن برای پر کردن صفحه"</string>
- <string name="screenshot_saving_ticker" msgid="7403652894056693515">"در حال ذخیره تصویر صفحه..."</string>
- <string name="screenshot_saving_title" msgid="8242282144535555697">"در حال ذخیره تصویر صفحه..."</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"تصویر صفحه ذخیره شد."</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"تصویر صفحه گرفته شد."</string>
+ <string name="screenshot_saving_ticker" msgid="7403652894056693515">"در حال ذخیره عکس صفحهنمایش..."</string>
+ <string name="screenshot_saving_title" msgid="8242282144535555697">"در حال ذخیره عکس صفحهنمایش..."</string>
+ <string name="screenshot_saving_text" msgid="2419718443411738818">"عکس صفحهنمایش ذخیره شد."</string>
+ <string name="screenshot_saved_title" msgid="6461865960961414961">"عکس صفحهنمایش گرفته شد."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"برای مشاهده عکس صفحهنمایشتان، لمس کنید."</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"تصویر صفحه گرفته نشد."</string>
+ <string name="screenshot_failed_title" msgid="705781116746922771">"عکس صفحهنمایش گرفته نشد."</string>
<string name="screenshot_failed_text" msgid="1260203058661337274">"به دلیل فضای ذخیرهسازی کم یا عدم اجازه برنامه یا سازمانتان، نمیتوان از صفحه عکس گرفت."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"گزینههای انتقال فایل USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"نصب بهعنوان دستگاه پخش رسانه (MTP)"</string>
@@ -219,7 +219,7 @@
<string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"داده موقتاً متوقف شده است"</string>
<string name="data_usage_disabled_dialog" msgid="8453242888903772524">"چون به محدودیت داده تنظیم شده رسیدهاید، دستگاه مصرف داده را برای باقیمانده این دوره موقتاً متوقف کرده است.\n\nاگر ادامه دهید شاید موجب کسر هزینه از طرف شرکت مخابراتی شما شود."</string>
<string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"از سرگیری"</string>
- <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"اتصال اینترنتی وجود ندارد"</string>
+ <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"اتصال اینترنتی ندارید"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi متصل شد"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"جستجو برای GPS"</string>
<string name="gps_notification_found_text" msgid="4619274244146446464">"مکان تنظیم شده توسط GPS"</string>
@@ -384,19 +384,16 @@
<string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"دستگاه قفل باقی میماند تا زمانی که قفل آن را به صورت دستی باز کنید"</string>
<string name="hidden_notifications_title" msgid="7139628534207443290">"دریافت سریعتر اعلانها"</string>
<string name="hidden_notifications_text" msgid="2326409389088668981">"قبل از باز کردن قفل آنها را مشاهده کنید"</string>
- <string name="hidden_notifications_cancel" msgid="3690709735122344913">"خیر، سپاسگزارم"</string>
+ <string name="hidden_notifications_cancel" msgid="3690709735122344913">"نه سپاسگزارم"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"راهاندازی"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"مشاهده همه"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"پنهان کردن همه"</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">"اکنون به پایان برسد"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"بزرگ کردن"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"کوچک کردن"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"صفحه نمایش پین شد"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"تا زمانی که پین را بردارید، در نما نگهداشته میشود. برای برداشتن پین، برگشت و نمای کلی را به صورت همزمان لمس کنید و نگهدارید."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"تا زمانی که پین را بردارید، در نما نگهداشته میشود. برای برداشتن پین، نمای کلی را لمس کنید و نگهدارید."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"در نما نگهداشته میشود تا اینکه پین را بردارید. برای برداشتن پین، «برگشت» را لمس کنید و نگهدارید."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"متوجه شدم"</string>
- <string name="screen_pinning_negative" msgid="3741602308343880268">"خیر متشکرم"</string>
+ <string name="screen_pinning_negative" msgid="3741602308343880268">"نه سپاسگزارم"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> مخفی شود؟"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"دفعه بعد که آن را روشن کنید، در تنظیمات نشان داده میشود."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"پنهان کردن"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"اجازه ندارد"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> کنترلکننده صدا است"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"برای بازیابی کنترلکننده اصلی، لمس کنید."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">"،"</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"درحال استفاده از نمایه کاریتان هستید"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"تنظیمکننده واسط کاربری سیستم"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"نمایش درصد شارژ باتری جاسازی شده"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"ثانیههای ساعت را در نوار وضعیت نشان میدهد. ممکن است بر ماندگاری باتری تأثیر بگذارد."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"ترتیب مجدد در تنظیمات سریع"</string>
<string name="show_brightness" msgid="6613930842805942519">"نمایش روشنایی در تنظیمات سریع"</string>
- <string name="qs_paging" msgid="7020133150248666132">"استفاده از صفحهبندی در تنظیمات سریع"</string>
<string name="experimental" msgid="6198182315536726162">"آزمایشی"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"بلوتوث روشن شود؟"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"برای مرتبط کردن صفحهکلید با رایانه لوحی، ابتدا باید بلوتوث را روشن کنید."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"روشن کردن"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 655cf31..ef6a064 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Näytä ennen lukituksen avaamista"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Ei kiitos"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Määritä asetukset"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Näytä kaikki"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Piilota kaikki"</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">"Lopeta nyt"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Laajenna."</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Tiivistä."</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Näyttö on kiinnitetty"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Tämä pitää sen näkyvissä, kunnes poistat kiinnityksen. Kosketa Edellinen- ja Viimeisimmät-kohtaa samanaikaisesti pitkään kiinnityksen poistamiseksi."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Tämä pitää sen näkyvissä, kunnes poistat kiinnityksen. Kosketa Viimeisimmät-kohtaa pitkään kiinnityksen poistamiseksi."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Tämä pitää sen näkyvissä, kunnes peruutat kiinnityksen. Peruuta kiinnitys koskettamalla Edellinen-kohtaa pitkään."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Selvä"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Ei kiitos"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Piilotetaanko <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Estä"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> on äänenvoimakkuusvalinta."</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Palauta alkuperäinen koskettamalla."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Käytät työprofiilia."</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Näytä akun varaus kuvakkeessa"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Näytä sekunnit tilapalkin kellossa. Tämä voi vaikuttaa akun kestoon."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Järjestä pika-asetukset uudelleen"</string>
<string name="show_brightness" msgid="6613930842805942519">"Näytä kirkkaus pika-asetuksissa"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Käytä sivutusta pika-asetuksissa"</string>
<string name="experimental" msgid="6198182315536726162">"Kokeellinen"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Otetaanko Bluetooth käyttöön?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Jotta voit yhdistää näppäimistön tablettiisi, sinun on ensin otettava Bluetooth käyttöön."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Ota käyttöön"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 2f2c331..52d2e2a 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -150,9 +150,7 @@
<string name="accessibility_airplane_mode" msgid="834748999790763092">"Mode Avion"</string>
<string name="accessibility_no_sims" msgid="3957997018324995781">"Aucune carte SIM."</string>
<string name="accessibility_carrier_network_change_mode" msgid="4017301580441304305">"Modification du réseau du fournisseur de services"</string>
- <!-- String.format failed for translation -->
- <!-- no translation found for accessibility_battery_level (7451474187113371965) -->
- <skip />
+ <string name="accessibility_battery_level" msgid="7451474187113371965">"Pile : <xliff:g id="NUMBER">%d</xliff:g> pour cent"</string>
<string name="accessibility_settings_button" msgid="799583911231893380">"Paramètres système"</string>
<string name="accessibility_notifications_button" msgid="4498000369779421892">"Notifications"</string>
<string name="accessibility_remove_notification" msgid="3603099514902182350">"Supprimer la notification"</string>
@@ -388,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Afficher les notifications avant de déverrouiller l\'appareil"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Non, merci"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Configurer"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Tout afficher"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Tout masquer"</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">"Arrêter maintenant"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Développer"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Réduire"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"L\'écran est épinglé"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Cet écran est épinglé jusqu\'à ce que vous annuliez l\'opération. Pour annuler l\'épinglage, maintenez un doigt simultanément sur « Retour » et « Aperçu »."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Cet écran est épinglé jusqu\'à ce que vous annuliez l\'opération. Pour annuler l\'épinglage, maintenez le doigt sur « Aperçu »."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Cet écran est épinglé jusqu\'à ce que vous annuliez l\'opération. Pour annuler l\'épinglage, maintenez le doigt sur « Retour »."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"OK"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Non, merci"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Masquer <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -407,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Refuser"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> correspond à la boîte de dialogue du volume"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Touchez pour restaurer l\'original."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Vous utilisez votre profil professionnel."</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Afficher le pourcentage intégré de charge"</string>
@@ -441,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Afficher les secondes sur l\'horloge dans la barre d\'état. Cela peut réduire l\'autonomie de la pile."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Réorganiser les paramètres rapides"</string>
<string name="show_brightness" msgid="6613930842805942519">"Afficher la luminosité dans les paramètres rapides"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Utiliser la pagination dans les paramètres rapides"</string>
<string name="experimental" msgid="6198182315536726162">"Fonctions expérimentales"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Activer Bluetooth?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Pour connecter votre clavier à votre tablette, vous devez d\'abord activer la connectivité Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Activer"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 9f34431..39a8dbc 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -150,9 +150,7 @@
<string name="accessibility_airplane_mode" msgid="834748999790763092">"Mode Avion"</string>
<string name="accessibility_no_sims" msgid="3957997018324995781">"Aucune carte SIM"</string>
<string name="accessibility_carrier_network_change_mode" msgid="4017301580441304305">"Modification du réseau de l\'opérateur"</string>
- <!-- String.format failed for translation -->
- <!-- no translation found for accessibility_battery_level (7451474187113371965) -->
- <skip />
+ <string name="accessibility_battery_level" msgid="7451474187113371965">"Batterie : <xliff:g id="NUMBER">%d</xliff:g> pour cent"</string>
<string name="accessibility_settings_button" msgid="799583911231893380">"Paramètres système"</string>
<string name="accessibility_notifications_button" msgid="4498000369779421892">"Notifications"</string>
<string name="accessibility_remove_notification" msgid="3603099514902182350">"Supprimer la notification"</string>
@@ -327,8 +325,8 @@
<string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Priorité\nuniquement"</string>
<string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Alarmes\nuniquement"</string>
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Charge en cours… (chargé à 100 %% dans <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
- <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Charge rapide… (chargé à 100 % dans <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
- <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Charge lente… (chargé à 100 % dans <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Charge rapide… (chargé à 100 %% dans <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Charge lente… (chargé à 100 %% dans <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Changer d\'utilisateur"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Changer d\'utilisateur (utilisateur actuel : <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Utilisateur actuel : <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -388,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Afficher les notifications avant de déverrouiller l\'appareil"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Non, merci"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Configurer"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Tout afficher"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Tout masquer"</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">"Arrêter maintenant"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Développer"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Réduire"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Écran épinglé"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Cet écran est épinglé jusqu\'à annulation de l\'opération. Pour annuler l\'épinglage, appuyez simultanément sur \"Retour\" et \"Aperçu\" de manière prolongée."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Cet écran est épinglé jusqu\'à annulation de l\'opération. Pour annuler l\'épinglage, appuyez de manière prolongée sur \"Aperçu\"."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Cet écran est épinglé jusqu\'à l\'annulation de l\'opération. Pour annuler l\'épinglage, appuyez de manière prolongée sur \"Retour\"."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"OK"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Non, merci"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Masquer <xliff:g id="TILE_LABEL">%1$s</xliff:g> ?"</string>
@@ -407,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Refuser"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> correspond à la boîte de dialogue du volume"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Appuyez pour restaurer l\'interface d\'origine."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Vous utilisez votre profil professionnel."</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Afficher le pourcentage intégré de la batterie"</string>
@@ -441,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Afficher les secondes dans la barre d\'état. Cela risque de réduire l\'autonomie de la batterie."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Réorganiser la fenêtre de configuration rapide"</string>
<string name="show_brightness" msgid="6613930842805942519">"Afficher la luminosité dans fenêtre de configuration rapide"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Utiliser mise en page dans fenêtre de configuration rapide"</string>
<string name="experimental" msgid="6198182315536726162">"Paramètres expérimentaux"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Activer le Bluetooth ?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Pour connecter un clavier à votre tablette, vous devez avoir activé le Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Activer"</string>
</resources>
diff --git a/packages/SystemUI/res/values-gl-rES/strings.xml b/packages/SystemUI/res/values-gl-rES/strings.xml
index df90ef4..398a055 100644
--- a/packages/SystemUI/res/values-gl-rES/strings.xml
+++ b/packages/SystemUI/res/values-gl-rES/strings.xml
@@ -150,9 +150,7 @@
<string name="accessibility_airplane_mode" msgid="834748999790763092">"Modo avión"</string>
<string name="accessibility_no_sims" msgid="3957997018324995781">"Non hai tarxeta SIM"</string>
<string name="accessibility_carrier_network_change_mode" msgid="4017301580441304305">"Cambio de rede do operador."</string>
- <!-- String.format failed for translation -->
- <!-- no translation found for accessibility_battery_level (7451474187113371965) -->
- <skip />
+ <string name="accessibility_battery_level" msgid="7451474187113371965">"Carga da batería: <xliff:g id="NUMBER">%d</xliff:g> por cento."</string>
<string name="accessibility_settings_button" msgid="799583911231893380">"Configuración do sistema"</string>
<string name="accessibility_notifications_button" msgid="4498000369779421892">"Notificacións"</string>
<string name="accessibility_remove_notification" msgid="3603099514902182350">"Eliminar notificación."</string>
@@ -388,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Consúltaas antes de desbloquear"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Non grazas"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Configurar"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Ver todo"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Ocultar todo"</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">"Finalizar agora"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Ampliar"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Contraer"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"A pantalla está fixada"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"A pantalla manterase visible ata que anules a fixación. Para facelo, mantén premido Atrás e Visión xeral ao mesmo tempo."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"A pantalla manterase visible ata que anules a fixación. Para facelo, mantén premido Visión xeral."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"A pantalla manterase visible ata que anules a fixación. Para facelo, mantén premido Atrás."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"De acordo"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Non, grazas"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Queres ocultar <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -407,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Denegar"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> é o cadro de diálogo de volume"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Toca para restaurar o orixinal."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Estás usando o perfil de traballo"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Configurador da IU do sistema"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Mostrar porcentaxe de batería inserida"</string>
@@ -441,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Mostra os segundos do reloxo na barra de estado. Pode influír na duración da batería."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Reorganizar Configuración rápida"</string>
<string name="show_brightness" msgid="6613930842805942519">"Mostrar brillo en Configuración rápida"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Utilizar paxinación en Configuración rápida"</string>
<string name="experimental" msgid="6198182315536726162">"Experimental"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Queres activar o Bluetooth?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Para conectar o teu teclado co tablet, primeiro tes que activar o Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Activar"</string>
</resources>
diff --git a/packages/SystemUI/res/values-gu-rIN/strings.xml b/packages/SystemUI/res/values-gu-rIN/strings.xml
index 42ee460..9ecaa75 100644
--- a/packages/SystemUI/res/values-gu-rIN/strings.xml
+++ b/packages/SystemUI/res/values-gu-rIN/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"તમે અનલૉક કરો તે પહેલાં તેમને જુઓ"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"નહીં આભાર"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"સેટ અપ"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"બધું જુઓ"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"બધું છુપાવો"</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">"હવે સમાપ્ત કરો"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"વિસ્તૃત કરો"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"સંકુચિત કરો"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"સ્ક્રીન પિન કરેલ છે"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"તમે જ્યાં સુધી અનપિન કરશો નહીં ત્યાં સુધી આ તેને દૃશ્યમાં રાખે છે. અનપિન કરવા માટે બેકને ટચ કરો અને પકડો અને તે જ સમયે વિહંગાવલોકન કરો."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"તમે જ્યાં સુધી અનપિન કરશો નહીં ત્યાં સુધી આ તેને દૃશ્યમાં રાખે છે. અનપિન કરવા માટે વિહંગાવલોકનને ટચ કરો અને પકડો."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"તમે જ્યાં સુધી અનપિન કરશો નહીં ત્યાં સુધી આ તેને દૃશ્યમાં રાખે છે. અનપિન કરવા માટે પાછાં ને ટચ કરો અને પકડી રાખો."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"સમજાઈ ગયું"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"નહીં આભાર"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> ને છુપાવીએ?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"નકારો"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> એ વૉલ્યૂમ સંવાદ છે"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"મૂળને પુનઃસ્થાપિત કરવા માટે ટચ કરો."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"તમે તમારી કાર્ય પ્રોફાઇલનો ઉપયોગ કરી રહ્યાં છો"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"સિસ્ટમ UI ટ્યૂનર"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"એમ્બેડ કરેલ બૅટરી ટકા બતાવો"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"ઘડિયાળ સેકન્ડ સ્થિતિ બારમાં બતાવો. બૅટરીની આવરદા પર અસર કરી શકે છે."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"ઝડપી સેટિંગ્સને ફરીથી ગોઠવો"</string>
<string name="show_brightness" msgid="6613930842805942519">"ઝડપી સેટિંગ્સમાં તેજ બતાવો"</string>
- <string name="qs_paging" msgid="7020133150248666132">"ઝડપી સેટિંગ્સમાં પૃષ્ઠાંકનનો ઉપયોગ કરો"</string>
<string name="experimental" msgid="6198182315536726162">"પ્રાયોગિક"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Bluetooth ચાલુ કરવુ છે?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"તમારા ટેબ્લેટ સાથે કીબોર્ડ કનેક્ટ કરવા માટે, તમારે પહેલાં Bluetooth ચાલુ કરવાની જરૂર પડશે."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"ચાલુ કરો"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 86f536d..62ef606 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"आपके द्वारा उन्हें अनलॉक किए जाने से पहले देखें"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"नहीं धन्यवाद"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"सेट करें"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"सभी देखें"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"सभी छिपाएं"</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">"अब समाप्त करें"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"विस्तृत करें"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"संक्षिप्त करें"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"स्क्रीन पिन कर दी गई है"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"इससे वह तब तक दृश्य में रहता है जब तक कि आप उसे अनपिन नहीं कर देते. अनपिन करने के लिए वापस जाएं और अवलोकन करें को एक ही समय पर स्पर्श करके रखें."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"इससे वह तब तक दृश्य में बना रहता है जब तक कि आप उसे अनपिन नहीं कर देते. अनपिन करने के लिए अवलोकन करें को स्पर्श करके रखें."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"इससे वह तब तक दृश्य में बना रहता है जब तक कि आप उसे अनपिन नहीं कर देते. अनपिन करने के लिए वापस जाएं को स्पर्श करके रखें."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"समझ लिया"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"नहीं, रहने दें"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> को छिपाएं?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"अस्वीकार करें"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> वॉल्यूम संवाद है"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"मूल वॉल्यूम को फिर से लाने के लिए स्पर्श करें."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"आप अपनी कार्य प्रोफ़ाइल का उपयोग कर रहे हैं"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"सिस्टम UI ट्यूनर"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"एम्बेड किया गया बैटरी प्रतिशत दिखाएं"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"स्थिति बार में घड़ी के सेकंड दिखाएं. इससे बैटरी के जीवनकाल पर प्रभाव पड़ सकता है."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"त्वरित सेटिंग को पुन: व्यवस्थित करें"</string>
<string name="show_brightness" msgid="6613930842805942519">"त्वरित सेटिंग में स्क्रीन की रोशनी दिखाएं"</string>
- <string name="qs_paging" msgid="7020133150248666132">"त्वरित सेटिंग में पेजिंग का उपयोग करें"</string>
<string name="experimental" msgid="6198182315536726162">"प्रयोगात्मक"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"ब्लूटूथ चालू करें?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"अपने कीबोर्ड को अपने टैबलेट से कनेक्ट करने के लिए, आपको पहले ब्लूटूथ चालू करना होगा."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"चालू करें"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 043e7ad..dafc96f 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -387,15 +387,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Pogledajte ih prije otključavanja"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Ne, hvala"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Postavi"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Pokaži sve"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Sakrij sve"</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">"Prekini sada"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Proširivanje"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Sažimanje"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Zaslon je prikvačen"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Zaslon će tako ostati u prvom planu dok ga ne otkvačite. Istovremeno dodirnite i držite Natrag i Pregled da biste ga otkvačili."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Zaslon će tako ostati u prvom planu dok ga ne otkvačite. Dodirnite i držite Pregled da biste ga otkvačili."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Zaslon će tako ostati u prvom planu dok ga ne otkvačite. Dodirnite i držite Natrag da biste ga otkvačili."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Shvaćam"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Ne, hvala"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Želite li sakriti pločicu <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -406,6 +403,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Odbij"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> predstavlja dijaloški okvir za upravljanje glasnoćom"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Dodirnite da biste vratili izvorno."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Upotrebljavate radni profil"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Ugađanje korisničkog sučelja sustava"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Prikaži ugrađeni postotak baterije"</string>
@@ -440,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Prikazuju se sekunde na satu na traci statusa. Može utjecati na trajanje baterije."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Promijeni raspored Brzih postavki"</string>
<string name="show_brightness" msgid="6613930842805942519">"Prikaži svjetlinu u Brzim postavkama"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Upotrijebi redni broj stranice u Brzim postavkama"</string>
<string name="experimental" msgid="6198182315536726162">"Eksperimentalno"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Želite li uključiti Bluetooth?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Da biste povezali tipkovnicu s tabletom, morate uključiti Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Uključi"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index e2b0812..cf08b55 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Már a képernyőzár feloldása előtt megtekintheti őket"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Nem, köszönöm"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Beállítás"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Az összes megjelenítése"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Az összes elrejtése"</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">"Befejezés most"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Kibontás"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Összecsukás"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"A képernyő rögzítve van"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Megjelenítve tartja addig, amíg Ön fel nem oldja fel a rögzítést. A rögzítés feloldásához tartsa egyszerre lenyomva a Vissza és az Áttekintés lehetőséget."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Megjelenítve tartja addig, amíg Ön fel nem oldja a rögzítést. A feloldáshoz tartsa lenyomva az Áttekintés lehetőséget."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Megjelenítve tartja addig, amíg Ön fel nem oldja a rögzítést. A feloldáshoz tartsa lenyomva a Vissza lehetőséget."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Értem"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Nem, köszönöm"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Elrejti ezt: <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Elutasítás"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazás kezeli a hangerőt"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Érintse meg az eredeti érték visszaállításához."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"A munkaprofilt használja"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Kezelőfelület-hangoló"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"A beépített akkumulátor töltöttségi szintjének megjelenítése"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Másodpercek megjelenítése az állapotsor óráján. Ez hatással lehet az akkumulátor üzemidejére."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Gyorsbeállítások átrendezése"</string>
<string name="show_brightness" msgid="6613930842805942519">"Fényerő megjelenítése a gyorsbeállításokban"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Oldalelrendezés használata a gyorsbeállításokban"</string>
<string name="experimental" msgid="6198182315536726162">"Kísérleti"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Engedélyezi a Bluetooth-kapcsolatot?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Ha a billentyűzetet csatlakoztatni szeretné táblagépéhez, először engedélyeznie kell a Bluetooth-kapcsolatot."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Bekapcsolás"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml
index b69fc0f..b4a265d 100644
--- a/packages/SystemUI/res/values-hy-rAM/strings.xml
+++ b/packages/SystemUI/res/values-hy-rAM/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Տեսեք դրանք մինչև ապակողպելը"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Ոչ, շնորհակալություն"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Կարգավորել"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Տեսնել բոլորը"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Թաքցնել բոլորը"</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">"Ավարտել"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Ընդարձակել"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Կոծկել"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Էկրանն ամրացված է"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Էկրանը կմնա տեսադաշտում, մինչև այն ապամրացնեք: Ապամրացնելու համար միաժամանակ հպեք և պահեք Համատեսքի և Հետ կոճակները:"</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Էկրանը կմնա տեսադաշտում, մինչև այն ապամրացնեք: Ապամրացնելու համար հպեք և պահեք Համատեսքի կոճակը:"</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Էկրանը կմնա տեսադաշտում, մինչև այն ապամրացնեք: Ապամրացնելու համար հպեք և պահեք Հետ կոճակը:"</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Հասկանալի է"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Ոչ, շնորհակալություն"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Թաքցնե՞լ <xliff:g id="TILE_LABEL">%1$s</xliff:g>-ը:"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Մերժել"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ը ձայնի ուժգնության երկխոսության հավելված է"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Դիպչեք՝ սկզբնօրինակը վերականգնելու համար:"</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Դուք օգտագործում եք ձեր աշխատանքային պրոֆիլը"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Համակարգի ՕՄ-ի կարգավորիչ"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Ցուցադրել ներկառուցված մարտկոցի տոկոսայնությունը"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Ցույց տալ ժամացույցի վայրկյանները կարգավիճակի տողում: Կարող է ազդել մարտկոցի աշխատանքի ժամանակի վրա:"</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Վերադասավորել Արագ կարգավորումները"</string>
<string name="show_brightness" msgid="6613930842805942519">"Ցույց տալ պայծառությունն Արագ կարգավորումներում"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Օգտագործել էջերի դասավորությունը Արագ կարգավորումներում"</string>
<string name="experimental" msgid="6198182315536726162">"Փորձնական"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Միացնե՞լ Bluetooth-ը:"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Ստեղնաշարը ձեր պլանշետին միացնելու համար նախ անհրաժեշտ է միացնել Bluetooth-ը:"</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Միացնել"</string>
</resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 7dbcda9..6d6c1c7 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Lihat sebelum membuka kunci"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Tidak"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Siapkan"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Lihat semua"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Sembunyikan semua"</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">"Akhiri sekarang"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Luaskan"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Ciutkan"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Layar dipasangi pin"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Ini akan terus ditampilkan sampai Anda melepas pin. Sentuh dan tahan tombol Kembali dan Ringkasan secara bersamaan untuk melepas pin."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Ini akan terus ditampilkan sampai Anda melepas pin. Sentuh dan tahan tombol Ringkasan untuk melepas pin."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Ini akan terus ditampilkan sampai Anda melepas pin. Sentuh dan tahan tombol Kembali untuk melepas pin."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Mengerti"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Lain kali"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Sembunyikan <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Tolak"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> adalah dialog volume"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Sentuh untuk memulihkan aslinya."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Anda menggunakan profil kerja"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Penyetel Antarmuka Pengguna Sistem"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Tampilkan persentase baterai yang tersemat"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Tampilkan detik jam di bilah status. Dapat memengaruhi masa pakai baterai."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Atur Ulang Setelan Cepat"</string>
<string name="show_brightness" msgid="6613930842805942519">"Tampilkan kecerahan di Setelan Cepat"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Gunakan pembagian laman di Setelan Cepat"</string>
<string name="experimental" msgid="6198182315536726162">"Eksperimental"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Aktifkan Bluetooth?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Untuk menghubungkan keyboard dengan tablet, terlebih dahulu aktifkan Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Aktifkan"</string>
</resources>
diff --git a/packages/SystemUI/res/values-is-rIS/strings.xml b/packages/SystemUI/res/values-is-rIS/strings.xml
index b28b6fd..8e7711e4 100644
--- a/packages/SystemUI/res/values-is-rIS/strings.xml
+++ b/packages/SystemUI/res/values-is-rIS/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Sjáðu þær áður en þú opnar"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Nei, takk"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Setja upp"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Sjá allt"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Fela allt"</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">"Hætta núna"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Stækka"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Minnka"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Skjárinn er festur"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Þetta heldur þessu opnu þangað til þú losar. Haltu bakk- og yfirlitshnöppunum inni á sama tíma til að losa."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Þetta heldur þessu opnu þangað til þú losar. Haltu yfirlitshnappinum inni til að losa."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Þetta heldur þessu opnu þangað til þú losar. Haltu „Til baka“ inni til að losa."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Ég skil"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Nei, takk"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Fela <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Hafna"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> er hljóðstyrksvalmyndin"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Snertu til að færa í upprunalegt horf."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Þú ert að nota vinnusniðið"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Fínstillingar kerfisviðmóts"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Sýna innfellda rafhlöðustöðu"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Sýna sekúndur á klukku í stöðustikunni. Getur haft áhrif á endingu rafhlöðu."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Endurraða flýtistillingum"</string>
<string name="show_brightness" msgid="6613930842805942519">"Sýna birtustig í flýtistillingum"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Nota síðuskoðun í flýtistillingum"</string>
<string name="experimental" msgid="6198182315536726162">"Tilraunastillingar"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Kveikja á Bluetooth?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Til að geta tengt lyklaborðið við spjaldtölvuna þarftu fyrst að kveikja á Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Kveikja"</string>
</resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 54e6f56..f12e626 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -150,9 +150,7 @@
<string name="accessibility_airplane_mode" msgid="834748999790763092">"Modalità aereo."</string>
<string name="accessibility_no_sims" msgid="3957997018324995781">"Nessuna SIM presente."</string>
<string name="accessibility_carrier_network_change_mode" msgid="4017301580441304305">"Cambio rete operatore."</string>
- <!-- String.format failed for translation -->
- <!-- no translation found for accessibility_battery_level (7451474187113371965) -->
- <skip />
+ <string name="accessibility_battery_level" msgid="7451474187113371965">"Batteria: <xliff:g id="NUMBER">%d</xliff:g> percento."</string>
<string name="accessibility_settings_button" msgid="799583911231893380">"Impostazioni di sistema."</string>
<string name="accessibility_notifications_button" msgid="4498000369779421892">"Notifiche."</string>
<string name="accessibility_remove_notification" msgid="3603099514902182350">"Cancella notifica."</string>
@@ -388,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Visualizza prima di sbloccare"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"No, grazie"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Configura"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Mostra tutto"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Nascondi tutto"</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">"Termina adesso"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Espandi"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Comprimi"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"La schermata è bloccata"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"La schermata rimane visibile finché la sblocchi. Tocca e tieni premuti contemporaneamente Indietro e Panoramica per sbloccare."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"La schermata rimane visibile finché la sblocchi. Tocca Panoramica e tieni premuto per sbloccare."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"La schermata rimane visibile finché non la sblocchi. Tocca Panoramica e tieni premuto Indietro per sbloccare."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"OK"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"No, grazie"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Nascondere <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -407,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Nega"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> rappresenta la finestra di dialogo relativa al volume"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Tocca per ripristinare l\'originale."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Stai utilizzando il profilo di lavoro"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Sintetizzatore interfaccia utente di sistema"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Mostra percentuale batteria incorporata"</string>
@@ -441,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Mostra i secondi nell\'orologio nella barra di stato. Ciò potrebbe ridurre la durata della carica della batteria."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Riorganizza Impostazioni rapide"</string>
<string name="show_brightness" msgid="6613930842805942519">"Mostra luminosità in Impostazioni rapide"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Utilizza nuovo layout in Impostazioni rapide"</string>
<string name="experimental" msgid="6198182315536726162">"Sperimentali"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Attivare il Bluetooth?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Per connettere la tastiera al tablet, devi prima attivare il Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Attiva"</string>
</resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index c959feb..c168f7d 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -388,15 +388,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"צפה בהן לפני שתבטל נעילה"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"לא, תודה"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"הגדר"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"הצג הכל"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"הסתר הכל"</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">"סיים כעת"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"הרחב"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"כווץ"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"המסך מוצמד"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"שומר בתצוגה עד לביטול ההצמדה. גע והחזק בו-זמנית ב\'הקודם\' ו\'סקירה\' כדי לבטל הצמדה."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"שומר בתצוגה עד לביטול ההצמדה. גע והחזק בו-זמנית ב\'הקודם\' ו\'סקירה\' כדי לבטל הצמדה."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"שומר בתצוגה עד לביטול ההצמדה. גע והחזק בו-זמנית ב\'הקודם\' כדי לבטל הצמדה."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"הבנתי"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"לא, תודה"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"להסתיר<xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -407,6 +404,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"דחה"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> הוא תיבת הדו-שיח של עוצמת הקול"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"גע כדי לשחזר את עוצמת הקול המקורית."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"אתה משתמש בפרופיל העבודה שלך"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"הצג בשורת הסטטוס את אחוז עוצמת הסוללה"</string>
@@ -441,6 +439,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"הצג שניות בשעון בשורת הסטטוס. פעולה זו עשויה להשפיע על אורך חיי הסוללה."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"סידור מחדש של הגדרות מהירות"</string>
<string name="show_brightness" msgid="6613930842805942519">"הצג בהירות בהגדרות מהירות"</string>
- <string name="qs_paging" msgid="7020133150248666132">"השתמש באפשרות הדפדוף בהגדרות המהירות"</string>
<string name="experimental" msgid="6198182315536726162">"ניסיוניות"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"האם להפעיל את ה-Bluetooth?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"כדי לחבר את המקלדת לטאבלט, תחילה עליך להפעיל את ה-Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"הפעל"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 29476d8..f053ee4 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -150,9 +150,7 @@
<string name="accessibility_airplane_mode" msgid="834748999790763092">"機内モード。"</string>
<string name="accessibility_no_sims" msgid="3957997018324995781">"SIMカードが挿入されていません。"</string>
<string name="accessibility_carrier_network_change_mode" msgid="4017301580441304305">"携帯通信会社のネットワークを変更します。"</string>
- <!-- String.format failed for translation -->
- <!-- no translation found for accessibility_battery_level (7451474187113371965) -->
- <skip />
+ <string name="accessibility_battery_level" msgid="7451474187113371965">"電池残量: <xliff:g id="NUMBER">%d</xliff:g>パーセント"</string>
<string name="accessibility_settings_button" msgid="799583911231893380">"システム設定。"</string>
<string name="accessibility_notifications_button" msgid="4498000369779421892">"通知。"</string>
<string name="accessibility_remove_notification" msgid="3603099514902182350">"通知を消去。"</string>
@@ -388,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"ロックを解除する前にご確認ください"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"キャンセル"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"設定"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"すべて表示"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"すべて非表示"</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">"今すぐ終了"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"展開"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"折りたたむ"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"画面が固定されました"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"固定を解除するまで画面が常に表示されるようになります。[戻る]と[最近]を同時に押し続けると固定が解除されます。"</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"固定を解除するまで画面が常に表示されるようになります。[最近]を押し続けると固定が解除されます。"</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"固定を解除するまで画面が常に表示されるようになります。[戻る]を押し続けると固定が解除されます。"</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"はい"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"いいえ"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g>を非表示にしますか?"</string>
@@ -407,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"許可しない"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g>を音量ダイアログとして使用"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"タップすると元の音量ダイアログが復元されます。"</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">"、"</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"仕事用プロファイルを使用しています"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"システムUI調整ツール"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"内蔵電池の残量の割合を表示する"</string>
@@ -441,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"ステータスバーに時計の秒を表示します。電池使用量に影響する可能性があります。"</string>
<string name="qs_rearrange" msgid="8060918697551068765">"クイック設定を並べ替え"</string>
<string name="show_brightness" msgid="6613930842805942519">"クイック設定に明るさ調整バーを表示する"</string>
- <string name="qs_paging" msgid="7020133150248666132">"クイック設定でページ設定を使用する"</string>
<string name="experimental" msgid="6198182315536726162">"試験運用版"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"BluetoothをONにしますか?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"タブレットでキーボードに接続するには、最初にBluetoothをONにする必要があります。"</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"ONにする"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml
index d83c9e9..8d754c4 100644
--- a/packages/SystemUI/res/values-ka-rGE/strings.xml
+++ b/packages/SystemUI/res/values-ka-rGE/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"იხილეთ განბლოკვამდე"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"არა, გმადლობთ"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"დაყენება"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"ყველას ჩვენება"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"ყველას დამალვა"</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">"გამორთვა"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"გავრცობა"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"ჩაკეცვა"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"ეკრანი ჩამაგრებულია"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"ამით ის ხედში დარჩება, სანამ ჩამაგრებას არ გააუქმებთ. ჩამაგრების გასაუქმებლად შეეხეთ და დააყოვნეთ „დაბრუნება“-ზე და „მიმოხილვა“-ზე ერთდროულად."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"ამით ის ხედში დარჩება, სანამ ჩამაგრებას არ გააუქმებთ. ჩამაგრების გასაუქმებლად შეეხეთ და დააყოვნეთ „მიმოხილვა“-ზე."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"ამით ის ხედში ჩამაგრების მოხსნამდე დარჩება. ჩამაგრების მოსახსნელად, ხანგრძლივად დააჭირეთ „უკან“-ს."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"გასაგებია"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"არა, გმადლობთ"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"დაიმალოს <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"უარყოფა"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> ხმოვან დიალოგშია"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"ორიგინალის აღდგენისათვის, შეეხეთ."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"თქვენ სამსახურის პროფილს იყენებთ"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"სისტემის UI ტუნერი"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"ჩამაგრებული ბატარეის პროცენტის ჩვენება"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"საათის წამების ჩვენება სტატუსის ზოლში. შეიძლება გავლენა იქონიოს ბატარეაზე."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"სწრაფი პარამეტრების გადაწყობა"</string>
<string name="show_brightness" msgid="6613930842805942519">"სიკაშკაშის ჩვენება სწრაფ პარამეტრებში"</string>
- <string name="qs_paging" msgid="7020133150248666132">"გამოიყენეთ გვერდების დანომვრა სწრაფ პარამეტრებში"</string>
<string name="experimental" msgid="6198182315536726162">"ექსპერიმენტული"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"გსურთ Bluetooth-ის ჩართვა?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"კლავიატურის ტაბლეტთან დასაკავშირებლად, ჯერ უნდა ჩართოთ Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"ჩართვა"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kk-rKZ/strings.xml b/packages/SystemUI/res/values-kk-rKZ/strings.xml
index 5c066b3..d0bd8bb 100644
--- a/packages/SystemUI/res/values-kk-rKZ/strings.xml
+++ b/packages/SystemUI/res/values-kk-rKZ/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Бекітпесін ашу алдында оларды көру"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Жоқ, рақмет"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Реттеу"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Барлығын көру"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Барлығын жасыру"</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">"Қазір өшіру"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Жаю"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Жию"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Экран түйрелді"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Бұл сіз оны босатқанша оны көрсетіп тұрады. Босату үшін «Кері» және «Шолу» түймелерін бір уақытта басып тұрыңыз."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Бұл сіз оны босатқанша оны көрсетіп тұрады. Босату үшін «Шолу» түймесін бір уақытта басып тұрыңыз."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Бұл сіз оны босатқанша оны көрсетіп тұрады. Босату үшін «Кері» түймесін басып тұрыңыз."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Түсіндім"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Жоқ, рақмет"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> жасыру керек пе?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Өшіру"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> — көлем диалогтық терезесі"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Түпнұсқаны қалпына келтіру үшін түртіңіз."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Сіз жұмыс профиліңізді пайдаланып жатырсыз"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Жүйелік пайдаланушылық интерфейс тюнері"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Ендірілген батарея пайыздық шамасын көрсету"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Күйін көрсету жолағында сағат секундтарын көрсету. Батареяның қызмет көрсету мерзіміне әсер етуі мүмкін."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Жылдам параметрлерді қайта реттеу"</string>
<string name="show_brightness" msgid="6613930842805942519">"Жылдам параметрлерде жарықтықты көрсету"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Жылдам параметрлерде беттерді нөмірлеуді пайдалану"</string>
<string name="experimental" msgid="6198182315536726162">"Эксперименттік"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Bluetooth функциясын қосу керек пе?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Пернетақтаны планшетке қосу үшін алдымен Bluetooth функциясын қосу керек."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Қосу"</string>
</resources>
diff --git a/packages/SystemUI/res/values-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml
index 4c469b0..d77f480 100644
--- a/packages/SystemUI/res/values-km-rKH/strings.xml
+++ b/packages/SystemUI/res/values-km-rKH/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"ឃើញពួកវាមុនពេលដោះសោ"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"ទេ អរគុណ!"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"រៀបចំ"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"មើលទាំងអស់"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"លាក់ទាំងអស់"</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">"បញ្ចប់ឥឡូវនេះ"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"ពង្រីក"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"បង្រួម"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"អេក្រង់ត្រូវបានភ្ជាប់"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"រក្សាទុកវាក្នុងទិដ្ឋភាពរហូតដល់អ្នកផ្ដាច់។ ប៉ះ ហើយសង្កត់ថយក្រោយ និងទិដ្ឋភាពនៅពេលតែមួយដើម្បីផ្ដាច់។"</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"វារក្សាទុកក្នុងទិដ្ឋភាពរហូតដល់អ្នកផ្ដាច់។ ប៉ះ និងសង្កត់ទិដ្ឋភាពដើម្បីផ្ដាច់។"</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"វានឹងនៅតែបង្ហាញ លុះត្រាតែអ្នកដកការដៅចេញ។ សូមប៉ះ និងសង្កត់ឲ្យជាប់ដើម្បីដកការដៅ។"</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"យល់ហើយ"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"ទេ អរគុណ"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"លាក់ <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"បដិសេធ"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> គឺជាប្រអប់សម្លេង"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"ប៉ះដើម្បីស្តារច្បាប់ដើម។"</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"អ្នកកំពុងប្រើប្រវត្តិរូបការងាររបស់អ្នក"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"កម្មវិធីសម្រួល UI ប្រព័ន្ធ"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"បង្ហាញភាគរយថាមពលថ្មដែលបានបង្កប់"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"បង្ហាញវិនាទីនៅលើរបារស្ថានភាពអាចនឹងប៉ះពាល់ដល់ថាមពលថ្ម។"</string>
<string name="qs_rearrange" msgid="8060918697551068765">"រៀបចំការកំណត់រហ័សឡើងវិញ"</string>
<string name="show_brightness" msgid="6613930842805942519">"បង្ហាញកម្រិតពន្លឺនៅក្នុងការកំណត់រហ័ស"</string>
- <string name="qs_paging" msgid="7020133150248666132">"ប្រើការចុះទំព័រនៅក្នុងការកំណត់រហ័ស"</string>
<string name="experimental" msgid="6198182315536726162">"ពិសោធន៍"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"បើកប៊្លូធូសឬ?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"ដើម្បីភ្ជាប់ក្តារចុចរបស់អ្នកជាមួយនឹងថេប្លេតរបស់អ្នក អ្នកត្រូវតែបើកប៊្លូធូសជាមុនសិន។"</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"បើក"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kn-rIN/strings.xml b/packages/SystemUI/res/values-kn-rIN/strings.xml
index 4215a91..3b9dfc2 100644
--- a/packages/SystemUI/res/values-kn-rIN/strings.xml
+++ b/packages/SystemUI/res/values-kn-rIN/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"ನೀವು ಅನ್ಲಾಕ್ ಮಾಡುವ ಮೊದಲೇ ಅವುಗಳನ್ನು ನೋಡಿ"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"ಧನ್ಯವಾದಗಳು"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"ಹೊಂದಿಸು"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"ಎಲ್ಲವನ್ನೂ ನೋಡಿ"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"ಎಲ್ಲ ಮರೆಮಾಡಿ"</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">"ಈಗಲೇ ಅಂತ್ಯಗೊಳಿಸು"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"ವಿಸ್ತರಿಸು"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"ಸಂಕುಚಿಸು"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"ಪರದೆಯನ್ನು ಪಿನ್ ಮಾಡಲಾಗಿದೆ"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"ನೀವು ಅನ್ಪಿನ್ ಮಾಡುವವರೆಗೆ ಅದನ್ನು ವೀಕ್ಷಣೆಯಲ್ಲಿಡುತ್ತದೆ. ಅನ್ಪಿನ್ ಮಾಡಲು ಒಂದೇ ಸಮಯದಲ್ಲಿ ಸ್ಪರ್ಶಿಸಿ ಮತ್ತು ಒತ್ತಿ ಹಿಡಿದುಕೊಳ್ಳಿ ಹಾಗೂ ಅವಲೋಕಿಸಿ."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"ನೀವು ಅನ್ಪಿನ್ ಮಾಡುವವರೆಗೆ ಅದನ್ನು ವೀಕ್ಷಣೆಯಲ್ಲಿಡುತ್ತದೆ. ಅನ್ಪಿನ್ ಮಾಡಲು ಅವಲೋಕನವನ್ನು ಸ್ಪರ್ಶಿಸಿ ಮತ್ತು ಹಿಡಿದುಕೊಳ್ಳಿ."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"ನೀವು ಅನ್ಪಿನ್ ಮಾಡುವವರೆಗೆ ಅದನ್ನು ವೀಕ್ಷಣೆಯಲ್ಲಿಡುತ್ತದೆ. ಅನ್ಪಿನ್ ಮಾಡಲು ಹಿಂದೆ ಸ್ಪರ್ಶಿಸಿ ಮತ್ತು ಹಿಡಿದುಕೊಳ್ಳಿ."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"ತಿಳಿಯಿತು"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"ಧನ್ಯವಾದಗಳು"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> ಮರೆಮಾಡುವುದೇ?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"ನಿರಾಕರಿಸು"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> ವಾಲ್ಯೂಮ್ ಸಂವಾದವಾಗಿದೆ"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"ಮೂಲ ಮರುಸ್ಥಾಪಿಸಲು ಸ್ಪರ್ಶಿಸಿ."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"ನಿಮ್ಮ ಕೆಲಸದ ಪ್ರೊಫೈಲ್ ಅನ್ನು ನೀವು ಬಳಸುತ್ತಿರುವಿರಿ"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"ಸಿಸ್ಟಮ್ UI ಟ್ಯೂನರ್"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"ಎಂಬೆಡ್ ಮಾಡಲಾದ ಬ್ಯಾಟರಿ ಶೇಕಡಾ ತೋರಿಸಿ"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"ಸ್ಥಿತಿ ಪಟ್ಟಿಯಲ್ಲಿ ಗಡಿಯಾರ ಸೆಕೆಂಡುಗಳನ್ನು ತೋರಿಸು. ಇದಕ್ಕೆ ಬ್ಯಾಟರಿ ಬಾಳಿಕೆಯು ಪರಿಣಾಮಬೀರಬಹುದು."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಮರುಹೊಂದಿಸಿ"</string>
<string name="show_brightness" msgid="6613930842805942519">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್ಗಳಲ್ಲಿ ಪ್ರಖರತೆಯನ್ನು ತೋರಿಸಿ"</string>
- <string name="qs_paging" msgid="7020133150248666132">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್ಗಳಲ್ಲಿ ಪುಟಗಳನ್ನು ಬಳಸಿ"</string>
<string name="experimental" msgid="6198182315536726162">"ಪ್ರಾಯೋಗಿಕ"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"ಬ್ಲೂಟೂತ್ ಆನ್ ಮಾಡುವುದೇ?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"ನಿಮ್ಮ ಕೀಬೋರ್ಡ್ ಅನ್ನು ಟ್ಯಾಬ್ಲೆಟ್ಗೆ ಸಂಪರ್ಕಿಸಲು, ನೀವು ಮೊದಲು ಬ್ಲೂಟೂತ್ ಆನ್ ಮಾಡಬೇಕಾಗುತ್ತದೆ."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"ಆನ್ ಮಾಡು"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 8ee5946..a5f14e4 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"잠금 해제하기 전에 알림을 봅니다."</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"사용 안함"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"설정"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"모두 보기"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"모두 숨기기"</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">"지금 종료"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"펼치기"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"접기"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"화면 고정됨"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"고정 해제하기 전까지 계속 표시됩니다. 고정 해제하려면 뒤로와 최근 사용을 동시에 길게 터치합니다."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"고정 해제하기 전까지 계속 표시됩니다. 고정 해제하려면 최근 사용을 길게 터치합니다."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"고정 해제하기 전까지 계속 표시됩니다. 고정 해제하려면 뒤로 버튼을 길게 터치합니다."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"확인"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"거부"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g>을(를) 숨기시겠습니까?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"거부"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g>은(는) 볼륨 대화입니다."</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"원본을 복원하려면 터치하세요."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"직장 프로필을 사용하고 있습니다."</string>
<string name="system_ui_tuner" msgid="708224127392452018">"시스템 UI 튜너"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"내장형 배터리 잔량 비율 표시"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"상태 표시줄에 시계 초 단위를 표시합니다. 배터리 수명에 영향을 줄 수도 있습니다."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"빠른 설정 재정렬"</string>
<string name="show_brightness" msgid="6613930842805942519">"빠른 설정에서 밝기 표시"</string>
- <string name="qs_paging" msgid="7020133150248666132">"빠른 설정에서 페이지 레이아웃 사용"</string>
<string name="experimental" msgid="6198182315536726162">"베타"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"블루투스를 켜시겠습니까?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"키보드를 태블릿에 연결하려면 먼저 블루투스를 켜야 합니다."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"사용"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ky-rKG/strings.xml b/packages/SystemUI/res/values-ky-rKG/strings.xml
index ce0afd4..2d6dc6f 100644
--- a/packages/SystemUI/res/values-ky-rKG/strings.xml
+++ b/packages/SystemUI/res/values-ky-rKG/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Аларды кулпудан чыгараардан мурун көрүңүз"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Жок, рахмат"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Орнотуу"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Бардыгын көрүү"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Баарын жашыруу"</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">"Азыр бүтүрүү"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Жайып көрсөтүү"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Жыйнап коюу"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Экран кадалган"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Бул бошотулмайынча көрүнө берет. Бошотуу үчүн, бир убакта Артка жана Карап чыгууну коё бербей басып туруңуз."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Бул бошотулмайынча көрүнө берет. Бошотуу үчүн, Карап чыгууну коё бербей басып туруңуз."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Бул бошотулмайынча көрүнө берет. Бошотуу үчүн, \"Артка\" баскычын басып туруңуз."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Түшүндүм"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Жок, рахмат"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> жашырылсынбы?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Жок"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> үндү катуулатуу диалогу"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Түпнусканы калыбына келтирүү үчүн тийип коюңуз."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Жумуш профилиңизди колдонуп жатасыз"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Батарянын кубатнын деңгээли пайыз менен көрсөтлсүн"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Абал тилкесинен сааттын секунддары көрсөтүлсүн. Батареянын кубаты көбүрөөк сарпталышы мүмкүн."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Ыкчам жөндөөлөрдү кайра коюу"</string>
<string name="show_brightness" msgid="6613930842805942519">"Ыкчам жөндөөлөрдөн жарык деңгээлин көрсөтүү"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Ыкчам жөндөөлөрдөн баракты номерлөөнү колдонуу"</string>
<string name="experimental" msgid="6198182315536726162">"Сынамык"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Bluetooth күйгүзүлсүнбү?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Баскычтобуңузду планшетиңизге туташтыруу үчүн, адегенде Bluetooth\'ту күйгүзүшүңүз керек."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Күйгүзүү"</string>
</resources>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 6ef1ada..456391d 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -22,4 +22,7 @@
<!-- Standard notification width + gravity -->
<dimen name="notification_panel_width">@dimen/standard_notification_panel_width</dimen>
<integer name="notification_panel_layout_gravity">@integer/standard_notification_panel_layout_gravity</integer>
+
+ <dimen name="docked_divider_handle_width">2dp</dimen>
+ <dimen name="docked_divider_handle_height">24dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-land/styles.xml b/packages/SystemUI/res/values-land/styles.xml
index 8919198..dd396d9 100644
--- a/packages/SystemUI/res/values-land/styles.xml
+++ b/packages/SystemUI/res/values-land/styles.xml
@@ -18,4 +18,15 @@
<style name="BrightnessDialogContainer" parent="@style/BaseBrightnessDialogContainer">
<item name="android:layout_width">360dp</item>
</style>
+
+ <style name="DockedDividerBackground">
+ <item name="android:layout_width">12dp</item>
+ <item name="android:layout_height">match_parent</item>
+ <item name="android:layout_gravity">center_horizontal</item>
+ </style>
+
+ <style name="DockedDividerHandle">
+ <item name="android:layout_gravity">center_vertical</item>
+ </style>
+
</resources>
diff --git a/packages/SystemUI/res/values-lo-rLA/strings.xml b/packages/SystemUI/res/values-lo-rLA/strings.xml
index b45395e..82a82e6 100644
--- a/packages/SystemUI/res/values-lo-rLA/strings.xml
+++ b/packages/SystemUI/res/values-lo-rLA/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"ເບິ່ງພວກມັນກ່ອນທ່ານຈະປົດລັອກ"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"ບໍ່, ຂອບໃຈ"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"ຕັ້ງຄ່າ"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"ເບິ່ງທັງໝົດ"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"ເຊື່ອງທັງຫມົດ"</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">"ຢຸດດຽວນີ້"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"ຂະຫຍາຍ"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"ຫຍໍ້ລົງ"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"ປັກໝຸດໜ້າຈໍແລ້ວ"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"ມັນຈະຮັກສາໜ້າຈໍໄວ້ໃນມຸມມອງຂອງທ່ານຈົນກວ່າທ່ານຈະຖອດໝຸດ. ແຕະປຸ່ມ ກັບຄືນ ແລະ ພາບຮວມ ຄ້າງໄວ້ພ້ອມກັນເພື່ອຖອດໝຸດ."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"ມັນຈະຮັກສາໜ້າຈໍໄວ້ໃນມຸມມອງຂອງທ່ານຈົນກວ່າທ່ານຈະຖອດໝຸດ. ແຕະປຸ່ມ ພາບຮວມ ຄ້າງໄວ້ເພື່ອຖອດໝຸດ."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"ອັນນີ້ຮັກສາມັນໄວ້ໃນມຸມມອງຂອງທ່ານຈົນກວ່າທ່ານຖອດໝຸດ. ແຕະປຸ່ມ ກັບຄືນ ຄ້າງໄວ້ເພື່ອຖອດໝຸດ."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"ເຂົ້າໃຈແລ້ວ"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"ບໍ່, ຂອບໃຈ"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"ເຊື່ອງ <xliff:g id="TILE_LABEL">%1$s</xliff:g> ຫຼືບໍ່?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"ປະຕິເສດ"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> ແມ່ນໜ້າຕ່າງລະດັບສຽງ"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"ສໍາຜັດເພື່ອກູ້ຄືນຕົ້ນສະບັບ."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"ທ່ານກຳລັງໃຊ້ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກຂອງທ່ານ"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"ສະແດງເປີເຊັນແບັດເຕີຣີທີ່ຕິດມາ"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"ສະແດງວິນາທີໂມງຢູ່ໃນແຖບສະຖານະ. ອາດຈະມີຜົນກະທົບຕໍ່ອາຍຸແບັດເຕີຣີ."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"ຈັດວາງການຕັ້ງຄ່າດ່ວນຄືນໃໝ່"</string>
<string name="show_brightness" msgid="6613930842805942519">"ສະແດງຄວາມແຈ້ງຢູ່ໃນການຕັ້ງຄ່າດ່ວນ"</string>
- <string name="qs_paging" msgid="7020133150248666132">"ໃຊ້ການໃສ່ໜ້າຢູ່ໃນການຕັ້ງຄ່າດ່ວນ"</string>
<string name="experimental" msgid="6198182315536726162">"ຍັງຢູ່ໃນການທົດລອງ"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"ເປີດໃຊ້ Bluetooth ບໍ່?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"ເພື່ອເຊື່ອມຕໍ່ແປ້ນພິມຂອງທ່ານກັບແທັບເລັດຂອງທ່ານ, ກ່ອນອື່ນໝົດທ່ານຕ້ອງເປີດ Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"ເປີດ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 194668b..2341e5a 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -388,15 +388,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Peržiūrėti prieš atrakinant"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Ne, ačiū"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Nustatyti"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Žr. viską"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Slėpti viską"</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">"Baigti dabar"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Išskleisti"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Sutraukti"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Ekranas prisegtas"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Tai bus rodoma, kol atsegsite. Kad atsegtumėte, tuo pačiu metu palieskite ir laikykite „Atgal“ ir „Apžvalga“."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Tai bus rodoma, kol atsegsite. Kad atsegtumėte, palieskite ir laikykite „Apžvalga“."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Tai bus rodoma, kol atsegsite. Kad atsegtumėte, palieskite ir palaikykite „Atgal“."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Supratau"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Ne, ačiū"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Slėpti „<xliff:g id="TILE_LABEL">%1$s</xliff:g>“?"</string>
@@ -407,6 +404,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Atmesti"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ yra garsumo valdymo dialogo langas"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Palieskite, kad atkurtumėte originalą."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Naudojate darbo profilį"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Sistemos naudotojo sąsajos derinimo priemonė"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Rodyti įterptą akumuliat. įkrovos procentinę vertę"</string>
@@ -441,6 +439,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Rodyti laikrodžio sekundes būsenos juostoje. Tai gali paveikti akumuliatoriaus naudojimo laiką."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Pertvarkyti sparčiuosius nustatymus"</string>
<string name="show_brightness" msgid="6613930842805942519">"Rodyti skaistį sparčiuosiuose nustatymuose"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Naudoti puslapių kaitą sparčiuosiuose nustatymuose"</string>
<string name="experimental" msgid="6198182315536726162">"Eksperimentinė versija"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Įjungti „Bluetooth“?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Kad galėtumėte prijungti klaviatūrą prie planšetinio kompiuterio, pirmiausia turite įjungti „Bluetooth“."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Įjungti"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 2b3be58..27663bd 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -387,15 +387,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Skatiet tos pirms atbloķēšanas."</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Nē"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Iestatīt"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Skatīt visus"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Slēpt visus"</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">"Izslēgt"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Izvērst"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Sakļaut"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Ekrāns ir piesprausts"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Šādi tas būs redzams līdz brīdim, kad to atspraudīsiet. Lai atspraustu, vienlaikus pieskarieties vienumiem “Atpakaļ” un “Pārskats” un turiet tos nospiestus."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Šādi tas būs redzams līdz brīdim, kad to atspraudīsiet. Lai atspraustu, pieskarieties vienumam “Pārskats” un turiet to nospiestu."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Šādi ekrāns būs redzams līdz brīdim, kad to atspraudīsiet. Lai atspraustu, pieskarieties vienumam “Atpakaļ” un turiet to nospiestu."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Sapratu!"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Nē, paldies"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Vai paslēpt vienumu <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -406,6 +403,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Neatļaut"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> ir skaļuma dialoglodziņš"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Pieskarieties, lai atjaunotu sākotnējo."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Jūs izmantojat darba profilu."</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Sistēmas saskarnes regulators"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Rādīt akumulatora uzlādes līmeni procentos"</string>
@@ -440,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Statusa joslā rādīt pulksteņa sekundes. Var ietekmēt akumulatora darbības laiku."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Pārkārtot ātros iestatījumus"</string>
<string name="show_brightness" msgid="6613930842805942519">"Rādīt spilgtumu ātrajos iestatījumos"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Izmantot lapošanu ātrajos iestatījumos"</string>
<string name="experimental" msgid="6198182315536726162">"Eksperimentāli"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Vai ieslēgt Bluetooth savienojumu?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Lai pievienotu tastatūru planšetdatoram, vispirms ir jāieslēdz Bluetooth savienojums."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Ieslēgt"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mk-rMK/strings.xml b/packages/SystemUI/res/values-mk-rMK/strings.xml
index 912c30f..7162a5c 100644
--- a/packages/SystemUI/res/values-mk-rMK/strings.xml
+++ b/packages/SystemUI/res/values-mk-rMK/strings.xml
@@ -150,9 +150,7 @@
<string name="accessibility_airplane_mode" msgid="834748999790763092">"Режим на работа во авион."</string>
<string name="accessibility_no_sims" msgid="3957997018324995781">"Нема СИМ-картичка"</string>
<string name="accessibility_carrier_network_change_mode" msgid="4017301580441304305">"Променување на мрежата на операторот."</string>
- <!-- String.format failed for translation -->
- <!-- no translation found for accessibility_battery_level (7451474187113371965) -->
- <skip />
+ <string name="accessibility_battery_level" msgid="7451474187113371965">"Батерија <xliff:g id="NUMBER">%d</xliff:g> проценти."</string>
<string name="accessibility_settings_button" msgid="799583911231893380">"Подесувања на систем."</string>
<string name="accessibility_notifications_button" msgid="4498000369779421892">"Известувања"</string>
<string name="accessibility_remove_notification" msgid="3603099514902182350">"Избриши известување."</string>
@@ -388,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Видете ги пред да отклучите"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Не, фала"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Постави"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Види ги сите"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Сокриј ги сите"</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">"Заврши сега"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Прошири"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Собери"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Екранот е прикачен"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Ќе се гледа сè додека не го откачите. Допрете и држете Назад и Краток преглед истовремено за откачување."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Ќе се гледа сè додека не го откачите. Допрете и држете Краток преглед за откачување."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Ќе се гледа сѐ додека не го откачите. Допрете и држете Назад за откачување."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Сфатив"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Не, фала"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Сокриј <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -407,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Одбиј"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> е дијалог за јачина на звук"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Допрете за да го вратите оригиналот."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Го користите работниот профил"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Адаптер на УИ на системот"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Прикажи вграден процент на батеријата"</string>
@@ -441,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Прикажи ги секундите на часовникот на статусната лента. Може да влијае на траењето на батеријата."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Преуредете ги Брзи поставки"</string>
<string name="show_brightness" msgid="6613930842805942519">"Прикажете ја осветленоста во Брзи поставки"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Користете прелистување страници во Брзи поставки"</string>
<string name="experimental" msgid="6198182315536726162">"Експериментално"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Да се вклучи Bluetooth?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"За да ја поврзете тастатурата со таблетот, најпрво треба да вклучите Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Вклучи"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ml-rIN/strings.xml b/packages/SystemUI/res/values-ml-rIN/strings.xml
index c85ecc7..5df98ec 100644
--- a/packages/SystemUI/res/values-ml-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ml-rIN/strings.xml
@@ -32,7 +32,7 @@
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"അറിയിപ്പുകൾ ഒന്നുമില്ല"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"നടന്നുകൊണ്ടിരിക്കുന്നവ"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"അറിയിപ്പുകൾ"</string>
- <string name="battery_low_title" msgid="6456385927409742437">"ബാറ്ററി നില കുറവാണ്"</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="invalid_charger" msgid="4549105996740522523">"USB ചാർജ്ജുചെയ്യൽ പിന്തുണയ്ക്കുന്നില്ല.\nഅതിന്റെ അനുബന്ധ ചാർജ്ജർ മാത്രം ഉപയോഗിക്കുക."</string>
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"അൺലോക്കുചെയ്യുന്നതിന് മുമ്പ് അവ കാണുക"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"വേണ്ട, നന്ദി"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"സജ്ജീകരിക്കുക"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"എല്ലാം കാണുക"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"എല്ലാം മറയ്ക്കുക"</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">"ഇപ്പോള് അവസാനിപ്പിക്കുക"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"വികസിപ്പിക്കുക"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"ചുരുക്കുക"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"സ്ക്രീൻ പിൻ ചെയ്തു"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"നിങ്ങൾ അൺപിൻ ചെയ്യുന്നതുവരെ ഇത് കാണുന്ന വിധത്തിൽ നിലനിർത്തുന്നു. അൺപിൻ ചെയ്യാൻ \'മടങ്ങുക\', \'ചുരുക്കവിവരണം\' എന്നിവ ഒരേ സമയം സ്പർശിച്ച് പിടിക്കുക."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"നിങ്ങൾ അൺപിൻ ചെയ്യുന്നതുവരെ ഇത് കാണുന്ന വിധത്തിൽ നിലനിർത്തുന്നു. അൺപിൻ ചെയ്യുന്നതിന് \'ചുരുക്കവിവരണം\' സ്പർശിച്ചുപിടിക്കുക."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"നിങ്ങൾ അൺപിൻ ചെയ്യുന്നതുവരെ ഇത് കാണുന്ന വിധത്തിൽ നിലനിർത്തും. അൺപിൻ ചെയ്യുന്നതിന് \'മടങ്ങുക\' സ്പർശിച്ചുപിടിക്കുക."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"മനസ്സിലായി"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"വേണ്ട, നന്ദി"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> എന്നത് മറയ്ക്കണോ?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"നിരസിക്കുക"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g>, വോളിയം ഡയലോഗാണ്"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"ആദ്യത്തേത് പുനഃസ്ഥാപിക്കാൻ സ്പർശിക്കുക."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"നിങ്ങൾ ഉപയോഗിക്കുന്നത് ഔദ്യോഗിക പ്രൊഫൈലാണ്"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"സിസ്റ്റം UI ട്യൂണർ"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"എംബഡ് ചെയ്ത ബാറ്ററി ശതമാനം കാണിക്കുക"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"സ്റ്റാറ്റസ് ബാറിൽ ക്ലോക്ക് സെക്കൻഡ് കാണിക്കുന്നത് ബാറ്ററിയുടെ ലൈഫിനെ ബാധിക്കാം."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"ദ്രുത ക്രമീകരണം പുനഃസജ്ജീകരിക്കുക"</string>
<string name="show_brightness" msgid="6613930842805942519">"ദ്രുത ക്രമീകരണത്തിൽ തെളിച്ചം കാണിക്കുക"</string>
- <string name="qs_paging" msgid="7020133150248666132">"ദ്രുത്ര ക്രമീകരണത്തിൽ പേജിംഗ് ഉപയോഗിക്കുക"</string>
<string name="experimental" msgid="6198182315536726162">"പരീക്ഷണാത്മകം!"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Bluetooth ഓണാക്കണോ?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"നിങ്ങളുടെ ടാബ്ലെറ്റുമായി കീബോർഡ് കണക്റ്റുചെയ്യുന്നതിന്, ആദ്യം Bluetooth ഓണാക്കേണ്ടതുണ്ട്."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"ഓണാക്കുക"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mn-rMN/strings.xml b/packages/SystemUI/res/values-mn-rMN/strings.xml
index 6b29aff..f960dfa 100644
--- a/packages/SystemUI/res/values-mn-rMN/strings.xml
+++ b/packages/SystemUI/res/values-mn-rMN/strings.xml
@@ -384,15 +384,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Түгжээг тайлахын өмнө үзнэ үү"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Үгүй"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Тохируулах"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Бүгдийг харах"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Бүгдийг нуух"</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">"Одоо дуусгах"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Дэлгэх"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Хураах"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Дэлгэц эхэнд байрлуулагдсан"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Таныг эхэнд нээхийг болиулах хүртэл харагдана. Хүрээд, Back дээр удаан дараад хаахдаа Overview-ийг дар"</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Таныг эхэнд нээхийг болиулах хүртэл харагдана. Хаахын тулд хүрээдOverview-ийг дар"</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Таныг эхэнд нээхийг болиулах хүртэл харагдана. Хаахын тулд Хүрэх, Буцах товчлуурыг удаан дараарай."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Ойлголоо"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Үгүй"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g>-ийг нуух уу?"</string>
@@ -403,6 +400,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Татгалзах"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> нь дууны диалог юм."</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Анхны хувилбарыг эргүүлэн хадгалахыг хүсвэл хүрнэ үү."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Та өөрийн ажлын профайлыг ашиглаж байна"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Системийн UI Тохируулагч"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Залгаатай тэжээлийн хувийг харуулах"</string>
@@ -437,6 +435,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Статус талбарт цагийн секундыг харуулах. Энэ нь тэжээлийн цэнэгт нөлөөлж болно."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Түргэн тохиргоог дахин засварлах"</string>
<string name="show_brightness" msgid="6613930842805942519">"Түргэн тохиргоонд гэрэлтүүлэг харах"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Хуудаслалтыг түргэн тохиргоонд ашиглаарай"</string>
<string name="experimental" msgid="6198182315536726162">"Туршилтын"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Bluetooth-г асаах уу?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Компьютерийн гараа таблетад холбохын тулд эхлээд Bluetooth-г асаана уу."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Асаах"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mr-rIN/strings.xml b/packages/SystemUI/res/values-mr-rIN/strings.xml
index 9d518a0..f599aca 100644
--- a/packages/SystemUI/res/values-mr-rIN/strings.xml
+++ b/packages/SystemUI/res/values-mr-rIN/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"आपण अनलॉक करण्यापूर्वी त्यांना पहा"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"नाही धन्यवाद"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"सेट अप"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"सर्व पहा"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"सर्व लपवा"</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">"आता समाप्त करा"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"विस्तृत करा"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"संकुचित करा"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"स्क्रीन पिन केलेली आहे"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"हे आपण अनपिन करेपर्यंत दृश्यामध्ये ते ठेवते. अनपिन करण्यासाठी एकाच वेळी परत आणि अलीकडील ला स्पर्श करा आणि धरून ठेवा आणि विहंगावलोकन करा."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"हे आपण अनपिन करेपर्यंत दृश्यामध्ये ते ठेवते. अनपिन करण्यासाठी स्पर्श करा आणि धरून ठेवा."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"हे आपण अनपिन करेपर्यंत दृश्यामध्ये ते ठेवते. अनपिन करण्यासाठी परत ला स्पर्श करा आणि धरून ठेवा."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"समजले"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"नाही धन्यवाद"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> लपवायचे?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"नकार द्या"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> हा व्हॉल्यूम संवाद आहे"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"मूळ पुनर्संचयित करण्यासाठी स्पर्श करा."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"आपण आपले कार्य प्रोफाईल वापरत आहात"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"सिस्टीम UI ट्यूनर"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"एम्बेडेड बॅटरी टक्केवारी दर्शवा"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"स्टेटस बारमध्ये घड्याळ सेकंद दर्शवा. कदाचित बॅटरी आयुष्य प्रभावित होऊ शकते."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"द्रुत सेटिंग्जची पुनर्रचना करा"</string>
<string name="show_brightness" msgid="6613930842805942519">"द्रुत सेटिंग्जमध्ये चमक दर्शवा"</string>
- <string name="qs_paging" msgid="7020133150248666132">"द्रुत सेटिंग्जमध्ये लिखाण वापरा"</string>
<string name="experimental" msgid="6198182315536726162">"प्रायोगिक"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"ब्लूटुथ सुरू करायचे?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"आपला कीबोर्ड आपल्या टॅब्लेटसह कनेक्ट करण्यासाठी, आपल्याला प्रथम ब्लूटुथ चालू करणे आवश्यक आहे."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"चालू करा"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml
index 10dca3e..6511d30 100644
--- a/packages/SystemUI/res/values-ms-rMY/strings.xml
+++ b/packages/SystemUI/res/values-ms-rMY/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Lihat sebelum anda membuka kunci"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Tidak"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Sediakan"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Lihat semua"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Sembunyikan semua"</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">"Tamatkan sekarang"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Kembangkan"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Runtuhkan"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Skrin telah disemat"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Ini akan memastikan skrin kelihatan sehingga anda menyahsemat. Sentuh dan tahan Kembali dan Gambaran Keseluruhan pada masa yang sama untuk menyahsemat."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Ini akan memastikan skrin kelihatan sehingga anda menyahsemat. Sentuh dan tahan Gambaran Keseluruhan untuk menyahsemat."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Ini akan memastikan skrin kelihatan sehingga anda menyahsemat. Sentuh dan tahan Kembali untuk menyahsemat."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Faham"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Tidak"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Sembunyikan <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Tolak"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> ialah dialog kelantangan"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Sentuh untuk memulihkan yang asal."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Anda sedang menggunakan profil kerja"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Penala UI Sistem"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Tunjukkan peratusan bateri terbenam"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Tunjukkan saat jam dalam bar status. Mungkin menjejaskan hayat bateri."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Susun Semula Tetapan Pantas"</string>
<string name="show_brightness" msgid="6613930842805942519">"Tunjukkan kecerahan dalam Tetapan Pantas"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Gunakan penghalaman dalam Tetapan Pantas"</string>
<string name="experimental" msgid="6198182315536726162">"Percubaan"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Hidupkan Bluetooth?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Untuk menyambungkan papan kekunci anda dengan tablet, anda perlu menghidupkan Bluetooth terlebih dahulu."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Hidupkan"</string>
</resources>
diff --git a/packages/SystemUI/res/values-my-rMM/strings.xml b/packages/SystemUI/res/values-my-rMM/strings.xml
index adfd9f8..c8551b7 100644
--- a/packages/SystemUI/res/values-my-rMM/strings.xml
+++ b/packages/SystemUI/res/values-my-rMM/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"မဖွင့်ခင် ၎င်းတို့ကို ကြည့်ပါ"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"မလိုအပ်ပါ"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"သတ်မှတ်ရန်"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"အားလုံးကို ကြည့်ရန်"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"အားလုံး ဝှက်ထားရန်"</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">"ယခု အဆုံးသတ်ရန်"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"တိုးချဲ့ရန်"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"ခေါက်သိမ်းရန်..."</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"မျက်နှာပြင် ပင်ထိုးပြီးပါပြီ"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"သင်ပင်ဖြုတ်သည့် တိုင်အောင် ၎င်းအား မြင်ကွင်းတွင် ထားရှိပါမည်။ ပင်ဖြုတ်ရန် အနောက်နှင့် ခြုံငုံကြည့်ခြင်းကို ဖိ၍ နှိပ်ထားနိုင်သည်။"</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"သင်ပင်ဖြုတ်သည့် တိုင်အောင် ၎င်းအား မြင်ကွင်းတွင် ထားရှိပါမည်။ ပင်ဖြုတ်ရန် ခြုံငုံကြည့်ခြင်းကို ဖိ၍ နှိပ်ထားနိုင်သည်။"</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"ပင်ဖြုတ်သည့်အထိ ၎င်းကို မြင်နေမည်။ ပင်ဖြုတ်ရန် နောက်သို့ ခလုတ်ကို ထိလျက် ကိုင်ထားပါ။"</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"အဲဒါ ရပြီ"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"မလို ကျေးဇူးပဲ"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> ဝှက်မည်လား?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"ငြင်းပယ်သည်"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> သည် အသံဒိုင်ယာလော့ခ်ဖြစ်သည်"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"မူရင်းအားပြန်လည်သိမ်းဆည်းရန် ထိပါ။"</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">"၊"</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"သင်သည် အလုပ်ပရိုဖိုင်းအား သုံးနေသည်"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"စနစ် UI ဖမ်းစက်"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"မြုတ်ထားသည့် ဘတ်ထရီ ရာခိုင်နှုန်းကို ပြပါ"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"အခြေအနေပြနေရာမှာ နာရီ စက္ကန့်များကို ပြပါ။ ဘက်ထရီ သက်တမ်းကို အကျိုးသက်ရောက်နိုင်တယ်။"</string>
<string name="qs_rearrange" msgid="8060918697551068765">"အမြန် ဆက်တင်များကို ပြန်စီစဉ်ရန်"</string>
<string name="show_brightness" msgid="6613930842805942519">"အမြန် ဆက်တင်များထဲက တောက်ပမှုကို ပြရန်"</string>
- <string name="qs_paging" msgid="7020133150248666132">"အမြန် ဆက်တင်များထဲတွင် စာမျက်နှာ ပုံစံချမှုကို အသုံးပြုပါ"</string>
<string name="experimental" msgid="6198182315536726162">"စမ်းသပ်ရေး"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"ဘလူးတုသ် ဖွင့်ရမလား။"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"ကီးဘုတ်ကို တပ်ဘလက်နှင့် ချိတ်ဆက်ရန်၊ ပမထဦးစွာ ဘလူးတုသ်ကို ဖွင့်ပါ။"</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"ဖွင့်ပါ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index ecfb7f2..7cfcbe2 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -384,19 +384,16 @@
<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="notification_expand_button_text" msgid="1037425494153780718">"Se alle"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Skjul alle"</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>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Utvid"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Skjul"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Skjermen er låst"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"På denne måten blir skjermen synlig frem til du låser den opp. Trykk på og hold inne Tilbake og Oversikt samtidig for å låse opp."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"På denne måten blir skjermen synlig frem til du låser den opp. Trykk på og hold inne Tilbake og Oversikt for å låse opp."</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>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Ikke tillat"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> er volumdialogen"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Trykk for å gå tilbake til den opprinnelige volumdialogen."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Du bruker jobbprofilen din"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Vis prosent for det innebygde batteriet"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Vis sekunder i statusfeltet på klokken. Det kan påvirke batteritiden."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Omorganiser hurtiginnstillingene"</string>
<string name="show_brightness" msgid="6613930842805942519">"Vis lysstyrke i hurtiginnstillingene"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Bruk sidetall i hurtiginnstillingene"</string>
<string name="experimental" msgid="6198182315536726162">"På forsøksstadiet"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Vil du slå på Bluetooth?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"For å koble tastaturet til nettbrettet ditt må du først slå på Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Slå på"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ne-rNP/strings.xml b/packages/SystemUI/res/values-ne-rNP/strings.xml
index d997aff..078f783 100644
--- a/packages/SystemUI/res/values-ne-rNP/strings.xml
+++ b/packages/SystemUI/res/values-ne-rNP/strings.xml
@@ -380,31 +380,29 @@
<string name="monitoring_description_app_personal" msgid="484599052118316268">"तपाईँ <xliff:g id="APPLICATION">%1$s</xliff:g> सँग जडित हुनुहुन्छ जसले इ-मेल, अनुप्रयोगहरू र वेबसाइट लगायतका तपाईँको निजी नेटवर्क गतिविधिका अनुगमन गर्न सक्छ।"</string>
<string name="monitoring_description_app_work" msgid="1754325860918060897">"तपाईँको कार्य प्रोफाइल <xliff:g id="ORGANIZATION">%1$s</xliff:g> द्वारा व्यवस्थापन गरिन्छ। यो <xliff:g id="APPLICATION">%2$s</xliff:g> सँग जोडिएको छ जसले इमेल, अनुप्रयोगहरू, र वेबसाइटहरू लगायतका तपाईँका नेटवर्क गतिविधि अनुगमन गर्न सक्छ।\n\nथप जानकारीको लागि, आफ्नो प्रशासकलाई सम्पर्क गर्नुहोस्।"</string>
<string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"तपाईँको कार्य प्रोफाइल <xliff:g id="ORGANIZATION">%1$s</xliff:g> द्वारा व्यवस्थापन गरिन्छ। यो <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> सँग जोडिएको छ जसले इमेल, अनुप्रयोगहरू, र वेबसाइटहरू लगायतका तपाईँका नेटवर्क गतिविधि अनुगमन गर्न सक्छ।\n\nतपाईँ <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> सँग पनि जडित हुनुहुन्छ, जसले तपाईँको व्यक्तिगत नेटवर्क गतिविधि अनुगमन गर्न सक्छ।"</string>
- <string name="monitoring_description_vpn_app_device_owned" msgid="4970443827043261703">"तपाईंको उपकरण <xliff:g id="ORGANIZATION">%1$s</xliff:g> द्वारा व्यवस्थित गरिन्छ।\n\nतपाईंको प्रशासकले तपाईँको यन्त्र र त्यसको स्थान जानकारीमार्फत सेटिङहरू, कर्पोरेट पहुँच, अनुप्रयोगहरू, तपाईँको यन्त्रसँग सम्बद्ध डेटा र तपाईँको यन्त्रको स्थान जानकारीको अनुगमन र व्यवस्थापन गर्न सक्छ।\n\nतपाईं <xliff:g id="APPLICATION">%2$s</xliff:g> सँग जडित हुनुहुन्छ जसले इमेल, अनुप्रयोगहरू, र वेबसाइटहरू लगायतका तपाईँका नेटवर्क गतिविधिका अनुगमन गर्न सक्छ।\n\nथप जानकारीको लागि तपाईको प्रशासकलाई सम्पर्क गर्नुहोस्।"</string>
+ <string name="monitoring_description_vpn_app_device_owned" msgid="4970443827043261703">"तपाईँको उपकरण <xliff:g id="ORGANIZATION">%1$s</xliff:g> द्वारा व्यवस्थित गरिन्छ।\n\nतपाईँको प्रशासकले तपाईँको यन्त्र र त्यसको स्थान जानकारीमार्फत सेटिङहरू, कर्पोरेट पहुँच, अनुप्रयोगहरू, तपाईँको यन्त्रसँग सम्बद्ध डेटा र तपाईँको यन्त्रको स्थान जानकारीको अनुगमन र व्यवस्थापन गर्न सक्छ।\n\nतपाईँ <xliff:g id="APPLICATION">%2$s</xliff:g> सँग जडित हुनुहुन्छ जसले इमेल, अनुप्रयोगहरू, र वेबसाइटहरू लगायतका तपाईँका नेटवर्क गतिविधिका अनुगमन गर्न सक्छ।\n\nथप जानकारीको लागि तपाईको प्रशासकलाई सम्पर्क गर्नुहोस्।"</string>
<string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"तपाईँले नखोले सम्म उपकरण बन्द रहनेछ"</string>
<string name="hidden_notifications_title" msgid="7139628534207443290">"छिटो सूचनाहरू प्राप्त गर्नुहोस्"</string>
<string name="hidden_notifications_text" msgid="2326409389088668981">"तपाईँले अनलक गर्नअघि तिनीहरूलाई हेर्नुहोस्"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"धन्यवाद पर्दैन"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"सेटअप गर्नुहोस्"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"सबै हेर्नुहोस्"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"सबै लुकाउनुहोस्"</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">"अहिल्यै अन्त्य गर्नुहोस्"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"विस्तार गर्नुहोस्"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"संक्षिप्त पार्नुहोस्"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"पर्दा राखेका छ"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"तपाईं अनपिन सम्म यो दृश्य मा राख्छ। छुनुहोस् र अनपिन फिर्ता र सिंहावलोकन नै समय मा पकड।"</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"तपाईं अनपिन सम्म यो दृश्य मा राख्छ। छुनुहोस् र अनपिन गर्न सिंहावलोकन पकड।"</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"तपाईँले अनपिन नगरेसम्म यसले त्यसलाई देखाइ राख्छ। अनपिन गर्न छुनुहोस् र होल्ड गर्नुहोस्"</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"बुझेँ"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"धन्यवाद पर्दैन"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"लुकाउनुहुन्छ <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
- <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"यो तपाईं सेटिङ् मा यो बारी अर्को समय देखापर्नेछ।"</string>
+ <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"यो तपाईँ सेटिङ् मा यो बारी अर्को समय देखापर्नेछ।"</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"लुकाउनुहोस्"</string>
<string name="volumeui_prompt_message" msgid="918680947433389110">"<xliff:g id="APP_NAME">%1$s</xliff:g> भोल्यूम संवाद बन्न चाहन्छ।"</string>
<string name="volumeui_prompt_allow" msgid="7954396902482228786">"अनुमति दिनुहोस्"</string>
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"अस्वीकार गर्नुहोस्"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> भोल्यूम संवाद हो"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"मूल पुनर्स्थापना गर्न छुनुहोस्।"</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"तपाईँले कार्य प्रोफाइल प्रयोग गर्दै हुनुहुन्छ"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"प्रणाली UI ट्युनर"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"इम्बेड गरिएको ब्याट्री प्रतिशत देखाउनुहोस्"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"वस्तुस्थिति पट्टीको घडीमा सेकेन्ड देखाउनुहोस्। ब्याट्री आयु प्रभावित हुन सक्छ।"</string>
<string name="qs_rearrange" msgid="8060918697551068765">"द्रुत सेटिङहरू पुनः व्यवस्थित गर्नुहोस्"</string>
<string name="show_brightness" msgid="6613930842805942519">"द्रुत सेटिङहरूमा उज्यालो देखाउनुहोस्"</string>
- <string name="qs_paging" msgid="7020133150248666132">"द्रुत सेटिङहरूमा पेजिंग प्रयोग गर्नुहोस्"</string>
<string name="experimental" msgid="6198182315536726162">"प्रयोगात्मक"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"ब्लुटुथ सक्रिय पार्ने हो?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"आफ्नो ट्याब्लेटसँग किबोर्ड जोड्न, पहिले तपाईँले ब्लुटुथ सक्रिय गर्नुपर्छ।"</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"सक्रिय पार्नुहोस्"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index c6bab06..de4e58a 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Weergeven voordat u ontgrendelt"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Nee, bedankt"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Configureren"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Alles weergeven"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Alles verbergen"</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">"Nu uitschakelen"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Uitvouwen"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Samenvouwen"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Scherm is vastgezet"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Het scherm blijft zichtbaar totdat u het u losmaakt. Houd \'Terug\' en \'Overzicht\' tegelijkertijd aangeraakt om het los te maken."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Het scherm blijft zichtbaar totdat u het u losmaakt. Houd \'Overzicht\' aangeraakt om het los te maken."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Het scherm blijft zichtbaar totdat je het losmaakt. Blijf \'Terug\' aanraken om het los te maken."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Ik snap het"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Nee, bedankt"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> verbergen?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Afwijzen"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> is het volumedialoogvenster"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Tik hierop om het origineel te herstellen."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"U gebruikt je werkprofiel"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Systeem-UI-tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Percentage ingebouwde accu weergeven"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Klokseconden op de statusbalk weergeven. Kan van invloed zijn op de accuduur."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Snelle instellingen opnieuw indelen"</string>
<string name="show_brightness" msgid="6613930842805942519">"Helderheid weergeven in Snelle instellingen"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Paginering gebruiken in Snelle instellingen"</string>
<string name="experimental" msgid="6198182315536726162">"Experimenteel"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Bluetooth inschakelen?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Als je je toetsenbord wilt verbinden met je tablet, moet je eerst Bluetooth inschakelen."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Inschakelen"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pa-rIN/strings.xml b/packages/SystemUI/res/values-pa-rIN/strings.xml
index 74e5fcb..e5cbf1f 100644
--- a/packages/SystemUI/res/values-pa-rIN/strings.xml
+++ b/packages/SystemUI/res/values-pa-rIN/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"ਅਨਲੌਕ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ ਉਹਨਾਂ ਨੂੰ ਦੇਖੋ"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"ਨਹੀਂ ਧੰਨਵਾਦ"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"ਸੈਟ ਅਪ"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"ਸਭ ਦੇਖੋ"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"ਸਾਰੇ ਲੁਕਾਓ"</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">"ਹੁਣੇ ਸਮਾਪਤ ਕਰੋ"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"ਵਿਸਤਾਰ ਕਰੋ"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"ਨਸ਼ਟ ਕਰੋ"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"ਸਕ੍ਰੀਨ ਪਿੰਨ ਕੀਤੀ"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"ਇਹ ਇਸਨੂੰ ਦ੍ਰਿਸ਼ ਵਿੱਚ ਰੱਖਦਾ ਹੈ ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਅਨਪਿਨ ਨਹੀਂ ਕਰਦੇ। ਅਨਪਿਨ ਕਰਨ ਲਈ ਪਿੱਛੇ ਅਤੇ ਰੂਪ-ਰੇਖਾ ਨੂੰ ਇੱਕੋ ਸਮੇਂ ਛੋਹਵੋ ਅਤੇ ਹੋਲਡ ਕਰੋ।"</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"ਇਹ ਇਸਨੂੰ ਦ੍ਰਿਸ਼ ਵਿੱਚ ਰੱਖਦਾ ਹੈ ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਅਨਪਿਨ ਨਹੀਂ ਕਰਦੇ। ਅਨਪਿਨ ਕਰਨ ਲਈ ਰੂਪ-ਰੇਖਾ ਨੂੰ ਛੋਹਵੋ ਅਤੇ ਹੋਲਡ ਕਰੋ।"</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"ਇਹ ਦ੍ਰਿਸ਼ ਵਿੱਚ ਉਦੋਂ ਤੱਕ ਬਣਿਆ ਰਹਿੰਦਾ ਹੈ ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਅਨਪਿੰਨ ਨਹੀਂ ਕਰਦੇ। ਅਨਪਿੰਨ ਕਰਨ ਲਈ ਛੂੁਹੋ ਅਤੇ ਹੋਲਡ ਕਰੋ।"</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"ਸਮਝ ਗਿਆ"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"ਨਹੀਂ ਧੰਨਵਾਦ"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"ਕੀ <xliff:g id="TILE_LABEL">%1$s</xliff:g> ਨੂੰ ਲੁਕਾਉਣਾ ਹੈ?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"ਅਸਵੀਕਾਰ ਕਰੋ"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਵੋਲਯੂਮ ਡਾਇਲੌਗ ਹੈ"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"ਅਸਲੀ ਨੂੰ ਰੀਸਟੋਰ ਕਰਨ ਲਈ ਛੋਹਵੋ।"</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"ਤੁਸੀਂ ਆਪਣੀ ਕੰਮ ਪ੍ਰੋਫਾਈਲ ਵਰਤ ਰਹੇ ਹੋ"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI ਟਿਊਨਰ"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"ਜੋਡ਼ੀ ਗਈ ਬੈਟਰੀ ਪ੍ਰਤਿਸ਼ਤਤਾ ਦਿਖਾਓ"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"ਸਥਿਤੀ ਬਾਰ ਵਿੱਚ ਘੜੀ ਸਕਿੰਟ ਦਿਖਾਓ। ਬੈਟਰੀ ਸਮਰੱਥਾ ਤੇ ਅਸਰ ਪੈ ਸਕਦਾ ਹੈ।"</string>
<string name="qs_rearrange" msgid="8060918697551068765">"ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ ਨੂੰ ਦੁਬਾਰਾ ਕ੍ਰਮ ਦਿਓ"</string>
<string name="show_brightness" msgid="6613930842805942519">"ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਚਮਕ ਦਿਖਾਓ"</string>
- <string name="qs_paging" msgid="7020133150248666132">"ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਪੇਜਿੰਗ ਵਰਤੋ"</string>
<string name="experimental" msgid="6198182315536726162">"ਪ੍ਰਯੋਗਾਤਮਿਕ"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Bluetooth ਚਾਲੂ ਕਰੋ?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"ਆਪਣੇ ਟੈਬਲੇਟ ਨਾਲ ਆਪਣਾ ਕੀ-ਬੋਰਡ ਕਨੈਕਟ ਕਰਨ ਲਈ, ਤੁਹਾਨੂੰ ਪਹਿਲਾਂ Bluetooth ਚਾਲੂ ਕਰਨ ਦੀ ਜ਼ਰੂਰਤ ਹੈ।"</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"ਚਾਲੂ ਕਰੋ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index e04fd71..65d19bb 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -388,15 +388,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Zobacz powiadomienia, jeszcze zanim odblokujesz ekran"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Nie, dziękuję"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Skonfiguruj"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Pokaż wszystkie"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Ukrywaj wszystkie"</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">"Zakończ teraz"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Rozwiń"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Zwiń"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Ekran jest przypięty"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Ekran będzie widoczny, dopóki go nie odepniesz. Aby to zrobić, kliknij i przytrzymaj jednocześnie Wstecz i Przegląd."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Ekran będzie widoczny, dopóki go nie odepniesz. Aby to zrobić, kliknij i przytrzymaj Przegląd."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Ekran będzie widoczny, dopóki go nie odepniesz. Aby to zrobić, kliknij i przytrzymaj Wstecz."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"OK"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Nie, dziękuję"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Ukryć <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -407,6 +404,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Odmów"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> steruje głośnością"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Dotknij, by przywrócić pierwotną."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Używasz profilu do pracy"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Kalibrator System UI"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Pokaż procent naładowania baterii"</string>
@@ -441,6 +439,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Pokaż sekundy na zegarku na pasku stanu. Może mieć wpływ na czas pracy baterii."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Uporządkuj Szybkie ustawienia"</string>
<string name="show_brightness" msgid="6613930842805942519">"Pokaż jasność w Szybkich ustawieniach"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Użyj stronicowania w Szybkich ustawieniach"</string>
<string name="experimental" msgid="6198182315536726162">"Eksperymentalne"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Włączyć Bluetooth?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Aby połączyć klawiaturę z tabletem, musisz najpierw włączyć Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Włącz"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 2a8e3ab..8448717 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -150,9 +150,7 @@
<string name="accessibility_airplane_mode" msgid="834748999790763092">"Modo avião."</string>
<string name="accessibility_no_sims" msgid="3957997018324995781">"Sem cartão SIM."</string>
<string name="accessibility_carrier_network_change_mode" msgid="4017301580441304305">"Alteração de rede de operadora."</string>
- <!-- String.format failed for translation -->
- <!-- no translation found for accessibility_battery_level (7451474187113371965) -->
- <skip />
+ <string name="accessibility_battery_level" msgid="7451474187113371965">"Bateria em <xliff:g id="NUMBER">%d</xliff:g> por cento."</string>
<string name="accessibility_settings_button" msgid="799583911231893380">"Configurações do sistema"</string>
<string name="accessibility_notifications_button" msgid="4498000369779421892">"Notificações."</string>
<string name="accessibility_remove_notification" msgid="3603099514902182350">"Limpar notificação."</string>
@@ -388,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Veja-as antes de desbloquear"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Não, obrigado"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Configurar"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Ver tudo"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Ocultar tudo"</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">"Finalizar agora"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Expandir"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Recolher"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"A tela está fixada"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Ela é mantida à vista até que seja liberada. Toque em \"Voltar\" e \"Visão Geral\" e mantenha essas opções pressionadas ao mesmo tempo para liberar."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Ela é mantida à vista até que seja liberada. Toque em \"Visão geral\" e mantenha essa opção pressionada para liberar."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Ela é mantida à vista até que seja liberada. Toque em \"Voltar\" e mantenha essa opção pressionada para liberar."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Entendi"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Não, obrigado"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Esconder <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -407,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Negar"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> é a caixa de diálogo referente ao volume"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Toque para restaurar o original."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Você está usando seu perfil de trabalho"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Sintonizador System UI"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Mostrar porcentagem de bateria incorporada"</string>
@@ -441,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Mostrar segundos do relógio na barra de status. Pode afetar a duração da bateria."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Reorganizar \"Configurações rápidas\""</string>
<string name="show_brightness" msgid="6613930842805942519">"Mostrar brilho nas \"Configurações rápidas\""</string>
- <string name="qs_paging" msgid="7020133150248666132">"Usar paginação nas \"Configurações rápidas\""</string>
<string name="experimental" msgid="6198182315536726162">"Experimentais"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Ativar o Bluetooth?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Para conectar o teclado ao tablet, é preciso primeiro ativar o Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Ativar"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 8453e5b..6abe9a9 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Ver antes de desbloquear"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Não, obrigado"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Configurar"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Ver tudo"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Ocultar tudo"</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">"Terminar agora"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Expandir"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Reduzir"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"O ecrã está fixado"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Será mantido na vista até soltar. Toque sem soltar em Anterior e Vista geral em simultâneo para soltar."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Será mantido na vista até soltar. Toque sem soltar em Vista geral para soltar."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Será mantido na vista até soltar. Toque sem soltar em Anterior para soltar."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Compreendi"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Não, obrigado"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Pretende ocultar <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Recusar"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> é a caixa de diálogo do volume"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Toque para restaurar o original."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Está a utilizar o seu perfil de trabalho"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Sintonizador da interface do sistema"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Mostrar percentagem da bateria incorporada"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Mostrar segundos do relógio na barra de estado. Pode afetar a autonomia da bateria."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Reorganizar as Definições rápidas"</string>
<string name="show_brightness" msgid="6613930842805942519">"Mostrar luminosidade nas Definições rápidas"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Utilizar paginação nas Definições rápidas"</string>
<string name="experimental" msgid="6198182315536726162">"Experimental"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Pretende ativar o Bluetooth?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Para ligar o teclado ao tablet, tem de ativar primeiro o Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Ativar"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 2a8e3ab..8448717 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -150,9 +150,7 @@
<string name="accessibility_airplane_mode" msgid="834748999790763092">"Modo avião."</string>
<string name="accessibility_no_sims" msgid="3957997018324995781">"Sem cartão SIM."</string>
<string name="accessibility_carrier_network_change_mode" msgid="4017301580441304305">"Alteração de rede de operadora."</string>
- <!-- String.format failed for translation -->
- <!-- no translation found for accessibility_battery_level (7451474187113371965) -->
- <skip />
+ <string name="accessibility_battery_level" msgid="7451474187113371965">"Bateria em <xliff:g id="NUMBER">%d</xliff:g> por cento."</string>
<string name="accessibility_settings_button" msgid="799583911231893380">"Configurações do sistema"</string>
<string name="accessibility_notifications_button" msgid="4498000369779421892">"Notificações."</string>
<string name="accessibility_remove_notification" msgid="3603099514902182350">"Limpar notificação."</string>
@@ -388,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Veja-as antes de desbloquear"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Não, obrigado"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Configurar"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Ver tudo"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Ocultar tudo"</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">"Finalizar agora"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Expandir"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Recolher"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"A tela está fixada"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Ela é mantida à vista até que seja liberada. Toque em \"Voltar\" e \"Visão Geral\" e mantenha essas opções pressionadas ao mesmo tempo para liberar."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Ela é mantida à vista até que seja liberada. Toque em \"Visão geral\" e mantenha essa opção pressionada para liberar."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Ela é mantida à vista até que seja liberada. Toque em \"Voltar\" e mantenha essa opção pressionada para liberar."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Entendi"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Não, obrigado"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Esconder <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -407,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Negar"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> é a caixa de diálogo referente ao volume"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Toque para restaurar o original."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Você está usando seu perfil de trabalho"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Sintonizador System UI"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Mostrar porcentagem de bateria incorporada"</string>
@@ -441,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Mostrar segundos do relógio na barra de status. Pode afetar a duração da bateria."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Reorganizar \"Configurações rápidas\""</string>
<string name="show_brightness" msgid="6613930842805942519">"Mostrar brilho nas \"Configurações rápidas\""</string>
- <string name="qs_paging" msgid="7020133150248666132">"Usar paginação nas \"Configurações rápidas\""</string>
<string name="experimental" msgid="6198182315536726162">"Experimentais"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Ativar o Bluetooth?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Para conectar o teclado ao tablet, é preciso primeiro ativar o Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Ativar"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index de693f5..b45347f 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -151,7 +151,7 @@
<string name="accessibility_airplane_mode" msgid="834748999790763092">"Mod Avion."</string>
<string name="accessibility_no_sims" msgid="3957997018324995781">"Niciun card SIM."</string>
<string name="accessibility_carrier_network_change_mode" msgid="4017301580441304305">"Se schimbă rețeaua operatorului."</string>
- <string name="accessibility_battery_level" msgid="7451474187113371965">"Baterie: <xliff:g id="NUMBER">%d</xliff:g> procente."</string>
+ <string name="accessibility_battery_level" msgid="7451474187113371965">"Baterie: <xliff:g id="NUMBER">%d</xliff:g> la sută."</string>
<string name="accessibility_settings_button" msgid="799583911231893380">"Setări de sistem."</string>
<string name="accessibility_notifications_button" msgid="4498000369779421892">"Notificări."</string>
<string name="accessibility_remove_notification" msgid="3603099514902182350">"Ștergeți notificarea."</string>
@@ -255,7 +255,7 @@
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Localizarea este dezactivată"</string>
<string name="quick_settings_media_device_label" msgid="1302906836372603762">"Dispozitiv media"</string>
<string name="quick_settings_rssi_label" msgid="7725671335550695589">"RSSI"</string>
- <string name="quick_settings_rssi_emergency_only" msgid="2713774041672886750">"Numai apeluri de urgenţă"</string>
+ <string name="quick_settings_rssi_emergency_only" msgid="2713774041672886750">"Numai apeluri de urgență"</string>
<string name="quick_settings_settings_label" msgid="5326556592578065401">"Setări"</string>
<string name="quick_settings_time_label" msgid="4635969182239736408">"Ora"</string>
<string name="quick_settings_user_label" msgid="5238995632130897840">"Eu"</string>
@@ -387,15 +387,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Doresc să se afișeze înainte de deblocare"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Nu, mulț."</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Config."</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Afișați toate"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Ascundeți toate"</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">"Opriți acum"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Extindeți"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Restrângeți"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Ecranul este fixat"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Ecranul este afișat până anulați fixarea. Apăsați lung pe Înapoi și pe Vizualizare generală simultan pentru a anula fixarea."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Ecranul este afișat până anulați fixarea. Apăsați lung pe Vizualizare generală pentru a anula fixarea."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Ecranul este afișat până anulați fixarea. Atingeți lung opțiunea Înapoi pentru a anula fixarea."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Am înțeles"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Nu, mulțumesc"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Ascundeți <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -406,6 +403,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Refuzați"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> afișează caseta de dialog pentru volum"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Atingeți pentru a reveni la setarea inițială."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Acum folosiți profilul de serviciu"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Afișați procentajul bateriei încorporat"</string>
@@ -440,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Afișează secundele pe ceas în bara de stare. Poate afecta autonomia bateriei."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Rearanjați Setările rapide"</string>
<string name="show_brightness" msgid="6613930842805942519">"Afișați luminozitatea în Setările rapide"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Folosiți paginarea în Setările rapide"</string>
<string name="experimental" msgid="6198182315536726162">"Experimentale"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Activați Bluetooth?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Pentru a conecta tastatura la tabletă, mai întâi trebuie să activați Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Activați"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 39d8617..96971ea 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -152,9 +152,7 @@
<string name="accessibility_airplane_mode" msgid="834748999790763092">"Режим полета."</string>
<string name="accessibility_no_sims" msgid="3957997018324995781">"Нет SIM-карты."</string>
<string name="accessibility_carrier_network_change_mode" msgid="4017301580441304305">"Сменить сеть"</string>
- <!-- String.format failed for translation -->
- <!-- no translation found for accessibility_battery_level (7451474187113371965) -->
- <skip />
+ <string name="accessibility_battery_level" msgid="7451474187113371965">"Заряд батареи в процентах: <xliff:g id="NUMBER">%d</xliff:g>."</string>
<string name="accessibility_settings_button" msgid="799583911231893380">"Настройки"</string>
<string name="accessibility_notifications_button" msgid="4498000369779421892">"Уведомления"</string>
<string name="accessibility_remove_notification" msgid="3603099514902182350">"Удалить уведомление"</string>
@@ -390,15 +388,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Просматривайте уведомления на заблокированном экране."</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Закрыть"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Настроить"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Показать все"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Скрыть все"</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">"Завершить"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Развернуть"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Свернуть"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Блокировка в приложении включена"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Это приложение останется активным, пока вы не отмените блокировку, одновременно нажав кнопки \"Назад\" и \"Обзор\"."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Приложение останется активным, пока вы не отмените блокировку, одновременно нажав кнопки Назад и Обзор."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Приложение останется активным, пока вы не отмените блокировку, одновременно нажав кнопки \"Назад\" и \"Обзор\"."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"ОК"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Нет, спасибо"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Скрыть параметр \"<xliff:g id="TILE_LABEL">%1$s</xliff:g>\"?"</string>
@@ -409,6 +404,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Нет"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"Приложение <xliff:g id="APP_NAME">%1$s</xliff:g> назначено регулятором громкости"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Нажмите, чтобы восстановить приложение по умолчанию."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Вы перешли в рабочий профиль"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Показывать уровень заряда батареи в процентах"</string>
@@ -443,6 +439,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Показывать в строке состояния время с точностью до секунды (заряд батареи может расходоваться быстрее)."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Изменить порядок Быстрых настроек"</string>
<string name="show_brightness" msgid="6613930842805942519">"Добавить яркость в Быстрые настройки"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Разбить Быстрые настройки на страницы"</string>
<string name="experimental" msgid="6198182315536726162">"Экспериментальная функция"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Подключение по Bluetooth"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Чтобы подключить клавиатуру к планшету, включите Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Включить"</string>
</resources>
diff --git a/packages/SystemUI/res/values-si-rLK/strings.xml b/packages/SystemUI/res/values-si-rLK/strings.xml
index 51bb047..64d460e 100644
--- a/packages/SystemUI/res/values-si-rLK/strings.xml
+++ b/packages/SystemUI/res/values-si-rLK/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"ඔබ අඟුළු හැරීමට කලින් ඒවා බලන්න"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"එපා ස්තූතියි"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"සකසන්න"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"සියල්ල බලන්න"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"සියල්ල සඟවන්න"</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">"දැන් අවසන් කරන්න"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"දිග හරින්න"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"හකුළන්න"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"තීරය අමුණන ලදි"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"ඔබ ගලවන තෙක් එය දර්ශනය තුළ මෙය තබයි. ගැලවීමට ආපසු සහ දළ විශ්ලේෂණය එකම වේලාවක ස්පර්ෂ කර අල්ලා සිටින්න."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"ඔබ ගලවන තෙක් එය දර්ශනය තුළ මෙය තබයි. ගැලවීමට දළ විශ්ලේෂණය ස්පර්ෂ කර අල්ලා සිටින්න."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"මෙය ඔබ ගලවන තෙක් එය දසුන තුළ තබයි. ගැලවීමට ස්පර්ශ කර අල්ලාගෙන සිටින්න."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"හරි, තේරුණා"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"එපා ස්තූතියි"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> සඟවන්නද?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"ප්රතික්ෂේප කරන්න"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> ධාරිතා සංවාදයයි"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"මුල් තත්ත්වය නැවත ප්රතිසාධනය කිරීමට ස්පර්ශ කරන්න."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"ඔබ ඔබේ කාර්යාල පැතිකඩ භාවිත කරමින් සිටී"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"පද්ධති UI සුසරකය"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"කාවද්දන ලද බැටරි ප්රතිශතය පෙන්වන්න"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"තත්ත්ව තීරුවෙහි ඔරලෝසු තත්පර පෙන්වන්න. බැටරි ආයු කාලයට බලපෑමට හැකිය."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"ඉක්මන් සැකසීම් යළි පිළිවෙළට සකසන්න"</string>
<string name="show_brightness" msgid="6613930842805942519">"ඉක්මන් සැකසීම්වල දීප්තිය පෙන්වන්න"</string>
- <string name="qs_paging" msgid="7020133150248666132">"ඉක්මන් සැකසීම්වල පිටු පිරිසැලසුම් භාවිත කරන්න"</string>
<string name="experimental" msgid="6198182315536726162">"පරීක්ෂණාත්මක"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"බ්ලූටූත් ක්රියාත්මක කරන්නද?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"ඔබේ යතුරු පුවරුව ඔබේ ටැබ්ලට් පරිගණකයට සම්බන්ධ කිරීමට, ඔබ පළමුව බ්ලූටූත් ක්රියාත්මක කළ යුතුය."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"ක්රියාත්මක කරන්න"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 2886e36..def49e9 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -152,9 +152,7 @@
<string name="accessibility_airplane_mode" msgid="834748999790763092">"Režim v lietadle."</string>
<string name="accessibility_no_sims" msgid="3957997018324995781">"Žiadna SIM karta."</string>
<string name="accessibility_carrier_network_change_mode" msgid="4017301580441304305">"Zmena siete operátora"</string>
- <!-- String.format failed for translation -->
- <!-- no translation found for accessibility_battery_level (7451474187113371965) -->
- <skip />
+ <string name="accessibility_battery_level" msgid="7451474187113371965">"Batéria <xliff:g id="NUMBER">%d</xliff:g> percent."</string>
<string name="accessibility_settings_button" msgid="799583911231893380">"Nastavenia systému."</string>
<string name="accessibility_notifications_button" msgid="4498000369779421892">"Upozornenia."</string>
<string name="accessibility_remove_notification" msgid="3603099514902182350">"Vymazať upozornenie."</string>
@@ -390,15 +388,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Zobraziť pred odomknutím"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Nie, vďaka"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Nastaviť"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Zobraziť všetko"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Skryť všetko"</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">"Skončiť"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Rozbaliť"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Zbaliť"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Obrazovka je pripnutá"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Obsah bude pripnutý v zobrazení, dokým ho neuvoľníte. Ak ho chcete uvoľniť, stlačte a podržte súčasne tlačidlá Späť a Prehľad."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Obsah bude pripnutý v zobrazení, dokým ho neuvoľníte. Uvoľníte ho stlačením a podržaním tlačidla Prehľad."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Obsah bude pripnutý v zobrazení, dokým ho neuvoľníte. Uvoľníte ho stlačením a podržaním tlačidla Späť."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Dobre"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Nie, vďaka"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Skryť <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -409,6 +404,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Odmietnuť"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> je dialóg hlasitosti"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Klepnutím obnovíte originál."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Používate svoj pracovný profil."</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Tuner používateľského rozhrania systému"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Zobraziť percentá vloženej batérie"</string>
@@ -443,6 +439,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Zobrazí sekundy v stavovom riadku. Môže to ovplyvňovať výdrž batérie."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Zmeniť usporiadanie Rýchlych nastavení"</string>
<string name="show_brightness" msgid="6613930842805942519">"Zobraziť jas v Rýchlych nastaveniach"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Použite stránkovanie v Rýchlych nastaveniach"</string>
<string name="experimental" msgid="6198182315536726162">"Experimentálne"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Zapnúť Bluetooth?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Ak chcete klávesnicu pripojiť k tabletu, najprv musíte zapnúť Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Zapnúť"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index e325ef1..14d3bdb 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -388,15 +388,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Oglejte si jih pred odklepanjem"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Ne, hvala"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Nastavitev"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Pokaži vse"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Skrij vse"</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">"Končaj zdaj"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Razširi"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Strni"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Zaslon je pripet"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"S tem ostane zaslon v pogledu, dokler ga ne odpnete. Hkrati pridržite tipko za nazaj in tipko za pregled, če ga želite odpeti."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"S tem ostane zaslon v pogledu, dokler ga ne odpnete. Pridržite tipko za pregled, če ga želite odpeti."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"S tem ostane zaslon v pogledu, dokler ga ne odpnete. Pridržite tipko za nazaj, če ga želite odpeti."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Razumem"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Ne, hvala"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Želite skriti <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -407,6 +404,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Zavrni"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> je pogovorno okno glede prostornine"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Dotaknite se, če želite obnoviti izvirnik."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Uporabljate delovni profil"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Uglaševalnik uporabniškega vmesnika sistema"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Prikaži odstotek napolnjenosti vgraj. akumulatorja"</string>
@@ -441,6 +439,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Prikaže sekunde pri uri v vrstici stanja. To lahko vpliva na čas delovanja pri akumulatorskem napajanju."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Preuredi hitre nastavitve"</string>
<string name="show_brightness" msgid="6613930842805942519">"Prikaz svetlosti v hitrih nastavitvah"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Uporaba postavitve strani v hitrih nastavitvah"</string>
<string name="experimental" msgid="6198182315536726162">"Poskusno"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Želite vklopiti Bluetooth?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Če želite povezati tipkovnico in tablični računalnik, vklopite Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Vklop"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sq-rAL/strings.xml b/packages/SystemUI/res/values-sq-rAL/strings.xml
index 5bcf941..0316c24 100644
--- a/packages/SystemUI/res/values-sq-rAL/strings.xml
+++ b/packages/SystemUI/res/values-sq-rAL/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Shikoji para se t\'i shkyçësh"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Jo, faleminderit!"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Konfiguro"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Shikoji të gjitha"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Fshihi të gjitha"</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">"Përfundo tani"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Zgjeroje"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Mbylle"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Ekrani u gozhdua"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Kjo e ruan në pamje derisa e heq nga gozhdimi. Prek dhe mbaj shtypur njëkohësisht \"Prapa\" dhe \"Përmbledhje\" për ta hequr nga gozhdimi."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Kjo e ruan në pamje derisa e heq nga gozhdimi. Prek dhe mbaj shtypur njëkohësisht \"Përmbledhje\" për ta hequr nga gozhdimi."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Kjo e ruan në pamje deri sa ta heqësh nga gozhdimi. Prek dhe mbaj të shtypur \"Prapa\" për ta hequr nga gozhdimi."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"E kuptova!"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Jo, faleminderit!"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Të fshihet <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Refuzo"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> është dialogu i volumit"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Prek për të restauruar origjinalin."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Po përdor profilin tënd të punës"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Sintonizuesi i Sistemit të Ndërfaqes së Përdoruesit"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Shfaq përqindjen e baterisë së integruar"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Trego sekondat e orës në shiritin e statusit. Mund të ndikojë te jeta e baterisë."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Risistemo Cilësimet e shpejta"</string>
<string name="show_brightness" msgid="6613930842805942519">"Shfaq ndriçimin te Cilësimet e shpejta"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Përdor faqosjen te Cilësimet e shpejta"</string>
<string name="experimental" msgid="6198182315536726162">"Eksperimentale"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Të aktivizohet \"bluetooth-i\"?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Për të lidhur tastierën me tabletin, në fillim duhet të aktivizosh \"bluetooth-in\"."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Aktivizo"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 522b2af..2139441 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -387,15 +387,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Прегледајте их пре откључавања"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Не, хвала"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Подеси"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Прикажи све"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Сакриј све"</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">"Прекини одмах"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Прошири"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Скупи"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Екран је закачен"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"На овај начин ово остаје приказано док га не откачите. Истовремено додирните и задржите Назад и Преглед да бисте га откачили."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"На овај начин ово остаје приказано док га не откачите. Додирните и задржите Преглед да бисте га откачили."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Због тога се он стално приказује док га не откачите. Додирните и задржите Назад да бисте га откачили."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Важи"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Не, хвала"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Желите ли да сакријете <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -406,6 +403,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Одбиј"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> је дијалог за јачину звука"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Додирните да бисте вратили оригинал."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Користите профил за Work"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Тјунер за кориснички интерфејс система"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Приказуј уграђени проценат батерије"</string>
@@ -440,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Секунде на сату се приказују на статусној траци. То може да утиче на трајање батерије."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Преуреди Брза подешавања"</string>
<string name="show_brightness" msgid="6613930842805942519">"Прикажи осветљеност у Брзим подешавањима"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Користи листање страница у Брзим подешавањима"</string>
<string name="experimental" msgid="6198182315536726162">"Експериментално"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Желите ли да укључите Bluetooth?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Да бисте повезали тастатуру са таблетом, прво морате да укључите Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Укључи"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 7323126..ad7bffd 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -313,10 +313,10 @@
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Mindre brådskande aviseringar nedan"</string>
<string name="notification_tap_again" msgid="8524949573675922138">"Tryck igen för att öppna"</string>
- <string name="keyguard_unlock" msgid="8043466894212841998">"Dra uppåt om du vill låsa upp"</string>
- <string name="phone_hint" msgid="4872890986869209950">"Dra från ikonen och öppna telefonen"</string>
- <string name="voice_hint" msgid="8939888732119726665">"Dra från ikonen och öppna röstassistenten"</string>
- <string name="camera_hint" msgid="7939688436797157483">"Dra från ikonen och öppna kameran"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Svep uppåt om du vill låsa upp"</string>
+ <string name="phone_hint" msgid="4872890986869209950">"Svep från ikonen och öppna telefonen"</string>
+ <string name="voice_hint" msgid="8939888732119726665">"Svep från ikonen och öppna röstassistenten"</string>
+ <string name="camera_hint" msgid="7939688436797157483">"Svep från ikonen och öppna kameran"</string>
<string name="interruption_level_none_with_warning" msgid="5114872171614161084">"Total tystnad. Även skärmläsningsprogram tystas."</string>
<string name="interruption_level_none" msgid="6000083681244492992">"Helt tyst"</string>
<string name="interruption_level_priority" msgid="6426766465363855505">"Bara prioriterade"</string>
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Visa dem innan du låser upp"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Nej tack"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Konfig."</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Visa alla"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Dölj alla"</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">"Sluta nu"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Utöka"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Komprimera"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Skärmen har fästs"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Med den här funktionen är skärmen synlig tills du lossar den. Tryck länge på Tillbaka och Översikt samtidigt om du vill lossa skärmen."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Detta visar skärmen tills du lossar den. Tryck länge på Översikt om du vill lossa skärmen."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Detta visar skärmen tills du lossar den. Tryck länge på Tillbaka om du vill lossa skärmen."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"OK"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Nej tack"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Vill du dölja <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Neka"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> används som volymkontroll"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Tryck här om du vill återställa den ursprungliga appen."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Du använder din jobbprofil"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Inställningar för systemgränssnitt"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Visa inbäddad batteriprocent"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Visa klocksekunder i statusfältet. Detta kan påverka batteritiden."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Ordna snabbinställningarna"</string>
<string name="show_brightness" msgid="6613930842805942519">"Visa ljusstyrka i snabbinställningarna"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Använd sidindelning i snabbinställningarna"</string>
<string name="experimental" msgid="6198182315536726162">"Experimentella"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Vill du aktivera Bluetooth?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Om du vill ansluta tangentbordet till surfplattan måste du först aktivera Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Aktivera"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 1cf4293..244e58d 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Zitazame kabla hujafungua"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Hapana, asante"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Sanidi"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Angalia zote"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Ficha zote"</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">"Komesha sasa"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Panua"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Kunja"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Skrini imebandikwa"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Hii itaendelea kuonyesha hadi ubandue. Gusa na ushikilie Nyuma na Muhtasari kwa wakati mmoja ili ubandue."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Hii itaendelea kuonyesha hadi uibandue. Gusa na ushikilie Muhtasari ili ubandue."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Hii itaendelea kuonyesha hadi uibandue. Gusa na ushikilie Nyuma ili ubandue."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Nimeelewa"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Hapana, asante"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Ungependa kuficha <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Kataa"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> ni mazungumzo ya sauti"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Gusa ili urejeshe ya awali."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Unatumia wasifu wako wa kazini"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Kipokea Ishara cha SystemUI"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Onyesha asilimia ya betri iliyopachikwa"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Onyesha sekunde za saa katika sehemu ya arifa. Inaweza kuathiri muda wa matumizi ya betri."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Panga Upya Mipangilio ya Haraka"</string>
<string name="show_brightness" msgid="6613930842805942519">"Onyesha unga\'avu katika Mipangilio ya Haraka"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Tumia nambari za ukurasa katika Mipangilio ya Haraka"</string>
<string name="experimental" msgid="6198182315536726162">"Ya majaribio"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Je, ungependa kuwasha Bluetooth?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Ili uunganishe Kibodi yako kwenye kompyuta yako kibao, lazima kwanza uwashe Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Washa"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ta-rIN/strings.xml b/packages/SystemUI/res/values-ta-rIN/strings.xml
index 670bccc..9bd280d 100644
--- a/packages/SystemUI/res/values-ta-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ta-rIN/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"திறக்கும் முன் அவற்றைப் பார்க்கவும்"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"வேண்டாம்"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"அமை"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"எல்லாம் காட்டு"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"எல்லாம் மறை"</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">"இப்போது முடி"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"விரிவாக்கு"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"சுருக்கு"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"திரை பொருத்தப்பட்டது"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"பொருத்தியதை விலக்கும்வரை இதைக் காட்சியில் வைக்கும். விலக்க, பின் மற்றும் மேலோட்டப் பார்வையை ஒரே நேரத்தில் தொட்டுப் பிடிக்கவும்."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"பொருத்தியதை விலக்கும்வரை இதைக் காட்சியில் வைக்கும். விலக்க, மேலோட்டப் பார்வையைத் தொட்டுப் பிடிக்கவும்."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"பொருத்தியதை விலக்கும்வரை இதைக் காட்சியில் வைக்கும். விலக்க, முந்தையது என்பதைத் தொட்டுப் பிடிக்கவும்."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"புரிந்தது"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"வேண்டாம்"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g>ஐ மறைக்கவா?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"நிராகரி"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"ஒலியளவு செய்தி: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"அசலை மீட்டமைக்கத் தொடவும்."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"பணி சுயவிவரத்தைப் பயன்படுத்துகிறீர்கள்"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"உள்ளிணைந்த பேட்டரி சதவீதத்தைக் காட்டு"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"நிலைப் பட்டியில் கடிகார வினாடிகளைக் காட்டும். பேட்டரியின் ஆயுளைக் குறைக்கலாம்."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"விரைவு அமைப்புகளை மறுவரிசைப்படுத்து"</string>
<string name="show_brightness" msgid="6613930842805942519">"விரைவு அமைப்புகளில் ஒளிர்வுப் பட்டியைக் காட்டு"</string>
- <string name="qs_paging" msgid="7020133150248666132">"விரைவு அமைப்புகளில் புதிய பக்கத் தளவமைப்பைப் பயன்படுத்து"</string>
<string name="experimental" msgid="6198182315536726162">"சோதனை முயற்சி"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"புளூடூத்தை இயக்கவா?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"உங்கள் டேப்லெட்டுடன் விசைப்பலகையை இணைக்க, முதலில் புளூடூத்தை இயக்க வேண்டும்."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"இயக்கு"</string>
</resources>
diff --git a/packages/SystemUI/res/values-te-rIN/strings.xml b/packages/SystemUI/res/values-te-rIN/strings.xml
index bbd9979..6e16a95 100644
--- a/packages/SystemUI/res/values-te-rIN/strings.xml
+++ b/packages/SystemUI/res/values-te-rIN/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"వీటిని మీరు అన్లాక్ చేయకముందే చూడండి"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"వద్దు, ధన్యవాదాలు"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"సెటప్ చేయి"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"అన్నీ చూడండి"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"అన్నీ దాచిపెట్టు"</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">"ఇప్పుడే ముగించు"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"విస్తరింపజేయండి"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"కుదించండి"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"స్క్రీన్ పిన్ చేయబడింది"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"ఇది మీరు అన్పిన్ చేసే వరకు దీన్ని వీక్షణలో ఉంచుతుంది. అన్పిన్ చేయడానికి వెనుకకు మరియు స్థూలదృష్టిని ఒకేసారి తాకి, ఉంచండి."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"ఇది మీరు అన్పిన్ చేసే వరకు దీన్ని వీక్షణలో ఉంచుతుంది. అన్పిన్ చేయడానికి స్థూలదృష్టిని తాకి, ఉంచండి."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"ఇది మీరు అన్పిన్ చేసే వరకు దీన్ని వీక్షణలో ఉంచుతుంది. అన్పిన్ చేయడానికి వెనుకకు బటన్ను తాకి, ఉంచండి."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"అర్థమైంది"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"వద్దు, ధన్యవాదాలు"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g>ని దాచాలా?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"తిరస్కరించు"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> అనేది వాల్యూమ్ డైలాగ్"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"అసలుదాన్ని పునరుద్ధరించడానికి తాకండి."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"మీరు మీ కార్యాలయ ప్రొఫైల్ను ఉపయోగిస్తున్నారు"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"సిస్టమ్ UI ట్యూనర్"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"పొందుపరిచిన బ్యాటరీ శాతం చూపు"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"స్థితి పట్టీలో గడియారం సెకన్లు చూపుతుంది. బ్యాటరీ శక్తి ప్రభావితం చేయవచ్చు."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"శీఘ్ర సెట్టింగ్ల ఏర్పాటు క్రమం మార్చు"</string>
<string name="show_brightness" msgid="6613930842805942519">"శీఘ్ర సెట్టింగ్ల్లో ప్రకాశం చూపు"</string>
- <string name="qs_paging" msgid="7020133150248666132">"శీఘ్ర సెట్టింగ్ల్లో పేజింగ్ను ఉపయోగించు"</string>
<string name="experimental" msgid="6198182315536726162">"ప్రయోగాత్మకం"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"బ్లూటూత్ ఆన్ చేయాలా?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"మీ కీబోర్డ్ను మీ టాబ్లెట్తో కనెక్ట్ చేయడానికి, మీరు ముందుగా బ్లూటూత్ ఆన్ చేయాలి."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"ఆన్ చేయి"</string>
</resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 2486519..256b497 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"ดูก่อนปลดล็อก"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"ไม่เป็นไร"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"ตั้งค่า"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"ดูทั้งหมด"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"ซ่อนทั้งหมด"</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">"ไม่ใช้แล้ว"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"ขยาย"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"ยุบ"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"ตรึงหน้าจอแล้ว"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"การดำเนินการนี้จะเปิดหน้าจอนี้ไว้เสมอจนกว่าคุณจะเลิกตรึง แตะ \"กลับ\" และ \"ภาพรวม\" พร้อมกันค้างไว้เพื่อเลิกตรึง"</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"การดำเนินการนี้จะเปิดหน้าจอนี้ไว้เสมอจนกว่าคุณจะเลิกตรึง แตะ \"ภาพรวม\" ค้างไว้เพื่อเลิกตรึง"</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"การดำเนินการนี้จะแสดงหน้าจอนี้ไว้เสมอจนกว่าคุณจะเลิกตรึง แตะ \"กลับ\" ค้างไว้เพื่อเลิกตรึง"</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"รับทราบ"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"ไม่เป็นไร ขอบคุณ"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"ซ่อน <xliff:g id="TILE_LABEL">%1$s</xliff:g> ไหม"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"ปฏิเสธ"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> เป็นช่องโต้ตอบระดับเสียง"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"แตะเพื่อคืนค่าดั้งเดิม"</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"คุณกำลังใช้โปรไฟล์งานของคุณ"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"ตัวรับสัญญาณ UI ระบบ"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"แสดงเปอร์เซ็นต์ของแบตเตอรี่ในตัว"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"แสดงวินาทีของนาฬิกาในแถบสถานะ อาจส่งผลต่ออายุแบตเตอรี"</string>
<string name="qs_rearrange" msgid="8060918697551068765">"จัดเรียงการตั้งค่าด่วนใหม่"</string>
<string name="show_brightness" msgid="6613930842805942519">"แสดงความสว่างในการตั้งค่าด่วน"</string>
- <string name="qs_paging" msgid="7020133150248666132">"ใช้การแบ่งหน้าในการตั้งค่าด่วน"</string>
<string name="experimental" msgid="6198182315536726162">"ทดสอบ"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"เปิดบลูทูธไหม"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"หากต้องการเชื่อมต่อแป้นพิมพ์กับแท็บเล็ต คุณต้องเปิดบลูทูธก่อน"</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"เปิด"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 3d1243e..66b0b6b 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Tingnan ang mga ito bago ka mag-unlock"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Hindi"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"I-set up"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Tingnan lahat"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Itago lahat"</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">"Tapusin ngayon"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Palawakin"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"I-collapse"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Naka-pin ang screen"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Pinapanatili nitong nasa view ito hanggang sa mag-unpin ka. Pindutin nang matagal ang Bumalik at Pangkalahatang-ideya nang sabay upang mag-unpin."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Pinapanatili nitong nasa view ito hanggang sa mag-unpin ka. Pindutin nang matagal ang Pangkalahatang-ideya upang mag-unpin."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Pinapanatili nitong nasa view ito hanggang sa mag-unpin ka. Pindutin nang matagal ang Bumalik upang mag-unpin."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Nakuha ko"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Hindi, salamat na lang"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Itago ang <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Tanggihan"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"Ang <xliff:g id="APP_NAME">%1$s</xliff:g> ang volume dialog"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Pindutin upang ibalik ang orihinal."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Ginagamit mo ang iyong profile sa trabaho"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Tuner ng System UI"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Ipakita ang naka-embed na porsyento ng baterya"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Ipakita ang mga segundo ng orasan sa status bar. Maaaring makaapekto sa tagal ng baterya."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Ayusing Muli ang Mga Mabilisang Setting"</string>
<string name="show_brightness" msgid="6613930842805942519">"Ipakita ang liwanag sa Mga Mabilisang Setting"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Gamitin ang paging sa Mga Mabilisang Setting"</string>
<string name="experimental" msgid="6198182315536726162">"Pang-eksperimento"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"I-on ang Bluetooth?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Upang ikonekta ang iyong keyboard sa iyong tablet, kailangan mo munang i-on ang Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"I-on"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 4dadfbc..5a6e5db 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Kilidi açmadan bildirimleri görün"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Hayır, teşekkürler"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Kur"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Tümünü göster"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Tümünü gizle"</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">"Şimdi sona erdir"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Genişlet"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Daralt"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Ekran sabitlendi"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Böylece siz sabitlemesini kaldırana kadar görüntülenmeye devam eder. Sabitlemeyi kaldırmak için Geri ve Genel Bakış öğesine aynı anda dokunun ve basılı tutun."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Böylece siz sabitlemesini kaldırana kadar görüntülenmeye devam eder. Sabitlemeyi kaldırmak için Genel Bakış\'a dokunun ve basılı tutun."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Siz sabitlemeyi kaldırana kadar görüntülenmeye devam eder. Sabitlemeyi kaldırmak için Geri\'ye dokunun ve basılı tutun."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Anladım"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Hayır, teşekkürler"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> gizlensin mi?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Reddet"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> ses denetimi iletişim kutusu olarak ayarlandı"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Orijinali geri yüklemek için dokunun."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"İş profilinizi kullanıyorsunuz"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Sistem Arayüzü Ayarlayıcısı"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Yerleşik pil yüzdesini göster"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Durum çubuğunda saatin saniyelerini gösterir. Pil ömrünü etkileyebilir."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Hızlı Ayarlar\'ı Yeniden Düzenle"</string>
<string name="show_brightness" msgid="6613930842805942519">"Hızlı Ayarlar\'da parlaklığı göster"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Hızlı Ayarlar\'da sayfa ayırmayı kullan"</string>
<string name="experimental" msgid="6198182315536726162">"Deneysel"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Bluetooth açılsın mı?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Klavyenizi tabletinize bağlamak için önce Bluetooth\'u açmanız gerekir."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Aç"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index d158dbe..afa9077 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -152,7 +152,7 @@
<string name="accessibility_airplane_mode" msgid="834748999790763092">"Режим польоту."</string>
<string name="accessibility_no_sims" msgid="3957997018324995781">"Немає SIM-карти."</string>
<string name="accessibility_carrier_network_change_mode" msgid="4017301580441304305">"Змінення мережі оператора."</string>
- <string name="accessibility_battery_level" msgid="7451474187113371965">"Заряд акумулятора: <xliff:g id="NUMBER">%d</xliff:g>."</string>
+ <string name="accessibility_battery_level" msgid="7451474187113371965">"Заряд акумулятора у відсотках: <xliff:g id="NUMBER">%d</xliff:g>."</string>
<string name="accessibility_settings_button" msgid="799583911231893380">"Налаштування системи."</string>
<string name="accessibility_notifications_button" msgid="4498000369779421892">"Сповіщення."</string>
<string name="accessibility_remove_notification" msgid="3603099514902182350">"Очистити сповіщення."</string>
@@ -388,15 +388,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Переглядайте сповіщення, перш ніж розблокувати екран"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Ні, дякую"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Налаштув."</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Показати всі"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Сховати всі"</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">"Закрити"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Розгорнути"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Згорнути"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Екран закріплено"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Закріпить екран, щоб ви могли постійно його бачити, доки не відкріпите. Щоб відкріпити, одночасно натисніть і втримуйте кнопки \"Назад\" і \"Огляд\"."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Закріпить екран, щоб ви могли постійно його бачити, доки не відкріпите. Щоб відкріпити, натисніть і втримуйте кнопку \"Огляд\"."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Ви постійно бачитимете екран, доки не відкріпите його. Щоб відкріпити екран, натисніть і утримуйте кнопку \"Назад\"."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Зрозуміло"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Ні, дякую"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Сховати <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -407,6 +404,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Відхилити"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> призначено регулятором гучності"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Торкніться, щоб відновити оригінал."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Ви в робочому профілі"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Показувати заряд акумулятора у відсотках"</string>
@@ -441,6 +439,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Показувати секунди на годиннику в рядку стану. Акумулятор може розряджатися швидше."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Упорядкувати швидкі налаштування"</string>
<string name="show_brightness" msgid="6613930842805942519">"Показувати панель яскравості у швидких налаштуваннях"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Показувати швидкі налаштування у вигляді сторінок"</string>
<string name="experimental" msgid="6198182315536726162">"Експериментальні налаштування"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Увімкнути Bluetooth?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Щоб під’єднати клавіатуру до планшета, спершу потрібно ввімкнути Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Увімкнути"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ur-rPK/strings.xml b/packages/SystemUI/res/values-ur-rPK/strings.xml
index f8d8c71..75160cd9 100644
--- a/packages/SystemUI/res/values-ur-rPK/strings.xml
+++ b/packages/SystemUI/res/values-ur-rPK/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"غیر مقفل کرنے سے پہلے انہیں دیکھیں"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"نہیں شکریہ"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"ترتیب دیں"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"سبھی دیکھیں"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"سبھی چھپائیں"</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">"ابھی ختم کریں"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"پھیلائیں"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"سکیڑیں"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"اسکرین پن کردہ ہے"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"یہ اس کو اس وقت تک منظر میں رکھتا ہے جب تک آپ اس سے پن نہیں ہٹا دیتے۔ پن ہٹانے کیلئے واپس اور عمومی جائزہ کو ایک ساتھ ٹچ کریں اور پکڑ کر رکھیں۔"</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"یہ اس کو اس وقت تک منظر میں رکھتا ہے جب تک آپ اس سے پن نہیں ہٹا دیتے۔ پن ہٹانے کیلئے عمومی جائزہ کو ٹچ کریں اور پکڑ کر رکھیں۔"</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"یہ اس کو اس وقت تک منظر میں رکھتا ہے جب تک آپ اس سے پن ہٹا نہیں دیتے۔ پن ہٹانے کیلئے پیچھے بٹن کو ٹچ کریں اور دبائے رکھیں۔"</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"سمجھ آ گئی"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"نہیں شکریہ"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> کو چھپائیں؟"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"مسترد کریں"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> والیوم ڈائلاگ ہے"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"اصل کو بحال کرنے کیلئے ٹچ کریں۔"</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">"،"</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"آپ اپنا دفتری پروفائل استعمال کر رہے ہیں۔"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"سسٹم UI ٹیونر"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"سرایت کردہ بیٹری کی فیصد دکھائیں"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"گھڑی کے سیکنڈز اسٹیٹس بار میں دکھائیں۔ اس کا بیٹری کی زندگی پر اثر پڑ سکتا ہے۔"</string>
<string name="qs_rearrange" msgid="8060918697551068765">"فوری ترتیبات کو دوبارہ ترتیب دیں"</string>
<string name="show_brightness" msgid="6613930842805942519">"فوری ترتیبات میں چمکیلا پن دکھائیں"</string>
- <string name="qs_paging" msgid="7020133150248666132">"فوری ترتیبات میں پیجنگ استعمال کریں"</string>
<string name="experimental" msgid="6198182315536726162">"تجرباتی"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"بلوٹوتھ آن کریں؟"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"اپنے کی بورڈ کو اپنے ٹیبلٹ کے ساتھ منسلک کرنے کیلئے پہلے آپ کو اپنا بلو ٹوتھ آن کرنا ہو گا۔"</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"آن کریں"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uz-rUZ/strings.xml b/packages/SystemUI/res/values-uz-rUZ/strings.xml
index 0186809..5ddd115 100644
--- a/packages/SystemUI/res/values-uz-rUZ/strings.xml
+++ b/packages/SystemUI/res/values-uz-rUZ/strings.xml
@@ -145,10 +145,10 @@
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Rouming"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
<string name="accessibility_data_connection_wifi" msgid="2324496756590645221">"Wi-Fi"</string>
- <string name="accessibility_no_sim" msgid="8274017118472455155">"SIM-karta yo‘q."</string>
+ <string name="accessibility_no_sim" msgid="8274017118472455155">"SIM karta yo‘q."</string>
<string name="accessibility_bluetooth_tether" msgid="4102784498140271969">"Bluetooth modem"</string>
<string name="accessibility_airplane_mode" msgid="834748999790763092">"Parvoz rejimi"</string>
- <string name="accessibility_no_sims" msgid="3957997018324995781">"Hech qanday SIM-karta yo‘q."</string>
+ <string name="accessibility_no_sims" msgid="3957997018324995781">"Hech qanday SIM karta yo‘q."</string>
<string name="accessibility_carrier_network_change_mode" msgid="4017301580441304305">"Mobil tarmoqni o‘zgartirish"</string>
<string name="accessibility_battery_level" msgid="7451474187113371965">"Batareya <xliff:g id="NUMBER">%d</xliff:g> foiz."</string>
<string name="accessibility_settings_button" msgid="799583911231893380">"Tizim sozlamalari."</string>
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Ularni qulfdan chiqarishdan oldin ko‘ring"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Yo‘q, kerak emas"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Sozlash"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Barchasini ko‘rsatish"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Barchasini yashirish"</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">"Tugatish"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Yoyish"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Yig‘ish"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Ekran qadaldi"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Ekran yechilmaguncha u qadalgan holatda qoladi. Uni yechish uchun “Orqaga” va “Umumiy nazar” tugmalarini bir vaqtda bosing va ushlab turing."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Ekran yechilmaguncha u qadalgan holatda qoladi. Uni yechish uchun “Umumiy nazar” tugmasini bosing va ushlab turing."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Ekran yechilmaguncha u o‘zgarmas holatda qoladi. Uni yechish uchun “Orqaga” tugmasini bosing va ushlab turing."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"OK"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Yo‘q, kerakmas"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> berkitilsinmi?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Rad etish"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> ovoz balandligini boshqaradi"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Aslini tiklash uchun bosing."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Siz ishchi profildan foydalanmoqdasiz"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"SystemUI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Batareya foizi ko‘rsatilsin"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Holat panelida soat soniyalari ko‘rsatilsin. Bu batareya resursiga ta’sir qilishi mumkin."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Tezkor sozlamalarni qayta tartiblash"</string>
<string name="show_brightness" msgid="6613930842805942519">"Tezkor sozlamalarda yorqinlikni ko‘rsatish"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Tezkor sozlamalarni sahifalarga ajratish"</string>
<string name="experimental" msgid="6198182315536726162">"Tajribaviy"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Bluetooth yoqilsinmi?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Klaviaturani planshetingizga ulash uchun Bluetooth xizmatini yoqishingiz kerak."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Yoqish"</string>
</resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 49b5d1d..1a5cc8b 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Xem thông báo trước khi bạn mở khóa"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Ko, cảm ơn"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Thiết lập"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Xem tất cả"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Ẩn tất cả"</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">"Kết thúc bây giờ"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Mở rộng"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Thu gọn"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Màn hình được ghim"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Thao tác này sẽ duy trì hiển thị màn hình cho đến khi bạn bỏ ghim. Chạm và giữ nút Quay lại và Tổng quan cùng một lúc để bỏ ghim."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Thao tác này sẽ duy trì hiển thị màn hình cho đến khi bạn bỏ ghim. Chạm và giữ nút Quay lại để bỏ ghim."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Thao tác này sẽ duy trì hiển thị màn hình cho đến khi bạn bỏ ghim. Chạm và giữ Quay lại để bỏ ghim."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Ok"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Không, cảm ơn"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Ẩn <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Từ chối"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> là hộp thoại khối lượng"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Chạm để khôi phục bản gốc."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Bạn đang sử dụng hồ sơ công việc của mình"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Bộ điều hướng giao diện người dùng hệ thống"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Hiển thị tỷ lệ phần trăm pin được nhúng"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Hiển thị giây đồng hồ trong thanh trạng thái. Có thể ảnh hưởng đến thời lượng pin."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Sắp xếp lại Cài đặt nhanh"</string>
<string name="show_brightness" msgid="6613930842805942519">"Hiển thị độ sáng trong Cài đặt nhanh"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Sử dụng đánh số trang trong Cài đặt nhanh"</string>
<string name="experimental" msgid="6198182315536726162">"Thử nghiệm"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Bật Bluetooth?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Để kết nối bàn phím với máy tính bảng, trước tiên, bạn phải bật Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Bật"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 93ed5ca..0c6b2c9 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -150,9 +150,7 @@
<string name="accessibility_airplane_mode" msgid="834748999790763092">"飞行模式。"</string>
<string name="accessibility_no_sims" msgid="3957997018324995781">"没有 SIM 卡。"</string>
<string name="accessibility_carrier_network_change_mode" msgid="4017301580441304305">"运营商网络正在更改。"</string>
- <!-- String.format failed for translation -->
- <!-- no translation found for accessibility_battery_level (7451474187113371965) -->
- <skip />
+ <string name="accessibility_battery_level" msgid="7451474187113371965">"电池电量为百分之 <xliff:g id="NUMBER">%d</xliff:g>。"</string>
<string name="accessibility_settings_button" msgid="799583911231893380">"系统设置。"</string>
<string name="accessibility_notifications_button" msgid="4498000369779421892">"通知。"</string>
<string name="accessibility_remove_notification" msgid="3603099514902182350">"清除通知。"</string>
@@ -388,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"无需解锁即可查看通知"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"不用了"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"设置"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"查看全部"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"全部隐藏"</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">"立即结束"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"展开"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"收起"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"已固定屏幕"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"这将会固定显示此屏幕,直到您取消固定为止。触摸并同时按住“返回”和“概览”即可取消固定屏幕。"</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"这将会固定显示此屏幕,直到您取消固定为止。触摸并按住“概览”即可取消固定屏幕。"</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"这将会固定显示此屏幕,直到您取消固定为止。触摸并按住“返回”即可取消固定屏幕。"</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"知道了"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"不用了"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"要隐藏“<xliff:g id="TILE_LABEL">%1$s</xliff:g>”吗?"</string>
@@ -407,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"拒绝"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”已用作音量控制对话框"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"触摸即可恢复原始设置。"</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">"、"</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"您当前正在使用工作资料"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"系统界面调谐器"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"嵌入式显示电池电量百分比 显示嵌入的电池电量百分比"</string>
@@ -441,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"在状态栏中显示时钟的秒数。这可能会影响电池的续航时间。"</string>
<string name="qs_rearrange" msgid="8060918697551068765">"重新排列快速设置"</string>
<string name="show_brightness" msgid="6613930842805942519">"在快速设置中显示亮度栏"</string>
- <string name="qs_paging" msgid="7020133150248666132">"在快速设置中使用分页功能"</string>
<string name="experimental" msgid="6198182315536726162">"实验性"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"要开启蓝牙吗?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"要将您的键盘连接到平板电脑,您必须先开启蓝牙。"</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"开启"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 2196111..ff97841 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -150,9 +150,7 @@
<string name="accessibility_airplane_mode" msgid="834748999790763092">"飛航模式。"</string>
<string name="accessibility_no_sims" msgid="3957997018324995781">"沒有 SIM 卡。"</string>
<string name="accessibility_carrier_network_change_mode" msgid="4017301580441304305">"流動網絡供應商網絡正在變更。"</string>
- <!-- String.format failed for translation -->
- <!-- no translation found for accessibility_battery_level (7451474187113371965) -->
- <skip />
+ <string name="accessibility_battery_level" msgid="7451474187113371965">"電池電量為百分之 <xliff:g id="NUMBER">%d</xliff:g>。"</string>
<string name="accessibility_settings_button" msgid="799583911231893380">"系統設定"</string>
<string name="accessibility_notifications_button" msgid="4498000369779421892">"通知。"</string>
<string name="accessibility_remove_notification" msgid="3603099514902182350">"清除通知。"</string>
@@ -388,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"解鎖前顯示"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"不用了,謝謝"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"設定"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"顯示全部"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"全部隱藏"</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">"立即終止"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"展開"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"收合"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"螢幕已固定"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"在您取消固定前,它會保持在檢視狀態。輕觸並同時按住 [返回] 和 [概覽],即可取消固定。"</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"在您取消固定前,它會保持在檢視狀態。輕觸並按住 [概覽] 即可取消固定。"</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"在您取消固定前,它會保持在檢視狀態。輕觸並按住 [返回] 即可取消固定。"</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"知道了"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"不用了,謝謝"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"隱藏 <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -407,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"拒絕"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」為音量對話框"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"輕觸即可復原。"</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">"、"</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"您正在使用工作設定檔"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"系統使用者介面調諧器"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"顯示嵌入的電池百分比"</string>
@@ -441,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"在狀態列中顯示時鐘秒數,但可能會影響電池壽命。"</string>
<string name="qs_rearrange" msgid="8060918697551068765">"重新排列快速設定"</string>
<string name="show_brightness" msgid="6613930842805942519">"在快速設定顯示亮度"</string>
- <string name="qs_paging" msgid="7020133150248666132">"在快速設定使用分頁"</string>
<string name="experimental" msgid="6198182315536726162">"實驗版"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"要開啟藍牙嗎?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"如要將鍵盤連接至平板電腦,請先開啟藍牙。"</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"開啟"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index fe4fe06..8d238d5 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -150,9 +150,7 @@
<string name="accessibility_airplane_mode" msgid="834748999790763092">"飛行模式。"</string>
<string name="accessibility_no_sims" msgid="3957997018324995781">"沒有 SIM 卡。"</string>
<string name="accessibility_carrier_network_change_mode" msgid="4017301580441304305">"行動通訊業者網路正在變更。"</string>
- <!-- String.format failed for translation -->
- <!-- no translation found for accessibility_battery_level (7451474187113371965) -->
- <skip />
+ <string name="accessibility_battery_level" msgid="7451474187113371965">"電池電量為百分之 <xliff:g id="NUMBER">%d</xliff:g>。"</string>
<string name="accessibility_settings_button" msgid="799583911231893380">"系統設定"</string>
<string name="accessibility_notifications_button" msgid="4498000369779421892">"通知。"</string>
<string name="accessibility_remove_notification" msgid="3603099514902182350">"清除通知。"</string>
@@ -388,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"解鎖前顯示"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"不用了,謝謝"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"設定"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"全部顯示"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"全部隱藏"</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">"立刻結束"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"展開"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"收合"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"螢幕已固定"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"這會讓目前的螢幕畫面保持顯示狀態,直到取消固定為止。同時按住返回按鈕和總覽按鈕即可取消固定。"</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"這會讓目前的螢幕畫面保持顯示狀態,直到取消固定為止。按住總覽按鈕即可取消固定。"</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"這會讓目前的螢幕畫面保持顯示狀態,直到取消固定為止。按住「返回」按鈕即可取消固定。"</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"知道了"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"不用了,謝謝"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"隱藏<xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -407,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"拒絕"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」現在是預設的音量控制對話方塊。"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"輕觸這裡即可恢復原始設定。"</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">"、"</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"您正在使用 Work 設定檔"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"系統使用者介面調整精靈"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"顯示嵌入式電池百分比"</string>
@@ -441,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"在狀態列中顯示時鐘秒數。這可能會影響電池續航力。"</string>
<string name="qs_rearrange" msgid="8060918697551068765">"重新排列快速設定"</string>
<string name="show_brightness" msgid="6613930842805942519">"在快速設定中顯示亮度"</string>
- <string name="qs_paging" msgid="7020133150248666132">"在快速設定中使用分頁功能"</string>
<string name="experimental" msgid="6198182315536726162">"實驗性"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"要開啟藍牙功能嗎?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"如要將鍵盤連線到平板電腦,您必須先開啟藍牙。"</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"開啟"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 2f068af..f77e01a 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -386,15 +386,12 @@
<string name="hidden_notifications_text" msgid="2326409389088668981">"Ibone ngaphambi kokuthi uyivule"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"Cha ngiyabonga"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Lungisa"</string>
- <string name="notification_expand_button_text" msgid="1037425494153780718">"Buka konke"</string>
- <string name="notification_collapse_button_text" msgid="6883253262134328057">"Fihla wonke"</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">"Qeda manje"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Nweba"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Goqa"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Isikrini siphiniwe"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Lokhu kukugcina kubukeka uze ususe ukuphina. Thinta futhi ubambe u-Emuva no-Ukubuka konke ngesikhathi esisodwa ukuze ususe ukuphina."</string>
- <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Lokhu kukugcina kubukeka uze ususe ukuphina. Thinta futhi ubambe u-Ukubuka konke ukuze ususe ukuphina."</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Lokhu kuyigcina ekubukekeni uze ususe ukuphina. Thinta uphinde ubambe ukubuyela emuva ukuze ususe ukuphina."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Ngiyitholile"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Cha ngiyabonga"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Fihla i-<xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -405,6 +402,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Phika"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> yingxoxo yevolumu"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Thinta ukuze ubuyisele kokwangempela."</string>
+ <string name="group_summary_concadenation" msgid="2705151242008937028">","</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Usebenzisa iphrofayela yakho yomsebenzi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Isishuni se-UI yesistimu"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Bonisa amaphesenti ebhethri elinamathiselwe"</string>
@@ -439,6 +437,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Bonisa amasekhondi wewashi kubha yesimo. Ingathinta impilo yebhethri."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Hlela kabusha izilungiselelo ezisheshayo"</string>
<string name="show_brightness" msgid="6613930842805942519">"Bonisa ukukhanya kuzilungiselelo ezisheshayo"</string>
- <string name="qs_paging" msgid="7020133150248666132">"Sebenzisa i-paging kuzilungiselelo ezisheshayo"</string>
<string name="experimental" msgid="6198182315536726162">"Okokulinga"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Vula i-Bluetooth?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Ukuze uxhume ikhibhodi yakho nethebhulethi yakho, kufanele uqale ngokuvula i-Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Vula"</string>
</resources>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index c070a0e..955efb5 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -20,6 +20,7 @@
<attr name="keyCode" format="integer" />
<!-- does this button generate longpress / repeat events? -->
<attr name="keyRepeat" format="boolean" />
+ <attr name="android:contentDescription" />
</declare-styleable>
<declare-styleable name="ToggleSlider">
<attr name="text" format="string" />
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index f40f0d9..26a0577 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -143,4 +143,7 @@
<color name="fab_ripple">#1fffffff</color><!-- 12% white -->
<color name="fab_shape">#ff009688</color><!-- Teal 500 -->
+
+ <color name="docked_divider_background">#ff000000</color>
+ <color name="docked_divider_handle">#ffffff</color>
</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index bae8017..0c638a2 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -136,7 +136,7 @@
<integer name="touch_acceptance_delay">700</integer>
<!-- The duration in seconds to wait before the dismiss buttons are shown. -->
- <integer name="recents_task_bar_dismiss_delay_seconds">1</integer>
+ <integer name="recents_task_bar_dismiss_delay_seconds">1000</integer>
<!-- The min animation duration for animating views that are currently visible. -->
<integer name="recents_filter_animate_current_views_duration">250</integer>
@@ -269,9 +269,6 @@
<!-- Duration of the expansion animation in the volume dialog -->
<item name="volume_expand_animation_duration" type="integer">300</item>
- <!-- Whether to show a "shelf" of apps at the bottom of the screen. -->
- <bool name="config_enableAppShelf">false</bool>
-
<!-- Whether to show the full screen user switcher. -->
<bool name="config_enableFullscreenUserSwitcher">false</bool>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 5661657..73f63a9 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -36,6 +36,18 @@
<!-- The font size for the clock -->
<dimen name="status_bar_clock_size">14sp</dimen>
+ <!-- The margin on the start of the content view -->
+ <dimen name="notification_content_margin_start">16dp</dimen>
+
+ <!-- The maximum size of the title when in single line mode -->
+ <dimen name="notification_maximum_title_length">150sp</dimen>
+
+ <!-- The margin on the end of the content view -->
+ <dimen name="notification_content_margin_end">8dp</dimen>
+
+ <!-- Height of a single line notification in the status bar -->
+ <dimen name="notification_single_line_height">32sp</dimen>
+
<!-- Height of a small notification in the status bar -->
<dimen name="notification_min_height">64dp</dimen>
@@ -125,8 +137,11 @@
<dimen name="pull_span_min">25dp</dimen>
<dimen name="qs_tile_height">88dp</dimen>
+ <dimen name="qs_new_tile_height">100dp</dimen>
<dimen name="qs_quick_actions_height">88dp</dimen>
<dimen name="qs_quick_actions_padding">25dp</dimen>
+ <dimen name="qs_quick_tile_size">48dp</dimen>
+ <dimen name="qs_quick_tile_padding">12dp</dimen>
<dimen name="qs_page_indicator_size">12dp</dimen>
<dimen name="qs_tile_icon_size">24dp</dimen>
<dimen name="qs_tile_text_size">12sp</dimen>
@@ -229,6 +244,9 @@
<!-- The size of the lock-to-app button icon. -->
<dimen name="recents_lock_to_app_icon_size">28dp</dimen>
+ <!-- The amount to allow the stack to overscroll. -->
+ <dimen name="recents_stack_overscroll">24dp</dimen>
+
<!-- Space reserved for the cards behind the top card in the top stack -->
<dimen name="top_stack_peek_amount">12dp</dimen>
@@ -327,8 +345,8 @@
<!-- radius of the corners of the material rounded rect background but negative-->
<dimen name="notification_material_rounded_rect_radius_negative">-2dp</dimen>
- <!-- height of the bottom decor below the notification if present (eg. an expand action) -->
- <dimen name="notification_bottom_decor_height">48dp</dimen>
+ <!-- height of notification header view if present -->
+ <dimen name="notification_header_height">32dp</dimen>
<!-- The padding between notification children -->
<dimen name="notification_children_padding">2dp</dimen>
@@ -559,4 +577,11 @@
<!-- TODO: Remove this -->
<dimen name="qs_header_neg_padding">-8dp</dimen>
+
+ <!-- How high we lift the divider when touching -->
+ <dimen name="docked_stack_divider_lift_elevation">6dp</dimen>
+
+ <dimen name="docked_divider_handle_width">24dp</dimen>
+ <dimen name="docked_divider_handle_height">2dp</dimen>
+
</resources>
diff --git a/packages/SystemUI/res/values/donottranslate.xml b/packages/SystemUI/res/values/donottranslate.xml
index 30ff704..351a1fd 100644
--- a/packages/SystemUI/res/values/donottranslate.xml
+++ b/packages/SystemUI/res/values/donottranslate.xml
@@ -20,10 +20,4 @@
<!-- Date format for display: should match the lockscreen in /policy. -->
<string name="system_ui_date_pattern">@*android:string/system_ui_date_pattern</string>
- <!-- DO NOT TRANSLATE - temporary hack to show the speed-less label if no translation is available -->
- <item type="string" name="keyguard_indication_charging_time_fast_if_translated">@string/keyguard_indication_charging_time</item>
-
- <!-- DO NOT TRANSLATE - temporary hack to show the speed-less label if no translation is available -->
- <item type="string" name="keyguard_indication_charging_time_slowly_if_translated">@string/keyguard_indication_charging_time</item>
-
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index a525fbb..d5f9557e 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -751,6 +751,9 @@
<!-- Text for overflow card on Keyguard when there is not enough space for all notifications on Keyguard. [CHAR LIMIT=1] -->
<string name="keyguard_more_overflow_text">+<xliff:g id="number_of_notifications" example="5">%d</xliff:g></string>
+ <!-- The divider symbol between different parts of the notification header. not translatable [CHAR LIMIT=1] -->
+ <string name="notification_header_divider_symbol" translatable="false">•</string>
+
<!-- An explanation for the visual speed bump in the notifications, which will appear when you click on it. [CHAR LIMIT=50] -->
<string name="speed_bump_explanation">Less urgent notifications below</string>
@@ -978,12 +981,6 @@
<!-- continue action for notification educating the user about enabling notifications on the lockscreen. [CHAR LIMIT=20] -->
<string name="hidden_notifications_setup">Set up</string>
- <!-- Text for the button to expand the notifications to show notification children [CHAR LIMIT=20] -->
- <string name="notification_expand_button_text">See all</string>
-
- <!-- Text for the button to expand the notifications to hide notification children [CHAR LIMIT=20] -->
- <string name="notification_collapse_button_text">Hide all</string>
-
<!-- Accessibility string for current zen mode and selected exit condition. A template that simply concatenates existing mode string and the current condition description. [CHAR LIMIT=20] -->
<string name="zen_mode_and_condition"><xliff:g id="zen_mode" example="Priority interruptions only">%1$s</xliff:g>. <xliff:g id="exit_condition" example="For one hour">%2$s</xliff:g></string>
@@ -999,9 +996,7 @@
<!-- Screen pinning dialog title. -->
<string name="screen_pinning_title">Screen is pinned</string>
<!-- Screen pinning dialog description. -->
- <string name="screen_pinning_description">This keeps it in view until you unpin. Touch and hold Back and Overview at the same time to unpin.</string>
- <!-- Screen pinning dialog description when in accessibility mode. -->
- <string name="screen_pinning_description_accessible">This keeps it in view until you unpin. Touch and hold Overview to unpin.</string>
+ <string name="screen_pinning_description">This keeps it in view until you unpin. Touch and hold Back to unpin.</string>
<!-- Screen pinning positive response. -->
<string name="screen_pinning_positive">Got it</string>
<!-- Screen pinning negative response. -->
@@ -1031,6 +1026,9 @@
<!-- VolumeUI restoration notification: text -->
<string name="volumeui_notification_text">Touch to restore the original.</string>
+ <!-- Describes the way 2 names are concatenated. An example would be ", " to produce "Peter Muller, Paul Curry". Please also include a space here if it's appropriate in the language and if it's a RTL language include it on the left. [CHAR LIMIT=3] -->
+ <string name="group_summary_concadenation">, </string>
+
<!-- Toast shown when user unlocks screen and managed profile activity is in the foreground -->
<string name="managed_profile_foreground_toast">You\'re using your work profile</string>
@@ -1166,5 +1164,16 @@
<string name="qs_customize" translatable="false">Allow long-press customize in Quick Settings</string>
<string name="qs_customize_info" translatable="false">Info</string>
<string name="qs_customize_remove" translatable="false">Remove</string>
+ <string name="no_tiles_add" translatable="false">No tiles to add</string>
+
+ <!-- Dialog title asking if Bluetooth should be enabled [CHAR LIMIT=NONE] -->
+ <string name="enable_bluetooth_title">Turn on Bluetooth?</string>
+
+ <!-- Dialog message explaining why Bluetooth should be enabled when a packaged keyboard is
+ conncted to the device [CHAR LIMIT=NONE] -->
+ <string name="enable_bluetooth_message">To connect your keyboard with your tablet, you first have to turn on Bluetooth.</string>
+
+ <!-- Bluetooth enablement ok text [CHAR LIMIT=40] -->
+ <string name="enable_bluetooth_confirmation_ok">Turn on</string>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 8241ddf..4462a03 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -244,6 +244,10 @@
<item name="android:textColor">#60000000</item>
</style>
+ <style name="TextAppearance.Material.Notification.HeaderTitle"
+ parent="@*android:style/TextAppearance.Material.Notification.Info">
+ </style>
+
<style name="SearchPanelCircle">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">match_parent</item>
@@ -300,4 +304,14 @@
<item name="android:background">@drawable/btn_borderless_rect</item>
</style>
+ <style name="DockedDividerBackground">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">12dp</item>
+ <item name="android:layout_gravity">center_vertical</item>
+ </style>
+
+ <style name="DockedDividerHandle">
+ <item name="android:layout_gravity">center_horizontal</item>
+ </style>
+
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index 1dca149..d9f7a46 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -21,8 +21,6 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.Context;
-import android.media.AudioAttributes;
-import android.os.Vibrator;
import android.util.Log;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
@@ -138,14 +136,14 @@
}
public void setHeight(float h) {
if (DEBUG_SCALE) Log.v(TAG, "SetHeight: setting to " + h);
- mView.setContentHeight((int) h);
+ mView.setActualHeight((int) h);
mCurrentHeight = h;
}
public float getHeight() {
- return mView.getContentHeight();
+ return mView.getActualHeight();
}
- public int getNaturalHeight(int maximum) {
- return Math.min(maximum, mView.getMaxContentHeight());
+ public int getNaturalHeight() {
+ return mView.getMaxContentHeight();
}
}
@@ -194,7 +192,7 @@
private float clamp(float target) {
float out = target;
- out = out < mSmallSize ? mSmallSize : (out > mLargeSize ? mLargeSize : out);
+ out = out < mSmallSize ? mSmallSize : out;
out = out > mNaturalHeight ? mNaturalHeight : out;
return out;
}
@@ -381,8 +379,8 @@
}
private boolean isFullyExpanded(ExpandableView underFocus) {
- return underFocus.areChildrenExpanded() || underFocus.getIntrinsicHeight()
- - underFocus.getBottomDecorHeight() == underFocus.getMaxContentHeight();
+ return underFocus.getIntrinsicHeight() == underFocus.getMaxContentHeight()
+ && (!underFocus.isSummaryWithChildren() || underFocus.areChildrenExpanded());
}
@Override
@@ -452,9 +450,7 @@
mScaler.setHeight(newHeight);
mLastMotionY = ev.getRawY();
if (isFinished) {
- mCallback.setUserExpandedChild(mResizedView, expanded);
mCallback.expansionStateChanged(false);
- return false;
} else {
mCallback.expansionStateChanged(true);
}
@@ -509,9 +505,11 @@
mScaler.setView(v);
mOldHeight = mScaler.getHeight();
mCurrentHeight = mOldHeight;
- if (mCallback.canChildBeExpanded(v)) {
+ boolean canBeExpanded = mCallback.canChildBeExpanded(v);
+ if (canBeExpanded) {
if (DEBUG) Log.d(TAG, "working on an expandable child");
- mNaturalHeight = mScaler.getNaturalHeight(mLargeSize);
+ mNaturalHeight = mScaler.getNaturalHeight();
+ mSmallSize = v.getMinHeight();
} else {
if (DEBUG) Log.d(TAG, "working on a non-expandable child");
mNaturalHeight = mOldHeight;
@@ -527,19 +525,22 @@
if (DEBUG) Log.d(TAG, "scale in finishing on view: " + mResizedView);
float currentHeight = mScaler.getHeight();
- float targetHeight = mSmallSize;
float h = mScaler.getHeight();
final boolean wasClosed = (mOldHeight == mSmallSize);
+ boolean nowExpanded;
+ int naturalHeight = mScaler.getNaturalHeight();
if (wasClosed) {
- targetHeight = (force || currentHeight > mSmallSize) ? mNaturalHeight : mSmallSize;
+ nowExpanded = (force || currentHeight > mOldHeight);
} else {
- targetHeight = (force || currentHeight < mNaturalHeight) ? mSmallSize : mNaturalHeight;
+ nowExpanded = !force && currentHeight >= mOldHeight;
}
+ nowExpanded |= mNaturalHeight == mSmallSize;
if (mScaleAnimation.isRunning()) {
mScaleAnimation.cancel();
}
- mCallback.setUserExpandedChild(mResizedView, targetHeight == mNaturalHeight);
+ mCallback.setUserExpandedChild(mResizedView, nowExpanded);
mCallback.expansionStateChanged(false);
+ float targetHeight = nowExpanded ? naturalHeight : mSmallSize;
if (targetHeight != currentHeight) {
mScaleAnimation.setFloatValues(targetHeight);
mScaleAnimation.setupStartValues();
diff --git a/packages/SystemUI/src/com/android/systemui/QSQuickTileView.java b/packages/SystemUI/src/com/android/systemui/QSQuickTileView.java
new file mode 100644
index 0000000..3362650
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/QSQuickTileView.java
@@ -0,0 +1,78 @@
+/*
+ * 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.systemui;
+
+import android.content.Context;
+import android.view.View;
+import android.widget.ImageView;
+import com.android.systemui.qs.QSTile;
+import com.android.systemui.qs.QSTileBaseView;
+
+public class QSQuickTileView extends QSTileBaseView {
+
+ private final int mPadding;
+ private final ImageView mIcon;
+
+ public QSQuickTileView(Context context) {
+ super(context);
+ mPadding = context.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_padding);
+ mIcon = createIcon();
+ addView(mIcon);
+ }
+
+ protected ImageView createIcon() {
+ final ImageView icon = new ImageView(mContext);
+ icon.setId(android.R.id.icon);
+ icon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
+ return icon;
+ }
+
+ @Override
+ public void init(OnClickListener click, OnClickListener clickSecondary,
+ OnLongClickListener longClick) {
+ setClickable(true);
+ setOnClickListener(click);
+ }
+
+ @Override
+ protected void handleStateChanged(QSTile.State state) {
+ mIcon.setImageDrawable(state.icon.getDrawable(getContext()));
+ setContentDescription(state.contentDescription);
+ }
+
+ @Override
+ public boolean setType(int type) {
+ return false;
+ }
+
+ @Override
+ public View updateAccessibilityOrder(View previousView) {
+ return this;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ mIcon.measure(exactly(getMeasuredWidth() - 2 * mPadding),
+ exactly(getMeasuredHeight() - 2 * mPadding));
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ layout(mIcon, mPadding, mPadding);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
index 1d0bfe7..9a4cd93 100644
--- a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
@@ -27,4 +27,9 @@
void cancelPreloadingRecents();
void showNextAffiliatedTask();
void showPrevAffiliatedTask();
+
+ /**
+ * Docks the top-most task and opens recents.
+ */
+ void dockTopTask();
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 5bf251b..949efc5 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -27,6 +27,8 @@
import android.os.UserHandle;
import android.util.Log;
+import com.android.systemui.stackdivider.Divider;
+
import java.util.HashMap;
import java.util.Map;
@@ -50,6 +52,8 @@
com.android.systemui.usb.StorageNotification.class,
com.android.systemui.power.PowerUI.class,
com.android.systemui.media.RingtonePlayer.class,
+ com.android.systemui.keyboard.KeyboardUI.class,
+ Divider.class
};
/**
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/AnglesPercentageEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/AnglesPercentageEvaluator.java
index a0ceb29..e6c42da 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/AnglesPercentageEvaluator.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/AnglesPercentageEvaluator.java
@@ -20,8 +20,8 @@
public static float evaluate(float value) {
float evaluation = 0.0f;
if (value < 1.00) evaluation++;
- if (value < 0.95) evaluation++;
if (value < 0.90) evaluation++;
+ if (value < 0.70) evaluation++;
return evaluation;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioClassifier.java
index c125e00..652d969 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioClassifier.java
@@ -27,10 +27,12 @@
@Override
public float getFalseTouchEvaluation(int type, Stroke stroke) {
+ float ratio;
if (stroke.getTotalLength() == 0.0f) {
- return 1.0f;
+ ratio = 1.0f;
+ } else {
+ ratio = stroke.getEndPointLength() / stroke.getTotalLength();
}
- return EndPointRatioEvaluator.evaluate(
- stroke.getEndPointLength() / stroke.getTotalLength());
+ return EndPointRatioEvaluator.evaluate(ratio);
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java
index a7a5694..27d4c0e 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java
@@ -48,7 +48,7 @@
private final float mDpi;
private HistoryEvaluator mHistoryEvaluator;
- private boolean mEnableClassifier = true;
+ private boolean mEnableClassifier = false;
private int mCurrentType = Classifier.GENERIC;
protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesPercentageEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesPercentageEvaluator.java
index 2a45fa3..d50d406 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesPercentageEvaluator.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesPercentageEvaluator.java
@@ -20,8 +20,8 @@
public static float evaluate(float value) {
float evaluation = 0.0f;
if (value < 1.00) evaluation++;
- if (value < 0.95) evaluation++;
if (value < 0.90) evaluation++;
+ if (value < 0.70) evaluation++;
return evaluation;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedEvaluator.java
index c0e4a2d..afd8d01 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/SpeedEvaluator.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/SpeedEvaluator.java
@@ -19,9 +19,10 @@
public class SpeedEvaluator {
public static float evaluate(float value) {
float evaluation = 0.0f;
- if (value < 4.0 || value > 35.0) evaluation += 1.0;
- if (value < 2.2) evaluation += 1.0;
- if (value > 50.0) evaluation += 1.0;
+ if (value < 4.0) evaluation++;
+ if (value < 2.2) evaluation++;
+ if (value > 35.0) evaluation++;
+ if (value > 50.0) evaluation++;
return evaluation;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedRatioEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedRatioEvaluator.java
index 349aa9e..4c6cea0 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/SpeedRatioEvaluator.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/SpeedRatioEvaluator.java
@@ -19,8 +19,10 @@
public class SpeedRatioEvaluator {
public static float evaluate(float value) {
float evaluation = 0.0f;
- if (value > 9.0) ++evaluation;
- if (value > 18.0) ++evaluation;
+ if (value <= 1.0) evaluation++;
+ if (value <= 0.5) evaluation++;
+ if (value > 9.0) evaluation++;
+ if (value > 18.0) evaluation++;
return evaluation;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedVarianceEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedVarianceEvaluator.java
index 8f9a7e1..48b1b6e 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/SpeedVarianceEvaluator.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/SpeedVarianceEvaluator.java
@@ -19,10 +19,10 @@
public class SpeedVarianceEvaluator {
public static float evaluate(float value) {
float evaluation = 0.0f;
- if (value > 0.06) evaluation += 1.0;
- if (value > 0.15) evaluation += 1.0;
- if (value > 0.3) evaluation += 1.0;
- if (value > 0.6) evaluation += 1.0;
+ if (value > 0.06) evaluation++;
+ if (value > 0.15) evaluation++;
+ if (value > 0.3) evaluation++;
+ if (value > 0.6) evaluation++;
return evaluation;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/BluetoothDialog.java b/packages/SystemUI/src/com/android/systemui/keyboard/BluetoothDialog.java
new file mode 100644
index 0000000..64f3e13f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/BluetoothDialog.java
@@ -0,0 +1,34 @@
+/*
+ * 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.systemui.keyboard;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.view.WindowManager;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
+
+public class BluetoothDialog extends SystemUIDialog {
+
+ public BluetoothDialog(Context context) {
+ super(context);
+
+ getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
+ setShowForAllUsers(true);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
new file mode 100644
index 0000000..96ee397
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
@@ -0,0 +1,573 @@
+/*
+ * 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.systemui.keyboard;
+
+import android.app.AlertDialog;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.le.BluetoothLeScanner;
+import android.bluetooth.le.ScanCallback;
+import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanRecord;
+import android.bluetooth.le.ScanResult;
+import android.bluetooth.le.ScanSettings;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.hardware.input.InputManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Settings.Secure;
+import android.text.TextUtils;
+import android.util.Slog;
+import android.view.WindowManager;
+
+import com.android.settingslib.bluetooth.BluetoothCallback;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
+import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.systemui.R;
+import com.android.systemui.SystemUI;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class KeyboardUI extends SystemUI implements InputManager.OnTabletModeChangedListener {
+ private static final String TAG = "KeyboardUI";
+ private static final boolean DEBUG = false;
+
+ // Give BT some time to start after SyUI comes up. This avoids flashing a dialog in the user's
+ // face because BT starts a little bit later in the boot process than SysUI and it takes some
+ // time for us to receive the signal that it's starting.
+ private static final long BLUETOOTH_START_DELAY_MILLIS = 10 * 1000;
+
+ private static final int STATE_NOT_ENABLED = -1;
+ private static final int STATE_UNKNOWN = 0;
+ private static final int STATE_WAITING_FOR_BOOT_COMPLETED = 1;
+ private static final int STATE_WAITING_FOR_TABLET_MODE_EXIT = 2;
+ private static final int STATE_WAITING_FOR_DEVICE_DISCOVERY = 3;
+ private static final int STATE_WAITING_FOR_BLUETOOTH = 4;
+ private static final int STATE_WAITING_FOR_STATE_PAIRED = 5;
+ private static final int STATE_PAIRING = 6;
+ private static final int STATE_PAIRED = 7;
+ private static final int STATE_USER_CANCELLED = 8;
+ private static final int STATE_DEVICE_NOT_FOUND = 9;
+
+ private static final int MSG_INIT = 0;
+ private static final int MSG_ON_BOOT_COMPLETED = 1;
+ private static final int MSG_PROCESS_KEYBOARD_STATE = 2;
+ private static final int MSG_ENABLE_BLUETOOTH = 3;
+ private static final int MSG_ON_BLUETOOTH_STATE_CHANGED = 4;
+ private static final int MSG_ON_DEVICE_BOND_STATE_CHANGED = 5;
+ private static final int MSG_ON_BLUETOOTH_DEVICE_ADDED = 6;
+ private static final int MSG_ON_BLE_SCAN_FAILED = 7;
+ private static final int MSG_SHOW_BLUETOOTH_DIALOG = 8;
+ private static final int MSG_DISMISS_BLUETOOTH_DIALOG = 9;
+
+ private volatile KeyboardHandler mHandler;
+ private volatile KeyboardUIHandler mUIHandler;
+
+ protected volatile Context mContext;
+
+ private boolean mEnabled;
+ private String mKeyboardName;
+ private CachedBluetoothDeviceManager mCachedDeviceManager;
+ private LocalBluetoothAdapter mLocalBluetoothAdapter;
+ private LocalBluetoothProfileManager mProfileManager;
+ private boolean mBootCompleted;
+ private long mBootCompletedTime;
+
+ private int mInTabletMode = InputManager.SWITCH_STATE_UNKNOWN;
+ private ScanCallback mScanCallback;
+ private BluetoothDialog mDialog;
+
+ private int mState;
+
+ @Override
+ public void start() {
+ mContext = super.mContext;
+ HandlerThread thread = new HandlerThread("Keyboard", Process.THREAD_PRIORITY_BACKGROUND);
+ thread.start();
+ mHandler = new KeyboardHandler(thread.getLooper());
+ mHandler.sendEmptyMessage(MSG_INIT);
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("KeyboardUI:");
+ pw.println(" mEnabled=" + mEnabled);
+ pw.println(" mBootCompleted=" + mEnabled);
+ pw.println(" mBootCompletedTime=" + mBootCompletedTime);
+ pw.println(" mKeyboardName=" + mKeyboardName);
+ pw.println(" mInTabletMode=" + mInTabletMode);
+ pw.println(" mState=" + stateToString(mState));
+ }
+
+ @Override
+ protected void onBootCompleted() {
+ mHandler.sendEmptyMessage(MSG_ON_BOOT_COMPLETED);
+ }
+
+ @Override
+ public void onTabletModeChanged(long whenNanos, boolean inTabletMode) {
+ if (DEBUG) {
+ Slog.d(TAG, "onTabletModeChanged(" + whenNanos + ", " + inTabletMode + ")");
+ }
+
+ if (inTabletMode && mInTabletMode != InputManager.SWITCH_STATE_ON
+ || !inTabletMode && mInTabletMode != InputManager.SWITCH_STATE_OFF) {
+ mInTabletMode = inTabletMode ?
+ InputManager.SWITCH_STATE_ON : InputManager.SWITCH_STATE_OFF;
+ processKeyboardState();
+ }
+ }
+
+ // Shoud only be called on the handler thread
+ private void init() {
+ Context context = mContext;
+ mKeyboardName =
+ context.getString(com.android.internal.R.string.config_packagedKeyboardName);
+ if (TextUtils.isEmpty(mKeyboardName)) {
+ if (DEBUG) {
+ Slog.d(TAG, "No packaged keyboard name given.");
+ }
+ return;
+ }
+
+ LocalBluetoothManager bluetoothManager = LocalBluetoothManager.getInstance(context, null);
+ if (bluetoothManager == null) {
+ if (DEBUG) {
+ Slog.e(TAG, "Failed to retrieve LocalBluetoothManager instance");
+ }
+ return;
+ }
+ mEnabled = true;
+ mCachedDeviceManager = bluetoothManager.getCachedDeviceManager();
+ mLocalBluetoothAdapter = bluetoothManager.getBluetoothAdapter();
+ mProfileManager = bluetoothManager.getProfileManager();
+ bluetoothManager.getEventManager().registerCallback(new BluetoothCallbackHandler());
+
+ InputManager im = (InputManager) context.getSystemService(Context.INPUT_SERVICE);
+ im.registerOnTabletModeChangedListener(this, mHandler);
+ mInTabletMode = im.isInTabletMode();
+
+ processKeyboardState();
+ mUIHandler = new KeyboardUIHandler();
+ }
+
+ // Should only be called on the handler thread
+ private void processKeyboardState() {
+ mHandler.removeMessages(MSG_PROCESS_KEYBOARD_STATE);
+
+ if (!mEnabled) {
+ mState = STATE_NOT_ENABLED;
+ return;
+ }
+
+ if (!mBootCompleted) {
+ mState = STATE_WAITING_FOR_BOOT_COMPLETED;
+ return;
+ }
+
+ if (mInTabletMode != InputManager.SWITCH_STATE_OFF) {
+ if (mState == STATE_WAITING_FOR_DEVICE_DISCOVERY) {
+ stopScanning();
+ }
+ mState = STATE_WAITING_FOR_TABLET_MODE_EXIT;
+ return;
+ }
+
+ final int btState = mLocalBluetoothAdapter.getState();
+ if (btState == BluetoothAdapter.STATE_TURNING_ON || btState == BluetoothAdapter.STATE_ON
+ && mState == STATE_WAITING_FOR_BLUETOOTH) {
+ // If we're waiting for bluetooth but it has come on in the meantime, or is coming
+ // on, just dismiss the dialog. This frequently happens during device startup.
+ mUIHandler.sendEmptyMessage(MSG_DISMISS_BLUETOOTH_DIALOG);
+ }
+
+ if (btState == BluetoothAdapter.STATE_TURNING_ON) {
+ mState = STATE_WAITING_FOR_BLUETOOTH;
+ // Wait for bluetooth to fully come on.
+ return;
+ }
+
+ if (btState != BluetoothAdapter.STATE_ON) {
+ mState = STATE_WAITING_FOR_BLUETOOTH;
+ showBluetoothDialog();
+ return;
+ }
+
+ CachedBluetoothDevice device = getPairedKeyboard();
+ if (mState == STATE_WAITING_FOR_TABLET_MODE_EXIT || mState == STATE_WAITING_FOR_BLUETOOTH) {
+ if (device != null) {
+ // If we're just coming out of tablet mode or BT just turned on,
+ // then we want to go ahead and automatically connect to the
+ // keyboard. We want to avoid this in other cases because we might
+ // be spuriously called after the user has manually disconnected
+ // the keyboard, meaning we shouldn't try to automtically connect
+ // it again.
+ mState = STATE_PAIRED;
+ device.connect(false);
+ return;
+ }
+ mCachedDeviceManager.clearNonBondedDevices();
+ }
+
+ device = getDiscoveredKeyboard();
+ if (device != null) {
+ mState = STATE_PAIRING;
+ device.startPairing();
+ } else {
+ mState = STATE_WAITING_FOR_DEVICE_DISCOVERY;
+ startScanning();
+ }
+ }
+
+ // Should only be called on the handler thread
+ public void onBootCompletedInternal() {
+ mBootCompleted = true;
+ mBootCompletedTime = SystemClock.uptimeMillis();
+ if (mState == STATE_WAITING_FOR_BOOT_COMPLETED) {
+ processKeyboardState();
+ }
+ }
+
+ // Should only be called on the handler thread
+ private void showBluetoothDialog() {
+ if (isUserSetupComplete()) {
+ long now = SystemClock.uptimeMillis();
+ long earliestDialogTime = mBootCompletedTime + BLUETOOTH_START_DELAY_MILLIS;
+ if (earliestDialogTime < now) {
+ mUIHandler.sendEmptyMessage(MSG_SHOW_BLUETOOTH_DIALOG);
+ } else {
+ mHandler.sendEmptyMessageAtTime(MSG_PROCESS_KEYBOARD_STATE, earliestDialogTime);
+ }
+ } else {
+ // If we're in setup wizard and the keyboard is docked, just automatically enable BT.
+ mLocalBluetoothAdapter.enable();
+ }
+ }
+
+ private boolean isUserSetupComplete() {
+ ContentResolver resolver = mContext.getContentResolver();
+ return Secure.getIntForUser(
+ resolver, Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
+ }
+
+ private CachedBluetoothDevice getPairedKeyboard() {
+ Set<BluetoothDevice> devices = mLocalBluetoothAdapter.getBondedDevices();
+ for (BluetoothDevice d : devices) {
+ if (mKeyboardName.equals(d.getName())) {
+ return getCachedBluetoothDevice(d);
+ }
+ }
+ return null;
+ }
+
+ private CachedBluetoothDevice getDiscoveredKeyboard() {
+ Collection<CachedBluetoothDevice> devices = mCachedDeviceManager.getCachedDevicesCopy();
+ for (CachedBluetoothDevice d : devices) {
+ if (d.getName().equals(mKeyboardName)) {
+ return d;
+ }
+ }
+ return null;
+ }
+
+
+ private CachedBluetoothDevice getCachedBluetoothDevice(BluetoothDevice d) {
+ CachedBluetoothDevice cachedDevice = mCachedDeviceManager.findDevice(d);
+ if (cachedDevice == null) {
+ cachedDevice = mCachedDeviceManager.addDevice(
+ mLocalBluetoothAdapter, mProfileManager, d);
+ }
+ return cachedDevice;
+ }
+
+ private void startScanning() {
+ BluetoothLeScanner scanner = mLocalBluetoothAdapter.getBluetoothLeScanner();
+ ScanFilter filter = (new ScanFilter.Builder()).setDeviceName(mKeyboardName).build();
+ ScanSettings settings = (new ScanSettings.Builder())
+ .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
+ .setNumOfMatches(ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT)
+ .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
+ .setReportDelay(0)
+ .build();
+ mScanCallback = new KeyboardScanCallback();
+ scanner.startScan(Arrays.asList(filter), settings, mScanCallback);
+ }
+
+ private void stopScanning() {
+ if (mScanCallback != null) {
+ mLocalBluetoothAdapter.getBluetoothLeScanner().stopScan(mScanCallback);
+ mScanCallback = null;
+ }
+ }
+
+ // Should only be called on the handler thread
+ private void onDeviceAddedInternal(CachedBluetoothDevice d) {
+ if (mState == STATE_WAITING_FOR_DEVICE_DISCOVERY && d.getName().equals(mKeyboardName)) {
+ stopScanning();
+ d.startPairing();
+ mState = STATE_PAIRING;
+ }
+ }
+
+ // Should only be called on the handler thread
+ private void onBluetoothStateChangedInternal(int bluetoothState) {
+ if (bluetoothState == BluetoothAdapter.STATE_ON && mState == STATE_WAITING_FOR_BLUETOOTH) {
+ processKeyboardState();
+ }
+ }
+
+ // Should only be called on the handler thread
+ private void onDeviceBondStateChangedInternal(CachedBluetoothDevice d, int bondState) {
+ if (d.getName().equals(mKeyboardName) && bondState == BluetoothDevice.BOND_BONDED) {
+ // We don't need to manually connect to the device here because it will automatically
+ // try to connect after it has been paired.
+ mState = STATE_PAIRED;
+ }
+ }
+
+ // Should only be called on the handler thread
+ private void onBleScanFailedInternal() {
+ mScanCallback = null;
+ if (mState == STATE_WAITING_FOR_DEVICE_DISCOVERY) {
+ mState = STATE_DEVICE_NOT_FOUND;
+ }
+ }
+
+ private final class KeyboardUIHandler extends Handler {
+ public KeyboardUIHandler() {
+ super(Looper.getMainLooper(), null, true /*async*/);
+ }
+ @Override
+ public void handleMessage(Message msg) {
+ switch(msg.what) {
+ case MSG_SHOW_BLUETOOTH_DIALOG: {
+ DialogInterface.OnClickListener listener = new BluetoothDialogClickListener();
+ mDialog = new BluetoothDialog(mContext);
+ mDialog.setTitle(R.string.enable_bluetooth_title);
+ mDialog.setMessage(R.string.enable_bluetooth_message);
+ mDialog.setPositiveButton(R.string.enable_bluetooth_confirmation_ok, listener);
+ mDialog.setNegativeButton(android.R.string.cancel, listener);
+ mDialog.show();
+ break;
+ }
+ case MSG_DISMISS_BLUETOOTH_DIALOG: {
+ if (mDialog != null) {
+ mDialog.dismiss();
+ mDialog = null;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ private final class KeyboardHandler extends Handler {
+ public KeyboardHandler(Looper looper) {
+ super(looper, null, true /*async*/);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch(msg.what) {
+ case MSG_INIT: {
+ init();
+ break;
+ }
+ case MSG_ON_BOOT_COMPLETED: {
+ onBootCompletedInternal();
+ break;
+ }
+ case MSG_PROCESS_KEYBOARD_STATE: {
+ processKeyboardState();
+ break;
+ }
+ case MSG_ENABLE_BLUETOOTH: {
+ boolean enable = msg.arg1 == 1;
+ if (enable) {
+ mLocalBluetoothAdapter.enable();
+ } else {
+ mState = STATE_USER_CANCELLED;
+ }
+ }
+ case MSG_ON_BLUETOOTH_STATE_CHANGED: {
+ int bluetoothState = msg.arg1;
+ onBluetoothStateChangedInternal(bluetoothState);
+ break;
+ }
+ case MSG_ON_DEVICE_BOND_STATE_CHANGED: {
+ CachedBluetoothDevice d = (CachedBluetoothDevice)msg.obj;
+ int bondState = msg.arg1;
+ onDeviceBondStateChangedInternal(d, bondState);
+ break;
+ }
+ case MSG_ON_BLUETOOTH_DEVICE_ADDED: {
+ BluetoothDevice d = (BluetoothDevice)msg.obj;
+ CachedBluetoothDevice cachedDevice = getCachedBluetoothDevice(d);
+ onDeviceAddedInternal(cachedDevice);
+ break;
+
+ }
+ case MSG_ON_BLE_SCAN_FAILED: {
+ onBleScanFailedInternal();
+ break;
+ }
+ }
+ }
+ }
+
+ private final class BluetoothDialogClickListener implements DialogInterface.OnClickListener {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ int enable = DialogInterface.BUTTON_POSITIVE == which ? 1 : 0;
+ mHandler.obtainMessage(MSG_ENABLE_BLUETOOTH, enable, 0).sendToTarget();
+ mDialog = null;
+ }
+ }
+
+ private final class KeyboardScanCallback extends ScanCallback {
+
+ private boolean isDeviceDiscoverable(ScanResult result) {
+ final ScanRecord scanRecord = result.getScanRecord();
+ final int flags = scanRecord.getAdvertiseFlags();
+ final int BT_DISCOVERABLE_MASK = 0x03;
+
+ return (flags & BT_DISCOVERABLE_MASK) != 0;
+ }
+
+ @Override
+ public void onBatchScanResults(List<ScanResult> results) {
+ if (DEBUG) {
+ Slog.d(TAG, "onBatchScanResults(" + results.size() + ")");
+ }
+
+ BluetoothDevice bestDevice = null;
+ int bestRssi = Integer.MIN_VALUE;
+
+ for (ScanResult result : results) {
+ if (DEBUG) {
+ Slog.d(TAG, "onBatchScanResults: considering " + result);
+ }
+
+ if (isDeviceDiscoverable(result) && result.getRssi() > bestRssi) {
+ bestDevice = result.getDevice();
+ bestRssi = result.getRssi();
+ }
+ }
+
+ if (bestDevice != null) {
+ mHandler.obtainMessage(MSG_ON_BLUETOOTH_DEVICE_ADDED, bestDevice).sendToTarget();
+ }
+ }
+
+ @Override
+ public void onScanFailed(int errorCode) {
+ if (DEBUG) {
+ Slog.d(TAG, "onScanFailed(" + errorCode + ")");
+ }
+ mHandler.obtainMessage(MSG_ON_BLE_SCAN_FAILED).sendToTarget();
+ }
+
+ @Override
+ public void onScanResult(int callbackType, ScanResult result) {
+ if (DEBUG) {
+ Slog.d(TAG, "onScanResult(" + callbackType + ", " + result + ")");
+ }
+
+ if (isDeviceDiscoverable(result)) {
+ mHandler.obtainMessage(MSG_ON_BLUETOOTH_DEVICE_ADDED,
+ result.getDevice()).sendToTarget();
+ } else if (DEBUG) {
+ Slog.d(TAG, "onScanResult: device " + result.getDevice() +
+ " is not discoverable, ignoring");
+ }
+ }
+ }
+
+ private final class BluetoothCallbackHandler implements BluetoothCallback {
+ @Override
+ public void onBluetoothStateChanged(int bluetoothState) {
+ mHandler.obtainMessage(MSG_ON_BLUETOOTH_STATE_CHANGED,
+ bluetoothState, 0).sendToTarget();
+ }
+
+ @Override
+ public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
+ mHandler.obtainMessage(MSG_ON_DEVICE_BOND_STATE_CHANGED,
+ bondState, 0, cachedDevice).sendToTarget();
+ }
+
+ @Override
+ public void onDeviceAdded(CachedBluetoothDevice cachedDevice) { }
+ @Override
+ public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) { }
+ @Override
+ public void onScanningStateChanged(boolean started) { }
+ @Override
+ public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) { }
+ }
+
+ private static String stateToString(int state) {
+ switch (state) {
+ case STATE_NOT_ENABLED:
+ return "STATE_NOT_ENABLED";
+ case STATE_WAITING_FOR_BOOT_COMPLETED:
+ return "STATE_WAITING_FOR_BOOT_COMPLETED";
+ case STATE_WAITING_FOR_TABLET_MODE_EXIT:
+ return "STATE_WAITING_FOR_TABLET_MODE_EXIT";
+ case STATE_WAITING_FOR_DEVICE_DISCOVERY:
+ return "STATE_WAITING_FOR_DEVICE_DISCOVERY";
+ case STATE_WAITING_FOR_BLUETOOTH:
+ return "STATE_WAITING_FOR_BLUETOOTH";
+ case STATE_WAITING_FOR_STATE_PAIRED:
+ return "STATE_WAITING_FOR_STATE_PAIRED";
+ case STATE_PAIRING:
+ return "STATE_PAIRING";
+ case STATE_PAIRED:
+ return "STATE_PAIRED";
+ case STATE_USER_CANCELLED:
+ return "STATE_USER_CANCELLED";
+ case STATE_DEVICE_NOT_FOUND:
+ return "STATE_DEVICE_NOT_FOUND";
+ case STATE_UNKNOWN:
+ default:
+ return "STATE_UNKNOWN";
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 2ea1e43..eee685f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -379,6 +379,13 @@
@Override
public void onDeviceProvisioned() {
sendUserPresentBroadcast();
+ synchronized (KeyguardViewMediator.this) {
+ // If system user is provisioned, we might want to lock now to avoid showing launcher
+ if (UserManager.isSplitSystemUser()
+ && KeyguardUpdateMonitor.getCurrentUser() == UserHandle.USER_SYSTEM) {
+ doKeyguardLocked(null);
+ }
+ }
}
@Override
@@ -1054,7 +1061,8 @@
// In split system user mode, we never unlock system user.
if (!UserManager.isSplitSystemUser()
- || KeyguardUpdateMonitor.getCurrentUser() != UserHandle.USER_SYSTEM) {
+ || KeyguardUpdateMonitor.getCurrentUser() != UserHandle.USER_SYSTEM
+ || !mUpdateMonitor.isDeviceProvisioned()) {
// if the setup wizard hasn't run yet, don't show
final boolean requireSim = !SystemProperties.getBoolean("keyguard.no_require_sim", false);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 32c906e..0e4a4e5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -75,8 +75,6 @@
@Override
public void setTileVisibility(TileRecord tile, int visibility) {
tile.tileView.setVisibility(visibility);
-// // TODO: Do something smarter here.
-// distributeTiles();
}
@Override
@@ -104,8 +102,7 @@
for (int i = 0; i < NT; i++) {
TileRecord tile = mTiles.get(i);
if (tile.tile.getTileType() == QSTileView.QS_TYPE_QUICK) {
- tile.tileView.setType(QSTileView.QS_TYPE_QUICK);
- mFirstPage.mQuickQuickTiles.addView(tile.tileView);
+ // Don't show any quick tiles for now.
continue;
}
if (mPages.get(index).isFull()) {
@@ -161,6 +158,7 @@
protected void onFinishInflate() {
super.onFinishInflate();
mQuickQuickTiles = (LinearLayout) findViewById(R.id.quick_tile_layout);
+ mQuickQuickTiles.setVisibility(View.GONE);
mTilePage = (TilePage) findViewById(R.id.tile_page);
// Less rows on first page, because it needs room for the quick tiles.
mTilePage.mMaxRows = 3;
@@ -176,7 +174,7 @@
}
public static class TilePage extends TileLayout {
- private int mMaxRows = 4;
+ private int mMaxRows = 3;
public TilePage(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -188,6 +186,11 @@
mMaxRows = maxRows;
}
+ @Override
+ protected int getCellHeight() {
+ return mContext.getResources().getDimensionPixelSize(R.dimen.qs_new_tile_height);
+ }
+
private void clear() {
if (DEBUG) Log.d(TAG, "Clearing page");
removeAllViews();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index c6bcfc4..049754e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -19,12 +19,14 @@
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorListenerAdapter;
+import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Handler;
import android.os.Message;
+import android.provider.Settings;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
@@ -34,6 +36,7 @@
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
+
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
@@ -566,7 +569,7 @@
public static final class TileRecord extends Record {
public QSTile<?> tile;
- public QSTileView tileView;
+ public QSTileBaseView tileView;
public int row;
public int col;
public boolean scanState;
@@ -620,4 +623,9 @@
int getOffsetTop(TileRecord tile);
void updateResources();
}
+
+ public static boolean isTheNewQS(Context context) {
+ return Settings.Secure.getIntForUser(context.getContentResolver(), QS_THE_NEW_QS,
+ ActivityManager.getCurrentUser(), 0) != 0;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
index e562682..5d928d6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
@@ -16,6 +16,7 @@
package com.android.systemui.qs;
+import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Animatable;
@@ -97,7 +98,7 @@
return mHost;
}
- public QSTileView createTileView(Context context) {
+ public QSTileBaseView createTileView(Context context) {
return new QSTileView(context);
}
@@ -136,7 +137,7 @@
mHandler.obtainMessage(H.SHOW_DETAIL, show ? 1 : 0, 0).sendToTarget();
}
- protected final void refreshState() {
+ public final void refreshState() {
refreshState(null);
}
@@ -325,6 +326,7 @@
public interface Host {
void startActivityDismissingKeyguard(Intent intent);
+ void startActivityDismissingKeyguard(PendingIntent intent);
void warn(String message, Throwable t);
void collapsePanels();
Looper getLooper();
@@ -358,6 +360,19 @@
}
}
+ public static class DrawableIcon extends Icon {
+ protected final Drawable mDrawable;
+
+ public DrawableIcon(Drawable drawable) {
+ mDrawable = drawable;
+ }
+
+ @Override
+ public Drawable getDrawable(Context context) {
+ return mDrawable;
+ }
+ }
+
public static class ResourceIcon extends Icon {
private static final SparseArray<Icon> ICONS = new SparseArray<Icon>();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java
new file mode 100644
index 0000000..72fc88d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java
@@ -0,0 +1,70 @@
+/*
+ * 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.systemui.qs;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+public abstract class QSTileBaseView extends ViewGroup {
+
+ public static final int QS_TYPE_NORMAL = 0;
+ public static final int QS_TYPE_DUAL = 1;
+ public static final int QS_TYPE_QUICK = 2;
+
+ private final H mHandler = new H();
+
+ public QSTileBaseView(Context context) {
+ super(context);
+ }
+
+ public QSTileBaseView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+
+ public void onStateChanged(QSTile.State state) {
+ mHandler.obtainMessage(H.STATE_CHANGED, state).sendToTarget();
+ }
+
+ public abstract void init(OnClickListener click, OnClickListener clickSecondary,
+ OnLongClickListener longClick);
+ public abstract View updateAccessibilityOrder(View previousView);
+ public abstract boolean setType(int type);
+
+ protected abstract void handleStateChanged(QSTile.State state);
+
+ protected static int exactly(int size) {
+ return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
+ }
+
+ protected static void layout(View child, int left, int top) {
+ child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight());
+ }
+
+ private class H extends Handler {
+ private static final int STATE_CHANGED = 1;
+ public H() {
+ super(Looper.getMainLooper());
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == STATE_CHANGED) {
+ handleStateChanged((QSTile.State) msg.obj);
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
index d914beb..cc264a0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
@@ -44,18 +44,13 @@
import java.util.Objects;
/** View that represents a standard quick settings tile. **/
-public class QSTileView extends ViewGroup {
+public class QSTileView extends QSTileBaseView {
private static final Typeface CONDENSED = Typeface.create("sans-serif-condensed",
Typeface.NORMAL);
- public static final int QS_TYPE_NORMAL = 0;
- public static final int QS_TYPE_DUAL = 1;
- public static final int QS_TYPE_QUICK = 2;
-
protected final Context mContext;
private final View mIcon;
private final View mDivider;
- private final H mHandler = new H();
private final int mIconSizePx;
private final int mTileSpacingPx;
private int mTilePaddingTopPx;
@@ -291,10 +286,6 @@
return MeasureSpec.EXACTLY;
}
- private static int exactly(int size) {
- return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
- }
-
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int w = getMeasuredWidth();
@@ -334,10 +325,6 @@
mRipple.setHotspotBounds(cx - rad, cy - rad, cx + rad, cy + rad);
}
- private static void layout(View child, int left, int top) {
- child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight());
- }
-
protected void handleStateChanged(QSTile.State state) {
if (mIcon instanceof ImageView) {
setIcon((ImageView) mIcon, state);
@@ -369,10 +356,6 @@
}
}
- public void onStateChanged(QSTile.State state) {
- mHandler.obtainMessage(H.STATE_CHANGED, state).sendToTarget();
- }
-
/**
* Update the accessibility order for this view.
*
@@ -392,17 +375,4 @@
firstView.setAccessibilityTraversalAfter(previousView.getId());
return lastView;
}
-
- private class H extends Handler {
- private static final int STATE_CHANGED = 1;
- public H() {
- super(Looper.getMainLooper());
- }
- @Override
- public void handleMessage(Message msg) {
- if (msg.what == STATE_CHANGED) {
- handleStateChanged((State) msg.obj);
- }
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 53d0402..6a053be 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -19,6 +19,8 @@
import android.content.Context;
import android.content.res.ColorStateList;
import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Gravity;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -58,9 +60,10 @@
ArrayList<QSTile<?>> quickTiles = new ArrayList<>();
for (QSTile<?> tile : tiles) {
if (tile.getTileType() == QSTileView.QS_TYPE_QUICK) {
+ Log.d("QSPanel", "Adding " + tile.getTileSpec());
quickTiles.add(tile);
}
- if (quickTiles.size() == 4) {
+ if (quickTiles.size() == 2) {
break;
}
}
@@ -71,16 +74,17 @@
public HeaderTileLayout(Context context) {
super(context);
+ setGravity(Gravity.CENTER_VERTICAL);
setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
- int qsCompensation = (int)
- context.getResources().getDimension(R.dimen.qs_header_neg_padding);
- setPadding(0, qsCompensation, 0, 0);
+
+ int padding =
+ mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_padding);
ImageView downArrow = new ImageView(context);
downArrow.setImageResource(R.drawable.ic_expand_more);
downArrow.setImageTintList(ColorStateList.valueOf(context.getResources().getColor(
android.R.color.white, null)));
downArrow.setLayoutParams(generateLayoutParams());
- downArrow.setPadding(0, -qsCompensation, 0, 0);
+ downArrow.setPadding(padding, padding, padding, padding);
addView(downArrow);
setOrientation(LinearLayout.HORIZONTAL);
}
@@ -95,7 +99,9 @@
}
private LayoutParams generateLayoutParams() {
- LayoutParams lp = new LayoutParams(0, LayoutParams.MATCH_PARENT);
+ int size =
+ mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size);
+ LayoutParams lp = new LayoutParams(0, size);
lp.weight = 1;
return lp;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index b8342e2..1336eec 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -67,18 +67,23 @@
public void updateResources() {
final Resources res = mContext.getResources();
final int columns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns));
- mCellHeight = res.getDimensionPixelSize(R.dimen.qs_tile_height);
+ mCellHeight = getCellHeight();
mCellWidth = (int) (mCellHeight * TILE_ASPECT);
mLargeCellHeight = mAllowDual ? res.getDimensionPixelSize(R.dimen.qs_dual_tile_height)
: mCellHeight;
mLargeCellWidth = mAllowDual ? (int) (mLargeCellHeight * TILE_ASPECT) : mCellWidth;
- mDualTileUnderlap = res.getDimensionPixelSize(R.dimen.qs_dual_tile_padding_vertical);
+ mDualTileUnderlap = mAllowDual
+ ? res.getDimensionPixelSize(R.dimen.qs_dual_tile_padding_vertical) : 0;
if (mColumns != columns) {
mColumns = columns;
postInvalidate();
}
}
+ protected int getCellHeight() {
+ return mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_height);
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int width = MeasureSpec.getSize(widthMeasureSpec);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java
index 3491cb6..3f85982 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java
@@ -49,7 +49,7 @@
}
@Override
- protected QSTile<?> createTile(String tileSpec) {
+ public QSTile<?> createTile(String tileSpec) {
QSTile<?> tile = super.createTile(tileSpec);
tile.setTileSpec(tileSpec);
return tile;
@@ -113,6 +113,11 @@
return mTiles;
}
+ public void addTile(String spec) {
+ mTiles.add(spec);
+ super.onTuningChanged(TILES_SETTING, null);
+ }
+
public void replace(String oldTile, String newTile) {
if (oldTile.equals(newTile)) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/DropButton.java b/packages/SystemUI/src/com/android/systemui/qs/customize/DropButton.java
index 0e15f2b..3135408 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/DropButton.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/DropButton.java
@@ -43,7 +43,7 @@
}
private void setHovering(boolean hovering) {
- setAlpha(hovering ? .5f : 1);
+ setAlpha(hovering ? .3f : 1);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/NonPagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/customize/NonPagedTileLayout.java
index 012633c..1669278 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/NonPagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/NonPagedTileLayout.java
@@ -61,6 +61,7 @@
protected void onFinishInflate() {
super.onFinishInflate();
mQuickTiles = (QuickTileLayout) findViewById(R.id.quick_tile_layout);
+ mQuickTiles.setVisibility(View.GONE);
TilePage page = (PagedTileLayout.TilePage) findViewById(R.id.tile_page);
page.setMaxRows(3 /* First page only gets 3 */);
mPages.add(page);
@@ -107,12 +108,12 @@
for (int i = 0; i < NT; i++) {
TileRecord tile = mTiles.get(i);
if (tile.tile.getTileType() == QSTileView.QS_TYPE_QUICK) {
- tile.tileView.setType(QSTileView.QS_TYPE_QUICK);
- mQuickTiles.addView(tile.tileView);
+ // Ignore quick tiles for now.
continue;
}
mPages.get(index).addTile(tile);
- if (mPages.get(index).isFull()) {
+ // Keep everything in one layout for now.
+ if (false && mPages.get(index).isFull()) {
if (++index == mPages.size()) {
LayoutInflater inflater = LayoutInflater.from(mContext);
inflater.inflate(R.layout.horizontal_divider, this);
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 601961b..b5a885c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -18,18 +18,31 @@
import android.animation.Animator;
import android.content.ClipData;
import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnCancelListener;
+import android.content.DialogInterface.OnDismissListener;
import android.util.AttributeSet;
import android.util.TypedValue;
-import android.view.*;
+import android.view.ContextThemeWrapper;
+import android.view.DragEvent;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.view.WindowManager;
import android.widget.LinearLayout;
+import android.widget.ListView;
import android.widget.Toolbar;
import android.widget.Toolbar.OnMenuItemClickListener;
+
import com.android.systemui.R;
import com.android.systemui.SystemUIApplication;
import com.android.systemui.qs.QSDetailClipper;
import com.android.systemui.qs.QSTile.Host.Callback;
import com.android.systemui.qs.customize.DropButton.OnDropListener;
+import com.android.systemui.qs.customize.TileAdapter.TileSelectedListener;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
import com.android.systemui.statusbar.phone.QSTileHost;
import com.android.systemui.statusbar.phone.SystemUIDialog;
@@ -44,7 +57,8 @@
* *someday* do fancy animations to get into/out of it.
*/
public class QSCustomizer extends LinearLayout implements OnMenuItemClickListener, Callback,
- OnDropListener, OnClickListener, Animator.AnimatorListener {
+ OnDropListener, OnClickListener, Animator.AnimatorListener, TileSelectedListener,
+ OnCancelListener, OnDismissListener {
private static final int MENU_SAVE = Menu.FIRST;
private static final int MENU_RESET = Menu.FIRST + 1;
@@ -61,6 +75,7 @@
private DropButton mInfoButton;
private DropButton mRemoveButton;
private FloatingActionButton mFab;
+ private SystemUIDialog mDialog;
public QSCustomizer(Context context, AttributeSet attrs) {
super(new ContextThemeWrapper(context, android.R.style.Theme_Material), attrs);
@@ -84,7 +99,7 @@
TypedValue value = new TypedValue();
mContext.getTheme().resolveAttribute(android.R.attr.homeAsUpIndicator, value, true);
mToolbar.setNavigationIcon(
- getResources().getDrawable(value.resourceId, mContext.getTheme()));
+ getResources().getDrawable(R.drawable.ic_close_white, mContext.getTheme()));
mToolbar.setNavigationOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
@@ -162,6 +177,14 @@
}
@Override
+ public void onTileSelected(String spec) {
+ if (mDialog != null) {
+ mHost.addTile(spec);
+ mDialog.dismiss();
+ }
+ }
+
+ @Override
public void onTilesChanged() {
mQsPanel.setTiles(mHost.getTiles());
}
@@ -193,11 +216,41 @@
@Override
public void onClick(View v) {
if (mFab == v) {
- // TODO: Show list of tiles.
+ mDialog = new SystemUIDialog(mContext,
+ android.R.style.Theme_Material_Dialog);
+ View view = LayoutInflater.from(mContext).inflate(R.layout.qs_add_tiles_list, null);
+ ListView listView = (ListView) view.findViewById(android.R.id.list);
+ TileAdapter adapter = new TileAdapter(mContext, mHost.getTiles(), mHost);
+ adapter.setListener(this);
+ listView.setDivider(null);
+ listView.setDividerHeight(0);
+ listView.setAdapter(adapter);
+ listView.setEmptyView(view.findViewById(R.id.empty_text));
+ mDialog.setView(view);
+ mDialog.setOnDismissListener(this);
+ mDialog.setOnCancelListener(this);
+ mDialog.show();
+ // Too lazy to figure out what this will be now, but it should probably be something
+ // besides just a dialog.
+ // For now, just make it big.
+ WindowManager.LayoutParams params = mDialog.getWindow().getAttributes();
+ params.width = WindowManager.LayoutParams.MATCH_PARENT;
+ params.height = WindowManager.LayoutParams.WRAP_CONTENT;
+ mDialog.getWindow().setAttributes(params);
}
}
@Override
+ public void onDismiss(DialogInterface dialog) {
+ mDialog = null;
+ }
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ mDialog = null;
+ }
+
+ @Override
public void onAnimationEnd(Animator animation) {
if (!isShown) {
mPhoneStatusBar.getStatusBarWindow().removeView(this);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
new file mode 100644
index 0000000..579f58d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -0,0 +1,257 @@
+/*
+ * 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.systemui.qs.customize;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.GridLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+import com.android.systemui.qs.QSTile;
+import com.android.systemui.qs.QSTile.Icon;
+import com.android.systemui.qs.tiles.CustomTile;
+import com.android.systemui.statusbar.phone.QSTileHost;
+import com.android.systemui.tuner.QSPagingSwitch;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+
+public class TileAdapter extends BaseAdapter {
+
+ private static final String TAG = "TileAdapter";
+
+ private final ArrayList<TileGroup> mGroups = new ArrayList<>();
+ private final Context mContext;
+
+ private TileSelectedListener mListener;
+ private ArrayList<String> mCurrentTiles;
+
+ public TileAdapter(Context context, Collection<QSTile<?>> currentTiles, QSTileHost host) {
+ mContext = context;
+ addSystemTiles(currentTiles, host);
+ // TODO: Live?
+ }
+
+ private void addSystemTiles(Collection<QSTile<?>> currentTiles, QSTileHost host) {
+ try {
+ ArrayList<String> tileSpecs = new ArrayList<>();
+ for (QSTile<?> tile : currentTiles) {
+ tileSpecs.add(tile.getTileSpec());
+ }
+ mCurrentTiles = tileSpecs;
+ final TileGroup group = new TileGroup("com.android.settings", mContext);
+ // TODO: Pull this list from a more authoritative place.
+ String[] possibleTiles = QSPagingSwitch.QS_PAGE_TILES.split(",");
+ for (int i = 0; i < possibleTiles.length; i++) {
+ final String spec = possibleTiles[i];
+ if (spec.startsWith("q")) {
+ // Quick tiles can't be customized.
+ continue;
+ }
+ if (tileSpecs.contains(spec)) {
+ continue;
+ }
+ final QSTile<?> tile = host.createTile(spec);
+ // Bad, bad, very bad.
+ tile.setListening(true);
+ tile.clearState();
+ tile.refreshState();
+ tile.setListening(false);
+ new Handler(host.getLooper()).post(new Runnable() {
+ @Override
+ public void run() {
+ group.addTile(spec, tile.getState().icon, tile.getState().label, mContext);
+ }
+ });
+ }
+ // Error: Badness (10000).
+ // Serialize this work after the host's looper's queue is empty.
+ new Handler(host.getLooper()).post(new Runnable() {
+ @Override
+ public void run() {
+ new Handler(Looper.getMainLooper()).post(new Runnable() {
+ @Override
+ public void run() {
+ if (group.mTiles.size() > 0) {
+ mGroups.add(group);
+ notifyDataSetChanged();
+ }
+ new QueryTilesTask().execute();
+ }
+ });
+ }
+ });
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Couldn't load system tiles", e);
+ }
+ }
+
+ public void setListener(TileSelectedListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public int getCount() {
+ return mGroups.size();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return mGroups.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ return mGroups.get(position).getView(mContext, convertView, parent, mListener);
+ }
+
+ private static class TileGroup {
+ private final ArrayList<TileInfo> mTiles = new ArrayList<>();
+ private CharSequence mLabel;
+ private Drawable mIcon;
+
+ public TileGroup(String pkg, Context context) throws NameNotFoundException {
+ PackageManager pm = context.getPackageManager();
+ ApplicationInfo info = pm.getApplicationInfo(pkg, 0);
+ mLabel = info.loadLabel(pm);
+ mIcon = info.loadIcon(pm);
+ Log.d(TAG, "Added " + mLabel);
+ }
+
+ private void addTile(String spec, Drawable icon, String label) {
+ TileInfo info = new TileInfo();
+ info.label = label;
+ info.drawable = icon;
+ info.spec = spec;
+ mTiles.add(info);
+ }
+
+ private void addTile(String spec, Icon icon, String label, Context context) {
+ addTile(spec, icon.getDrawable(context), label);
+ }
+
+ private View getView(Context context, View convertView, ViewGroup parent,
+ final TileSelectedListener listener) {
+ if (convertView == null) {
+ convertView = LayoutInflater.from(context).inflate(R.layout.tile_listing, parent,
+ false);
+ }
+ ((TextView) convertView.findViewById(android.R.id.title)).setText(mLabel);
+ ((ImageView) convertView.findViewById(android.R.id.icon)).setImageDrawable(mIcon);
+ GridLayout grid = (GridLayout) convertView.findViewById(R.id.tile_grid);
+ final int N = mTiles.size();
+ if (grid.getChildCount() != N) {
+ grid.removeAllViews();
+ }
+ for (int i = 0; i < N; i++) {
+ if (grid.getChildCount() <= i) {
+ grid.addView(createTile(context));
+ }
+ View view = grid.getChildAt(i);
+ final TileInfo tileInfo = mTiles.get(i);
+ ((ImageView) view.findViewById(R.id.tile_icon)).setImageDrawable(tileInfo.drawable);
+ ((TextView) view.findViewById(R.id.tile_label)).setText(tileInfo.label);
+ view.setClickable(true);
+ view.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ listener.onTileSelected(tileInfo.spec);
+ }
+ });
+ }
+ return convertView;
+ }
+
+ private View createTile(Context context) {
+ return LayoutInflater.from(context).inflate(R.layout.qs_add_tile_layout, null);
+ }
+ }
+
+ private static class TileInfo {
+ private String spec;
+ private Drawable drawable;
+ private String label;
+ }
+
+ private class QueryTilesTask extends AsyncTask<Void, Void, Collection<TileGroup>> {
+ // TODO: Become non-prototype and an API.
+ private static final String TILE_ACTION = "android.intent.action.QS_TILE";
+
+ @Override
+ protected Collection<TileGroup> doInBackground(Void... params) {
+ HashMap<String, TileGroup> pkgMap = new HashMap<>();
+ PackageManager pm = mContext.getPackageManager();
+ // TODO: Handle userness.
+ List<ResolveInfo> services = pm.queryIntentServices(new Intent(TILE_ACTION), 0);
+ for (ResolveInfo info : services) {
+ String packageName = info.serviceInfo.packageName;
+ ComponentName componentName = new ComponentName(packageName, info.serviceInfo.name);
+ String spec = CustomTile.PREFIX + componentName.flattenToShortString() + ")";
+ if (mCurrentTiles.contains(spec)) {
+ continue;
+ }
+ try {
+ TileGroup group = pkgMap.get(packageName);
+ if (group == null) {
+ group = new TileGroup(packageName, mContext);
+ pkgMap.put(packageName, group);
+ }
+ Drawable icon = info.serviceInfo.loadIcon(pm);
+ CharSequence label = info.serviceInfo.loadLabel(pm);
+ group.addTile(spec, icon, label != null ? label.toString() : "null");
+ } catch (NameNotFoundException e) {
+ Log.w(TAG, "Couldn't find resolved package... " + packageName, e);
+ }
+ }
+ return pkgMap.values();
+ }
+
+ @Override
+ protected void onPostExecute(Collection<TileGroup> result) {
+ mGroups.addAll(result);
+ notifyDataSetChanged();
+ }
+ }
+
+ public interface TileSelectedListener {
+ void onTileSelected(String spec);
+ }
+}
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 61695b2..48b74a4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -28,6 +28,7 @@
import com.android.systemui.R;
import com.android.systemui.qs.QSDetailItems;
import com.android.systemui.qs.QSDetailItems.Item;
+import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QSTile;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.CastDevice;
@@ -93,7 +94,7 @@
@Override
protected void handleUpdateState(BooleanState state, Object arg) {
state.visible = !mKeyguard.isSecure() || !mKeyguard.isShowing()
- || mKeyguard.canSkipBouncer();
+ || mKeyguard.canSkipBouncer() || QSPanel.isTheNewQS(mContext);
state.label = mContext.getString(R.string.quick_settings_cast_title);
state.value = false;
state.autoMirrorDrawable = 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 f3ad9d8..e2d2ffb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -27,13 +27,12 @@
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
-import com.android.systemui.qs.QSTileView;
+import com.android.systemui.qs.QSTileBaseView;
import com.android.systemui.qs.SignalTileView;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NetworkController.IconState;
import com.android.systemui.statusbar.policy.NetworkController.MobileDataController;
import com.android.systemui.statusbar.policy.NetworkController.MobileDataController.DataUsageInfo;
-import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
import com.android.systemui.statusbar.policy.SignalCallbackAdapter;
/** Quick settings tile: Cellular **/
@@ -74,7 +73,7 @@
}
@Override
- public QSTileView createTileView(Context context) {
+ public QSTileBaseView createTileView(Context context) {
return new SignalTileView(context);
}
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 c6fc6ff..2f9a496 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -21,6 +21,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.Prefs;
import com.android.systemui.R;
+import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QSTile;
import com.android.systemui.qs.SecureSetting;
import com.android.systemui.qs.UsageTracker;
@@ -110,7 +111,7 @@
protected void handleUpdateState(BooleanState state, Object arg) {
final int value = arg instanceof Integer ? (Integer) arg : mSetting.getValue();
final boolean enabled = value != 0;
- state.visible = enabled || mUsageTracker.isRecentlyUsed();
+ state.visible = enabled || mUsageTracker.isRecentlyUsed() || QSPanel.isTheNewQS(mContext);
state.value = enabled;
state.label = mContext.getString(R.string.quick_settings_inversion_label);
state.icon = enabled ? mEnable : mDisable;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomTile.java
new file mode 100644
index 0000000..cf76ed4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomTile.java
@@ -0,0 +1,89 @@
+/*
+ * 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.systemui.qs.tiles;
+
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.qs.QSTile;
+
+public class CustomTile extends QSTile<QSTile.State> {
+ public static final String PREFIX = "custom(";
+
+ private final ComponentName mComponent;
+
+ private CustomTile(Host host, String action) {
+ super(host);
+ mComponent = ComponentName.unflattenFromString(action);
+ }
+
+ public static QSTile<?> create(Host host, String spec) {
+ if (spec == null || !spec.startsWith(PREFIX) || !spec.endsWith(")")) {
+ throw new IllegalArgumentException("Bad intent tile spec: " + spec);
+ }
+ final String action = spec.substring(PREFIX.length(), spec.length() - 1);
+ if (action.isEmpty()) {
+ throw new IllegalArgumentException("Empty intent tile spec action");
+ }
+ return new CustomTile(host, action);
+ }
+
+ @Override
+ public void setListening(boolean listening) {
+ }
+
+ @Override
+ protected State newTileState() {
+ return new State();
+ }
+
+ @Override
+ protected void handleUserSwitch(int newUserId) {
+ super.handleUserSwitch(newUserId);
+ }
+
+ @Override
+ protected void handleClick() {
+ MetricsLogger.action(mContext, getMetricsCategory(), mComponent.getPackageName());
+ }
+
+ @Override
+ protected void handleLongClick() {
+ }
+
+ @Override
+ protected void handleUpdateState(State state, Object arg) {
+ // TODO: Actual things.
+ try {
+ PackageManager pm = mContext.getPackageManager();
+ ServiceInfo info = pm.getServiceInfo(mComponent, 0);
+ state.visible = true;
+ state.icon = new DrawableIcon(info.loadIcon(pm));
+ state.label = info.loadLabel(pm).toString();
+ state.contentDescription = state.label;
+ } catch (Exception e) {
+ state.visible = false;
+ }
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return MetricsLogger.QS_INTENT;
+ }
+}
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 7b83e6a..79084ae 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -23,6 +23,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.Prefs;
import com.android.systemui.R;
+import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QSTile;
import com.android.systemui.qs.UsageTracker;
import com.android.systemui.statusbar.policy.HotspotController;
@@ -88,7 +89,8 @@
@Override
protected void handleUpdateState(BooleanState state, Object arg) {
- state.visible = mController.isHotspotSupported() && mUsageTracker.isRecentlyUsed();
+ state.visible = (mController.isHotspotSupported() && mUsageTracker.isRecentlyUsed())
+ || QSPanel.isTheNewQS(mContext);
state.label = mContext.getString(R.string.quick_settings_hotspot_label);
if (arg instanceof Boolean) {
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 3d0dc7b..c7f2284 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
@@ -99,7 +99,7 @@
try {
if (pi != null) {
if (pi.isActivity()) {
- getHost().startActivityDismissingKeyguard(pi.getIntent());
+ getHost().startActivityDismissingKeyguard(pi);
} else {
pi.send();
}
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 e6fade4..0e2672c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -18,6 +18,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
+import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QSTile;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.statusbar.policy.LocationController;
@@ -73,7 +74,7 @@
// Work around for bug 15916487: don't show location tile on top of lock screen. After the
// bug is fixed, this should be reverted to only hiding it on secure lock screens:
// state.visible = !(mKeyguard.isSecure() && mKeyguard.isShowing());
- state.visible = !mKeyguard.isShowing();
+ state.visible = !mKeyguard.isShowing() || QSPanel.isTheNewQS(mContext);
state.value = locationEnabled;
if (locationEnabled) {
state.icon = mEnable;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QAirplaneTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QAirplaneTile.java
index 13ccaaa..b77191e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QAirplaneTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QAirplaneTile.java
@@ -16,9 +16,12 @@
package com.android.systemui.qs.tiles;
+import android.content.Context;
+import com.android.systemui.QSQuickTileView;
+import com.android.systemui.qs.QSTileBaseView;
import com.android.systemui.qs.QSTileView;
-/** Quick settings tile: Wifi **/
+/** Quick settings tile: Airplane mode **/
public class QAirplaneTile extends AirplaneModeTile {
public QAirplaneTile(Host host) {
@@ -26,6 +29,11 @@
}
@Override
+ public QSTileBaseView createTileView(Context context) {
+ return new QSQuickTileView(context);
+ }
+
+ @Override
public int getTileType() {
return QSTileView.QS_TYPE_QUICK;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QBluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QBluetoothTile.java
index 02975cb..4fe7e45 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QBluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QBluetoothTile.java
@@ -16,6 +16,9 @@
package com.android.systemui.qs.tiles;
+import android.content.Context;
+import com.android.systemui.QSQuickTileView;
+import com.android.systemui.qs.QSTileBaseView;
import com.android.systemui.qs.QSTileView;
/** Quick settings tile: Bluetooth **/
@@ -26,6 +29,11 @@
}
@Override
+ public QSTileBaseView createTileView(Context context) {
+ return new QSQuickTileView(context);
+ }
+
+ @Override
public int getTileType() {
return QSTileView.QS_TYPE_QUICK;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QFlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QFlashlightTile.java
index 31035cd..e115755e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QFlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QFlashlightTile.java
@@ -16,9 +16,12 @@
package com.android.systemui.qs.tiles;
+import android.content.Context;
+import com.android.systemui.QSQuickTileView;
+import com.android.systemui.qs.QSTileBaseView;
import com.android.systemui.qs.QSTileView;
-/** Quick settings tile: Wifi **/
+/** Quick settings tile: Flashlight **/
public class QFlashlightTile extends FlashlightTile {
public QFlashlightTile(Host host) {
@@ -26,6 +29,11 @@
}
@Override
+ public QSTileBaseView createTileView(Context context) {
+ return new QSQuickTileView(context);
+ }
+
+ @Override
public int getTileType() {
return QSTileView.QS_TYPE_QUICK;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QLockTile.java
index 3675f02..8b3013a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QLockTile.java
@@ -15,11 +15,14 @@
*/
package com.android.systemui.qs.tiles;
+import android.content.Context;
import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.QSQuickTileView;
+import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
+import com.android.systemui.qs.QSTileBaseView;
import com.android.systemui.qs.QSTileView;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
-import com.android.systemui.R;
public class QLockTile extends QSTile<QSTile.State> implements KeyguardMonitor.Callback {
@@ -31,6 +34,11 @@
}
@Override
+ public QSTileBaseView createTileView(Context context) {
+ return new QSQuickTileView(context);
+ }
+
+ @Override
public int getTileType() {
return QSTileView.QS_TYPE_QUICK;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRotationLockTile.java
index e066bab..5f5cab5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRotationLockTile.java
@@ -16,6 +16,9 @@
package com.android.systemui.qs.tiles;
+import android.content.Context;
+import com.android.systemui.QSQuickTileView;
+import com.android.systemui.qs.QSTileBaseView;
import com.android.systemui.qs.QSTileView;
/** Quick settings tile: Rotation **/
@@ -26,6 +29,11 @@
}
@Override
+ public QSTileBaseView createTileView(Context context) {
+ return new QSQuickTileView(context);
+ }
+
+ @Override
public int getTileType() {
return QSTileView.QS_TYPE_QUICK;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QWifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QWifiTile.java
index b157275..f0fe87d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QWifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QWifiTile.java
@@ -16,6 +16,9 @@
package com.android.systemui.qs.tiles;
+import android.content.Context;
+import com.android.systemui.QSQuickTileView;
+import com.android.systemui.qs.QSTileBaseView;
import com.android.systemui.qs.QSTileView;
import com.android.systemui.statusbar.policy.WifiIcons;
@@ -27,6 +30,11 @@
}
@Override
+ public QSTileBaseView createTileView(Context context) {
+ return new QSQuickTileView(context);
+ }
+
+ @Override
public int getTileType() {
return QSTileView.QS_TYPE_QUICK;
}
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 91e0218..3763618 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -30,6 +30,7 @@
import com.android.systemui.qs.QSDetailItems;
import com.android.systemui.qs.QSDetailItems.Item;
import com.android.systemui.qs.QSTile;
+import com.android.systemui.qs.QSTileBaseView;
import com.android.systemui.qs.QSTileView;
import com.android.systemui.qs.SignalTileView;
import com.android.systemui.statusbar.policy.NetworkController;
@@ -94,7 +95,7 @@
}
@Override
- public QSTileView createTileView(Context context) {
+ public QSTileBaseView createTileView(Context context) {
return new SignalTileView(context);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index 9781664..a429447 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -21,6 +21,7 @@
*/
public class Constants {
+ // TODO: Move into RecentsMetrics
public static class Metrics {
// DO NOT MODIFY THE ORDER OF THESE METRICS
public static final int DismissSourceKeyboard = 0;
@@ -28,6 +29,7 @@
public static final int DismissSourceHeaderButton = 2;
}
+ // TODO: Move into RecentsDebugFlags
public static class DebugFlags {
public static class App {
@@ -37,8 +39,12 @@
public static final boolean EnableTaskFiltering = false;
// Enables dismiss-all
public static final boolean EnableDismissAll = false;
+ // Enables fast-toggling by just tapping on the recents button
+ public static final boolean EnableFastToggleRecents = false;
// Enables the thumbnail alpha on the front-most task
public static final boolean EnableThumbnailAlphaOnFrontmost = false;
+ // This disables the search bar integration
+ public static final boolean DisableSearchBar = true;
// This disables the bitmap and icon caches
public static final boolean DisableBackgroundCache = false;
// Enables the simulated task affiliations
@@ -53,16 +59,4 @@
public static final int SystemServicesProxyMockTaskCount = 100;
}
}
-
- public static class Values {
- public static class App {
- public static int AppWidgetHostId = 1024;
- }
-
- public static class TaskStackView {
- public static final int TaskStackMinOverscrollRange = 32;
- public static final int TaskStackMaxOverscrollRange = 128;
- public static final int FilterStartDelay = 25;
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl b/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
new file mode 100644
index 0000000..79eca30d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
@@ -0,0 +1,31 @@
+/*
+ * 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.systemui.recents;
+
+/**
+ * Due to the fact that RecentsActivity is per-user, we need to establish an
+ * interface (this) for the system user to callback to the secondary users in
+ * response to UI events coming in from the system user's SystemUI.
+ */
+oneway interface IRecentsNonSystemUserCallbacks {
+ void preloadRecents();
+ void cancelPreloadingRecents();
+ void showRecents(boolean triggeredFromAltTab);
+ void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
+ void toggleRecents();
+ void onConfigurationChanged();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl b/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
new file mode 100644
index 0000000..6b49195
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
@@ -0,0 +1,29 @@
+/*
+ * 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.systemui.recents;
+
+/**
+ * Due to the fact that RecentsActivity is per-user, we need to establish an
+ * interface (this) for the non-system user to register itself for callbacks and to
+ * callback to the system user to update internal state.
+ */
+oneway interface IRecentsSystemUserCallbacks {
+ void registerNonSystemUserCallbacks(IBinder nonSystemUserCallbacks, int userId);
+
+ void updateRecentsVisibility(boolean visible);
+ void startScreenPinning();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index aee3623..bf5417d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -16,908 +16,457 @@
package com.android.systemui.recents;
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.ActivityOptions;
-import android.app.ITaskStackListener;
-import android.content.ActivityNotFoundException;
-import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
+import android.content.ServiceConnection;
import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.os.AsyncTask;
+import android.os.Build;
import android.os.Handler;
-import android.os.SystemClock;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemProperties;
import android.os.UserHandle;
-import android.util.MutableBoolean;
+import android.util.Log;
import android.view.Display;
-import android.view.LayoutInflater;
import android.view.View;
-import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.Prefs;
-import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
import com.android.systemui.SystemUI;
-import com.android.systemui.SystemUIApplication;
-import com.android.systemui.recents.misc.Console;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
+import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.model.RecentsTaskLoadPlan;
import com.android.systemui.recents.model.RecentsTaskLoader;
-import com.android.systemui.recents.model.Task;
-import com.android.systemui.recents.model.TaskGrouping;
-import com.android.systemui.recents.model.TaskStack;
-import com.android.systemui.recents.views.TaskStackView;
-import com.android.systemui.recents.views.TaskStackViewLayoutAlgorithm;
-import com.android.systemui.recents.views.TaskViewHeader;
-import com.android.systemui.recents.views.TaskViewTransform;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
import java.util.ArrayList;
-/**
- * Annotation for a method that is only called from the system user's SystemUI process and will be
- * proxied to the current user.
- */
-@interface ProxyFromSystemToCurrentUser {}
-/**
- * Annotation for a method that may be called from any user's SystemUI process and will be proxied
- * to the system user.
- */
-@interface ProxyFromAnyToSystemUser {}
-/** A proxy implementation for the recents component */
+/**
+ * An implementation of the SystemUI recents component, which supports both system and secondary
+ * users.
+ */
public class Recents extends SystemUI
- implements ActivityOptions.OnAnimationStartedListener, RecentsComponent {
+ implements RecentsComponent {
+
+ private final static String TAG = "Recents";
+ private final static boolean DEBUG = false;
public final static int EVENT_BUS_PRIORITY = 1;
+ public final static int BIND_TO_SYSTEM_USER_RETRY_DELAY = 5000;
- final public static String EXTRA_TRIGGERED_FROM_ALT_TAB = "triggeredFromAltTab";
- final public static String EXTRA_TRIGGERED_FROM_HOME_KEY = "triggeredFromHomeKey";
- final public static String EXTRA_RECENTS_VISIBILITY = "recentsVisibility";
+ // Purely for experimentation
+ private final static String RECENTS_OVERRIDE_SYSPROP_KEY = "persist.recents_override_pkg";
+ private final static String ACTION_SHOW_RECENTS = "com.android.systemui.recents.ACTION_SHOW";
+ private final static String ACTION_HIDE_RECENTS = "com.android.systemui.recents.ACTION_HIDE";
+ private final static String ACTION_TOGGLE_RECENTS = "com.android.systemui.recents.ACTION_TOGGLE";
- // Owner proxy events
- final public static String ACTION_PROXY_NOTIFY_RECENTS_VISIBLITY_TO_OWNER =
- "action_notify_recents_visibility_change";
- final public static String ACTION_PROXY_SCREEN_PINNING_REQUEST_TO_OWNER =
- "action_screen_pinning_request";
+ private static SystemServicesProxy sSystemServicesProxy;
+ private static RecentsTaskLoader sTaskLoader;
+ private static RecentsConfiguration sConfiguration;
- final public static String ACTION_START_ENTER_ANIMATION = "action_start_enter_animation";
- final public static String ACTION_TOGGLE_RECENTS_ACTIVITY = "action_toggle_recents_activity";
- final public static String ACTION_HIDE_RECENTS_ACTIVITY = "action_hide_recents_activity";
+ // For experiments only, allows another package to handle recents if it is defined in the system
+ // properties. This is limited to show/toggle/hide, and does not tie into the ActivityManager,
+ // and does not reside in the home stack.
+ private String mOverrideRecentsPackageName;
- final static int sMinToggleDelay = 350;
+ private Handler mHandler;
+ private RecentsImpl mImpl;
- public final static String sToggleRecentsAction = "com.android.systemui.recents.SHOW_RECENTS";
- public final static String sRecentsPackage = "com.android.systemui";
- public final static String sRecentsActivity = "com.android.systemui.recents.RecentsActivity";
+ // Only For system user, this is the callbacks instance we return to each secondary user
+ private RecentsSystemUser mSystemUserCallbacks;
- /**
- * An implementation of ITaskStackListener, that allows us to listen for changes to the system
- * task stacks and update recents accordingly.
- */
- class TaskStackListenerImpl extends ITaskStackListener.Stub implements Runnable {
- Handler mHandler;
+ // Only for secondary users, this is the callbacks instance provided by the system user to make
+ // calls back
+ private IRecentsSystemUserCallbacks mCallbacksToSystemUser;
- public TaskStackListenerImpl(Handler handler) {
- mHandler = handler;
- }
+ // The set of runnables to run after binding to the system user's service.
+ private final ArrayList<Runnable> mOnConnectRunnables = new ArrayList<>();
+ // Only for secondary users, this is the death handler for the binder from the system user
+ private final IBinder.DeathRecipient mCallbacksToSystemUserDeathRcpt = new IBinder.DeathRecipient() {
@Override
- public void onTaskStackChanged() {
- // Debounce any task stack changes
- mHandler.removeCallbacks(this);
- mHandler.post(this);
- }
+ public void binderDied() {
+ mCallbacksToSystemUser = null;
- /** Preloads the next task */
- public void run() {
- // TODO: Temporarily skip this if multi stack is enabled
- /*
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- if (config.svelteLevel == RecentsConfiguration.SVELTE_NONE) {
- RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
- SystemServicesProxy ssp = loader.getSystemServicesProxy();
- ActivityManager.RunningTaskInfo runningTaskInfo = ssp.getTopMostTask();
-
- // Load the next task only if we aren't svelte
- RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
- loader.preloadTasks(plan, true);
- RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
- // This callback is made when a new activity is launched and the old one is paused
- // so ignore the current activity and try and preload the thumbnail for the
- // previous one.
- if (runningTaskInfo != null) {
- launchOpts.runningTaskId = runningTaskInfo.id;
+ // Retry after a fixed duration
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ registerWithSystemUser();
}
- launchOpts.numVisibleTasks = 2;
- launchOpts.numVisibleTaskThumbnails = 2;
- launchOpts.onlyLoadForCache = true;
- launchOpts.onlyLoadPausedActivities = true;
- loader.loadTasks(mContext, plan, launchOpts);
- }
- */
+ }, BIND_TO_SYSTEM_USER_RETRY_DELAY);
}
- }
+ };
- /**
- * A proxy for Recents events which happens strictly for the owner.
- */
- class RecentsOwnerEventProxyReceiver extends BroadcastReceiver {
+ // Only for secondary users, this is the service connection we use to connect to the system user
+ private final ServiceConnection mServiceConnectionToSystemUser = new ServiceConnection() {
@Override
- public void onReceive(Context context, Intent intent) {
- switch (intent.getAction()) {
- case ACTION_PROXY_NOTIFY_RECENTS_VISIBLITY_TO_OWNER:
- visibilityChanged(context,
- intent.getBooleanExtra(EXTRA_RECENTS_VISIBILITY, false));
- break;
- case ACTION_PROXY_SCREEN_PINNING_REQUEST_TO_OWNER:
- onStartScreenPinning(context);
- break;
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ if (service != null) {
+ mCallbacksToSystemUser = IRecentsSystemUserCallbacks.Stub.asInterface(
+ service);
+
+ // Listen for system user's death, so that we can reconnect later
+ try {
+ service.linkToDeath(mCallbacksToSystemUserDeathRcpt, 0);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Lost connection to (System) SystemUI", e);
+ }
+
+ // Run each of the queued runnables
+ runAndFlushOnConnectRunnables();
}
+
+ // Unbind ourselves now that we've registered our callbacks. The
+ // binder to the system user are still valid at this point.
+ mContext.unbindService(this);
}
- }
- static RecentsTaskLoadPlan sInstanceLoadPlan;
- static Recents sInstance;
-
- SystemServicesProxy mSystemServicesProxy;
- Handler mHandler;
- TaskStackListenerImpl mTaskStackListener;
- RecentsOwnerEventProxyReceiver mProxyBroadcastReceiver;
- RecentsAppWidgetHost mAppWidgetHost;
- boolean mBootCompleted;
- boolean mStartAnimationTriggered;
- boolean mCanReuseTaskStackViews = true;
-
- // Task launching
- RecentsConfiguration mConfig;
- Rect mSearchBarBounds = new Rect();
- Rect mTaskStackBounds = new Rect();
- Rect mLastTaskViewBounds = new Rect();
- TaskViewTransform mTmpTransform = new TaskViewTransform();
- int mStatusBarHeight;
- int mNavBarHeight;
- int mNavBarWidth;
- int mTaskBarHeight;
-
- // Header (for transition)
- TaskViewHeader mHeaderBar;
- final Object mHeaderBarLock = new Object();
- TaskStackView mDummyStackView;
-
- // Variables to keep track of if we need to start recents after binding
- boolean mTriggeredFromAltTab;
- long mLastToggleTime;
-
- Bitmap mThumbnailTransitionBitmapCache;
- Task mThumbnailTransitionBitmapCacheKey;
-
- public Recents() {
- }
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ // Do nothing
+ }
+ };
/**
- * Gets the singleton instance and starts it if needed. On the primary user on the device, this
- * component gets started as a normal {@link SystemUI} component. On a secondary user, this
- * lifecycle doesn't exist, so we need to start it manually here if needed.
+ * Returns the callbacks interface that non-system users can call.
*/
- public static Recents getInstanceAndStartIfNeeded(Context ctx) {
- if (sInstance == null) {
- sInstance = new Recents();
- sInstance.mContext = ctx;
- sInstance.start();
- sInstance.onBootCompleted();
- }
- return sInstance;
+ public IBinder getSystemUserCallbacks() {
+ return mSystemUserCallbacks;
}
- /** Creates a new broadcast intent */
- static Intent createLocalBroadcastIntent(Context context, String action) {
- Intent intent = new Intent(action);
- intent.setPackage(context.getPackageName());
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT |
- Intent.FLAG_RECEIVER_FOREGROUND);
- return intent;
+ public static RecentsTaskLoader getTaskLoader() {
+ return sTaskLoader;
}
- /** Initializes the Recents. */
- @ProxyFromSystemToCurrentUser
+ public static SystemServicesProxy getSystemServices() {
+ return sSystemServicesProxy;
+ }
+
+ public static RecentsConfiguration getConfiguration() {
+ return sConfiguration;
+ }
+
@Override
public void start() {
- if (sInstance == null) {
- sInstance = this;
- }
- Resources res = mContext.getResources();
- RecentsTaskLoader.initialize(mContext);
- LayoutInflater inflater = LayoutInflater.from(mContext);
- mSystemServicesProxy = new SystemServicesProxy(mContext);
+ sSystemServicesProxy = new SystemServicesProxy(mContext);
+ sTaskLoader = new RecentsTaskLoader(mContext);
+ sConfiguration = new RecentsConfiguration(mContext);
mHandler = new Handler();
- mAppWidgetHost = new RecentsAppWidgetHost(mContext, Constants.Values.App.AppWidgetHostId);
+ mImpl = new RecentsImpl(mContext);
- // Register the task stack listener
- mTaskStackListener = new TaskStackListenerImpl(mHandler);
- mSystemServicesProxy.registerTaskStackListener(mTaskStackListener);
-
- // Only the owner has the callback to update the SysUI visibility flags, so all non-owner
- // instances of RecentsComponent needs to notify the owner when the visibility
- // changes.
- if (mSystemServicesProxy.isForegroundUserSystem()) {
- mProxyBroadcastReceiver = new RecentsOwnerEventProxyReceiver();
- IntentFilter filter = new IntentFilter();
- filter.addAction(Recents.ACTION_PROXY_NOTIFY_RECENTS_VISIBLITY_TO_OWNER);
- filter.addAction(Recents.ACTION_PROXY_SCREEN_PINNING_REQUEST_TO_OWNER);
- mContext.registerReceiverAsUser(mProxyBroadcastReceiver, UserHandle.CURRENT, filter,
- null, mHandler);
+ // Check if there is a recents override package
+ if ("userdebug".equals(Build.TYPE) || "eng".equals(Build.TYPE)) {
+ String cnStr = SystemProperties.get(RECENTS_OVERRIDE_SYSPROP_KEY);
+ if (!cnStr.isEmpty()) {
+ mOverrideRecentsPackageName = cnStr;
+ }
}
- // Initialize the static configuration resources
- mConfig = RecentsConfiguration.initialize(mContext, mSystemServicesProxy);
- mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
- mNavBarHeight = res.getDimensionPixelSize(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_bar_height);
- mDummyStackView = new TaskStackView(mContext, new TaskStack());
- mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header,
- null, false);
- reloadHeaderBarLayout(true /* tryAndBindSearchWidget */);
+ // Register with the event bus
+ EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
+ EventBus.getDefault().register(sTaskLoader, EVENT_BUS_PRIORITY);
- // 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.
- RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
- RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
- loader.preloadTasks(plan, true /* isTopTaskHome */);
- RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
- launchOpts.numVisibleTasks = loader.getApplicationIconCacheSize();
- launchOpts.numVisibleTaskThumbnails = loader.getThumbnailCacheSize();
- launchOpts.onlyLoadForCache = true;
- loader.loadTasks(mContext, plan, launchOpts);
+ // Due to the fact that RecentsActivity is per-user, we need to establish and interface for
+ // the system user's Recents component to pass events (like show/hide/toggleRecents) to the
+ // secondary user, and vice versa (like visibility change, screen pinning).
+ final int processUser = sSystemServicesProxy.getProcessUser();
+ if (sSystemServicesProxy.isSystemUser(processUser)) {
+ // For the system user, initialize an instance of the interface that we can pass to the
+ // secondary user
+ mSystemUserCallbacks = new RecentsSystemUser(mContext, mImpl);
+ } else {
+ // For the secondary user, bind to the primary user's service to get a persistent
+ // interface to register its implementation and to later update its state
+ registerWithSystemUser();
+ }
putComponent(Recents.class, this);
}
@Override
public void onBootCompleted() {
- mBootCompleted = true;
- reloadHeaderBarLayout(true /* tryAndBindSearchWidget */);
+ mImpl.onBootCompleted();
}
- /** Shows the Recents. */
- @ProxyFromSystemToCurrentUser
+ /**
+ * Shows the Recents.
+ */
@Override
public void showRecents(boolean triggeredFromAltTab, View statusBarView) {
- if (mSystemServicesProxy.isForegroundUserSystem()) {
- showRecentsInternal(triggeredFromAltTab);
+ if (proxyToOverridePackage(ACTION_SHOW_RECENTS)) {
+ return;
+ }
+
+ int currentUser = sSystemServicesProxy.getCurrentUser();
+ if (sSystemServicesProxy.isSystemUser(currentUser)) {
+ mImpl.showRecents(triggeredFromAltTab);
} else {
- Intent intent = createLocalBroadcastIntent(mContext,
- RecentsUserEventProxyReceiver.ACTION_PROXY_SHOW_RECENTS_TO_USER);
- intent.putExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, triggeredFromAltTab);
- mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
+ if (mSystemUserCallbacks != null) {
+ IRecentsNonSystemUserCallbacks callbacks =
+ mSystemUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
+ if (callbacks != null) {
+ try {
+ callbacks.showRecents(triggeredFromAltTab);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Callback failed", e);
+ }
+ } else {
+ Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
+ }
+ }
}
}
- void showRecentsInternal(boolean triggeredFromAltTab) {
- mTriggeredFromAltTab = triggeredFromAltTab;
-
- try {
- showRecentsActivity();
- } catch (ActivityNotFoundException e) {
- Console.logRawError("Failed to launch RecentAppsIntent", e);
- }
- }
-
- /** Hides the Recents. */
- @ProxyFromSystemToCurrentUser
+ /**
+ * Hides the Recents.
+ */
@Override
public void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
- if (mSystemServicesProxy.isForegroundUserSystem()) {
- hideRecentsInternal(triggeredFromAltTab, triggeredFromHomeKey);
+ if (proxyToOverridePackage(ACTION_HIDE_RECENTS)) {
+ return;
+ }
+
+ int currentUser = sSystemServicesProxy.getCurrentUser();
+ if (sSystemServicesProxy.isSystemUser(currentUser)) {
+ mImpl.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
} else {
- Intent intent = createLocalBroadcastIntent(mContext,
- RecentsUserEventProxyReceiver.ACTION_PROXY_HIDE_RECENTS_TO_USER);
- intent.putExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, triggeredFromAltTab);
- intent.putExtra(EXTRA_TRIGGERED_FROM_HOME_KEY, triggeredFromHomeKey);
- mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
+ if (mSystemUserCallbacks != null) {
+ IRecentsNonSystemUserCallbacks callbacks =
+ mSystemUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
+ if (callbacks != null) {
+ try {
+ callbacks.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Callback failed", e);
+ }
+ } else {
+ Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
+ }
+ }
}
}
- void hideRecentsInternal(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
- if (mBootCompleted) {
- // Defer to the activity to handle hiding recents, if it handles it, then it must still
- // be visible
- Intent intent = createLocalBroadcastIntent(mContext, ACTION_HIDE_RECENTS_ACTIVITY);
- intent.putExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, triggeredFromAltTab);
- intent.putExtra(EXTRA_TRIGGERED_FROM_HOME_KEY, triggeredFromHomeKey);
- mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
- }
- }
-
- /** Toggles the Recents activity. */
- @ProxyFromSystemToCurrentUser
+ /**
+ * Toggles the Recents activity.
+ */
@Override
public void toggleRecents(Display display, int layoutDirection, View statusBarView) {
- if (mSystemServicesProxy.isForegroundUserSystem()) {
- toggleRecentsInternal();
+ if (proxyToOverridePackage(ACTION_TOGGLE_RECENTS)) {
+ return;
+ }
+
+ int currentUser = sSystemServicesProxy.getCurrentUser();
+ if (sSystemServicesProxy.isSystemUser(currentUser)) {
+ mImpl.toggleRecents();
} else {
- Intent intent = createLocalBroadcastIntent(mContext,
- RecentsUserEventProxyReceiver.ACTION_PROXY_TOGGLE_RECENTS_TO_USER);
- mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
+ if (mSystemUserCallbacks != null) {
+ IRecentsNonSystemUserCallbacks callbacks =
+ mSystemUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
+ if (callbacks != null) {
+ try {
+ callbacks.toggleRecents();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Callback failed", e);
+ }
+ } else {
+ Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
+ }
+ }
}
}
- void toggleRecentsInternal() {
- mTriggeredFromAltTab = false;
-
- try {
- toggleRecentsActivity();
- } catch (ActivityNotFoundException e) {
- Console.logRawError("Failed to launch RecentAppsIntent", e);
- }
- }
-
- /** Preloads info for the Recents activity. */
- @ProxyFromSystemToCurrentUser
+ /**
+ * Preloads info for the Recents activity.
+ */
@Override
public void preloadRecents() {
- if (mSystemServicesProxy.isForegroundUserSystem()) {
- preloadRecentsInternal();
+ int currentUser = sSystemServicesProxy.getCurrentUser();
+ if (sSystemServicesProxy.isSystemUser(currentUser)) {
+ mImpl.preloadRecents();
} else {
- Intent intent = createLocalBroadcastIntent(mContext,
- RecentsUserEventProxyReceiver.ACTION_PROXY_PRELOAD_RECENTS_TO_USER);
- mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
- }
- }
-
- void preloadRecentsInternal() {
- // Preload only the raw task list into a new load plan (which will be consumed by the
- // RecentsActivity) only if there is a task to animate to.
- ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();
- MutableBoolean topTaskHome = new MutableBoolean(true);
- RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
- sInstanceLoadPlan = loader.createLoadPlan(mContext);
- if (topTask != null && !mSystemServicesProxy.isRecentsTopMost(topTask, topTaskHome)) {
- sInstanceLoadPlan.preloadRawTasks(topTaskHome.value);
- loader.preloadTasks(sInstanceLoadPlan, topTaskHome.value);
- TaskStack stack = sInstanceLoadPlan.getTaskStack();
- if (stack.getTaskCount() > 0) {
- preCacheThumbnailTransitionBitmapAsync(topTask, stack, mDummyStackView,
- topTaskHome.value);
+ if (mSystemUserCallbacks != null) {
+ IRecentsNonSystemUserCallbacks callbacks =
+ mSystemUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
+ if (callbacks != null) {
+ try {
+ callbacks.preloadRecents();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Callback failed", e);
+ }
+ } else {
+ Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
+ }
}
}
}
@Override
public void cancelPreloadingRecents() {
- // Do nothing
+ int currentUser = sSystemServicesProxy.getCurrentUser();
+ if (sSystemServicesProxy.isSystemUser(currentUser)) {
+ mImpl.cancelPreloadingRecents();
+ } else {
+ if (mSystemUserCallbacks != null) {
+ IRecentsNonSystemUserCallbacks callbacks =
+ mSystemUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
+ if (callbacks != null) {
+ try {
+ callbacks.cancelPreloadingRecents();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Callback failed", e);
+ }
+ } else {
+ Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
+ }
+ }
+ }
}
- void showRelativeAffiliatedTask(boolean showNextTask) {
- // Return early if there is no focused stack
- int focusedStackId = mSystemServicesProxy.getFocusedStack();
- RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
- RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
- loader.preloadTasks(plan, true /* isTopTaskHome */);
- TaskStack focusedStack = plan.getTaskStack();
-
- // Return early if there are no tasks in the focused stack
- if (focusedStack == null || focusedStack.getTaskCount() == 0) return;
-
- ActivityManager.RunningTaskInfo runningTask = mSystemServicesProxy.getTopMostTask();
- // Return early if there is no running task (can't determine affiliated tasks in this case)
- if (runningTask == null) return;
- // Return early if the running task is in the home stack (optimization)
- if (mSystemServicesProxy.isInHomeStack(runningTask.id)) return;
-
- // Find the task in the recents list
- ArrayList<Task> tasks = focusedStack.getTasks();
- Task toTask = null;
- ActivityOptions launchOpts = null;
- int taskCount = tasks.size();
- int numAffiliatedTasks = 0;
- for (int i = 0; i < taskCount; i++) {
- Task task = tasks.get(i);
- if (task.key.id == runningTask.id) {
- TaskGrouping group = task.group;
- Task.TaskKey toTaskKey;
- if (showNextTask) {
- toTaskKey = group.getNextTaskInGroup(task);
- launchOpts = ActivityOptions.makeCustomAnimation(mContext,
- R.anim.recents_launch_next_affiliated_task_target,
- R.anim.recents_launch_next_affiliated_task_source);
- } else {
- toTaskKey = group.getPrevTaskInGroup(task);
- launchOpts = ActivityOptions.makeCustomAnimation(mContext,
- R.anim.recents_launch_prev_affiliated_task_target,
- R.anim.recents_launch_prev_affiliated_task_source);
- }
- if (toTaskKey != null) {
- toTask = focusedStack.findTaskWithId(toTaskKey.id);
- }
- numAffiliatedTasks = group.getTaskCount();
- break;
- }
- }
-
- // Return early if there is no next task
- if (toTask == null) {
- if (numAffiliatedTasks > 1) {
- if (showNextTask) {
- mSystemServicesProxy.startInPlaceAnimationOnFrontMostApplication(
- ActivityOptions.makeCustomInPlaceAnimation(mContext,
- R.anim.recents_launch_next_affiliated_task_bounce));
- } else {
- mSystemServicesProxy.startInPlaceAnimationOnFrontMostApplication(
- ActivityOptions.makeCustomInPlaceAnimation(mContext,
- R.anim.recents_launch_prev_affiliated_task_bounce));
- }
- }
- return;
- }
-
- // Keep track of actually launched affiliated tasks
- MetricsLogger.count(mContext, "overview_affiliated_task_launch", 1);
-
- // Launch the task
- if (toTask.isActive) {
- // Bring an active task to the foreground
- mSystemServicesProxy.moveTaskToFront(toTask.key.id, launchOpts);
- } else {
- mSystemServicesProxy.startActivityFromRecents(mContext, toTask.key.id,
- toTask.activityLabel, launchOpts);
- }
+ @Override
+ public void dockTopTask() {
+ mImpl.dockTopTask();
}
@Override
public void showNextAffiliatedTask() {
- // Keep track of when the affiliated task is triggered
- MetricsLogger.count(mContext, "overview_affiliated_task_next", 1);
- showRelativeAffiliatedTask(true);
+ mImpl.showNextAffiliatedTask();
}
@Override
public void showPrevAffiliatedTask() {
- // Keep track of when the affiliated task is triggered
- MetricsLogger.count(mContext, "overview_affiliated_task_prev", 1);
- showRelativeAffiliatedTask(false);
+ mImpl.showPrevAffiliatedTask();
}
- /** Updates on configuration change. */
- @ProxyFromSystemToCurrentUser
+ /**
+ * Updates on configuration change.
+ */
public void onConfigurationChanged(Configuration newConfig) {
- if (mSystemServicesProxy.isForegroundUserSystem()) {
- configurationChanged();
+ int currentUser = sSystemServicesProxy.getCurrentUser();
+ if (sSystemServicesProxy.isSystemUser(currentUser)) {
+ mImpl.onConfigurationChanged();
} else {
- Intent intent = createLocalBroadcastIntent(mContext,
- RecentsUserEventProxyReceiver.ACTION_PROXY_CONFIG_CHANGE_TO_USER);
- mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
- }
- }
- void configurationChanged() {
- // Don't reuse task stack views if the configuration changes
- mCanReuseTaskStackViews = false;
- mConfig.updateOnConfigurationChange();
- }
-
- /**
- * Prepares the header bar layout for the next transition, if the task view bounds has changed
- * since the last call, it will attempt to re-measure and layout the header bar to the new size.
- *
- * @param tryAndBindSearchWidget if set, will attempt to fetch and bind the search widget if one
- * is not already bound (can be expensive)
- */
- void reloadHeaderBarLayout(boolean tryAndBindSearchWidget) {
- Rect windowRect = mSystemServicesProxy.getWindowRect();
-
- // Update the configuration for the current state
- mConfig.update(mContext, mSystemServicesProxy, mSystemServicesProxy.getWindowRect());
-
- if (tryAndBindSearchWidget) {
- // Try and pre-emptively bind the search widget on startup to ensure that we
- // have the right thumbnail bounds to animate to.
- // Note: We have to reload the widget id before we get the task stack bounds below
- if (mSystemServicesProxy.getOrBindSearchAppWidget(mContext, mAppWidgetHost) != null) {
- mConfig.getSearchBarBounds(windowRect,
- mStatusBarHeight, mSearchBarBounds);
- }
- }
- Rect systemInsets = new Rect(0, mStatusBarHeight,
- (mConfig.hasTransposedNavBar ? mNavBarWidth : 0),
- (mConfig.hasTransposedNavBar ? 0 : mNavBarHeight));
- mConfig.getTaskStackBounds(windowRect, systemInsets.top, systemInsets.right,
- mSearchBarBounds, mTaskStackBounds);
-
- // Rebind the header bar and draw it for the transition
- TaskStackViewLayoutAlgorithm algo = mDummyStackView.getStackAlgorithm();
- Rect taskStackBounds = new Rect(mTaskStackBounds);
- algo.setSystemInsets(systemInsets);
- algo.computeRects(windowRect.width(), windowRect.height(), taskStackBounds);
- Rect taskViewBounds = algo.getUntransformedTaskViewBounds();
- if (!taskViewBounds.equals(mLastTaskViewBounds)) {
- mLastTaskViewBounds.set(taskViewBounds);
-
- int taskViewWidth = taskViewBounds.width();
- synchronized (mHeaderBarLock) {
- mHeaderBar.measure(
- View.MeasureSpec.makeMeasureSpec(taskViewWidth, View.MeasureSpec.EXACTLY),
- View.MeasureSpec.makeMeasureSpec(mTaskBarHeight, View.MeasureSpec.EXACTLY));
- mHeaderBar.layout(0, 0, taskViewWidth, mTaskBarHeight);
- }
- }
- }
-
- /** Toggles the recents activity */
- void toggleRecentsActivity() {
- // If the user has toggled it too quickly, then just eat up the event here (it's better than
- // showing a janky screenshot).
- // NOTE: Ideally, the screenshot mechanism would take the window transform into account
- if ((SystemClock.elapsedRealtime() - mLastToggleTime) < sMinToggleDelay) {
- return;
- }
-
- // If Recents is the front most activity, then we should just communicate with it directly
- // to launch the first task or dismiss itself
- ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();
- MutableBoolean isTopTaskHome = new MutableBoolean(true);
- if (topTask != null && mSystemServicesProxy.isRecentsTopMost(topTask, isTopTaskHome)) {
- // Notify recents to toggle itself
- Intent intent = createLocalBroadcastIntent(mContext, ACTION_TOGGLE_RECENTS_ACTIVITY);
- mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
- mLastToggleTime = SystemClock.elapsedRealtime();
- return;
- } else {
- // Otherwise, start the recents activity
- showRecentsActivity(topTask, isTopTaskHome.value);
- }
- }
-
- /** Shows the recents activity if it is not already running */
- void showRecentsActivity() {
- // Check if the top task is in the home stack, and start the recents activity
- ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();
- MutableBoolean isTopTaskHome = new MutableBoolean(true);
- if (topTask == null || !mSystemServicesProxy.isRecentsTopMost(topTask, isTopTaskHome)) {
- showRecentsActivity(topTask, isTopTaskHome.value);
- }
- }
-
- /**
- * Creates the activity options for a unknown state->recents transition.
- */
- ActivityOptions getUnknownTransitionActivityOptions() {
- mStartAnimationTriggered = false;
- return ActivityOptions.makeCustomAnimation(mContext,
- R.anim.recents_from_unknown_enter,
- R.anim.recents_from_unknown_exit,
- mHandler, this);
- }
-
- /**
- * Creates the activity options for a home->recents transition.
- */
- ActivityOptions getHomeTransitionActivityOptions(boolean fromSearchHome) {
- mStartAnimationTriggered = false;
- if (fromSearchHome) {
- return ActivityOptions.makeCustomAnimation(mContext,
- R.anim.recents_from_search_launcher_enter,
- R.anim.recents_from_search_launcher_exit,
- mHandler, this);
- }
- return ActivityOptions.makeCustomAnimation(mContext,
- R.anim.recents_from_launcher_enter,
- R.anim.recents_from_launcher_exit,
- mHandler, this);
- }
-
- /**
- * Creates the activity options for an app->recents transition.
- */
- ActivityOptions getThumbnailTransitionActivityOptions(ActivityManager.RunningTaskInfo topTask,
- TaskStack stack, TaskStackView stackView) {
-
- // Update the destination rect
- Task toTask = new Task();
- TaskViewTransform toTransform = getThumbnailTransitionTransform(stack, stackView,
- topTask.id, toTask);
- Rect toTaskRect = toTransform.rect;
- 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);
- }
- if (thumbnail != null) {
- mStartAnimationTriggered = false;
- return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
- thumbnail, toTaskRect.left, toTaskRect.top, toTaskRect.width(),
- toTaskRect.height(), mHandler, this);
- }
-
- // If both the screenshot and thumbnail fails, then just fall back to the default transition
- return getUnknownTransitionActivityOptions();
- }
-
- /**
- * Preloads the icon of a task.
- */
- void preloadIcon(ActivityManager.RunningTaskInfo task) {
-
- // Ensure that we load the running task's icon
- RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
- launchOpts.runningTaskId = task.id;
- launchOpts.loadThumbnails = false;
- launchOpts.onlyLoadForCache = true;
- RecentsTaskLoader.getInstance().loadTasks(mContext, sInstanceLoadPlan, launchOpts);
- }
-
- /**
- * Caches the header thumbnail used for a window animation asynchronously into
- * {@link #mThumbnailTransitionBitmapCache}.
- */
- void preCacheThumbnailTransitionBitmapAsync(ActivityManager.RunningTaskInfo topTask,
- TaskStack stack, TaskStackView stackView, boolean isTopTaskHome) {
- preloadIcon(topTask);
-
- // Update the header bar if necessary
- reloadHeaderBarLayout(false /* tryAndBindSearchWidget */);
-
- // Update the destination rect
- mDummyStackView.updateMinMaxScrollForStack(stack);
- final Task toTask = new Task();
- final TaskViewTransform toTransform = getThumbnailTransitionTransform(stack, stackView,
- topTask.id, toTask);
- new AsyncTask<Void, Void, Bitmap>() {
- @Override
- protected Bitmap doInBackground(Void... params) {
- return drawThumbnailTransitionBitmap(toTask, toTransform);
- }
-
- @Override
- protected void onPostExecute(Bitmap bitmap) {
- mThumbnailTransitionBitmapCache = bitmap;
- mThumbnailTransitionBitmapCacheKey = toTask;
- }
- }.execute();
- }
-
- /**
- * Draws the header of a task used for the window animation into a bitmap.
- */
- Bitmap drawThumbnailTransitionBitmap(Task toTask, TaskViewTransform toTransform) {
- if (toTransform != null && toTask.key != null) {
- Bitmap thumbnail;
- synchronized (mHeaderBarLock) {
- int toHeaderWidth = (int) (mHeaderBar.getMeasuredWidth() * toTransform.scale);
- int toHeaderHeight = (int) (mHeaderBar.getMeasuredHeight() * toTransform.scale);
- thumbnail = Bitmap.createBitmap(toHeaderWidth, toHeaderHeight,
- Bitmap.Config.ARGB_8888);
- if (Constants.DebugFlags.App.EnableTransitionThumbnailDebugMode) {
- thumbnail.eraseColor(0xFFff0000);
- } else {
- Canvas c = new Canvas(thumbnail);
- c.scale(toTransform.scale, toTransform.scale);
- mHeaderBar.rebindToTask(toTask);
- mHeaderBar.draw(c);
- c.setBitmap(null);
- }
- }
- return thumbnail.createAshmemBitmap();
- }
- return null;
- }
-
- /** Returns the transition rect for the given task id. */
- TaskViewTransform getThumbnailTransitionTransform(TaskStack stack, TaskStackView stackView,
- int runningTaskId, Task runningTaskOut) {
- // Find the running task in the TaskStack
- Task task = null;
- ArrayList<Task> tasks = stack.getTasks();
- if (runningTaskId != -1) {
- // Otherwise, try and find the task with the
- int taskCount = tasks.size();
- for (int i = taskCount - 1; i >= 0; i--) {
- Task t = tasks.get(i);
- if (t.key.id == runningTaskId) {
- task = t;
- runningTaskOut.copyFrom(t);
- break;
- }
- }
- }
- if (task == null) {
- // If no task is specified or we can not find the task just use the front most one
- task = tasks.get(tasks.size() - 1);
- runningTaskOut.copyFrom(task);
- }
-
- // Get the transform for the running task
- stackView.getScroller().setStackScrollToInitialState();
- mTmpTransform = stackView.getStackAlgorithm().getStackTransform(task,
- stackView.getScroller().getStackScroll(), mTmpTransform, null);
- return mTmpTransform;
- }
-
- /** Shows the recents activity */
- void showRecentsActivity(ActivityManager.RunningTaskInfo topTask, boolean isTopTaskHome) {
- RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
-
- // Update the header bar if necessary
- reloadHeaderBarLayout(false /* tryAndBindSearchWidget */);
-
- if (sInstanceLoadPlan == null) {
- // Create a new load plan if onPreloadRecents() was never triggered
- sInstanceLoadPlan = loader.createLoadPlan(mContext);
- }
-
- if (!sInstanceLoadPlan.hasTasks()) {
- loader.preloadTasks(sInstanceLoadPlan, isTopTaskHome);
- }
- TaskStack stack = sInstanceLoadPlan.getTaskStack();
-
- // Prepare the dummy stack for the transition
- mDummyStackView.updateMinMaxScrollForStack(stack);
- TaskStackViewLayoutAlgorithm.VisibilityReport stackVr =
- mDummyStackView.computeStackVisibilityReport();
- boolean hasRecentTasks = stack.getTaskCount() > 0;
- boolean useThumbnailTransition = (topTask != null) && !isTopTaskHome && hasRecentTasks;
-
- if (useThumbnailTransition) {
- // Try starting with a thumbnail transition
- ActivityOptions opts = getThumbnailTransitionActivityOptions(topTask, stack,
- mDummyStackView);
- if (opts != null) {
- startRecentsActivity(topTask, opts, false /* fromHome */,
- false /* fromSearchHome */, true /* fromThumbnail */, stackVr);
- } else {
- // Fall through below to the non-thumbnail transition
- useThumbnailTransition = false;
- }
- }
-
- if (!useThumbnailTransition) {
- // If there is no thumbnail transition, but is launching from home into recents, then
- // use a quick home transition and do the animation from home
- if (hasRecentTasks) {
- String homeActivityPackage = mSystemServicesProxy.getHomeActivityPackageName();
- String searchWidgetPackage =
- Prefs.getString(mContext, Prefs.Key.SEARCH_APP_WIDGET_PACKAGE, null);
-
- // Determine whether we are coming from a search owned home activity
- boolean fromSearchHome = (homeActivityPackage != null) &&
- homeActivityPackage.equals(searchWidgetPackage);
- ActivityOptions opts = getHomeTransitionActivityOptions(fromSearchHome);
- startRecentsActivity(topTask, opts, true /* fromHome */, fromSearchHome,
- false /* fromThumbnail */, stackVr);
- } else {
- // Otherwise we do the normal fade from an unknown source
- ActivityOptions opts = getUnknownTransitionActivityOptions();
- startRecentsActivity(topTask, opts, true /* fromHome */,
- false /* fromSearchHome */, false /* fromThumbnail */, stackVr);
- }
- }
- mLastToggleTime = SystemClock.elapsedRealtime();
- }
-
- /** Starts the recents activity */
- void startRecentsActivity(ActivityManager.RunningTaskInfo topTask,
- ActivityOptions opts, boolean fromHome, boolean fromSearchHome, boolean fromThumbnail,
- TaskStackViewLayoutAlgorithm.VisibilityReport vr) {
- // Update the configuration based on the launch options
- RecentsActivityLaunchState launchState = mConfig.getLaunchState();
- launchState.launchedFromHome = fromSearchHome || fromHome;
- launchState.launchedFromSearchHome = fromSearchHome;
- launchState.launchedFromAppWithThumbnail = fromThumbnail;
- launchState.launchedToTaskId = (topTask != null) ? topTask.id : -1;
- launchState.launchedWithAltTab = mTriggeredFromAltTab;
- launchState.launchedReuseTaskStackViews = mCanReuseTaskStackViews;
- launchState.launchedNumVisibleTasks = vr.numVisibleTasks;
- launchState.launchedNumVisibleThumbnails = vr.numVisibleThumbnails;
- launchState.launchedHasConfigurationChanged = false;
-
- Intent intent = new Intent(sToggleRecentsAction);
- intent.setClassName(sRecentsPackage, sRecentsActivity);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
- | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
- if (opts != null) {
- mContext.startActivityAsUser(intent, opts.toBundle(), UserHandle.CURRENT);
- } else {
- mContext.startActivityAsUser(intent, UserHandle.CURRENT);
- }
- mCanReuseTaskStackViews = true;
- }
-
- /** Notifies the callbacks that the visibility of Recents has changed. */
- @ProxyFromAnyToSystemUser
- public static void notifyVisibilityChanged(Context context, SystemServicesProxy ssp,
- boolean visible) {
- if (ssp.isForegroundUserSystem()) {
- visibilityChanged(context, visible);
- } else {
- Intent intent = createLocalBroadcastIntent(context,
- ACTION_PROXY_NOTIFY_RECENTS_VISIBLITY_TO_OWNER);
- intent.putExtra(EXTRA_RECENTS_VISIBILITY, visible);
- context.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
- }
- }
- static void visibilityChanged(Context context, boolean visible) {
- // For the primary user, the context for the SystemUI component is the SystemUIApplication
- SystemUIApplication app = (SystemUIApplication)
- getInstanceAndStartIfNeeded(context.getApplicationContext()).mContext;
- PhoneStatusBar statusBar = app.getComponent(PhoneStatusBar.class);
- if (statusBar != null) {
- statusBar.updateRecentsVisibility(visible);
- }
- }
-
- /** Notifies the status bar to trigger screen pinning. */
- @ProxyFromAnyToSystemUser
- public static void startScreenPinning(Context context, SystemServicesProxy ssp) {
- if (ssp.isForegroundUserSystem()) {
- onStartScreenPinning(context);
- } else {
- Intent intent = createLocalBroadcastIntent(context,
- ACTION_PROXY_SCREEN_PINNING_REQUEST_TO_OWNER);
- context.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
- }
- }
- static void onStartScreenPinning(Context context) {
- // For the primary user, the context for the SystemUI component is the SystemUIApplication
- SystemUIApplication app = (SystemUIApplication)
- getInstanceAndStartIfNeeded(context.getApplicationContext()).mContext;
- PhoneStatusBar statusBar = app.getComponent(PhoneStatusBar.class);
- if (statusBar != null) {
- statusBar.showScreenPinningRequest(false);
- }
- }
-
- /**
- * Returns the preloaded load plan and invalidates it.
- */
- public static RecentsTaskLoadPlan consumeInstanceLoadPlan() {
- RecentsTaskLoadPlan plan = sInstanceLoadPlan;
- sInstanceLoadPlan = null;
- return plan;
- }
-
- /**** OnAnimationStartedListener Implementation ****/
-
- @Override
- public void onAnimationStarted() {
- // Notify recents to start the enter animation
- if (!mStartAnimationTriggered) {
- // There can be a race condition between the start animation callback and
- // the start of the new activity (where we register the receiver that listens
- // to this broadcast, so we add our own receiver and if that gets called, then
- // we know the activity has not yet started and we can retry sending the broadcast.
- BroadcastReceiver fallbackReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (getResultCode() == Activity.RESULT_OK) {
- mStartAnimationTriggered = true;
- return;
+ if (mSystemUserCallbacks != null) {
+ IRecentsNonSystemUserCallbacks callbacks =
+ mSystemUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
+ if (callbacks != null) {
+ try {
+ callbacks.onConfigurationChanged();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Callback failed", e);
}
-
- // Schedule for the broadcast to be sent again after some time
- mHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- onAnimationStarted();
- }
- }, 25);
+ } else {
+ Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
}
- };
-
- // Send the broadcast to notify Recents that the animation has started
- Intent intent = createLocalBroadcastIntent(mContext, ACTION_START_ENTER_ANIMATION);
- mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null,
- fallbackReceiver, null, Activity.RESULT_CANCELED, null, null);
+ }
}
}
+
+ /**
+ * Handle Recents activity visibility changed.
+ */
+ public final void onBusEvent(final RecentsVisibilityChangedEvent event) {
+ int processUser = event.systemServicesProxy.getProcessUser();
+ if (event.systemServicesProxy.isSystemUser(processUser)) {
+ mImpl.onVisibilityChanged(event.applicationContext, event.visible);
+ } else {
+ postToSystemUser(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ mCallbacksToSystemUser.updateRecentsVisibility(event.visible);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Callback failed", e);
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * Handle screen pinning request.
+ */
+ public final void onBusEvent(final ScreenPinningRequestEvent event) {
+ int processUser = sSystemServicesProxy.getProcessUser();
+ if (sSystemServicesProxy.isSystemUser(processUser)) {
+ mImpl.onStartScreenPinning(event.applicationContext);
+ } else {
+ postToSystemUser(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ mCallbacksToSystemUser.startScreenPinning();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Callback failed", e);
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * Attempts to register with the system user.
+ */
+ private void registerWithSystemUser() {
+ final int processUser = sSystemServicesProxy.getProcessUser();
+ postToSystemUser(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ mCallbacksToSystemUser.registerNonSystemUserCallbacks(mImpl, processUser);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to register", e);
+ }
+ }
+ });
+ }
+
+ /**
+ * Runs the runnable in the system user's Recents context, connecting to the service if
+ * necessary.
+ */
+ private void postToSystemUser(final Runnable onConnectRunnable) {
+ mOnConnectRunnables.add(onConnectRunnable);
+ if (mCallbacksToSystemUser == null) {
+ Intent systemUserServiceIntent = new Intent();
+ systemUserServiceIntent.setClass(mContext, RecentsSystemUserService.class);
+ boolean bound = mContext.bindServiceAsUser(systemUserServiceIntent,
+ mServiceConnectionToSystemUser, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM);
+ if (!bound) {
+ // Retry after a fixed duration
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ registerWithSystemUser();
+ }
+ }, BIND_TO_SYSTEM_USER_RETRY_DELAY);
+ }
+ } else {
+ runAndFlushOnConnectRunnables();
+ }
+ }
+
+ /**
+ * Runs all the queued runnables after a service connection is made.
+ */
+ private void runAndFlushOnConnectRunnables() {
+ for (Runnable r : mOnConnectRunnables) {
+ r.run();
+ }
+ mOnConnectRunnables.clear();
+ }
+
+ /**
+ * Attempts to proxy the following action to the override recents package.
+ * @return whether the proxying was successful
+ */
+ private boolean proxyToOverridePackage(String action) {
+ if (mOverrideRecentsPackageName != null) {
+ Intent intent = new Intent(action);
+ intent.setPackage(mOverrideRecentsPackageName);
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ mContext.sendBroadcast(intent);
+ return true;
+ }
+ return false;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index e647c1f..58f7124 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -32,19 +32,35 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
+import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewStub;
+import android.view.ViewTreeObserver;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.AppWidgetProviderChangedEvent;
-import com.android.systemui.recents.events.ui.DismissTaskEvent;
+import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
+import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationStartedEvent;
+import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent;
+import com.android.systemui.recents.events.activity.HideRecentsEvent;
+import com.android.systemui.recents.events.activity.IterateRecentsEvent;
+import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent;
+import com.android.systemui.recents.events.activity.LaunchTaskSucceededEvent;
+import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
+import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
+import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
+import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
import com.android.systemui.recents.events.ui.ResizeTaskEvent;
import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent;
+import com.android.systemui.recents.events.ui.UserInteractionEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
-import com.android.systemui.recents.misc.Console;
+import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent;
+import com.android.systemui.recents.events.ui.focus.FocusNextTaskViewEvent;
+import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent;
+import com.android.systemui.recents.misc.DozeTrigger;
import com.android.systemui.recents.misc.ReferenceCountedTrigger;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.model.RecentsPackageMonitor;
@@ -61,13 +77,17 @@
/**
* The main Recents activity that is started from AlternateRecentsComponent.
*/
-public class RecentsActivity extends Activity implements RecentsView.RecentsViewCallbacks {
+public class RecentsActivity extends Activity implements RecentsView.RecentsViewCallbacks,
+ ViewTreeObserver.OnPreDrawListener {
+
+ private final static String TAG = "RecentsActivity";
+ private final static boolean DEBUG = false;
public final static int EVENT_BUS_PRIORITY = Recents.EVENT_BUS_PRIORITY + 1;
- RecentsConfiguration mConfig;
RecentsPackageMonitor mPackageMonitor;
long mLastTabKeyEventTime;
+ boolean mFinishedOnStartup;
// Top level views
RecentsView mRecentsView;
@@ -86,8 +106,13 @@
// Runnables to finish the Recents activity
FinishRecentsRunnable mFinishLaunchHomeRunnable;
- // Runnable to be executed after we paused ourselves
- Runnable mAfterPauseRunnable;
+ // The trigger to automatically launch the current task
+ DozeTrigger mIterateTrigger = new DozeTrigger(500, new Runnable() {
+ @Override
+ public void run() {
+ dismissRecentsToFocusedTask(false);
+ }
+ });
/**
* A common Runnable to finish Recents either by calling finish() (with a custom animation) or
@@ -114,43 +139,12 @@
try {
startActivityAsUser(mLaunchIntent, mLaunchOpts.toBundle(), UserHandle.CURRENT);
} catch (Exception e) {
- Console.logError(RecentsActivity.this,
- getString(R.string.recents_launch_error_message, "Home"));
+ Log.e(TAG, getString(R.string.recents_launch_error_message, "Home"), e);
}
}
}
/**
- * Broadcast receiver to handle messages from AlternateRecentsComponent.
- */
- final BroadcastReceiver mServiceBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action.equals(Recents.ACTION_HIDE_RECENTS_ACTIVITY)) {
- if (intent.getBooleanExtra(Recents.EXTRA_TRIGGERED_FROM_ALT_TAB, false)) {
- // If we are hiding from releasing Alt-Tab, dismiss Recents to the focused app
- dismissRecentsToFocusedTaskOrHome(false);
- } else if (intent.getBooleanExtra(Recents.EXTRA_TRIGGERED_FROM_HOME_KEY, false)) {
- // Otherwise, dismiss Recents to Home
- dismissRecentsToHome(true);
- } else {
- // Do nothing
- }
- } else if (action.equals(Recents.ACTION_TOGGLE_RECENTS_ACTIVITY)) {
- // If we are toggling Recents, then first unfilter any filtered stacks first
- dismissRecentsToFocusedTaskOrHome(true);
- } else if (action.equals(Recents.ACTION_START_ENTER_ANIMATION)) {
- // Trigger the enter animation
- onEnterAnimationTriggered();
- // Notify the fallback receiver that we have successfully got the broadcast
- // See AlternateRecentsComponent.onAnimationStarted()
- setResultCode(Activity.RESULT_OK);
- }
- }
- };
-
- /**
* Broadcast receiver to handle messages from the system
*/
final BroadcastReceiver mSystemBroadcastReceiver = new BroadcastReceiver() {
@@ -162,7 +156,7 @@
dismissRecentsToHomeIfVisible(false);
} else if (action.equals(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED)) {
// When the search activity changes, update the search widget view
- SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
+ SystemServicesProxy ssp = Recents.getSystemServices();
mSearchWidgetInfo = ssp.getOrBindSearchAppWidget(context, mAppWidgetHost);
refreshSearchWidgetView();
}
@@ -173,14 +167,15 @@
void updateRecentsTasks() {
// If AlternateRecentsComponent has preloaded a load plan, then use that to prevent
// reconstructing the task stack
- RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
- RecentsTaskLoadPlan plan = Recents.consumeInstanceLoadPlan();
+ RecentsTaskLoader loader = Recents.getTaskLoader();
+ RecentsTaskLoadPlan plan = RecentsImpl.consumeInstanceLoadPlan();
if (plan == null) {
plan = loader.createLoadPlan(this);
}
// Start loading tasks according to the load plan
- RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ RecentsConfiguration config = Recents.getConfiguration();
+ RecentsActivityLaunchState launchState = config.getLaunchState();
if (!plan.hasTasks()) {
loader.preloadTasks(plan, launchState.launchedFromHome);
}
@@ -229,15 +224,19 @@
mEmptyView = mEmptyViewStub.inflate();
}
mEmptyView.setVisibility(View.VISIBLE);
- mRecentsView.setSearchBarVisibility(View.GONE);
+ if (!Constants.DebugFlags.App.DisableSearchBar) {
+ mRecentsView.setSearchBarVisibility(View.GONE);
+ }
} else {
if (mEmptyView != null) {
mEmptyView.setVisibility(View.GONE);
}
- if (mRecentsView.hasValidSearchBar()) {
- mRecentsView.setSearchBarVisibility(View.VISIBLE);
- } else {
- refreshSearchWidgetView();
+ if (!Constants.DebugFlags.App.DisableSearchBar) {
+ if (mRecentsView.hasValidSearchBar()) {
+ mRecentsView.setSearchBarVisibility(View.VISIBLE);
+ } else {
+ refreshSearchWidgetView();
+ }
}
}
@@ -263,23 +262,32 @@
MetricsLogger.histogram(this, "overview_task_count", taskCount);
}
- /** Dismisses recents if we are already visible and the intent is to toggle the recents view */
+ /**
+ * Dismisses recents if we are already visible and the intent is to toggle the recents view.
+ */
+ boolean dismissRecentsToFocusedTask(boolean checkFilteredStackState) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) {
+ // If we currently have filtered stacks, then unfilter those first
+ if (checkFilteredStackState &&
+ mRecentsView.unfilterFilteredStacks()) return true;
+ // If we have a focused Task, launch that Task now
+ if (mRecentsView.launchFocusedTask()) return true;
+ }
+ return false;
+ }
+
+ /**
+ * Dismisses recents if we are already visible and the intent is to toggle the recents view.
+ */
boolean dismissRecentsToFocusedTaskOrHome(boolean checkFilteredStackState) {
- RecentsActivityLaunchState launchState = mConfig.getLaunchState();
- SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
+ SystemServicesProxy ssp = Recents.getSystemServices();
if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) {
// If we currently have filtered stacks, then unfilter those first
if (checkFilteredStackState &&
mRecentsView.unfilterFilteredStacks()) return true;
// If we have a focused Task, launch that Task now
if (mRecentsView.launchFocusedTask()) return true;
- // If we launched from Home, then return to Home
- if (launchState.launchedFromHome) {
- dismissRecentsToHome(true);
- return true;
- }
- // Otherwise, try and return to the Task that Recents was launched from
- if (mRecentsView.launchPreviousTask()) return true;
// If none of the other cases apply, then just go Home
dismissRecentsToHome(true);
return true;
@@ -296,7 +304,6 @@
null, mFinishLaunchHomeRunnable, null);
mRecentsView.startExitToHomeAnimation(
new ViewAnimation.TaskViewExitContext(exitTrigger));
- mScrimViews.startExitRecentsAnimation();
} else {
mFinishLaunchHomeRunnable.run();
}
@@ -310,7 +317,7 @@
/** Dismisses Recents directly to Home if we currently aren't transitioning. */
boolean dismissRecentsToHomeIfVisible(boolean animated) {
- SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
+ SystemServicesProxy ssp = Recents.getSystemServices();
if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) {
// Return to Home
dismissRecentsToHome(animated);
@@ -323,20 +330,26 @@
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ mFinishedOnStartup = false;
+
+ // In the case that the activity starts up before the Recents component has initialized
+ // (usually when debugging/pushing the SysUI apk), just finish this activity.
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ if (ssp == null) {
+ mFinishedOnStartup = true;
+ finish();
+ return;
+ }
// Register this activity with the event bus
EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
- // For the non-primary user, ensure that the SystemServicesProxy and configuration is
- // initialized
- RecentsTaskLoader.initialize(this);
- SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
- mConfig = RecentsConfiguration.initialize(this, ssp);
- mConfig.update(this, ssp, ssp.getWindowRect());
- mPackageMonitor = new RecentsPackageMonitor();
-
// Initialize the widget host (the host id is static and does not change)
- mAppWidgetHost = new RecentsAppWidgetHost(this, Constants.Values.App.AppWidgetHostId);
+ if (!Constants.DebugFlags.App.DisableSearchBar) {
+ mAppWidgetHost = new RecentsAppWidgetHost(this, RecentsAppWidgetHost.HOST_ID);
+ }
+ mPackageMonitor = new RecentsPackageMonitor();
+ mPackageMonitor.register(this);
// Set the Recents layout
setContentView(R.layout.recents);
@@ -349,12 +362,16 @@
mScrimViews = new SystemBarScrimViews(this);
// Bind the search app widget when we first start up
- mSearchWidgetInfo = ssp.getOrBindSearchAppWidget(this, mAppWidgetHost);
+ if (!Constants.DebugFlags.App.DisableSearchBar) {
+ mSearchWidgetInfo = ssp.getOrBindSearchAppWidget(this, mAppWidgetHost);
+ }
// Register the broadcast receiver to handle messages when the screen is turned off
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_OFF);
- filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
+ if (!Constants.DebugFlags.App.DisableSearchBar) {
+ filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
+ }
registerReceiver(mSystemBroadcastReceiver, filter);
}
@@ -367,21 +384,6 @@
@Override
protected void onStart() {
super.onStart();
- RecentsActivityLaunchState launchState = mConfig.getLaunchState();
- MetricsLogger.visible(this, MetricsLogger.OVERVIEW_ACTIVITY);
- RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
- SystemServicesProxy ssp = loader.getSystemServicesProxy();
- Recents.notifyVisibilityChanged(this, ssp, true);
-
- // Register the broadcast receiver to handle messages from our service
- IntentFilter filter = new IntentFilter();
- filter.addAction(Recents.ACTION_HIDE_RECENTS_ACTIVITY);
- filter.addAction(Recents.ACTION_TOGGLE_RECENTS_ACTIVITY);
- filter.addAction(Recents.ACTION_START_ENTER_ANIMATION);
- registerReceiver(mServiceBroadcastReceiver, filter);
-
- // Register any broadcast receivers for the task loader
- mPackageMonitor.register(this);
// Update the recent tasks
updateRecentsTasks();
@@ -389,92 +391,112 @@
// 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
+ RecentsConfiguration config = Recents.getConfiguration();
+ RecentsActivityLaunchState launchState = config.getLaunchState();
boolean wasLaunchedByAm = !launchState.launchedFromHome &&
!launchState.launchedFromAppWithThumbnail;
if (launchState.launchedHasConfigurationChanged || wasLaunchedByAm) {
- onEnterAnimationTriggered();
+ EventBus.getDefault().send(new EnterRecentsWindowAnimationStartedEvent());
}
if (!launchState.launchedHasConfigurationChanged) {
mRecentsView.disableLayersForOneFrame();
}
+
+ // Notify that recents is now visible
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, ssp, true));
+
+ MetricsLogger.visible(this, MetricsLogger.OVERVIEW_ACTIVITY);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ final RecentsActivityLaunchState state = Recents.getConfiguration().getLaunchState();
+ if (state.startHidden) {
+ state.startHidden = false;
+ mRecentsView.setStackViewVisibility(View.INVISIBLE);
+ }
}
@Override
protected void onPause() {
super.onPause();
- if (mAfterPauseRunnable != null) {
- mRecentsView.post(mAfterPauseRunnable);
- mAfterPauseRunnable = null;
+
+ if (Constants.DebugFlags.App.EnableFastToggleRecents) {
+ // Stop the fast-toggle dozer
+ mIterateTrigger.stopDozing();
+ }
+
+ if (Constants.DebugFlags.App.EnableFastToggleRecents) {
+ // Stop the fast-toggle dozer
+ mIterateTrigger.stopDozing();
}
}
@Override
protected void onStop() {
super.onStop();
- MetricsLogger.hidden(this, MetricsLogger.OVERVIEW_ACTIVITY);
- RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
- SystemServicesProxy ssp = loader.getSystemServicesProxy();
- Recents.notifyVisibilityChanged(this, ssp, false);
- // Notify the views that we are no longer visible
- mRecentsView.onRecentsHidden();
-
- // Unregister the RecentsService receiver
- unregisterReceiver(mServiceBroadcastReceiver);
-
- // Unregister any broadcast receivers for the task loader
- mPackageMonitor.unregister();
+ // Notify that recents is now hidden
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, ssp, false));
// Workaround for b/22542869, if the RecentsActivity is started again, but without going
// through SystemUI, we need to reset the config launch flags to ensure that we do not
// wait on the system to send a signal that was never queued.
- RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ RecentsConfiguration config = Recents.getConfiguration();
+ RecentsActivityLaunchState launchState = config.getLaunchState();
launchState.launchedFromHome = false;
launchState.launchedFromSearchHome = false;
launchState.launchedFromAppWithThumbnail = false;
launchState.launchedToTaskId = -1;
launchState.launchedWithAltTab = false;
launchState.launchedHasConfigurationChanged = false;
+
+ MetricsLogger.hidden(this, MetricsLogger.OVERVIEW_ACTIVITY);
}
@Override
protected void onDestroy() {
super.onDestroy();
+ // In the case that the activity finished on startup, just skip the unregistration below
+ if (mFinishedOnStartup) {
+ return;
+ }
+
// Unregister the system broadcast receivers
unregisterReceiver(mSystemBroadcastReceiver);
+ // Unregister any broadcast receivers for the task loader
+ mPackageMonitor.unregister();
+
// Stop listening for widget package changes if there was one bound
- mAppWidgetHost.stopListening();
+ if (!Constants.DebugFlags.App.DisableSearchBar) {
+ mAppWidgetHost.stopListening();
+ }
+
EventBus.getDefault().unregister(this);
}
- public void onEnterAnimationTriggered() {
- // Try and start the enter animation (or restart it on configuration changed)
- ReferenceCountedTrigger t = new ReferenceCountedTrigger(this, null, null, null);
- ViewAnimation.TaskViewEnterContext ctx = new ViewAnimation.TaskViewEnterContext(t);
- mRecentsView.startEnterRecentsAnimation(ctx);
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ EventBus.getDefault().register(mScrimViews, EVENT_BUS_PRIORITY);
+ }
- if (mSearchWidgetInfo != null) {
- ctx.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
- @Override
- public void run() {
- // Start listening for widget package changes if there is one bound
- if (mAppWidgetHost != null) {
- mAppWidgetHost.startListening();
- }
- }
- });
- }
-
- // Animate the SystemUI scrim views
- mScrimViews.startEnterRecentsAnimation();
+ @Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ EventBus.getDefault().unregister(mScrimViews);
}
@Override
public void onTrimMemory(int level) {
- RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
+ RecentsTaskLoader loader = Recents.getTaskLoader();
if (loader != null) {
loader.onTrimMemory(level);
}
@@ -488,24 +510,33 @@
boolean hasRepKeyTimeElapsed = (SystemClock.elapsedRealtime() -
mLastTabKeyEventTime) > altTabKeyDelay;
if (event.getRepeatCount() <= 0 || hasRepKeyTimeElapsed) {
+ // As we iterate to the next/previous task, cancel any current/lagging window
+ // transition animations
+ EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null));
+
// Focus the next task in the stack
final boolean backward = event.isShiftPressed();
- mRecentsView.focusNextTask(!backward);
+ if (backward) {
+ EventBus.getDefault().send(new FocusPreviousTaskViewEvent());
+ } else {
+ EventBus.getDefault().send(new FocusNextTaskViewEvent());
+ }
mLastTabKeyEventTime = SystemClock.elapsedRealtime();
}
return true;
}
case KeyEvent.KEYCODE_DPAD_UP: {
- mRecentsView.focusNextTask(true);
+ EventBus.getDefault().send(new FocusNextTaskViewEvent());
return true;
}
case KeyEvent.KEYCODE_DPAD_DOWN: {
- mRecentsView.focusNextTask(false);
+ EventBus.getDefault().send(new FocusPreviousTaskViewEvent());
return true;
}
case KeyEvent.KEYCODE_DEL:
case KeyEvent.KEYCODE_FORWARD_DEL: {
- mRecentsView.dismissFocusedTask();
+ EventBus.getDefault().send(new DismissFocusedTaskViewEvent());
+
// Keep track of deletions by keyboard
MetricsLogger.histogram(this, "overview_task_dismissed_source",
Constants.Metrics.DismissSourceKeyboard);
@@ -519,7 +550,7 @@
@Override
public void onUserInteraction() {
- mRecentsView.onUserInteraction();
+ EventBus.getDefault().send(new UserInteractionEvent());
}
@Override
@@ -540,51 +571,97 @@
/**** RecentsView.RecentsViewCallbacks Implementation ****/
@Override
- public void onExitToHomeAnimationTriggered() {
- // Animate the SystemUI scrim views out
- mScrimViews.startExitRecentsAnimation();
- }
-
- @Override
- public void onTaskViewClicked() {
- }
-
- @Override
- public void onTaskLaunchFailed() {
- // Return to Home
- dismissRecentsToHome(true);
- }
-
- @Override
public void onAllTaskViewsDismissed() {
mFinishLaunchHomeRunnable.run();
}
- @Override
- public void onScreenPinningRequest() {
- RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
- SystemServicesProxy ssp = loader.getSystemServicesProxy();
- Recents.startScreenPinning(this, ssp);
-
- MetricsLogger.count(this, "overview_screen_pinned", 1);
- }
-
- @Override
- public void runAfterPause(Runnable r) {
- mAfterPauseRunnable = r;
- }
-
/**** EventBus events ****/
+ public final void onBusEvent(ToggleRecentsEvent event) {
+ dismissRecentsToFocusedTaskOrHome(true /* checkFilteredStackState */);
+ }
+
+ public final void onBusEvent(IterateRecentsEvent event) {
+ // Focus the next task
+ EventBus.getDefault().send(new FocusNextTaskViewEvent());
+ mIterateTrigger.poke();
+ }
+
+ public final void onBusEvent(UserInteractionEvent event) {
+ mIterateTrigger.stopDozing();
+ }
+
+ public final void onBusEvent(HideRecentsEvent event) {
+ if (event.triggeredFromAltTab) {
+ // If we are hiding from releasing Alt-Tab, dismiss Recents to the focused app
+ dismissRecentsToFocusedTaskOrHome(false /* checkFilteredStackState */);
+ } else if (event.triggeredFromHomeKey) {
+ // Otherwise, dismiss Recents to Home
+ dismissRecentsToHome(true /* checkFilteredStackState */);
+ } else {
+ // Do nothing
+ }
+ }
+
+ public final void onBusEvent(EnterRecentsWindowAnimationStartedEvent event) {
+ // Try and start the enter animation (or restart it on configuration changed)
+ ReferenceCountedTrigger t = new ReferenceCountedTrigger(this, null, null, null);
+ ViewAnimation.TaskViewEnterContext ctx = new ViewAnimation.TaskViewEnterContext(t);
+ ctx.postAnimationTrigger.increment();
+ if (mSearchWidgetInfo != null) {
+ if (!Constants.DebugFlags.App.DisableSearchBar) {
+ ctx.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+ @Override
+ public void run() {
+ // Start listening for widget package changes if there is one bound
+ if (mAppWidgetHost != null) {
+ mAppWidgetHost.startListening();
+ }
+ }
+ });
+ }
+ }
+ ctx.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+ @Override
+ public void run() {
+ // If we are not launching with alt-tab and fast-toggle is enabled, then start
+ // the dozer now
+ RecentsConfiguration config = Recents.getConfiguration();
+ RecentsActivityLaunchState launchState = config.getLaunchState();
+ if (Constants.DebugFlags.App.EnableFastToggleRecents &&
+ !launchState.launchedWithAltTab) {
+ mIterateTrigger.startDozing();
+ }
+ }
+ });
+ mRecentsView.startEnterRecentsAnimation(ctx);
+ ctx.postAnimationTrigger.decrement();
+ }
+
+ public final void onBusEvent(EnterRecentsWindowLastAnimationFrameEvent event) {
+ mRecentsView.setStackViewVisibility(View.VISIBLE);
+ mRecentsView.getViewTreeObserver().addOnPreDrawListener(this);
+ }
+
+ public final void onBusEvent(CancelEnterRecentsWindowAnimationEvent event) {
+ RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
+ int launchToTaskId = launchState.launchedToTaskId;
+ if (launchToTaskId != -1 &&
+ (event.launchTask == null || launchToTaskId != event.launchTask.key.id)) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ ssp.cancelWindowTransition(launchState.launchedToTaskId);
+ ssp.cancelThumbnailTransition(getTaskId());
+ }
+ }
+
public final void onBusEvent(AppWidgetProviderChangedEvent event) {
refreshSearchWidgetView();
}
public final void onBusEvent(ShowApplicationInfoEvent event) {
// Create a new task stack with the application info details activity
- Intent baseIntent = event.task.key.baseIntent;
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
- Uri.fromParts("package", baseIntent.getComponent().getPackageName(), null));
+ Uri.fromParts("package", event.task.key.getComponent().getPackageName(), null));
intent.setComponent(intent.resolveActivity(getPackageManager()));
TaskStackBuilder.create(this)
.addNextIntentWithParentStack(intent).startActivities(null,
@@ -594,13 +671,14 @@
MetricsLogger.count(this, "overview_app_info", 1);
}
- public final void onBusEvent(DismissTaskEvent event) {
+ public final void onBusEvent(DismissTaskViewEvent event) {
// Remove any stored data from the loader
- RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
+ RecentsTaskLoader loader = Recents.getTaskLoader();
loader.deleteTaskData(event.task, false);
// Remove the task from activity manager
- loader.getSystemServicesProxy().removeTask(event.task.key.id);
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ ssp.removeTask(event.task.key.id);
}
public final void onBusEvent(ResizeTaskEvent event) {
@@ -619,9 +697,24 @@
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_BEHIND);
}
+ public final void onBusEvent(LaunchTaskSucceededEvent event) {
+ MetricsLogger.histogram(this, "overview_task_launch_index", event.taskIndexFromStackFront);
+ }
+
+ public final void onBusEvent(LaunchTaskFailedEvent event) {
+ // Return to Home
+ dismissRecentsToHome(true);
+
+ MetricsLogger.count(this, "overview_task_launch_failed", 1);
+ }
+
+ public final void onBusEvent(ScreenPinningRequestEvent event) {
+ MetricsLogger.count(this, "overview_screen_pinned", 1);
+ }
+
private void refreshSearchWidgetView() {
if (mSearchWidgetInfo != null) {
- SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
+ SystemServicesProxy ssp = Recents.getSystemServices();
int searchWidgetId = ssp.getSearchAppWidgetId(this);
mSearchWidgetHostView = (RecentsAppWidgetHostView) mAppWidgetHost.createView(
this, searchWidgetId, mSearchWidgetInfo);
@@ -636,4 +729,18 @@
mRecentsView.setSearchBar(null);
}
}
+
+ @Override
+ public boolean onPreDraw() {
+ mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this);
+ // We post to make sure that this information is delivered after this traversals is
+ // finished.
+ mRecentsView.post(new Runnable() {
+ @Override
+ public void run() {
+ Recents.getSystemServices().endProlongedAnimations();
+ }
+ });
+ return true;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
index e2e0e918..01ffd2a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
@@ -19,7 +19,6 @@
/**
* The launch state of the RecentsActivity.
*
- * TODO: We will be refactoring this out RecentsConfiguration.
* Current Constraints:
* - needed in onStart() before onNewIntent()
* - needs to be reset when Recents is hidden
@@ -28,8 +27,6 @@
*/
public class RecentsActivityLaunchState {
- public RecentsConfiguration mConfig;
-
public boolean launchedWithAltTab;
public boolean launchedWithNoRecentTasks;
public boolean launchedFromAppWithThumbnail;
@@ -37,14 +34,11 @@
public boolean launchedFromSearchHome;
public boolean launchedReuseTaskStackViews;
public boolean launchedHasConfigurationChanged;
+ public boolean startHidden;
public int launchedToTaskId;
public int launchedNumVisibleTasks;
public int launchedNumVisibleThumbnails;
- RecentsActivityLaunchState(RecentsConfiguration config) {
- mConfig = config;
- }
-
/** Called when the configuration has changed, and we want to reset any configuration specific
* members. */
public void updateOnConfigurationChange() {
@@ -56,7 +50,7 @@
/** Returns whether the status bar scrim should be animated when shown for the first time. */
public boolean shouldAnimateStatusBarScrim() {
- return launchedFromHome;
+ return true;
}
/** Returns whether the status bar scrim should be visible. */
@@ -72,6 +66,44 @@
/** Returns whether the nav bar scrim should be visible. */
public boolean hasNavBarScrim() {
// Only show the scrim if we have recent tasks, and if the nav bar is not transposed
- return !launchedWithNoRecentTasks && mConfig.hasTransposedNavBar;
+ RecentsConfiguration config = Recents.getConfiguration();
+ return !launchedWithNoRecentTasks && !config.hasTransposedNavBar;
+ }
+
+ /**
+ * Returns the task to focus given the current launch state.
+ */
+ public int getInitialFocusTaskIndex(int numTasks) {
+ if (Constants.DebugFlags.App.EnableFastToggleRecents && !launchedWithAltTab) {
+ // If we are fast toggling, then focus the next task depending on when you are on home
+ // or coming in from another app
+ if (launchedFromHome) {
+ return numTasks - 1;
+ } else {
+ return numTasks - 2;
+ }
+ }
+
+ if (launchedWithAltTab && launchedFromAppWithThumbnail) {
+ // If alt-tabbing from another app, focus the next task
+ return numTasks - 2;
+ } else if ((launchedWithAltTab && launchedFromHome) ||
+ (!launchedWithAltTab && launchedFromAppWithThumbnail)) {
+ // If alt-tabbing from home, or launching from an app normally, focus that task
+ return numTasks - 1;
+ } else {
+ // Otherwise, we are launching recents from home normally, focus no tasks so that we
+ // know to return home
+ return -1;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "RecentsActivityLaunchState altTab: " + launchedWithAltTab +
+ ", noTasks: " + launchedWithNoRecentTasks +
+ ", fromHome: " + launchedFromHome +
+ ", fromSearchHome: " + launchedFromSearchHome +
+ ", reuse: " + launchedReuseTaskStackViews;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java
index fc96c11..573db98 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java
@@ -26,6 +26,8 @@
/** Our special app widget host for the Search widget */
public class RecentsAppWidgetHost extends AppWidgetHost {
+ public static final int HOST_ID = 1024;
+
boolean mIsListening;
public RecentsAppWidgetHost(Context context, int hostId) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 37439e7..a73f323 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -22,14 +22,12 @@
import android.provider.Settings;
import com.android.systemui.R;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.model.RecentsTaskLoader;
/**
* Application resources that can be retrieved from the application context and are not specifically
* tied to the current activity.
*/
public class RecentsConfiguration {
- static RecentsConfiguration sInstance;
private static final int LARGE_SCREEN_MIN_DP = 600;
private static final int XLARGE_SCREEN_MIN_DP = 720;
@@ -54,7 +52,7 @@
public static final int SVELTE_DISABLE_LOADING = 3;
// Launch states
- public RecentsActivityLaunchState mLaunchState = new RecentsActivityLaunchState(this);
+ 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
@@ -80,10 +78,10 @@
/** Dev options and global settings */
public boolean lockToAppEnabled;
- /** Private constructor */
- private RecentsConfiguration(Context context, SystemServicesProxy ssp) {
+ public RecentsConfiguration(Context context) {
// Load only resources that can not change after the first load either through developer
// settings or via multi window
+ SystemServicesProxy ssp = Recents.getSystemServices();
Context appContext = context.getApplicationContext();
Resources res = appContext.getResources();
useHardwareLayers = res.getBoolean(R.bool.config_recents_use_hardware_layers);
@@ -112,8 +110,8 @@
void update(Context context, SystemServicesProxy ssp, Rect windowRect) {
// Only update resources that can change after the first load, either through developer
// settings or via multi window
- lockToAppEnabled = ssp.getSystemSetting(context,
- Settings.System.LOCK_TO_APP_ENABLED) != 0;
+ lockToAppEnabled = !ssp.hasFreeformWorkspaceSupport() &&
+ ssp.getSystemSetting(context, Settings.System.LOCK_TO_APP_ENABLED) != 0;
hasDockedTasks = ssp.hasDockedTask();
// Recompute some values based on the given state, since we can not rely on the resource
@@ -123,19 +121,6 @@
hasTransposedSearchBar = isLandscape && isLargeScreen && !isXLargeScreen;
}
- /** Updates the configuration to the current context */
- public static RecentsConfiguration initialize(Context context, SystemServicesProxy ssp) {
- if (sInstance == null) {
- sInstance = new RecentsConfiguration(context, ssp);
- }
- return sInstance;
- }
-
- /** Returns the current recents configuration */
- public static RecentsConfiguration getInstance() {
- return sInstance;
- }
-
/**
* Returns the activity launch state.
* TODO: This will be refactored out of RecentsConfiguration.
@@ -144,8 +129,10 @@
return mLaunchState;
}
- /** Called when the configuration has changed, and we want to reset any configuration specific
- * members. */
+ /**
+ * Called when the configuration has changed, and we want to reset any configuration specific
+ * members.
+ */
public void updateOnConfigurationChange() {
mLaunchState.updateOnConfigurationChange();
}
@@ -165,7 +152,8 @@
} else {
// In portrait, the search bar appears on the top (which already has the inset)
int swInset = getInsetToSmallestWidth(windowBounds.right - windowBounds.left);
- taskStackBounds.set(windowBounds.left + swInset, searchBarBounds.bottom,
+ int top = searchBarBounds.isEmpty() ? topInset : 0;
+ taskStackBounds.set(windowBounds.left + swInset, searchBarBounds.bottom + top,
windowBounds.right - swInset, windowBounds.bottom);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
new file mode 100644
index 0000000..4059543
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -0,0 +1,888 @@
+/*
+ * 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.systemui.recents;
+
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.ITaskStackListener;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.MutableBoolean;
+import android.view.AppTransitionAnimationSpec;
+import android.view.LayoutInflater;
+import android.view.View;
+import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.Prefs;
+import com.android.systemui.R;
+import com.android.systemui.SystemUIApplication;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationStartedEvent;
+import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent;
+import com.android.systemui.recents.events.activity.HideRecentsEvent;
+import com.android.systemui.recents.events.activity.IterateRecentsEvent;
+import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
+import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
+import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
+import com.android.systemui.recents.misc.DozeTrigger;
+import com.android.systemui.recents.misc.ForegroundThread;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.model.RecentsTaskLoadPlan;
+import com.android.systemui.recents.model.RecentsTaskLoader;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.TaskGrouping;
+import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
+import com.android.systemui.recents.views.TaskStackView;
+import com.android.systemui.recents.views.TaskViewHeader;
+import com.android.systemui.recents.views.TaskViewTransform;
+import com.android.systemui.statusbar.phone.PhoneStatusBar;
+
+import java.util.ArrayList;
+
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+
+/**
+ * An implementation of the Recents component for the current user. For secondary users, this can
+ * be called remotely from the system user.
+ */
+public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub
+ implements ActivityOptions.OnAnimationStartedListener, ActivityOptions.OnAnimationFinishedListener {
+
+ private final static String TAG = "RecentsImpl";
+ private final static boolean DEBUG = false;
+
+ // The minimum amount of time between each recents button press that we will handle
+ private final static int MIN_TOGGLE_DELAY_MS = 350;
+ // The duration within which the user releasing the alt tab (from when they pressed alt tab)
+ // that the fast alt-tab animation will run. If the user's alt-tab takes longer than this
+ // duration, then we will toggle recents after this duration.
+ private final static int FAST_ALT_TAB_DELAY_MS = 225;
+
+ public final static String RECENTS_PACKAGE = "com.android.systemui";
+ public final static String RECENTS_ACTIVITY = "com.android.systemui.recents.RecentsActivity";
+
+ /**
+ * An implementation of ITaskStackListener, that allows us to listen for changes to the system
+ * task stacks and update recents accordingly.
+ */
+ class TaskStackListenerImpl extends ITaskStackListener.Stub implements Runnable {
+ Handler mHandler;
+
+ public TaskStackListenerImpl(Handler handler) {
+ mHandler = handler;
+ }
+
+ @Override
+ public void onTaskStackChanged() {
+ // Debounce any task stack changes
+ mHandler.removeCallbacks(this);
+ mHandler.post(this);
+ }
+
+ /** Preloads the next task */
+ public void run() {
+ // TODO: Temporarily skip this if multi stack is enabled
+ /*
+ RecentsConfiguration config = RecentsConfiguration.getInstance();
+ if (config.svelteLevel == RecentsConfiguration.SVELTE_NONE) {
+ RecentsTaskLoader loader = Recents.getTaskLoader();
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ ActivityManager.RunningTaskInfo runningTaskInfo = ssp.getTopMostTask();
+
+ // Load the next task only if we aren't svelte
+ RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
+ loader.preloadTasks(plan, true);
+ RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
+ // This callback is made when a new activity is launched and the old one is paused
+ // so ignore the current activity and try and preload the thumbnail for the
+ // previous one.
+ if (runningTaskInfo != null) {
+ launchOpts.runningTaskId = runningTaskInfo.id;
+ }
+ launchOpts.numVisibleTasks = 2;
+ launchOpts.numVisibleTaskThumbnails = 2;
+ launchOpts.onlyLoadForCache = true;
+ launchOpts.onlyLoadPausedActivities = true;
+ loader.loadTasks(mContext, plan, launchOpts);
+ }
+ */
+ }
+ }
+
+ private static RecentsTaskLoadPlan sInstanceLoadPlan;
+
+ Context mContext;
+ Handler mHandler;
+ TaskStackListenerImpl mTaskStackListener;
+ RecentsAppWidgetHost mAppWidgetHost;
+ boolean mBootCompleted;
+ boolean mStartAnimationTriggered;
+ boolean mCanReuseTaskStackViews = true;
+
+ // Task launching
+ Rect mSearchBarBounds = new Rect();
+ Rect mTaskStackBounds = new Rect();
+ Rect mLastTaskViewBounds = new Rect();
+ TaskViewTransform mTmpTransform = new TaskViewTransform();
+ int mStatusBarHeight;
+ int mNavBarHeight;
+ int mNavBarWidth;
+ int mTaskBarHeight;
+
+ // Header (for transition)
+ TaskViewHeader mHeaderBar;
+ final Object mHeaderBarLock = new Object();
+ TaskStackView mDummyStackView;
+
+ // Variables to keep track of if we need to start recents after binding
+ boolean mTriggeredFromAltTab;
+ long mLastToggleTime;
+ DozeTrigger mFastAltTabTrigger = new DozeTrigger(FAST_ALT_TAB_DELAY_MS, new Runnable() {
+ @Override
+ public void run() {
+ // When this fires, then the user has not released alt-tab for at least
+ // FAST_ALT_TAB_DELAY_MS milliseconds
+ showRecents(mTriggeredFromAltTab);
+ }
+ });
+
+ Bitmap mThumbnailTransitionBitmapCache;
+ Task mThumbnailTransitionBitmapCacheKey;
+
+
+ public RecentsImpl(Context context) {
+ mContext = context;
+ mHandler = new Handler();
+ mAppWidgetHost = new RecentsAppWidgetHost(mContext, RecentsAppWidgetHost.HOST_ID);
+ Resources res = mContext.getResources();
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+
+ // Initialize the static foreground thread
+ ForegroundThread.get();
+
+ // Register the task stack listener
+ mTaskStackListener = new TaskStackListenerImpl(mHandler);
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ ssp.registerTaskStackListener(mTaskStackListener);
+
+ // Initialize the static configuration resources
+ mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
+ mNavBarHeight = res.getDimensionPixelSize(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_bar_height);
+ mDummyStackView = new TaskStackView(mContext, new TaskStack());
+ mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header,
+ null, false);
+ reloadHeaderBarLayout(true /* tryAndBindSearchWidget */);
+
+ // 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.
+ RecentsTaskLoader loader = Recents.getTaskLoader();
+ RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
+ loader.preloadTasks(plan, true /* isTopTaskHome */);
+ RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
+ launchOpts.numVisibleTasks = loader.getApplicationIconCacheSize();
+ launchOpts.numVisibleTaskThumbnails = loader.getThumbnailCacheSize();
+ launchOpts.onlyLoadForCache = true;
+ loader.loadTasks(mContext, plan, launchOpts);
+ }
+
+ public void onBootCompleted() {
+ mBootCompleted = true;
+ reloadHeaderBarLayout(true /* tryAndBindSearchWidget */);
+ }
+
+ @Override
+ public void onConfigurationChanged() {
+ // Don't reuse task stack views if the configuration changes
+ mCanReuseTaskStackViews = false;
+ Recents.getConfiguration().updateOnConfigurationChange();
+ }
+
+ /**
+ * This is only called from the system user's Recents. Secondary users will instead proxy their
+ * visibility change events through to the system user via
+ * {@link Recents#onBusEvent(RecentsVisibilityChangedEvent)}.
+ */
+ public void onVisibilityChanged(Context context, boolean visible) {
+ SystemUIApplication app = (SystemUIApplication) context;
+ PhoneStatusBar statusBar = app.getComponent(PhoneStatusBar.class);
+ if (statusBar != null) {
+ statusBar.updateRecentsVisibility(visible);
+ }
+ }
+
+ /**
+ * This is only called from the system user's Recents. Secondary users will instead proxy their
+ * visibility change events through to the system user via
+ * {@link Recents#onBusEvent(ScreenPinningRequestEvent)}.
+ */
+ public void onStartScreenPinning(Context context) {
+ SystemUIApplication app = (SystemUIApplication) context;
+ PhoneStatusBar statusBar = app.getComponent(PhoneStatusBar.class);
+ if (statusBar != null) {
+ statusBar.showScreenPinningRequest(false);
+ }
+ }
+
+ @Override
+ public void showRecents(boolean triggeredFromAltTab) {
+ mTriggeredFromAltTab = triggeredFromAltTab;
+ if (mFastAltTabTrigger.hasTriggered()) {
+ // We are calling this from the doze trigger, so just fall through to show Recents
+ mFastAltTabTrigger.resetTrigger();
+ } else if (mFastAltTabTrigger.isDozing()) {
+ // We are dozing but haven't yet triggered, ignore this if this is not another alt-tab,
+ // otherwise, this is an additional tab (alt-tab*), which means that we should trigger
+ // immediately (fall through and disable the pending trigger)
+ // TODO: This is tricky, we need to handle the tab key, but Recents has not yet started
+ // so we may actually additional signal to handle multiple quick tab cases. The
+ // severity of this is inversely proportional to the FAST_ALT_TAB_DELAY_MS
+ // duration though
+ if (!triggeredFromAltTab) {
+ return;
+ }
+ mFastAltTabTrigger.stopDozing();
+ } else {
+ // Otherwise, the doze trigger is not running, and if this is an alt tab, we should
+ // start the trigger and then wait for the hide (or for it to elapse)
+ if (triggeredFromAltTab) {
+ mFastAltTabTrigger.startDozing();
+ return;
+ }
+ }
+
+ try {
+ // Check if the top task is in the home stack, and start the recents activity
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ ActivityManager.RunningTaskInfo topTask = ssp.getTopMostTask();
+ MutableBoolean isTopTaskHome = new MutableBoolean(true);
+ if (topTask == null || !ssp.isRecentsTopMost(topTask, isTopTaskHome)) {
+ startRecentsActivity(topTask, isTopTaskHome.value);
+ }
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "Failed to launch RecentsActivity", e);
+ }
+ }
+
+ @Override
+ public void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
+ if (mBootCompleted) {
+ if (triggeredFromAltTab && mFastAltTabTrigger.isDozing()) {
+ // The user has released alt-tab before the trigger has run, so just show the next
+ // task immediately
+ showNextTask();
+
+ // Cancel the fast alt-tab trigger
+ mFastAltTabTrigger.stopDozing();
+ mFastAltTabTrigger.resetTrigger();
+ return;
+ }
+
+ // Defer to the activity to handle hiding recents, if it handles it, then it must still
+ // be visible
+ EventBus.getDefault().post(new HideRecentsEvent(triggeredFromAltTab,
+ triggeredFromHomeKey));
+ }
+ }
+
+ @Override
+ public void toggleRecents() {
+ // Skip this toggle if we are already waiting to trigger recents via alt-tab
+ if (mFastAltTabTrigger.isDozing()) {
+ return;
+ }
+
+ mTriggeredFromAltTab = false;
+
+ try {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ ActivityManager.RunningTaskInfo topTask = ssp.getTopMostTask();
+ MutableBoolean isTopTaskHome = new MutableBoolean(true);
+ if (topTask != null && ssp.isRecentsTopMost(topTask, isTopTaskHome)) {
+ RecentsConfiguration config = Recents.getConfiguration();
+ RecentsActivityLaunchState launchState = config.getLaunchState();
+ if (Constants.DebugFlags.App.EnableFastToggleRecents &&
+ !launchState.launchedWithAltTab) {
+ // Notify recents to move onto the next task
+ EventBus.getDefault().post(new IterateRecentsEvent());
+ } else {
+ // If the user has toggled it too quickly, then just eat up the event here (it's
+ // better than showing a janky screenshot).
+ // NOTE: Ideally, the screenshot mechanism would take the window transform into
+ // account
+ if ((SystemClock.elapsedRealtime() - mLastToggleTime) < MIN_TOGGLE_DELAY_MS) {
+ return;
+ }
+
+ EventBus.getDefault().post(new ToggleRecentsEvent());
+ mLastToggleTime = SystemClock.elapsedRealtime();
+ }
+ return;
+ } else {
+ // If the user has toggled it too quickly, then just eat up the event here (it's
+ // better than showing a janky screenshot).
+ // NOTE: Ideally, the screenshot mechanism would take the window transform into
+ // account
+ if ((SystemClock.elapsedRealtime() - mLastToggleTime) < MIN_TOGGLE_DELAY_MS) {
+ return;
+ }
+
+ // Otherwise, start the recents activity
+ startRecentsActivity(topTask, isTopTaskHome.value);
+ mLastToggleTime = SystemClock.elapsedRealtime();
+ }
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "Failed to launch RecentsActivity", e);
+ }
+ }
+
+ @Override
+ public void preloadRecents() {
+ // Preload only the raw task list into a new load plan (which will be consumed by the
+ // RecentsActivity) only if there is a task to animate to.
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ ActivityManager.RunningTaskInfo topTask = ssp.getTopMostTask();
+ MutableBoolean topTaskHome = new MutableBoolean(true);
+ RecentsTaskLoader loader = Recents.getTaskLoader();
+ sInstanceLoadPlan = loader.createLoadPlan(mContext);
+ if (topTask != null && !ssp.isRecentsTopMost(topTask, topTaskHome)) {
+ sInstanceLoadPlan.preloadRawTasks(topTaskHome.value);
+ loader.preloadTasks(sInstanceLoadPlan, 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);
+ }
+ }
+ }
+
+ @Override
+ public void cancelPreloadingRecents() {
+ // Do nothing
+ }
+
+ /**
+ * Transitions to the next recent task in the stack.
+ */
+ public void showNextTask() {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ RecentsTaskLoader loader = Recents.getTaskLoader();
+ RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
+ loader.preloadTasks(plan, true /* isTopTaskHome */);
+ TaskStack focusedStack = plan.getTaskStack();
+
+ // Return early if there are no tasks in the focused stack
+ if (focusedStack == null || focusedStack.getTaskCount() == 0) return;
+
+ ActivityManager.RunningTaskInfo runningTask = ssp.getTopMostTask();
+ // Return early if there is no running task
+ if (runningTask == null) return;
+ // Return early if the running task is in the home stack (optimization)
+ if (SystemServicesProxy.isHomeStack(runningTask.stackId)) return;
+
+ // Find the task in the recents list
+ ArrayList<Task> tasks = focusedStack.getTasks();
+ Task toTask = null;
+ ActivityOptions launchOpts = null;
+ int taskCount = tasks.size();
+ for (int i = taskCount - 1; i >= 1; i--) {
+ Task task = tasks.get(i);
+ if (task.key.id == runningTask.id) {
+ toTask = tasks.get(i - 1);
+ launchOpts = ActivityOptions.makeCustomAnimation(mContext,
+ R.anim.recents_launch_prev_affiliated_task_target,
+ R.anim.recents_launch_prev_affiliated_task_source);
+ break;
+ }
+ }
+
+ // Return early if there is no next task
+ if (toTask == null) {
+ ssp.startInPlaceAnimationOnFrontMostApplication(
+ ActivityOptions.makeCustomInPlaceAnimation(mContext,
+ R.anim.recents_launch_prev_affiliated_task_bounce));
+ return;
+ }
+
+ // Launch the task
+ ssp.startActivityFromRecents(mContext, toTask.key.id, toTask.activityLabel, launchOpts);
+ }
+
+ /**
+ * Transitions to the next affiliated task.
+ */
+ public void showRelativeAffiliatedTask(boolean showNextTask) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ RecentsTaskLoader loader = Recents.getTaskLoader();
+ RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
+ loader.preloadTasks(plan, true /* isTopTaskHome */);
+ TaskStack focusedStack = plan.getTaskStack();
+
+ // Return early if there are no tasks in the focused stack
+ if (focusedStack == null || focusedStack.getTaskCount() == 0) return;
+
+ ActivityManager.RunningTaskInfo runningTask = ssp.getTopMostTask();
+ // Return early if there is no running task (can't determine affiliated tasks in this case)
+ if (runningTask == null) return;
+ // Return early if the running task is in the home stack (optimization)
+ if (SystemServicesProxy.isHomeStack(runningTask.stackId)) return;
+
+ // Find the task in the recents list
+ ArrayList<Task> tasks = focusedStack.getTasks();
+ Task toTask = null;
+ ActivityOptions launchOpts = null;
+ int taskCount = tasks.size();
+ int numAffiliatedTasks = 0;
+ for (int i = 0; i < taskCount; i++) {
+ Task task = tasks.get(i);
+ if (task.key.id == runningTask.id) {
+ TaskGrouping group = task.group;
+ Task.TaskKey toTaskKey;
+ if (showNextTask) {
+ toTaskKey = group.getNextTaskInGroup(task);
+ launchOpts = ActivityOptions.makeCustomAnimation(mContext,
+ R.anim.recents_launch_next_affiliated_task_target,
+ R.anim.recents_launch_next_affiliated_task_source);
+ } else {
+ toTaskKey = group.getPrevTaskInGroup(task);
+ launchOpts = ActivityOptions.makeCustomAnimation(mContext,
+ R.anim.recents_launch_prev_affiliated_task_target,
+ R.anim.recents_launch_prev_affiliated_task_source);
+ }
+ if (toTaskKey != null) {
+ toTask = focusedStack.findTaskWithId(toTaskKey.id);
+ }
+ numAffiliatedTasks = group.getTaskCount();
+ break;
+ }
+ }
+
+ // Return early if there is no next task
+ if (toTask == null) {
+ if (numAffiliatedTasks > 1) {
+ if (showNextTask) {
+ ssp.startInPlaceAnimationOnFrontMostApplication(
+ ActivityOptions.makeCustomInPlaceAnimation(mContext,
+ R.anim.recents_launch_next_affiliated_task_bounce));
+ } else {
+ ssp.startInPlaceAnimationOnFrontMostApplication(
+ ActivityOptions.makeCustomInPlaceAnimation(mContext,
+ R.anim.recents_launch_prev_affiliated_task_bounce));
+ }
+ }
+ return;
+ }
+
+ // Keep track of actually launched affiliated tasks
+ MetricsLogger.count(mContext, "overview_affiliated_task_launch", 1);
+
+ // Launch the task
+ ssp.startActivityFromRecents(mContext, toTask.key.id, toTask.activityLabel, launchOpts);
+ }
+
+ public void showNextAffiliatedTask() {
+ // Keep track of when the affiliated task is triggered
+ MetricsLogger.count(mContext, "overview_affiliated_task_next", 1);
+ showRelativeAffiliatedTask(true);
+ }
+
+ public void showPrevAffiliatedTask() {
+ // Keep track of when the affiliated task is triggered
+ MetricsLogger.count(mContext, "overview_affiliated_task_prev", 1);
+ showRelativeAffiliatedTask(false);
+ }
+
+ public void dockTopTask() {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ ActivityManager.RunningTaskInfo topTask = ssp.getTopMostTask();
+ if (topTask != null && !SystemServicesProxy.isHomeStack(topTask.stackId)) {
+ ssp.startTaskInDockedMode(topTask.id,
+ ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT);
+ showRecents(false /* triggeredFromAltTab */);
+ }
+ }
+
+ /**
+ * Returns the preloaded load plan and invalidates it.
+ */
+ public static RecentsTaskLoadPlan consumeInstanceLoadPlan() {
+ RecentsTaskLoadPlan plan = sInstanceLoadPlan;
+ sInstanceLoadPlan = null;
+ return plan;
+ }
+
+ /**
+ * Prepares the header bar layout for the next transition, if the task view bounds has changed
+ * since the last call, it will attempt to re-measure and layout the header bar to the new size.
+ *
+ * @param tryAndBindSearchWidget if set, will attempt to fetch and bind the search widget if one
+ * is not already bound (can be expensive)
+ */
+ private void reloadHeaderBarLayout(boolean tryAndBindSearchWidget) {
+ RecentsConfiguration config = Recents.getConfiguration();
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ Rect windowRect = ssp.getWindowRect();
+
+ // Update the configuration for the current state
+ config.update(mContext, ssp, ssp.getWindowRect());
+
+ if (!Constants.DebugFlags.App.DisableSearchBar && tryAndBindSearchWidget) {
+ // Try and pre-emptively bind the search widget on startup to ensure that we
+ // have the right thumbnail bounds to animate to.
+ // Note: We have to reload the widget id before we get the task stack bounds below
+ if (ssp.getOrBindSearchAppWidget(mContext, mAppWidgetHost) != null) {
+ config.getSearchBarBounds(windowRect, mStatusBarHeight, mSearchBarBounds);
+ }
+ }
+ Rect systemInsets = new Rect(0, mStatusBarHeight,
+ (config.hasTransposedNavBar ? mNavBarWidth : 0),
+ (config.hasTransposedNavBar ? 0 : mNavBarHeight));
+ config.getTaskStackBounds(windowRect, systemInsets.top, systemInsets.right,
+ mSearchBarBounds, mTaskStackBounds);
+
+ // Rebind the header bar and draw it for the transition
+ TaskStackLayoutAlgorithm algo = mDummyStackView.getStackAlgorithm();
+ Rect taskStackBounds = new Rect(mTaskStackBounds);
+ algo.setSystemInsets(systemInsets);
+ algo.initialize(taskStackBounds);
+ Rect taskViewBounds = algo.getUntransformedTaskViewBounds();
+ if (!taskViewBounds.equals(mLastTaskViewBounds)) {
+ mLastTaskViewBounds.set(taskViewBounds);
+
+ int taskViewWidth = taskViewBounds.width();
+ synchronized (mHeaderBarLock) {
+ mHeaderBar.measure(
+ View.MeasureSpec.makeMeasureSpec(taskViewWidth, View.MeasureSpec.EXACTLY),
+ View.MeasureSpec.makeMeasureSpec(mTaskBarHeight, View.MeasureSpec.EXACTLY));
+ mHeaderBar.layout(0, 0, taskViewWidth, mTaskBarHeight);
+ }
+ }
+ }
+
+ /**
+ * Preloads the icon of a task.
+ */
+ private void preloadIcon(ActivityManager.RunningTaskInfo task) {
+ // Ensure that we load the running task's icon
+ RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
+ launchOpts.runningTaskId = task.id;
+ launchOpts.loadThumbnails = false;
+ launchOpts.onlyLoadForCache = true;
+ Recents.getTaskLoader().loadTasks(mContext, sInstanceLoadPlan, launchOpts);
+ }
+
+ /**
+ * 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
+ reloadHeaderBarLayout(false /* tryAndBindSearchWidget */);
+
+ // Update the destination rect
+ mDummyStackView.updateLayoutForStack(stack);
+ final Task toTask = new Task();
+ final TaskViewTransform toTransform = getThumbnailTransitionTransform(stack, stackView,
+ topTask.id, toTask);
+ ForegroundThread.getHandler().post(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.
+ */
+ private ActivityOptions getUnknownTransitionActivityOptions() {
+ return ActivityOptions.makeCustomAnimation(mContext,
+ R.anim.recents_from_unknown_enter,
+ R.anim.recents_from_unknown_exit,
+ mHandler, this);
+ }
+
+ /**
+ * Creates the activity options for a home->recents transition.
+ */
+ private ActivityOptions getHomeTransitionActivityOptions(boolean fromSearchHome) {
+ if (fromSearchHome) {
+ return ActivityOptions.makeCustomAnimation(mContext,
+ R.anim.recents_from_search_launcher_enter,
+ R.anim.recents_from_search_launcher_exit,
+ mHandler, this);
+ }
+ return ActivityOptions.makeCustomAnimation(mContext,
+ R.anim.recents_from_launcher_enter,
+ R.anim.recents_from_launcher_exit,
+ mHandler, this);
+ }
+
+ /**
+ * Creates the activity options for an app->recents transition.
+ */
+ private ActivityOptions getThumbnailTransitionActivityOptions(
+ ActivityManager.RunningTaskInfo topTask, TaskStack stack, TaskStackView stackView) {
+ if (topTask.stackId == FREEFORM_WORKSPACE_STACK_ID) {
+ ArrayList<AppTransitionAnimationSpec> specs = new ArrayList<>();
+ stackView.getScroller().setStackScrollToInitialState();
+ ArrayList<Task> tasks = stack.getTasks();
+ for (int i = tasks.size() - 1; i >= 0; i--) {
+ Task task = tasks.get(i);
+ if (SystemServicesProxy.isFreeformStack(task.key.stackId)) {
+ mTmpTransform = stackView.getStackAlgorithm().getStackTransform(task,
+ stackView.getScroller().getStackScroll(), mTmpTransform, null);
+ Rect toTaskRect = new Rect();
+ mTmpTransform.rect.round(toTaskRect);
+ Bitmap thumbnail = getThumbnailBitmap(topTask, task, mTmpTransform);
+ specs.add(new AppTransitionAnimationSpec(task.key.id, thumbnail, toTaskRect));
+ }
+ }
+ AppTransitionAnimationSpec[] specsArray = new AppTransitionAnimationSpec[specs.size()];
+ specs.toArray(specsArray);
+ return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
+ specsArray, mHandler, this, this);
+ } else {
+ // Update the destination rect
+ Task toTask = new Task();
+ TaskViewTransform toTransform = getThumbnailTransitionTransform(stack, stackView,
+ topTask.id, toTask);
+ RectF toTaskRect = toTransform.rect;
+ Bitmap thumbnail = getThumbnailBitmap(topTask, toTask, toTransform);
+ if (thumbnail != null) {
+ return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
+ thumbnail, (int) toTaskRect.left, (int) toTaskRect.top,
+ (int) toTaskRect.width(), (int) toTaskRect.height(), mHandler, this);
+ }
+ // If both the screenshot and thumbnail fails, then just fall back to the default transition
+ return getUnknownTransitionActivityOptions();
+ }
+ }
+
+ 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.
+ */
+ private TaskViewTransform getThumbnailTransitionTransform(TaskStack stack,
+ TaskStackView stackView, int runningTaskId, Task runningTaskOut) {
+ // Find the running task in the TaskStack
+ Task task = null;
+ ArrayList<Task> tasks = stack.getTasks();
+ if (runningTaskId != -1) {
+ // Otherwise, try and find the task with the
+ int taskCount = tasks.size();
+ for (int i = taskCount - 1; i >= 0; i--) {
+ Task t = tasks.get(i);
+ if (t.key.id == runningTaskId) {
+ task = t;
+ runningTaskOut.copyFrom(t);
+ break;
+ }
+ }
+ }
+ if (task == null) {
+ // If no task is specified or we can not find the task just use the front most one
+ task = tasks.get(tasks.size() - 1);
+ runningTaskOut.copyFrom(task);
+ }
+
+ // Get the transform for the running task
+ stackView.getScroller().setStackScrollToInitialState();
+ mTmpTransform = stackView.getStackAlgorithm().getStackTransform(task,
+ stackView.getScroller().getStackScroll(), mTmpTransform, null);
+ return mTmpTransform;
+ }
+
+ /**
+ * Draws the header of a task used for the window animation into a bitmap.
+ */
+ private Bitmap drawThumbnailTransitionBitmap(Task toTask, TaskViewTransform toTransform) {
+ if (toTransform != null && toTask.key != null) {
+ Bitmap thumbnail;
+ synchronized (mHeaderBarLock) {
+ int toHeaderWidth = (int) (mHeaderBar.getMeasuredWidth() * toTransform.scale);
+ int toHeaderHeight = (int) (mHeaderBar.getMeasuredHeight() * toTransform.scale);
+ thumbnail = Bitmap.createBitmap(toHeaderWidth, toHeaderHeight,
+ Bitmap.Config.ARGB_8888);
+ if (Constants.DebugFlags.App.EnableTransitionThumbnailDebugMode) {
+ thumbnail.eraseColor(0xFFff0000);
+ } else {
+ Canvas c = new Canvas(thumbnail);
+ c.scale(toTransform.scale, toTransform.scale);
+ mHeaderBar.rebindToTask(toTask);
+ mHeaderBar.draw(c);
+ c.setBitmap(null);
+ }
+ }
+ return thumbnail.createAshmemBitmap();
+ }
+ return null;
+ }
+
+ /**
+ * Shows the recents activity
+ */
+ private void startRecentsActivity(ActivityManager.RunningTaskInfo topTask,
+ boolean isTopTaskHome) {
+ RecentsTaskLoader loader = Recents.getTaskLoader();
+
+ // Update the header bar if necessary
+ reloadHeaderBarLayout(false /* tryAndBindSearchWidget */);
+
+ if (sInstanceLoadPlan == null) {
+ // Create a new load plan if onPreloadRecents() was never triggered
+ sInstanceLoadPlan = loader.createLoadPlan(mContext);
+ }
+
+ if (!sInstanceLoadPlan.hasTasks()) {
+ loader.preloadTasks(sInstanceLoadPlan, isTopTaskHome);
+ }
+ TaskStack stack = sInstanceLoadPlan.getTaskStack();
+
+ // Prepare the dummy stack for the transition
+ mDummyStackView.updateLayoutForStack(stack);
+ TaskStackLayoutAlgorithm.VisibilityReport stackVr =
+ mDummyStackView.computeStackVisibilityReport();
+ boolean hasRecentTasks = stack.getTaskCount() > 0;
+ boolean useThumbnailTransition = (topTask != null) && !isTopTaskHome && hasRecentTasks;
+
+ if (useThumbnailTransition) {
+ // Try starting with a thumbnail transition
+ ActivityOptions opts = getThumbnailTransitionActivityOptions(topTask, stack,
+ mDummyStackView);
+ if (opts != null) {
+ startRecentsActivity(topTask, opts, false /* fromHome */,
+ false /* fromSearchHome */, true /* fromThumbnail */, stackVr);
+ } else {
+ // Fall through below to the non-thumbnail transition
+ useThumbnailTransition = false;
+ }
+ }
+
+ if (!useThumbnailTransition) {
+ // If there is no thumbnail transition, but is launching from home into recents, then
+ // use a quick home transition and do the animation from home
+ if (!Constants.DebugFlags.App.DisableSearchBar && hasRecentTasks) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ String homeActivityPackage = ssp.getHomeActivityPackageName();
+ String searchWidgetPackage = Prefs.getString(mContext,
+ Prefs.Key.SEARCH_APP_WIDGET_PACKAGE, null);
+
+ // Determine whether we are coming from a search owned home activity
+ boolean fromSearchHome = (homeActivityPackage != null) &&
+ homeActivityPackage.equals(searchWidgetPackage);
+ ActivityOptions opts = getHomeTransitionActivityOptions(fromSearchHome);
+ startRecentsActivity(topTask, opts, true /* fromHome */, fromSearchHome,
+ false /* fromThumbnail */, stackVr);
+ } else {
+ // Otherwise we do the normal fade from an unknown source
+ ActivityOptions opts = getUnknownTransitionActivityOptions();
+ startRecentsActivity(topTask, opts, true /* fromHome */,
+ false /* fromSearchHome */, false /* fromThumbnail */, stackVr);
+ }
+ }
+ mLastToggleTime = SystemClock.elapsedRealtime();
+ }
+
+ /**
+ * Starts the recents activity.
+ */
+ private void startRecentsActivity(ActivityManager.RunningTaskInfo topTask,
+ ActivityOptions opts, boolean fromHome, boolean fromSearchHome, boolean fromThumbnail,
+ TaskStackLayoutAlgorithm.VisibilityReport vr) {
+ mStartAnimationTriggered = false;
+
+ // Update the configuration based on the launch options
+ RecentsConfiguration config = Recents.getConfiguration();
+ RecentsActivityLaunchState launchState = config.getLaunchState();
+ launchState.launchedFromHome = fromSearchHome || fromHome;
+ launchState.launchedFromSearchHome = fromSearchHome;
+ launchState.launchedFromAppWithThumbnail = fromThumbnail;
+ 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.startHidden = topTask != null && topTask.stackId == FREEFORM_WORKSPACE_STACK_ID;
+
+ Intent intent = new Intent();
+ intent.setClassName(RECENTS_PACKAGE, RECENTS_ACTIVITY);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+ | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
+ if (opts != null) {
+ mContext.startActivityAsUser(intent, opts.toBundle(), UserHandle.CURRENT);
+ } else {
+ mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+ }
+ mCanReuseTaskStackViews = true;
+ }
+
+ /**** OnAnimationStartedListener Implementation ****/
+
+ @Override
+ public void onAnimationStarted() {
+ // Notify recents to start the enter animation
+ if (!mStartAnimationTriggered) {
+ mStartAnimationTriggered = true;
+ EventBus.getDefault().post(new EnterRecentsWindowAnimationStartedEvent());
+ }
+ }
+
+ @Override
+ public void onAnimationFinished() {
+ EventBus.getDefault().post(new EnterRecentsWindowLastAnimationFrameEvent());
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
index 8827065..d415845 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
@@ -30,10 +30,12 @@
import android.widget.Toast;
import com.android.systemui.R;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.views.RecentsView;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+
/**
* A helper for the dialogs that show when task debugging is on.
*/
@@ -77,7 +79,6 @@
private View mResizeTaskDialogContent;
private RecentsActivity mRecentsActivity;
private RecentsView mRecentsView;
- private SystemServicesProxy mSsp;
private Rect[] mBounds = {new Rect(), new Rect(), new Rect(), new Rect()};
private Task[] mTasks = {null, null, null, null};
@@ -90,7 +91,6 @@
public RecentsResizeTaskDialog(FragmentManager mgr, RecentsActivity activity) {
mFragmentManager = mgr;
mRecentsActivity = activity;
- mSsp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
}
/** Shows the resize-task dialog. */
@@ -141,7 +141,8 @@
/** Helper function to place window(s) on the display according to an arrangement request. */
private void placeTasks(int arrangement) {
- Rect rect = mSsp.getDisplayRect();
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ Rect rect = ssp.getDisplayRect();
for (int i = 0; i < mBounds.length; ++i) {
mBounds[i].set(rect);
if (i != 0) {
@@ -233,19 +234,11 @@
dismissAllowingStateLoss();
mRecentsActivity.dismissRecentsToHomeWithoutTransitionAnimation();
- // In debug mode, we force all task to be resizeable regardless of the
- // current app configuration.
- for (int i = additionalTasks; i >= 0; --i) {
- if (mTasks[i] != null) {
- mSsp.setTaskResizeable(mTasks[i].key.id);
- }
- }
-
// Show tasks as they might not be currently visible - beginning with the oldest so that
// the focus ends on the selected one.
for (int i = additionalTasks; i >= 0; --i) {
if (mTasks[i] != null) {
- mRecentsView.launchTask(mTasks[i], mBounds[i]);
+ mRecentsView.launchTask(mTasks[i], mBounds[i], FREEFORM_WORKSPACE_STACK_ID);
}
}
}
@@ -273,11 +266,11 @@
// Dismiss the dialog before trying to launch the task
dismissAllowingStateLoss();
- if (mTasks[0].key.stackId != ActivityManager.DOCKED_STACK_ID) {
+ if (mTasks[0].key.stackId != DOCKED_STACK_ID) {
int taskId = mTasks[0].key.id;
- mSsp.setTaskResizeable(taskId);
- mSsp.dockTask(taskId, createMode);
- mRecentsView.launchTask(mTasks[0], null);
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ ssp.startTaskInDockedMode(taskId, createMode);
+ mRecentsView.launchTask(mTasks[0], null, DOCKED_STACK_ID);
} else {
Toast.makeText(getContext(), "Already docked", Toast.LENGTH_SHORT);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java
new file mode 100644
index 0000000..fb21500
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java
@@ -0,0 +1,73 @@
+/*
+ * 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.systemui.recents;
+
+import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.SparseArray;
+
+/**
+ * An implementation of the system user's Recents interface to be called remotely by secondary
+ * users.
+ */
+public class RecentsSystemUser extends IRecentsSystemUserCallbacks.Stub {
+
+ private static final String TAG = "RecentsSystemUser";
+
+ private Context mContext;
+ private RecentsImpl mImpl;
+ private final SparseArray<IRecentsNonSystemUserCallbacks> mNonSystemUserRecents =
+ new SparseArray<>();
+
+ public RecentsSystemUser(Context context, RecentsImpl impl) {
+ mContext = context;
+ mImpl = impl;
+ }
+
+ @Override
+ public void registerNonSystemUserCallbacks(final IBinder nonSystemUserCallbacks, int userId) {
+ try {
+ final IRecentsNonSystemUserCallbacks callback =
+ IRecentsNonSystemUserCallbacks.Stub.asInterface(nonSystemUserCallbacks);
+ nonSystemUserCallbacks.linkToDeath(new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ mNonSystemUserRecents.removeAt(mNonSystemUserRecents.indexOfValue(callback));
+ }
+ }, 0);
+ mNonSystemUserRecents.put(userId, callback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to register NonSystemUserCallbacks", e);
+ }
+ }
+
+ public IRecentsNonSystemUserCallbacks getNonSystemUserRecentsForUser(int userId) {
+ return mNonSystemUserRecents.get(userId);
+ }
+
+ @Override
+ public void updateRecentsVisibility(boolean visible) {
+ mImpl.onVisibilityChanged(mContext, visible);
+ }
+
+ @Override
+ public void startScreenPinning() {
+ mImpl.onStartScreenPinning(mContext);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUserService.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUserService.java
new file mode 100644
index 0000000..39d0d59
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUserService.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Log;
+import com.android.systemui.SystemUIApplication;
+
+/**
+ * A strictly system-user service that is started by the secondary user's Recents (with a limited
+ * lifespan), to get the interface that the secondary user's Recents can call through to the system
+ * user's Recents.
+ */
+public class RecentsSystemUserService extends Service {
+
+ private static final String TAG = "RecentsSystemUserService";
+ private static final boolean DEBUG = false;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ SystemUIApplication app = (SystemUIApplication) getApplication();
+ Recents recents = app.getComponent(Recents.class);
+ if (DEBUG) {
+ Log.d(TAG, "onBind: " + recents);
+ }
+ if (recents != null) {
+ return recents.getSystemUserCallbacks();
+ }
+ return null;
+ }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsUserEventProxyReceiver.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsUserEventProxyReceiver.java
deleted file mode 100644
index 5eefbc7..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsUserEventProxyReceiver.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-
-/**
- * A proxy for Recents events which happens strictly for non-owner users.
- */
-public class RecentsUserEventProxyReceiver extends BroadcastReceiver {
- final public static String ACTION_PROXY_SHOW_RECENTS_TO_USER =
- "com.android.systemui.recents.action.SHOW_RECENTS_FOR_USER";
- final public static String ACTION_PROXY_HIDE_RECENTS_TO_USER =
- "com.android.systemui.recents.action.HIDE_RECENTS_FOR_USER";
- final public static String ACTION_PROXY_TOGGLE_RECENTS_TO_USER =
- "com.android.systemui.recents.action.TOGGLE_RECENTS_FOR_USER";
- final public static String ACTION_PROXY_PRELOAD_RECENTS_TO_USER =
- "com.android.systemui.recents.action.PRELOAD_RECENTS_FOR_USER";
- final public static String ACTION_PROXY_CONFIG_CHANGE_TO_USER =
- "com.android.systemui.recents.action.CONFIG_CHANGED_FOR_USER";
-
- @Override
- public void onReceive(Context context, Intent intent) {
- Recents recents = Recents.getInstanceAndStartIfNeeded(context);
- switch (intent.getAction()) {
- case ACTION_PROXY_SHOW_RECENTS_TO_USER: {
- boolean triggeredFromAltTab = intent.getBooleanExtra(
- Recents.EXTRA_TRIGGERED_FROM_ALT_TAB, false);
- recents.showRecentsInternal(triggeredFromAltTab);
- break;
- }
- case ACTION_PROXY_HIDE_RECENTS_TO_USER: {
- boolean triggeredFromAltTab = intent.getBooleanExtra(
- Recents.EXTRA_TRIGGERED_FROM_ALT_TAB, false);
- boolean triggeredFromHome = intent.getBooleanExtra(
- Recents.EXTRA_TRIGGERED_FROM_HOME_KEY, false);
- recents.hideRecentsInternal(triggeredFromAltTab, triggeredFromHome);
- break;
- }
- case ACTION_PROXY_TOGGLE_RECENTS_TO_USER:
- recents.toggleRecentsInternal();
- break;
- case ACTION_PROXY_PRELOAD_RECENTS_TO_USER:
- recents.preloadRecentsInternal();
- break;
- case ACTION_PROXY_CONFIG_CHANGE_TO_USER:
- recents.configurationChanged();
- break;
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 231843e..10075bc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -214,11 +214,8 @@
.setVisibility(View.INVISIBLE);
}
- final int description = mAccessibilityService.isEnabled()
- ? R.string.screen_pinning_description_accessible
- : R.string.screen_pinning_description;
((TextView) mLayout.findViewById(R.id.screen_pinning_description))
- .setText(description);
+ .setText(R.string.screen_pinning_description);
final int backBgVisibility =
mAccessibilityService.isEnabled() ? View.INVISIBLE : View.VISIBLE;
mLayout.findViewById(R.id.screen_pinning_back_bg).setVisibility(backBgVisibility);
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 ef543d0..b091f05 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
@@ -200,7 +200,8 @@
*/
public class EventBus extends BroadcastReceiver {
- public static final String TAG = "EventBus";
+ private static final String TAG = "EventBus";
+ private static final boolean DEBUG_TRACE_ALL = false;
/**
* An event super class that allows us to track internal event state across subscriber
@@ -208,7 +209,7 @@
*
* Events should not be edited by subscribers.
*/
- public static class Event {
+ public static class Event implements Cloneable {
// Indicates that this event's dispatch should be traced and logged to logcat
boolean trace;
// Indicates that this event must be posted on the EventBus's looper thread before invocation
@@ -218,6 +219,14 @@
// Only accessible from derived events
protected Event() {}
+
+ @Override
+ protected Object clone() throws CloneNotSupportedException {
+ Event evt = (Event) super.clone();
+ // When cloning an event, reset the cancelled-dispatch state
+ evt.cancelled = false;
+ return evt;
+ }
}
/**
@@ -269,9 +278,6 @@
// The default priority of all subscribers
private static final int DEFAULT_SUBSCRIBER_PRIORITY = 1;
- // Used for debugging everything
- private static final boolean DEBUG_TRACE_ALL = false;
-
// Orders the handlers by priority and registration time
private static final Comparator<EventHandler> EVENT_HANDLER_COMPARATOR = new Comparator<EventHandler>() {
@Override
@@ -543,7 +549,7 @@
InvocationTargetException|
InstantiationException|
IllegalAccessException e) {
- Log.e(TAG, "Failed to create InterprocessEvent", e);
+ Log.e(TAG, "Failed to create InterprocessEvent", e.getCause());
}
}
@@ -740,9 +746,9 @@
Log.e(TAG, "Failed to deliver event to null subscriber");
}
} catch (IllegalAccessException e) {
- Log.e(TAG, "Failed to invoke method", e);
+ Log.e(TAG, "Failed to invoke method", e.getCause());
} catch (InvocationTargetException e) {
- throw new RuntimeException(e.getCause());
+ Log.e(TAG, "Failed to invoke method", e.getCause());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/CancelEnterRecentsWindowAnimationEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/CancelEnterRecentsWindowAnimationEvent.java
new file mode 100644
index 0000000..7604de1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/CancelEnterRecentsWindowAnimationEvent.java
@@ -0,0 +1,34 @@
+/*
+ * 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.systemui.recents.events.activity;
+
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.model.Task;
+
+/**
+ * This is sent when we want to cancel the enter-recents window animation for the launch task.
+ */
+public class CancelEnterRecentsWindowAnimationEvent extends EventBus.Event {
+
+ // This is set for the task that is launching, which allows us to ensure that we are not
+ // cancelling the same task animation (it will just be overwritten instead)
+ public final Task launchTask;
+
+ public CancelEnterRecentsWindowAnimationEvent(Task launchTask) {
+ this.launchTask = launchTask;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/DismissRecentsToHomeAnimationStarted.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/DismissRecentsToHomeAnimationStarted.java
new file mode 100644
index 0000000..5f3e830
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/DismissRecentsToHomeAnimationStarted.java
@@ -0,0 +1,26 @@
+/*
+ * 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.systemui.recents.events.activity;
+
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * This is sent when the task animation when dismissing Recents starts.
+ */
+public class DismissRecentsToHomeAnimationStarted extends EventBus.Event {
+ // Simple event
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationStartedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationStartedEvent.java
new file mode 100644
index 0000000..f187178
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationStartedEvent.java
@@ -0,0 +1,26 @@
+/*
+ * 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.systemui.recents.events.activity;
+
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * This is sent when the window animation into Recents starts.
+ */
+public class EnterRecentsWindowAnimationStartedEvent extends EventBus.Event {
+ // Simple event
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowLastAnimationFrameEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowLastAnimationFrameEvent.java
new file mode 100644
index 0000000..fd023d8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowLastAnimationFrameEvent.java
@@ -0,0 +1,22 @@
+/*
+ * 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.systemui.recents.events.activity;
+
+import com.android.systemui.recents.events.EventBus;
+
+public class EnterRecentsWindowLastAnimationFrameEvent extends EventBus.Event {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideRecentsEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideRecentsEvent.java
new file mode 100644
index 0000000..bf9b421
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideRecentsEvent.java
@@ -0,0 +1,34 @@
+/*
+ * 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.systemui.recents.events.activity;
+
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * This is sent when the user taps on the Home button or finishes alt-tabbing to hide the Recents
+ * activity.
+ */
+public class HideRecentsEvent extends EventBus.Event {
+
+ public final boolean triggeredFromAltTab;
+ public final boolean triggeredFromHomeKey;
+
+ public HideRecentsEvent(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
+ this.triggeredFromAltTab = triggeredFromAltTab;
+ this.triggeredFromHomeKey = triggeredFromHomeKey;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/IterateRecentsEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/IterateRecentsEvent.java
new file mode 100644
index 0000000..f7b2706
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/IterateRecentsEvent.java
@@ -0,0 +1,27 @@
+/*
+ * 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.systemui.recents.events.activity;
+
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * This is sent when the user taps on the Overview button to iterate to the next item in the
+ * Recents list.
+ */
+public class IterateRecentsEvent extends EventBus.Event {
+ // Simple event
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTaskFailedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTaskFailedEvent.java
new file mode 100644
index 0000000..3a2d58c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTaskFailedEvent.java
@@ -0,0 +1,26 @@
+/*
+ * 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.systemui.recents.events.activity;
+
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * This is sent when we fail to launch a task.
+ */
+public class LaunchTaskFailedEvent extends EventBus.Event {
+ // Simple event
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTaskSucceededEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTaskSucceededEvent.java
new file mode 100644
index 0000000..ec5089f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTaskSucceededEvent.java
@@ -0,0 +1,31 @@
+/*
+ * 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.systemui.recents.events.activity;
+
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * This is sent when we successfully launch a task.
+ */
+public class LaunchTaskSucceededEvent extends EventBus.Event {
+
+ public final int taskIndexFromStackFront;
+
+ public LaunchTaskSucceededEvent(int taskIndexFromStackFront) {
+ this.taskIndexFromStackFront = taskIndexFromStackFront;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ToggleRecentsEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ToggleRecentsEvent.java
new file mode 100644
index 0000000..49655b4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ToggleRecentsEvent.java
@@ -0,0 +1,26 @@
+/*
+ * 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.systemui.recents.events.activity;
+
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * This is sent when the user taps on the Overview button to toggle the Recents activity.
+ */
+public class ToggleRecentsEvent extends EventBus.Event {
+ // Simple event
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/component/RecentsVisibilityChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/component/RecentsVisibilityChangedEvent.java
new file mode 100644
index 0000000..898d1fc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/component/RecentsVisibilityChangedEvent.java
@@ -0,0 +1,38 @@
+/*
+ * 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.systemui.recents.events.component;
+
+import android.content.Context;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+
+/**
+ * This is sent when the visibility of the RecentsActivity for the current user changes.
+ */
+public class RecentsVisibilityChangedEvent extends EventBus.Event {
+
+ public final Context applicationContext;
+ public final SystemServicesProxy systemServicesProxy;
+ public final boolean visible;
+
+ public RecentsVisibilityChangedEvent(Context context, SystemServicesProxy systemServicesProxy,
+ boolean visible) {
+ this.applicationContext = context.getApplicationContext();
+ this.systemServicesProxy = systemServicesProxy;
+ this.visible = visible;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/component/ScreenPinningRequestEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/component/ScreenPinningRequestEvent.java
new file mode 100644
index 0000000..9d96d8e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/component/ScreenPinningRequestEvent.java
@@ -0,0 +1,33 @@
+/*
+ * 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.systemui.recents.events.component;
+
+import android.content.Context;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+
+/**
+ * This is sent when we want to start screen pinning.
+ */
+public class ScreenPinningRequestEvent extends EventBus.Event {
+
+ public final Context applicationContext;
+
+ public ScreenPinningRequestEvent(Context context) {
+ this.applicationContext = context.getApplicationContext();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissTaskEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissTaskEvent.java
deleted file mode 100644
index 12e5d3d..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissTaskEvent.java
+++ /dev/null
@@ -1,35 +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.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;
-
-/**
- * This is sent when a {@link TaskView} has been dismissed.
- */
-public class DismissTaskEvent extends EventBus.Event {
-
- public final Task task;
- public final TaskView taskView;
-
- public DismissTaskEvent(Task task, TaskView taskView) {
- this.task = task;
- this.taskView = taskView;
- }
-}
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
new file mode 100644
index 0000000..968890a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissTaskViewEvent.java
@@ -0,0 +1,35 @@
+/*
+ * 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.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;
+
+/**
+ * This is sent when a {@link TaskView} has been dismissed.
+ */
+public class DismissTaskViewEvent extends EventBus.Event {
+
+ public final Task task;
+ public final TaskView taskView;
+
+ public DismissTaskViewEvent(Task task, TaskView taskView) {
+ this.task = task;
+ this.taskView = taskView;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/UserInteractionEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/UserInteractionEvent.java
new file mode 100644
index 0000000..6e6cd84
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/UserInteractionEvent.java
@@ -0,0 +1,26 @@
+/*
+ * 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.systemui.recents.events.ui;
+
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * This is sent whenever the user interacts with the activity.
+ */
+public class UserInteractionEvent extends EventBus.Event {
+ // Simple event
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDockStateChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDockStateChangedEvent.java
deleted file mode 100644
index f2c3c33..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDockStateChangedEvent.java
+++ /dev/null
@@ -1,35 +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.systemui.recents.events.ui.dragndrop;
-
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.model.Task;
-import com.android.systemui.recents.model.TaskStack;
-
-/**
- * This event is sent when a user drag enters or exits a dock region.
- */
-public class DragDockStateChangedEvent extends EventBus.Event {
-
- public final Task task;
- public final TaskStack.DockState dockState;
-
- public DragDockStateChangedEvent(Task task, TaskStack.DockState dockState) {
- this.task = task;
- this.dockState = dockState;
- }
-}
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
new file mode 100644
index 0000000..21321f2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java
@@ -0,0 +1,35 @@
+/*
+ * 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.systemui.recents.events.ui.dragndrop;
+
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.views.DropTarget;
+
+/**
+ * This event is sent when a user drags in/out of a drop target.
+ */
+public class DragDropTargetChangedEvent extends EventBus.Event {
+
+ public final Task task;
+ public final DropTarget dropTarget;
+
+ public DragDropTargetChangedEvent(Task task, DropTarget dropTarget) {
+ this.task = task;
+ this.dropTarget = dropTarget;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java
index 827998d..957da94 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java
@@ -19,8 +19,8 @@
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.misc.ReferenceCountedTrigger;
import com.android.systemui.recents.model.Task;
-import com.android.systemui.recents.model.TaskStack;
import com.android.systemui.recents.views.DragView;
+import com.android.systemui.recents.views.DropTarget;
import com.android.systemui.recents.views.TaskView;
/**
@@ -31,15 +31,15 @@
public final Task task;
public final TaskView taskView;
public final DragView dragView;
- public final TaskStack.DockState dockState;
+ public final DropTarget dropTarget;
public final ReferenceCountedTrigger postAnimationTrigger;
- public DragEndEvent(Task task, TaskView taskView, DragView dragView,
- TaskStack.DockState dockState, ReferenceCountedTrigger postAnimationTrigger) {
+ public DragEndEvent(Task task, TaskView taskView, DragView dragView, DropTarget dropTarget,
+ ReferenceCountedTrigger postAnimationTrigger) {
this.task = task;
this.taskView = taskView;
this.dragView = dragView;
- this.dockState = dockState;
+ this.dropTarget = dropTarget;
this.postAnimationTrigger = postAnimationTrigger;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartInitializeDropTargetsEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartInitializeDropTargetsEvent.java
new file mode 100644
index 0000000..b450b1f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartInitializeDropTargetsEvent.java
@@ -0,0 +1,36 @@
+/*
+ * 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.systemui.recents.events.ui.dragndrop;
+
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.views.RecentsViewTouchHandler;
+
+/**
+ * This event is sent by the drag manager when it requires drop targets to register themselves for
+ * the current drag gesture.
+ */
+public class DragStartInitializeDropTargetsEvent extends EventBus.Event {
+
+ public final Task task;
+ public final RecentsViewTouchHandler handler;
+
+ public DragStartInitializeDropTargetsEvent(Task task, RecentsViewTouchHandler handler) {
+ this.task = task;
+ this.handler = handler;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/DismissFocusedTaskViewEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/DismissFocusedTaskViewEvent.java
new file mode 100644
index 0000000..9f3e9d5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/DismissFocusedTaskViewEvent.java
@@ -0,0 +1,26 @@
+/*
+ * 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.systemui.recents.events.ui.focus;
+
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * Dismisses the currently focused task view.
+ */
+public class DismissFocusedTaskViewEvent extends EventBus.Event {
+ // Simple event
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/FocusNextTaskViewEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/FocusNextTaskViewEvent.java
new file mode 100644
index 0000000..171ab5e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/FocusNextTaskViewEvent.java
@@ -0,0 +1,26 @@
+/*
+ * 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.systemui.recents.events.ui.focus;
+
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * Focuses the next task view in the stack.
+ */
+public class FocusNextTaskViewEvent extends EventBus.Event {
+ // Simple event
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/FocusPreviousTaskViewEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/FocusPreviousTaskViewEvent.java
new file mode 100644
index 0000000..22469e7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/FocusPreviousTaskViewEvent.java
@@ -0,0 +1,26 @@
+/*
+ * 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.systemui.recents.events.ui.focus;
+
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * Focuses the previous task view in the stack.
+ */
+public class FocusPreviousTaskViewEvent extends EventBus.Event {
+ // Simple event
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/Console.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Console.java
deleted file mode 100644
index 28ac9d3..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/Console.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright (C) 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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.misc;
-
-
-import android.content.ComponentCallbacks2;
-import android.content.Context;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.widget.Toast;
-
-import java.util.HashMap;
-import java.util.Map;
-
-
-public class Console {
- // Timer
- public static final Map<Object, Long> mTimeLogs = new HashMap<Object, Long>();
-
- // Colors
- public static final String AnsiReset = "\u001B[0m";
- public static final String AnsiBlack = "\u001B[30m";
- public static final String AnsiRed = "\u001B[31m"; // SystemUIHandshake
- public static final String AnsiGreen = "\u001B[32m"; // MeasureAndLayout
- public static final String AnsiYellow = "\u001B[33m"; // SynchronizeViewsWithModel
- public static final String AnsiBlue = "\u001B[34m"; // TouchEvents, Search
- public static final String AnsiPurple = "\u001B[35m"; // Draw
- public static final String AnsiCyan = "\u001B[36m"; // ClickEvents
- public static final String AnsiWhite = "\u001B[37m";
-
- // Console enabled state
- public static boolean Enabled = false;
-
- /** Logs a key */
- public static void log(String key) {
- log(true, key, "", AnsiReset);
- }
-
- /** Logs a conditioned key */
- public static void log(boolean condition, String key) {
- if (condition) {
- log(condition, key, "", AnsiReset);
- }
- }
-
- /** Logs a key in a specific color */
- public static void log(boolean condition, String key, Object data) {
- if (condition) {
- log(condition, key, data, AnsiReset);
- }
- }
-
- /** Logs a key with data in a specific color */
- public static void log(boolean condition, String key, Object data, String color) {
- if (condition) {
- System.out.println(color + key + AnsiReset + " " + data.toString());
- }
- }
-
- /** Logs an error */
- public static void logError(Context context, String msg) {
- Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
- Log.e("Recents", msg);
- }
-
- /** Logs a raw error */
- public static void logRawError(String msg, Exception e) {
- Log.e("Recents", msg, e);
- }
-
- /** Logs a divider bar */
- public static void logDivider(boolean condition) {
- if (condition) {
- System.out.println("==== [" + System.currentTimeMillis() +
- "] ============================================================");
- }
- }
-
- /** Starts a time trace */
- public static void logStartTracingTime(boolean condition, String key) {
- if (condition) {
- long curTime = System.currentTimeMillis();
- mTimeLogs.put(key, curTime);
- Console.log(condition, "[Recents|" + key + "]",
- "started @ " + curTime);
- }
- }
-
- /** Continues a time trace */
- public static void logTraceTime(boolean condition, String key, String desc) {
- if (condition) {
- long timeDiff = System.currentTimeMillis() - mTimeLogs.get(key);
- Console.log(condition, "[Recents|" + key + "|" + desc + "]",
- "+" + timeDiff + "ms");
- }
- }
-
- /** Logs a stack trace */
- public static void logStackTrace() {
- logStackTrace("", 99);
- }
-
- /** Logs a stack trace to a certain depth */
- public static void logStackTrace(int depth) {
- logStackTrace("", depth);
- }
-
- /** Logs a stack trace to a certain depth with a key */
- public static void logStackTrace(String key, int depth) {
- int offset = 0;
- StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
- String tinyStackTrace = "";
- // Skip over the known stack trace classes
- for (int i = 0; i < callStack.length; i++) {
- StackTraceElement el = callStack[i];
- String className = el.getClassName();
- if (className.indexOf("dalvik.system.VMStack") == -1 &&
- className.indexOf("java.lang.Thread") == -1 &&
- className.indexOf("recents.Console") == -1) {
- break;
- } else {
- offset++;
- }
- }
- // Build the pretty stack trace
- int start = Math.min(offset + depth, callStack.length);
- int end = offset;
- String indent = "";
- for (int i = start - 1; i >= end; i--) {
- StackTraceElement el = callStack[i];
- tinyStackTrace += indent + " -> " + el.getClassName() +
- "[" + el.getLineNumber() + "]." + el.getMethodName();
- if (i > end) {
- tinyStackTrace += "\n";
- indent += " ";
- }
- }
- log(true, key, tinyStackTrace, AnsiRed);
- }
-
-
- /** Returns the stringified MotionEvent action */
- public static String motionEventActionToString(int action) {
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- return "Down";
- case MotionEvent.ACTION_UP:
- return "Up";
- case MotionEvent.ACTION_MOVE:
- return "Move";
- case MotionEvent.ACTION_CANCEL:
- return "Cancel";
- case MotionEvent.ACTION_POINTER_DOWN:
- return "Pointer Down";
- case MotionEvent.ACTION_POINTER_UP:
- return "Pointer Up";
- default:
- return "" + action;
- }
- }
-
- public static String trimMemoryLevelToString(int level) {
- switch (level) {
- case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
- return "UI Hidden";
- case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
- return "Running Moderate";
- case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
- return "Background";
- case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
- return "Running Low";
- case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
- return "Moderate";
- case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
- return "Critical";
- case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
- return "Complete";
- default:
- return "" + level;
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
index 735f79f..dc8f547 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
@@ -19,8 +19,8 @@
import android.os.Handler;
/**
- * A dozer is a class that fires a trigger after it falls asleep. You can occasionally poke it to
- * wake it up, but it will fall asleep if left untouched.
+ * A dozer is a class that fires a trigger after it falls asleep.
+ * You can occasionally poke the trigger to wake it up, but it will fall asleep if left untouched.
*/
public class DozeTrigger {
@@ -28,48 +28,57 @@
boolean mIsDozing;
boolean mHasTriggered;
- int mDozeDurationSeconds;
- Runnable mSleepRunnable;
+ int mDozeDurationMilliseconds;
+ Runnable mOnSleepRunnable;
// Sleep-runnable
Runnable mDozeRunnable = new Runnable() {
@Override
public void run() {
- mSleepRunnable.run();
mIsDozing = false;
mHasTriggered = true;
+ mOnSleepRunnable.run();
}
};
- public DozeTrigger(int dozeDurationSeconds, Runnable sleepRunnable) {
+ public DozeTrigger(int dozeDurationMilliseconds, Runnable onSleepRunnable) {
mHandler = new Handler();
- mDozeDurationSeconds = dozeDurationSeconds;
- mSleepRunnable = sleepRunnable;
+ mDozeDurationMilliseconds = dozeDurationMilliseconds;
+ mOnSleepRunnable = onSleepRunnable;
}
- /** Starts dozing. This also resets the trigger flag. */
+ /**
+ * Starts dozing and queues the onSleepRunnable to be called. This also resets the trigger flag.
+ */
public void startDozing() {
forcePoke();
mHasTriggered = false;
}
- /** Stops dozing. */
+ /**
+ * Stops dozing and prevents the onSleepRunnable from being called.
+ */
public void stopDozing() {
mHandler.removeCallbacks(mDozeRunnable);
mIsDozing = false;
}
- /** Poke this dozer to wake it up for a little bit, if it is dozing. */
+ /**
+ * Poke this dozer to wake it up if it is dozing, delaying the onSleepRunnable from being
+ * called for a for the doze duration.
+ */
public void poke() {
if (mIsDozing) {
forcePoke();
}
}
- /** Poke this dozer to wake it up for a little bit. */
+ /**
+ * Poke this dozer to wake it up even if it is not currently dozing.
+ */
void forcePoke() {
mHandler.removeCallbacks(mDozeRunnable);
- mHandler.postDelayed(mDozeRunnable, mDozeDurationSeconds * 1000);
+ mHandler.postDelayed(mDozeRunnable, mDozeDurationMilliseconds);
mIsDozing = true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/ForegroundThread.java b/packages/SystemUI/src/com/android/systemui/recents/misc/ForegroundThread.java
new file mode 100644
index 0000000..784ac4e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/ForegroundThread.java
@@ -0,0 +1,55 @@
+/*
+ * 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.systemui.recents.misc;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+
+/**
+ * Similar to {@link com.android.internal.os.BackgroundThread}, this is a shared singleton
+ * foreground thread for each process.
+ */
+public final class ForegroundThread extends HandlerThread {
+ private static ForegroundThread sInstance;
+ private static Handler sHandler;
+
+ private ForegroundThread() {
+ super("recents.fg");
+ }
+
+ private static void ensureThreadLocked() {
+ if (sInstance == null) {
+ sInstance = new ForegroundThread();
+ sInstance.start();
+ sHandler = new Handler(sInstance.getLooper());
+ }
+ }
+
+ public static ForegroundThread get() {
+ synchronized (ForegroundThread.class) {
+ ensureThreadLocked();
+ return sInstance;
+ }
+ }
+
+ public static Handler getHandler() {
+ synchronized (ForegroundThread.class) {
+ ensureThreadLocked();
+ return sHandler;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/ParametricCurve.java b/packages/SystemUI/src/com/android/systemui/recents/misc/ParametricCurve.java
index 8e85bfc..515c3bd 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/ParametricCurve.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/ParametricCurve.java
@@ -48,6 +48,7 @@
float[] xp;
float[] px;
+ float mLength;
CurveFunction mFn;
ParametricCurveFunction mScaleFn;
@@ -79,6 +80,7 @@
dx[xStep] = (float) Math.hypot(fx[xStep] - fx[xStep - 1], step);
pLength += dx[xStep];
}
+ mLength = pLength;
// Approximate p(x), a function of cumulative progress with x, normalized to 0..1
float p = 0;
px[0] = 0f;
@@ -187,6 +189,7 @@
public float computePOffsetForScaledHeight(int height, Rect bounds) {
int top = bounds.top;
int bottom = bounds.bottom;
+ height = Math.min(height, bottom - top);
if (bounds.height() == 0) {
return 0;
@@ -229,6 +232,7 @@
public float computePOffsetForHeight(int height, Rect bounds) {
int top = bounds.top;
int bottom = bounds.bottom;
+ height = Math.min(height, bottom - top);
if (bounds.height() == 0) {
return 0;
@@ -260,4 +264,11 @@
}
return 1f - xToP(maxX, bounds);
}
+
+ /**
+ * Returns the length of this curve.
+ */
+ public float getArcLength() {
+ return mLength;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
index c87a901..401b448 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
@@ -19,6 +19,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
+import android.util.Log;
import java.util.ArrayList;
@@ -28,6 +29,8 @@
*/
public class ReferenceCountedTrigger {
+ private static final String TAG = "ReferenceCountedTrigger";
+
Context mContext;
int mCount;
ArrayList<Runnable> mFirstIncRunnables = new ArrayList<Runnable>();
@@ -94,8 +97,7 @@
if (mErrorRunnable != null) {
mErrorRunnable.run();
} else {
- new Throwable("Invalid ref count").printStackTrace();
- Console.logError(mContext, "Invalid ref count");
+ Log.e(TAG, "Invalid ref count");
}
}
}
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 568d2b1..0432ac9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -16,9 +16,6 @@
package com.android.systemui.recents.misc;
-import static android.app.ActivityManager.DOCKED_STACK_ID;
-import static android.app.ActivityManager.INVALID_STACK_ID;
-
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.ActivityOptions;
@@ -49,24 +46,25 @@
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.Settings;
import android.util.Log;
import android.util.MutableBoolean;
import android.util.Pair;
import android.view.Display;
import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
import com.android.internal.app.AssistUtils;
+import com.android.internal.os.BackgroundThread;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.recents.Constants;
-import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.RecentsImpl;
import java.io.IOException;
import java.util.ArrayList;
@@ -74,6 +72,11 @@
import java.util.List;
import java.util.Random;
+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.HOME_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+
/**
* Acts as a shim around the real system services that we need to access data from, and provides
* a point of injection when testing UI.
@@ -82,12 +85,8 @@
final static String TAG = "SystemServicesProxy";
final static BitmapFactory.Options sBitmapOptions;
- final static HandlerThread sBgThread;
static {
- sBgThread = new HandlerThread("Recents-SystemServicesProxy",
- android.os.Process.THREAD_PRIORITY_BACKGROUND);
- sBgThread.start();
sBitmapOptions = new BitmapFactory.Options();
sBitmapOptions.inMutable = true;
}
@@ -100,11 +99,11 @@
IPackageManager mIpm;
AssistUtils mAssistUtils;
WindowManager mWm;
+ UserManager mUm;
Display mDisplay;
String mRecentsPackage;
ComponentName mAssistComponent;
-
- Handler mBgThreadHandler;
+ boolean mHasFreeformWorkspaceSupport;
Bitmap mDummyIcon;
int mDummyThumbnailWidth;
@@ -122,9 +121,11 @@
mIpm = AppGlobals.getPackageManager();
mAssistUtils = new AssistUtils(context);
mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ mUm = UserManager.get(context);
mDisplay = mWm.getDefaultDisplay();
mRecentsPackage = context.getPackageName();
- mBgThreadHandler = new Handler(sBgThread.getLooper());
+ mHasFreeformWorkspaceSupport = false && mPm.hasSystemFeature(
+ PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT);
// Get the dummy thumbnail width/heights
Resources res = context.getResources();
@@ -237,6 +238,15 @@
return null;
}
+ /**
+ * Returns whether this device has freeform workspaces.
+ */
+ public boolean hasFreeformWorkspaceSupport() {
+ if (mPm == null) return false;
+
+ return mHasFreeformWorkspaceSupport;
+ }
+
/** Returns whether the recents is currently running */
public boolean isRecentsTopMost(ActivityManager.RunningTaskInfo topTask,
MutableBoolean isHomeTopMost) {
@@ -244,16 +254,18 @@
ComponentName topActivity = topTask.topActivity;
// Check if the front most activity is recents
- if (topActivity.getPackageName().equals(Recents.sRecentsPackage) &&
- topActivity.getClassName().equals(Recents.sRecentsActivity)) {
+ if (topActivity.getPackageName().equals(RecentsImpl.RECENTS_PACKAGE) &&
+ topActivity.getClassName().equals(RecentsImpl.RECENTS_ACTIVITY)) {
if (isHomeTopMost != null) {
isHomeTopMost.value = false;
}
return true;
}
+ // Note, this is only valid because we currently only allow the recents and home
+ // activities in the home stack
if (isHomeTopMost != null) {
- isHomeTopMost.value = isInHomeStack(topTask.id);
+ isHomeTopMost.value = SystemServicesProxy.isHomeStack(topTask.stackId);
}
}
return false;
@@ -271,17 +283,6 @@
return null;
}
- /** Allow a task to resize. */
- public void setTaskResizeable(int taskId) {
- if (mIam == null) return;
-
- try {
- mIam.setTaskResizeable(taskId, true);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
-
/**
* Resizes the given task to the new bounds.
*/
@@ -295,12 +296,14 @@
}
}
- /** Docks a task to the side of the screen. */
- public void dockTask(int taskId, int createMode) {
+ /** Docks a task to the side of the screen and starts it. */
+ public void startTaskInDockedMode(int taskId, int createMode) {
if (mIam == null) return;
try {
- mIam.startActivityFromRecents(taskId, DOCKED_STACK_ID, null);
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setDockCreateMode(createMode);
+ mIam.startActivityFromRecents(taskId, DOCKED_STACK_ID, options.toBundle());
} catch (RemoteException e) {
e.printStackTrace();
}
@@ -318,16 +321,18 @@
}
}
- /** Returns whether the specified task is in the home stack */
- public boolean isInHomeStack(int taskId) {
- if (mAm == null) return false;
+ /**
+ * Returns whether the given stack id is the home stack id.
+ */
+ public static boolean isHomeStack(int stackId) {
+ return stackId == HOME_STACK_ID;
+ }
- // If we are mocking, then just return false
- if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
- return false;
- }
-
- return mAm.isInHomeStack(taskId);
+ /**
+ * Returns whether the given stack id is the freeform workspace stack id.
+ */
+ public static boolean isFreeformStack(int stackId) {
+ return stackId == FREEFORM_WORKSPACE_STACK_ID;
}
/**
@@ -345,6 +350,32 @@
return stackInfo != null;
}
+ /**
+ * Cancels the current window transtion to/from Recents for the given task id.
+ */
+ public void cancelWindowTransition(int taskId) {
+ if (mWm == null) return;
+
+ try {
+ WindowManagerGlobal.getWindowManagerService().cancelTaskWindowTransition(taskId);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Cancels the current thumbnail transtion to/from Recents for the given task id.
+ */
+ public void cancelThumbnailTransition(int taskId) {
+ if (mWm == null) return;
+
+ try {
+ WindowManagerGlobal.getWindowManagerService().cancelTaskThumbnailTransition(taskId);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
/** Returns the top task thumbnail for the given task id */
public Bitmap getTaskThumbnail(int taskId) {
if (mAm == null) return null;
@@ -396,6 +427,19 @@
return thumbnail;
}
+ /**
+ * Moves a task into another stack.
+ */
+ public void moveTaskToStack(int taskId, int stackId) {
+ if (mIam == null) return;
+
+ try {
+ mIam.positionTaskInStack(taskId, stackId, 0);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
/** Moves a task to the front with the specified activity options. */
public void moveTaskToFront(int taskId, ActivityOptions opts) {
if (mAm == null) return;
@@ -415,7 +459,7 @@
if (Constants.DebugFlags.App.EnableSystemServicesProxy) return;
// Remove the task.
- mBgThreadHandler.post(new Runnable() {
+ BackgroundThread.getHandler().post(new Runnable() {
@Override
public void run() {
mAm.removeTask(taskId);
@@ -552,12 +596,27 @@
}
/**
- * Returns whether the foreground user is the owner.
+ * Returns whether the provided {@param userId} represents the system user.
*/
- public boolean isForegroundUserSystem() {
- if (mAm == null) return false;
+ public boolean isSystemUser(int userId) {
+ return userId == UserHandle.USER_SYSTEM;
+ }
- return mAm.getCurrentUser() == UserHandle.USER_SYSTEM;
+ /**
+ * Returns the current user id.
+ */
+ public int getCurrentUser() {
+ if (mAm == null) return 0;
+
+ return mAm.getCurrentUser();
+ }
+
+ /**
+ * Returns the processes user id.
+ */
+ public int getProcessUser() {
+ if (mUm == null) return 0;
+ return mUm.getUserHandle();
}
/**
@@ -709,7 +768,7 @@
try {
// Use the home stack bounds
- ActivityManager.StackInfo stackInfo = mIam.getStackInfo(ActivityManager.HOME_STACK_ID);
+ ActivityManager.StackInfo stackInfo = mIam.getStackInfo(HOME_STACK_ID);
if (stackInfo != null) {
windowRect.set(stackInfo.bounds);
}
@@ -729,8 +788,7 @@
taskId, INVALID_STACK_ID, options == null ? null : options.toBundle());
return true;
} catch (Exception e) {
- Console.logError(context,
- context.getString(R.string.recents_launch_error_message, taskName));
+ Log.e(TAG, context.getString(R.string.recents_launch_error_message, taskName), e);
}
}
return false;
@@ -757,4 +815,15 @@
e.printStackTrace();
}
}
+
+ public void endProlongedAnimations() {
+ if (mWm == null) {
+ return;
+ }
+ try {
+ WindowManagerGlobal.getWindowManagerService().endProlongedAnimations();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
}
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 93c5ee7..2bf2ccb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
@@ -18,13 +18,10 @@
import android.animation.Animator;
import android.graphics.Color;
-import android.graphics.Matrix;
-import android.graphics.Rect;
+import android.graphics.RectF;
import android.view.View;
import android.view.ViewParent;
-import java.util.ArrayList;
-
/* Common code */
public class Utilities {
@@ -45,93 +42,19 @@
}
/** Scales a rect about its centroid */
- public static void scaleRectAboutCenter(Rect r, float scale) {
+ public static void scaleRectAboutCenter(RectF r, float scale) {
if (scale != 1.0f) {
- int cx = r.centerX();
- int cy = r.centerY();
+ float cx = r.centerX();
+ float cy = r.centerY();
r.offset(-cx, -cy);
- r.left = (int) (r.left * scale + 0.5f);
- r.top = (int) (r.top * scale + 0.5f);
- r.right = (int) (r.right * scale + 0.5f);
- r.bottom = (int) (r.bottom * scale + 0.5f);
+ r.left *= scale;
+ r.top *= scale;
+ r.right *= scale;
+ r.bottom *= scale;
r.offset(cx, cy);
}
}
- /** Maps a coorindate in a descendant view into the parent. */
- public static float mapCoordInDescendentToSelf(View descendant, View root,
- float[] coord, boolean includeRootScroll) {
- ArrayList<View> ancestorChain = new ArrayList<View>();
-
- float[] pt = {coord[0], coord[1]};
-
- View v = descendant;
- while(v != root && v != null) {
- ancestorChain.add(v);
- v = (View) v.getParent();
- }
- ancestorChain.add(root);
-
- float scale = 1.0f;
- int count = ancestorChain.size();
- for (int i = 0; i < count; i++) {
- View v0 = ancestorChain.get(i);
- // For TextViews, scroll has a meaning which relates to the text position
- // which is very strange... ignore the scroll.
- if (v0 != descendant || includeRootScroll) {
- pt[0] -= v0.getScrollX();
- pt[1] -= v0.getScrollY();
- }
-
- v0.getMatrix().mapPoints(pt);
- pt[0] += v0.getLeft();
- pt[1] += v0.getTop();
- scale *= v0.getScaleX();
- }
-
- coord[0] = pt[0];
- coord[1] = pt[1];
- return scale;
- }
-
- /** Maps a coordinate in the root to a descendent. */
- public static float mapCoordInSelfToDescendent(View descendant, View root,
- float[] coord, Matrix tmpInverseMatrix) {
- ArrayList<View> ancestorChain = new ArrayList<View>();
-
- float[] pt = {coord[0], coord[1]};
-
- View v = descendant;
- while(v != root) {
- ancestorChain.add(v);
- v = (View) v.getParent();
- }
- ancestorChain.add(root);
-
- float scale = 1.0f;
- int count = ancestorChain.size();
- tmpInverseMatrix.set(Matrix.IDENTITY_MATRIX);
- for (int i = count - 1; i >= 0; i--) {
- View ancestor = ancestorChain.get(i);
- View next = i > 0 ? ancestorChain.get(i-1) : null;
-
- pt[0] += ancestor.getScrollX();
- pt[1] += ancestor.getScrollY();
-
- if (next != null) {
- pt[0] -= next.getLeft();
- pt[1] -= next.getTop();
- next.getMatrix().invert(tmpInverseMatrix);
- tmpInverseMatrix.mapPoints(pt);
- scale *= next.getScaleX();
- }
- }
-
- coord[0] = pt[0];
- coord[1] = pt[1];
- return scale;
- }
-
/** Calculates the constrast between two colors, using the algorithm provided by the WCAG v2. */
public static float computeContrastBetweenColors(int bg, int fg) {
float bgR = Color.red(bg) / 255f;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/BitmapLruCache.java b/packages/SystemUI/src/com/android/systemui/recents/model/BitmapLruCache.java
deleted file mode 100644
index 624a8ff..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/model/BitmapLruCache.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.model;
-
-import android.graphics.Bitmap;
-
-/**
- * The Bitmap LRU cache.
- */
-class BitmapLruCache extends KeyStoreLruCache<Bitmap> {
- public BitmapLruCache(int cacheSize) {
- super(cacheSize);
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/DrawableLruCache.java b/packages/SystemUI/src/com/android/systemui/recents/model/DrawableLruCache.java
deleted file mode 100644
index 01a515b..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/model/DrawableLruCache.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.model;
-
-import android.graphics.drawable.Drawable;
-
-/**
- * The Drawable LRU cache.
- */
-class DrawableLruCache extends KeyStoreLruCache<Drawable> {
- public DrawableLruCache(int cacheSize) {
- super(cacheSize);
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/KeyStoreLruCache.java b/packages/SystemUI/src/com/android/systemui/recents/model/KeyStoreLruCache.java
deleted file mode 100644
index 97e0916..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/model/KeyStoreLruCache.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.model;
-
-import android.util.LruCache;
-
-import java.util.HashMap;
-
-/**
- * An LRU cache that internally support querying the keys as well as values. We use this to keep
- * track of the task metadata to determine when to invalidate the cache when tasks have been
- * updated. Generally, this cache will return the last known cache value for the requested task
- * key.
- */
-public class KeyStoreLruCache<V> {
- // We keep a set of keys that are associated with the LRU cache, so that we can find out
- // information about the Task that was previously in the cache.
- HashMap<Integer, Task.TaskKey> mTaskKeys = new HashMap<Integer, Task.TaskKey>();
- // The cache implementation, mapping task id -> value
- LruCache<Integer, V> mCache;
-
- public KeyStoreLruCache(int cacheSize) {
- mCache = new LruCache<Integer, V>(cacheSize) {
-
- @Override
- protected void entryRemoved(boolean evicted, Integer taskId, V oldV, V newV) {
- mTaskKeys.remove(taskId);
- }
- };
- }
-
- /** Gets a specific entry in the cache. */
- final V get(Task.TaskKey key) {
- return mCache.get(key.id);
- }
-
- /**
- * Returns the value only if the Task has not updated since the last time it was in the cache.
- */
- final V getAndInvalidateIfModified(Task.TaskKey key) {
- Task.TaskKey lastKey = mTaskKeys.get(key.id);
- if (lastKey != null && (lastKey.lastActiveTime < key.lastActiveTime)) {
- // The task has updated (been made active since the last time it was put into the
- // LRU cache) so invalidate that item in the cache
- remove(key);
- return null;
- }
- // Either the task does not exist in the cache, or the last active time is the same as
- // the key specified, so return what is in the cache
- return mCache.get(key.id);
- }
-
- /** Puts an entry in the cache for a specific key. */
- final void put(Task.TaskKey key, V value) {
- mCache.put(key.id, value);
- mTaskKeys.put(key.id, key);
- }
-
- /** Removes a cache entry for a specific key. */
- final void remove(Task.TaskKey key) {
- mCache.remove(key.id);
- mTaskKeys.remove(key.id);
- }
-
- /** Removes all the entries in the cache. */
- final void evictAll() {
- mCache.evictAll();
- mTaskKeys.clear();
- }
-
- /** Returns the size of the cache. */
- final int size() {
- return mCache.size();
- }
-
- /** Trims the cache to a specific size */
- final void trimToSize(int cacheSize) {
- mCache.resize(cacheSize);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
index 8f9a293..d9057b8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
@@ -16,17 +16,12 @@
package com.android.systemui.recents.model;
-import android.content.ComponentName;
import android.content.Context;
-import android.os.Looper;
import android.os.UserHandle;
import com.android.internal.content.PackageMonitor;
+import com.android.internal.os.BackgroundThread;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.PackagesChangedEvent;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-
-import java.util.HashSet;
-import java.util.List;
/**
* The package monitor listens for changes from PackageManager to update the contents of the
@@ -38,8 +33,9 @@
public void register(Context context) {
try {
// We register for events from all users, but will cross-reference them with
- // packages for the current user and any profiles they have
- register(context, Looper.getMainLooper(), UserHandle.ALL, true);
+ // packages for the current user and any profiles they have. Ensure that events are
+ // handled in a background thread.
+ register(context, BackgroundThread.get().getLooper(), UserHandle.ALL, true);
} catch (IllegalStateException e) {
e.printStackTrace();
}
@@ -57,9 +53,9 @@
@Override
public void onPackageRemoved(String packageName, int uid) {
- // Notify callbacks that a package has changed
+ // Notify callbacks on the main thread that a package has changed
final int eventUserId = getChangingUserId();
- EventBus.getDefault().send(new PackagesChangedEvent(this, packageName, eventUserId));
+ EventBus.getDefault().post(new PackagesChangedEvent(this, packageName, eventUserId));
}
@Override
@@ -70,39 +66,8 @@
@Override
public void onPackageModified(String packageName) {
- // Notify callbacks that a package has changed
+ // Notify callbacks on the main thread that a package has changed
final int eventUserId = getChangingUserId();
- EventBus.getDefault().send(new PackagesChangedEvent(this, packageName, eventUserId));
- }
-
- /**
- * Computes the components that have been removed as a result of a change in the specified
- * package.
- */
- public HashSet<ComponentName> computeComponentsRemoved(List<Task.TaskKey> taskKeys,
- String packageName, int userId) {
- // Identify all the tasks that should be removed as a result of the package being removed.
- // Using a set to ensure that we callback once per unique component.
- HashSet<ComponentName> existingComponents = new HashSet<ComponentName>();
- HashSet<ComponentName> removedComponents = new HashSet<ComponentName>();
- for (Task.TaskKey t : taskKeys) {
- // Skip if this doesn't apply to the current user
- if (t.userId != userId) continue;
-
- ComponentName cn = t.baseIntent.getComponent();
- if (cn.getPackageName().equals(packageName)) {
- if (existingComponents.contains(cn)) {
- // If we know that the component still exists in the package, then skip
- continue;
- }
- SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
- if (ssp.getActivityInfo(cn, userId) != null) {
- existingComponents.add(cn);
- } else {
- removedComponents.add(cn);
- }
- }
- }
- return removedComponents;
+ EventBus.getDefault().post(new PackagesChangedEvent(this, packageName, eventUserId));
}
}
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 6ef7253..7b04493 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -23,12 +23,12 @@
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
import android.util.Log;
+import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.misc.SystemServicesProxy;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
@@ -41,8 +41,11 @@
* options specified, such that we can transition into the Recents activity seamlessly
*/
public class RecentsTaskLoadPlan {
- static String TAG = "RecentsTaskLoadPlan";
- static boolean DEBUG = false;
+
+ private static String TAG = "RecentsTaskLoadPlan";
+ private static boolean DEBUG = false;
+
+ private static int INVALID_TASK_ID = -1;
/** The set of conditions to load tasks. */
public static class Options {
@@ -56,44 +59,50 @@
}
Context mContext;
- RecentsConfiguration mConfig;
- SystemServicesProxy mSystemServicesProxy;
List<ActivityManager.RecentTaskInfo> mRawTasks;
TaskStack mStack;
- HashMap<Task.ComponentNameKey, ActivityInfoHandle> mActivityInfoCache =
- new HashMap<Task.ComponentNameKey, ActivityInfoHandle>();
/** Package level ctor */
- RecentsTaskLoadPlan(Context context, RecentsConfiguration config, SystemServicesProxy ssp) {
+ RecentsTaskLoadPlan(Context context) {
mContext = context;
- mConfig = config;
- mSystemServicesProxy = ssp;
}
/**
- * An optimization to preload the raw list of tasks.
+ * An optimization to preload the raw list of tasks. The raw tasks are saved in least-recent
+ * to most-recent order.
*/
public synchronized void preloadRawTasks(boolean isTopTaskHome) {
- mRawTasks = mSystemServicesProxy.getRecentTasks(ActivityManager.getMaxRecentTasksStatic(),
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ mRawTasks = ssp.getRecentTasks(ActivityManager.getMaxRecentTasksStatic(),
UserHandle.CURRENT.getIdentifier(), isTopTaskHome);
+ // Since the raw tasks are given in most-recent to least-recent order, we need to reverse it
Collections.reverse(mRawTasks);
- if (DEBUG) Log.d(TAG, "preloadRawTasks, tasks: " + mRawTasks.size());
+ if (DEBUG) {
+ Log.d(TAG, "preloadRawTasks, tasks: " + mRawTasks.size());
+ for (ActivityManager.RecentTaskInfo info : mRawTasks) {
+ Log.d(TAG, " " + info.baseIntent + ", " + info.lastActiveTime);
+ }
+ }
}
/**
* Preloads the list of recent tasks from the system. After this call, the TaskStack will
* have a list of all the recent tasks with their metadata, not including icons or
* thumbnails which were not cached and have to be loaded.
+ *
+ * The tasks will be ordered by:
+ * - least-recent to most-recent stack tasks
+ * - least-recent to most-recent freeform tasks
*/
- synchronized void preloadPlan(RecentsTaskLoader loader, boolean isTopTaskHome) {
+ public synchronized void preloadPlan(RecentsTaskLoader loader, boolean isTopTaskHome) {
if (DEBUG) Log.d(TAG, "preloadPlan");
- // This activity info cache will be used for both preloadPlan() and executePlan()
- mActivityInfoCache.clear();
-
+ RecentsConfiguration config = Recents.getConfiguration();
+ SystemServicesProxy ssp = Recents.getSystemServices();
Resources res = mContext.getResources();
+ ArrayList<Task> freeformTasks = new ArrayList<>();
ArrayList<Task> stackTasks = new ArrayList<>();
if (mRawTasks == null) {
preloadRawTasks(isTopTaskHome);
@@ -106,64 +115,56 @@
Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, t.stackId, t.baseIntent,
t.userId, t.firstActiveTime, t.lastActiveTime);
- // Get an existing activity info handle if possible
- Task.ComponentNameKey cnKey = taskKey.getComponentNameKey();
- ActivityInfoHandle infoHandle;
- boolean hadCachedActivityInfo = false;
- if (mActivityInfoCache.containsKey(cnKey)) {
- infoHandle = mActivityInfoCache.get(cnKey);
- hadCachedActivityInfo = true;
- } else {
- infoHandle = new ActivityInfoHandle();
- }
-
// Load the label, icon, and color
String activityLabel = loader.getAndUpdateActivityLabel(taskKey, t.taskDescription,
- mSystemServicesProxy, infoHandle);
+ ssp);
String contentDescription = loader.getAndUpdateContentDescription(taskKey,
- activityLabel, mSystemServicesProxy, res);
- Drawable activityIcon = loader.getAndUpdateActivityIcon(taskKey, t.taskDescription,
- mSystemServicesProxy, res, infoHandle, false);
- int activityColor = loader.getActivityPrimaryColor(t.taskDescription, res);
-
- // Update the activity info cache
- if (!hadCachedActivityInfo && infoHandle.info != null) {
- mActivityInfoCache.put(cnKey, infoHandle);
- }
+ activityLabel, ssp, res);
+ Drawable activityIcon = loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, ssp,
+ res, false);
+ int activityColor = loader.getActivityPrimaryColor(t.taskDescription);
Bitmap icon = t.taskDescription != null
- ? t.taskDescription.getInMemoryIcon()
- : null;
+ ? t.taskDescription.getInMemoryIcon() : null;
String iconFilename = t.taskDescription != null
- ? t.taskDescription.getIconFilename()
- : null;
+ ? t.taskDescription.getIconFilename() : null;
// Add the task to the stack
- Task task = new Task(taskKey, (t.id != RecentsTaskLoader.INVALID_TASK_ID),
- t.affiliatedTaskId, t.affiliatedTaskColor, activityLabel, contentDescription,
- activityIcon, activityColor, (i == (taskCount - 1)), mConfig.lockToAppEnabled,
- icon, iconFilename);
- task.thumbnail = loader.getAndUpdateThumbnail(taskKey, mSystemServicesProxy, false);
- if (DEBUG) Log.d(TAG, "\tthumbnail: " + taskKey + ", " + task.thumbnail);
+ Task task = new Task(taskKey, t.affiliatedTaskId, t.affiliatedTaskColor, activityLabel,
+ contentDescription, activityIcon, activityColor, (i == (taskCount - 1)),
+ config.lockToAppEnabled, icon, iconFilename, t.bounds);
+ task.thumbnail = loader.getAndUpdateThumbnail(taskKey, ssp, false);
+ if (DEBUG) {
+ Log.d(TAG, activityLabel + " bounds: " + t.bounds);
+ }
- stackTasks.add(task);
+ if (task.isFreeformTask()) {
+ freeformTasks.add(task);
+ } else {
+ stackTasks.add(task);
+ }
}
// Initialize the stacks
+ ArrayList<Task> allTasks = new ArrayList<>();
+ allTasks.addAll(stackTasks);
+ allTasks.addAll(freeformTasks);
mStack = new TaskStack();
- mStack.setTasks(stackTasks);
+ mStack.setTasks(allTasks);
mStack.createAffiliatedGroupings(mContext);
}
/**
* Called to apply the actual loading based on the specified conditions.
*/
- synchronized void executePlan(Options opts, RecentsTaskLoader loader,
+ public synchronized void executePlan(Options opts, RecentsTaskLoader loader,
TaskResourceLoadQueue loadQueue) {
if (DEBUG) Log.d(TAG, "executePlan, # tasks: " + opts.numVisibleTasks +
", # thumbnails: " + opts.numVisibleTaskThumbnails +
", running task id: " + opts.runningTaskId);
+ RecentsConfiguration config = Recents.getConfiguration();
+ SystemServicesProxy ssp = Recents.getSystemServices();
Resources res = mContext.getResources();
// Iterate through each of the tasks and load them according to the load conditions.
@@ -174,17 +175,6 @@
Task task = tasks.get(i);
Task.TaskKey taskKey = task.key;
- // Get an existing activity info handle if possible
- Task.ComponentNameKey cnKey = taskKey.getComponentNameKey();
- ActivityInfoHandle infoHandle;
- boolean hadCachedActivityInfo = false;
- if (mActivityInfoCache.containsKey(cnKey)) {
- infoHandle = mActivityInfoCache.get(cnKey);
- hadCachedActivityInfo = true;
- } else {
- infoHandle = new ActivityInfoHandle();
- }
-
boolean isRunningTask = (task.key.id == opts.runningTaskId);
boolean isVisibleTask = i >= (taskCount - opts.numVisibleTasks);
boolean isVisibleThumbnail = i >= (taskCount - opts.numVisibleTaskThumbnails);
@@ -197,26 +187,20 @@
if (opts.loadIcons && (isRunningTask || isVisibleTask)) {
if (task.activityIcon == null) {
if (DEBUG) Log.d(TAG, "\tLoading icon: " + taskKey);
- task.activityIcon = loader.getAndUpdateActivityIcon(taskKey,
- t.taskDescription, mSystemServicesProxy, res, infoHandle, true);
+ task.activityIcon = loader.getAndUpdateActivityIcon(taskKey, t.taskDescription,
+ ssp, res, true);
}
}
if (opts.loadThumbnails && (isRunningTask || isVisibleThumbnail)) {
if (task.thumbnail == null || isRunningTask) {
if (DEBUG) Log.d(TAG, "\tLoading thumbnail: " + taskKey);
- if (mConfig.svelteLevel <= RecentsConfiguration.SVELTE_LIMIT_CACHE) {
- task.thumbnail = loader.getAndUpdateThumbnail(taskKey,
- mSystemServicesProxy, true);
- } else if (mConfig.svelteLevel == RecentsConfiguration.SVELTE_DISABLE_CACHE) {
+ if (config.svelteLevel <= RecentsConfiguration.SVELTE_LIMIT_CACHE) {
+ task.thumbnail = loader.getAndUpdateThumbnail(taskKey, ssp, true);
+ } else if (config.svelteLevel == RecentsConfiguration.SVELTE_DISABLE_CACHE) {
loadQueue.addTask(task);
}
}
}
-
- // Update the activity info cache
- if (!hadCachedActivityInfo && infoHandle.info != null) {
- mActivityInfoCache.put(cnKey, infoHandle);
- }
}
}
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 39bef81..bba453a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -18,6 +18,7 @@
import android.app.ActivityManager;
import android.content.ComponentCallbacks2;
+import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Resources;
@@ -27,20 +28,21 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
+import android.util.LruCache;
import com.android.systemui.R;
import com.android.systemui.recents.Constants;
+import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.events.activity.PackagesChangedEvent;
import com.android.systemui.recents.misc.SystemServicesProxy;
+import java.util.Map;
import java.util.concurrent.ConcurrentLinkedQueue;
-/** Handle to an ActivityInfo */
-class ActivityInfoHandle {
- ActivityInfo info;
-}
-
-/** A bitmap load queue */
+/**
+ * A Task load queue
+ */
class TaskResourceLoadQueue {
ConcurrentLinkedQueue<Task> mQueue = new ConcurrentLinkedQueue<Task>();
@@ -78,8 +80,10 @@
}
}
-/* Task resource loader */
-class TaskResourceLoader implements Runnable {
+/**
+ * Task resource loader
+ */
+class BackgroundTaskLoader implements Runnable {
static String TAG = "TaskResourceLoader";
static boolean DEBUG = false;
@@ -88,10 +92,9 @@
Handler mLoadThreadHandler;
Handler mMainThreadHandler;
- SystemServicesProxy mSystemServicesProxy;
TaskResourceLoadQueue mLoadQueue;
- DrawableLruCache mApplicationIconCache;
- BitmapLruCache mThumbnailCache;
+ TaskKeyLruCache<Drawable> mApplicationIconCache;
+ TaskKeyLruCache<Bitmap> mThumbnailCache;
Bitmap mDefaultThumbnail;
BitmapDrawable mDefaultApplicationIcon;
@@ -99,9 +102,9 @@
boolean mWaitingOnLoadQueue;
/** Constructor, creates a new loading thread that loads task resources in the background */
- public TaskResourceLoader(TaskResourceLoadQueue loadQueue, DrawableLruCache applicationIconCache,
- BitmapLruCache thumbnailCache, Bitmap defaultThumbnail,
- BitmapDrawable defaultApplicationIcon) {
+ public BackgroundTaskLoader(TaskResourceLoadQueue loadQueue,
+ TaskKeyLruCache<Drawable> applicationIconCache, TaskKeyLruCache<Bitmap> thumbnailCache,
+ Bitmap defaultThumbnail, BitmapDrawable defaultApplicationIcon) {
mLoadQueue = loadQueue;
mApplicationIconCache = applicationIconCache;
mThumbnailCache = thumbnailCache;
@@ -119,7 +122,6 @@
void start(Context context) {
mContext = context;
mCancelled = false;
- mSystemServicesProxy = new SystemServicesProxy(context);
// Notify the load thread to start loading
synchronized(mLoadThread) {
mLoadThread.notifyAll();
@@ -130,7 +132,6 @@
void stop() {
// Mark as cancelled for the thread to pick up
mCancelled = true;
- mSystemServicesProxy = null;
// If we are waiting for the load queue for more tasks, then we can just reset the
// Context now, since nothing is using it
if (mWaitingOnLoadQueue) {
@@ -154,8 +155,8 @@
}
}
} else {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- SystemServicesProxy ssp = mSystemServicesProxy;
+ RecentsConfiguration config = Recents.getConfiguration();
+ SystemServicesProxy ssp = Recents.getSystemServices();
// If we've stopped the loader, then fall through to the above logic to wait on
// the load thread
if (ssp != null) {
@@ -172,7 +173,7 @@
if (cachedIcon == null) {
ActivityInfo info = ssp.getActivityInfo(
- t.key.baseIntent.getComponent(), t.key.userId);
+ t.key.getComponent(), t.key.userId);
if (info != null) {
if (DEBUG) Log.d(TAG, "Loading icon: " + t.key);
cachedIcon = ssp.getActivityIcon(info, t.key.userId);
@@ -246,201 +247,67 @@
}
}
-/* Recents task loader
- * NOTE: We should not hold any references to non-application Context from a static instance */
+/**
+ * Recents task loader
+ */
public class RecentsTaskLoader {
+
private static final String TAG = "RecentsTaskLoader";
+ private static final boolean DEBUG = false;
- static RecentsTaskLoader sInstance;
- static int INVALID_TASK_ID = -1;
+ // This activity info LruCache is useful because it can be expensive to retrieve ActivityInfos
+ // for many tasks, which we use to get the activity labels and icons. Unlike the other caches
+ // below, this is per-package so we can't invalidate the items in the cache based on the last
+ // active time. Instead, we rely on the RecentsPackageMonitor to keep us informed whenever a
+ // package in the cache has been updated, so that we may remove it.
+ private final LruCache<ComponentName, ActivityInfo> mActivityInfoCache;
+ private final TaskKeyLruCache<Drawable> mApplicationIconCache;
+ private final TaskKeyLruCache<Bitmap> mThumbnailCache;
+ private final TaskKeyLruCache<String> mActivityLabelCache;
+ private final TaskKeyLruCache<String> mContentDescriptionCache;
+ private final TaskResourceLoadQueue mLoadQueue;
+ private final BackgroundTaskLoader mLoader;
- SystemServicesProxy mSystemServicesProxy;
- DrawableLruCache mApplicationIconCache;
- BitmapLruCache mThumbnailCache;
- StringLruCache mActivityLabelCache;
- StringLruCache mContentDescriptionCache;
- TaskResourceLoadQueue mLoadQueue;
- TaskResourceLoader mLoader;
+ private final int mMaxThumbnailCacheSize;
+ private final int mMaxIconCacheSize;
+ private int mNumVisibleTasksLoaded;
+ private int mNumVisibleThumbnailsLoaded;
- int mMaxThumbnailCacheSize;
- int mMaxIconCacheSize;
- int mNumVisibleTasksLoaded;
- int mNumVisibleThumbnailsLoaded;
-
+ int mDefaultTaskBarBackgroundColor;
BitmapDrawable mDefaultApplicationIcon;
Bitmap mDefaultThumbnail;
- /** Private Constructor */
- private RecentsTaskLoader(Context context) {
- mMaxThumbnailCacheSize = context.getResources().getInteger(
- R.integer.config_recents_max_thumbnail_count);
- mMaxIconCacheSize = context.getResources().getInteger(
- R.integer.config_recents_max_icon_count);
+ public RecentsTaskLoader(Context context) {
+ Resources res = context.getResources();
+ mDefaultTaskBarBackgroundColor =
+ res.getColor(R.color.recents_task_bar_default_background_color);
+ mMaxThumbnailCacheSize = res.getInteger(R.integer.config_recents_max_thumbnail_count);
+ mMaxIconCacheSize = res.getInteger(R.integer.config_recents_max_icon_count);
int iconCacheSize = Constants.DebugFlags.App.DisableBackgroundCache ? 1 :
mMaxIconCacheSize;
int thumbnailCacheSize = Constants.DebugFlags.App.DisableBackgroundCache ? 1 :
mMaxThumbnailCacheSize;
// Create the default assets
- Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
- icon.eraseColor(0x00000000);
+ Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
+ icon.eraseColor(0);
mDefaultThumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
mDefaultThumbnail.setHasAlpha(false);
mDefaultThumbnail.eraseColor(0xFFffffff);
mDefaultApplicationIcon = new BitmapDrawable(context.getResources(), icon);
// Initialize the proxy, cache and loaders
- mSystemServicesProxy = new SystemServicesProxy(context);
+ int numRecentTasks = ActivityManager.getMaxRecentTasksStatic();
mLoadQueue = new TaskResourceLoadQueue();
- mApplicationIconCache = new DrawableLruCache(iconCacheSize);
- mThumbnailCache = new BitmapLruCache(thumbnailCacheSize);
- mActivityLabelCache = new StringLruCache(100);
- mContentDescriptionCache = new StringLruCache(100);
- mLoader = new TaskResourceLoader(mLoadQueue, mApplicationIconCache, mThumbnailCache,
+ mApplicationIconCache = new TaskKeyLruCache<>(iconCacheSize);
+ mThumbnailCache = new TaskKeyLruCache<>(thumbnailCacheSize);
+ mActivityLabelCache = new TaskKeyLruCache<>(numRecentTasks);
+ mContentDescriptionCache = new TaskKeyLruCache<>(numRecentTasks);
+ mActivityInfoCache = new LruCache(numRecentTasks);
+ mLoader = new BackgroundTaskLoader(mLoadQueue, mApplicationIconCache, mThumbnailCache,
mDefaultThumbnail, mDefaultApplicationIcon);
}
- /** Initializes the recents task loader */
- public static RecentsTaskLoader initialize(Context context) {
- if (sInstance == null) {
- sInstance = new RecentsTaskLoader(context);
- }
- return sInstance;
- }
-
- /** Returns the current recents task loader */
- public static RecentsTaskLoader getInstance() {
- return sInstance;
- }
-
- /** Returns the system services proxy */
- public SystemServicesProxy getSystemServicesProxy() {
- return mSystemServicesProxy;
- }
-
- /** Returns the activity label using as many cached values as we can. */
- public String getAndUpdateActivityLabel(Task.TaskKey taskKey,
- ActivityManager.TaskDescription td, SystemServicesProxy ssp,
- ActivityInfoHandle infoHandle) {
- // Return the task description label if it exists
- if (td != null && td.getLabel() != null) {
- return td.getLabel();
- }
- // Return the cached activity label if it exists
- String label = mActivityLabelCache.getAndInvalidateIfModified(taskKey);
- if (label != null) {
- return label;
- }
- // All short paths failed, load the label from the activity info and cache it
- if (infoHandle.info == null) {
- infoHandle.info = ssp.getActivityInfo(taskKey.baseIntent.getComponent(),
- taskKey.userId);
- }
- if (infoHandle.info != null) {
- label = ssp.getActivityLabel(infoHandle.info);
- mActivityLabelCache.put(taskKey, label);
- return label;
- } else {
- Log.w(TAG, "Missing ActivityInfo for " + taskKey.baseIntent.getComponent()
- + " u=" + taskKey.userId);
- }
- // If the activity info does not exist or fails to load, return an empty label for now,
- // but do not cache it
- return "";
- }
-
- /** Returns the content description using as many cached values as we can. */
- public String getAndUpdateContentDescription(Task.TaskKey taskKey, String activityLabel,
- SystemServicesProxy ssp, Resources res) {
- // Return the cached content description if it exists
- String label = mContentDescriptionCache.getAndInvalidateIfModified(taskKey);
- if (label != null) {
- return label;
- }
- // If the given activity label is empty, don't compute or cache the content description
- if (activityLabel.isEmpty()) {
- return "";
- }
-
- label = ssp.getContentDescription(taskKey.baseIntent, taskKey.userId, activityLabel, res);
- if (label != null) {
- mContentDescriptionCache.put(taskKey, label);
- return label;
- } else {
- Log.w(TAG, "Missing content description for " + taskKey.baseIntent.getComponent()
- + " u=" + taskKey.userId);
- }
- // If the content description does not exist, return an empty label for now, but do not
- // cache it
- return "";
- }
-
- /** Returns the activity icon using as many cached values as we can. */
- public Drawable getAndUpdateActivityIcon(Task.TaskKey taskKey,
- ActivityManager.TaskDescription td, SystemServicesProxy ssp,
- Resources res, ActivityInfoHandle infoHandle, boolean loadIfNotCached) {
- // Return the cached activity icon if it exists
- Drawable icon = mApplicationIconCache.getAndInvalidateIfModified(taskKey);
- if (icon != null) {
- return icon;
- }
-
- if (loadIfNotCached) {
- // Return and cache the task description icon if it exists
- Drawable tdDrawable = mLoader.getTaskDescriptionIcon(taskKey, td.getInMemoryIcon(),
- td.getIconFilename(), ssp, res);
- if (tdDrawable != null) {
- mApplicationIconCache.put(taskKey, tdDrawable);
- return tdDrawable;
- }
-
- // Load the icon from the activity info and cache it
- if (infoHandle.info == null) {
- infoHandle.info = ssp.getActivityInfo(taskKey.baseIntent.getComponent(),
- taskKey.userId);
- }
- if (infoHandle.info != null) {
- icon = ssp.getActivityIcon(infoHandle.info, taskKey.userId);
- if (icon != null) {
- mApplicationIconCache.put(taskKey, icon);
- return icon;
- }
- }
- }
- // We couldn't load any icon
- return null;
- }
-
- /** Returns the bitmap using as many cached values as we can. */
- public Bitmap getAndUpdateThumbnail(Task.TaskKey taskKey, SystemServicesProxy ssp,
- boolean loadIfNotCached) {
- // Return the cached thumbnail if it exists
- Bitmap thumbnail = mThumbnailCache.getAndInvalidateIfModified(taskKey);
- if (thumbnail != null) {
- return thumbnail;
- }
-
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- if (config.svelteLevel < RecentsConfiguration.SVELTE_DISABLE_LOADING && loadIfNotCached) {
- // Load the thumbnail from the system
- thumbnail = ssp.getTaskThumbnail(taskKey.id);
- if (thumbnail != null) {
- mThumbnailCache.put(taskKey, thumbnail);
- return thumbnail;
- }
- }
- // We couldn't load any thumbnail
- return null;
- }
-
- /** Returns the activity's primary color. */
- public int getActivityPrimaryColor(ActivityManager.TaskDescription td, Resources res) {
- if (td != null && td.getPrimaryColor() != 0) {
- return td.getPrimaryColor();
- }
- return res.getColor(R.color.recents_task_bar_default_background_color);
- }
-
/** Returns the size of the app icon cache. */
public int getApplicationIconCacheSize() {
return mMaxIconCacheSize;
@@ -453,8 +320,7 @@
/** Creates a new plan for loading the recent tasks. */
public RecentsTaskLoadPlan createLoadPlan(Context context) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(context, config, mSystemServicesProxy);
+ RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(context);
return plan;
}
@@ -505,23 +371,18 @@
mLoadQueue.removeTask(t);
mThumbnailCache.remove(t.key);
mApplicationIconCache.remove(t.key);
+ mActivityInfoCache.remove(t.key.getComponent());
if (notifyTaskDataUnloaded) {
t.notifyTaskDataUnloaded(null, mDefaultApplicationIcon);
}
}
- /** Stops the task loader and clears all pending tasks */
- void stopLoader() {
- mLoader.stop();
- mLoadQueue.clearTasks();
- }
-
/**
* Handles signals from the system, trimming memory when requested to prevent us from running
* out of memory.
*/
public void onTrimMemory(int level) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
+ RecentsConfiguration config = Recents.getConfiguration();
switch (level) {
case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
// Stop the loader immediately when the UI is no longer visible
@@ -542,18 +403,23 @@
// We are leaving recents, so trim the data a bit
mThumbnailCache.trimToSize(Math.max(1, mMaxThumbnailCacheSize / 2));
mApplicationIconCache.trimToSize(Math.max(1, mMaxIconCacheSize / 2));
+ mActivityInfoCache.trimToSize(Math.max(1,
+ ActivityManager.getMaxRecentTasksStatic() / 2));
break;
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
// We are going to be low on memory
mThumbnailCache.trimToSize(Math.max(1, mMaxThumbnailCacheSize / 4));
mApplicationIconCache.trimToSize(Math.max(1, mMaxIconCacheSize / 4));
+ mActivityInfoCache.trimToSize(Math.max(1,
+ ActivityManager.getMaxRecentTasksStatic() / 4));
break;
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
// We are low on memory, so release everything
mThumbnailCache.evictAll();
mApplicationIconCache.evictAll();
+ mActivityInfoCache.evictAll();
// The cache is small, only clear the label cache when we are critical
mActivityLabelCache.evictAll();
mContentDescriptionCache.evictAll();
@@ -562,4 +428,170 @@
break;
}
}
+
+ /**
+ * Returns the cached task label if the task key is not expired, updating the cache if it is.
+ */
+ String getAndUpdateActivityLabel(Task.TaskKey taskKey, ActivityManager.TaskDescription td,
+ SystemServicesProxy ssp) {
+ // Return the task description label if it exists
+ if (td != null && td.getLabel() != null) {
+ return td.getLabel();
+ }
+ // Return the cached activity label if it exists
+ String label = mActivityLabelCache.getAndInvalidateIfModified(taskKey);
+ if (label != null) {
+ return label;
+ }
+ // All short paths failed, load the label from the activity info and cache it
+ ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey, ssp);
+ if (activityInfo != null) {
+ label = ssp.getActivityLabel(activityInfo);
+ mActivityLabelCache.put(taskKey, label);
+ return label;
+ }
+ // If the activity info does not exist or fails to load, return an empty label for now,
+ // but do not cache it
+ return "";
+ }
+
+ /**
+ * Returns the cached task content description if the task key is not expired, updating the
+ * cache if it is.
+ */
+ String getAndUpdateContentDescription(Task.TaskKey taskKey, String activityLabel,
+ SystemServicesProxy ssp, Resources res) {
+ // Return the cached content description if it exists
+ String label = mContentDescriptionCache.getAndInvalidateIfModified(taskKey);
+ if (label != null) {
+ return label;
+ }
+ // If the given activity label is empty, don't compute or cache the content description
+ if (activityLabel.isEmpty()) {
+ return "";
+ }
+
+ label = ssp.getContentDescription(taskKey.baseIntent, taskKey.userId, activityLabel, res);
+ if (label != null) {
+ mContentDescriptionCache.put(taskKey, label);
+ return label;
+ }
+ // If the content description does not exist, return an empty label for now, but do not
+ // cache it
+ return "";
+ }
+
+ /**
+ * Returns the cached task icon if the task key is not expired, updating the cache if it is.
+ */
+ Drawable getAndUpdateActivityIcon(Task.TaskKey taskKey, ActivityManager.TaskDescription td,
+ SystemServicesProxy ssp, Resources res, boolean loadIfNotCached) {
+ // Return the cached activity icon if it exists
+ Drawable icon = mApplicationIconCache.getAndInvalidateIfModified(taskKey);
+ if (icon != null) {
+ return icon;
+ }
+
+ if (loadIfNotCached) {
+ // Return and cache the task description icon if it exists
+ icon = mLoader.getTaskDescriptionIcon(taskKey, td.getInMemoryIcon(),
+ td.getIconFilename(), ssp, res);
+ if (icon != null) {
+ mApplicationIconCache.put(taskKey, icon);
+ return icon;
+ }
+
+ // Load the icon from the activity info and cache it
+ ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey, ssp);
+ if (activityInfo != null) {
+ icon = ssp.getActivityIcon(activityInfo, taskKey.userId);
+ if (icon != null) {
+ mApplicationIconCache.put(taskKey, icon);
+ return icon;
+ }
+ }
+ }
+ // We couldn't load any icon
+ return null;
+ }
+
+ /**
+ * Returns the cached thumbnail if the task key is not expired, updating the cache if it is.
+ */
+ Bitmap getAndUpdateThumbnail(Task.TaskKey taskKey, SystemServicesProxy ssp,
+ boolean loadIfNotCached) {
+ // Return the cached thumbnail if it exists
+ Bitmap thumbnail = mThumbnailCache.getAndInvalidateIfModified(taskKey);
+ if (thumbnail != null) {
+ return 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;
+ }
+ }
+ }
+ // We couldn't load any thumbnail
+ return null;
+ }
+
+ /**
+ * Returns the task's primary color.
+ */
+ int getActivityPrimaryColor(ActivityManager.TaskDescription td) {
+ if (td != null && td.getPrimaryColor() != 0) {
+ return td.getPrimaryColor();
+ }
+ return mDefaultTaskBarBackgroundColor;
+ }
+
+ /**
+ * Returns the activity info for the given task key, retrieving one from the system if the
+ * task key is expired.
+ */
+ private ActivityInfo getAndUpdateActivityInfo(Task.TaskKey taskKey, SystemServicesProxy ssp) {
+ ComponentName cn = taskKey.getComponent();
+ ActivityInfo activityInfo = mActivityInfoCache.get(cn);
+ if (activityInfo == null) {
+ activityInfo = ssp.getActivityInfo(cn, taskKey.userId);
+ if (cn == null || activityInfo == null) {
+ Log.e(TAG, "Unexpected null component name or activity info: " + cn + ", " +
+ activityInfo);
+ return null;
+ }
+ mActivityInfoCache.put(cn, activityInfo);
+ }
+ return activityInfo;
+ }
+
+ /**
+ * Stops the task loader and clears all queued, pending task loads.
+ */
+ private void stopLoader() {
+ mLoader.stop();
+ mLoadQueue.clearTasks();
+ }
+
+ /**** Event Bus Events ****/
+
+ public final void onBusEvent(PackagesChangedEvent event) {
+ // Remove all the cached activity infos for this package. The other caches do not need to
+ // be pruned at this time, as the TaskKey expiration checks will flush them next time their
+ // cached contents are requested
+ Map<ComponentName, ActivityInfo> activityInfoCache = mActivityInfoCache.snapshot();
+ for (ComponentName cn : activityInfoCache.keySet()) {
+ if (cn.getPackageName().equals(event.packageName)) {
+ if (DEBUG) {
+ Log.d(TAG, "Removing activity info from cache: " + cn);
+ }
+ mActivityInfoCache.remove(cn);
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/StringLruCache.java b/packages/SystemUI/src/com/android/systemui/recents/model/StringLruCache.java
deleted file mode 100644
index 6769716..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/model/StringLruCache.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.model;
-
-/**
- * The String LRU cache.
- */
-class StringLruCache extends KeyStoreLruCache<String> {
- public StringLruCache(int cacheSize) {
- super(cacheSize);
- }
-}
\ No newline at end of file
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 c14adf4..67e18f3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -20,7 +20,10 @@
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Color;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
import java.util.Objects;
@@ -38,38 +41,11 @@
public void onTaskDataUnloaded();
/* Notifies when a task's stack id has changed. */
- public void onMultiStackDebugTaskStackIdChanged();
- }
-
- /** The ComponentNameKey represents the unique primary key for a component
- * belonging to a specified user. */
- public static class ComponentNameKey {
- final ComponentName component;
- final int userId;
-
- public ComponentNameKey(ComponentName cn, int user) {
- component = cn;
- userId = user;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(component, userId);
- }
-
- @Override
- public boolean equals(Object o) {
- if (!(o instanceof ComponentNameKey)) {
- return false;
- }
- return component.equals(((ComponentNameKey) o).component) &&
- userId == ((ComponentNameKey) o).userId;
- }
+ public void onTaskStackIdChanged();
}
/* The Task Key represents the unique primary key for the task */
public static class TaskKey {
- final ComponentNameKey mComponentNameKey;
public final int id;
public int stackId;
public final Intent baseIntent;
@@ -79,7 +55,6 @@
public TaskKey(int id, int stackId, Intent intent, int userId, long firstActiveTime,
long lastActiveTime) {
- mComponentNameKey = new ComponentNameKey(intent.getComponent(), userId);
this.id = id;
this.stackId = stackId;
this.baseIntent = intent;
@@ -88,9 +63,8 @@
this.lastActiveTime = lastActiveTime;
}
- /** Returns the component name key for this task. */
- public ComponentNameKey getComponentNameKey() {
- return mComponentNameKey;
+ public ComponentName getComponent() {
+ return this.baseIntent.getComponent();
}
@Override
@@ -113,7 +87,7 @@
+ "s: " + stackId + ", "
+ "u: " + userId + ", "
+ "lat: " + lastActiveTime + ", "
- + baseIntent.getComponent().getPackageName();
+ + getComponent().getPackageName();
}
}
@@ -129,21 +103,22 @@
public int colorPrimary;
public boolean useLightOnPrimaryColor;
public Bitmap thumbnail;
- public boolean isActive;
public boolean lockToThisTask;
public boolean lockToTaskEnabled;
public Bitmap icon;
public String iconFilename;
- TaskCallbacks mCb;
+ public Rect bounds;
+
+ private TaskCallbacks mCb;
public Task() {
// Do nothing
}
- public Task(TaskKey key, boolean isActive, int taskAffiliation, int taskAffiliationColor,
+ public Task(TaskKey key, int taskAffiliation, int taskAffiliationColor,
String activityTitle, String contentDescription, Drawable activityIcon,
int colorPrimary, boolean lockToThisTask, boolean lockToTaskEnabled, Bitmap icon,
- String iconFilename) {
+ String iconFilename, Rect bounds) {
boolean isInAffiliationGroup = (taskAffiliation != key.id);
boolean hasAffiliationGroupColor = isInAffiliationGroup && (taskAffiliationColor != 0);
this.key = key;
@@ -155,11 +130,11 @@
this.colorPrimary = hasAffiliationGroupColor ? taskAffiliationColor : colorPrimary;
this.useLightOnPrimaryColor = Utilities.computeContrastBetweenColors(this.colorPrimary,
Color.WHITE) > 3f;
- this.isActive = isActive;
this.lockToThisTask = lockToTaskEnabled && lockToThisTask;
this.lockToTaskEnabled = lockToTaskEnabled;
this.icon = icon;
this.iconFilename = iconFilename;
+ this.bounds = bounds;
}
/** Copies the other task. */
@@ -172,7 +147,6 @@
this.activityIcon = o.activityIcon;
this.colorPrimary = o.colorPrimary;
this.useLightOnPrimaryColor = o.useLightOnPrimaryColor;
- this.isActive = o.isActive;
this.lockToThisTask = o.lockToThisTask;
this.lockToTaskEnabled = o.lockToTaskEnabled;
}
@@ -190,14 +164,24 @@
this.group = group;
}
- /** Updates the stack id of this task. */
+ /**
+ * Updates the stack id of this task.
+ */
public void setStackId(int stackId) {
key.stackId = stackId;
if (mCb != null) {
- mCb.onMultiStackDebugTaskStackIdChanged();
+ mCb.onTaskStackIdChanged();
}
}
+ /**
+ * Returns whether this task is on the freeform task stack.
+ */
+ public boolean isFreeformTask() {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ return ssp.hasFreeformWorkspaceSupport() && ssp.isFreeformStack(key.stackId);
+ }
+
/** Notifies the callback listeners that this task has been loaded */
public void notifyTaskDataLoaded(Bitmap thumbnail, Drawable applicationIcon) {
this.applicationIcon = applicationIcon;
@@ -229,7 +213,7 @@
if (group != null) {
groupAffiliation = Integer.toString(group.affiliation);
}
- return "Task (" + groupAffiliation + "): " + key.baseIntent.getComponent().getPackageName() +
+ return "Task (" + groupAffiliation + "): " + key +
" [" + super.toString() + "]";
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskKeyLruCache.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskKeyLruCache.java
new file mode 100644
index 0000000..67a6a9f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskKeyLruCache.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.model;
+
+import android.util.Log;
+import android.util.LruCache;
+import android.util.SparseArray;
+
+/**
+ * A mapping of {@link Task.TaskKey} to value, with additional LRU functionality where the least
+ * recently referenced key/values will be evicted as more values than the given cache size are
+ * inserted.
+ *
+ * In addition, this also allows the caller to invalidate cached values for keys that have since
+ * changed.
+ */
+public class TaskKeyLruCache<V> {
+
+ private static final String TAG = "TaskKeyLruCache";
+
+ private final SparseArray<Task.TaskKey> mKeys = new SparseArray<>();
+ private final LruCache<Integer, V> mCache;
+
+ public TaskKeyLruCache(int cacheSize) {
+ mCache = new LruCache<Integer, V>(cacheSize) {
+
+ @Override
+ protected void entryRemoved(boolean evicted, Integer taskId, V oldV, V newV) {
+ mKeys.remove(taskId);
+ }
+ };
+ }
+
+ /**
+ * Gets a specific entry in the cache with the specified key, regardless of whether the cached
+ * value is valid or not.
+ */
+ final V get(Task.TaskKey key) {
+ return mCache.get(key.id);
+ }
+
+ /**
+ * Returns the value only if the key is valid (has not been updated since the last time it was
+ * in the cache)
+ */
+ final V getAndInvalidateIfModified(Task.TaskKey key) {
+ Task.TaskKey lastKey = mKeys.get(key.id);
+ if (lastKey != null) {
+ if ((lastKey.stackId != key.stackId) || (lastKey.lastActiveTime < key.lastActiveTime)) {
+ // The task has updated (been made active since the last time it was put into the
+ // LRU cache) or the stack id for the task has changed, invalidate that cache item
+ remove(key);
+ return null;
+ }
+ }
+ // Either the task does not exist in the cache, or the last active time is the same as
+ // the key specified, so return what is in the cache
+ return mCache.get(key.id);
+ }
+
+ /** Puts an entry in the cache for a specific key. */
+ final void put(Task.TaskKey key, V value) {
+ if (key == null || value == null) {
+ Log.e(TAG, "Unexpected null key or value: " + key + ", " + value);
+ return;
+ }
+ mKeys.put(key.id, key);
+ mCache.put(key.id, value);
+ }
+
+ /** Removes a cache entry for a specific key. */
+ final void remove(Task.TaskKey key) {
+ mKeys.remove(key.id);
+ mCache.remove(key.id);
+ }
+
+ /** Removes all the entries in the cache. */
+ final void evictAll() {
+ mCache.evictAll();
+ mKeys.clear();
+ }
+
+ /** Trims the cache to a specific size */
+ final void trimToSize(int cacheSize) {
+ mCache.resize(cacheSize);
+ }
+}
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 6598112..6734012 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -17,7 +17,7 @@
package com.android.systemui.recents.model;
import android.animation.ObjectAnimator;
-import android.app.ActivityManager;
+import android.content.ComponentName;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Rect;
@@ -25,18 +25,24 @@
import android.graphics.drawable.ColorDrawable;
import com.android.systemui.R;
import com.android.systemui.recents.Constants;
+import com.android.systemui.recents.Recents;
import com.android.systemui.recents.misc.NamedCounter;
+import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
+import com.android.systemui.recents.views.DropTarget;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Random;
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
/**
@@ -51,9 +57,13 @@
* A list of filtered tasks.
*/
class FilteredTaskList {
- ArrayList<Task> mTasks = new ArrayList<Task>();
- ArrayList<Task> mFilteredTasks = new ArrayList<Task>();
- HashMap<Task.TaskKey, Integer> mTaskIndices = new HashMap<Task.TaskKey, Integer>();
+
+ private static final String TAG = "FilteredTaskList";
+ private static final boolean DEBUG = true;
+
+ ArrayList<Task> mTasks = new ArrayList<>();
+ ArrayList<Task> mFilteredTasks = new ArrayList<>();
+ HashMap<Task.TaskKey, Integer> mTaskIndices = new HashMap<>();
TaskFilter mFilter;
/** Sets the task filter, saving the current touch state */
@@ -90,6 +100,25 @@
updateFilteredTasks();
}
+ /**
+ * Moves the given task.
+ */
+ public void moveTaskToStack(Task task, int insertIndex, int newStackId) {
+ int taskIndex = indexOf(task);
+ if (taskIndex != insertIndex) {
+ mTasks.remove(taskIndex);
+ if (taskIndex < insertIndex) {
+ insertIndex--;
+ }
+ mTasks.add(insertIndex, task);
+ }
+
+ // Update the stack id now, after we've moved the task, and before we update the
+ // filtered tasks
+ task.setStackId(newStackId);
+ updateFilteredTasks();
+ }
+
/** Sets the list of tasks */
void set(List<Task> tasks) {
mTasks.clear();
@@ -184,16 +213,21 @@
}
- public enum DockState {
- NONE(-1, 96, null, null, null),
+ public enum DockState implements DropTarget {
+ NONE(-1, 96, null, null),
LEFT(DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, 192,
- new RectF(0, 0, 0.3f, 1), new RectF(0, 0, 0.3f, 1), new RectF(0.7f, 0, 1, 1)),
+ new RectF(0, 0, 0.25f, 1), new RectF(0, 0, 0.25f, 1)),
TOP(DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, 192,
- new RectF(0, 0, 1, 0.3f), new RectF(0, 0, 1, 0.3f), new RectF(0, 0.7f, 1, 1)),
+ new RectF(0, 0, 1, 0.25f), new RectF(0, 0, 1, 0.25f)),
RIGHT(DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, 192,
- new RectF(0.7f, 0, 1, 1), new RectF(0.7f, 0, 1, 1), new RectF(0, 0, 0.3f, 1)),
+ new RectF(0.75f, 0, 1, 1), new RectF(0.75f, 0, 1, 1)),
BOTTOM(DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, 192,
- new RectF(0, 0.7f, 1, 1), new RectF(0, 0.7f, 1, 1), new RectF(0, 0, 1, 0.3f));
+ new RectF(0, 0.75f, 1, 1), new RectF(0, 0.75f, 1, 1));
+
+ @Override
+ public boolean acceptsDrop(int x, int y, int width, int height) {
+ return touchAreaContainsPoint(width, height, x, y);
+ }
// Represents the view state of this dock state
public class ViewState {
@@ -226,20 +260,17 @@
public final ViewState viewState;
private final RectF dockArea;
private final RectF touchArea;
- private final RectF stackArea;
/**
* @param createMode used to pass to ActivityManager to dock the task
* @param touchArea the area in which touch will initiate this dock state
- * @param stackArea the area for the stack if a task is docked
+ * @param dockArea the visible dock area
*/
- DockState(int createMode, int dockAreaAlpha, RectF touchArea, RectF dockArea,
- RectF stackArea) {
+ DockState(int createMode, int dockAreaAlpha, RectF touchArea, RectF dockArea) {
this.createMode = createMode;
this.viewState = new ViewState(dockAreaAlpha);
this.dockArea = dockArea;
this.touchArea = touchArea;
- this.stackArea = stackArea;
}
/**
@@ -261,18 +292,6 @@
return new Rect((int) (dockArea.left * width), (int) (dockArea.top * height),
(int) (dockArea.right * width), (int) (dockArea.bottom * height));
}
-
- /**
- * Returns the stack bounds with the given {@param width} and {@param height}.
- */
- public Rect getStackBounds(Rect stackRect) {
- int width = stackRect.width();
- int height = stackRect.height();
- return new Rect((int) (stackRect.left + stackArea.left * width),
- (int) (stackRect.top + stackArea.top * height),
- (int) (stackRect.left + (stackArea.right - stackArea.left) * width),
- (int) (stackRect.top + (stackArea.bottom - stackArea.top) * height));
- }
}
// The task offset to apply to a task id as a group affiliation
@@ -305,6 +324,29 @@
}
}
+ /**
+ * Moves the given task to either the front of the freeform workspace or the stack.
+ */
+ public void moveTaskToStack(Task task, int newStackId) {
+ // Find the index to insert into
+ ArrayList<Task> taskList = mTaskList.getTasks();
+ int taskCount = taskList.size();
+ if (!task.isFreeformTask() && (newStackId == FREEFORM_WORKSPACE_STACK_ID)) {
+ // Insert freeform tasks at the front
+ mTaskList.moveTaskToStack(task, taskCount, newStackId);
+ } else if (task.isFreeformTask() && (newStackId == FULLSCREEN_WORKSPACE_STACK_ID)) {
+ // Insert after the first stacked task
+ int insertIndex = 0;
+ for (int i = taskCount - 1; i >= 0; i--) {
+ if (!taskList.get(i).isFreeformTask()) {
+ insertIndex = i + 1;
+ break;
+ }
+ }
+ mTaskList.moveTaskToStack(task, insertIndex, newStackId);
+ }
+ }
+
/** Does the actual work associated with removing the task. */
void removeTaskImpl(Task t) {
// Remove the task from the list
@@ -396,6 +438,21 @@
return mTaskList.size();
}
+ /**
+ * Returns the task in this stack which is the launch target.
+ */
+ public Task getLaunchTarget() {
+ ArrayList<Task> tasks = mTaskList.getTasks();
+ int taskCount = tasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ Task task = tasks.get(i);
+ if (task.isLaunchTarget) {
+ return task;
+ }
+ }
+ return null;
+ }
+
/** Returns the index of this task in this current task stack */
public int indexOfTask(Task t) {
return mTaskList.indexOf(t);
@@ -414,6 +471,21 @@
return null;
}
+ /**
+ * Returns whether this stack has freeform tasks.
+ */
+ public boolean hasFreeformTasks() {
+ ArrayList<Task> tasks = mTaskList.getTasks();
+ int taskCount = tasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ Task task = tasks.get(i);
+ if (task.isFreeformTask()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/******** Filtering ********/
/** Filters the stack into tasks similar to the one specified */
@@ -424,8 +496,8 @@
boolean filtered = mTaskList.setFilter(new TaskFilter() {
@Override
public boolean acceptTask(Task at, int i) {
- return t.key.baseIntent.getComponent().getPackageName().equals(
- at.key.baseIntent.getComponent().getPackageName());
+ return t.key.getComponent().getPackageName().equals(
+ at.key.getComponent().getPackageName());
}
});
if (filtered && mCb != null) {
@@ -490,7 +562,7 @@
int groupCountDown = Constants.DebugFlags.App.TaskAffiliationsGroupCount;
for (int i = 0; i < taskCount; i++) {
Task t = tasks.get(i);
- String packageName = t.key.baseIntent.getComponent().getPackageName();
+ String packageName = t.key.getComponent().getPackageName();
packageName = "pkg";
TaskGrouping group;
if (packageName.equals(prevPackage) && groupCountDown > 0) {
@@ -577,6 +649,37 @@
}
}
+ /**
+ * Computes the components of tasks in this stack that have been removed as a result of a change
+ * in the specified package.
+ */
+ public HashSet<ComponentName> computeComponentsRemoved(String packageName, int userId) {
+ // Identify all the tasks that should be removed as a result of the package being removed.
+ // Using a set to ensure that we callback once per unique component.
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ HashSet<ComponentName> existingComponents = new HashSet<>();
+ HashSet<ComponentName> removedComponents = new HashSet<>();
+ ArrayList<Task.TaskKey> taskKeys = getTaskKeys();
+ for (Task.TaskKey t : taskKeys) {
+ // Skip if this doesn't apply to the current user
+ if (t.userId != userId) continue;
+
+ ComponentName cn = t.getComponent();
+ if (cn.getPackageName().equals(packageName)) {
+ if (existingComponents.contains(cn)) {
+ // If we know that the component still exists in the package, then skip
+ continue;
+ }
+ if (ssp.getActivityInfo(cn, userId) != null) {
+ existingComponents.add(cn);
+ } else {
+ removedComponents.add(cn);
+ }
+ }
+ }
+ return removedComponents;
+ }
+
@Override
public String toString() {
String str = "Tasks:\n";
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java b/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
index fb05c01..70370ec 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
@@ -20,13 +20,12 @@
import android.graphics.Rect;
import android.view.View;
import android.view.ViewOutlineProvider;
+import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsConfiguration;
/* An outline provider that has a clip and outline that can be animated. */
public class AnimateableViewBounds extends ViewOutlineProvider {
- RecentsConfiguration mConfig;
-
TaskView mSourceView;
Rect mClipRect = new Rect();
Rect mClipBounds = new Rect();
@@ -35,10 +34,9 @@
final float mMinAlpha = 0.25f;
public AnimateableViewBounds(TaskView source, int cornerRadius) {
- mConfig = RecentsConfiguration.getInstance();
mSourceView = source;
mCornerRadius = cornerRadius;
- setClipBottom(getClipBottom());
+ setClipBottom(getClipBottom(), false /* force */);
}
@Override
@@ -59,12 +57,14 @@
}
/** Sets the bottom clip. */
- public void setClipBottom(int bottom) {
- if (bottom != mClipRect.bottom) {
+ public void setClipBottom(int bottom, boolean force) {
+ if (bottom != mClipRect.bottom || force) {
mClipRect.bottom = bottom;
mSourceView.invalidateOutline();
updateClipBounds();
- if (!mConfig.useHardwareLayers) {
+
+ RecentsConfiguration config = Recents.getConfiguration();
+ if (!config.useHardwareLayers) {
mSourceView.mThumbnailView.updateThumbnailVisibility(
bottom - mSourceView.getPaddingBottom());
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/DropTarget.java b/packages/SystemUI/src/com/android/systemui/recents/views/DropTarget.java
new file mode 100644
index 0000000..8ae00a7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/DropTarget.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.views;
+
+/**
+ * Represents a drop target for a drag gesture.
+ */
+public interface DropTarget {
+
+ /**
+ * Returns whether this target can accept this drop. The x,y are relative to the top level
+ * RecentsView, and the width/height are of the RecentsView.
+ */
+ boolean acceptsDrop(int x, int y, int width, int height);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java
new file mode 100644
index 0000000..765f686
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.views;
+
+import android.util.Log;
+import com.android.systemui.recents.misc.Utilities;
+import com.android.systemui.recents.model.Task;
+
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * The layout logic for the contents of the freeform workspace.
+ */
+public class FreeformWorkspaceLayoutAlgorithm {
+
+ private static final String TAG = "FreeformWorkspaceLayoutAlgorithm";
+ private static final boolean DEBUG = false;
+
+ // The number of cells in the freeform workspace
+ private int mFreeformCellXCount;
+ private int mFreeformCellYCount;
+
+ // The width and height of the cells in the freeform workspace
+ private int mFreeformCellWidth;
+ private int mFreeformCellHeight;
+
+ // Optimization, allows for quick lookup of task -> index
+ private HashMap<Task.TaskKey, Integer> mTaskIndexMap = new HashMap<>();
+
+ /**
+ * Updates the layout for each of the freeform workspace tasks. This is called after the stack
+ * layout is updated.
+ */
+ public void update(List<Task> freeformTasks, TaskStackLayoutAlgorithm stackLayout) {
+ mTaskIndexMap.clear();
+
+ int numFreeformTasks = stackLayout.mNumFreeformTasks;
+ if (!freeformTasks.isEmpty()) {
+ // Calculate the cell width/height depending on the number of freeform tasks
+ mFreeformCellXCount = Math.max(2, (int) Math.ceil(Math.sqrt(numFreeformTasks)));
+ mFreeformCellYCount = Math.max(2, (int) Math.ceil((float) numFreeformTasks /
+ mFreeformCellXCount));
+ // For now, make the cells square
+ mFreeformCellWidth = Math.min(stackLayout.mFreeformRect.width() / mFreeformCellXCount,
+ stackLayout.mFreeformRect.height() / mFreeformCellYCount);
+ mFreeformCellHeight = mFreeformCellWidth;
+
+ // Put each of the tasks in the progress map at a fixed index (does not need to actually
+ // map to a scroll position, just by index)
+ int taskCount = freeformTasks.size();
+ for (int i = taskCount - 1; i >= 0; i--) {
+ Task task = freeformTasks.get(i);
+ mTaskIndexMap.put(task.key, taskCount - i - 1);
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "mFreeformCellXCount: " + mFreeformCellXCount);
+ Log.d(TAG, "mFreeformCellYCount: " + mFreeformCellYCount);
+ Log.d(TAG, "mFreeformCellWidth: " + mFreeformCellWidth);
+ Log.d(TAG, "mFreeformCellHeight: " + mFreeformCellHeight);
+ }
+ }
+ }
+
+ /**
+ * Returns whether the transform is available for the given task.
+ */
+ public boolean isTransformAvailable(Task task, TaskStackLayoutAlgorithm stackLayout) {
+ if (stackLayout.mNumFreeformTasks == 0 || task == null) {
+ return false;
+ }
+ return mTaskIndexMap.containsKey(task.key);
+ }
+
+ /**
+ * Returns the transform for the given task. Any rect returned will be offset by the actual
+ * transform for the freeform workspace.
+ */
+ public TaskViewTransform getTransform(Task task, TaskViewTransform transformOut,
+ TaskStackLayoutAlgorithm stackLayout) {
+ if (mTaskIndexMap.containsKey(task.key)) {
+ // This is a freeform task, so lay it out in the freeform workspace
+ int taskIndex = mTaskIndexMap.get(task.key);
+ int topOffset = (stackLayout.mFreeformRect.top - stackLayout.mTaskRect.top);
+ int x = taskIndex % mFreeformCellXCount;
+ int y = taskIndex / mFreeformCellXCount;
+ float scale = (float) mFreeformCellWidth / stackLayout.mTaskRect.width();
+ int scaleXOffset = (int) (((1f - scale) * stackLayout.mTaskRect.width()) / 2);
+ int scaleYOffset = (int) (((1f - scale) * stackLayout.mTaskRect.height()) / 2);
+ transformOut.scale = scale * 0.9f;
+ transformOut.translationX = x * mFreeformCellWidth - scaleXOffset;
+ transformOut.translationY = topOffset + y * mFreeformCellHeight - scaleYOffset;
+ transformOut.translationZ = stackLayout.mMaxTranslationZ;
+ transformOut.rect.set(stackLayout.mTaskRect);
+ transformOut.rect.offset(transformOut.translationX, transformOut.translationY);
+ Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
+ transformOut.visible = true;
+ transformOut.p = 0;
+
+ if (DEBUG) {
+ Log.d(TAG, "getTransform: " + task.key + ", " + transformOut);
+ }
+
+ return transformOut;
+ }
+ return null;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
new file mode 100644
index 0000000..a28601b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
@@ -0,0 +1,331 @@
+/*
+ * Copyright (C) 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.views;
+
+import android.annotation.Nullable;
+import android.app.ActivityOptions;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IRemoteCallback;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.AppTransitionAnimationSpec;
+import android.view.IAppTransitionAnimationSpecsFuture;
+import android.view.WindowManagerGlobal;
+import com.android.internal.annotations.GuardedBy;
+import com.android.systemui.recents.Constants;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
+import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent;
+import com.android.systemui.recents.events.activity.LaunchTaskSucceededEvent;
+import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
+import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
+
+import java.util.ArrayList;
+import java.util.List;
+
+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.INVALID_STACK_ID;
+
+/**
+ * A helper class to create transitions to/from Recents
+ */
+public class RecentsTransitionHelper {
+
+ private static final String TAG = "RecentsTransitionHelper";
+ private static final boolean DEBUG = false;
+
+ /**
+ * Special value for {@link #mAppTransitionAnimationSpecs}: Indicate that we are currently
+ * waiting for the specs to be retrieved.
+ */
+ private static final List<AppTransitionAnimationSpec> SPECS_WAITING = new ArrayList<>();
+
+ @GuardedBy("this")
+ private List<AppTransitionAnimationSpec> mAppTransitionAnimationSpecs = SPECS_WAITING;
+
+ private Context mContext;
+ private Handler mHandler;
+ private TaskViewTransform mTmpTransform = new TaskViewTransform();
+
+ private Runnable mStartScreenPinningRunnable = new Runnable() {
+ @Override
+ public void run() {
+ EventBus.getDefault().send(new ScreenPinningRequestEvent(mContext));
+ }
+ };
+
+ public RecentsTransitionHelper(Context context, Handler handler) {
+ mContext = context;
+ mHandler = handler;
+ }
+
+ /**
+ * Launches the specified {@link Task}.
+ */
+ public void launchTaskFromRecents(final TaskStack stack, @Nullable final Task task,
+ final TaskStackView stackView, final TaskView taskView,
+ final boolean lockToTask, final Rect bounds, int destinationStack) {
+ final ActivityOptions opts = ActivityOptions.makeBasic();
+ if (bounds != null) {
+ opts.setBounds(bounds.isEmpty() ? null : bounds);
+ }
+
+ final ActivityOptions.OnAnimationStartedListener animStartedListener;
+ final IAppTransitionAnimationSpecsFuture transitionFuture;
+ if (task.thumbnail != null && task.thumbnail.getWidth() > 0 &&
+ task.thumbnail.getHeight() > 0) {
+ transitionFuture = getAppTransitionFuture(task, stackView, destinationStack);
+ animStartedListener = new ActivityOptions.OnAnimationStartedListener() {
+ @Override
+ public void onAnimationStarted() {
+ // If we are launching into another task, cancel the previous task's
+ // window transition
+ EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
+
+ if (lockToTask) {
+ // Request screen pinning after the animation runs
+ mHandler.postDelayed(mStartScreenPinningRunnable, 350);
+ }
+ }
+ };
+ } else {
+ // This is only the case if the task is not on screen (scrolled offscreen for example)
+ transitionFuture = null;
+ animStartedListener = null;
+ }
+
+ if (taskView == null) {
+ // If there is no task view, then we do not need to worry about animating out occluding
+ // task views, and we can launch immediately
+ startTaskActivity(stack, task, taskView, opts, transitionFuture, animStartedListener);
+ } else {
+ if (task.group != null && !task.group.isFrontMostTask(task)) {
+ stackView.startLaunchTaskAnimation(taskView, new Runnable() {
+ @Override
+ public void run() {
+ startTaskActivity(stack, task, taskView, opts, transitionFuture,
+ animStartedListener);
+ }
+ }, lockToTask);
+ } else {
+ stackView.startLaunchTaskAnimation(taskView, null, lockToTask);
+ startTaskActivity(stack, task, taskView, opts, transitionFuture,
+ animStartedListener);
+ }
+ }
+ }
+
+ /**
+ * Starts the activity for the launch task.
+ *
+ * @param taskView this is the {@link TaskView} that we are launching from. This can be null if
+ * we are toggling recents and the launch-to task is now offscreen.
+ */
+ private void startTaskActivity(TaskStack stack, Task task, @Nullable TaskView taskView,
+ ActivityOptions opts, IAppTransitionAnimationSpecsFuture transitionFuture,
+ final ActivityOptions.OnAnimationStartedListener animStartedListener) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ if (ssp.startActivityFromRecents(mContext, task.key.id, task.activityLabel, opts)) {
+ // Keep track of the index of the task launch
+ int taskIndexFromFront = 0;
+ int taskIndex = stack.indexOfTask(task);
+ if (taskIndex > -1) {
+ taskIndexFromFront = stack.getTaskCount() - taskIndex - 1;
+ }
+ EventBus.getDefault().send(new LaunchTaskSucceededEvent(taskIndexFromFront));
+ } else {
+ // Dismiss the task if we fail to launch it
+ EventBus.getDefault().send(new DismissTaskViewEvent(task, taskView));
+
+ // Keep track of failed launches
+ EventBus.getDefault().send(new LaunchTaskFailedEvent());
+ }
+ 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 {
+ WindowManagerGlobal.getWindowManagerService()
+ .overridePendingAppTransitionMultiThumbFuture(transitionFuture,
+ callback, true /* scaleUp */);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to override transition: " + e);
+ }
+ }
+ }
+
+ /**
+ * Creates a future which will later be queried for animation specs for this current transition.
+ */
+ private IAppTransitionAnimationSpecsFuture getAppTransitionFuture(final Task task,
+ final TaskStackView stackView, final int destinationStack) {
+ return new IAppTransitionAnimationSpecsFuture.Stub() {
+ @Override
+ public AppTransitionAnimationSpec[] get() throws RemoteException {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ synchronized (RecentsTransitionHelper.this) {
+ mAppTransitionAnimationSpecs = composeAnimationSpecs(task, stackView,
+ destinationStack);
+ RecentsTransitionHelper.this.notifyAll();
+ }
+ }
+ });
+ synchronized (RecentsTransitionHelper.this) {
+ while (mAppTransitionAnimationSpecs == SPECS_WAITING) {
+ try {
+ RecentsTransitionHelper.this.wait();
+ } catch (InterruptedException e) {}
+ }
+ if (mAppTransitionAnimationSpecs == null) {
+ return null;
+ }
+ AppTransitionAnimationSpec[] specs
+ = new AppTransitionAnimationSpec[mAppTransitionAnimationSpecs.size()];
+ mAppTransitionAnimationSpecs.toArray(specs);
+ mAppTransitionAnimationSpecs = SPECS_WAITING;
+ return specs;
+ }
+ }
+ };
+ }
+
+ /**
+ * Composes the animation specs for all the tasks in the target stack.
+ */
+ private List<AppTransitionAnimationSpec> composeAnimationSpecs(final Task task,
+ final TaskStackView stackView, final int destinationStack) {
+ // Ensure we have a valid target stack id
+ final int targetStackId = destinationStack != INVALID_STACK_ID ?
+ destinationStack : task.key.stackId;
+ if (targetStackId != FREEFORM_WORKSPACE_STACK_ID
+ && targetStackId != FULLSCREEN_WORKSPACE_STACK_ID) {
+ return null;
+ }
+
+ // Calculate the offscreen task rect (for tasks that are not backed by views)
+ float stackScroll = stackView.getScroller().getStackScroll();
+ TaskView taskView = stackView.getChildViewForTask(task);
+ TaskStackLayoutAlgorithm layoutAlgorithm = stackView.getStackAlgorithm();
+ Rect offscreenTaskRect = new Rect(layoutAlgorithm.mTaskRect);
+ offscreenTaskRect.offsetTo(offscreenTaskRect.left,
+ layoutAlgorithm.mCurrentStackRect.bottom);
+
+ // If this is a full screen stack, the transition will be towards the single, full screen
+ // task. We only need the transition spec for this task.
+ List<AppTransitionAnimationSpec> specs = new ArrayList<>();
+ if (targetStackId == FULLSCREEN_WORKSPACE_STACK_ID) {
+ if (taskView == null) {
+ specs.add(composeOffscreenAnimationSpec(task, offscreenTaskRect));
+ } else {
+ layoutAlgorithm.getStackTransform(task, stackScroll, mTmpTransform, null);
+ specs.add(composeAnimationSpec(taskView, mTmpTransform, true /* addHeaderBitmap */));
+ }
+ return specs;
+ }
+
+ // Otherwise, for freeform tasks, create a new animation spec for each task we have to
+ // launch
+ TaskStack stack = stackView.getStack();
+ ArrayList<Task> tasks = stack.getTasks();
+ int taskCount = tasks.size();
+ for (int i = taskCount - 1; i >= 0; i--) {
+ Task t = tasks.get(i);
+ if (t.isFreeformTask()) {
+ TaskView tv = stackView.getChildViewForTask(t);
+ if (tv == null) {
+ // TODO: Create a different animation task rect for this case (though it should
+ // never happen)
+ specs.add(composeOffscreenAnimationSpec(t, offscreenTaskRect));
+ } else {
+ layoutAlgorithm.getStackTransform(task, stackScroll, mTmpTransform, null);
+ specs.add(composeAnimationSpec(tv, mTmpTransform, true /* addHeaderBitmap */));
+ }
+ }
+ }
+
+ return specs;
+ }
+
+ /**
+ * Composes a single animation spec for the given {@link Task}
+ */
+ private static AppTransitionAnimationSpec composeOffscreenAnimationSpec(Task task,
+ Rect taskRect) {
+ return new AppTransitionAnimationSpec(task.key.id, null, taskRect);
+ }
+
+ /**
+ * Composes a single animation spec for the given {@link TaskView}
+ */
+ private static AppTransitionAnimationSpec composeAnimationSpec(TaskView taskView,
+ TaskViewTransform transform, boolean addHeaderBitmap) {
+ // Disable any focused state before we draw the header
+ // Upfront the processing of the thumbnail
+ if (taskView.isFocusedTask()) {
+ taskView.setFocusedState(false, false /* animated */, false /* requestViewFocus */);
+ }
+
+ Bitmap b = null;
+ if (addHeaderBitmap) {
+ float scale = transform.scale;
+ int fromHeaderWidth = (int) (taskView.mHeaderView.getMeasuredWidth() * scale);
+ int fromHeaderHeight = (int) (taskView.mHeaderView.getMeasuredHeight() * scale);
+ b = Bitmap.createBitmap(fromHeaderWidth, fromHeaderHeight,
+ Bitmap.Config.ARGB_8888);
+
+ if (Constants.DebugFlags.App.EnableTransitionThumbnailDebugMode) {
+ b.eraseColor(0xFFff0000);
+ } else {
+ Canvas c = new Canvas(b);
+ c.scale(scale, scale);
+ taskView.mHeaderView.draw(c);
+ c.setBitmap(null);
+ }
+ b = b.createAshmemBitmap();
+ }
+
+ Rect taskRect = new Rect();
+ transform.rect.round(taskRect);
+ return new AppTransitionAnimationSpec(taskView.getTask().key.id, b, taskRect);
+ }
+}
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 1266120..e37b7dc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -16,48 +16,43 @@
package com.android.systemui.recents.views;
-import android.app.ActivityManager;
-import android.app.ActivityOptions;
import android.content.Context;
-import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.os.IRemoteCallback;
-import android.os.RemoteException;
+import android.os.Handler;
import android.util.ArraySet;
import android.util.AttributeSet;
-import android.util.Log;
-import android.util.SparseArray;
import android.view.AppTransitionAnimationSpec;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowInsets;
-import android.view.WindowManagerGlobal;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
-import com.android.systemui.recents.Constants;
+import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsActivity;
import com.android.systemui.recents.RecentsAppWidgetHostView;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.ui.DismissTaskEvent;
-import com.android.systemui.recents.events.ui.dragndrop.DragDockStateChangedEvent;
+import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
+import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
+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;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
import java.util.ArrayList;
import java.util.List;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+
/**
* This view is the the top level layout that contains TaskStacks (which are laid out according
* to their SpaceNode bounds.
@@ -65,27 +60,24 @@
public class RecentsView extends FrameLayout implements TaskStackView.TaskStackViewCallbacks {
private static final String TAG = "RecentsView";
+ private static final boolean DEBUG = false;
- private static final boolean ADD_HEADER_BITMAP = true;
+ private int mStackViewVisibility = View.VISIBLE;
/** The RecentsView callbacks */
public interface RecentsViewCallbacks {
- public void onTaskViewClicked();
- public void onTaskLaunchFailed();
public void onAllTaskViewsDismissed();
- public void onExitToHomeAnimationTriggered();
- public void onScreenPinningRequest();
- public void runAfterPause(Runnable r);
}
- RecentsConfiguration mConfig;
LayoutInflater mInflater;
+ Handler mHandler;
ArrayList<TaskStack> mStacks;
TaskStackView mTaskStackView;
RecentsAppWidgetHostView mSearchBar;
RecentsViewCallbacks mCb;
+ RecentsTransitionHelper mTransitionHelper;
RecentsViewTouchHandler mTouchHandler;
DragView mDragView;
TaskStack.DockState[] mVisibleDockStates = {
@@ -114,8 +106,9 @@
public RecentsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
setWillNotDraw(false);
- mConfig = RecentsConfiguration.getInstance();
mInflater = LayoutInflater.from(context);
+ mHandler = new Handler();
+ mTransitionHelper = new RecentsTransitionHelper(getContext(), mHandler);
mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
com.android.internal.R.interpolator.fast_out_slow_in);
mTouchHandler = new RecentsViewTouchHandler(this);
@@ -128,7 +121,8 @@
/** Set/get the bsp root node */
public void setTaskStack(TaskStack stack) {
- if (mConfig.getLaunchState().launchedReuseTaskStackViews) {
+ RecentsConfiguration config = Recents.getConfiguration();
+ if (config.getLaunchState().launchedReuseTaskStackViews) {
if (mTaskStackView != null) {
// If onRecentsHidden is not triggered, we need to the stack view again here
mTaskStackView.reset();
@@ -146,6 +140,7 @@
mTaskStackView.setCallbacks(this);
addView(mTaskStackView);
}
+ mTaskStackView.setVisibility(mStackViewVisibility);
// Trigger a new layout
requestLayout();
@@ -178,23 +173,19 @@
public boolean launchFocusedTask() {
if (mTaskStackView != null) {
TaskStack stack = mTaskStackView.getStack();
- // Iterate the stack views and try and find the focused task
- List<TaskView> taskViews = mTaskStackView.getTaskViews();
- int taskViewCount = taskViews.size();
- for (int j = 0; j < taskViewCount; j++) {
- TaskView tv = taskViews.get(j);
- Task task = tv.getTask();
- if (tv.isFocusedTask()) {
- onTaskViewClicked(mTaskStackView, tv, stack, task, false, false, null);
- return true;
- }
+ Task task = mTaskStackView.getFocusedTask();
+ if (task != null) {
+ TaskView taskView = mTaskStackView.getChildViewForTask(task);
+ onTaskViewClicked(mTaskStackView, taskView, stack, task, false, null,
+ INVALID_STACK_ID);
+ return true;
}
}
return false;
}
/** Launches a given task. */
- public boolean launchTask(Task task, Rect taskBounds) {
+ public boolean launchTask(Task task, Rect taskBounds, int destinationStack) {
if (mTaskStackView != null) {
TaskStack stack = mTaskStackView.getStack();
// Iterate the stack views and try and find the given task.
@@ -203,8 +194,8 @@
for (int j = 0; j < taskViewCount; j++) {
TaskView tv = taskViews.get(j);
if (tv.getTask() == task) {
- onTaskViewClicked(mTaskStackView, tv, stack, task, false, taskBounds != null,
- taskBounds);
+ onTaskViewClicked(mTaskStackView, tv, stack, task, false,
+ taskBounds, destinationStack);
return true;
}
}
@@ -212,29 +203,6 @@
return false;
}
- /** Launches the task that Recents was launched from, if possible */
- public boolean launchPreviousTask() {
- if (mTaskStackView != null) {
- TaskStack stack = mTaskStackView.getStack();
- ArrayList<Task> tasks = stack.getTasks();
-
- // Find the launch task in the stack
- // TODO: replace this with an event from RecentsActivity
- if (!tasks.isEmpty()) {
- int taskCount = tasks.size();
- for (int j = 0; j < taskCount; j++) {
- if (tasks.get(j).isLaunchTarget) {
- Task task = tasks.get(j);
- TaskView tv = mTaskStackView.getChildViewForTask(task);
- onTaskViewClicked(mTaskStackView, tv, stack, task, false, false, null);
- return true;
- }
- }
- }
- }
- return false;
- }
-
/** Requests all task stacks to start their enter-recents animation */
public void startEnterRecentsAnimation(ViewAnimation.TaskViewEnterContext ctx) {
// We have to increment/decrement the post animation trigger in case there are no children
@@ -256,8 +224,11 @@
}
ctx.postAnimationTrigger.decrement();
+ // If we are going home, cancel the previous task's window transition
+ EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null));
+
// Notify of the exit animation
- mCb.onExitToHomeAnimationTriggered();
+ EventBus.getDefault().send(new DismissRecentsToHomeAnimationStarted());
}
/** Adds the search bar */
@@ -306,13 +277,14 @@
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ RecentsConfiguration config = Recents.getConfiguration();
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
// Get the search bar bounds and measure the search bar layout
Rect searchBarSpaceBounds = new Rect();
if (mSearchBar != null) {
- mConfig.getSearchBarBounds(new Rect(0, 0, width, height), mSystemInsets.top,
+ config.getSearchBarBounds(new Rect(0, 0, width, height), mSystemInsets.top,
searchBarSpaceBounds);
mSearchBar.measure(
MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.width(), MeasureSpec.EXACTLY),
@@ -320,7 +292,7 @@
}
Rect taskStackBounds = new Rect();
- mConfig.getTaskStackBounds(new Rect(0, 0, width, height), mSystemInsets.top,
+ config.getTaskStackBounds(new Rect(0, 0, width, height), mSystemInsets.top,
mSystemInsets.right, searchBarSpaceBounds, taskStackBounds);
if (mTaskStackView != null && mTaskStackView.getVisibility() != GONE) {
mTaskStackView.setTaskStackBounds(taskStackBounds, mSystemInsets);
@@ -328,9 +300,10 @@
}
if (mDragView != null) {
+ Rect taskRect = mTaskStackView.mLayoutAlgorithm.mTaskRect;
mDragView.measure(
- MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),
- MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
+ MeasureSpec.makeMeasureSpec(taskRect.width(), MeasureSpec.AT_MOST),
+ MeasureSpec.makeMeasureSpec(taskRect.height(), MeasureSpec.AT_MOST));
}
setMeasuredDimension(width, height);
@@ -341,11 +314,13 @@
*/
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ RecentsConfiguration config = Recents.getConfiguration();
+
// Get the search bar bounds so that we lay it out
Rect measuredRect = new Rect(0, 0, getMeasuredWidth(), getMeasuredHeight());
Rect searchBarSpaceBounds = new Rect();
if (mSearchBar != null) {
- mConfig.getSearchBarBounds(measuredRect,
+ config.getSearchBarBounds(measuredRect,
mSystemInsets.top, searchBarSpaceBounds);
mSearchBar.layout(searchBarSpaceBounds.left, searchBarSpaceBounds.top,
searchBarSpaceBounds.right, searchBarSpaceBounds.bottom);
@@ -400,30 +375,6 @@
return super.verifyDrawable(who);
}
- /** Notifies each task view of the user interaction. */
- public void onUserInteraction() {
- // Get the first stack view
- if (mTaskStackView != null) {
- mTaskStackView.onUserInteraction();
- }
- }
-
- /** Focuses the next task in the first stack view */
- public void focusNextTask(boolean forward) {
- // Get the first stack view
- if (mTaskStackView != null) {
- mTaskStackView.focusNextTask(forward, true);
- }
- }
-
- /** Dismisses the focused task. */
- public void dismissFocusedTask() {
- // Get the first stack view
- if (mTaskStackView != null) {
- mTaskStackView.dismissFocusedTask();
- }
- }
-
/** Unfilters any filtered stacks */
public boolean unfilterFilteredStacks() {
if (mStacks != null) {
@@ -448,274 +399,14 @@
}
}
- private void postDrawHeaderThumbnailTransitionRunnable(final TaskStackView view,
- final TaskView clickedView, final int offsetX, final int offsetY,
- final float stackScroll,
- final ActivityOptions.OnAnimationStartedListener animStartedListener) {
- Runnable r = new Runnable() {
- @Override
- public void run() {
- overrideDrawHeaderThumbnailTransition(view, clickedView, offsetX, offsetY,
- stackScroll, animStartedListener);
-
- }
- };
-
- mCb.runAfterPause(r);
- }
-
- private void overrideDrawHeaderThumbnailTransition(TaskStackView stackView,
- TaskView clickedTask, int offsetX, int offsetY, float stackScroll,
- final ActivityOptions.OnAnimationStartedListener animStartedListener) {
- List<AppTransitionAnimationSpec> specs = getAppTransitionAnimationSpecs(stackView,
- clickedTask, offsetX, offsetY, stackScroll);
- if (specs == null) {
- return;
- }
-
- IRemoteCallback.Stub callback = new IRemoteCallback.Stub() {
- @Override
- public void sendResult(Bundle data) throws RemoteException {
- post(new Runnable() {
- @Override
- public void run() {
- if (animStartedListener != null) {
- animStartedListener.onAnimationStarted();
- }
- }
- });
- }
- };
-
- AppTransitionAnimationSpec[] specsArray =
- new AppTransitionAnimationSpec[specs.size()];
- try {
- WindowManagerGlobal.getWindowManagerService().overridePendingAppTransitionMultiThumb(
- specs.toArray(specsArray), callback, true /* scaleUp */);
-
- } catch (RemoteException e) {
- Log.w(TAG, "Error overriding app transition", e);
- }
- }
-
- private List<AppTransitionAnimationSpec> getAppTransitionAnimationSpecs(TaskStackView stackView,
- TaskView clickedTask, int offsetX, int offsetY, float stackScroll) {
- final int targetStackId = clickedTask.getTask().key.stackId;
- if (targetStackId != ActivityManager.FREEFORM_WORKSPACE_STACK_ID
- && targetStackId != ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID) {
- return null;
- }
- // If this is a full screen stack, the transition will be towards the single, full screen
- // task. We only need the transition spec for this task.
- List<AppTransitionAnimationSpec> specs = new ArrayList<>();
- if (targetStackId == ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID) {
- specs.add(createThumbnailHeaderAnimationSpec(
- stackView, offsetX, offsetY, stackScroll, clickedTask,
- clickedTask.getTask().key.id, ADD_HEADER_BITMAP));
- return specs;
- }
- // This is a free form stack or full screen stack, so there will be multiple windows
- // animating from thumbnails. We need transition animation specs for all of them.
-
- // We will use top and bottom task views as a base for tasks, that aren't visible on the
- // screen. This is necessary for cascade recents list, where some of the tasks might be
- // hidden.
- List<TaskView> taskViews = stackView.getTaskViews();
- int childCount = taskViews.size();
- TaskView topChild = taskViews.get(0);
- TaskView bottomChild = taskViews.get(childCount - 1);
- SparseArray<TaskView> taskViewsByTaskId = new SparseArray<>();
- for (int i = 0; i < childCount; i++) {
- TaskView taskView = taskViews.get(i);
- taskViewsByTaskId.put(taskView.getTask().key.id, taskView);
- }
-
- TaskStack stack = stackView.getStack();
- // We go through all tasks now and for each generate transition animation spec. If there is
- // a view associated with a task, we use that view as a base for the animation. If there
- // isn't, we use bottom or top view, depending on which one would be closer to the task
- // view if it existed.
- ArrayList<Task> tasks = stack.getTasks();
- boolean passedClickedTask = false;
- for (int i = 0, n = tasks.size(); i < n; i++) {
- Task task = tasks.get(i);
- TaskView taskView = taskViewsByTaskId.get(task.key.id);
- if (taskView != null) {
- specs.add(createThumbnailHeaderAnimationSpec(stackView, offsetX, offsetY,
- stackScroll, taskView, taskView.getTask().key.id, ADD_HEADER_BITMAP));
- if (taskView == clickedTask) {
- passedClickedTask = true;
- }
- } else {
- taskView = passedClickedTask ? bottomChild : topChild;
- specs.add(createThumbnailHeaderAnimationSpec(stackView, offsetX, offsetY,
- stackScroll, taskView, task.key.id, !ADD_HEADER_BITMAP));
- }
- }
-
- return specs;
- }
-
- private AppTransitionAnimationSpec createThumbnailHeaderAnimationSpec(TaskStackView stackView,
- int offsetX, int offsetY, float stackScroll, TaskView tv, int taskId,
- boolean addHeaderBitmap) {
- // Disable any focused state before we draw the header
- // Upfront the processing of the thumbnail
- if (tv.isFocusedTask()) {
- tv.unsetFocusedTask();
- }
- TaskViewTransform transform = new TaskViewTransform();
- transform = stackView.getStackAlgorithm().getStackTransform(tv.mTask, stackScroll,
- transform, null);
-
- float scale = tv.getScaleX();
- int fromHeaderWidth = (int) (tv.mHeaderView.getMeasuredWidth() * scale);
- int fromHeaderHeight = (int) (tv.mHeaderView.getMeasuredHeight() * scale);
-
- Bitmap b = null;
- if (addHeaderBitmap) {
- b = Bitmap.createBitmap(fromHeaderWidth, fromHeaderHeight,
- Bitmap.Config.ARGB_8888);
-
- if (Constants.DebugFlags.App.EnableTransitionThumbnailDebugMode) {
- b.eraseColor(0xFFff0000);
- } else {
- Canvas c = new Canvas(b);
- c.scale(tv.getScaleX(), tv.getScaleY());
- tv.mHeaderView.draw(c);
- c.setBitmap(null);
-
- }
- b = b.createAshmemBitmap();
- }
-
- int[] pts = new int[2];
- tv.getLocationOnScreen(pts);
-
- final int left = pts[0] + offsetX;
- final int top = pts[1] + offsetY;
- final Rect rect = new Rect(left, top, left + transform.rect.width(),
- top + transform.rect.height());
-
- return new AppTransitionAnimationSpec(taskId, b, rect);
- }
-
/**** TaskStackView.TaskStackCallbacks Implementation ****/
@Override
public void onTaskViewClicked(final TaskStackView stackView, final TaskView tv,
- final TaskStack stack, final Task task, final boolean lockToTask,
- final boolean boundsValid, final Rect bounds) {
-
- // Notify any callbacks of the launching of a new task
- if (mCb != null) {
- mCb.onTaskViewClicked();
- }
-
- // Upfront the processing of the thumbnail
- TaskViewTransform transform = new TaskViewTransform();
- View sourceView;
- int offsetX = 0;
- int offsetY = 0;
- float stackScroll = stackView.getScroller().getStackScroll();
- if (tv == null) {
- // If there is no actual task view, then use the stack view as the source view
- // and then offset to the expected transform rect, but bound this to just
- // outside the display rect (to ensure we don't animate from too far away)
- sourceView = stackView;
- offsetX = transform.rect.left;
- offsetY = getMeasuredHeight();
- } else {
- sourceView = tv.mThumbnailView;
- }
-
- // Compute the thumbnail to scale up from
- final SystemServicesProxy ssp =
- RecentsTaskLoader.getInstance().getSystemServicesProxy();
- ActivityOptions opts = null;
- ActivityOptions.OnAnimationStartedListener animStartedListener = null;
- if (task.thumbnail != null && task.thumbnail.getWidth() > 0 &&
- task.thumbnail.getHeight() > 0) {
- if (lockToTask) {
- animStartedListener = new ActivityOptions.OnAnimationStartedListener() {
- boolean mTriggered = false;
- @Override
- public void onAnimationStarted() {
- if (!mTriggered) {
- postDelayed(new Runnable() {
- @Override
- public void run() {
- mCb.onScreenPinningRequest();
- }
- }, 350);
- mTriggered = true;
- }
- }
- };
- }
- postDrawHeaderThumbnailTransitionRunnable(stackView, tv, offsetX, offsetY, stackScroll,
- animStartedListener);
- opts = ActivityOptions.makeThumbnailAspectScaleUpAnimation(sourceView,
- Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8).createAshmemBitmap(),
- offsetX, offsetY, transform.rect.width(), transform.rect.height(),
- sourceView.getHandler(), animStartedListener);
- } else {
- opts = ActivityOptions.makeBasic();
- }
- if (boundsValid) {
- opts.setBounds(bounds.isEmpty() ? null : bounds);
- }
- final ActivityOptions launchOpts = opts;
- final boolean screenPinningRequested = (animStartedListener == null) && lockToTask;
- final Runnable launchRunnable = new Runnable() {
- @Override
- public void run() {
- if (task.isActive) {
- // Bring an active task to the foreground
- ssp.moveTaskToFront(task.key.id, launchOpts);
- } else {
- if (ssp.startActivityFromRecents(getContext(), task.key.id,
- task.activityLabel, launchOpts)) {
- if (screenPinningRequested) {
- mCb.onScreenPinningRequest();
- }
- } else {
- // Dismiss the task and return the user to home if we fail to
- // launch the task
- EventBus.getDefault().send(new DismissTaskEvent(task, tv));
- if (mCb != null) {
- mCb.onTaskLaunchFailed();
- }
-
- // Keep track of failed launches
- MetricsLogger.count(getContext(), "overview_task_launch_failed", 1);
- }
- }
- }
- };
-
- // Keep track of the index of the task launch
- int taskIndexFromFront = 0;
- int taskIndex = stack.indexOfTask(task);
- if (taskIndex > -1) {
- taskIndexFromFront = stack.getTaskCount() - taskIndex - 1;
- }
- MetricsLogger.histogram(getContext(), "overview_task_launch_index", taskIndexFromFront);
-
- // Launch the app right away if there is no task view, otherwise, animate the icon out first
- if (tv == null) {
- launchRunnable.run();
- } else {
- if (task.group != null && !task.group.isFrontMostTask(task)) {
- // For affiliated tasks that are behind other tasks, we must animate the front cards
- // out of view before starting the task transition
- stackView.startLaunchTaskAnimation(tv, launchRunnable, lockToTask);
- } else {
- // Otherwise, we can start the task transition immediately
- stackView.startLaunchTaskAnimation(tv, null, lockToTask);
- launchRunnable.run();
- }
- }
+ final TaskStack stack, final Task task, final boolean lockToTask,
+ final Rect bounds, int destinationStack) {
+ mTransitionHelper.launchTaskFromRecents(stack, task, stackView, tv, lockToTask, bounds,
+ destinationStack);
}
@Override
@@ -735,14 +426,6 @@
MetricsLogger.count(getContext(), "overview_task_all_dismissed", 1);
}
- /** Final callback after Recents is finally hidden. */
- public void onRecentsHidden() {
- // Notify each task stack view
- if (mTaskStackView != null) {
- mTaskStackView.onRecentsHidden();
- }
- }
-
@Override
public void onTaskStackFilterTriggered() {
// Hide the search bar
@@ -786,53 +469,76 @@
TaskStack.DockState.NONE.viewState.dockAreaAlpha);
}
- public final void onBusEvent(DragDockStateChangedEvent event) {
- if (event.dockState == TaskStack.DockState.NONE) {
+ public final void onBusEvent(DragDropTargetChangedEvent event) {
+ if (event.dropTarget == null || !(event.dropTarget instanceof TaskStack.DockState)) {
updateVisibleDockRegions(mTouchHandler.getDockStatesForCurrentOrientation(),
TaskStack.DockState.NONE.viewState.dockAreaAlpha);
} else {
- updateVisibleDockRegions(new TaskStack.DockState[] {event.dockState}, -1);
+ final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget;
+ updateVisibleDockRegions(new TaskStack.DockState[] {dockState}, -1);
}
}
public final void onBusEvent(final DragEndEvent event) {
- event.postAnimationTrigger.increment();
- event.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+ final Runnable cleanUpRunnable = new Runnable() {
@Override
public void run() {
// Remove the drag view
removeView(mDragView);
mDragView = null;
- updateVisibleDockRegions(null, -1);
-
- // Dock the new task if we are hovering over a valid dock state
- if (event.dockState != TaskStack.DockState.NONE) {
- SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
- ssp.setTaskResizeable(event.task.key.id);
- ssp.dockTask(event.task.key.id, event.dockState.createMode);
- launchTask(event.task, null);
- }
}
- });
- if (event.dockState == TaskStack.DockState.NONE) {
- // Animate the alpha back to what it was before
- Rect taskBounds = mTaskStackView.getStackAlgorithm().getUntransformedTaskViewBounds();
- int left = taskBounds.left + (int) ((1f - event.taskView.getScaleX()) * taskBounds.width()) / 2;
- int top = taskBounds.top + (int) ((1f - event.taskView.getScaleY()) * taskBounds.height()) / 2;
+ };
+
+ // Animate the overlay alpha back to 0
+ updateVisibleDockRegions(null, -1);
+
+ if (event.dropTarget == null) {
+ // No drop targets for hit, so just animate the task back to its place
+ event.postAnimationTrigger.increment();
+ event.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+ @Override
+ public void run() {
+ cleanUpRunnable.run();
+ }
+ });
+ // Animate the task back to where it was before then clean up afterwards
+ TaskViewTransform taskTransform = new TaskViewTransform();
+ TaskStackLayoutAlgorithm layoutAlgorithm = mTaskStackView.getStackAlgorithm();
+ layoutAlgorithm.getStackTransform(event.task,
+ mTaskStackView.getScroller().getStackScroll(), taskTransform, null);
event.dragView.animate()
- .scaleX(1f)
- .scaleY(1f)
- .translationX(left + event.taskView.getTranslationX())
- .translationY(top + event.taskView.getTranslationY())
+ .scaleX(taskTransform.scale)
+ .scaleY(taskTransform.scale)
+ .translationX((layoutAlgorithm.mTaskRect.left - event.dragView.getLeft())
+ + taskTransform.translationX)
+ .translationY((layoutAlgorithm.mTaskRect.top - event.dragView.getTop())
+ + taskTransform.translationY)
.setDuration(175)
.setInterpolator(mFastOutSlowInInterpolator)
.withEndAction(event.postAnimationTrigger.decrementAsRunnable())
.start();
- // Animate the overlay alpha back to 0
- updateVisibleDockRegions(null, -1);
+ } else if (event.dropTarget instanceof TaskStack.DockState) {
+ final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget;
+
+ // For now, just remove the drag view and the original task
+ // TODO: Animate the task to the drop target rect before launching it above
+ cleanUpRunnable.run();
+
+ // Dock the task and launch it
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ ssp.startTaskInDockedMode(event.task.key.id, dockState.createMode);
+ launchTask(event.task, null, INVALID_STACK_ID);
+
} else {
- event.postAnimationTrigger.decrement();
+ // We dropped on another drop target, so just add the cleanup to the post animation
+ // trigger
+ event.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+ @Override
+ public void run() {
+ cleanUpRunnable.run();
+ }
+ });
}
}
@@ -861,4 +567,12 @@
}
}
}
+
+ public void setStackViewVisibility(int stackViewVisibility) {
+ mStackViewVisibility = stackViewVisibility;
+ if (mTaskStackView != null) {
+ mTaskStackView.setVisibility(stackViewVisibility);
+ invalidate();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
index 76399f4..c7edc92 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
@@ -19,29 +19,35 @@
import android.content.res.Configuration;
import android.graphics.Point;
import android.view.MotionEvent;
+import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.ui.dragndrop.DragDockStateChangedEvent;
+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;
+import com.android.systemui.recents.events.ui.dragndrop.DragStartInitializeDropTargetsEvent;
import com.android.systemui.recents.misc.ReferenceCountedTrigger;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
+import java.util.ArrayList;
+
/**
* Represents the dock regions for each orientation.
*/
class DockRegion {
public static TaskStack.DockState[] PHONE_LANDSCAPE = {
- TaskStack.DockState.LEFT, TaskStack.DockState.RIGHT
+ // We only allow docking to the left for now on small devices
+ TaskStack.DockState.LEFT
};
public static TaskStack.DockState[] PHONE_PORTRAIT = {
- // We only allow docking to the top for now
+ // We only allow docking to the top for now on small devices
TaskStack.DockState.TOP
};
public static TaskStack.DockState[] TABLET_LANDSCAPE = {
- TaskStack.DockState.LEFT, TaskStack.DockState.RIGHT
+ TaskStack.DockState.LEFT,
+ TaskStack.DockState.RIGHT
};
public static TaskStack.DockState[] TABLET_PORTRAIT = PHONE_PORTRAIT;
}
@@ -49,7 +55,10 @@
/**
* Handles touch events for a RecentsView.
*/
-class RecentsViewTouchHandler {
+public class RecentsViewTouchHandler {
+
+ private static final String TAG = "RecentsViewTouchHandler";
+ private static final boolean DEBUG = false;
private RecentsView mRv;
@@ -59,19 +68,28 @@
private Point mDownPos = new Point();
private boolean mDragging;
- private TaskStack.DockState mLastDockState = TaskStack.DockState.NONE;
+
+ private DropTarget mLastDropTarget;
+ private ArrayList<DropTarget> mDropTargets = new ArrayList<>();
public RecentsViewTouchHandler(RecentsView rv) {
mRv = rv;
}
/**
+ * Registers a new drop target for the current drag only.
+ */
+ public void registerDropTargetForCurrentDrag(DropTarget target) {
+ mDropTargets.add(target);
+ }
+
+ /**
* Returns the preferred dock states for the current orientation.
*/
public TaskStack.DockState[] getDockStatesForCurrentOrientation() {
boolean isLandscape = mRv.getResources().getConfiguration().orientation ==
Configuration.ORIENTATION_LANDSCAPE;
- RecentsConfiguration config = RecentsConfiguration.getInstance();
+ RecentsConfiguration config = Recents.getConfiguration();
TaskStack.DockState[] dockStates = isLandscape ?
(config.isLargeScreen ? DockRegion.TABLET_LANDSCAPE : DockRegion.PHONE_LANDSCAPE) :
(config.isLargeScreen ? DockRegion.TABLET_PORTRAIT : DockRegion.PHONE_PORTRAIT);
@@ -98,11 +116,24 @@
mDragTask = event.task;
mTaskView = event.taskView;
mDragView = event.dragView;
+ mDropTargets.clear();
float x = mDownPos.x - mDragView.getTopLeftOffset().x;
float y = mDownPos.y - mDragView.getTopLeftOffset().y;
mDragView.setTranslationX(x);
mDragView.setTranslationY(y);
+
+ RecentsConfiguration config = Recents.getConfiguration();
+ if (!config.hasDockedTasks) {
+ // Add the dock state drop targets (these take priority)
+ TaskStack.DockState[] dockStates = getDockStatesForCurrentOrientation();
+ for (TaskStack.DockState dockState : dockStates) {
+ registerDropTargetForCurrentDrag(dockState);
+ }
+ }
+
+ // Request other drop targets to register themselves
+ EventBus.getDefault().send(new DragStartInitializeDropTargetsEvent(event.task, this));
}
public final void onBusEvent(DragEndEvent event) {
@@ -110,7 +141,7 @@
mDragTask = null;
mTaskView = null;
mDragView = null;
- mLastDockState = TaskStack.DockState.NONE;
+ mLastDropTarget = null;
}
/**
@@ -118,8 +149,6 @@
* @param ev
*/
private void handleTouchEvent(MotionEvent ev) {
- boolean isLandscape = mRv.getResources().getConfiguration().orientation ==
- Configuration.ORIENTATION_LANDSCAPE;
int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
@@ -134,20 +163,17 @@
float x = evX - mDragView.getTopLeftOffset().x;
float y = evY - mDragView.getTopLeftOffset().y;
- // Update the dock state
- TaskStack.DockState[] dockStates = getDockStatesForCurrentOrientation();
- TaskStack.DockState foundDockState = TaskStack.DockState.NONE;
- for (int i = dockStates.length - 1; i >= 0; i--) {
- TaskStack.DockState state = dockStates[i];
- if (state.touchAreaContainsPoint(width, height, evX, evY)) {
- foundDockState = state;
+ DropTarget currentDropTarget = null;
+ for (DropTarget target : mDropTargets) {
+ if (target.acceptsDrop((int) evX, (int) evY, width, height)) {
+ currentDropTarget = target;
break;
}
}
- if (mLastDockState != foundDockState) {
- mLastDockState = foundDockState;
- EventBus.getDefault().send(new DragDockStateChangedEvent(mDragTask,
- foundDockState));
+ if (mLastDropTarget != currentDropTarget) {
+ mLastDropTarget = currentDropTarget;
+ EventBus.getDefault().send(new DragDropTargetChangedEvent(mDragTask,
+ currentDropTarget));
}
mDragView.setTranslationX(x);
@@ -162,7 +188,7 @@
mRv.getContext(), null, null, null);
postAnimationTrigger.increment();
EventBus.getDefault().send(new DragEndEvent(mDragTask, mTaskView, mDragView,
- mLastDockState, postAnimationTrigger));
+ mLastDropTarget, postAnimationTrigger));
postAnimationTrigger.decrement();
break;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
index e04699c1..b7c1de3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
@@ -68,6 +68,7 @@
private float mInitialTouchPos;
private boolean mDragging;
+ private float mSnapBackTranslationX;
private View mCurrView;
private boolean mCanCurrViewBeDimissed;
@@ -92,6 +93,10 @@
mDensityScale = densityScale;
}
+ public void setSnapBackTranslationX(float translationX) {
+ mSnapBackTranslationX = translationX;
+ }
+
public void setPagingTouchSlop(float pagingTouchSlop) {
mPagingTouchSlop = pagingTouchSlop;
}
@@ -267,7 +272,7 @@
private void snapChild(final View view, float velocity) {
final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(view);
- ValueAnimator anim = createTranslationAnimation(view, 0);
+ ValueAnimator anim = createTranslationAnimation(view, mSnapBackTranslationX);
int duration = SNAP_ANIM_LEN;
anim.setDuration(duration);
anim.setInterpolator(mLinearOutSlowInInterpolator);
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 b28cc21..c4e2d8a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
@@ -22,14 +22,16 @@
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import com.android.systemui.R;
+import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsActivityLaunchState;
import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
+import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationStartedEvent;
/** Manages the scrims for the various system bars. */
public class SystemBarScrimViews {
Context mContext;
- RecentsConfiguration mConfig;
View mStatusBarScrimView;
View mNavBarScrimView;
@@ -46,7 +48,6 @@
public SystemBarScrimViews(Activity activity) {
mContext = activity;
- mConfig = RecentsConfiguration.getInstance();
mStatusBarScrimView = activity.findViewById(R.id.status_bar_scrim);
mNavBarScrimView = activity.findViewById(R.id.nav_bar_scrim);
mNavBarScrimEnterDuration = activity.getResources().getInteger(
@@ -62,7 +63,8 @@
* the first draw.
*/
public void prepareEnterRecentsAnimation() {
- RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ RecentsConfiguration config = Recents.getConfiguration();
+ RecentsActivityLaunchState launchState = config.getLaunchState();
mHasNavBarScrim = launchState.hasNavBarScrim();
mShouldAnimateNavBarScrim = launchState.shouldAnimateNavBarScrim();
mHasStatusBarScrim = launchState.hasStatusBarScrim();
@@ -74,11 +76,14 @@
View.VISIBLE : View.INVISIBLE);
}
+ /**** EventBus events ****/
+
/**
* Starts animating the scrim views when entering Recents.
*/
- public void startEnterRecentsAnimation() {
- RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ public final void onBusEvent(EnterRecentsWindowAnimationStartedEvent event) {
+ RecentsConfiguration config = Recents.getConfiguration();
+ RecentsActivityLaunchState launchState = config.getLaunchState();
int transitionEnterFromAppDelay = mContext.getResources().getInteger(
R.integer.recents_enter_from_app_transition_duration);
int transitionEnterFromHomeDelay = mContext.getResources().getInteger(
@@ -124,7 +129,7 @@
* Starts animating the scrim views when leaving Recents (either via launching a task, or
* going home).
*/
- public void startExitRecentsAnimation() {
+ public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
int taskViewExitToAppDuration = mContext.getResources().getInteger(
R.integer.recents_task_exit_to_app_duration);
if (mHasStatusBarScrim && mShouldAnimateStatusBarScrim) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
new file mode 100644
index 0000000..51091c3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -0,0 +1,527 @@
+/*
+ * Copyright (C) 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.views;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.util.Log;
+import com.android.systemui.R;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.misc.ParametricCurve;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.misc.Utilities;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+
+/**
+ * The layout logic for a TaskStackView.
+ */
+public class TaskStackLayoutAlgorithm {
+
+ private static final String TAG = "TaskStackViewLayoutAlgorithm";
+ private static final boolean DEBUG = false;
+
+ // The min scale of the last task at the top of the curve
+ private static final float STACK_PEEK_MIN_SCALE = 0.85f;
+ // The scale of the last task
+ private static final float SINGLE_TASK_SCALE = 0.95f;
+ // The percentage of height of task to show between tasks
+ private static final float VISIBLE_TASK_HEIGHT_BETWEEN_TASKS = 0.5f;
+
+ // A report of the visibility state of the stack
+ public class VisibilityReport {
+ public int numVisibleTasks;
+ public int numVisibleThumbnails;
+
+ /** Package level ctor */
+ VisibilityReport(int tasks, int thumbnails) {
+ numVisibleTasks = tasks;
+ numVisibleThumbnails = thumbnails;
+ }
+ }
+
+ Context mContext;
+
+ // The task bounds (untransformed) for layout. This rect is anchored at mTaskRoot.
+ public Rect mTaskRect = new Rect();
+ // The freeform workspace bounds, inset from the top by the search bar, and is a fixed height
+ public Rect mFreeformRect = new Rect();
+ // The freeform stack bounds, inset from the top by the search bar and freeform workspace, and
+ // runs to the bottom of the screen
+ private Rect mFreeformStackRect = new Rect();
+ // The stack bounds, inset from the top by the search bar, and runs to
+ // the bottom of the screen
+ private Rect mStackRect = new Rect();
+ // The current stack rect, can either by mFreeformStackRect or mStackRect depending on whether
+ // there is a freeform workspace
+ public Rect mCurrentStackRect;
+ // This is the current system insets
+ public Rect mSystemInsets = new Rect();
+
+ // The smallest scroll progress, at this value, the back most task will be visible
+ float mMinScrollP;
+ // The largest scroll progress, at this value, the front most task will be visible above the
+ // navigation bar
+ float mMaxScrollP;
+ // The initial progress that the scroller is set when you first enter recents
+ float mInitialScrollP;
+ // The task progress for the front-most task in the stack
+ float mFrontMostTaskP;
+
+ // The relative progress to ensure that the height between affiliated tasks is respected
+ float mWithinAffiliationPOffset;
+ // The relative progress to ensure that the height between non-affiliated tasks is
+ // respected
+ float mBetweenAffiliationPOffset;
+ // The relative progress to ensure that the task height is respected
+ float mTaskHeightPOffset;
+ // The relative progress to ensure that the half task height is respected
+ float mTaskHalfHeightPOffset;
+ // The front-most task bottom offset
+ int mStackBottomOffset;
+ // The relative progress to ensure that the offset from the bottom of the stack to the bottom
+ // of the task is respected
+ float mStackBottomPOffset;
+
+ // The last computed task counts
+ int mNumStackTasks;
+ int mNumFreeformTasks;
+ // The min/max z translations
+ int mMinTranslationZ;
+ int mMaxTranslationZ;
+
+ // Optimization, allows for quick lookup of task -> progress
+ HashMap<Task.TaskKey, Float> mTaskProgressMap = new HashMap<>();
+
+ // The freeform workspace layout
+ FreeformWorkspaceLayoutAlgorithm mFreeformLayoutAlgorithm;
+
+ // Log function
+ static ParametricCurve sCurve;
+
+ public TaskStackLayoutAlgorithm(Context context) {
+ Resources res = context.getResources();
+ mMinTranslationZ = res.getDimensionPixelSize(R.dimen.recents_task_view_z_min);
+ mMaxTranslationZ = res.getDimensionPixelSize(R.dimen.recents_task_view_z_max);
+ mContext = context;
+ mFreeformLayoutAlgorithm = new FreeformWorkspaceLayoutAlgorithm();
+ if (sCurve == null) {
+ sCurve = new ParametricCurve(new ParametricCurve.CurveFunction() {
+ // The large the XScale, the longer the flat area of the curve
+ private static final float XScale = 1.75f;
+ private static final float LogBase = 3000;
+
+ float reverse(float x) {
+ return (-x * XScale) + 1;
+ }
+
+ @Override
+ public float f(float x) {
+ return 1f - (float) (Math.pow(LogBase, reverse(x))) / (LogBase);
+ }
+
+ @Override
+ public float invF(float y) {
+ return (float) (Math.log(1f - reverse(y)) / (-Math.log(LogBase) * XScale));
+ }
+ }, new ParametricCurve.ParametricCurveFunction() {
+ @Override
+ public float f(float p) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ if (ssp.hasFreeformWorkspaceSupport()) {
+ return 1f;
+ }
+
+ if (p < 0) return STACK_PEEK_MIN_SCALE;
+ if (p > 1) return 1f;
+ float scaleRange = (1f - STACK_PEEK_MIN_SCALE);
+ float scale = STACK_PEEK_MIN_SCALE + (p * scaleRange);
+ return scale;
+ }
+ });
+ }
+ }
+
+ /**
+ * Sets the system insets.
+ */
+ public void setSystemInsets(Rect systemInsets) {
+ mSystemInsets.set(systemInsets);
+ if (DEBUG) {
+ Log.d(TAG, "setSystemInsets: " + systemInsets);
+ }
+ }
+
+ /**
+ * Computes the stack and task rects. The given task stack bounds is the whole bounds not
+ * including the search bar.
+ */
+ public void initialize(Rect taskStackBounds) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+
+ RecentsConfiguration config = Recents.getConfiguration();
+ int widthPadding = (int) (config.taskStackWidthPaddingPct * taskStackBounds.width());
+ int heightPadding = mContext.getResources().getDimensionPixelSize(
+ R.dimen.recents_stack_top_padding);
+
+ // The freeform height is the visible height (not including system insets) - padding above
+ // freeform and below stack - gap between the freeform and stack
+ mStackBottomOffset = mSystemInsets.bottom + heightPadding;
+ int ffHeight = (taskStackBounds.height() - 2 * heightPadding - mStackBottomOffset) / 2;
+ mFreeformRect.set(taskStackBounds.left + widthPadding,
+ taskStackBounds.top + heightPadding,
+ taskStackBounds.right - widthPadding,
+ taskStackBounds.top + heightPadding + ffHeight);
+ mFreeformStackRect.set(taskStackBounds.left + widthPadding,
+ taskStackBounds.top + heightPadding + ffHeight + heightPadding,
+ taskStackBounds.right - widthPadding,
+ taskStackBounds.bottom);
+ mStackRect.set(taskStackBounds.left + widthPadding,
+ taskStackBounds.top + heightPadding,
+ taskStackBounds.right - widthPadding,
+ taskStackBounds.bottom);
+ // Anchor the task rect to the top-center of the non-freeform stack rect
+ int size = Math.min(mStackRect.width(), mStackRect.height() - mStackBottomOffset);
+ mTaskRect.set(mStackRect.left, mStackRect.top,
+ mStackRect.left + size, mStackRect.top + size);
+ mCurrentStackRect = ssp.hasFreeformWorkspaceSupport() ? mFreeformStackRect : mStackRect;
+
+ // Compute the progress offsets
+ int withinAffiliationOffset = mContext.getResources().getDimensionPixelSize(
+ R.dimen.recents_task_bar_height);
+ int betweenAffiliationOffset = (int) (VISIBLE_TASK_HEIGHT_BETWEEN_TASKS * mTaskRect.height());
+ mWithinAffiliationPOffset = sCurve.computePOffsetForScaledHeight(withinAffiliationOffset,
+ mCurrentStackRect);
+ mBetweenAffiliationPOffset = sCurve.computePOffsetForScaledHeight(betweenAffiliationOffset,
+ mCurrentStackRect);
+ mTaskHeightPOffset = sCurve.computePOffsetForScaledHeight(mTaskRect.height(),
+ mCurrentStackRect);
+ mTaskHalfHeightPOffset = sCurve.computePOffsetForScaledHeight(mTaskRect.height() / 2,
+ mCurrentStackRect);
+ mStackBottomPOffset = sCurve.computePOffsetForHeight(mStackBottomOffset, mCurrentStackRect);
+
+ if (DEBUG) {
+ Log.d(TAG, "initialize");
+ Log.d(TAG, "\tarclength: " + sCurve.getArcLength());
+ Log.d(TAG, "\tmFreeformRect: " + mFreeformRect);
+ Log.d(TAG, "\tmFreeformStackRect: " + mFreeformStackRect);
+ Log.d(TAG, "\tmStackRect: " + mStackRect);
+ Log.d(TAG, "\tmTaskRect: " + mTaskRect);
+ Log.d(TAG, "\tmSystemInsets: " + mSystemInsets);
+
+ Log.d(TAG, "\tpWithinAffiliateOffset: " + mWithinAffiliationPOffset);
+ Log.d(TAG, "\tpBetweenAffiliateOffset: " + mBetweenAffiliationPOffset);
+ Log.d(TAG, "\tmTaskHeightPOffset: " + mTaskHeightPOffset);
+ Log.d(TAG, "\tmTaskHalfHeightPOffset: " + mTaskHalfHeightPOffset);
+ Log.d(TAG, "\tmStackBottomPOffset: " + mStackBottomPOffset);
+
+ Log.d(TAG, "\ty at p=0: " + sCurve.pToX(0f, mCurrentStackRect));
+ Log.d(TAG, "\ty at p=1: " + sCurve.pToX(1f, mCurrentStackRect));
+ }
+ }
+
+ /**
+ * Computes the minimum and maximum scroll progress values and the progress values for each task
+ * in the stack.
+ */
+ void update(TaskStack stack) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+
+ // Clear the progress map
+ mTaskProgressMap.clear();
+
+ // Return early if we have no tasks
+ ArrayList<Task> tasks = stack.getTasks();
+ if (tasks.isEmpty()) {
+ mFrontMostTaskP = 0;
+ mMinScrollP = mMaxScrollP = 0;
+ mNumStackTasks = mNumFreeformTasks = 0;
+ return;
+ }
+
+ // Filter the set of freeform and stack tasks
+ ArrayList<Task> freeformTasks = new ArrayList<>();
+ ArrayList<Task> stackTasks = new ArrayList<>();
+ for (int i = 0; i < tasks.size(); i++) {
+ Task task = tasks.get(i);
+ if (task.isFreeformTask()) {
+ freeformTasks.add(task);
+ } else {
+ stackTasks.add(task);
+ }
+ }
+ mNumStackTasks = stackTasks.size();
+ mNumFreeformTasks = freeformTasks.size();
+
+ float pAtBackMostTaskTop = 0;
+ float pAtFrontMostTaskTop = pAtBackMostTaskTop;
+ if (!stackTasks.isEmpty()) {
+ // Update the for each task from back to front.
+ int taskCount = stackTasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ Task task = stackTasks.get(i);
+ mTaskProgressMap.put(task.key, pAtFrontMostTaskTop);
+
+ if (DEBUG) {
+ Log.d(TAG, "Update: " + task.activityLabel + " p: " + pAtFrontMostTaskTop);
+ }
+
+ if (i < (taskCount - 1)) {
+ // Increment the peek height
+ float pPeek = task.group == null || task.group.isFrontMostTask(task) ?
+ mBetweenAffiliationPOffset : mWithinAffiliationPOffset;
+ pAtFrontMostTaskTop += pPeek;
+ }
+ }
+
+ mFrontMostTaskP = pAtFrontMostTaskTop;
+ if (mNumStackTasks > 1) {
+ // Set the stack end scroll progress to the point at which the bottom of the front-most
+ // task is aligned to the bottom of the stack
+ mMaxScrollP = alignToStackBottom(pAtFrontMostTaskTop,
+ mStackBottomPOffset + (ssp.hasFreeformWorkspaceSupport() ?
+ mTaskHalfHeightPOffset : mTaskHeightPOffset));
+ // Basically align the back-most task such that the last two tasks would be visible
+ mMinScrollP = alignToStackBottom(pAtBackMostTaskTop,
+ mStackBottomPOffset + (ssp.hasFreeformWorkspaceSupport() ?
+ mTaskHalfHeightPOffset : mTaskHeightPOffset));
+ } else {
+ // When there is a single item, then just make all the stack progresses the same
+ mMinScrollP = mMaxScrollP = 0;
+ }
+ }
+
+ if (!freeformTasks.isEmpty()) {
+ mFreeformLayoutAlgorithm.update(freeformTasks, this);
+ mInitialScrollP = mMaxScrollP;
+ } else {
+ mInitialScrollP = Math.max(mMinScrollP, mMaxScrollP - mTaskHalfHeightPOffset);
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "mNumStackTasks: " + mNumStackTasks);
+ Log.d(TAG, "mNumFreeformTasks: " + mNumFreeformTasks);
+ Log.d(TAG, "mMinScrollP: " + mMinScrollP);
+ Log.d(TAG, "mMaxScrollP: " + mMaxScrollP);
+ }
+ }
+
+ /**
+ * Computes the maximum number of visible tasks and thumbnails. Requires that
+ * update() is called first.
+ */
+ public VisibilityReport computeStackVisibilityReport(ArrayList<Task> tasks) {
+ // Ensure minimum visibility count
+ if (tasks.size() <= 1) {
+ return new VisibilityReport(1, 1);
+ }
+
+ // Quick return when there are no stack tasks
+ if (mNumStackTasks == 0) {
+ return new VisibilityReport(Math.max(mNumFreeformTasks, 1),
+ Math.max(mNumFreeformTasks, 1));
+ }
+
+ // Otherwise, walk backwards in the stack and count the number of tasks and visible
+ // thumbnails and add that to the total freeform task count
+ int taskHeight = mTaskRect.height();
+ int taskBarHeight = mContext.getResources().getDimensionPixelSize(
+ R.dimen.recents_task_bar_height);
+ int numVisibleTasks = Math.max(mNumFreeformTasks, 1);
+ int numVisibleThumbnails = Math.max(mNumFreeformTasks, 1);
+ Task firstNonFreeformTask = tasks.get(tasks.size() - mNumFreeformTasks - 1);
+ float progress = mTaskProgressMap.get(firstNonFreeformTask.key) - mInitialScrollP;
+ int prevScreenY = sCurve.pToX(progress, mCurrentStackRect);
+ for (int i = tasks.size() - 2; i >= 0; i--) {
+ Task task = tasks.get(i);
+ if (task.isFreeformTask()) {
+ continue;
+ }
+
+ progress = mTaskProgressMap.get(task.key) - mInitialScrollP;
+ if (progress < 0) {
+ break;
+ }
+ boolean isFrontMostTaskInGroup = task.group == null || task.group.isFrontMostTask(task);
+ if (isFrontMostTaskInGroup) {
+ float scaleAtP = sCurve.pToScale(progress);
+ int scaleYOffsetAtP = (int) (((1f - scaleAtP) * taskHeight) / 2);
+ int screenY = sCurve.pToX(progress, mCurrentStackRect) + scaleYOffsetAtP;
+ boolean hasVisibleThumbnail = (prevScreenY - screenY) > taskBarHeight;
+ if (hasVisibleThumbnail) {
+ numVisibleThumbnails++;
+ numVisibleTasks++;
+ prevScreenY = screenY;
+ } else {
+ // Once we hit the next front most task that does not have a visible thumbnail,
+ // w alk through remaining visible set
+ for (int j = i; j >= 0; j--) {
+ numVisibleTasks++;
+ progress = mTaskProgressMap.get(tasks.get(j).key) - mInitialScrollP;
+ if (progress < 0) {
+ break;
+ }
+ }
+ break;
+ }
+ } else if (!isFrontMostTaskInGroup) {
+ // Affiliated task, no thumbnail
+ numVisibleTasks++;
+ }
+ }
+ return new VisibilityReport(numVisibleTasks, numVisibleThumbnails);
+ }
+
+ /**
+ * Returns the transform for the given task. This transform is relative to the mTaskRect, which
+ * is what the view is measured and laid out with.
+ */
+ public TaskViewTransform getStackTransform(Task task, float stackScroll,
+ TaskViewTransform transformOut, TaskViewTransform prevTransform) {
+ if (mFreeformLayoutAlgorithm.isTransformAvailable(task, this)) {
+ mFreeformLayoutAlgorithm.getTransform(task, transformOut, this);
+ return transformOut;
+ } else {
+ // Return early if we have an invalid index
+ if (task == null || !mTaskProgressMap.containsKey(task.key)) {
+ transformOut.reset();
+ return transformOut;
+ }
+ return getStackTransform(mTaskProgressMap.get(task.key), stackScroll, transformOut,
+ prevTransform);
+ }
+ }
+
+ /** Update/get the transform */
+ public TaskViewTransform getStackTransform(float taskProgress, float stackScroll,
+ TaskViewTransform transformOut, TaskViewTransform prevTransform) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+
+ if (!ssp.hasFreeformWorkspaceSupport() && mNumStackTasks == 1) {
+ // Center the task in the stack, changing the scale will not follow the curve, but just
+ // modulate some values directly
+ float pTaskRelative = mMinScrollP - stackScroll;
+ float scale = ssp.hasFreeformWorkspaceSupport() ? 1f : SINGLE_TASK_SCALE;
+ int topOffset = (mCurrentStackRect.top - mTaskRect.top) +
+ (mCurrentStackRect.height() - mTaskRect.height()) / 2;
+ transformOut.scale = scale;
+ transformOut.translationX = (mStackRect.width() - mTaskRect.width()) / 2;
+ transformOut.translationY = (int) (topOffset + (pTaskRelative * mCurrentStackRect.height()));
+ transformOut.translationZ = mMaxTranslationZ;
+ transformOut.rect.set(mTaskRect);
+ transformOut.rect.offset(transformOut.translationX, transformOut.translationY);
+ Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
+ transformOut.visible = true;
+ transformOut.p = pTaskRelative;
+ return transformOut;
+
+ } else {
+ float pTaskRelative = taskProgress - stackScroll;
+ float pBounded = Math.max(0, Math.min(pTaskRelative, 1f));
+ if (DEBUG) {
+ Log.d(TAG, "getStackTransform (normal): " + taskProgress + ", " + stackScroll);
+ }
+
+ // If the task top is outside of the bounds below the screen, then immediately reset it
+ if (pTaskRelative > 1f) {
+ transformOut.reset();
+ transformOut.rect.set(mTaskRect);
+ return transformOut;
+ }
+ // The check for the top is trickier, since we want to show the next task if it is at
+ // all visible, even if p < 0.
+ if (pTaskRelative < 0f) {
+ if (prevTransform != null && Float.compare(prevTransform.p, 0f) <= 0) {
+ transformOut.reset();
+ transformOut.rect.set(mTaskRect);
+ return transformOut;
+ }
+ }
+ float scale = sCurve.pToScale(pBounded);
+ int scaleYOffset = (int) (((1f - scale) * mTaskRect.height()) / 2);
+ transformOut.scale = scale;
+ transformOut.translationX = (mStackRect.width() - mTaskRect.width()) / 2;
+ transformOut.translationY = (mCurrentStackRect.top - mTaskRect.top) +
+ (sCurve.pToX(pBounded, mCurrentStackRect) - mCurrentStackRect.top) -
+ scaleYOffset;
+ transformOut.translationZ = Math.max(mMinTranslationZ,
+ mMinTranslationZ + (pBounded * (mMaxTranslationZ - mMinTranslationZ)));
+ transformOut.rect.set(mTaskRect);
+ transformOut.rect.offset(transformOut.translationX, transformOut.translationY);
+ Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
+ transformOut.visible = true;
+ transformOut.p = pTaskRelative;
+ if (DEBUG) {
+ Log.d(TAG, "\t" + transformOut);
+ }
+
+ return transformOut;
+ }
+ }
+
+ /**
+ * Returns the untransformed task view bounds.
+ */
+ public Rect getUntransformedTaskViewBounds() {
+ return new Rect(mTaskRect);
+ }
+
+ /**
+ * Returns the scroll progress to scroll to such that the top of the task is at the top of the
+ * stack.
+ */
+ float getStackScrollForTask(Task t) {
+ if (!mTaskProgressMap.containsKey(t.key)) return 0f;
+ return mTaskProgressMap.get(t.key);
+ }
+
+ /**
+ * Maps a movement in screen y, relative to {@param downY}, to a movement in along the arc
+ * length of the curve. We know the curve is mostly flat, so we just map the length of the
+ * screen along the arc-length proportionally (1/arclength).
+ */
+ public float getDeltaPForY(int downY, int y) {
+ float deltaP = (float) (y - downY) / mCurrentStackRect.height() * (1f / sCurve.getArcLength());
+ return -deltaP;
+ }
+
+ /**
+ * This is the inverse of {@link #getDeltaPForY}. Given a movement along the arc length
+ * of the curve, map back to the screen y.
+ */
+ public int getYForDeltaP(float downScrollP, float p) {
+ int y = (int) ((p - downScrollP) * mCurrentStackRect.height() * sCurve.getArcLength());
+ return -y;
+ }
+
+ private float alignToStackTop(float p) {
+ // At scroll progress == p, then p is at the top of the stack
+ return p;
+ }
+
+ private float alignToStackBottom(float p, float pOffsetFromBottom) {
+ // At scroll progress == p, then p is at the top of the stack
+ // At scroll progress == p + 1, then p is at the bottom of the stack
+ return p - (1 - pOffsetFromBottom);
+ }
+}
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 08e4e20..9c8829f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -20,26 +20,39 @@
import android.content.ComponentName;
import android.content.Context;
import android.graphics.Canvas;
-import android.graphics.Matrix;
import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
import android.widget.FrameLayout;
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.events.EventBus;
import com.android.systemui.recents.events.activity.PackagesChangedEvent;
-import com.android.systemui.recents.events.ui.DismissTaskEvent;
+import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
+import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
+import com.android.systemui.recents.events.ui.UserInteractionEvent;
+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;
+import com.android.systemui.recents.events.ui.dragndrop.DragStartInitializeDropTargetsEvent;
+import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent;
+import com.android.systemui.recents.events.ui.focus.FocusNextTaskViewEvent;
+import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent;
import com.android.systemui.recents.misc.DozeTrigger;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
-import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
@@ -50,55 +63,65 @@
import java.util.Iterator;
import java.util.List;
+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.INVALID_STACK_ID;
+
/* The visual representation of a task stack view */
public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCallbacks,
TaskView.TaskViewCallbacks, TaskStackViewScroller.TaskStackViewScrollerCallbacks,
ViewPool.ViewPoolConsumer<TaskView, Task> {
+ private final static String TAG = "TaskStackView";
+ private final static boolean DEBUG = false;
+
/** The TaskView callbacks */
interface TaskStackViewCallbacks {
public void onTaskViewClicked(TaskStackView stackView, TaskView tv, TaskStack stack, Task t,
- boolean lockToTask, boolean boundsValid, Rect bounds);
+ boolean lockToTask, Rect bounds, int destinationStack);
public void onAllTaskViewsDismissed(ArrayList<Task> removedTasks);
public void onTaskStackFilterTriggered();
public void onTaskStackUnfilterTriggered();
}
- RecentsConfiguration mConfig;
TaskStack mStack;
- TaskStackViewLayoutAlgorithm mLayoutAlgorithm;
+ TaskStackLayoutAlgorithm mLayoutAlgorithm;
TaskStackViewFilterAlgorithm mFilterAlgorithm;
TaskStackViewScroller mStackScroller;
TaskStackViewTouchHandler mTouchHandler;
TaskStackViewCallbacks mCb;
+ ColorDrawable mFreeformWorkspaceBackground;
ViewPool<TaskView, Task> mViewPool;
ArrayList<TaskViewTransform> mCurrentTaskTransforms = new ArrayList<>();
DozeTrigger mUIDozeTrigger;
int mFocusedTaskIndex = -1;
- int mPrevAccessibilityFocusedIndex = -1;
// Optimizations
int mStackViewsAnimationDuration;
boolean mStackViewsDirty = true;
boolean mStackViewsClipDirty = true;
boolean mAwaitingFirstLayout = true;
boolean mStartEnterAnimationRequestedAfterLayout;
- boolean mStartEnterAnimationCompleted;
ViewAnimation.TaskViewEnterContext mStartEnterAnimationContext;
+
Rect mTaskStackBounds = new Rect();
int[] mTmpVisibleRange = new int[2];
- float[] mTmpCoord = new float[2];
- Matrix mTmpMatrix = new Matrix();
Rect mTmpRect = new Rect();
+ RectF mTmpTaskRect = new RectF();
TaskViewTransform mTmpTransform = new TaskViewTransform();
+ TaskViewTransform mTmpStackBackTransform = new TaskViewTransform();
+ TaskViewTransform mTmpStackFrontTransform = new TaskViewTransform();
HashMap<Task, TaskView> mTmpTaskViewMap = new HashMap<>();
ArrayList<TaskView> mTaskViews = new ArrayList<>();
List<TaskView> mImmutableTaskViews = new ArrayList<>();
LayoutInflater mInflater;
boolean mLayersDisabled;
+ boolean mTouchExplorationEnabled;
+
+ Interpolator mFastOutSlowInInterpolator;
// A convenience update listener to request updating clipping of tasks
- ValueAnimator.AnimatorUpdateListener mRequestUpdateClippingListener =
+ private ValueAnimator.AnimatorUpdateListener mRequestUpdateClippingListener =
new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
@@ -106,18 +129,34 @@
}
};
+ // The drop targets for a task drag
+ private DropTarget mFreeformWorkspaceDropTarget = new DropTarget() {
+ @Override
+ public boolean acceptsDrop(int x, int y, int width, int height) {
+ return mLayoutAlgorithm.mFreeformRect.contains(x, y);
+ }
+ };
+
+ private DropTarget mStackDropTarget = new DropTarget() {
+ @Override
+ public boolean acceptsDrop(int x, int y, int width, int height) {
+ return mLayoutAlgorithm.mCurrentStackRect.contains(x, y);
+ }
+ };
+
public TaskStackView(Context context, TaskStack stack) {
super(context);
// Set the stack first
setStack(stack);
- mConfig = RecentsConfiguration.getInstance();
mViewPool = new ViewPool<>(context, this);
mInflater = LayoutInflater.from(context);
- mLayoutAlgorithm = new TaskStackViewLayoutAlgorithm(context, mConfig);
+ mLayoutAlgorithm = new TaskStackLayoutAlgorithm(context);
mFilterAlgorithm = new TaskStackViewFilterAlgorithm(this, mViewPool);
mStackScroller = new TaskStackViewScroller(context, mLayoutAlgorithm);
mStackScroller.setCallbacks(this);
mTouchHandler = new TaskStackViewTouchHandler(context, this, mStackScroller);
+ mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.fast_out_slow_in);
int taskBarDismissDozeDelaySeconds = getResources().getInteger(
R.integer.recents_task_bar_dismiss_delay_seconds);
@@ -134,6 +173,8 @@
}
});
setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+
+ mFreeformWorkspaceBackground = new ColorDrawable(0x33000000);
}
/** Sets the callbacks */
@@ -143,6 +184,8 @@
@Override
protected void onAttachedToWindow() {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ mTouchExplorationEnabled = ssp.isTouchExplorationEnabled();
EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
super.onAttachedToWindow();
}
@@ -187,6 +230,42 @@
return mImmutableTaskViews;
}
+ /**
+ * Returns the front most task view.
+ *
+ * @param stackTasksOnly if set, will return the front most task view in the stack (by default
+ * the front most task view will be freeform since they are placed above
+ * stack tasks)
+ */
+ private TaskView getFrontMostTaskView(boolean stackTasksOnly) {
+ List<TaskView> taskViews = getTaskViews();
+ int taskViewCount = taskViews.size();
+ for (int i = taskViewCount - 1; i >= 0; i--) {
+ TaskView tv = taskViews.get(i);
+ Task task = tv.getTask();
+ if (stackTasksOnly && task.isFreeformTask()) {
+ continue;
+ }
+ return tv;
+ }
+ return null;
+ }
+
+ /**
+ * Finds the child view given a specific {@param task}.
+ */
+ public TaskView getChildViewForTask(Task t) {
+ List<TaskView> taskViews = getTaskViews();
+ int taskViewCount = taskViews.size();
+ for (int i = 0; i < taskViewCount; i++) {
+ TaskView tv = taskViews.get(i);
+ if (tv.getTask() == t) {
+ return tv;
+ }
+ }
+ return null;
+ }
+
/** Resets this TaskStackView for reuse. */
void reset() {
// Reset the focused task
@@ -215,7 +294,6 @@
mStackViewsDirty = true;
mStackViewsClipDirty = true;
mAwaitingFirstLayout = true;
- mPrevAccessibilityFocusedIndex = -1;
if (mUIDozeTrigger != null) {
mUIDozeTrigger.stopDozing();
mUIDozeTrigger.resetTrigger();
@@ -248,26 +326,14 @@
}
}
- /** Finds the child view given a specific task. */
- public TaskView getChildViewForTask(Task t) {
- List<TaskView> taskViews = getTaskViews();
- int taskViewCount = taskViews.size();
- for (int i = 0; i < taskViewCount; i++) {
- TaskView tv = taskViews.get(i);
- if (tv.getTask() == t) {
- return tv;
- }
- }
- return null;
- }
-
/** Returns the stack algorithm for this task stack. */
- public TaskStackViewLayoutAlgorithm getStackAlgorithm() {
+ public TaskStackLayoutAlgorithm getStackAlgorithm() {
return mLayoutAlgorithm;
}
/**
* Gets the stack transforms of a list of tasks, and returns the visible range of tasks.
+ * This call ignores freeform tasks.
*/
private boolean updateStackTransforms(ArrayList<TaskViewTransform> taskTransforms,
ArrayList<Task> tasks,
@@ -293,8 +359,16 @@
// Update the stack transforms
TaskViewTransform prevTransform = null;
for (int i = taskCount - 1; i >= 0; i--) {
- TaskViewTransform transform = mLayoutAlgorithm.getStackTransform(tasks.get(i),
- stackScroll, taskTransforms.get(i), prevTransform);
+ Task task = tasks.get(i);
+ if (task.isFreeformTask()) {
+ continue;
+ }
+
+ TaskViewTransform transform = mLayoutAlgorithm.getStackTransform(task, stackScroll,
+ taskTransforms.get(i), prevTransform);
+ if (DEBUG) {
+ Log.d(TAG, "updateStackTransform: " + i + ", " + transform.visible);
+ }
if (transform.visible) {
if (frontMostVisibleIndex < 0) {
frontMostVisibleIndex = i;
@@ -314,7 +388,7 @@
if (boundTranslationsToRect) {
transform.translationY = Math.min(transform.translationY,
- mLayoutAlgorithm.mStackRect.bottom);
+ mLayoutAlgorithm.mCurrentStackRect.bottom);
}
prevTransform = transform;
}
@@ -328,39 +402,92 @@
/** Synchronizes the views with the model */
boolean synchronizeStackViewsWithModel() {
if (mStackViewsDirty) {
- RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
- SystemServicesProxy ssp = loader.getSystemServicesProxy();
-
// Get all the task transforms
ArrayList<Task> tasks = mStack.getTasks();
float stackScroll = mStackScroller.getStackScroll();
- int[] visibleRange = mTmpVisibleRange;
- boolean isValidVisibleRange = updateStackTransforms(mCurrentTaskTransforms, tasks,
- stackScroll, visibleRange, false);
+ int[] visibleStackRange = mTmpVisibleRange;
+ boolean isValidVisibleStackRange = updateStackTransforms(mCurrentTaskTransforms, tasks,
+ stackScroll, visibleStackRange, false);
+ boolean hasStackBackTransform = false;
+ boolean hasStackFrontTransform = false;
+ if (DEBUG) {
+ Log.d(TAG, "visibleRange: " + visibleStackRange[0] + " to " + visibleStackRange[1]);
+ }
// Return all the invisible children to the pool
mTmpTaskViewMap.clear();
List<TaskView> taskViews = getTaskViews();
+ boolean wasLastFocusedTaskAnimated = false;
+ int lastFocusedTaskIndex = -1;
int taskViewCount = taskViews.size();
- boolean reaquireAccessibilityFocus = false;
for (int i = taskViewCount - 1; i >= 0; i--) {
TaskView tv = taskViews.get(i);
Task task = tv.getTask();
int taskIndex = mStack.indexOfTask(task);
- if (visibleRange[1] <= taskIndex && taskIndex <= visibleRange[0]) {
+ if (task.isFreeformTask() ||
+ visibleStackRange[1] <= taskIndex && taskIndex <= visibleStackRange[0]) {
mTmpTaskViewMap.put(task, tv);
} else {
+ if (mTouchExplorationEnabled && tv.isFocusedTask()) {
+ wasLastFocusedTaskAnimated = tv.isFocusAnimated();
+ lastFocusedTaskIndex = taskIndex;
+ resetFocusedTask();
+ }
+ if (DEBUG) {
+ Log.d(TAG, "returning to pool: " + task.key);
+ }
mViewPool.returnViewToPool(tv);
- reaquireAccessibilityFocus |= (i == mPrevAccessibilityFocusedIndex);
}
}
+ // Pick up all the freeform tasks
+ int firstVisStackIndex = isValidVisibleStackRange ? visibleStackRange[0] : 0;
+ for (int i = mStack.getTaskCount() - 1; i >= firstVisStackIndex; i--) {
+ Task task = tasks.get(i);
+ if (!task.isFreeformTask()) {
+ continue;
+ }
+ TaskViewTransform transform = mLayoutAlgorithm.getStackTransform(task, stackScroll,
+ mCurrentTaskTransforms.get(i), null);
+ TaskView tv = mTmpTaskViewMap.get(task);
+ if (tv == null) {
+ if (DEBUG) {
+ Log.d(TAG, "picking up from pool: " + task.key);
+ }
+ tv = mViewPool.pickUpViewFromPool(task, task);
+ if (mLayersDisabled) {
+ tv.disableLayersForOneFrame();
+ }
+ }
+
+ // Animate the task into place
+ tv.updateViewPropertiesToTaskTransform(transform,
+ mStackViewsAnimationDuration, mRequestUpdateClippingListener);
+
+ // Reattach it in the right z order
+ detachViewFromParent(tv);
+ int insertIndex = -1;
+ int taskIndex = mStack.indexOfTask(task);
+ taskViews = getTaskViews();
+ taskViewCount = taskViews.size();
+ for (int j = 0; j < taskViewCount; j++) {
+ Task tvTask = taskViews.get(j).getTask();
+ if (taskIndex < mStack.indexOfTask(tvTask)) {
+ insertIndex = j;
+ break;
+ }
+ }
+ attachViewToParent(tv, insertIndex, tv.getLayoutParams());
+
+ // Update the task views list after adding the new task view
+ updateTaskViewsList();
+ }
+
// Pick up all the newly visible children and update all the existing children
- for (int i = visibleRange[0]; isValidVisibleRange && i >= visibleRange[1]; i--) {
+ for (int i = visibleStackRange[0]; isValidVisibleStackRange && i >= visibleStackRange[1]; i--) {
Task task = tasks.get(i);
TaskViewTransform transform = mCurrentTaskTransforms.get(i);
TaskView tv = mTmpTaskViewMap.get(task);
- int taskIndex = mStack.indexOfTask(task);
if (tv == null) {
tv = mViewPool.pickUpViewFromPool(task, task);
@@ -371,32 +498,34 @@
// For items in the list, put them in start animating them from the
// approriate ends of the list where they are expected to appear
if (Float.compare(transform.p, 0f) <= 0) {
- mLayoutAlgorithm.getStackTransform(0f, 0f, mTmpTransform, null);
+ if (!hasStackBackTransform) {
+ hasStackBackTransform = true;
+ mLayoutAlgorithm.getStackTransform(0f, 0f, mTmpStackBackTransform,
+ null);
+ }
+ tv.updateViewPropertiesToTaskTransform(mTmpStackBackTransform, 0);
} else {
- mLayoutAlgorithm.getStackTransform(1f, 0f, mTmpTransform, null);
+ if (!hasStackFrontTransform) {
+ hasStackFrontTransform = true;
+ mLayoutAlgorithm.getStackTransform(1f, 0f, mTmpStackFrontTransform,
+ null);
+ }
+ tv.updateViewPropertiesToTaskTransform(mTmpStackFrontTransform, 0);
}
- tv.updateViewPropertiesToTaskTransform(mTmpTransform, 0);
}
}
// Animate the task into place
- tv.updateViewPropertiesToTaskTransform(mCurrentTaskTransforms.get(taskIndex),
+ tv.updateViewPropertiesToTaskTransform(transform,
mStackViewsAnimationDuration, mRequestUpdateClippingListener);
+ }
- // Request accessibility focus on the next view if we removed the task
- // that previously held accessibility focus
- if (reaquireAccessibilityFocus) {
- taskViews = getTaskViews();
- taskViewCount = taskViews.size();
- if (taskViewCount > 0 && ssp.isTouchExplorationEnabled() &&
- mPrevAccessibilityFocusedIndex != -1) {
- TaskView atv = taskViews.get(taskViewCount - 1);
- int indexOfTask = mStack.indexOfTask(atv.getTask());
- if (mPrevAccessibilityFocusedIndex != indexOfTask) {
- tv.requestAccessibilityFocus();
- mPrevAccessibilityFocusedIndex = indexOfTask;
- }
- }
+ // Update the focus if the previous focused task was returned to the view pool
+ if (lastFocusedTaskIndex != -1) {
+ if (lastFocusedTaskIndex < visibleStackRange[1]) {
+ setFocusedTask(visibleStackRange[1], false, wasLastFocusedTaskAnimated);
+ } else {
+ setFocusedTask(visibleStackRange[0], false, wasLastFocusedTaskAnimated);
}
}
@@ -409,23 +538,24 @@
return false;
}
- /** Updates the clip for each of the task views. */
- void clipTaskViews() {
+ /**
+ * Updates the clip for each of the task views from back to front.
+ */
+ void clipTaskViews(boolean forceUpdate) {
// Update the clip on each task child
List<TaskView> taskViews = getTaskViews();
+ TaskView tmpTv = null;
int taskViewCount = taskViews.size();
- for (int i = 0; i < taskViewCount - 1; i++) {
+ for (int i = 0; i < taskViewCount; i++) {
TaskView tv = taskViews.get(i);
- TaskView nextTv = null;
- TaskView tmpTv = null;
+ TaskView frontTv = null;
int clipBottom = 0;
- if (tv.shouldClipViewInStack()) {
+ if (i < (taskViewCount - 1) && tv.shouldClipViewInStack()) {
// Find the next view to clip against
- int nextIndex = i;
- while (nextIndex < (taskViewCount - 1)) {
- tmpTv = taskViews.get(++nextIndex);
- if (tmpTv != null && tmpTv.shouldClipViewInStack()) {
- nextTv = tmpTv;
+ for (int j = i + 1; j < taskViewCount; j++) {
+ tmpTv = taskViews.get(j);
+ if (tmpTv.shouldClipViewInStack()) {
+ frontTv = tmpTv;
break;
}
}
@@ -433,30 +563,38 @@
// Clip against the next view, this is just an approximation since we are
// stacked and we can make assumptions about the visibility of the this
// task relative to the ones in front of it.
- if (nextTv != null) {
- // Map the top edge of next task view into the local space of the current
- // task view to find the clip amount in local space
- mTmpCoord[0] = mTmpCoord[1] = 0;
- Utilities.mapCoordInDescendentToSelf(nextTv, this, mTmpCoord, false);
- Utilities.mapCoordInSelfToDescendent(tv, this, mTmpCoord, mTmpMatrix);
- clipBottom = (int) Math.floor(tv.getMeasuredHeight() - mTmpCoord[1]
- - nextTv.getPaddingTop() - 1);
+ if (frontTv != null) {
+ mTmpTaskRect.set(mLayoutAlgorithm.mTaskRect);
+ mTmpTaskRect.offset(0, tv.getTranslationY());
+ Utilities.scaleRectAboutCenter(mTmpTaskRect, tv.getScaleX());
+ float taskBottom = mTmpTaskRect.bottom;
+ mTmpTaskRect.set(mLayoutAlgorithm.mTaskRect);
+ mTmpTaskRect.offset(0, frontTv.getTranslationY());
+ Utilities.scaleRectAboutCenter(mTmpTaskRect, frontTv.getScaleX());
+ float frontTaskTop = mTmpTaskRect.top;
+ if (frontTaskTop < taskBottom) {
+ // Map the stack view space coordinate (the rects) to view space
+ clipBottom = (int) ((taskBottom - frontTaskTop) / tv.getScaleX()) - 1;
+ }
}
}
- tv.getViewBounds().setClipBottom(clipBottom);
- }
- if (taskViewCount > 0) {
- // The front most task should never be clipped
- TaskView tv = taskViews.get(taskViewCount - 1);
- tv.getViewBounds().setClipBottom(0);
+ tv.getViewBounds().setClipBottom(clipBottom, forceUpdate);
}
mStackViewsClipDirty = false;
}
/** Updates the min and max virtual scroll bounds */
- void updateMinMaxScroll(boolean boundScrollToNewMinMax) {
+ void updateLayout(boolean boundScrollToNewMinMax) {
// Compute the min and max scroll values
- mLayoutAlgorithm.computeMinMaxScroll(mStack.getTasks());
+ mLayoutAlgorithm.update(mStack);
+
+ // Update the freeform workspace
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ if (ssp.hasFreeformWorkspaceSupport()) {
+ mTmpRect.set(mLayoutAlgorithm.mFreeformRect);
+ mFreeformWorkspaceBackground.setAlpha(255);
+ mFreeformWorkspaceBackground.setBounds(mTmpRect);
+ }
// Debug logging
if (boundScrollToNewMinMax) {
@@ -469,118 +607,126 @@
return mStackScroller;
}
- /** Focuses the task at the specified index in the stack */
- void focusTask(int taskIndex, boolean scrollToNewPosition, final boolean animateFocusedState) {
- // Return early if the task is already focused
- if (taskIndex == mFocusedTaskIndex) return;
+ /**
+ * Sets the focused task to the provided (bounded taskIndex).
+ */
+ private void setFocusedTask(int taskIndex, boolean scrollToTask, final boolean animated) {
+ setFocusedTask(taskIndex, scrollToTask, animated, true);
+ }
- if (0 <= taskIndex && taskIndex < mStack.getTaskCount()) {
- mFocusedTaskIndex = taskIndex;
- mPrevAccessibilityFocusedIndex = taskIndex;
+ /**
+ * Sets the focused task to the provided (bounded taskIndex).
+ */
+ private void setFocusedTask(int taskIndex, boolean scrollToTask, final boolean animated,
+ final boolean requestViewFocus) {
+ // Find the next task to focus
+ int newFocusedTaskIndex = mStack.getTaskCount() > 0 ?
+ Math.max(0, Math.min(mStack.getTaskCount() - 1, taskIndex)) : -1;
+ final Task newFocusedTask = (newFocusedTaskIndex != -1) ?
+ mStack.getTasks().get(newFocusedTaskIndex) : null;
- // Focus the view if possible, otherwise, focus the view after we scroll into position
- final Task t = mStack.getTasks().get(mFocusedTaskIndex);
- Runnable postScrollRunnable = new Runnable() {
+ // Reset the last focused task state if changed
+ if (mFocusedTaskIndex != -1) {
+ Task focusedTask = mStack.getTasks().get(mFocusedTaskIndex);
+ if (focusedTask != newFocusedTask) {
+ resetFocusedTask();
+ }
+ }
+
+ mFocusedTaskIndex = newFocusedTaskIndex;
+ if (mFocusedTaskIndex != -1) {
+ Runnable focusTaskRunnable = new Runnable() {
@Override
public void run() {
- TaskView tv = getChildViewForTask(t);
+ TaskView tv = getChildViewForTask(newFocusedTask);
if (tv != null) {
- tv.setFocusedTask(animateFocusedState);
- tv.requestAccessibilityFocus();
+ tv.setFocusedState(true, animated, requestViewFocus);
}
}
};
- // Scroll the view into position (just center it in the curve)
- if (scrollToNewPosition) {
- float newScroll = mLayoutAlgorithm.getStackScrollForTask(t) - 0.5f;
+ if (scrollToTask) {
+ // TODO: Center the newly focused task view, only if not freeform
+ float newScroll = mLayoutAlgorithm.getStackScrollForTask(newFocusedTask) - 0.5f;
newScroll = mStackScroller.getBoundedStackScroll(newScroll);
- mStackScroller.animateScroll(mStackScroller.getStackScroll(), newScroll, postScrollRunnable);
+ mStackScroller.animateScroll(mStackScroller.getStackScroll(), newScroll,
+ focusTaskRunnable);
} else {
- if (postScrollRunnable != null) {
- postScrollRunnable.run();
- }
+ focusTaskRunnable.run();
}
-
}
}
/**
- * Ensures that there is a task focused, if nothing is focused, then we will use the task
- * at the center of the visible stack.
+ * Sets the focused task relative to the currently focused task.
+ *
+ * @param stackTasksOnly if set, will ensure that the traversal only goes along stack tasks, and
+ * if the currently focused task is not a stack task, will set the focus
+ * to the first visible stack task
+ * @param animated determines whether to actually draw the highlight along with the change in
+ * focus.
*/
- public boolean ensureFocusedTask(boolean findClosestToCenter) {
- if (mFocusedTaskIndex < 0) {
- List<TaskView> taskViews = getTaskViews();
- int taskViewCount = taskViews.size();
- if (findClosestToCenter) {
- // If there is no task focused, then find the task that is closes to the center
- // of the screen and use that as the currently focused task
- int x = mLayoutAlgorithm.mStackRect.centerX();
- int y = mLayoutAlgorithm.mStackRect.centerY();
- for (int i = taskViewCount - 1; i >= 0; i--) {
- TaskView tv = taskViews.get(i);
- tv.getHitRect(mTmpRect);
- if (mTmpRect.contains(x, y)) {
- mFocusedTaskIndex = mStack.indexOfTask(tv.getTask());
- mPrevAccessibilityFocusedIndex = mFocusedTaskIndex;
- break;
+ public void setRelativeFocusedTask(boolean forward, boolean stackTasksOnly, boolean animated) {
+ int newIndex = -1;
+ if (mFocusedTaskIndex != -1) {
+ if (stackTasksOnly) {
+ List<Task> tasks = mStack.getTasks();
+ newIndex = mFocusedTaskIndex;
+ Task task = tasks.get(mFocusedTaskIndex);
+ if (task.isFreeformTask()) {
+ // Try and focus the front most stack task
+ TaskView tv = getFrontMostTaskView(stackTasksOnly);
+ if (tv != null) {
+ newIndex = mStack.indexOfTask(tv.getTask());
+ }
+ } else {
+ // Try the next task if it is a stack task
+ int tmpNewIndex = mFocusedTaskIndex + (forward ? -1 : 1);
+ if (0 <= tmpNewIndex && tmpNewIndex < tasks.size()) {
+ Task t = tasks.get(tmpNewIndex);
+ if (!t.isFreeformTask()) {
+ newIndex = tmpNewIndex;
+ }
}
}
+ } else {
+ // No restrictions, lets just move to the new task
+ newIndex = mFocusedTaskIndex + (forward ? -1 : 1);
}
- // If we can't find the center task, then use the front most index
- if (mFocusedTaskIndex < 0 && taskViewCount > 0) {
- TaskView tv = taskViews.get(taskViewCount - 1);
- mFocusedTaskIndex = mStack.indexOfTask(tv.getTask());
- mPrevAccessibilityFocusedIndex = mFocusedTaskIndex;
+ } else {
+ // We don't have a focused task, so focus the first visible task view
+ TaskView tv = getFrontMostTaskView(stackTasksOnly);
+ if (tv != null) {
+ newIndex = mStack.indexOfTask(tv.getTask());
}
}
- return mFocusedTaskIndex >= 0;
+ if (newIndex != -1) {
+ setFocusedTask(newIndex, true, animated);
+ }
}
/**
- * Focuses the next task in the stack.
- * @param animateFocusedState determines whether to actually draw the highlight along with
- * the change in focus, as well as whether to scroll to fit the
- * task into view.
+ * Resets the focused task.
*/
- public void focusNextTask(boolean forward, boolean animateFocusedState) {
- // Find the next index to focus
- int numTasks = mStack.getTaskCount();
- if (numTasks == 0) return;
-
- int direction = (forward ? -1 : 1);
- int newIndex = mFocusedTaskIndex + direction;
- if (newIndex >= 0 && newIndex <= (numTasks - 1)) {
- newIndex = Math.max(0, Math.min(numTasks - 1, newIndex));
- focusTask(newIndex, true, animateFocusedState);
- }
- }
-
- /** Dismisses the focused task. */
- public void dismissFocusedTask() {
- // Return early if the focused task index is invalid
- if (mFocusedTaskIndex < 0 || mFocusedTaskIndex >= mStack.getTaskCount()) {
- mFocusedTaskIndex = -1;
- return;
- }
-
- Task t = mStack.getTasks().get(mFocusedTaskIndex);
- TaskView tv = getChildViewForTask(t);
- tv.dismissTask();
- }
-
- /** Resets the focused task. */
void resetFocusedTask() {
- if ((0 <= mFocusedTaskIndex) && (mFocusedTaskIndex < mStack.getTaskCount())) {
+ if (mFocusedTaskIndex != -1) {
Task t = mStack.getTasks().get(mFocusedTaskIndex);
TaskView tv = getChildViewForTask(t);
if (tv != null) {
- tv.unsetFocusedTask();
+ tv.setFocusedState(false, false /* animated */, false /* requestViewFocus */);
}
}
mFocusedTaskIndex = -1;
- mPrevAccessibilityFocusedIndex = -1;
+ }
+
+ /**
+ * Returns the focused task.
+ */
+ Task getFocusedTask() {
+ if (mFocusedTaskIndex != -1) {
+ return mStack.getTasks().get(mFocusedTaskIndex);
+ }
+ return null;
}
@Override
@@ -605,12 +751,12 @@
super.onInitializeAccessibilityNodeInfo(info);
List<TaskView> taskViews = getTaskViews();
int taskViewCount = taskViews.size();
- if (taskViewCount > 1 && mPrevAccessibilityFocusedIndex != -1) {
+ if (taskViewCount > 1 && mFocusedTaskIndex != -1) {
info.setScrollable(true);
- if (mPrevAccessibilityFocusedIndex > 0) {
+ if (mFocusedTaskIndex > 0) {
info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
}
- if (mPrevAccessibilityFocusedIndex < mStack.getTaskCount() - 1) {
+ if (mFocusedTaskIndex < mStack.getTaskCount() - 1) {
info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
}
}
@@ -626,22 +772,14 @@
if (super.performAccessibilityAction(action, arguments)) {
return true;
}
- if (ensureFocusedTask(false)) {
- switch (action) {
- case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
- if (mPrevAccessibilityFocusedIndex > 0) {
- focusNextTask(true, false);
- return true;
- }
- }
- break;
- case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
- if (mPrevAccessibilityFocusedIndex < mStack.getTaskCount() - 1) {
- focusNextTask(false, false);
- return true;
- }
- }
- break;
+ switch (action) {
+ case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
+ setRelativeFocusedTask(true, false /* stackTasksOnly */, false /* animated */);
+ return true;
+ }
+ case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
+ setRelativeFocusedTask(false, false /* stackTasksOnly */, false /* animated */);
+ return true;
}
}
return false;
@@ -667,35 +805,34 @@
mStackScroller.computeScroll();
// Synchronize the views
synchronizeStackViewsWithModel();
- clipTaskViews();
+ clipTaskViews(false /* forceUpdate */);
// Notify accessibility
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SCROLLED);
}
/** Computes the stack and task rects */
- public void computeRects(int windowWidth, int windowHeight, Rect taskStackBounds,
- boolean launchedWithAltTab, boolean launchedFromHome) {
+ public void computeRects(Rect taskStackBounds) {
// Compute the rects in the stack algorithm
- mLayoutAlgorithm.computeRects(windowWidth, windowHeight, taskStackBounds);
+ mLayoutAlgorithm.initialize(taskStackBounds);
// Update the scroll bounds
- updateMinMaxScroll(false);
+ updateLayout(false);
}
/**
- * This is ONLY used from AlternateRecentsComponent to update the dummy stack view for purposes
+ * This is ONLY used from the Recents component to update the dummy stack view for purposes
* of getting the task rect to animate to.
*/
- public void updateMinMaxScrollForStack(TaskStack stack) {
+ public void updateLayoutForStack(TaskStack stack) {
mStack = stack;
- updateMinMaxScroll(false);
+ updateLayout(false);
}
/**
* Computes the maximum number of visible tasks and thumbnails. Requires that
- * updateMinMaxScrollForStack() is called first.
+ * updateLayoutForStack() is called first.
*/
- public TaskStackViewLayoutAlgorithm.VisibilityReport computeStackVisibilityReport() {
+ public TaskStackLayoutAlgorithm.VisibilityReport computeStackVisibilityReport() {
return mLayoutAlgorithm.computeStackVisibilityReport(mStack.getTasks());
}
@@ -714,9 +851,7 @@
int height = MeasureSpec.getSize(heightMeasureSpec);
// Compute our stack/task rects
- RecentsActivityLaunchState launchState = mConfig.getLaunchState();
- computeRects(width, height, mTaskStackBounds, launchState.launchedWithAltTab,
- launchState.launchedFromHome);
+ computeRects(mTaskStackBounds);
// If this is the first layout, then scroll to the front of the stack and synchronize the
// stack views immediately to load all the views
@@ -737,12 +872,12 @@
mTmpRect.setEmpty();
}
tv.measure(
- MeasureSpec.makeMeasureSpec(
- mLayoutAlgorithm.mTaskRect.width() + mTmpRect.left + mTmpRect.right,
- MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(
- mLayoutAlgorithm.mTaskRect.height() + mTmpRect.top + mTmpRect.bottom,
- MeasureSpec.EXACTLY));
+ MeasureSpec.makeMeasureSpec(
+ mLayoutAlgorithm.mTaskRect.width() + mTmpRect.left + mTmpRect.right,
+ MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(
+ mLayoutAlgorithm.mTaskRect.height() + mTmpRect.top + mTmpRect.bottom,
+ MeasureSpec.EXACTLY));
}
setMeasuredDimension(width, height);
@@ -755,7 +890,7 @@
*/
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- // Layout each of the children
+ // Layout each of the TaskViews
List<TaskView> taskViews = getTaskViews();
int taskViewCount = taskViews.size();
for (int i = 0; i < taskViewCount; i++) {
@@ -765,34 +900,31 @@
} else {
mTmpRect.setEmpty();
}
- tv.layout(mLayoutAlgorithm.mTaskRect.left - mTmpRect.left,
- mLayoutAlgorithm.mTaskRect.top - mTmpRect.top,
- mLayoutAlgorithm.mTaskRect.right + mTmpRect.right,
- mLayoutAlgorithm.mTaskRect.bottom + mTmpRect.bottom);
+ Rect taskRect = mLayoutAlgorithm.mTaskRect;
+ tv.layout(taskRect.left - mTmpRect.left, taskRect.top - mTmpRect.top,
+ taskRect.right + mTmpRect.right, taskRect.bottom + mTmpRect.bottom);
}
if (mAwaitingFirstLayout) {
mAwaitingFirstLayout = false;
onFirstLayout();
}
+
+ if (changed) {
+ requestSynchronizeStackViewsWithModel();
+ synchronizeStackViewsWithModel();
+ clipTaskViews(true /* forceUpdate */);
+ }
}
/** Handler for the first layout. */
void onFirstLayout() {
- int offscreenY = mLayoutAlgorithm.mStackRect.bottom;
+ int offscreenY = mLayoutAlgorithm.mCurrentStackRect.bottom;
// Find the launch target task
- Task launchTargetTask = null;
+ Task launchTargetTask = mStack.getLaunchTarget();
List<TaskView> taskViews = getTaskViews();
int taskViewCount = taskViews.size();
- for (int i = taskViewCount - 1; i >= 0; i--) {
- TaskView tv = taskViews.get(i);
- Task task = tv.getTask();
- if (task.isLaunchTarget) {
- launchTargetTask = task;
- break;
- }
- }
// Prepare the first view for its enter animation
for (int i = taskViewCount - 1; i >= 0; i--) {
@@ -800,7 +932,8 @@
Task task = tv.getTask();
boolean occludesLaunchTarget = (launchTargetTask != null) &&
launchTargetTask.group.isTaskAboveTask(task, launchTargetTask);
- tv.prepareEnterRecentsAnimation(task.isLaunchTarget, occludesLaunchTarget, offscreenY);
+ tv.prepareEnterRecentsAnimation(task.isLaunchTarget, occludesLaunchTarget,
+ offscreenY);
}
// If the enter animation started already and we haven't completed a layout yet, do the
@@ -811,17 +944,14 @@
mStartEnterAnimationContext = null;
}
- // When Alt-Tabbing, focus the previous task (but leave the animation until we finish the
- // enter animation).
- RecentsActivityLaunchState launchState = mConfig.getLaunchState();
- if (launchState.launchedWithAltTab) {
- if (launchState.launchedFromAppWithThumbnail) {
- focusTask(Math.max(0, mStack.getTaskCount() - 2), false,
- launchState.launchedHasConfigurationChanged);
- } else {
- focusTask(Math.max(0, mStack.getTaskCount() - 1), false,
- launchState.launchedHasConfigurationChanged);
- }
+ // Set the task focused state without requesting view focus, and leave the focus animations
+ // until after the enter-animation
+ RecentsConfiguration config = Recents.getConfiguration();
+ RecentsActivityLaunchState launchState = config.getLaunchState();
+ int focusedTaskIndex = launchState.getInitialFocusTaskIndex(mStack.getTaskCount());
+ if (focusedTaskIndex != -1) {
+ setFocusedTask(focusedTaskIndex, false /* scrollToTask */, false /* animated */,
+ false /* requestViewFocus */);
}
// Start dozing
@@ -839,17 +969,9 @@
if (mStack.getTaskCount() > 0) {
// Find the launch target task
- Task launchTargetTask = null;
+ Task launchTargetTask = mStack.getLaunchTarget();
List<TaskView> taskViews = getTaskViews();
int taskViewCount = taskViews.size();
- for (int i = taskViewCount - 1; i >= 0; i--) {
- TaskView tv = taskViews.get(i);
- Task task = tv.getTask();
- if (task.isLaunchTarget) {
- launchTargetTask = task;
- break;
- }
- }
// Animate all the task views into view
for (int i = taskViewCount - 1; i >= 0; i--) {
@@ -862,7 +984,8 @@
ctx.currentTaskOccludesLaunchTarget = (launchTargetTask != null) &&
launchTargetTask.group.isTaskAboveTask(task, launchTargetTask);
ctx.updateListener = mRequestUpdateClippingListener;
- mLayoutAlgorithm.getStackTransform(task, mStackScroller.getStackScroll(), ctx.currentTaskTransform, null);
+ mLayoutAlgorithm.getStackTransform(task, mStackScroller.getStackScroll(),
+ ctx.currentTaskTransform, null);
tv.startEnterRecentsAnimation(ctx);
}
@@ -870,33 +993,18 @@
ctx.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
@Override
public void run() {
- mStartEnterAnimationCompleted = true;
// Poke the dozer to restart the trigger after the animation completes
mUIDozeTrigger.poke();
- RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
- SystemServicesProxy ssp = loader.getSystemServicesProxy();
- List<TaskView> taskViews = getTaskViews();
- int taskViewCount = taskViews.size();
- if (taskViewCount > 0) {
- // Focus the first view if accessibility is enabled
- if (ssp.isTouchExplorationEnabled()) {
- TaskView tv = taskViews.get(taskViewCount - 1);
- tv.requestAccessibilityFocus();
- mPrevAccessibilityFocusedIndex = mStack.indexOfTask(tv.getTask());
- }
- }
-
- // Start the focus animation when alt-tabbing
- ArrayList<Task> tasks = mStack.getTasks();
- RecentsActivityLaunchState launchState = mConfig.getLaunchState();
- if (launchState.launchedWithAltTab &&
- !launchState.launchedHasConfigurationChanged &&
- 0 <= mFocusedTaskIndex && mFocusedTaskIndex < tasks.size()) {
- TaskView tv = getChildViewForTask(tasks.get(mFocusedTaskIndex));
- if (tv != null) {
- tv.setFocusedTask(true);
- }
+ // Update the focused state here -- since we only set the focused task without
+ // requesting view focus in onFirstLayout(), actually request view focus and
+ // animate the focused state if we are alt-tabbing now, after the window enter
+ // animation is completed
+ if (mFocusedTaskIndex != -1) {
+ RecentsConfiguration config = Recents.getConfiguration();
+ RecentsActivityLaunchState launchState = config.getLaunchState();
+ setFocusedTask(mFocusedTaskIndex, false /* scrollToTask */,
+ launchState.launchedWithAltTab);
}
}
});
@@ -909,7 +1017,7 @@
mStackScroller.stopScroller();
mStackScroller.stopBoundScrollAnimation();
// Animate all the task views out of view
- ctx.offscreenTranslationY = mLayoutAlgorithm.mStackRect.bottom;
+ ctx.offscreenTranslationY = mLayoutAlgorithm.mCurrentStackRect.bottom;
List<TaskView> taskViews = getTaskViews();
int taskViewCount = taskViews.size();
@@ -951,24 +1059,19 @@
}
}
- /** Final callback after Recents is finally hidden. */
- void onRecentsHidden() {
- reset();
- }
-
public boolean isTransformedTouchPointInView(float x, float y, View child) {
return isTransformedTouchPointInView(x, y, child, null);
}
- /** Pokes the dozer on user interaction. */
- void onUserInteraction() {
- // Poke the doze trigger if it is dozing
- mUIDozeTrigger.poke();
- }
-
@Override
protected void dispatchDraw(Canvas canvas) {
mLayersDisabled = false;
+
+ // Draw the freeform workspace background
+ if (mFreeformWorkspaceBackground.getAlpha() > 0) {
+ mFreeformWorkspaceBackground.draw(canvas);
+ }
+
super.dispatchDraw(canvas);
}
@@ -980,6 +1083,18 @@
}
}
+ /**
+ * Launches the freeform tasks.
+ */
+ public boolean launchFreeformTasks() {
+ Task frontTask = mStack.getFrontMostTask();
+ if (frontTask != null && frontTask.isFreeformTask()) {
+ onTaskViewClicked(getChildViewForTask(frontTask), frontTask, false);
+ return true;
+ }
+ return false;
+ }
+
/**** TaskStackCallbacks Implementation ****/
@Override
@@ -990,42 +1105,57 @@
@Override
public void onStackTaskRemoved(TaskStack stack, Task removedTask, boolean wasFrontMostTask,
Task newFrontMostTask) {
- // Remove the view associated with this task, we can't rely on updateTransforms
- // to work here because the task is no longer in the list
- TaskView tv = getChildViewForTask(removedTask);
- if (tv != null) {
- mViewPool.returnViewToPool(tv);
+ if (!removedTask.isFreeformTask()) {
+ // Remove the view associated with this task, we can't rely on updateTransforms
+ // to work here because the task is no longer in the list
+ TaskView tv = getChildViewForTask(removedTask);
+ if (tv != null) {
+ mViewPool.returnViewToPool(tv);
+ }
+
+ // Get the stack scroll of the task to anchor to (since we are removing something, the front
+ // most task will be our anchor task)
+ Task anchorTask = null;
+ float prevAnchorTaskScroll = 0;
+ boolean pullStackForward = stack.getTaskCount() > 0;
+ if (pullStackForward) {
+ anchorTask = mStack.getFrontMostTask();
+ prevAnchorTaskScroll = mLayoutAlgorithm.getStackScrollForTask(anchorTask);
+ }
+
+ // Update the min/max scroll and animate other task views into their new positions
+ updateLayout(true);
+
+ if (wasFrontMostTask) {
+ // Since the max scroll progress is offset from the bottom of the stack, just scroll
+ // to ensure that the new front most task is now fully visible
+ mStackScroller.setStackScroll(mLayoutAlgorithm.mMaxScrollP);
+ } else if (pullStackForward) {
+ // Otherwise, offset the scroll by half the movement of the anchor task to allow the
+ // tasks behind the removed task to move forward, and the tasks in front to move back
+ float anchorTaskScroll = mLayoutAlgorithm.getStackScrollForTask(anchorTask);
+ mStackScroller.setStackScroll(mStackScroller.getStackScroll() + (anchorTaskScroll
+ - prevAnchorTaskScroll) / 2);
+ mStackScroller.boundScroll();
+ }
+
+ // Animate all the tasks into place
+ requestSynchronizeStackViewsWithModel(200);
+ } else {
+ // Remove the view associated with this task, we can't rely on updateTransforms
+ // to work here because the task is no longer in the list
+ TaskView tv = getChildViewForTask(removedTask);
+ if (tv != null) {
+ mViewPool.returnViewToPool(tv);
+ }
+
+ // Update the min/max scroll and animate other task views into their new positions
+ updateLayout(true);
+
+ // Animate all the tasks into place
+ requestSynchronizeStackViewsWithModel(200);
}
- // Get the stack scroll of the task to anchor to (since we are removing something, the front
- // most task will be our anchor task)
- Task anchorTask = null;
- float prevAnchorTaskScroll = 0;
- boolean pullStackForward = stack.getTaskCount() > 0;
- if (pullStackForward) {
- anchorTask = mStack.getFrontMostTask();
- prevAnchorTaskScroll = mLayoutAlgorithm.getStackScrollForTask(anchorTask);
- }
-
- // Update the min/max scroll and animate other task views into their new positions
- updateMinMaxScroll(true);
-
- if (wasFrontMostTask) {
- // Since the max scroll progress is offset from the bottom of the stack, just scroll
- // to ensure that the new front most task is now fully visible
- mStackScroller.setStackScroll(mLayoutAlgorithm.mMaxScrollP);
- } else if (pullStackForward) {
- // Otherwise, offset the scroll by half the movement of the anchor task to allow the
- // tasks behind the removed task to move forward, and the tasks in front to move back
- float anchorTaskScroll = mLayoutAlgorithm.getStackScrollForTask(anchorTask);
- mStackScroller.setStackScroll(mStackScroller.getStackScroll() + (anchorTaskScroll
- - prevAnchorTaskScroll) / 2);
- mStackScroller.boundScroll();
- }
-
- // Animate all the tasks into place
- requestSynchronizeStackViewsWithModel(200);
-
// Update the new front most task
if (newFrontMostTask != null) {
TaskView frontTv = getChildViewForTask(newFrontMostTask);
@@ -1080,7 +1210,7 @@
mLayoutAlgorithm.updateTaskOffsets(mStack.getTasks());
// Scroll the item to the top of the stack (sans-peek) rect so that we can see it better
- updateMinMaxScroll(false);
+ updateLayout(false);
float overlapHeight = mLayoutAlgorithm.getTaskOverlapHeight();
setStackScrollRaw((int) (newStack.indexOfTask(filteredTask) * overlapHeight));
boundScrollRaw();
@@ -1109,7 +1239,7 @@
mLayoutAlgorithm.updateTaskOffsets(mStack.getTasks());
// Restore the stashed scroll
- updateMinMaxScroll(false);
+ updateLayout(false);
setStackScrollRaw(mStashedScroll);
boundScrollRaw();
@@ -1140,13 +1270,8 @@
public void prepareViewToEnterPool(TaskView tv) {
Task task = tv.getTask();
- // Clear the accessibility focus for that view
- if (tv.isAccessibilityFocused()) {
- tv.clearAccessibilityFocus();
- }
-
// Report that this tasks's data is no longer being used
- RecentsTaskLoader.getInstance().unloadTaskData(task);
+ Recents.getTaskLoader().unloadTaskData(task);
// Detach the view from the hierarchy
detachViewFromParent(tv);
@@ -1170,21 +1295,15 @@
tv.onTaskBound(task);
// Load the task data
- RecentsTaskLoader.getInstance().loadTaskData(task);
+ Recents.getTaskLoader().loadTaskData(task);
// If the doze trigger has already fired, then update the state for this task view
tv.setNoUserInteractionState();
- // If we've finished the start animation, then ensure we always enable the focus animations
- if (mStartEnterAnimationCompleted) {
- tv.enableFocusAnimations();
- }
-
// Find the index where this task should be placed in the stack
int insertIndex = -1;
int taskIndex = mStack.indexOfTask(task);
if (taskIndex != -1) {
-
List<TaskView> taskViews = getTaskViews();
int taskViewCount = taskViews.size();
for (int i = 0; i < taskViewCount; i++) {
@@ -1227,7 +1346,7 @@
mUIDozeTrigger.stopDozing();
if (mCb != null) {
- mCb.onTaskViewClicked(this, tv, mStack, task, lockToTask, false, null);
+ mCb.onTaskViewClicked(this, tv, mStack, task, lockToTask, null, INVALID_STACK_ID);
}
}
@@ -1238,13 +1357,6 @@
}
}
- @Override
- public void onTaskViewFocusChanged(TaskView tv, boolean focused) {
- if (focused) {
- mFocusedTaskIndex = mStack.indexOfTask(tv.getTask());
- }
- }
-
/**** TaskStackViewScroller.TaskStackViewScrollerCallbacks ****/
@Override
@@ -1258,21 +1370,21 @@
public final void onBusEvent(PackagesChangedEvent event) {
// Compute which components need to be removed
- HashSet<ComponentName> removedComponents = event.monitor.computeComponentsRemoved(
- mStack.getTaskKeys(), event.packageName, event.userId);
+ HashSet<ComponentName> removedComponents = mStack.computeComponentsRemoved(
+ event.packageName, event.userId);
// For other tasks, just remove them directly if they no longer exist
ArrayList<Task> tasks = mStack.getTasks();
for (int i = tasks.size() - 1; i >= 0; i--) {
final Task t = tasks.get(i);
- if (removedComponents.contains(t.key.baseIntent.getComponent())) {
- TaskView tv = getChildViewForTask(t);
+ if (removedComponents.contains(t.key.getComponent())) {
+ final TaskView tv = getChildViewForTask(t);
if (tv != null) {
// For visible children, defer removing the task until after the animation
tv.startDeleteTaskAnimation(new Runnable() {
@Override
public void run() {
- mStack.removeTask(t);
+ removeTaskViewFromStack(tv);
}
}, 0);
} else {
@@ -1283,33 +1395,139 @@
}
}
- public final void onBusEvent(DismissTaskEvent event) {
- TaskView tv = event.taskView;
+ public final void onBusEvent(DismissTaskViewEvent event) {
+ removeTaskViewFromStack(event.taskView);
+ }
+
+ public final void onBusEvent(FocusNextTaskViewEvent event) {
+ setRelativeFocusedTask(true, false /* stackTasksOnly */, true /* animated */);
+ }
+
+ public final void onBusEvent(FocusPreviousTaskViewEvent event) {
+ setRelativeFocusedTask(false, false /* stackTasksOnly */, true /* animated */);
+ }
+
+ public final void onBusEvent(DismissFocusedTaskViewEvent event) {
+ if (mFocusedTaskIndex != -1) {
+ Task t = mStack.getTasks().get(mFocusedTaskIndex);
+ TaskView tv = getChildViewForTask(t);
+ tv.dismissTask();
+ }
+ }
+
+ public final void onBusEvent(UserInteractionEvent event) {
+ // Poke the doze trigger on user interaction
+ mUIDozeTrigger.poke();
+ }
+
+ public final void onBusEvent(RecentsVisibilityChangedEvent event) {
+ if (!event.visible) {
+ reset();
+ }
+ }
+
+ public final void onBusEvent(DragStartEvent event) {
+ if (event.task.isFreeformTask()) {
+ // Animate to the front of the stack
+ mStackScroller.animateScroll(mStackScroller.getStackScroll(),
+ mLayoutAlgorithm.mInitialScrollP, null);
+ }
+ }
+
+ public final void onBusEvent(DragStartInitializeDropTargetsEvent event) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ if (ssp.hasFreeformWorkspaceSupport()) {
+ event.handler.registerDropTargetForCurrentDrag(mStackDropTarget);
+ event.handler.registerDropTargetForCurrentDrag(mFreeformWorkspaceDropTarget);
+ }
+ }
+
+ public final void onBusEvent(DragDropTargetChangedEvent event) {
+ // TODO: Animate the freeform workspace background etc.
+ }
+
+ public final void onBusEvent(final DragEndEvent event) {
+ if (event.dropTarget != mFreeformWorkspaceDropTarget &&
+ event.dropTarget != mStackDropTarget) {
+ return;
+ }
+ if (event.task.isFreeformTask() && event.dropTarget == mFreeformWorkspaceDropTarget) {
+ // TODO: Animate back into view
+ return;
+ }
+ if (!event.task.isFreeformTask() && event.dropTarget == mStackDropTarget) {
+ // TODO: Animate back into view
+ return;
+ }
+
+ // Move the task to the right position in the stack (ie. the front of the stack if freeform
+ // or the front of the stack if fullscreen). Note, we MUST move the tasks before we update
+ // their stack ids, otherwise, the keys will have changed.
+ if (event.dropTarget == mFreeformWorkspaceDropTarget) {
+ mStack.moveTaskToStack(event.task, FREEFORM_WORKSPACE_STACK_ID);
+ updateLayout(true);
+ } else if (event.dropTarget == mStackDropTarget) {
+ mStack.moveTaskToStack(event.task, FULLSCREEN_WORKSPACE_STACK_ID);
+ updateLayout(true);
+ }
+
+ event.postAnimationTrigger.increment();
+ event.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+ @Override
+ public void run() {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ ssp.moveTaskToStack(event.task.key.id, event.task.key.stackId);
+ }
+ });
+
+ // Animate the drag view to the new position
+ mLayoutAlgorithm.getStackTransform(event.task, mStackScroller.getStackScroll(),
+ mTmpTransform, null);
+ event.dragView.animate()
+ .scaleX(mTmpTransform.scale)
+ .scaleY(mTmpTransform.scale)
+ .translationX((mLayoutAlgorithm.mTaskRect.left - event.dragView.getLeft())
+ + mTmpTransform.translationX)
+ .translationY((mLayoutAlgorithm.mTaskRect.top - event.dragView.getTop())
+ + mTmpTransform.translationY)
+ .setDuration(175)
+ .setInterpolator(mFastOutSlowInInterpolator)
+ .withEndAction(event.postAnimationTrigger.decrementAsRunnable())
+ .start();
+
+ // Animate the other views into place
+ requestSynchronizeStackViewsWithModel(175);
+ }
+
+ /**
+ * 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) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
Task task = tv.getTask();
int taskIndex = mStack.indexOfTask(task);
boolean taskWasFocused = tv.isFocusedTask();
- // Announce for accessibility
- tv.announceForAccessibility(getContext().getString(R.string.accessibility_recents_item_dismissed,
- tv.getTask().activityLabel));
+ // Reset the previously focused task before it is removed from the stack
+ resetFocusedTask();
- // Remove the task from the view
+ // Announce for accessibility
+ tv.announceForAccessibility(getContext().getString(
+ R.string.accessibility_recents_item_dismissed, tv.getTask().activityLabel));
+
+ // Remove the task from the stack
mStack.removeTask(task);
- // If the dismissed task was focused, then we should focus the new task in the same index
- if (taskWasFocused) {
- ArrayList<Task> tasks = mStack.getTasks();
- int nextTaskIndex = Math.min(tasks.size() - 1, taskIndex - 1);
- if (nextTaskIndex >= 0) {
- Task nextTask = tasks.get(nextTaskIndex);
- TaskView nextTv = getChildViewForTask(nextTask);
- if (nextTv != null) {
- // Focus the next task, and only animate the visible state if we are launched
- // from Alt-Tab
- RecentsActivityLaunchState launchState = mConfig.getLaunchState();
- nextTv.setFocusedTask(launchState.launchedWithAltTab);
- }
- }
+ if (taskWasFocused || ssp.isTouchExplorationEnabled()) {
+ // If the dismissed task was focused or if we are in touch exploration mode, then focus
+ // the next task
+ RecentsConfiguration config = Recents.getConfiguration();
+ RecentsActivityLaunchState launchState = config.getLaunchState();
+ boolean isFreeformTask = taskIndex > 0 ?
+ mStack.getTasks().get(taskIndex - 1).isFreeformTask() : false;
+ setFocusedTask(taskIndex - 1, !isFreeformTask /* scrollToTask */,
+ launchState.launchedWithAltTab);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java
index a32b242..45f573d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java
@@ -17,7 +17,6 @@
package com.android.systemui.recents.views;
import com.android.systemui.R;
-import com.android.systemui.recents.Constants;
import com.android.systemui.recents.model.Task;
import java.util.ArrayList;
@@ -113,7 +112,7 @@
tv.prepareTaskTransformForFilterTaskHidden(fromTransform);
tv.updateViewPropertiesToTaskTransform(fromTransform, 0);
- toTransform.startDelay = offset * Constants.Values.TaskStackView.FilterStartDelay;
+ toTransform.startDelay = offset * 25;
childViewTransformsOut.put(tv, toTransform);
// Use the movement of the new views to calculate the duration of the animation
@@ -166,7 +165,7 @@
(int) tv.getTranslationY()));
}
- toTransform.startDelay = offset * Constants.Values.TaskStackView.FilterStartDelay;
+ toTransform.startDelay = offset * 25;
childViewTransformsOut.put(tv, toTransform);
offset++;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
deleted file mode 100644
index 9a5d9bd..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
+++ /dev/null
@@ -1,409 +0,0 @@
-/*
- * Copyright (C) 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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.views;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Rect;
-import android.util.Log;
-import com.android.systemui.R;
-import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.misc.ParametricCurve;
-import com.android.systemui.recents.misc.Utilities;
-import com.android.systemui.recents.model.Task;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-
-
-/**
- * The layout logic for a TaskStackView.
- */
-public class TaskStackViewLayoutAlgorithm {
-
- private static final boolean DEBUG = false;
- private static final String TAG = "TaskStackViewLayoutAlgorithm";
-
- // The min scale of the last task at the top of the curve
- private static final float STACK_PEEK_MIN_SCALE = 0.75f;
- // The scale of the last task
- private static final float SINGLE_TASK_SCALE = 0.95f;
- // The percentage of height of task to show between tasks
- private static final float VISIBLE_TASK_HEIGHT_BETWEEN_TASKS = 0.5f;
-
- // A report of the visibility state of the stack
- public class VisibilityReport {
- public int numVisibleTasks;
- public int numVisibleThumbnails;
-
- /** Package level ctor */
- VisibilityReport(int tasks, int thumbnails) {
- numVisibleTasks = tasks;
- numVisibleThumbnails = thumbnails;
- }
- }
-
- Context mContext;
- RecentsConfiguration mConfig;
-
- // This is the view bounds inset exactly by the search bar, but without the bottom inset
- // see RecentsConfiguration.getTaskStackBounds()
- public Rect mStackRect = new Rect();
- // This is the task view bounds for layout (untransformed), the rect is top-aligned to the top
- // of the stack rect
- public Rect mTaskRect = new Rect();
- // This is the current system insets
- public Rect mSystemInsets = new Rect();
-
- // The smallest scroll progress, at this value, the back most task will be visible
- float mMinScrollP;
- // The largest scroll progress, at this value, the front most task will be visible above the
- // navigation bar
- float mMaxScrollP;
- // The initial progress that the scroller is set
- float mInitialScrollP;
-
- // The relative progress to ensure that the height between affiliated tasks is respected
- float mWithinAffiliationPOffset;
- // The relative progress to ensure that the height between non-affiliated tasks is
- // respected
- float mBetweenAffiliationPOffset;
- // The relative progress to ensure that the task height is respected
- float mTaskHeightPOffset;
- // The relative progress to ensure that the half task height is respected
- float mTaskHalfHeightPOffset;
- // The relative progress to ensure that the offset from the bottom of the stack to the bottom
- // of the task is respected
- float mTaskBottomPOffset;
- // The front-most task bottom offset
- int mTaskBottomOffset;
-
- // The last computed task count
- int mNumTasks;
- // The min/max z translations
- int mMinTranslationZ;
- int mMaxTranslationZ;
-
- // Optimization, allows for quick lookup of task -> progress
- HashMap<Task.TaskKey, Float> mTaskProgressMap = new HashMap<>();
-
- // Log function
- static ParametricCurve sCurve;
-
- public TaskStackViewLayoutAlgorithm(Context context, RecentsConfiguration config) {
- Resources res = context.getResources();
- mMinTranslationZ = res.getDimensionPixelSize(R.dimen.recents_task_view_z_min);
- mMaxTranslationZ = res.getDimensionPixelSize(R.dimen.recents_task_view_z_max);
- mContext = context;
- mConfig = config;
- if (sCurve == null) {
- sCurve = new ParametricCurve(new ParametricCurve.CurveFunction() {
- // The large the XScale, the longer the flat area of the curve
- private static final float XScale = 1.75f;
- private static final float LogBase = 3000;
-
- float reverse(float x) {
- return (-x * XScale) + 1;
- }
-
- @Override
- public float f(float x) {
- return 1f - (float) (Math.pow(LogBase, reverse(x))) / (LogBase);
- }
-
- @Override
- public float invF(float y) {
- return (float) (Math.log(1f - reverse(y)) / (-Math.log(LogBase) * XScale));
- }
- }, new ParametricCurve.ParametricCurveFunction() {
- @Override
- public float f(float p) {
- if (p < 0) return STACK_PEEK_MIN_SCALE;
- if (p > 1) return 1f;
- float scaleRange = (1f - STACK_PEEK_MIN_SCALE);
- float scale = STACK_PEEK_MIN_SCALE + (p * scaleRange);
- return scale;
- }
- });
- }
- }
-
- /**
- * Sets the system insets.
- */
- public void setSystemInsets(Rect systemInsets) {
- mSystemInsets.set(systemInsets);
- if (DEBUG) {
- Log.d(TAG, "setSystemInsets: " + systemInsets);
- }
- }
-
- /**
- * Computes the stack and task rects
- */
- public void computeRects(int windowWidth, int windowHeight, Rect taskStackBounds) {
- int widthPadding = (int) (mConfig.taskStackWidthPaddingPct * taskStackBounds.width());
- int heightPadding = mContext.getResources().getDimensionPixelSize(
- R.dimen.recents_stack_top_padding);
-
- // Compute the stack rect, inset from the given task stack bounds
- mStackRect.set(taskStackBounds.left + widthPadding, taskStackBounds.top + heightPadding,
- taskStackBounds.right - widthPadding, windowHeight);
- mTaskBottomOffset = mSystemInsets.bottom + heightPadding;
-
- // Compute the task rect, align it to the top-center square in the stack rect
- int size = Math.min(mStackRect.width(), taskStackBounds.height() - mTaskBottomOffset);
- int xOffset = (mStackRect.width() - size) / 2;
- mTaskRect.set(mStackRect.left + xOffset, mStackRect.top,
- mStackRect.right - xOffset, mStackRect.top + size);
-
- // Compute the progress offsets
- int withinAffiliationOffset = mContext.getResources().getDimensionPixelSize(
- R.dimen.recents_task_bar_height);
- int betweenAffiliationOffset = (int) (VISIBLE_TASK_HEIGHT_BETWEEN_TASKS * mTaskRect.height());
- mWithinAffiliationPOffset = sCurve.computePOffsetForScaledHeight(withinAffiliationOffset,
- mStackRect);
- mBetweenAffiliationPOffset = sCurve.computePOffsetForScaledHeight(betweenAffiliationOffset,
- mStackRect);
- mTaskHeightPOffset = sCurve.computePOffsetForScaledHeight(mTaskRect.height(),
- mStackRect);
- mTaskHalfHeightPOffset = sCurve.computePOffsetForScaledHeight(mTaskRect.height() / 2,
- mStackRect);
- mTaskBottomPOffset = sCurve.computePOffsetForHeight(mTaskBottomOffset, mStackRect);
-
- if (DEBUG) {
- Log.d(TAG, "computeRects");
- Log.d(TAG, "\tmStackRect: " + mStackRect);
- Log.d(TAG, "\tmTaskRect: " + mTaskRect);
- Log.d(TAG, "\tmSystemInsets: " + mSystemInsets);
-
- Log.d(TAG, "\tpWithinAffiliateOffset: " + mWithinAffiliationPOffset);
- Log.d(TAG, "\tpBetweenAffiliateOffset: " + mBetweenAffiliationPOffset);
- Log.d(TAG, "\tmTaskHeightPOffset: " + mTaskHeightPOffset);
- Log.d(TAG, "\tmTaskHalfHeightPOffset: " + mTaskHalfHeightPOffset);
- Log.d(TAG, "\tmTaskBottomPOffset: " + mTaskBottomPOffset);
-
- Log.d(TAG, "\ty at p=0: " + sCurve.pToX(0f, mStackRect));
- Log.d(TAG, "\ty at p=1: " + sCurve.pToX(1f, mStackRect));
-
- for (int height = 0; height <= 1000; height += 50) {
- float p = sCurve.computePOffsetForScaledHeight(height, mStackRect);
- float p2 = sCurve.computePOffsetForHeight(height, mStackRect);
- Log.d(TAG, "offset: " + height + ", " +
- p + " => " + (mStackRect.bottom - sCurve.pToX(1f - p, mStackRect)) /
- sCurve.pToScale(1f - p) + ", " +
- p2 + " => " + (mStackRect.bottom - sCurve.pToX(1f - p2, mStackRect)));
- }
- }
- }
-
- /**
- * Computes the minimum and maximum scroll progress values. This method may be called before
- * the RecentsConfiguration is set, so we need to pass in the alt-tab state.
- */
- void computeMinMaxScroll(ArrayList<Task> tasks) {
- if (DEBUG) {
- Log.d(TAG, "computeMinMaxScroll");
- }
-
- // Clear the progress map
- mTaskProgressMap.clear();
- mNumTasks = tasks.size();
-
- // Return early if we have no tasks
- if (tasks.isEmpty()) {
- mMinScrollP = mMaxScrollP = 0;
- return;
- }
-
- // We calculate the progress by taking the progress of the element from the bottom of the
- // screen
- if (mNumTasks == 1) {
- // Just center the task in the visible stack rect
- mMinScrollP = mMaxScrollP = mInitialScrollP = 0f;
- mTaskProgressMap.put(tasks.get(0).key, 0f);
- } else {
- // Update the tasks from back to front with the new progresses. We set the initial
- // progress to the progress at which the top of the last task is near the center of the
- // visible stack rect.
- float pAtBackMostTaskTop = 0;
- float pAtFrontMostTaskTop = pAtBackMostTaskTop;
- int taskCount = tasks.size();
- for (int i = 0; i < taskCount; i++) {
- Task task = tasks.get(i);
- mTaskProgressMap.put(task.key, pAtFrontMostTaskTop);
-
- if (i < (taskCount - 1)) {
- // Increment the peek height
- float pPeek = task.group.isFrontMostTask(task) ?
- mBetweenAffiliationPOffset : mWithinAffiliationPOffset;
- pAtFrontMostTaskTop += pPeek;
- }
- }
-
- // Set the max scroll progress to the point at which the bottom of the front-most task
- // is aligned to the bottom of the stack (including nav bar and stack padding)
- mMaxScrollP = pAtFrontMostTaskTop - 1f + mTaskBottomPOffset + mTaskHeightPOffset;
- // Basically align the back-most task such that its progress is the same as the top of
- // the front most task at the max scroll
- mMinScrollP = pAtBackMostTaskTop - 1f + mTaskBottomPOffset + mTaskHeightPOffset;
- // The offset the inital scroll position to the front of the stack, with half the front
- // task height visible
- mInitialScrollP = Math.max(mMinScrollP, mMaxScrollP - mTaskHalfHeightPOffset);
- }
- }
-
- /**
- * Computes the maximum number of visible tasks and thumbnails. Requires that
- * computeMinMaxScroll() is called first.
- */
- public VisibilityReport computeStackVisibilityReport(ArrayList<Task> tasks) {
- if (tasks.size() <= 1) {
- return new VisibilityReport(1, 1);
- }
-
- // Walk backwards in the task stack and count the number of tasks and visible thumbnails
- int taskHeight = mTaskRect.height();
- int taskBarHeight = mContext.getResources().getDimensionPixelSize(
- R.dimen.recents_task_bar_height);
- int numVisibleTasks = 1;
- int numVisibleThumbnails = 1;
- float progress = mTaskProgressMap.get(tasks.get(tasks.size() - 1).key) - mInitialScrollP;
- int prevScreenY = sCurve.pToX(progress, mStackRect);
- for (int i = tasks.size() - 2; i >= 0; i--) {
- Task task = tasks.get(i);
- progress = mTaskProgressMap.get(task.key) - mInitialScrollP;
- if (progress < 0) {
- break;
- }
- boolean isFrontMostTaskInGroup = task.group.isFrontMostTask(task);
- if (isFrontMostTaskInGroup) {
- float scaleAtP = sCurve.pToScale(progress);
- int scaleYOffsetAtP = (int) (((1f - scaleAtP) * taskHeight) / 2);
- int screenY = sCurve.pToX(progress, mStackRect) + scaleYOffsetAtP;
- boolean hasVisibleThumbnail = (prevScreenY - screenY) > taskBarHeight;
- if (hasVisibleThumbnail) {
- numVisibleThumbnails++;
- numVisibleTasks++;
- prevScreenY = screenY;
- } else {
- // Once we hit the next front most task that does not have a visible thumbnail,
- // walk through remaining visible set
- for (int j = i; j >= 0; j--) {
- numVisibleTasks++;
- progress = mTaskProgressMap.get(tasks.get(j).key) - mInitialScrollP;
- if (progress < 0) {
- break;
- }
- }
- break;
- }
- } else if (!isFrontMostTaskInGroup) {
- // Affiliated task, no thumbnail
- numVisibleTasks++;
- }
- }
- return new VisibilityReport(numVisibleTasks, numVisibleThumbnails);
- }
-
- /**
- * Returns the transform for the given task. This transform is relative to the mTaskRect, which
- * is what the view is measured and laid out with.
- */
- public TaskViewTransform getStackTransform(Task task, float stackScroll,
- TaskViewTransform transformOut, TaskViewTransform prevTransform) {
- // Return early if we have an invalid index
- if (task == null || !mTaskProgressMap.containsKey(task.key)) {
- transformOut.reset();
- return transformOut;
- }
- return getStackTransform(mTaskProgressMap.get(task.key), stackScroll, transformOut,
- prevTransform);
- }
-
- /** Update/get the transform */
- public TaskViewTransform getStackTransform(float taskProgress, float stackScroll,
- TaskViewTransform transformOut, TaskViewTransform prevTransform) {
- if (DEBUG) {
- Log.d(TAG, "getStackTransform: " + stackScroll);
- }
-
- if (mNumTasks == 1) {
- // Center the task in the stack, changing the scale will not follow the curve, but just
- // modulate some values directly
- float pTaskRelative = -stackScroll;
- float scale = SINGLE_TASK_SCALE;
- int topOffset = (mStackRect.height() - mTaskBottomOffset - mTaskRect.height()) / 2;
- transformOut.scale = scale;
- transformOut.translationY = (int) (topOffset + (pTaskRelative * mStackRect.height()));
- transformOut.translationZ = mMaxTranslationZ;
- transformOut.rect.set(mTaskRect);
- transformOut.rect.offset(0, transformOut.translationY);
- Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
- transformOut.visible = true;
- transformOut.p = pTaskRelative;
- return transformOut;
- } else {
- float pTaskRelative = taskProgress - stackScroll;
- float pBounded = Math.max(0, Math.min(pTaskRelative, 1f));
- // If the task top is outside of the bounds below the screen, then immediately reset it
- if (pTaskRelative > 1f) {
- transformOut.reset();
- transformOut.rect.set(mTaskRect);
- return transformOut;
- }
- // The check for the top is trickier, since we want to show the next task if it is at
- // all visible, even if p < 0.
- if (pTaskRelative < 0f) {
- if (prevTransform != null && Float.compare(prevTransform.p, 0f) <= 0) {
- transformOut.reset();
- transformOut.rect.set(mTaskRect);
- return transformOut;
- }
- }
- float scale = sCurve.pToScale(pBounded);
- int scaleYOffset = (int) (((1f - scale) * mTaskRect.height()) / 2);
- transformOut.scale = scale;
- transformOut.translationY = sCurve.pToX(pBounded, mStackRect) - mStackRect.top -
- scaleYOffset;
- transformOut.translationZ = Math.max(mMinTranslationZ,
- mMinTranslationZ + (pBounded * (mMaxTranslationZ - mMinTranslationZ)));
- transformOut.rect.set(mTaskRect);
- transformOut.rect.offset(0, transformOut.translationY);
- Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
- transformOut.visible = true;
- transformOut.p = pTaskRelative;
- return transformOut;
- }
- }
-
- /**
- * Returns the untransformed task view bounds.
- */
- public Rect getUntransformedTaskViewBounds() {
- return new Rect(mTaskRect);
- }
-
- /**
- * Returns the scroll progress to scroll to such that the top of the task is at the top of the
- * stack.
- */
- float getStackScrollForTask(Task t) {
- if (!mTaskProgressMap.containsKey(t.key)) return 0f;
- return mTaskProgressMap.get(t.key);
- }
-}
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 3d3b13d..3a2ed0f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
@@ -21,6 +21,7 @@
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
+import android.util.Log;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.OverScroller;
@@ -29,15 +30,21 @@
/* The scrolling logic for a TaskStackView */
public class TaskStackViewScroller {
+
+ private static final String TAG = "TaskStackViewScroller";
+ private static final boolean DEBUG = false;
+
public interface TaskStackViewScrollerCallbacks {
- public void onScrollChanged(float p);
+ void onScrollChanged(float p);
}
Context mContext;
- TaskStackViewLayoutAlgorithm mLayoutAlgorithm;
+ TaskStackLayoutAlgorithm mLayoutAlgorithm;
TaskStackViewScrollerCallbacks mCb;
float mStackScrollP;
+ float mFlingDownScrollP;
+ int mFlingDownY;
OverScroller mScroller;
ObjectAnimator mScrollAnimator;
@@ -45,7 +52,7 @@
Interpolator mLinearOutSlowInInterpolator;
- public TaskStackViewScroller(Context context, TaskStackViewLayoutAlgorithm layoutAlgorithm) {
+ public TaskStackViewScroller(Context context, TaskStackLayoutAlgorithm layoutAlgorithm) {
mContext = context;
mScroller = new OverScroller(context);
mLayoutAlgorithm = layoutAlgorithm;
@@ -77,11 +84,6 @@
}
}
- /** Sets the current stack scroll without calling the callback. */
- void setStackScrollRaw(float s) {
- mStackScrollP = s;
- }
-
/**
* Sets the current stack scroll to the initial state when you first enter recents.
* @return whether the stack progress changed.
@@ -92,6 +94,20 @@
return Float.compare(prevStackScrollP, mStackScrollP) != 0;
}
+ /**
+ * Starts a fling that is coordinated with the {@link TaskStackViewTouchHandler}.
+ */
+ public void fling(float downScrollP, int downY, int y, int velY, int minY, int maxY,
+ int overscroll) {
+ if (DEBUG) {
+ Log.d(TAG, "fling: " + downScrollP + ", downY: " + downY + ", y: " + y +
+ ", velY: " + velY + ", minY: " + minY + ", maxY: " + maxY);
+ }
+ mFlingDownScrollP = downScrollP;
+ mFlingDownY = downY;
+ mScroller.fling(0, y, 0, velY, 0, 0, minY, maxY, 0, overscroll);
+ }
+
/** Bounds the current scroll if necessary */
public boolean boundScroll() {
float curScroll = getStackScroll();
@@ -105,7 +121,8 @@
/** Returns the bounded stack scroll */
float getBoundedStackScroll(float scroll) {
- return Math.max(mLayoutAlgorithm.mMinScrollP, Math.min(mLayoutAlgorithm.mMaxScrollP, scroll));
+ return Math.max(mLayoutAlgorithm.mMinScrollP,
+ Math.min(mLayoutAlgorithm.mMaxScrollP, scroll));
}
/** Returns the amount that the absolute value of how much the scroll is out of bounds. */
@@ -174,21 +191,20 @@
/**** OverScroller ****/
+ // TODO: Remove
+ @Deprecated
int progressToScrollRange(float p) {
- return (int) (p * mLayoutAlgorithm.mStackRect.height());
- }
-
- float scrollRangeToProgress(int s) {
- return (float) s / mLayoutAlgorithm.mStackRect.height();
+ return (int) (p * mLayoutAlgorithm.mCurrentStackRect.height());
}
/** Called from the view draw, computes the next scroll. */
boolean computeScroll() {
if (mScroller.computeScrollOffset()) {
- float scroll = scrollRangeToProgress(mScroller.getCurrY());
- setStackScrollRaw(scroll);
- if (mCb != null) {
- mCb.onScrollChanged(scroll);
+ float deltaP = mLayoutAlgorithm.getDeltaPForY(mFlingDownY, mScroller.getCurrY());
+ float scroll = mFlingDownScrollP + deltaP;
+ setStackScroll(scroll);
+ if (DEBUG) {
+ Log.d(TAG, "computeScroll: " + scroll);
}
return true;
}
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 18e07a3..81c89a1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -16,7 +16,11 @@
package com.android.systemui.recents.views;
+import android.animation.ValueAnimator;
import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.util.Log;
import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.VelocityTracker;
@@ -27,36 +31,42 @@
import com.android.systemui.R;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.RecentsActivityLaunchState;
import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.ui.DismissTaskEvent;
+import com.android.systemui.recents.events.activity.HideRecentsEvent;
+import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.misc.Utilities;
+import com.android.systemui.statusbar.FlingAnimationUtils;
import java.util.List;
/* Handles touch events for a TaskStackView. */
class TaskStackViewTouchHandler implements SwipeHelper.Callback {
- static int INACTIVE_POINTER_ID = -1;
+
+ private static final String TAG = "TaskStackViewTouchHandler";
+ private static final boolean DEBUG = false;
+
+ private static int INACTIVE_POINTER_ID = -1;
Context mContext;
TaskStackView mSv;
TaskStackViewScroller mScroller;
VelocityTracker mVelocityTracker;
+ FlingAnimationUtils mFlingAnimUtils;
+ ValueAnimator mScrollFlingAnimator;
boolean mIsScrolling;
-
- float mInitialP;
- float mLastP;
- float mTotalPMotion;
- int mInitialMotionX, mInitialMotionY;
- int mLastMotionX, mLastMotionY;
+ float mDownScrollP;
+ int mDownX, mDownY;
int mActivePointerId = INACTIVE_POINTER_ID;
+ int mOverscrollSize;
TaskView mActiveTaskView = null;
int mMinimumVelocity;
int mMaximumVelocity;
// The scroll touch slop is used to calculate when we start scrolling
int mScrollTouchSlop;
- // The page touch slop is used to calculate when we start swiping
- float mPagingTouchSlop;
// Used to calculate when a tap is outside a task view rectangle.
final int mWindowTouchSlop;
@@ -65,18 +75,21 @@
public TaskStackViewTouchHandler(Context context, TaskStackView sv,
TaskStackViewScroller scroller) {
- mContext = context;
+ Resources res = context.getResources();
ViewConfiguration configuration = ViewConfiguration.get(context);
+ mContext = context;
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
mScrollTouchSlop = configuration.getScaledTouchSlop();
- mPagingTouchSlop = configuration.getScaledPagingTouchSlop();
mWindowTouchSlop = configuration.getScaledWindowTouchSlop();
mSv = sv;
mScroller = scroller;
+ mFlingAnimUtils = new FlingAnimationUtils(context, 0.2f);
- float densityScale = context.getResources().getDisplayMetrics().density;
- mSwipeHelper = new SwipeHelper(context, SwipeHelper.X, this, densityScale, mPagingTouchSlop);
+ float densityScale = res.getDisplayMetrics().density;
+ mOverscrollSize = res.getDimensionPixelSize(R.dimen.recents_stack_overscroll);
+ mSwipeHelper = new SwipeHelper(context, SwipeHelper.X, this, densityScale,
+ configuration.getScaledPagingTouchSlop());
mSwipeHelper.setMinAlpha(1f);
}
@@ -88,11 +101,6 @@
mVelocityTracker.clear();
}
}
- void initVelocityTrackerIfNotExists() {
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
- }
void recycleVelocityTracker() {
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
@@ -115,153 +123,76 @@
return null;
}
- /** Constructs a simulated motion event for the current stack scroll. */
- MotionEvent createMotionEventForStackScroll(MotionEvent ev) {
- MotionEvent pev = MotionEvent.obtainNoHistory(ev);
- pev.setLocation(0, mScroller.progressToScrollRange(mScroller.getStackScroll()));
- return pev;
- }
-
/** Touch preprocessing for handling below */
public boolean onInterceptTouchEvent(MotionEvent ev) {
- // Return early if we have no children
- boolean hasTaskViews = (mSv.getTaskViews().size() > 0);
- if (!hasTaskViews) {
- return false;
- }
-
// Pass through to swipe helper if we are swiping
mInterceptedBySwipeHelper = mSwipeHelper.onInterceptTouchEvent(ev);
if (mInterceptedBySwipeHelper) {
return true;
}
- TaskStackViewLayoutAlgorithm layoutAlgorithm = mSv.mLayoutAlgorithm;
- boolean wasScrolling = mScroller.isScrolling() ||
- (mScroller.mScrollAnimator != null && mScroller.mScrollAnimator.isRunning());
- int action = ev.getAction();
- switch (action & MotionEvent.ACTION_MASK) {
- case MotionEvent.ACTION_DOWN: {
- // Save the touch down info
- mInitialMotionX = mLastMotionX = (int) ev.getX();
- mInitialMotionY = mLastMotionY = (int) ev.getY();
- mInitialP = mLastP = layoutAlgorithm.sCurve.xToP(mLastMotionY,
- layoutAlgorithm.mStackRect);
- mActivePointerId = ev.getPointerId(0);
- mActiveTaskView = findViewAtPoint(mLastMotionX, mLastMotionY);
- // Stop the current scroll if it is still flinging
- mScroller.stopScroller();
- mScroller.stopBoundScrollAnimation();
- // Initialize the velocity tracker
- initOrResetVelocityTracker();
- mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
- break;
- }
- case MotionEvent.ACTION_MOVE: {
- if (mActivePointerId == INACTIVE_POINTER_ID) break;
-
- // Initialize the velocity tracker if necessary
- initVelocityTrackerIfNotExists();
- mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
-
- int activePointerIndex = ev.findPointerIndex(mActivePointerId);
- int y = (int) ev.getY(activePointerIndex);
- int x = (int) ev.getX(activePointerIndex);
- if (Math.abs(y - mInitialMotionY) > mScrollTouchSlop) {
- // Save the touch move info
- mIsScrolling = true;
- // Disallow parents from intercepting touch events
- final ViewParent parent = mSv.getParent();
- if (parent != null) {
- parent.requestDisallowInterceptTouchEvent(true);
- }
- }
-
- mLastMotionX = x;
- mLastMotionY = y;
- mLastP = layoutAlgorithm.sCurve.xToP(mLastMotionY, layoutAlgorithm.mStackRect);
- break;
- }
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP: {
- // Animate the scroll back if we've cancelled
- mScroller.animateBoundScroll();
- // Reset the drag state and the velocity tracker
- mIsScrolling = false;
- mActivePointerId = INACTIVE_POINTER_ID;
- mActiveTaskView = null;
- mTotalPMotion = 0;
- recycleVelocityTracker();
- break;
- }
- }
-
- return wasScrolling || mIsScrolling;
+ return handleTouchEvent(ev);
}
/** Handles touch events once we have intercepted them */
public boolean onTouchEvent(MotionEvent ev) {
- // Short circuit if we have no children
- boolean hasTaskViews = (mSv.getTaskViews().size() > 0);
- if (!hasTaskViews) {
- return false;
- }
-
// Pass through to swipe helper if we are swiping
if (mInterceptedBySwipeHelper && mSwipeHelper.onTouchEvent(ev)) {
return true;
}
- // Update the velocity tracker
- initVelocityTrackerIfNotExists();
+ handleTouchEvent(ev);
+ return true;
+ }
- TaskStackViewLayoutAlgorithm layoutAlgorithm = mSv.mLayoutAlgorithm;
+ private boolean handleTouchEvent(MotionEvent ev) {
+ // Short circuit if we have no children
+ if (mSv.getTaskViews().size() == 0) {
+ return false;
+ }
+ // Short circuit while we are alt-tabbing
+ RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
+ if (launchState.launchedWithAltTab) {
+ return false;
+ }
+
+ final TaskStackLayoutAlgorithm layoutAlgorithm = mSv.mLayoutAlgorithm;
int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
// Save the touch down info
- mInitialMotionX = mLastMotionX = (int) ev.getX();
- mInitialMotionY = mLastMotionY = (int) ev.getY();
- mInitialP = mLastP = layoutAlgorithm.sCurve.xToP(mLastMotionY,
- layoutAlgorithm.mStackRect);
+ mDownX = (int) ev.getX();
+ mDownY = (int) ev.getY();
+ mDownScrollP = mScroller.getStackScroll();
mActivePointerId = ev.getPointerId(0);
- mActiveTaskView = findViewAtPoint(mLastMotionX, mLastMotionY);
+ mActiveTaskView = findViewAtPoint(mDownX, mDownY);
+
// Stop the current scroll if it is still flinging
mScroller.stopScroller();
mScroller.stopBoundScrollAnimation();
+ Utilities.cancelAnimationWithoutCallbacks(mScrollFlingAnimator);
+
// Initialize the velocity tracker
initOrResetVelocityTracker();
- mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
- // Disallow parents from intercepting touch events
- final ViewParent parent = mSv.getParent();
- if (parent != null) {
- parent.requestDisallowInterceptTouchEvent(true);
- }
+ mVelocityTracker.addMovement(ev);
break;
}
case MotionEvent.ACTION_POINTER_DOWN: {
final int index = ev.getActionIndex();
+ mDownX = (int) ev.getX();
+ mDownY = (int) ev.getY();
+ mDownScrollP = mScroller.getStackScroll();
mActivePointerId = ev.getPointerId(index);
- mLastMotionX = (int) ev.getX(index);
- mLastMotionY = (int) ev.getY(index);
- mLastP = layoutAlgorithm.sCurve.xToP(mLastMotionY,
- layoutAlgorithm.mStackRect);
+ mVelocityTracker.addMovement(ev);
break;
}
case MotionEvent.ACTION_MOVE: {
- if (mActivePointerId == INACTIVE_POINTER_ID) break;
-
- mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
-
int activePointerIndex = ev.findPointerIndex(mActivePointerId);
- int x = (int) ev.getX(activePointerIndex);
int y = (int) ev.getY(activePointerIndex);
- int yTotal = Math.abs(y - mInitialMotionY);
- float curP = layoutAlgorithm.sCurve.xToP(y, layoutAlgorithm.mStackRect);
- float deltaP = mLastP - curP;
if (!mIsScrolling) {
- if (yTotal > mScrollTouchSlop) {
+ if (Math.abs(y - mDownY) > mScrollTouchSlop) {
mIsScrolling = true;
+
// Disallow parents from intercepting touch events
final ViewParent parent = mSv.getParent();
if (parent != null) {
@@ -270,54 +201,17 @@
}
}
if (mIsScrolling) {
- float curStackScroll = mScroller.getStackScroll();
- float overScrollAmount = mScroller.getScrollAmountOutOfBounds(curStackScroll + deltaP);
- if (Float.compare(overScrollAmount, 0f) != 0) {
- // Bound the overscroll to a fixed amount, and inversely scale the y-movement
- // relative to how close we are to the max overscroll
- float maxOverScroll = mContext.getResources().getFloat(
- R.dimen.recents_stack_overscroll_percentage);
- deltaP *= (1f - (Math.min(maxOverScroll, overScrollAmount)
- / maxOverScroll));
+ // If we just move linearly on the screen, then that would map to 1/arclength
+ // of the curve, so just move the scroll proportional to that
+ float deltaP = layoutAlgorithm.getDeltaPForY(mDownY, y);
+ float curScrollP = mDownScrollP + deltaP;
+ mScroller.setStackScroll(curScrollP);
+ if (DEBUG) {
+ Log.d(TAG, "scroll: " + curScrollP);
}
- mScroller.setStackScroll(curStackScroll + deltaP);
- }
- mLastMotionX = x;
- mLastMotionY = y;
- mLastP = layoutAlgorithm.sCurve.xToP(mLastMotionY, layoutAlgorithm.mStackRect);
- mTotalPMotion += Math.abs(deltaP);
- break;
- }
- case MotionEvent.ACTION_UP: {
- mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
- int velocity = (int) mVelocityTracker.getYVelocity(mActivePointerId);
- if (mIsScrolling && (Math.abs(velocity) > mMinimumVelocity)) {
- float overscrollRangePct = Math.abs((float) velocity / mMaximumVelocity);
- int overscrollRange = (int) (Math.min(1f, overscrollRangePct) *
- (Constants.Values.TaskStackView.TaskStackMaxOverscrollRange -
- Constants.Values.TaskStackView.TaskStackMinOverscrollRange));
- mScroller.mScroller.fling(0,
- mScroller.progressToScrollRange(mScroller.getStackScroll()),
- 0, velocity,
- 0, 0,
- mScroller.progressToScrollRange(mSv.mLayoutAlgorithm.mMinScrollP),
- mScroller.progressToScrollRange(mSv.mLayoutAlgorithm.mMaxScrollP),
- 0, Constants.Values.TaskStackView.TaskStackMinOverscrollRange +
- overscrollRange);
- // Invalidate to kick off computeScroll
- mSv.invalidate();
- } else if (mIsScrolling && mScroller.isScrollOutOfBounds()) {
- // Animate the scroll back into bounds
- mScroller.animateBoundScroll();
- } else if (mActiveTaskView == null) {
- // This tap didn't start on a task.
- maybeHideRecentsFromBackgroundTap((int) ev.getX(), (int) ev.getY());
}
- mActivePointerId = INACTIVE_POINTER_ID;
- mIsScrolling = false;
- mTotalPMotion = 0;
- recycleVelocityTracker();
+ mVelocityTracker.addMovement(ev);
break;
}
case MotionEvent.ACTION_POINTER_UP: {
@@ -327,34 +221,54 @@
// Select a new active pointer id and reset the motion state
final int newPointerIndex = (pointerIndex == 0) ? 1 : 0;
mActivePointerId = ev.getPointerId(newPointerIndex);
- mLastMotionX = (int) ev.getX(newPointerIndex);
- mLastMotionY = (int) ev.getY(newPointerIndex);
- mLastP = layoutAlgorithm.sCurve.xToP(mLastMotionY, layoutAlgorithm.mStackRect);
- mVelocityTracker.clear();
}
+ mVelocityTracker.addMovement(ev);
+ break;
+ }
+ case MotionEvent.ACTION_UP: {
+ mVelocityTracker.addMovement(ev);
+ mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+ int activePointerIndex = ev.findPointerIndex(mActivePointerId);
+ int y = (int) ev.getY(activePointerIndex);
+ int velocity = (int) mVelocityTracker.getYVelocity(mActivePointerId);
+ if (mIsScrolling) {
+ if (mScroller.isScrollOutOfBounds()) {
+ mScroller.animateBoundScroll();
+ } else if (Math.abs(velocity) > mMinimumVelocity) {
+ float minY = mDownY + layoutAlgorithm.getYForDeltaP(mDownScrollP,
+ layoutAlgorithm.mMaxScrollP);
+ float maxY = mDownY + layoutAlgorithm.getYForDeltaP(mDownScrollP,
+ layoutAlgorithm.mMinScrollP);
+ mScroller.fling(mDownScrollP, mDownY, y, velocity, (int) minY, (int) maxY,
+ mOverscrollSize);
+ mSv.invalidate();
+ }
+ } else if (mActiveTaskView == null) {
+ // This tap didn't start on a task.
+ maybeHideRecentsFromBackgroundTap((int) ev.getX(), (int) ev.getY());
+ }
+
+ mActivePointerId = INACTIVE_POINTER_ID;
+ mIsScrolling = false;
+ recycleVelocityTracker();
break;
}
case MotionEvent.ACTION_CANCEL: {
- if (mScroller.isScrollOutOfBounds()) {
- // Animate the scroll back into bounds
- mScroller.animateBoundScroll();
- }
mActivePointerId = INACTIVE_POINTER_ID;
mIsScrolling = false;
- mTotalPMotion = 0;
recycleVelocityTracker();
break;
}
}
- return true;
+ return mIsScrolling;
}
/** Hides recents if the up event at (x, y) is a tap on the background area. */
void maybeHideRecentsFromBackgroundTap(int x, int y) {
// Ignore the up event if it's too far from its start position. The user might have been
// trying to scroll or swipe.
- int dx = Math.abs(mInitialMotionX - x);
- int dy = Math.abs(mInitialMotionY - y);
+ int dx = Math.abs(mDownX - x);
+ int dy = Math.abs(mDownY - y);
if (dx > mScrollTouchSlop || dy > mScrollTouchSlop) {
return;
}
@@ -371,10 +285,20 @@
return;
}
+ // If tapping on the freeform workspace background, just launch the first freeform task
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ if (ssp.hasFreeformWorkspaceSupport()) {
+ Rect freeformRect = mSv.mLayoutAlgorithm.mFreeformRect;
+ if (freeformRect.top <= y && y <= freeformRect.bottom) {
+ if (mSv.launchFreeformTasks()) {
+ return;
+ }
+ }
+ }
+
// The user intentionally tapped on the background, which is like a tap on the "desktop".
// Hide recents and transition to the launcher.
- Recents recents = Recents.getInstanceAndStartIfNeeded(mSv.getContext());
- recents.hideRecents(false /* altTab */, true /* homeKey */);
+ EventBus.getDefault().send(new HideRecentsEvent(false, true));
}
/** Handles generic motion events */
@@ -387,13 +311,11 @@
// Find the front most task and scroll the next task to the front
float vScroll = ev.getAxisValue(MotionEvent.AXIS_VSCROLL);
if (vScroll > 0) {
- if (mSv.ensureFocusedTask(true)) {
- mSv.focusNextTask(true, false);
- }
+ mSv.setRelativeFocusedTask(true, true /* stackTasksOnly */,
+ false /* animated */);
} else {
- if (mSv.ensureFocusedTask(true)) {
- mSv.focusNextTask(false, false);
- }
+ mSv.setRelativeFocusedTask(false, true /* stackTasksOnly */,
+ false /* animated */);
}
return true;
}
@@ -416,6 +338,7 @@
@Override
public void onBeginDrag(View v) {
TaskView tv = (TaskView) v;
+ mSwipeHelper.setSnapBackTranslationX(tv.getTranslationX());
// Disable clipping with the stack while we are swiping
tv.setClipViewInStack(false);
// Disallow touch events from this task view
@@ -440,7 +363,7 @@
// Re-enable touch events from this task view
tv.setTouchEnabled(true);
// Remove the task view from the stack
- EventBus.getDefault().send(new DismissTaskEvent(tv.getTask(), tv));
+ EventBus.getDefault().send(new DismissTaskViewEvent(tv.getTask(), tv));
// Keep track of deletions by keyboard
MetricsLogger.histogram(tv.getContext(), "overview_task_dismissed_source",
Constants.Metrics.DismissSourceSwipeGesture);
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 d2003bf..4f4b91a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -31,6 +31,7 @@
import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewOutlineProvider;
@@ -40,32 +41,33 @@
import android.widget.FrameLayout;
import com.android.systemui.R;
import com.android.systemui.recents.Constants;
+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.events.EventBus;
-import com.android.systemui.recents.events.ui.DismissTaskEvent;
+import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
-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.statusbar.phone.PhoneStatusBar;
/* A task view */
public class TaskView extends FrameLayout implements Task.TaskCallbacks,
View.OnClickListener, View.OnLongClickListener {
+ private final static String TAG = "TaskView";
+ private final static boolean DEBUG = false;
+
/** The TaskView callbacks */
interface TaskViewCallbacks {
public void onTaskViewClicked(TaskView tv, Task task, boolean lockToTask);
public void onTaskViewClipStateChanged(TaskView tv);
- public void onTaskViewFocusChanged(TaskView tv, boolean focused);
}
- RecentsConfiguration mConfig;
-
float mTaskProgress;
ObjectAnimator mTaskProgressAnimator;
float mMaxDimScale;
@@ -78,7 +80,7 @@
Task mTask;
boolean mTaskDataLoaded;
boolean mIsFocused;
- boolean mFocusAnimationsEnabled;
+ boolean mIsFocusAnimated;
boolean mClipViewInStack;
AnimateableViewBounds mViewBounds;
@@ -118,8 +120,8 @@
public TaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ RecentsConfiguration config = Recents.getConfiguration();
Resources res = context.getResources();
- mConfig = RecentsConfiguration.getInstance();
mMaxDimScale = res.getInteger(R.integer.recents_max_task_stack_view_dim) / 255f;
mClipViewInStack = true;
mViewBounds = new AnimateableViewBounds(this, res.getDimensionPixelSize(
@@ -132,8 +134,8 @@
com.android.internal.R.interpolator.decelerate_quint);
setTaskProgress(getTaskProgress());
setDim(getDim());
- if (mConfig.fakeShadows) {
- setBackground(new FakeShadowDrawable(res, mConfig));
+ if (config.fakeShadows) {
+ setBackground(new FakeShadowDrawable(res, config));
}
setOutlineProvider(mViewBounds);
}
@@ -167,7 +169,6 @@
mContent = findViewById(R.id.task_view_content);
mHeaderView = (TaskViewHeader) findViewById(R.id.task_view_bar);
mThumbnailView = (TaskViewThumbnail) findViewById(R.id.task_view_thumbnail);
- mThumbnailView.updateClipToTaskBar(mHeaderView);
mActionButtonView = findViewById(R.id.lock_to_app_fab);
mActionButtonView.setOutlineProvider(new ViewOutlineProvider() {
@Override
@@ -210,6 +211,7 @@
mThumbnailView.measure(
MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY));
+ mThumbnailView.updateClipToTaskBar(mHeaderView);
setMeasuredDimension(width, height);
invalidateOutline();
}
@@ -221,9 +223,11 @@
void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform, int duration,
ValueAnimator.AnimatorUpdateListener updateCallback) {
+ RecentsConfiguration config = Recents.getConfiguration();
+
// Apply the transform
toTransform.applyToTaskView(this, duration, mFastOutSlowInInterpolator, false,
- !mConfig.fakeShadows, updateCallback);
+ !config.fakeShadows, updateCallback);
// Update the task progress
Utilities.cancelAnimationWithoutCallbacks(mTaskProgressAnimator);
@@ -274,7 +278,8 @@
* first layout because the actual animation into recents may take a long time. */
void prepareEnterRecentsAnimation(boolean isTaskViewLaunchTargetTask,
boolean occludesLaunchTarget, int offscreenY) {
- RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ RecentsConfiguration config = Recents.getConfiguration();
+ RecentsActivityLaunchState launchState = config.getLaunchState();
int initialDim = getDim();
if (launchState.launchedHasConfigurationChanged) {
// Just load the views as-is
@@ -304,7 +309,8 @@
/** Animates this task view as it enters recents */
void startEnterRecentsAnimation(final ViewAnimation.TaskViewEnterContext ctx) {
- RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ RecentsConfiguration config = Recents.getConfiguration();
+ RecentsActivityLaunchState launchState = config.getLaunchState();
Resources res = mContext.getResources();
final TaskViewTransform transform = ctx.currentTaskTransform;
final int transitionEnterFromAppDelay = res.getInteger(
@@ -319,7 +325,6 @@
R.integer.recents_task_enter_from_home_stagger_delay);
final int taskViewAffiliateGroupEnterOffset = res.getDimensionPixelSize(
R.dimen.recents_task_view_affiliate_group_enter_offset);
- int startDelay = 0;
if (launchState.launchedFromAppWithThumbnail) {
if (mTask.isLaunchTarget) {
@@ -368,7 +373,6 @@
ctx.postAnimationTrigger.increment();
}
}
- startDelay = transitionEnterFromAppDelay;
} else if (launchState.launchedFromHome) {
// Animate the tasks up
@@ -378,7 +382,7 @@
setScaleX(transform.scale);
setScaleY(transform.scale);
- if (!mConfig.fakeShadows) {
+ if (!config.fakeShadows) {
animate().translationZ(transform.translationZ);
}
animate()
@@ -397,17 +401,7 @@
})
.start();
ctx.postAnimationTrigger.increment();
- startDelay = delay;
}
-
- // Enable the focus animations from this point onwards so that they aren't affected by the
- // window transitions
- postDelayed(new Runnable() {
- @Override
- public void run() {
- enableFocusAnimations();
- }
- }, startDelay);
}
public void fadeInActionButton(int delay, int duration) {
@@ -549,7 +543,7 @@
startDeleteTaskAnimation(new Runnable() {
@Override
public void run() {
- EventBus.getDefault().send(new DismissTaskEvent(mTask, tv));
+ EventBus.getDefault().send(new DismissTaskViewEvent(mTask, tv));
}
}, 0);
}
@@ -559,7 +553,11 @@
* view.
*/
boolean shouldClipViewInStack() {
- return mClipViewInStack && (getVisibility() == View.VISIBLE);
+ // Never clip for freeform tasks or if invisible
+ if (mTask.isFreeformTask() || getVisibility() != View.VISIBLE) {
+ return false;
+ }
+ return mClipViewInStack;
}
/** Sets whether this view should be clipped, or clipped against. */
@@ -586,8 +584,10 @@
/** Returns the current dim. */
public void setDim(int dim) {
+ RecentsConfiguration config = Recents.getConfiguration();
+
mDimAlpha = dim;
- if (mConfig.useHardwareLayers) {
+ if (config.useHardwareLayers) {
// Defer setting hardware layers if we have not yet measured, or there is no dim to draw
if (getMeasuredWidth() > 0 && getMeasuredHeight() > 0) {
mDimColorFilter.setColor(Color.argb(mDimAlpha, 0, 0, 0));
@@ -622,6 +622,8 @@
anim.addListener(postAnimRunnable);
}
anim.start();
+ } else {
+ postAnimRunnable.onAnimationEnd(null);
}
}
@@ -640,61 +642,53 @@
setDim(getDimFromTaskProgress());
}
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ super.dispatchDraw(canvas);
+ if (Constants.DebugFlags.App.EnableFastToggleRecents && mIsFocused) {
+ Paint tmpPaint = new Paint();
+ Rect tmpRect = new Rect();
+ tmpRect.set(0, 0, getWidth(), getHeight());
+ tmpPaint.setColor(0xFFFF0000);
+ tmpPaint.setStrokeWidth(35);
+ tmpPaint.setStyle(Paint.Style.STROKE);
+ canvas.drawRect(tmpRect, tmpPaint);
+ }
+ }
+
/**** View focus state ****/
/**
- * Sets the focused task explicitly. We need a separate flag because requestFocus() won't happen
- * if the view is not currently visible, or we are in touch state (where we still want to keep
- * track of focus).
+ * Explicitly sets the focused state of this task.
*/
- public void setFocusedTask(boolean animateFocusedState) {
- mIsFocused = true;
- if (mFocusAnimationsEnabled) {
- // Focus the header bar
- mHeaderView.onTaskViewFocusChanged(true, animateFocusedState);
- }
- // Update the thumbnail alpha with the focus
- mThumbnailView.onFocusChanged(true);
- // Call the callback
- if (mCb != null) {
- mCb.onTaskViewFocusChanged(this, true);
- }
- // Workaround, we don't always want it focusable in touch mode, but we want the first task
- // to be focused after the enter-recents animation, which can be triggered from either touch
- // or keyboard
- setFocusableInTouchMode(true);
- requestFocus();
- setFocusableInTouchMode(false);
- invalidate();
- }
-
- /**
- * Unsets the focused task explicitly.
- */
- void unsetFocusedTask() {
- mIsFocused = false;
- if (mFocusAnimationsEnabled) {
- // Un-focus the header bar
- mHeaderView.onTaskViewFocusChanged(false, true);
+ public void setFocusedState(boolean isFocused, boolean animated, boolean requestViewFocus) {
+ if (DEBUG) {
+ Log.d(TAG, "setFocusedState: " + mTask.activityLabel + " focused: " + isFocused +
+ " mIsFocused: " + mIsFocused + " animated: " + animated +
+ " requestViewFocus: " + requestViewFocus + " isFocused(): " + isFocused() +
+ " isAccessibilityFocused(): " + isAccessibilityFocused());
}
- // Update the thumbnail alpha with the focus
- mThumbnailView.onFocusChanged(false);
- // Call the callback
- if (mCb != null) {
- mCb.onTaskViewFocusChanged(this, false);
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ mIsFocused = isFocused;
+ mIsFocusAnimated = animated;
+ mHeaderView.onTaskViewFocusChanged(isFocused, animated);
+ mThumbnailView.onFocusChanged(isFocused);
+ if (isFocused) {
+ if (requestViewFocus && !isFocused()) {
+ requestFocus();
+ }
+ if (requestViewFocus && !isAccessibilityFocused() && ssp.isTouchExplorationEnabled()) {
+ requestAccessibilityFocus();
+ }
+ } else {
+ if (isAccessibilityFocused() && ssp.isTouchExplorationEnabled()) {
+ clearAccessibilityFocus();
+ }
}
- invalidate();
- }
-
- /**
- * Updates the explicitly focused state when the view focus changes.
- */
- @Override
- protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
- super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
- if (!gainFocus) {
- unsetFocusedTask();
+ if (Constants.DebugFlags.App.EnableFastToggleRecents) {
+ invalidate();
}
}
@@ -702,17 +696,14 @@
* Returns whether we have explicitly been focused.
*/
public boolean isFocusedTask() {
- return mIsFocused || isFocused();
+ return mIsFocused;
}
- /** Enables all focus animations. */
- void enableFocusAnimations() {
- boolean wasFocusAnimationsEnabled = mFocusAnimationsEnabled;
- mFocusAnimationsEnabled = true;
- if (mIsFocused && !wasFocusAnimationsEnabled) {
- // Re-notify the header if we were focused and animations were not previously enabled
- mHeaderView.onTaskViewFocusChanged(true, true);
- }
+ /**
+ * Returns whether this focused task is animated.
+ */
+ public boolean isFocusAnimated() {
+ return mIsFocusAnimated;
}
public void disableLayersForOneFrame() {
@@ -736,13 +727,22 @@
@Override
public void onTaskDataLoaded() {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ RecentsConfiguration config = Recents.getConfiguration();
if (mThumbnailView != null && mHeaderView != null) {
// Bind each of the views to the new task data
mThumbnailView.rebindToTask(mTask);
mHeaderView.rebindToTask(mTask);
// Rebind any listeners
mActionButtonView.setOnClickListener(this);
- setOnLongClickListener(mConfig.hasDockedTasks ? null : this);
+
+ // Only enable long-click if we have a freeform workspace to drag to/from, or if we
+ // aren't already docked
+ if (ssp.hasFreeformWorkspaceSupport() || !config.hasDockedTasks) {
+ setOnLongClickListener(this);
+ } else {
+ setOnLongClickListener(null);
+ }
}
mTaskDataLoaded = true;
}
@@ -761,7 +761,7 @@
}
@Override
- public void onMultiStackDebugTaskStackIdChanged() {
+ public void onTaskStackIdChanged() {
mHeaderView.rebindToTask(mTask);
}
@@ -786,18 +786,31 @@
// Start listening for drag events
setClipViewInStack(false);
- int width = (int) (getScaleX() * getWidth());
- int height = (int) (getScaleY() * getHeight());
+ final float finalScale = getScaleX() * 1.05f;
+ final int width = getWidth();
+ final int height = getHeight();
Bitmap dragBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(dragBitmap);
- c.scale(getScaleX(), getScaleY());
mThumbnailView.draw(c);
mHeaderView.draw(c);
c.setBitmap(null);
+ // The downTouchPos is relative to the currently transformed TaskView, but we will be
+ // dragging a copy of the full task view, which makes it easier for us to animate them
+ // when the user drops
+ mDownTouchPos.x += ((1f - getScaleX()) * width) / 2;
+ mDownTouchPos.y += ((1f - getScaleY()) * height) / 2;
+
// Initiate the drag
final DragView dragView = new DragView(getContext(), dragBitmap, mDownTouchPos);
- dragView.setOutlineProvider(mViewBounds);
+ dragView.setOutlineProvider(new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ outline.setRect(0, 0, width, height);
+ }
+ });
+ dragView.setScaleX(getScaleX());
+ dragView.setScaleY(getScaleY());
dragView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
@@ -807,8 +820,8 @@
dragView.setElevation(getElevation());
dragView.setTranslationZ(getTranslationZ());
dragView.animate()
- .scaleX(1.05f)
- .scaleY(1.05f)
+ .scaleX(finalScale)
+ .scaleY(finalScale)
.setDuration(175)
.setInterpolator(mFastOutSlowInInterpolator)
.start();
@@ -829,18 +842,19 @@
/**** Events ****/
public final void onBusEvent(DragEndEvent event) {
- event.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
- @Override
- public void run() {
- // If docked state == null:
- // Animate the drag view back from where it is, to the view location, then after it returns,
- // update the clip state
- setClipViewInStack(true);
+ if (!(event.dropTarget instanceof TaskStack.DockState)) {
+ event.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+ @Override
+ public void run() {
+ // Show this task view
+ setVisibility(View.VISIBLE);
- // Show this task view
- setVisibility(View.VISIBLE);
- }
- });
+ // Animate the drag view back from where it is, to the view location, then after
+ // it returns, update the clip state
+ setClipViewInStack(true);
+ }
+ });
+ }
EventBus.getDefault().unregister(this);
}
}
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 949d515..649199e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -39,7 +39,6 @@
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewOutlineProvider;
-import android.view.accessibility.AccessibilityManager;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
@@ -48,13 +47,12 @@
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.recents.Constants;
-import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.Recents;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.ui.ResizeTaskEvent;
import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
-import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.Task;
@@ -62,8 +60,6 @@
public class TaskViewHeader extends FrameLayout
implements View.OnClickListener, View.OnLongClickListener {
- RecentsConfiguration mConfig;
- private SystemServicesProxy mSsp;
Task mTask;
// Header views
@@ -111,8 +107,6 @@
public TaskViewHeader(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- mConfig = RecentsConfiguration.getInstance();
- mSsp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
setWillNotDraw(false);
setClipToOutline(true);
setOutlineProvider(new ViewOutlineProvider() {
@@ -249,9 +243,8 @@
mMoveTaskButton.setOnClickListener(this);
// In accessibility, a single click on the focused app info button will show it
- AccessibilityManager am = (AccessibilityManager) getContext().
- getSystemService(Context.ACCESSIBILITY_SERVICE);
- if (am != null && am.isEnabled()) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ if (ssp.isTouchExplorationEnabled()) {
mApplicationIcon.setOnClickListener(this);
}
}
@@ -262,12 +255,17 @@
mApplicationIcon.setImageDrawable(null);
mApplicationIcon.setOnClickListener(null);
mMoveTaskButton.setOnClickListener(null);
+
+ // Stop any focus animations
+ Utilities.cancelAnimationWithoutCallbacks(mFocusAnimator);
+ mBackground.jumpToCurrentState();
}
/** Updates the resize task bar button. */
void updateResizeTaskBarIcon(Task t) {
- Rect display = mSsp.getWindowRect();
- Rect taskRect = mSsp.getTaskBounds(t.key.id);
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ Rect display = ssp.getWindowRect();
+ Rect taskRect = ssp.getTaskBounds(t.key.id);
int resId = R.drawable.star;
if (display.equals(taskRect) || taskRect.isEmpty()) {
resId = R.drawable.vector_drawable_place_fullscreen;
@@ -373,16 +371,17 @@
/** Notifies the associated TaskView has been focused. */
void onTaskViewFocusChanged(boolean focused, boolean animateFocusedState) {
- // If we are not animating the visible state, just return
- if (!animateFocusedState) return;
-
boolean isRunning = false;
if (mFocusAnimator != null) {
isRunning = mFocusAnimator.isRunning();
- Utilities.cancelAnimationWithoutCallbacks(mFocusAnimator);
}
+ Utilities.cancelAnimationWithoutCallbacks(mFocusAnimator);
+ mBackground.jumpToCurrentState();
if (focused) {
+ // If we are not animating the visible state, just return
+ if (!animateFocusedState) return;
+
int currentColor = mBackgroundColor;
int secondaryColor = getSecondaryColor(mCurrentPrimaryColor, mCurrentPrimaryColorIsDark);
int[][] states = new int[][] {
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 f8f7052..174ff33 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
@@ -17,7 +17,7 @@
package com.android.systemui.recents.views;
import android.animation.ValueAnimator;
-import android.graphics.Rect;
+import android.graphics.RectF;
import android.view.View;
import android.view.ViewPropertyAnimator;
import android.view.animation.Interpolator;
@@ -26,6 +26,7 @@
/* The transform state for a task view */
public class TaskViewTransform {
public int startDelay = 0;
+ public int translationX = 0;
public int translationY = 0;
public float translationZ = 0;
public float scale = 1f;
@@ -35,7 +36,7 @@
// This is a window-space rect that is purely used for coordinating the animation of an app
// window into Recents.
- public Rect rect = new Rect();
+ public RectF rect = new RectF();
public TaskViewTransform() {
// Do nothing
@@ -43,6 +44,7 @@
public TaskViewTransform(TaskViewTransform o) {
startDelay = o.startDelay;
+ translationX = o.translationX;
translationY = o.translationY;
translationZ = o.translationZ;
scale = o.scale;
@@ -52,9 +54,12 @@
p = o.p;
}
- /** Resets the current transform */
+ /**
+ * Resets the current transform.
+ */
public void reset() {
startDelay = 0;
+ translationX = 0;
translationY = 0;
translationZ = 0;
scale = 1f;
@@ -71,6 +76,9 @@
public boolean hasScaleChangedFrom(float v) {
return (Float.compare(scale, v) != 0);
}
+ public boolean hasTranslationXChangedFrom(float v) {
+ return (Float.compare(translationX, v) != 0);
+ }
public boolean hasTranslationYChangedFrom(float v) {
return (Float.compare(translationY, v) != 0);
}
@@ -87,6 +95,9 @@
boolean requiresLayers = false;
// Animate to the final state
+ if (hasTranslationXChangedFrom(v.getTranslationX())) {
+ anim.translationX(translationX);
+ }
if (hasTranslationYChangedFrom(v.getTranslationY())) {
anim.translationY(translationY);
}
@@ -117,6 +128,9 @@
.start();
} else {
// Set the changed properties
+ if (hasTranslationXChangedFrom(v.getTranslationX())) {
+ v.setTranslationX(translationX);
+ }
if (hasTranslationYChangedFrom(v.getTranslationY())) {
v.setTranslationY(translationY);
}
@@ -147,7 +161,8 @@
@Override
public String toString() {
- return "TaskViewTransform delay: " + startDelay + " y: " + translationY + " z: " + translationZ +
+ return "TaskViewTransform delay: " + startDelay +
+ " x: " + translationX + " y: " + translationY + " z: " + translationZ +
" scale: " + scale + " alpha: " + alpha + " visible: " + visible + " rect: " + rect +
" p: " + p;
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
new file mode 100644
index 0000000..dd894ce
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -0,0 +1,68 @@
+/*
+ * 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.systemui.stackdivider;
+
+import android.content.res.Configuration;
+import android.view.LayoutInflater;
+
+import com.android.systemui.R;
+import com.android.systemui.SystemUI;
+
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+
+/**
+ * Controls the docked stack divider.
+ */
+public class Divider extends SystemUI {
+ private static final String TAG = "Divider";
+ private int mDividerWindowWidth;
+ private DividerWindowManager mWindowManager;
+
+ @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());
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ update(newConfig);
+ }
+
+ private void addDivider(Configuration configuration) {
+ DividerView view = (DividerView)
+ LayoutInflater.from(mContext).inflate(R.layout.docked_stack_divider, null);
+ final boolean landscape = configuration.orientation == ORIENTATION_LANDSCAPE;
+ final int width = landscape ? mDividerWindowWidth : MATCH_PARENT;
+ final int height = landscape ? MATCH_PARENT : mDividerWindowWidth;
+ mWindowManager.add(view, width, height);
+ view.setWindowManager(mWindowManager);
+ }
+
+ private void removeDivider() {
+ mWindowManager.remove();
+ }
+
+ private void update(Configuration configuration) {
+ removeDivider();
+ addDivider(configuration);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerSnapAlgorithm.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerSnapAlgorithm.java
new file mode 100644
index 0000000..5f983c5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerSnapAlgorithm.java
@@ -0,0 +1,130 @@
+/*
+ * 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.systemui.stackdivider;
+
+import android.content.Context;
+import android.util.DisplayMetrics;
+import android.view.DisplayInfo;
+
+import com.android.systemui.statusbar.FlingAnimationUtils;
+
+import java.util.ArrayList;
+
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+
+/**
+ * Calculates the snap targets and the snap position given a position and a velocity. All positions
+ * here are to be interpreted as the left/top edge of the divider rectangle.
+ */
+public class DividerSnapAlgorithm {
+
+ private final Context mContext;
+ private final FlingAnimationUtils mFlingAnimationUtils;
+ private final int mDividerSize;
+ private final ArrayList<SnapTarget> mTargets;
+
+ /** The first target which is still splitting the screen */
+ private final SnapTarget mFirstSplitTarget;
+
+ /** The last target which is still splitting the screen */
+ private final SnapTarget mLastSplitTarget;
+
+ private final SnapTarget mDismissStartTarget;
+ private final SnapTarget mDismissEndTarget;
+
+ public DividerSnapAlgorithm(Context ctx, FlingAnimationUtils flingAnimationUtils,
+ int dividerSize, boolean isHorizontalDivision) {
+ mContext = ctx;
+ mFlingAnimationUtils = flingAnimationUtils;
+ mDividerSize = dividerSize;
+ mTargets = calculateTargets(isHorizontalDivision);
+ mFirstSplitTarget = mTargets.get(1);
+ mLastSplitTarget = mTargets.get(mTargets.size() - 2);
+ mDismissStartTarget = mTargets.get(0);
+ mDismissEndTarget = mTargets.get(mTargets.size() - 1);
+ }
+
+ public SnapTarget calculateSnapTarget(int position, float velocity) {
+ if (Math.abs(velocity) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
+ return snap(position);
+ }
+ if (position < mFirstSplitTarget.position && velocity < 0) {
+ return mDismissStartTarget;
+ }
+ if (position > mLastSplitTarget.position && velocity > 0) {
+ return mDismissEndTarget;
+ }
+ if (velocity < 0) {
+ return mFirstSplitTarget;
+ } else {
+ return mLastSplitTarget;
+ }
+ }
+
+ private SnapTarget snap(int position) {
+ int minIndex = -1;
+ int minDistance = Integer.MAX_VALUE;
+ int size = mTargets.size();
+ for (int i = 0; i < size; i++) {
+ int distance = Math.abs(position - mTargets.get(i).position);
+ if (distance < minDistance) {
+ minIndex = i;
+ minDistance = distance;
+ }
+ }
+ return mTargets.get(minIndex);
+ }
+
+ private ArrayList<SnapTarget> calculateTargets(boolean isHorizontalDivision) {
+ ArrayList<SnapTarget> targets = new ArrayList<>();
+ DisplayMetrics info = mContext.getResources().getDisplayMetrics();
+ int dividerMax = isHorizontalDivision
+ ? info.heightPixels
+ : info.widthPixels;
+
+ // TODO: Better calculation
+ targets.add(new SnapTarget(-mDividerSize, SnapTarget.FLAG_DISMISS_START));
+ targets.add(new SnapTarget((int) (0.35f * dividerMax) - mDividerSize / 2,
+ SnapTarget.FLAG_NONE));
+ targets.add(new SnapTarget(dividerMax / 2 - mDividerSize / 2, SnapTarget.FLAG_NONE));
+ targets.add(new SnapTarget((int) (0.65f * dividerMax) - mDividerSize / 2,
+ SnapTarget.FLAG_NONE));
+ targets.add(new SnapTarget(dividerMax, SnapTarget.FLAG_DISMISS_END));
+ return targets;
+ }
+
+ /**
+ * Represents a snap target for the divider.
+ */
+ public static class SnapTarget {
+ public static final int FLAG_NONE = 0;
+
+ /** If the divider reaches this value, the left/top task should be dismissed. */
+ public static final int FLAG_DISMISS_START = 1;
+
+ /** If the divider reaches this value, the right/bottom task should be dismissed */
+ public static final int FLAG_DISMISS_END = 2;
+
+ public final int position;
+ public final int flag;
+
+ public SnapTarget(int position, int flag) {
+ this.position = position;
+ this.flag = flag;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
new file mode 100644
index 0000000..59d4011
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -0,0 +1,322 @@
+/*
+ * 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.systemui.stackdivider;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.graphics.Region.Op;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.View.OnTouchListener;
+import android.view.ViewTreeObserver.InternalInsetsInfo;
+import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
+import android.view.WindowManager;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+import android.widget.FrameLayout;
+import android.widget.ImageButton;
+
+import com.android.systemui.R;
+import com.android.systemui.stackdivider.DividerSnapAlgorithm.SnapTarget;
+import com.android.systemui.statusbar.FlingAnimationUtils;
+
+import static android.view.PointerIcon.STYLE_HORIZONTAL_DOUBLE_ARROW;
+import static android.view.PointerIcon.STYLE_VERTICAL_DOUBLE_ARROW;
+
+/**
+ * Docked stack divider.
+ */
+public class DividerView extends FrameLayout implements OnTouchListener,
+ OnComputeInternalInsetsListener {
+
+ private static final String TAG = "DividerView";
+
+ private ImageButton mHandle;
+ private View mBackground;
+ private int mStartX;
+ private int mStartY;
+ private int mStartPosition;
+ private int mDockSide;
+ private final int[] mTempInt2 = new int[2];
+
+ private int mDividerInsets;
+ private int mDisplayWidth;
+ private int mDisplayHeight;
+ private int mDividerWindowWidth;
+ private int mDividerSize;
+ private int mTouchElevation;
+
+ private final Rect mTmpRect = new Rect();
+ private final Rect mLastResizeRect = new Rect();
+ private final WindowManagerProxy mWindowManagerProxy = new WindowManagerProxy();
+ private Interpolator mFastOutSlowInInterpolator;
+ private final Interpolator mTouchResponseInterpolator =
+ new PathInterpolator(0.3f, 0f, 0.1f, 1f);
+ private DividerWindowManager mWindowManager;
+ private VelocityTracker mVelocityTracker;
+ private FlingAnimationUtils mFlingAnimationUtils;
+
+ public DividerView(Context context) {
+ super(context);
+ }
+
+ public DividerView(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public DividerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public DividerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mHandle = (ImageButton) findViewById(R.id.docked_divider_handle);
+ mBackground = findViewById(R.id.docked_divider_background);
+ mHandle.setOnTouchListener(this);
+ mDividerWindowWidth = getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.docked_stack_divider_thickness);
+ mDividerInsets = getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.docked_stack_divider_insets);
+ mDividerSize = mDividerWindowWidth - 2 * mDividerInsets;
+ mTouchElevation = getResources().getDimensionPixelSize(
+ R.dimen.docked_stack_divider_lift_elevation);
+ mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(),
+ android.R.interpolator.fast_out_slow_in);
+ mFlingAnimationUtils = new FlingAnimationUtils(getContext(), 0.3f);
+ updateDisplayInfo();
+ boolean landscape = getResources().getConfiguration().orientation
+ == Configuration.ORIENTATION_LANDSCAPE;
+ mHandle.setPointerShape(
+ landscape ? STYLE_HORIZONTAL_DOUBLE_ARROW : STYLE_VERTICAL_DOUBLE_ARROW);
+ getViewTreeObserver().addOnComputeInternalInsetsListener(this);
+ }
+
+ public void setWindowManager(DividerWindowManager windowManager) {
+ mWindowManager = windowManager;
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ convertToScreenCoordinates(event);
+ final int action = event.getAction() & MotionEvent.ACTION_MASK;
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ mVelocityTracker = VelocityTracker.obtain();
+ mVelocityTracker.addMovement(event);
+ mStartX = (int) event.getX();
+ mStartY = (int) event.getY();
+ getLocationOnScreen(mTempInt2);
+ mDockSide = mWindowManagerProxy.getDockSide();
+ if (isHorizontalDivision()) {
+ mStartPosition = mTempInt2[1] + mDividerInsets;
+ } else {
+ mStartPosition = mTempInt2[0] + mDividerInsets;
+ }
+ if (mDockSide != WindowManager.DOCKED_INVALID) {
+ mWindowManagerProxy.setResizing(true);
+ mWindowManager.setSlippery(false);
+ liftBackground();
+ return true;
+ } else {
+ return false;
+ }
+ case MotionEvent.ACTION_MOVE:
+ mVelocityTracker.addMovement(event);
+ int x = (int) event.getX();
+ int y = (int) event.getY();
+ if (mDockSide != WindowManager.DOCKED_INVALID) {
+ resizeStack(calculatePosition(x, y));
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ mVelocityTracker.addMovement(event);
+
+ x = (int) event.getRawX();
+ y = (int) event.getRawY();
+
+ mVelocityTracker.computeCurrentVelocity(1000);
+ fling(x, y, mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
+
+ mWindowManager.setSlippery(true);
+ releaseBackground();
+ break;
+ }
+ return true;
+ }
+
+ private void convertToScreenCoordinates(MotionEvent event) {
+ event.setLocation(event.getRawX(), event.getRawY());
+ }
+
+ private void fling(int x, int y, float xVelocity, float yVelocity) {
+ int position = calculatePosition(x, y);
+ float velocity = isHorizontalDivision() ? yVelocity : xVelocity;
+ final SnapTarget snapTarget = new DividerSnapAlgorithm(getContext(), mFlingAnimationUtils,
+ mDividerSize, isHorizontalDivision()).calculateSnapTarget(position, velocity);
+
+ ValueAnimator anim = ValueAnimator.ofInt(position, snapTarget.position);
+ anim.addUpdateListener(new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ resizeStack((Integer) animation.getAnimatedValue());
+ }
+ });
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ commitSnapFlags(snapTarget);
+ mWindowManagerProxy.setResizing(false);
+ mDockSide = WindowManager.DOCKED_INVALID;
+ }
+ });
+ mFlingAnimationUtils.apply(anim, position, snapTarget.position, velocity);
+ anim.start();
+ }
+
+ private void commitSnapFlags(SnapTarget target) {
+ if (target.flag == SnapTarget.FLAG_NONE) {
+ return;
+ }
+ boolean dismissOrMaximize;
+ if (target.flag == SnapTarget.FLAG_DISMISS_START) {
+ dismissOrMaximize = mDockSide == WindowManager.DOCKED_LEFT
+ || mDockSide == WindowManager.DOCKED_TOP;
+ } else {
+ dismissOrMaximize = mDockSide == WindowManager.DOCKED_RIGHT
+ || mDockSide == WindowManager.DOCKED_BOTTOM;
+ }
+ if (dismissOrMaximize) {
+ mWindowManagerProxy.dismissDockedStack();
+ } else {
+ mWindowManagerProxy.maximizeDockedStack();
+ }
+ }
+
+ private void liftBackground() {
+ if (isHorizontalDivision()) {
+ mBackground.animate().scaleY(1.5f);
+ } else {
+ mBackground.animate().scaleX(1.5f);
+ }
+ mBackground.animate()
+ .setInterpolator(mTouchResponseInterpolator)
+ .setDuration(150)
+ .translationZ(mTouchElevation);
+
+ // Lift handle as well so it doesn't get behind the background, even though it doesn't
+ // cast shadow.
+ mHandle.animate()
+ .setInterpolator(mTouchResponseInterpolator)
+ .setDuration(150)
+ .translationZ(mTouchElevation);
+ }
+
+ private void releaseBackground() {
+ mBackground.animate()
+ .setInterpolator(mFastOutSlowInInterpolator)
+ .setDuration(200)
+ .translationZ(0)
+ .scaleX(1f)
+ .scaleY(1f);
+ mHandle.animate()
+ .setInterpolator(mFastOutSlowInInterpolator)
+ .setDuration(200)
+ .translationZ(0);
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ updateDisplayInfo();
+ }
+
+ private void updateDisplayInfo() {
+ DisplayMetrics info = mContext.getResources().getDisplayMetrics();
+ mDisplayWidth = info.widthPixels;
+ mDisplayHeight = info.heightPixels;
+ }
+
+ private int calculatePosition(int touchX, int touchY) {
+ return isHorizontalDivision() ? calculateYPosition(touchY) : calculateXPosition(touchX);
+ }
+
+ private boolean isHorizontalDivision() {
+ return mDockSide == WindowManager.DOCKED_TOP
+ || mDockSide == WindowManager.DOCKED_BOTTOM;
+ }
+
+ private int calculateXPosition(int touchX) {
+ return mStartPosition + touchX - mStartX;
+ }
+
+ private int calculateYPosition(int touchY) {
+ return mStartPosition + touchY - mStartY;
+ }
+
+ private void resizeStack(int position) {
+ mTmpRect.set(0, 0, mDisplayWidth, mDisplayHeight);
+ switch (mDockSide) {
+ case WindowManager.DOCKED_LEFT:
+ mTmpRect.right = position;
+ break;
+ case WindowManager.DOCKED_TOP:
+ mTmpRect.bottom = position;
+ break;
+ case WindowManager.DOCKED_RIGHT:
+ mTmpRect.left = position + mDividerWindowWidth - 2 * mDividerInsets;
+ break;
+ case WindowManager.DOCKED_BOTTOM:
+ mTmpRect.top = position + mDividerWindowWidth - 2 * mDividerInsets;
+ break;
+ }
+ if (mTmpRect.equals(mLastResizeRect)) {
+ return;
+ }
+
+ // Make sure shadows are updated
+ mBackground.invalidate();
+
+ mLastResizeRect.set(mTmpRect);
+ mWindowManagerProxy.resizeDockedStack(mTmpRect);
+ }
+
+ @Override
+ public void onComputeInternalInsets(InternalInsetsInfo inoutInfo) {
+ inoutInfo.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+ inoutInfo.touchableRegion.set(mHandle.getLeft(), mHandle.getTop(), mHandle.getRight(),
+ mHandle.getBottom());
+ inoutInfo.touchableRegion.op(mBackground.getLeft(), mBackground.getTop(),
+ mBackground.getRight(), mBackground.getBottom(), Op.UNION);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
new file mode 100644
index 0000000..2251874
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
@@ -0,0 +1,77 @@
+/*
+ * 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.systemui.stackdivider;
+
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.view.View;
+import android.view.WindowManager;
+
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
+import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
+import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
+
+/**
+ * Manages the window parameters of the docked stack divider.
+ */
+public class DividerWindowManager {
+
+ private static final String WINDOW_TITLE = "DockedStackDivider";
+
+ private final WindowManager mWindowManager;
+ private WindowManager.LayoutParams mLp;
+ private View mView;
+
+ public DividerWindowManager(Context ctx) {
+ mWindowManager = ctx.getSystemService(WindowManager.class);
+ }
+
+ public void add(View view, int width, int height) {
+ mLp = new WindowManager.LayoutParams(
+ width, height, TYPE_DOCK_DIVIDER,
+ FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL
+ | FLAG_WATCH_OUTSIDE_TOUCH | FLAG_SPLIT_TOUCH | FLAG_SLIPPERY,
+ PixelFormat.TRANSLUCENT);
+ mLp.setTitle(WINDOW_TITLE);
+ mWindowManager.addView(view, mLp);
+ mView = view;
+ }
+
+ public void remove() {
+ if (mView != null) {
+ mWindowManager.removeView(mView);
+ }
+ mView = null;
+ }
+
+ public void setSlippery(boolean slippery) {
+ boolean changed = false;
+ if (slippery && (mLp.flags & FLAG_SLIPPERY) == 0) {
+ mLp.flags |= FLAG_SLIPPERY;
+ changed = true;
+ } else if (!slippery && (mLp.flags & FLAG_SLIPPERY) != 0) {
+ mLp.flags &= ~FLAG_SLIPPERY;
+ changed = true;
+ }
+ if (changed) {
+ mWindowManager.updateViewLayout(mView, mLp);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
new file mode 100644
index 0000000..0d3f803
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
@@ -0,0 +1,117 @@
+/*
+ * 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.systemui.stackdivider;
+
+import android.app.ActivityManagerNative;
+import android.graphics.Rect;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.WindowManagerGlobal;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.view.WindowManager.DOCKED_INVALID;
+
+/**
+ * Proxy to simplify calls into window manager/activity manager
+ */
+public class WindowManagerProxy {
+
+ private static final String TAG = "WindowManagerProxy";
+
+ @GuardedBy("mResizeRect")
+ private final Rect mResizeRect = new Rect();
+ private final Rect mTmpRect = new Rect();
+ private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
+
+ private final Runnable mResizeRunnable = new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mResizeRect) {
+ mTmpRect.set(mResizeRect);
+ }
+ try {
+ ActivityManagerNative.getDefault().resizeStack(DOCKED_STACK_ID, mTmpRect, true);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to resize stack: " + e);
+ }
+ }
+ };
+
+ private final Runnable mDismissRunnable = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ ActivityManagerNative.getDefault().removeStack(DOCKED_STACK_ID);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to remove stack: " + e);
+ }
+ }
+ };
+
+ private final Runnable mMaximizeRunnable = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ ActivityManagerNative.getDefault().resizeStack(DOCKED_STACK_ID, null, true);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to resize stack: " + e);
+ }
+ }
+ };
+
+ public void resizeDockedStack(Rect rect) {
+ synchronized (mResizeRect) {
+ mResizeRect.set(rect);
+ }
+ mExecutor.execute(mResizeRunnable);
+ }
+
+ public void dismissDockedStack() {
+ mExecutor.execute(mDismissRunnable);
+ }
+
+ public void maximizeDockedStack() {
+ mExecutor.execute(mMaximizeRunnable);
+ }
+
+ public void setResizing(final boolean resizing) {
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ WindowManagerGlobal.getWindowManagerService().setDockedStackResizing(resizing);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Error calling setDockedStackResizing: " + e);
+ }
+ }
+ });
+ }
+
+ public int getDockSide() {
+ try {
+ return WindowManagerGlobal.getWindowManagerService().getDockedStackSide();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to get dock side: " + e);
+ }
+ return DOCKED_INVALID;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index 2e3e00c..6f8cd8c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -340,7 +340,7 @@
return;
}
mDark = dark;
- if (!dark && fade) {
+ if (!dark && fade && !shouldHideBackground()) {
if (mActivated) {
mBackgroundDimmed.setVisibility(View.VISIBLE);
mBackgroundNormal.setVisibility(View.VISIBLE);
@@ -380,7 +380,7 @@
updateBackgroundTint();
}
- private void updateBackgroundTint() {
+ protected void updateBackgroundTint() {
int color = getBgColor();
int rippleColor = getRippleColor();
if (color == mNormalColor) {
@@ -428,10 +428,12 @@
private void fadeDimmedBackground() {
mBackgroundDimmed.animate().cancel();
mBackgroundNormal.animate().cancel();
- if (mDimmed) {
- mBackgroundDimmed.setVisibility(View.VISIBLE);
- } else {
- mBackgroundNormal.setVisibility(View.VISIBLE);
+ if (!shouldHideBackground()) {
+ if (mDimmed) {
+ mBackgroundDimmed.setVisibility(View.VISIBLE);
+ } else {
+ mBackgroundNormal.setVisibility(View.VISIBLE);
+ }
}
float startAlpha = mDimmed ? 1f : 0;
float endAlpha = mDimmed ? 0 : 1f;
@@ -466,9 +468,9 @@
mBackgroundAnimator.start();
}
- private void updateBackground() {
+ protected void updateBackground() {
cancelFadeAnimations();
- if (mDark) {
+ if (shouldHideBackground()) {
mBackgroundDimmed.setVisibility(View.INVISIBLE);
mBackgroundNormal.setVisibility(View.INVISIBLE);
} else if (mDimmed) {
@@ -482,6 +484,10 @@
}
}
+ protected boolean shouldHideBackground() {
+ return mDark;
+ }
+
private void cancelFadeAnimations() {
if (mBackgroundAnimator != null) {
mBackgroundAnimator.cancel();
@@ -676,7 +682,7 @@
protected abstract View getContentView();
- private int getBgColor() {
+ public int getBgColor() {
if (mBgTint != 0) {
return mBgTint;
} else if (mShowingLegacyBackground) {
@@ -739,6 +745,10 @@
setBelowSpeedBump(false);
}
+ public boolean hasSameBgColor(ActivatableNotificationView otherView) {
+ return getBgColor() == otherView.getBgColor();
+ }
+
public interface OnActivatedListener {
void onActivated(ActivatableNotificationView view);
void onActivationReset(ActivatableNotificationView view);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index aaed735..3f0000e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -124,7 +124,7 @@
private static final boolean NOTIFICATION_CLICK_DEBUG = true;
public static final boolean ENABLE_REMOTE_INPUT =
- Build.IS_DEBUGGABLE && SystemProperties.getBoolean("debug.enable_remote_input", false);
+ SystemProperties.getBoolean("debug.enable_remote_input", true);
public static final boolean ENABLE_CHILD_NOTIFICATIONS = Build.IS_DEBUGGABLE
&& SystemProperties.getBoolean("debug.child_notifs", false);
@@ -217,7 +217,7 @@
private boolean mDeviceProvisioned = false;
- private RecentsComponent mRecents;
+ protected RecentsComponent mRecents;
protected int mZenMode;
@@ -891,8 +891,7 @@
private void bindGuts(ExpandableNotificationRow row) {
row.inflateGuts();
final StatusBarNotification sbn = row.getStatusBarNotification();
- PackageManager pmUser = getPackageManagerForUser(
- sbn.getUser().getIdentifier());
+ PackageManager pmUser = getPackageManagerForUser(mContext, sbn.getUser().getIdentifier());
row.setTag(sbn.getPackageName());
final View guts = row.getGuts();
final String pkg = sbn.getPackageName();
@@ -1270,25 +1269,27 @@
}
protected boolean inflateViews(Entry entry, ViewGroup parent) {
- PackageManager pmUser = getPackageManagerForUser(
+ PackageManager pmUser = getPackageManagerForUser(mContext,
entry.notification.getUser().getIdentifier());
int maxHeight = mRowMaxHeight;
final StatusBarNotification sbn = entry.notification;
- RemoteViews contentView = sbn.getNotification().contentView;
- RemoteViews bigContentView = sbn.getNotification().bigContentView;
- RemoteViews headsUpContentView = sbn.getNotification().headsUpContentView;
+ entry.cacheContentViews(mContext, null);
+
+ final RemoteViews contentView = entry.cachedContentView;
+ final RemoteViews bigContentView = entry.cachedBigContentView;
+ final RemoteViews headsUpContentView = entry.cachedHeadsUpContentView;
+ final RemoteViews publicContentView = entry.cachedPublicContentView;
if (contentView == null) {
+ Log.v(TAG, "no contentView for: " + sbn.getNotification());
return false;
}
if (DEBUG) {
- Log.v(TAG, "publicNotification: " + sbn.getNotification().publicVersion);
+ Log.v(TAG, "publicContentView: " + publicContentView);
}
- Notification publicNotification = sbn.getNotification().publicVersion;
-
ExpandableNotificationRow row;
// Stash away previous user expansion state so we can restore it at
@@ -1377,9 +1378,9 @@
// now the public version
View publicViewLocal = null;
- if (publicNotification != null) {
+ if (publicContentView != null) {
try {
- publicViewLocal = publicNotification.contentView.apply(
+ publicViewLocal = publicContentView.apply(
sbn.getPackageContext(mContext),
contentContainerPublic, mOnClickHandler);
@@ -1500,7 +1501,7 @@
row.setUserExpanded(userExpanded);
}
row.setUserLocked(userLocked);
- row.setStatusBarNotification(entry.notification);
+ row.setEntry(entry);
applyRemoteInput(entry);
return true;
}
@@ -1537,30 +1538,9 @@
}
if (viableAction != null) {
- Notification stripped = n.clone();
- Notification.Builder.stripForDelivery(stripped);
- stripped.extras.putBoolean("android.rebuild", true);
- stripped.actions = new Notification.Action[] { viableAction };
- stripped.extras.putBoolean("android.rebuild.contentView", true);
- stripped.contentView = null;
- stripped.extras.putBoolean("android.rebuild.bigView", true);
- stripped.bigContentView = null;
- stripped.extras.putBoolean("android.rebuild.hudView", true);
- stripped.headsUpContentView = null;
-
- stripped.extras.putParcelable(Notification.EXTRA_LARGE_ICON,
- stripped.getLargeIcon());
- if (SystemProperties.getBoolean("debug.strip_third_line", false)) {
- stripped.extras.putCharSequence(Notification.EXTRA_INFO_TEXT, null);
- stripped.extras.putCharSequence(Notification.EXTRA_SUMMARY_TEXT, null);
- }
-
- Notification rebuilt = Notification.Builder.rebuild(mContext, stripped);
-
- n.actions = rebuilt.actions;
- n.bigContentView = rebuilt.bigContentView;
- n.headsUpContentView = rebuilt.headsUpContentView;
- n.publicVersion = rebuilt.publicVersion;
+ Notification.Builder rebuilder = Notification.Builder.recoverBuilder(mContext, n);
+ rebuilder.setActions(viableAction);
+ rebuilder.build(); // will rewrite n
}
}
}
@@ -1617,6 +1597,59 @@
return null;
}
+ public void startPendingIntentDismissingKeyguard(final PendingIntent intent) {
+ if (!isDeviceProvisioned()) return;
+
+ final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
+ final boolean afterKeyguardGone = intent.isActivity()
+ && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
+ mCurrentUserId);
+ dismissKeyguardThenExecute(new OnDismissAction() {
+ public boolean onDismiss() {
+ new Thread() {
+ @Override
+ public void run() {
+ try {
+ if (keyguardShowing && !afterKeyguardGone) {
+ ActivityManagerNative.getDefault()
+ .keyguardWaitingForActivityDrawn();
+ }
+
+ // The intent we are sending is for the application, which
+ // won't have permission to immediately start an activity after
+ // the user switches to home. We know it is safe to do at this
+ // point, so make sure new activity switches are now allowed.
+ ActivityManagerNative.getDefault().resumeAppSwitches();
+ } catch (RemoteException e) {
+ }
+
+ try {
+ intent.send();
+ } catch (PendingIntent.CanceledException e) {
+ // the stack trace isn't very helpful here.
+ // Just log the exception message.
+ Log.w(TAG, "Sending intent failed: " + e);
+
+ // TODO: Dismiss Keyguard.
+ }
+ if (intent.isActivity()) {
+ mAssistManager.hideAssist();
+ overrideActivityPendingAppTransition(keyguardShowing
+ && !afterKeyguardGone);
+ }
+ }
+ }.start();
+
+ // close the shade if it was open
+ animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
+ true /* force */, true /* delayed */);
+ visibilityChanged(false);
+
+ return true;
+ }
+ }, afterKeyguardGone);
+ }
+
private final class NotificationClicker implements View.OnClickListener {
public void onClick(final View v) {
if (!(v instanceof ExpandableNotificationRow)) {
@@ -1896,27 +1929,29 @@
for (int i = 0; i < N; i++) {
NotificationData.Entry entry = activeNotifications.get(i);
if (onKeyguard) {
- entry.row.setExpansionDisabled(true);
+ entry.row.setOnKeyguard(true);
} else {
- entry.row.setExpansionDisabled(false);
- if (!entry.row.isUserLocked()) {
- boolean top = (i == 0);
- entry.row.setSystemExpanded(top);
- }
+ entry.row.setOnKeyguard(false);
+ boolean top = (i == 0);
+ entry.row.setSystemExpanded(top);
}
- boolean isInvisibleChild = !mGroupManager.isVisible(entry.notification);
+ boolean childNotification = mGroupManager.isChildInGroupWithSummary(entry.notification);
+ boolean childWithVisibleSummary = childNotification
+ && mGroupManager.getGroupSummary(entry.notification).getVisibility()
+ == View.VISIBLE;
boolean showOnKeyguard = shouldShowOnKeyguard(entry.notification);
if ((isLockscreenPublicMode() && !mShowLockscreenNotifications) ||
(onKeyguard && (visibleNotifications >= maxKeyguardNotifications
- || !showOnKeyguard || isInvisibleChild))) {
+ && !childWithVisibleSummary
+ || !showOnKeyguard))) {
entry.row.setVisibility(View.GONE);
- if (onKeyguard && showOnKeyguard && !isInvisibleChild) {
+ if (onKeyguard && showOnKeyguard && !childNotification) {
mKeyguardIconOverflowContainer.getIconsView().addNotification(entry);
}
} else {
boolean wasGone = entry.row.getVisibility() == View.GONE;
entry.row.setVisibility(View.VISIBLE);
- if (!isInvisibleChild) {
+ if (!childNotification) {
if (wasGone) {
// notify the scroller of a child addition
mStackScroller.generateAddAnimation(entry.row, true /* fromMoreCard */);
@@ -1981,12 +2016,15 @@
}
Notification n = notification.getNotification();
- if (DEBUG) {
- logUpdate(entry, n);
- }
- boolean applyInPlace = shouldApplyInPlace(entry, n);
+
+ boolean applyInPlace = !entry.cacheContentViews(mContext, notification.getNotification());
boolean shouldInterrupt = shouldInterrupt(entry, notification);
boolean alertAgain = alertAgain(entry, n);
+ if (DEBUG) {
+ Log.d(TAG, "applyInPlace=" + applyInPlace
+ + " shouldInterrupt=" + shouldInterrupt
+ + " alertAgain=" + alertAgain);
+ }
entry.notification = notification;
mGroupManager.onEntryUpdated(entry, entry.notification);
@@ -2051,101 +2089,32 @@
protected abstract void updateHeadsUp(String key, Entry entry, boolean shouldInterrupt,
boolean alertAgain);
- private void logUpdate(Entry oldEntry, Notification n) {
- StatusBarNotification oldNotification = oldEntry.notification;
- Log.d(TAG, "old notification: when=" + oldNotification.getNotification().when
- + " ongoing=" + oldNotification.isOngoing()
- + " expanded=" + oldEntry.getContentView()
- + " contentView=" + oldNotification.getNotification().contentView
- + " bigContentView=" + oldNotification.getNotification().bigContentView
- + " publicView=" + oldNotification.getNotification().publicVersion
- + " rowParent=" + oldEntry.row.getParent());
- Log.d(TAG, "new notification: when=" + n.when
- + " ongoing=" + oldNotification.isOngoing()
- + " contentView=" + n.contentView
- + " bigContentView=" + n.bigContentView
- + " publicView=" + n.publicVersion);
- }
-
- /**
- * @return whether we can just reapply the RemoteViews from a notification in-place when it is
- * updated
- */
- private boolean shouldApplyInPlace(Entry entry, Notification n) {
- StatusBarNotification oldNotification = entry.notification;
- // XXX: modify when we do something more intelligent with the two content views
- final RemoteViews oldContentView = oldNotification.getNotification().contentView;
- final RemoteViews contentView = n.contentView;
- final RemoteViews oldBigContentView = oldNotification.getNotification().bigContentView;
- final RemoteViews bigContentView = n.bigContentView;
- final RemoteViews oldHeadsUpContentView
- = oldNotification.getNotification().headsUpContentView;
- final RemoteViews headsUpContentView = n.headsUpContentView;
- final Notification oldPublicNotification = oldNotification.getNotification().publicVersion;
- final RemoteViews oldPublicContentView = oldPublicNotification != null
- ? oldPublicNotification.contentView : null;
- final Notification publicNotification = n.publicVersion;
- final RemoteViews publicContentView = publicNotification != null
- ? publicNotification.contentView : null;
- boolean contentsUnchanged = entry.getContentView() != null
- && contentView.getPackage() != null
- && oldContentView.getPackage() != null
- && oldContentView.getPackage().equals(contentView.getPackage())
- && oldContentView.getLayoutId() == contentView.getLayoutId();
- // large view may be null
- boolean bigContentsUnchanged =
- (entry.getExpandedContentView() == null && bigContentView == null)
- || ((entry.getExpandedContentView() != null && bigContentView != null)
- && bigContentView.getPackage() != null
- && oldBigContentView.getPackage() != null
- && oldBigContentView.getPackage().equals(bigContentView.getPackage())
- && oldBigContentView.getLayoutId() == bigContentView.getLayoutId());
- boolean headsUpContentsUnchanged =
- (oldHeadsUpContentView == null && headsUpContentView == null)
- || ((oldHeadsUpContentView != null && headsUpContentView != null)
- && headsUpContentView.getPackage() != null
- && oldHeadsUpContentView.getPackage() != null
- && oldHeadsUpContentView.getPackage().equals(headsUpContentView.getPackage())
- && oldHeadsUpContentView.getLayoutId() == headsUpContentView.getLayoutId());
- boolean publicUnchanged =
- (oldPublicContentView == null && publicContentView == null)
- || ((oldPublicContentView != null && publicContentView != null)
- && publicContentView.getPackage() != null
- && oldPublicContentView.getPackage() != null
- && oldPublicContentView.getPackage().equals(publicContentView.getPackage())
- && oldPublicContentView.getLayoutId() == publicContentView.getLayoutId());
- return contentsUnchanged && bigContentsUnchanged && headsUpContentsUnchanged
- && publicUnchanged;
- }
-
- private void updateNotificationViews(Entry entry, StatusBarNotification notification) {
- final RemoteViews contentView = notification.getNotification().contentView;
- final RemoteViews bigContentView = notification.getNotification().bigContentView;
- final RemoteViews headsUpContentView = notification.getNotification().headsUpContentView;
- final Notification publicVersion = notification.getNotification().publicVersion;
- final RemoteViews publicContentView = publicVersion != null ? publicVersion.contentView
- : null;
+ private void updateNotificationViews(Entry entry, StatusBarNotification sbn) {
+ final RemoteViews contentView = entry.cachedContentView;
+ final RemoteViews bigContentView = entry.cachedBigContentView;
+ final RemoteViews headsUpContentView = entry.cachedHeadsUpContentView;
+ final RemoteViews publicContentView = entry.cachedPublicContentView;
// Reapply the RemoteViews
contentView.reapply(mContext, entry.getContentView(), mOnClickHandler);
if (bigContentView != null && entry.getExpandedContentView() != null) {
- bigContentView.reapply(notification.getPackageContext(mContext),
+ bigContentView.reapply(sbn.getPackageContext(mContext),
entry.getExpandedContentView(),
mOnClickHandler);
}
View headsUpChild = entry.getHeadsUpContentView();
if (headsUpContentView != null && headsUpChild != null) {
- headsUpContentView.reapply(notification.getPackageContext(mContext),
+ headsUpContentView.reapply(sbn.getPackageContext(mContext),
headsUpChild, mOnClickHandler);
}
if (publicContentView != null && entry.getPublicContentView() != null) {
- publicContentView.reapply(notification.getPackageContext(mContext),
+ publicContentView.reapply(sbn.getPackageContext(mContext),
entry.getPublicContentView(), mOnClickHandler);
}
// update the contentIntent
- mNotificationClicker.register(entry.row, notification);
+ mNotificationClicker.register(entry.row, sbn);
- entry.row.setStatusBarNotification(notification);
+ entry.row.setEntry(entry);
entry.row.notifyContentUpdated();
entry.row.resetHeight();
@@ -2239,15 +2208,15 @@
* @return a PackageManger for userId or if userId is < 0 (USER_ALL etc) then
* return PackageManager for mContext
*/
- protected PackageManager getPackageManagerForUser(int userId) {
- Context contextForUser = mContext;
+ public static PackageManager getPackageManagerForUser(Context context, int userId) {
+ Context contextForUser = context;
// UserHandle defines special userId as negative values, e.g. USER_ALL
if (userId >= 0) {
try {
// Create a context for the correct user so if a package isn't installed
// for user 0 we can still load information about the package.
contextForUser =
- mContext.createPackageContextAsUser(mContext.getPackageName(),
+ context.createPackageContextAsUser(context.getPackageName(),
Context.CONTEXT_RESTRICTED,
new UserHandle(userId));
} catch (NameNotFoundException e) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
index 687f6c1..14051916 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
@@ -134,6 +134,8 @@
(int) (y - mInitialTouchY))) {
if (mStartingChild == null) {
mDragDownCallback.setEmptyDragAmount(0f);
+ } else {
+ mCallback.setUserLockedChild(mStartingChild, false);
}
mDraggingDown = false;
} else {
@@ -149,10 +151,7 @@
}
private boolean isFalseTouch() {
- if (mFalsingManager.isClassiferEnabled()) {
- return mFalsingManager.isFalseTouch();
- }
- return !mDraggedFarEnough;
+ return mFalsingManager.isFalseTouch() || !mDraggedFarEnough;
}
private void captureStartingChild(float x, float y) {
@@ -178,15 +177,16 @@
overshoot *= (1 - RUBBERBAND_FACTOR_STATIC);
rubberband -= overshoot;
}
- child.setContentHeight((int) (child.getMinHeight() + rubberband));
+ child.setActualHeight((int) (child.getMinHeight() + rubberband));
}
private void cancelExpansion(final ExpandableView child) {
- if (child.getContentHeight() == child.getMinHeight()) {
+ if (child.getActualHeight() == child.getMinHeight()) {
+ mCallback.setUserLockedChild(child, false);
return;
}
- ObjectAnimator anim = ObjectAnimator.ofInt(child, "contentHeight",
- child.getContentHeight(), child.getMinHeight());
+ ObjectAnimator anim = ObjectAnimator.ofInt(child, "actualHeight",
+ child.getActualHeight(), child.getMinHeight());
anim.setInterpolator(mInterpolator);
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 210be9f..3603900 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -16,9 +16,7 @@
package com.android.systemui.statusbar;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
+import android.app.Notification;
import android.content.Context;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.AnimationDrawable;
@@ -31,12 +29,13 @@
import android.view.ViewStub;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.LinearInterpolator;
+import android.widget.Chronometer;
import android.widget.ImageView;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingManager;
+import com.android.systemui.statusbar.notification.NotificationHeaderView;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
import com.android.systemui.statusbar.stack.NotificationChildrenContainer;
import com.android.systemui.statusbar.stack.StackScrollState;
import com.android.systemui.statusbar.stack.StackStateAnimator;
@@ -72,9 +71,9 @@
private boolean mIsSystemExpanded;
/**
- * Whether the notification expansion is disabled. This is the case on Keyguard.
+ * Whether the notification is on the keyguard and the expansion is disabled.
*/
- private boolean mExpansionDisabled;
+ private boolean mOnKeyguard;
private NotificationContentView mPublicLayout;
private NotificationContentView mPrivateLayout;
@@ -88,31 +87,24 @@
private NotificationGuts mGuts;
private StatusBarNotification mStatusBarNotification;
private boolean mIsHeadsUp;
- private View mExpandButton;
- private View mExpandButtonDivider;
- private ViewStub mExpandButtonStub;
+ private boolean mLastChronometerRunning = true;
+ private NotificationHeaderView mNotificationHeader;
+ private ViewStub mNotificationHeaderStub;
private ViewStub mChildrenContainerStub;
private NotificationGroupManager mGroupManager;
- private View mExpandButtonContainer;
private boolean mChildrenExpanded;
+ private boolean mIsSummaryWithChildren;
private NotificationChildrenContainer mChildrenContainer;
- private ValueAnimator mChildExpandAnimator;
- private float mChildrenExpandProgress;
- private float mExpandButtonStart;
private ViewStub mGutsStub;
- private boolean mHasExpandAction;
+ private boolean mHasNotificationHeader;
private boolean mIsSystemChildExpanded;
private boolean mIsPinned;
- private OnClickListener mExpandClickListener = new OnClickListener() {
- @Override
- public void onClick(View v) {
- mGroupManager.setGroupExpanded(mStatusBarNotification,
- !mChildrenExpanded);
- }
- };
private FalsingManager mFalsingManager;
private boolean mJustClicked;
+ private NotificationData.Entry mEntry;
+ private boolean mShowNoBackground;
+ private ExpandableNotificationRow mNotificationParent;
public NotificationContentView getPrivateLayout() {
return mPrivateLayout;
@@ -169,10 +161,11 @@
}
}
- public void setStatusBarNotification(StatusBarNotification statusBarNotification) {
+ private void setStatusBarNotification(StatusBarNotification statusBarNotification) {
mStatusBarNotification = statusBarNotification;
+ mPrivateLayout.setStatusBarNotification(statusBarNotification);
updateVetoButton();
- updateExpandButton();
+ onChildrenCountChanged();
}
public StatusBarNotification getStatusBarNotification() {
@@ -194,6 +187,7 @@
public void setGroupManager(NotificationGroupManager groupManager) {
mGroupManager = groupManager;
+ mPrivateLayout.setGroupManager(groupManager);
}
public void addChildNotification(ExpandableNotificationRow row) {
@@ -211,12 +205,45 @@
mChildrenContainerStub.inflate();
}
mChildrenContainer.addNotification(row, childIndex);
+ onChildrenCountChanged();
+ row.setIsChildInGroup(true, this);
}
public void removeChildNotification(ExpandableNotificationRow row) {
if (mChildrenContainer != null) {
mChildrenContainer.removeNotification(row);
}
+ onChildrenCountChanged();
+ row.setIsChildInGroup(false, null);
+ }
+
+ public boolean isChildInGroup() {
+ return mNotificationParent != null;
+ }
+
+ public ExpandableNotificationRow getNotificationParent() {
+ return mNotificationParent;
+ }
+
+ /**
+ * @param isChildInGroup Is this notification now in a group
+ * @param parent the new parent notification
+ */
+ public void setIsChildInGroup(boolean isChildInGroup, ExpandableNotificationRow parent) {;
+ boolean childInGroup = BaseStatusBar.ENABLE_CHILD_NOTIFICATIONS && isChildInGroup;
+ mNotificationParent = childInGroup ? parent : null;
+ mPrivateLayout.setIsChildInGroup(childInGroup);
+ updateNoBackgroundState();
+ }
+
+ @Override
+ protected boolean shouldHideBackground() {
+ return super.shouldHideBackground() || mShowNoBackground;
+ }
+
+ @Override
+ public boolean isSummaryWithChildren() {
+ return mIsSummaryWithChildren;
}
@Override
@@ -228,6 +255,13 @@
return mChildrenContainer == null ? null : mChildrenContainer.getNotificationChildren();
}
+ public int getNumberOfNotificationChildren() {
+ if (mChildrenContainer == null) {
+ return 0;
+ }
+ return mChildrenContainer.getNotificationChildren().size();
+ }
+
/**
* Apply the order given in the list to the children.
*
@@ -239,34 +273,34 @@
}
public void getChildrenStates(StackScrollState resultState) {
- if (mChildrenExpanded) {
+ if (mIsSummaryWithChildren) {
StackViewState parentState = resultState.getViewStateForView(this);
mChildrenContainer.getState(resultState, parentState);
}
}
public void applyChildrenState(StackScrollState state) {
- if (mChildrenExpanded) {
+ if (mIsSummaryWithChildren) {
mChildrenContainer.applyState(state);
}
}
public void prepareExpansionChanged(StackScrollState state) {
- if (mChildrenExpanded) {
+ if (mIsSummaryWithChildren) {
mChildrenContainer.prepareExpansionChanged(state);
}
}
public void startChildAnimation(StackScrollState finalState,
StackStateAnimator stateAnimator, boolean withDelays, long delay, long duration) {
- if (mChildrenExpanded) {
+ if (mIsSummaryWithChildren) {
mChildrenContainer.startAnimationToState(finalState, stateAnimator, withDelays, delay,
duration);
}
}
public ExpandableNotificationRow getViewAtPosition(float y) {
- if (!mChildrenExpanded) {
+ if (!mIsSummaryWithChildren || !mChildrenExpanded) {
return this;
} else {
ExpandableNotificationRow view = mChildrenContainer.getViewAtPosition(y);
@@ -278,15 +312,6 @@
return mGuts;
}
- protected int calculateContentHeightFromActualHeight(int actualHeight) {
- int realActualHeight = actualHeight;
- if (hasBottomDecor()) {
- realActualHeight -= getBottomDecorHeight();
- }
- realActualHeight = Math.max(getMinHeight(), realActualHeight);
- return realActualHeight;
- }
-
/**
* Set this notification to be pinned to the top if {@link #isHeadsUp()} is true. By doing this
* the notification will be rendered on top of the screen.
@@ -295,6 +320,7 @@
*/
public void setPinned(boolean pinned) {
mIsPinned = pinned;
+ setChronometerRunning(mLastChronometerRunning);
}
public boolean isPinned() {
@@ -320,6 +346,59 @@
return mJustClicked;
}
+ public void setChronometerRunning(boolean running) {
+ mLastChronometerRunning = running;
+ setChronometerRunning(running, mPrivateLayout);
+ setChronometerRunning(running, mPublicLayout);
+ if (mChildrenContainer != null) {
+ List<ExpandableNotificationRow> notificationChildren =
+ mChildrenContainer.getNotificationChildren();
+ for (int i = 0; i < notificationChildren.size(); i++) {
+ ExpandableNotificationRow child = notificationChildren.get(i);
+ child.setChronometerRunning(running);
+ }
+ }
+ }
+
+ private void setChronometerRunning(boolean running, NotificationContentView layout) {
+ if (layout != null) {
+ running = running || isPinned();
+ View contractedChild = layout.getContractedChild();
+ View expandedChild = layout.getExpandedChild();
+ View headsUpChild = layout.getHeadsUpChild();
+ setChronometerRunningForChild(running, contractedChild);
+ setChronometerRunningForChild(running, expandedChild);
+ setChronometerRunningForChild(running, headsUpChild);
+ }
+ }
+
+ private void setChronometerRunningForChild(boolean running, View child) {
+ if (child != null) {
+ View chronometer = child.findViewById(com.android.internal.R.id.chronometer);
+ if (chronometer instanceof Chronometer) {
+ ((Chronometer) chronometer).setStarted(running);
+ }
+ }
+ }
+
+ public void setEntry(NotificationData.Entry entry) {
+ mEntry = entry;
+ setStatusBarNotification(entry.notification);
+ }
+
+ public CharSequence getSubText() {
+ Notification notification = mStatusBarNotification.getNotification();
+ CharSequence subText = notification.extras.getCharSequence(Notification.EXTRA_SUMMARY_TEXT);
+ if (subText == null) {
+ subText = notification.extras.getCharSequence(Notification.EXTRA_SUB_TEXT);
+ }
+ return subText;
+ }
+
+ public void setContentSubTextVisible(boolean visible) {
+ mPrivateLayout.setSubTextVisible(visible);
+ }
+
public interface ExpansionLogger {
public void logNotificationExpansion(String key, boolean userAction, boolean expanded);
}
@@ -345,7 +424,7 @@
mSensitive = false;
mShowingPublicInitialized = false;
mIsSystemExpanded = false;
- mExpansionDisabled = false;
+ mOnKeyguard = false;
mPublicLayout.reset(mIsHeadsUp);
mPrivateLayout.reset(mIsHeadsUp);
resetHeight();
@@ -383,15 +462,13 @@
mGutsStub = null;
}
});
- mExpandButtonStub = (ViewStub) findViewById(R.id.more_button_stub);
- mExpandButtonStub.setOnInflateListener(new ViewStub.OnInflateListener() {
-
+ mNotificationHeaderStub = (ViewStub) findViewById(R.id.notification_header_stub);
+ mNotificationHeaderStub.setOnInflateListener(new ViewStub.OnInflateListener() {
@Override
public void onInflate(ViewStub stub, View inflated) {
- mExpandButtonContainer = inflated;
- mExpandButton = inflated.findViewById(R.id.notification_expand_button);
- mExpandButtonDivider = inflated.findViewById(R.id.notification_expand_divider);
- mExpandButtonContainer.setOnClickListener(mExpandClickListener);
+ mNotificationHeader = (NotificationHeaderView) inflated;
+ mNotificationHeader.setGroupManager(mGroupManager);
+ mNotificationHeader.bind(mEntry);
}
});
mChildrenContainerStub = (ViewStub) findViewById(R.id.child_container_stub);
@@ -400,8 +477,7 @@
@Override
public void onInflate(ViewStub stub, View inflated) {
mChildrenContainer = (NotificationChildrenContainer) inflated;
- mChildrenContainer.setCollapseClickListener(mExpandClickListener);
- updateChildrenVisibility(false);
+ mChildrenContainer.setNotificationParent(ExpandableNotificationRow.this);
}
});
mVetoButton = findViewById(R.id.veto);
@@ -417,48 +493,8 @@
if (mChildrenContainer == null) {
return;
}
- if (mChildExpandAnimator != null) {
- mChildExpandAnimator.cancel();
- }
- float targetProgress = mChildrenExpanded ? 1.0f : 0.0f;
- if (animated) {
- if (mChildrenExpanded) {
- mChildrenContainer.setVisibility(VISIBLE);
- }
- mExpandButtonStart = mExpandButtonContainer.getTranslationY();
- mChildExpandAnimator = ValueAnimator.ofFloat(mChildrenExpandProgress, targetProgress);
- mChildExpandAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- setChildrenExpandProgress((float) animation.getAnimatedValue());
- }
- });
- mChildExpandAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mChildExpandAnimator = null;
- if (!mChildrenExpanded) {
- mChildrenContainer.setVisibility(INVISIBLE);
- }
- }
- });
- mChildExpandAnimator.setInterpolator(mLinearInterpolator);
- mChildExpandAnimator.setDuration(
- StackStateAnimator.ANIMATION_DURATION_EXPAND_CLICKED);
- mChildExpandAnimator.start();
- } else {
- setChildrenExpandProgress(targetProgress);
- mChildrenContainer.setVisibility(mChildrenExpanded ? VISIBLE : INVISIBLE);
- }
- }
-
- private void setChildrenExpandProgress(float progress) {
- mChildrenExpandProgress = progress;
- updateExpandButtonAppearance();
- NotificationContentView showingLayout = getShowingLayout();
- float alpha = 1.0f - mChildrenExpandProgress;
- alpha = PhoneStatusBar.ALPHA_OUT.getInterpolation(alpha);
- showingLayout.setAlpha(alpha);
+ mChildrenContainer.setVisibility(mIsSummaryWithChildren ? VISIBLE : INVISIBLE);
+ mPrivateLayout.setVisibility(!mIsSummaryWithChildren ? VISIBLE : INVISIBLE);
}
@Override
@@ -490,6 +526,9 @@
}
public boolean isExpandable() {
+ if (mIsSummaryWithChildren && !mShowingPublic) {
+ return !mChildrenExpanded;
+ }
return mExpandable;
}
@@ -514,7 +553,21 @@
* @param userExpanded whether the user wants this notification to be expanded
*/
public void setUserExpanded(boolean userExpanded) {
+ setUserExpanded(userExpanded, false /* allowChildExpansion */);
+ }
+
+ /**
+ * Set this notification to be expanded by the user
+ *
+ * @param userExpanded whether the user wants this notification to be expanded
+ * @param allowChildExpansion whether a call to this method allows expanding children
+ */
+ public void setUserExpanded(boolean userExpanded, boolean allowChildExpansion) {
mFalsingManager.setNotificationExpanded();
+ if (mIsSummaryWithChildren && !mShowingPublic && allowChildExpansion) {
+ mGroupManager.setGroupExpanded(mStatusBarNotification, userExpanded);
+ return;
+ }
if (userExpanded && !mExpandable) return;
final boolean wasExpanded = isExpanded();
mHasUserChangedExpansion = true;
@@ -553,16 +606,19 @@
mIsSystemExpanded = expand;
notifyHeightChanged(false /* needsAnimation */);
logExpansionEvent(false, wasExpanded);
+ if (mChildrenContainer != null) {
+ mChildrenContainer.updateGroupOverflow();
+ }
}
}
/**
- * @param expansionDisabled whether to prevent notification expansion
+ * @param onKeyguard whether to prevent notification expansion
*/
- public void setExpansionDisabled(boolean expansionDisabled) {
- if (expansionDisabled != mExpansionDisabled) {
+ public void setOnKeyguard(boolean onKeyguard) {
+ if (onKeyguard != mOnKeyguard) {
final boolean wasExpanded = isExpanded();
- mExpansionDisabled = expansionDisabled;
+ mOnKeyguard = onKeyguard;
logExpansionEvent(false, wasExpanded);
if (wasExpanded != isExpanded()) {
notifyHeightChanged(false /* needsAnimation */);
@@ -583,9 +639,9 @@
public void applyExpansionToLayout() {
boolean expand = isExpanded();
if (expand && mExpandable) {
- setContentHeight(mMaxExpandHeight);
+ setActualHeight(mMaxExpandHeight);
} else {
- setContentHeight(mRowMinHeight);
+ setActualHeight(mRowMinHeight);
}
}
@@ -595,34 +651,43 @@
return getActualHeight();
}
boolean inExpansionState = isExpanded();
- int maxContentHeight;
if (mSensitive && mHideSensitiveForIntrinsicHeight) {
return mRowMinHeight;
+ } else if (mIsSummaryWithChildren && !mOnKeyguard) {
+ return mChildrenContainer.getIntrinsicHeight()
+ + mNotificationHeader.getHeight();
} else if (mIsHeadsUp) {
if (inExpansionState) {
- maxContentHeight = Math.max(mMaxExpandHeight, mHeadsUpHeight);
+ return Math.max(mMaxExpandHeight, mHeadsUpHeight);
} else {
- maxContentHeight = Math.max(mRowMinHeight, mHeadsUpHeight);
+ return Math.max(mRowMinHeight, mHeadsUpHeight);
}
- } else if ((!inExpansionState && !mChildrenExpanded)) {
- maxContentHeight = mRowMinHeight;
- } else if (mChildrenExpanded) {
- maxContentHeight = mChildrenContainer.getIntrinsicHeight();
+ } else if (!inExpansionState || (isChildInGroup() && !isGroupExpanded())) {
+ return getMinHeight();
} else {
- maxContentHeight = getMaxExpandHeight();
+ return getMaxExpandHeight();
}
- return maxContentHeight + getBottomDecorHeight();
}
- @Override
- protected boolean hasBottomDecor() {
- return BaseStatusBar.ENABLE_CHILD_NOTIFICATIONS
- && !mIsHeadsUp && mGroupManager.hasGroupChildren(mStatusBarNotification);
+ private boolean isGroupExpanded() {
+ return mGroupManager.isGroupExpanded(mStatusBarNotification);
}
- @Override
- protected boolean canHaveBottomDecor() {
- return BaseStatusBar.ENABLE_CHILD_NOTIFICATIONS && !mIsHeadsUp;
+ /**
+ * @return whether this view has a header on the top of the content
+ */
+ private boolean hasNotificationHeader() {
+ return mIsSummaryWithChildren;
+ }
+
+ private void onChildrenCountChanged() {
+ mIsSummaryWithChildren = BaseStatusBar.ENABLE_CHILD_NOTIFICATIONS
+ && mGroupManager.hasGroupChildren(mStatusBarNotification);
+ if (mIsSummaryWithChildren && mChildrenContainer == null) {
+ mChildrenContainerStub.inflate();
+ }
+ updateNotificationHeader();
+ updateChildrenVisibility(true);
}
/**
@@ -633,8 +698,8 @@
*
* @return whether the view state is currently expanded.
*/
- private boolean isExpanded() {
- return !mExpansionDisabled
+ public boolean isExpanded() {
+ return !mOnKeyguard
&& (!hasUserChangedExpansion() && (isSystemExpanded() || isSystemChildExpanded())
|| isUserExpanded());
}
@@ -658,15 +723,6 @@
mWasReset = false;
}
- @Override
- protected boolean isChildInvisible(View child) {
-
- // We don't want to layout the ChildrenContainer if this is a heads-up view, otherwise the
- // view will get too high and the shadows will be off.
- boolean isInvisibleChildContainer = child == mChildrenContainer && mIsHeadsUp;
- return super.isChildInvisible(child) || isInvisibleChildContainer;
- }
-
private void updateMaxHeights() {
int intrinsicBefore = getIntrinsicHeight();
View expandedChild = mPrivateLayout.getExpandedChild();
@@ -709,7 +765,8 @@
mPublicLayout.setAlpha(1f);
mPrivateLayout.setAlpha(1f);
mPublicLayout.setVisibility(mShowingPublic ? View.VISIBLE : View.INVISIBLE);
- mPrivateLayout.setVisibility(mShowingPublic ? View.INVISIBLE : View.VISIBLE);
+ mPrivateLayout.setVisibility(!mShowingPublic && !mIsSummaryWithChildren ? View.VISIBLE
+ : View.INVISIBLE);
} else {
animateShowingPublic(delay, duration);
}
@@ -749,105 +806,28 @@
public void setChildrenExpanded(boolean expanded, boolean animate) {
mChildrenExpanded = expanded;
- updateChildrenVisibility(animate);
+ if (mChildrenContainer != null) {
+ mChildrenContainer.setChildrenExpanded(expanded);
+ }
}
- public void updateExpandButton() {
- boolean hasExpand = hasBottomDecor();
- if (hasExpand != mHasExpandAction) {
- if (hasExpand) {
- if (mExpandButtonContainer == null) {
- mExpandButtonStub.inflate();
+ public void updateNotificationHeader() {
+ boolean hasHeader = hasNotificationHeader();
+ if (hasHeader != mHasNotificationHeader) {
+ if (hasHeader) {
+ if (mNotificationHeader == null) {
+ mNotificationHeaderStub.inflate();
}
- mExpandButtonContainer.setVisibility(View.VISIBLE);
- updateExpandButtonAppearance();
- updateExpandButtonColor();
- } else if (mExpandButtonContainer != null) {
- mExpandButtonContainer.setVisibility(View.GONE);
+ mNotificationHeader.setVisibility(View.VISIBLE);
+ } else if (mNotificationHeader != null) {
+ mNotificationHeader.setVisibility(View.GONE);
}
notifyHeightChanged(true /* needsAnimation */);
}
- mHasExpandAction = hasExpand;
- }
-
- private void updateExpandButtonAppearance() {
- if (mExpandButtonContainer == null) {
- return;
+ if (hasHeader) {
+ mNotificationHeader.bind(mEntry);
}
- float expandButtonAlpha = 0.0f;
- float expandButtonTranslation = 0.0f;
- float containerTranslation = 0.0f;
- int minHeight = getMinHeight();
- if (!mChildrenExpanded || mChildExpandAnimator != null) {
- int expandActionHeight = getBottomDecorHeight();
- int translationY = getActualHeight() - expandActionHeight;
- if (translationY > minHeight) {
- containerTranslation = translationY;
- expandButtonAlpha = 1.0f;
- expandButtonTranslation = 0.0f;
- } else {
- containerTranslation = minHeight;
- float progress = expandActionHeight != 0
- ? (minHeight - translationY) / (float) expandActionHeight
- : 1.0f;
- expandButtonTranslation = -progress * expandActionHeight * 0.7f;
- float alphaProgress = Math.min(progress / 0.7f, 1.0f);
- alphaProgress = PhoneStatusBar.ALPHA_OUT.getInterpolation(alphaProgress);
- expandButtonAlpha = 1.0f - alphaProgress;
- }
- }
- if (mChildExpandAnimator != null || mChildrenExpanded) {
- expandButtonAlpha = (1.0f - mChildrenExpandProgress)
- * expandButtonAlpha;
- expandButtonTranslation = (1.0f - mChildrenExpandProgress)
- * expandButtonTranslation;
- float newTranslation = -getBottomDecorHeight();
-
- // We don't want to take the actual height of the view as this is already
- // interpolated by a custom interpolator leading to a confusing animation. We want
- // to have a stable end value to interpolate in between
- float collapsedHeight = !mChildrenExpanded
- ? Math.max(StackStateAnimator.getFinalActualHeight(this)
- - getBottomDecorHeight(), minHeight)
- : mExpandButtonStart;
- float translationProgress = mFastOutSlowInInterpolator.getInterpolation(
- mChildrenExpandProgress);
- containerTranslation = (1.0f - translationProgress) * collapsedHeight
- + translationProgress * newTranslation;
- }
- mExpandButton.setAlpha(expandButtonAlpha);
- mExpandButtonDivider.setAlpha(expandButtonAlpha);
- mExpandButton.setTranslationY(expandButtonTranslation);
- mExpandButtonContainer.setTranslationY(containerTranslation);
- NotificationContentView showingLayout = getShowingLayout();
- float layoutTranslation =
- mExpandButtonContainer.getTranslationY() - showingLayout.getContentHeight();
- layoutTranslation = Math.min(layoutTranslation, 0);
- if (!mChildrenExpanded && mChildExpandAnimator == null) {
- // Needed for the DragDownHelper in order not to jump there, as the position
- // can be negative for a short time.
- layoutTranslation = 0;
- }
- showingLayout.setTranslationY(layoutTranslation);
- if (mChildrenContainer != null) {
- mChildrenContainer.setTranslationY(
- mExpandButtonContainer.getTranslationY() + getBottomDecorHeight());
- }
- }
-
- private void updateExpandButtonColor() {
- // TODO: This needs some more baking, currently only the divider is colored according to
- // the tint, but legacy black doesn't work yet perfectly for the button etc.
- int color = getRippleColor();
- if (color == mNormalRippleColor) {
- color = 0;
- }
- if (mExpandButtonDivider != null) {
- applyTint(mExpandButtonDivider, color);
- }
- if (mChildrenContainer != null) {
- mChildrenContainer.setTintColor(color);
- }
+ mHasNotificationHeader = hasHeader;
}
public static void applyTint(View v, int color) {
@@ -884,29 +864,41 @@
@Override
public void setActualHeight(int height, boolean notifyListeners) {
super.setActualHeight(height, notifyListeners);
- int contentHeight = calculateContentHeightFromActualHeight(height);
+ int contentHeight = Math.max(getMinHeight(), height);
mPrivateLayout.setContentHeight(contentHeight);
mPublicLayout.setContentHeight(contentHeight);
if (mGuts != null) {
mGuts.setActualHeight(height);
}
invalidate();
- updateExpandButtonAppearance();
}
@Override
public int getMaxContentHeight() {
+ if (mIsSummaryWithChildren && !mShowingPublic) {
+ return mChildrenContainer.getMaxContentHeight()
+ + mNotificationHeader.getHeight();
+ }
NotificationContentView showingLayout = getShowingLayout();
return showingLayout.getMaxHeight();
}
@Override
public int getMinHeight() {
+ if (mIsSummaryWithChildren && !mOnKeyguard) {
+ return mChildrenContainer.getMinHeight()
+ + mNotificationHeader.getHeight();
+ }
NotificationContentView showingLayout = getShowingLayout();
return showingLayout.getMinHeight();
}
@Override
+ protected boolean shouldLimitViewHeight() {
+ return !mIsSummaryWithChildren;
+ }
+
+ @Override
public void setClipTopAmount(int clipTopAmount) {
super.setClipTopAmount(clipTopAmount);
mPrivateLayout.setClipTopAmount(clipTopAmount);
@@ -936,6 +928,25 @@
mPublicLayout.setShowingLegacyBackground(showing);
}
+ @Override
+ protected void updateBackgroundTint() {
+ super.updateBackgroundTint();
+ updateNoBackgroundState();
+ if (mIsSummaryWithChildren) {
+ List<ExpandableNotificationRow> notificationChildren =
+ mChildrenContainer.getNotificationChildren();
+ for (int i = 0; i < notificationChildren.size(); i++) {
+ ExpandableNotificationRow child = notificationChildren.get(i);
+ child.updateNoBackgroundState();
+ }
+ }
+ }
+
+ private void updateNoBackgroundState() {
+ mShowNoBackground = isChildInGroup() && hasSameBgColor(mNotificationParent);
+ updateBackground();
+ }
+
public void setExpansionLogger(ExpansionLogger logger, String key) {
mLogger = logger;
mLoggingKey = key;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index 71baf57..af59ac7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -33,7 +33,6 @@
*/
public abstract class ExpandableView extends FrameLayout {
- private final int mBottomDecorHeight;
protected OnHeightChangedListener mOnHeightChangedListener;
protected int mMaxViewHeight;
private int mActualHeight;
@@ -50,17 +49,12 @@
super(context, attrs);
mMaxViewHeight = getResources().getDimensionPixelSize(
R.dimen.notification_max_height);
- mBottomDecorHeight = resolveBottomDecorHeight();
- }
-
- protected int resolveBottomDecorHeight() {
- return getResources().getDimensionPixelSize(
- R.dimen.notification_bottom_decor_height);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int ownMaxHeight = mMaxViewHeight;
+ boolean limitViewHeight = shouldLimitViewHeight();
+ int ownMaxHeight = limitViewHeight ? mMaxViewHeight : Integer.MAX_VALUE;
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
boolean hasFixedHeight = heightMode == MeasureSpec.EXACTLY;
if (hasFixedHeight) {
@@ -72,7 +66,7 @@
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
- if (child.getVisibility() == GONE || isChildInvisible(child)) {
+ if (child.getVisibility() == GONE) {
continue;
}
int childHeightSpec = newHeightSpec;
@@ -80,7 +74,7 @@
if (layoutParams.height != ViewGroup.LayoutParams.MATCH_PARENT) {
if (layoutParams.height >= 0) {
// An actual height is set
- childHeightSpec = layoutParams.height > ownMaxHeight
+ childHeightSpec = layoutParams.height > ownMaxHeight && limitViewHeight
? MeasureSpec.makeMeasureSpec(ownMaxHeight, MeasureSpec.EXACTLY)
: MeasureSpec.makeMeasureSpec(layoutParams.height, MeasureSpec.EXACTLY);
}
@@ -102,20 +96,20 @@
}
mMatchParentViews.clear();
int width = MeasureSpec.getSize(widthMeasureSpec);
- if (canHaveBottomDecor()) {
- // We always account for the expandAction as well.
- ownHeight += mBottomDecorHeight;
- }
setMeasuredDimension(width, ownHeight);
}
+ protected boolean shouldLimitViewHeight() {
+ return true;
+ }
+
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (!mActualHeightInitialized && mActualHeight == 0) {
int initialHeight = getInitialHeight();
if (initialHeight != 0) {
- setContentHeight(initialHeight);
+ setActualHeight(initialHeight);
}
}
updateClipping();
@@ -173,8 +167,8 @@
}
}
- public void setContentHeight(int contentHeight) {
- setActualHeight(contentHeight + getBottomDecorHeight(), true);
+ public void setActualHeight(int actualHeight) {
+ setActualHeight(actualHeight, true /* notifyListeners */);
}
/**
@@ -187,31 +181,6 @@
}
/**
- * This view may have a bottom decor which will be placed below the content. If it has one, this
- * view will be layouted higher than just the content by {@link #mBottomDecorHeight}.
- * @return the height of the decor if it currently has one
- */
- public int getBottomDecorHeight() {
- return hasBottomDecor() ? mBottomDecorHeight : 0;
- }
-
- /**
- * @return whether this view may have a bottom decor at all. This will force the view to layout
- * itself higher than just it's content
- */
- protected boolean canHaveBottomDecor() {
- return false;
- }
-
- /**
- * @return whether this view has a decor view below it's content. This will make the intrinsic
- * height from {@link #getIntrinsicHeight()} higher as well
- */
- protected boolean hasBottomDecor() {
- return false;
- }
-
- /**
* @return The maximum height of this notification.
*/
public int getMaxContentHeight() {
@@ -358,14 +327,7 @@
outRect.top += getClipTopOptimization();
}
- public int getContentHeight() {
- return mActualHeight - getBottomDecorHeight();
- }
-
- /**
- * @return whether the given child can be ignored for layouting and measuring purposes
- */
- protected boolean isChildInvisible(View child) {
+ public boolean isSummaryWithChildren() {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 5e6fdd0..6d13947 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -200,12 +200,12 @@
switch (mChargingSpeed) {
case KeyguardUpdateMonitor.BatteryStatus.CHARGING_FAST:
chargingId = hasChargingTime
- ? R.string.keyguard_indication_charging_time_fast_if_translated
+ ? R.string.keyguard_indication_charging_time_fast
: R.string.keyguard_plugged_in_charging_fast;
break;
case KeyguardUpdateMonitor.BatteryStatus.CHARGING_SLOWLY:
chargingId = hasChargingTime
- ? R.string.keyguard_indication_charging_time_slowly_if_translated
+ ? R.string.keyguard_indication_charging_time_slowly
: R.string.keyguard_plugged_in_charging_slowly;
break;
default:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 86d7f4f..5aedaf1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -22,6 +22,7 @@
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
+import android.service.notification.StatusBarNotification;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
@@ -32,6 +33,9 @@
import android.widget.FrameLayout;
import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.HybridNotificationView;
+import com.android.systemui.statusbar.notification.HybridNotificationViewManager;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
/**
* A frame layout containing the actual payload of the notification, including the contracted,
@@ -44,8 +48,10 @@
private static final int VISIBLE_TYPE_CONTRACTED = 0;
private static final int VISIBLE_TYPE_EXPANDED = 1;
private static final int VISIBLE_TYPE_HEADSUP = 2;
+ private static final int VISIBLE_TYPE_SINGLELINE = 3;
private final Rect mClipBounds = new Rect();
+ private final int mSingleLineHeight;
private final int mSmallHeight;
private final int mHeadsUpHeight;
private final int mRoundRectRadius;
@@ -55,10 +61,12 @@
private View mContractedChild;
private View mExpandedChild;
private View mHeadsUpChild;
+ private HybridNotificationView mSingleLineView;
private NotificationViewWrapper mContractedWrapper;
private NotificationViewWrapper mExpandedWrapper;
private NotificationViewWrapper mHeadsUpWrapper;
+ private HybridNotificationViewManager mHybridViewManager;
private int mClipTopAmount;
private int mContentHeight;
private int mUnrestrictedContentHeight;
@@ -68,6 +76,8 @@
private boolean mAnimate;
private boolean mIsHeadsUp;
private boolean mShowingLegacyBackground;
+ private boolean mIsChildInGroup;
+ private StatusBarNotification mStatusBarNotification;
private final ViewTreeObserver.OnPreDrawListener mEnableAnimationPredrawListener
= new ViewTreeObserver.OnPreDrawListener() {
@@ -86,10 +96,14 @@
mRoundRectRadius);
}
};
+ private NotificationGroupManager mGroupManager;
public NotificationContentView(Context context, AttributeSet attrs) {
super(context, attrs);
+ mHybridViewManager = new HybridNotificationViewManager(getContext(), this);
mFadePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
+ mSingleLineHeight = getResources().getDimensionPixelSize(
+ R.dimen.notification_single_line_height);
mSmallHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height);
mHeadsUpHeight = getResources().getDimensionPixelSize(R.dimen.notification_mid_height);
mRoundRectRadius = getResources().getDimensionPixelSize(
@@ -140,6 +154,12 @@
MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST));
maxChildHeight = Math.max(maxChildHeight, mHeadsUpChild.getMeasuredHeight());
}
+ if (mSingleLineView != null) {
+ int size = Math.min(maxSize, mSingleLineHeight);
+ mSingleLineView.measure(widthMeasureSpec,
+ MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY));
+ maxChildHeight = Math.max(maxChildHeight, mSingleLineView.getMeasuredHeight());
+ }
int ownHeight = Math.min(maxChildHeight, maxSize);
int width = MeasureSpec.getSize(widthMeasureSpec);
setMeasuredDimension(width, ownHeight);
@@ -271,7 +291,15 @@
}
public int getMinHeight() {
- return mSmallHeight;
+ if (mIsChildInGroup && !isGroupExpanded()) {
+ return mSingleLineHeight;
+ } else {
+ return mSmallHeight;
+ }
+ }
+
+ private boolean isGroupExpanded() {
+ return mGroupManager.isGroupExpanded(mStatusBarNotification);
}
public void setClipTopAmount(int clipTopAmount) {
@@ -313,6 +341,7 @@
if (visibleType != mVisibleType || force) {
if (animate && ((visibleType == VISIBLE_TYPE_EXPANDED && mExpandedChild != null)
|| (visibleType == VISIBLE_TYPE_HEADSUP && mHeadsUpChild != null)
+ || (visibleType == VISIBLE_TYPE_SINGLELINE && mSingleLineView != null)
|| visibleType == VISIBLE_TYPE_CONTRACTED)) {
runSwitchAnimation(visibleType);
} else {
@@ -339,6 +368,12 @@
mHeadsUpChild.setAlpha(headsUpVisible ? 1f : 0f);
mHeadsUpChild.setLayerType(LAYER_TYPE_NONE, null);
}
+ if (mSingleLineView != null) {
+ boolean singleLineVisible = visibleType == VISIBLE_TYPE_SINGLELINE;
+ mSingleLineView.setVisibility(singleLineVisible ? View.VISIBLE : View.INVISIBLE);
+ mSingleLineView.setAlpha(singleLineVisible ? 1f : 0f);
+ mSingleLineView.setLayerType(LAYER_TYPE_NONE, null);
+ }
setLayerType(LAYER_TYPE_NONE, null);
updateRoundRectClipping();
}
@@ -379,6 +414,8 @@
return mExpandedChild;
case VISIBLE_TYPE_HEADSUP:
return mHeadsUpChild;
+ case VISIBLE_TYPE_SINGLELINE:
+ return mSingleLineView;
default:
return mContractedChild;
}
@@ -396,7 +433,9 @@
return VISIBLE_TYPE_EXPANDED;
}
} else {
- if (mContentHeight <= mSmallHeight || noExpandedChild) {
+ if (mIsChildInGroup && !isGroupExpanded()) {
+ return VISIBLE_TYPE_SINGLELINE;
+ } else if (mContentHeight <= mSmallHeight || noExpandedChild) {
return VISIBLE_TYPE_CONTRACTED;
} else {
return VISIBLE_TYPE_EXPANDED;
@@ -405,6 +444,7 @@
}
public void notifyContentUpdated() {
+ updateSingleLineView();
selectLayout(false /* animate */, true /* force */);
if (mContractedChild != null) {
mContractedWrapper.notifyContentUpdated();
@@ -442,4 +482,37 @@
public void setShowingLegacyBackground(boolean showing) {
mShowingLegacyBackground = showing;
}
+
+ public void setIsChildInGroup(boolean isChildInGroup) {
+ mIsChildInGroup = isChildInGroup;
+ updateSingleLineView();
+ }
+
+ public void setStatusBarNotification(StatusBarNotification statusBarNotification) {
+ mStatusBarNotification = statusBarNotification;
+ updateSingleLineView();
+ }
+
+ private void updateSingleLineView() {
+ if (mIsChildInGroup) {
+ mSingleLineView = mHybridViewManager.bindFromNotification(
+ mSingleLineView, mStatusBarNotification.getNotification());
+ }
+ }
+
+ public void setSubTextVisible(boolean visible) {
+ if (mExpandedChild != null) {
+ mExpandedWrapper.setSubTextVisible(visible);
+ }
+ if (mContractedChild != null) {
+ mContractedWrapper.setSubTextVisible(visible);
+ }
+ if (mHeadsUpChild != null) {
+ mHeadsUpWrapper.setSubTextVisible(visible);
+ }
+ }
+
+ public void setGroupManager(NotificationGroupManager groupManager) {
+ mGroupManager = groupManager;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index aedae52..6a90d8e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar;
import android.app.Notification;
+import android.content.Context;
import android.os.SystemClock;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.Ranking;
@@ -24,6 +25,7 @@
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
import android.view.View;
+import android.widget.RemoteViews;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -53,6 +55,10 @@
public boolean legacy; // whether the notification has a legacy, dark background
public int targetSdk;
private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET;
+ public RemoteViews cachedContentView;
+ public RemoteViews cachedBigContentView;
+ public RemoteViews cachedHeadsUpContentView;
+ public RemoteViews cachedPublicContentView;
public Entry(StatusBarNotification n, StatusBarIconView ic) {
this.key = n.getKey();
@@ -98,6 +104,67 @@
return row.getPublicLayout().getContractedChild();
}
+ public boolean cacheContentViews(Context ctx, Notification updatedNotification) {
+ boolean cached = false;
+ if (updatedNotification != null) {
+ final Notification.Builder updatedNotificationBuilder
+ = Notification.Builder.recoverBuilder(ctx, updatedNotification);
+ final RemoteViews newContentView = updatedNotificationBuilder.makeContentView();
+ if (!compareRemoteViews(cachedContentView, newContentView)) {
+ cachedContentView = newContentView;
+ cached |= true;
+ }
+ final RemoteViews newBigContentView =
+ updatedNotificationBuilder.makeBigContentView();
+ if (!compareRemoteViews(cachedBigContentView, newBigContentView)) {
+ cachedBigContentView = newBigContentView;
+ cached |= true;
+ }
+ final RemoteViews newHeadsUpContentView =
+ updatedNotificationBuilder.makeHeadsUpContentView();
+ if (!compareRemoteViews(cachedHeadsUpContentView, newBigContentView)) {
+ cachedHeadsUpContentView = newHeadsUpContentView;
+ cached |= true;
+ }
+ final Notification updatedPublicNotification = updatedNotification.publicVersion;
+ final RemoteViews newPubContentView = (updatedPublicNotification != null)
+ ? Notification.Builder.recoverBuilder(
+ ctx, updatedPublicNotification).makeContentView()
+ : null;
+ if (!compareRemoteViews(cachedPublicContentView, newPubContentView)) {
+ cachedPublicContentView = newPubContentView;
+ cached |= true;
+ }
+ } else {
+ final Notification.Builder builder
+ = Notification.Builder.recoverBuilder(ctx, notification.getNotification());
+
+ cachedContentView = builder.makeContentView();
+ cachedBigContentView = builder.makeBigContentView();
+ cachedHeadsUpContentView = builder.makeHeadsUpContentView();
+
+ final Notification publicNotification =
+ notification.getNotification().publicVersion;
+ if (publicNotification != null) {
+ final Notification.Builder publicBuilder
+ = Notification.Builder.recoverBuilder(ctx, publicNotification);
+ cachedPublicContentView = publicBuilder.makeContentView();
+ }
+ cached = true;
+ }
+ return cached;
+ }
+
+ // Returns true if the RemoteViews are the same.
+ private boolean compareRemoteViews(final RemoteViews a, final RemoteViews b) {
+ return (a == null && b == null) ||
+ (a != null && b != null
+ && b.getPackage() != null
+ && a.getPackage() != null
+ && a.getPackage().equals(b.getPackage())
+ && a.getLayoutId() == b.getLayoutId());
+ }
+
public void notifyFullScreenIntentLaunched() {
lastFullScreenIntentLaunchTime = SystemClock.elapsedRealtime();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
index 9653b67..8e8ce1a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
@@ -70,13 +70,4 @@
public NotificationOverflowIconsView getIconsView() {
return mIconsView;
}
-
- protected int getContentHeightFromActualHeight(int actualHeight) {
- int realActualHeight = actualHeight;
- if (hasBottomDecor()) {
- realActualHeight -= getBottomDecorHeight();
- }
- realActualHeight = Math.max(getMinHeight(), realActualHeight);
- return realActualHeight;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java
index 958b8b5..af6ccd8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java
@@ -27,10 +27,12 @@
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
import android.view.View;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.ImageView;
+import android.widget.TextView;
import com.android.systemui.R;
import com.android.systemui.ViewInvertHelper;
@@ -55,6 +57,11 @@
/** Whether the icon needs to be forced grayscale when in dark mode. */
private boolean mIconForceGraysaleWhenDark;
+ private TextView mSubText;
+ private TextView mInfoText;
+ private View mProfileBadge;
+ private View mThirdLineDivider;
+ private View mThirdLine;
protected NotificationTemplateViewWrapper(Context ctx, View view) {
super(view);
@@ -76,6 +83,11 @@
mIcon = resolveIcon(largeIcon, rightIcon);
mPicture = resolvePicture(largeIcon);
mIconBackgroundColor = resolveBackgroundColor(mIcon);
+ mSubText = (TextView) mView.findViewById(com.android.internal.R.id.text);
+ mInfoText = (TextView) mView.findViewById(com.android.internal.R.id.info);
+ mProfileBadge = mView.findViewById(com.android.internal.R.id.profile_badge_line3);
+ mThirdLineDivider = mView.findViewById(com.android.internal.R.id.overflow_divider);
+ mThirdLine = mView.findViewById(com.android.internal.R.id.line3);
// If the icon already has a color filter, we assume that we already forced the icon to be
// white when we created the notification.
@@ -230,6 +242,43 @@
}
}
+ @Override
+ public void setSubTextVisible(boolean visible) {
+ if (mSubText == null) {
+ return;
+ }
+ boolean subTextAvailable = !TextUtils.isEmpty(mSubText.getText());
+ if (visible && subTextAvailable) {
+ mSubText.setVisibility(View.VISIBLE);
+ } else {
+ mSubText.setVisibility(View.GONE);
+ }
+ // TODO: figure out what to do with the number (same place as contentInfo)
+ // work profile badge. For now we hide it since it looks nicer.
+ boolean infoAvailable = !TextUtils.isEmpty(mInfoText.getText());
+ if (visible && infoAvailable) {
+ mInfoText.setVisibility(View.VISIBLE);
+ } else {
+ mInfoText.setVisibility(View.GONE);
+ }
+ boolean showThirdLine = (visible && (infoAvailable || subTextAvailable))
+ || mProfileBadge.getVisibility() == View.VISIBLE;
+ if (mThirdLineDivider != null) {
+ if (showThirdLine) {
+ mThirdLineDivider.setVisibility(View.VISIBLE);
+ } else {
+ mThirdLineDivider.setVisibility(View.GONE);
+ }
+ }
+ if (mThirdLine != null) {
+ if (showThirdLine) {
+ mThirdLine.setVisibility(View.VISIBLE);
+ } else {
+ mThirdLine.setVisibility(View.GONE);
+ }
+ }
+ }
+
private void updateGrayscaleMatrix(float intensity) {
mGrayscaleColorMatrix.setSaturation(1 - intensity);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java
index b362a29..9bce548 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java
@@ -30,6 +30,7 @@
private static final String TAG_BIG_PICTURE = "bigPicture";
protected final View mView;
+ private boolean mSubTextVisible = true;
public static NotificationViewWrapper wrap(Context ctx, View v) {
if (v.getId() == com.android.internal.R.id.status_bar_latest_event_content) {
@@ -63,7 +64,9 @@
/**
* Notifies this wrapper that the content of the view might have changed.
*/
- public void notifyContentUpdated() {}
+ public void notifyContentUpdated() {
+ setSubTextVisible(mSubTextVisible);
+ }
/**
* @return true if this template might need to be clipped with a round rect to make it look
@@ -72,4 +75,12 @@
public boolean needsRoundRectClipping() {
return false;
}
+
+ /**
+ * Change the subTextVisibility
+ * @param visible Should the subtext be visible
+ */
+ public void setSubTextVisible(boolean visible) {
+ mSubTextVisible = visible;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java
new file mode 100644
index 0000000..8f46e89
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java
@@ -0,0 +1,128 @@
+/*
+ * 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.systemui.statusbar.notification;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.AlphaOptimizedFrameLayout;
+
+/**
+ * A hybrid view which may contain information about one ore more notifications.
+ */
+public class HybridNotificationView extends AlphaOptimizedFrameLayout {
+
+ protected final int mSingleLineHeight;
+ protected final int mStartMargin;
+ protected final int mEndMargin;
+ protected TextView mTitleView;
+ protected TextView mTextView;
+
+ public HybridNotificationView(Context context) {
+ this(context, null);
+ }
+
+ public HybridNotificationView(Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public HybridNotificationView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public HybridNotificationView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ mSingleLineHeight = context.getResources().getDimensionPixelSize(
+ R.dimen.notification_single_line_height);
+ mStartMargin = context.getResources().getDimensionPixelSize(
+ R.dimen.notification_content_margin_start);
+ mEndMargin = context.getResources().getDimensionPixelSize(
+ R.dimen.notification_content_margin_end);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int totalWidth = MeasureSpec.getSize(widthMeasureSpec);
+ int remainingWidth = totalWidth - mStartMargin - mEndMargin;
+ int newHeightSpec = MeasureSpec.makeMeasureSpec(
+ MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.AT_MOST);
+ int newWidthSpec = MeasureSpec.makeMeasureSpec(remainingWidth, MeasureSpec.AT_MOST);
+ mTitleView.measure(newWidthSpec, newHeightSpec);
+ int maxTitleLength = getResources().getDimensionPixelSize(
+ R.dimen.notification_maximum_title_length);
+ int titleWidth = mTitleView.getMeasuredWidth();
+ int heightSpec = MeasureSpec.makeMeasureSpec(mSingleLineHeight, MeasureSpec.AT_MOST);
+ boolean hasText = !TextUtils.isEmpty(mTextView.getText());
+ if (titleWidth > maxTitleLength && hasText) {
+ titleWidth = maxTitleLength;
+ int widthSpec = MeasureSpec.makeMeasureSpec(titleWidth, MeasureSpec.EXACTLY);
+ mTitleView.measure(widthSpec, heightSpec);
+ }
+ if (hasText) {
+ remainingWidth -= titleWidth;
+ int widthSpec = MeasureSpec.makeMeasureSpec(remainingWidth, MeasureSpec.AT_MOST);
+ mTextView.measure(widthSpec, newHeightSpec);
+ }
+ setMeasuredDimension(totalWidth, mSingleLineHeight);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ int childLeft = mStartMargin;
+ int childRight = childLeft + mTitleView.getMeasuredWidth();
+ int childBottom = (mSingleLineHeight + mTitleView.getMeasuredHeight()) / 2;
+ int childTop = childBottom - mTitleView.getMeasuredHeight();
+ int rtlLeft = transformForRtl(childLeft);
+ int rtlRight = transformForRtl(childRight);
+ mTitleView.layout(Math.min(rtlLeft, rtlRight), childTop, Math.max(rtlLeft, rtlRight),
+ childBottom);
+ childLeft = childRight;
+ childRight = childLeft + mTextView.getMeasuredWidth();
+ childTop = mTitleView.getTop() + mTitleView.getBaseline() - mTextView.getBaseline();
+ childBottom = childTop + mTextView.getMeasuredHeight();
+ rtlLeft = transformForRtl(childLeft);
+ rtlRight = transformForRtl(childRight);
+ mTextView.layout(Math.min(rtlLeft, rtlRight), childTop, Math.max(rtlLeft, rtlRight),
+ childBottom);
+ }
+
+ private int transformForRtl(int left) {
+ return isLayoutRtl() ? getWidth() - left : left;
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mTitleView = (TextView) findViewById(R.id.notification_title);
+ mTextView = (TextView) findViewById(R.id.notification_text);
+ }
+
+ public void bind(CharSequence title) {
+ bind(title, null);
+ }
+
+ public void bind(CharSequence title, CharSequence text) {
+ mTitleView.setText(title);
+ mTextView.setText(text);
+ requestLayout();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationViewManager.java
new file mode 100644
index 0000000..987f7b8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationViewManager.java
@@ -0,0 +1,112 @@
+/*
+ * 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.systemui.statusbar.notification;
+
+import android.app.Notification;
+import android.content.Context;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
+
+import java.util.List;
+
+/**
+ * A class managing {@link HybridNotificationView} views
+ */
+public class HybridNotificationViewManager {
+
+ private final Context mContext;
+ private ViewGroup mParent;
+ private String mConcadenationString;
+
+ public HybridNotificationViewManager(Context ctx, ViewGroup parent) {
+ mContext = ctx;
+ mParent = parent;
+ mConcadenationString = mContext.getString(R.string.group_summary_concadenation);
+ }
+
+ private HybridNotificationView inflateHybridView() {
+ LayoutInflater inflater = mContext.getSystemService(LayoutInflater.class);
+ HybridNotificationView hybrid = (HybridNotificationView) inflater.inflate(
+ R.layout.hybrid_notification, mParent, false);
+ mParent.addView(hybrid);
+ return hybrid;
+ }
+
+ public HybridNotificationView bindFromNotification(HybridNotificationView reusableView,
+ Notification notification) {
+ CharSequence titleText = resolveTitle(notification);
+ if (titleText == null) {
+ if (reusableView != null) {
+ mParent.removeView(reusableView);
+ }
+ return null;
+ }
+ if (reusableView == null) {
+ reusableView = inflateHybridView();
+ }
+ CharSequence contentText = resolveText(notification);
+ reusableView.bind(titleText, contentText);
+ return reusableView;
+ }
+
+ private CharSequence resolveText(Notification notification) {
+ CharSequence contentText = notification.extras.getCharSequence(Notification.EXTRA_BIG_TEXT);
+ if (contentText == null) {
+ contentText = notification.extras.getCharSequence(Notification.EXTRA_TEXT);
+ }
+ return contentText;
+ }
+
+ private CharSequence resolveTitle(Notification notification) {
+ CharSequence titleText = notification.extras.getCharSequence(Notification.EXTRA_TITLE);
+ if (titleText == null) {
+ titleText = notification.extras.getCharSequence(Notification.EXTRA_TITLE_BIG);
+ }
+ return titleText;
+ }
+
+ public HybridNotificationView bindFromNotificationGroup(
+ HybridNotificationView reusableView,
+ List<ExpandableNotificationRow> group, int startIndex) {
+ if (reusableView == null) {
+ reusableView = inflateHybridView();
+ }
+ CharSequence summary = null;
+ int childCount = group.size();
+ for (int i = startIndex; i < childCount; i++) {
+ ExpandableNotificationRow child = group.get(i);
+ CharSequence titleText = resolveTitle(
+ child.getStatusBarNotification().getNotification());
+ if (titleText == null) {
+ continue;
+ }
+ if (TextUtils.isEmpty(summary)) {
+ summary = titleText;
+ } else if (reusableView.isLayoutRtl()) {
+ summary = titleText + mConcadenationString + summary;
+ } else {
+ summary = summary + mConcadenationString + titleText;
+ }
+ }
+ reusableView.bind(summary);
+ return reusableView;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderView.java
new file mode 100644
index 0000000..ec26cc4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderView.java
@@ -0,0 +1,209 @@
+/*
+ * 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.systemui.statusbar.notification;
+
+import android.annotation.Nullable;
+import android.app.Notification;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.service.notification.StatusBarNotification;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.internal.util.NotificationColorUtil;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.BaseStatusBar;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+
+import java.util.List;
+
+/**
+ * A header for a notification view
+ */
+public class NotificationHeaderView extends FrameLayout {
+
+ private static final int DEFAULT_ICON_TINT_COLOR = 0xff616161;
+ private final NotificationColorUtil mNotificationColorUtil;
+ private NotificationData.Entry mNotificationEntry;
+ private ImageView mIconView;
+ private TextView mAppName;
+ private TextView mPostTime;
+ private TextView mChildCount;
+ private TextView mSubTextDivider;
+ private TextView mSubText;
+ private NotificationGroupManager mGroupManager;
+ private ImageButton mExpandButton;
+
+ public NotificationHeaderView(Context context) {
+ this(context, null);
+ }
+
+ public NotificationHeaderView(Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public NotificationHeaderView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public NotificationHeaderView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ mNotificationColorUtil = NotificationColorUtil.getInstance(context);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mIconView = (ImageView) findViewById(R.id.header_notification_icon);
+ mAppName = (TextView) findViewById(R.id.app_name_text);
+ mSubTextDivider = (TextView) findViewById(R.id.app_title_sub_text_divider);
+ mSubText = (TextView) findViewById(R.id.title_sub_text);
+ mPostTime = (TextView) findViewById(R.id.post_time);
+ mChildCount = (TextView) findViewById(R.id.number_of_children);
+ mExpandButton = (ImageButton) findViewById(R.id.notification_expand_button);
+ mExpandButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mGroupManager.toggleGroupExpansion(mNotificationEntry.notification);
+ }
+ });
+ }
+
+ public void bind(NotificationData.Entry notificationEntry) {
+ mNotificationEntry = notificationEntry;
+ StatusBarNotification sbn = notificationEntry.notification;
+ int notificationColor = getNotificationColor(sbn);
+ bindIcon(notificationColor);
+ bindNumber(notificationColor);
+ bindAppName(sbn);
+ bindSubText();
+ bindTime(sbn);
+ bindExpandButton(sbn);
+ }
+
+ private void bindExpandButton(StatusBarNotification sbn) {
+ boolean summaryOfGroup = mGroupManager.isSummaryOfGroup(sbn);
+ mExpandButton.setVisibility(summaryOfGroup ? VISIBLE : GONE);
+ }
+
+ private void bindSubText() {
+ List<ExpandableNotificationRow> notificationChildren =
+ mNotificationEntry.row.getNotificationChildren();
+ CharSequence subText = null;
+ if (notificationChildren != null) {
+ for (int i = 0; i < notificationChildren.size(); i++) {
+ ExpandableNotificationRow row = notificationChildren.get(i);
+ CharSequence rowSubText = row.getSubText();
+ if (TextUtils.isEmpty(rowSubText)
+ || (subText != null && !subText.equals(rowSubText))) {
+ // The children don't have a common subText
+ subText = null;
+ break;
+ } else if (subText == null) {
+ subText = rowSubText;
+ }
+ }
+ };
+ setSubText(subText);
+ }
+
+ private void setSubText(CharSequence subText) {
+ boolean goneInHeader = TextUtils.isEmpty(subText);
+ if (goneInHeader) {
+ mSubText.setVisibility(GONE);
+ mSubTextDivider.setVisibility(GONE);
+ } else {
+ mSubText.setVisibility(VISIBLE);
+ mSubText.setText(subText);
+ mSubTextDivider.setVisibility(VISIBLE);
+ }
+ List<ExpandableNotificationRow> notificationChildren =
+ mNotificationEntry.row.getNotificationChildren();
+ if (notificationChildren != null) {
+ for (int i = 0; i < notificationChildren.size(); i++) {
+ ExpandableNotificationRow row = notificationChildren.get(i);
+ row.setContentSubTextVisible(goneInHeader);
+ }
+ }
+ }
+
+ private int getNotificationColor(StatusBarNotification sbn) {
+ int color = sbn.getNotification().color;
+ if (color == Notification.COLOR_DEFAULT) {
+ return DEFAULT_ICON_TINT_COLOR;
+ }
+ return color;
+ }
+
+ private void bindNumber(int notificationColor) {
+ int numberOfNotificationChildren = mNotificationEntry.row.getNumberOfNotificationChildren();
+ boolean visible = numberOfNotificationChildren > 0;
+ if (visible) {
+ mChildCount.setText("(" + numberOfNotificationChildren + ")");
+ mChildCount.setTextColor(notificationColor);
+ mChildCount.setVisibility(VISIBLE);
+ } else {
+ mChildCount.setVisibility(GONE);
+ }
+ }
+
+ private void bindTime(StatusBarNotification sbn) {
+
+ }
+
+ private void bindIcon(int notificationColor) {
+ Drawable icon = mNotificationEntry.icon.getDrawable().getConstantState()
+ .newDrawable(getResources()).mutate();
+ mIconView.setImageDrawable(icon);
+ if (NotificationUtils.isGrayscale(mIconView, mNotificationColorUtil)) {
+ icon.setTint(notificationColor);
+ }
+ }
+
+ private void bindAppName(StatusBarNotification sbn) {
+ PackageManager pmUser = BaseStatusBar.getPackageManagerForUser(getContext(),
+ sbn.getUser().getIdentifier());
+ final String pkg = sbn.getPackageName();
+ String appname = pkg;
+ try {
+ final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
+ PackageManager.GET_UNINSTALLED_PACKAGES
+ | PackageManager.GET_DISABLED_COMPONENTS);
+ if (info != null) {
+ appname = String.valueOf(pmUser.getApplicationLabel(info));
+
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // app is gone, just show package name
+ }
+ mAppName.setText(appname);
+ }
+
+ public void setGroupManager(NotificationGroupManager groupManager) {
+ mGroupManager = groupManager;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
new file mode 100644
index 0000000..c931800
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
@@ -0,0 +1,39 @@
+/*
+ * 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.systemui.statusbar.notification;
+
+import android.view.View;
+import android.widget.ImageView;
+
+import com.android.internal.util.NotificationColorUtil;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.StatusBarIconView;
+
+/**
+ * A util class for various reusable functions
+ */
+public class NotificationUtils {
+ public static boolean isGrayscale(ImageView v, NotificationColorUtil colorUtil) {
+ Object isGrayscale = v.getTag(R.id.icon_is_grayscale);
+ if (isGrayscale != null) {
+ return Boolean.TRUE.equals(isGrayscale);
+ }
+ boolean grayscale = colorUtil.isGrayscaleIcon(v.getDrawable());
+ v.setTag(R.id.icon_is_grayscale, grayscale);
+ return grayscale;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarter.java
index 9ef320bc..8f689c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarter.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
+import android.app.PendingIntent;
import android.content.Intent;
/**
@@ -24,6 +25,7 @@
* Keyguard.
*/
public interface ActivityStarter {
+ void startPendingIntentDismissingKeyguard(PendingIntent intent);
void startActivity(Intent intent, boolean dismissShade);
void startActivity(Intent intent, boolean dismissShade, Callback callback);
void preventNextAnimation();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
index 2912963..7135836 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
@@ -173,7 +173,7 @@
if (DEBUG_FP_WAKELOCK) {
Log.i(TAG, "fp wakelock: Authenticated, waking up...");
}
- mPowerManager.wakeUp(SystemClock.uptimeMillis());
+ mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.policy:FINGERPRINT");
}
releaseFingerprintWakeLock();
switch (mMode) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
index 4ac2c31..58c9722 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.phone;
-import static android.app.ActivityManager.INVALID_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import android.animation.LayoutTransition;
import android.annotation.Nullable;
@@ -1060,7 +1060,7 @@
ris = packageManager.queryIntentActivitiesAsUser(intentToResolve, 0, userId);
}
if (ris == null || ris.size() <= 0) {
- Slog.e(TAG, "Failed to build intent for " + packageName);
+ Slog.i(TAG, "Failed to build intent for " + packageName);
return null;
}
return new ComponentName(ris.get(0).activityInfo.packageName,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarAppsModel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarAppsModel.java
index d527f29..76a9798 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarAppsModel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarAppsModel.java
@@ -179,7 +179,7 @@
}
}
- Slog.e(TAG, "Activity doesn't have category Intent.CATEGORY_LAUNCHER " + component);
+ Slog.i(TAG, "Activity doesn't have category Intent.CATEGORY_LAUNCHER " + component);
return null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index 310625e..bbef1c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -21,7 +21,6 @@
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.NotificationData;
-import com.android.systemui.statusbar.StatusBarState;
import java.util.HashMap;
import java.util.HashSet;
@@ -93,21 +92,8 @@
if (group.children.isEmpty()) {
if (group.summary == null) {
mGroupMap.remove(groupKey);
- } else {
- if (group.expanded) {
- // only the summary is left. Change it to unexpanded in a few ms. We do this to
- // avoid raceconditions
- removed.row.post(new Runnable() {
- @Override
- public void run() {
- if (group.children.isEmpty()) {
- setGroupExpanded(sbn, false);
- }
- }
- });
- } else {
- group.summary.row.updateExpandButton();
- }
+ } else if (!group.expanded) {
+ group.summary.row.updateNotificationHeader();
}
}
}
@@ -130,7 +116,7 @@
} else {
group.children.add(added);
if (group.summary != null && group.children.size() == 1 && !group.expanded) {
- group.summary.row.updateExpandButton();
+ group.summary.row.updateNotificationHeader();
}
}
}
@@ -155,9 +141,6 @@
}
public boolean hasGroupChildren(StatusBarNotification sbn) {
- if (areGroupsProhibited()) {
- return false;
- }
if (!sbn.getNotification().isGroupSummary()) {
return false;
}
@@ -168,29 +151,6 @@
return !group.children.isEmpty();
}
- public void setStatusBarState(int newState) {
- if (mBarState == newState) {
- return;
- }
- boolean prohibitedBefore = areGroupsProhibited();
- mBarState = newState;
- boolean nowProhibited = areGroupsProhibited();
- if (nowProhibited != prohibitedBefore) {
- if (nowProhibited) {
- for (NotificationGroup group : mGroupMap.values()) {
- if (group.expanded) {
- setGroupExpanded(group, false);
- }
- }
- }
- mListener.onGroupsProhibitedChanged();
- }
- }
-
- private boolean areGroupsProhibited() {
- return mBarState == StatusBarState.KEYGUARD;
- }
-
/**
* @return whether a given notification is a child in a group which has a summary
*/
@@ -205,6 +165,20 @@
return true;
}
+ /**
+ * @return whether a given notification is a summary in a group which has children
+ */
+ public boolean isSummaryOfGroup(StatusBarNotification sbn) {
+ if (sbn.getNotification().isGroupChild()) {
+ return false;
+ }
+ NotificationGroup group = mGroupMap.get(sbn.getGroupKey());
+ if (group == null) {
+ return false;
+ }
+ return !group.children.isEmpty();
+ }
+
public ExpandableNotificationRow getGroupSummary(StatusBarNotification sbn) {
NotificationGroup group = mGroupMap.get(sbn.getGroupKey());
return group == null ? null
@@ -212,6 +186,18 @@
: group.summary.row;
}
+ public void onEntryHeadsUped(NotificationData.Entry headsUp) {
+ // TODO: handle this nicely
+ }
+
+ public void toggleGroupExpansion(StatusBarNotification sbn) {
+ NotificationGroup group = mGroupMap.get(sbn.getGroupKey());
+ if (group == null) {
+ return;
+ }
+ setGroupExpanded(group, !group.expanded);
+ }
+
public static class NotificationGroup {
public final HashSet<NotificationData.Entry> children = new HashSet<>();
public NotificationData.Entry summary;
@@ -228,11 +214,6 @@
void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded);
/**
- * Children group policy has changed and children may no be prohibited or allowed.
- */
- void onGroupsProhibitedChanged();
-
- /**
* A group of children just received a summary notification and should therefore become
* children of it.
*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index bd16257..fafedc3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -25,6 +25,7 @@
import android.content.res.Resources;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import android.view.ViewTreeObserver;
@@ -127,7 +128,6 @@
};
protected void onExpandingFinished() {
- endClosing();
mBar.onExpandingFinished();
}
@@ -142,6 +142,7 @@
}
protected final void notifyExpandingFinished() {
+ endClosing();
if (mExpanding) {
mExpanding = false;
onExpandingFinished();
@@ -228,8 +229,7 @@
}
// On expanding, single mouse click expands the panel instead of dragging.
- if (isFullyCollapsed() && event.getPointerCount() == 1
- && event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE) {
+ if (isFullyCollapsed() && event.isFromSource(InputDevice.SOURCE_MOUSE)) {
if (event.getAction() == MotionEvent.ACTION_UP) {
expand(true);
}
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 1c1a1d7..3e52515 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -33,6 +33,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -88,7 +89,6 @@
import android.view.ViewStub;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
-import android.view.accessibility.AccessibilityEvent;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Interpolator;
@@ -146,7 +146,6 @@
import com.android.systemui.statusbar.policy.FullscreenUserSwitcher;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.HotspotControllerImpl;
-import com.android.systemui.statusbar.policy.KeyButtonView;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
import com.android.systemui.statusbar.policy.LocationControllerImpl;
@@ -248,15 +247,25 @@
* Prudently disable QS and notifications. */
private static final boolean ONLY_CORE_APPS;
+ /* If true, the device supports freeform window management.
+ * This affects the status bar UI. */
+ private static final boolean FREEFORM_WINDOW_MANAGEMENT;
+
static {
boolean onlyCoreApps;
+ boolean freeformWindowManagement;
try {
- onlyCoreApps = IPackageManager.Stub.asInterface(ServiceManager.getService("package"))
- .isOnlyCoreApps();
+ IPackageManager packageManager =
+ IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
+ onlyCoreApps = packageManager.isOnlyCoreApps();
+ freeformWindowManagement = packageManager.hasSystemFeature(
+ PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT);
} catch (RemoteException e) {
onlyCoreApps = false;
+ freeformWindowManagement = false;
}
ONLY_CORE_APPS = onlyCoreApps;
+ FREEFORM_WINDOW_MANAGEMENT = freeformWindowManagement;
}
PhoneStatusBarPolicy mIconPolicy;
@@ -705,7 +714,7 @@
R.color.notification_panel_solid_background)));
}
- mHeadsUpManager = new HeadsUpManager(context, mStatusBarWindow);
+ mHeadsUpManager = new HeadsUpManager(context, mStatusBarWindow, mGroupManager);
mHeadsUpManager.setBar(this);
mHeadsUpManager.addListener(this);
mHeadsUpManager.addListener(mNotificationPanel);
@@ -984,8 +993,8 @@
if (shelfOverride != DEFAULT) {
return shelfOverride != 0;
}
- // Otherwise default to the build setting.
- return mContext.getResources().getBoolean(R.bool.config_enableAppShelf);
+ // Otherwise default to the platform feature.
+ return FREEFORM_WINDOW_MANAGEMENT;
}
private void clearAllNotifications() {
@@ -1115,13 +1124,22 @@
}
};
- private long mLastLockToAppLongPress;
- private View.OnLongClickListener mLongPressBackRecentsListener =
- new View.OnLongClickListener() {
+ private View.OnLongClickListener mLongPressBackListener = new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
- handleLongPressBackRecents(v);
- return true;
+ return handleLongPressBack();
+ }
+ };
+
+ private View.OnLongClickListener mRecentsLongClickListener = new View.OnLongClickListener() {
+
+ @Override
+ public boolean onLongClick(View v) {
+ if (mRecents != null) {
+ mRecents.dockTopTask();
+ return true;
+ }
+ return false;
}
};
@@ -1170,9 +1188,9 @@
mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPreloadOnTouchListener);
mNavigationBarView.getRecentsButton().setLongClickable(true);
- mNavigationBarView.getRecentsButton().setOnLongClickListener(mLongPressBackRecentsListener);
+ mNavigationBarView.getRecentsButton().setOnLongClickListener(mRecentsLongClickListener);
mNavigationBarView.getBackButton().setLongClickable(true);
- mNavigationBarView.getBackButton().setOnLongClickListener(mLongPressBackRecentsListener);
+ mNavigationBarView.getBackButton().setOnLongClickListener(mLongPressBackListener);
mNavigationBarView.getHomeButton().setOnTouchListener(mHomeActionListener);
mNavigationBarView.getHomeButton().setOnLongClickListener(mLongPressHomeListener);
mAssistManager.onConfigurationChanged();
@@ -3261,6 +3279,15 @@
return !isDeviceProvisioned() || (mDisabled1 & StatusBarManager.DISABLE_SEARCH) != 0;
}
+ public void postStartActivityDismissingKeyguard(final PendingIntent intent) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ startPendingIntentDismissingKeyguard(intent);
+ }
+ });
+ }
+
public void postStartActivityDismissingKeyguard(final Intent intent, int delay) {
mHandler.postDelayed(new Runnable() {
@Override
@@ -3832,7 +3859,6 @@
clearNotificationEffects();
}
mState = state;
- mGroupManager.setStatusBarState(state);
mFalsingManager.setStatusBarState(state);
mStatusBarWindowManager.setStatusBarState(state);
updateDozing();
@@ -3959,9 +3985,6 @@
mNotificationPanel.animateToFullShade(0 /* delay */);
setBarState(StatusBarState.SHADE_LOCKED);
updateKeyguardState(false /* goingToFullShade */, false /* fromShadeLocked */);
- if (row != null) {
- row.setUserLocked(false);
- }
}
}
@@ -4039,7 +4062,7 @@
private void vibrateForCameraGesture() {
// Make sure to pass -1 for repeat so VibratorService doesn't stop us when going to sleep.
- mVibrator.vibrate(new long[] { 0, 750L }, -1 /* repeat */);
+ mVibrator.vibrate(new long[]{0, 750L}, -1 /* repeat */);
}
public void onScreenTurnedOn() {
@@ -4048,58 +4071,22 @@
}
/**
- * This handles long-press of both back and recents. They are
- * handled together to capture them both being long-pressed
- * at the same time to exit screen pinning (lock task).
- *
- * When accessibility mode is on, only a long-press from recents
- * is required to exit.
- *
- * In all other circumstances we try to pass through long-press events
- * for Back, so that apps can still use it. Which can be from two things.
- * 1) Not currently in screen pinning (lock task).
- * 2) Back is long-pressed without recents.
+ * Handles long press for back button. This exits screen pinning.
*/
- private void handleLongPressBackRecents(View v) {
+ private boolean handleLongPressBack() {
try {
- boolean sendBackLongPress = false;
IActivityManager activityManager = ActivityManagerNative.getDefault();
- boolean isAccessiblityEnabled = mAccessibilityManager.isEnabled();
- if (activityManager.isInLockTaskMode() && !isAccessiblityEnabled) {
- long time = System.currentTimeMillis();
- // If we recently long-pressed the other button then they were
- // long-pressed 'together'
- if ((time - mLastLockToAppLongPress) < LOCK_TO_APP_GESTURE_TOLERENCE) {
- activityManager.stopLockTaskModeOnCurrent();
- // When exiting refresh disabled flags.
- mNavigationBarView.setDisabledFlags(mDisabled1, true);
- } else if ((v.getId() == R.id.back)
- && !mNavigationBarView.getRecentsButton().isPressed()) {
- // If we aren't pressing recents right now then they presses
- // won't be together, so send the standard long-press action.
- sendBackLongPress = true;
- }
- mLastLockToAppLongPress = time;
- } else {
- // If this is back still need to handle sending the long-press event.
- if (v.getId() == R.id.back) {
- sendBackLongPress = true;
- } else if (isAccessiblityEnabled && activityManager.isInLockTaskMode()) {
- // When in accessibility mode a long press that is recents (not back)
- // should stop lock task.
- activityManager.stopLockTaskModeOnCurrent();
- // When exiting refresh disabled flags.
- mNavigationBarView.setDisabledFlags(mDisabled1, true);
- }
- }
- if (sendBackLongPress) {
- KeyButtonView keyButtonView = (KeyButtonView) v;
- keyButtonView.sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
- keyButtonView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
+ if (activityManager.isInLockTaskMode()) {
+ activityManager.stopLockTaskModeOnCurrent();
+
+ // When exiting refresh disabled flags.
+ mNavigationBarView.setDisabledFlags(mDisabled1, true);
+ return true;
}
} catch (RemoteException e) {
Log.d(TAG, "Unable to reach activity manager", e);
}
+ return false;
}
// Recents
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 540b9d0..fa9c4bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -109,6 +109,14 @@
}
};
+ private Runnable mRemoveCastIconRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (DEBUG) Log.v(TAG, "updateCast: hiding icon NOW");
+ mService.setIconVisibility(SLOT_CAST, false);
+ }
+ };
+
public PhoneStatusBarPolicy(Context context, CastController cast, HotspotController hotspot,
UserInfoController userInfoController, BluetoothController bluetooth) {
mContext = context;
@@ -328,11 +336,17 @@
}
}
if (DEBUG) Log.v(TAG, "updateCast: isCasting: " + isCasting);
+ mHandler.removeCallbacks(mRemoveCastIconRunnable);
if (isCasting) {
mService.setIcon(SLOT_CAST, R.drawable.stat_sys_cast, 0,
mContext.getString(R.string.accessibility_casting));
+ mService.setIconVisibility(SLOT_CAST, true);
+ } else {
+ // don't turn off the screen-record icon for a few seconds, just to make sure the user
+ // has seen it
+ if (DEBUG) Log.v(TAG, "updateCast: hiding icon in 3 sec...");
+ mHandler.postDelayed(mRemoveCastIconRunnable, 3000);
}
- mService.setIconVisibility(SLOT_CAST, isCasting);
}
private void profileChanged(int userId) {
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 6906a52..96b919e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
+import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
@@ -121,6 +122,11 @@
}
@Override
+ public void startActivityDismissingKeyguard(PendingIntent intent) {
+ mStatusBar.postStartActivityDismissingKeyguard(intent);
+ }
+
+ @Override
public void warn(String message, Throwable t) {
// already logged
}
@@ -240,7 +246,7 @@
}
}
- protected QSTile<?> createTile(String tileSpec) {
+ public QSTile<?> createTile(String tileSpec) {
if (tileSpec.equals("wifi")) return new WifiTile(this, false);
else if (tileSpec.equals("bt")) return new BluetoothTile(this, false);
else if (tileSpec.equals("inversion")) return new ColorInversionTile(this);
@@ -266,6 +272,7 @@
else if (tileSpec.equals("qlock")) return new QLockTile(this);
// Intent tiles.
else if (tileSpec.startsWith(IntentTile.PREFIX)) return IntentTile.create(this,tileSpec);
+ else if (tileSpec.startsWith(CustomTile.PREFIX)) return CustomTile.create(this,tileSpec);
else throw new IllegalArgumentException("Bad tile spec: " + tileSpec);
}
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 01f0667..662dbd9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
@@ -20,7 +20,9 @@
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.graphics.Rect;
import android.graphics.drawable.Animatable;
+import android.graphics.drawable.RippleDrawable;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
@@ -28,6 +30,7 @@
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;
+
import com.android.systemui.R;
import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QSTile;
@@ -86,6 +89,20 @@
mQsDetailHeaderTitle = (TextView) mQsDetailHeader.findViewById(android.R.id.title);
mQsDetailHeaderSwitch = (Switch) mQsDetailHeader.findViewById(android.R.id.toggle);
mQsDetailHeaderProgress = (ImageView) findViewById(R.id.qs_detail_header_progress);
+
+ // 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);
+
+ addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right,
+ int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ setClipBounds(new Rect(getPaddingLeft(), 0, getWidth() - getPaddingRight(),
+ getHeight()));
+ }
+ });
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
index 18db5b8..512af1b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
@@ -19,16 +19,13 @@
import android.animation.Animator.AnimatorListener;
import android.animation.ObjectAnimator;
import android.content.Context;
-import android.graphics.drawable.RippleDrawable;
-import android.os.Handler;
-import android.os.Message;
import android.util.AttributeSet;
+import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
-
import com.android.keyguard.AlphaOptimizedImageButton;
public class SettingsButton extends AlphaOptimizedImageButton {
@@ -157,6 +154,7 @@
protected void startContinuousSpin() {
cancelAnimation();
+ performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
mUpToSpeed = true;
mAnimator = ObjectAnimator.ofFloat(this, View.ROTATION, 0, 360);
mAnimator.setInterpolator(AnimationUtils.loadInterpolator(mContext,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index 65d84e2..6cda561 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -554,8 +554,8 @@
startBatteryActivity();
} else if (v == mAlarmStatus && mNextAlarm != null) {
PendingIntent showIntent = mNextAlarm.getShowIntent();
- if (showIntent != null && showIntent.isActivity()) {
- mActivityStarter.startActivity(showIntent.getIntent(), true /* dismissShade */);
+ if (showIntent != null) {
+ mActivityStarter.startPendingIntentDismissingKeyguard(showIntent);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 5de1c13..d5b980e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -42,6 +42,7 @@
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.SignalClusterView;
import com.android.systemui.statusbar.StatusBarIconView;
+import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
@@ -401,23 +402,13 @@
for (int i = 0; i < mNotificationIcons.getChildCount(); i++) {
StatusBarIconView v = (StatusBarIconView) mNotificationIcons.getChildAt(i);
boolean isPreL = Boolean.TRUE.equals(v.getTag(R.id.icon_is_pre_L));
- boolean colorize = !isPreL || isGrayscale(v);
+ boolean colorize = !isPreL || NotificationUtils.isGrayscale(v, mNotificationColorUtil);
if (colorize) {
v.setImageTintList(ColorStateList.valueOf(mIconTint));
}
}
}
- private boolean isGrayscale(StatusBarIconView v) {
- Object isGrayscale = v.getTag(R.id.icon_is_grayscale);
- if (isGrayscale != null) {
- return Boolean.TRUE.equals(isGrayscale);
- }
- boolean grayscale = mNotificationColorUtil.isGrayscaleIcon(v.getDrawable());
- v.setTag(R.id.icon_is_grayscale, grayscale);
- return grayscale;
- }
-
public void appTransitionPending() {
mTransitionPending = true;
}
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 d701b3c..116237d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -30,7 +30,11 @@
private final Context mContext;
public SystemUIDialog(Context context) {
- super(context, R.style.Theme_SystemUI_Dialog);
+ this(context, R.style.Theme_SystemUI_Dialog);
+ }
+
+ public SystemUIDialog(Context context, int theme) {
+ super(context, theme);
mContext = context;
getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index 4f7756e..dc9f5e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -33,6 +33,7 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
import java.io.FileDescriptor;
@@ -85,6 +86,7 @@
private final int mStatusBarHeight;
private final int mNotificationsTopPadding;
private final Context mContext;
+ private final NotificationGroupManager mGroupManager;
private PhoneStatusBar mBar;
private int mSnoozeLengthMs;
private ContentObserver mSettingsObserver;
@@ -104,7 +106,8 @@
private boolean mIsObserving;
private boolean mRemoteInputActive;
- public HeadsUpManager(final Context context, View statusBarWindowView) {
+ public HeadsUpManager(final Context context, View statusBarWindowView,
+ NotificationGroupManager groupManager) {
mContext = context;
Resources resources = mContext.getResources();
mTouchAcceptanceDelay = resources.getInteger(R.integer.touch_acceptance_delay);
@@ -132,6 +135,7 @@
Settings.Global.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS), false,
mSettingsObserver);
mStatusBarWindowView = statusBarWindowView;
+ mGroupManager = groupManager;
mStatusBarHeight = resources.getDimensionPixelSize(
com.android.internal.R.dimen.status_bar_height);
mNotificationsTopPadding = context.getResources()
@@ -170,7 +174,6 @@
*/
public void showNotification(NotificationData.Entry headsUp) {
if (DEBUG) Log.v(TAG, "showNotification");
- MetricsLogger.count(mContext, "note_peek", 1);
addHeadsUpEntry(headsUp);
updateNotification(headsUp, true);
headsUp.setInterruption();
@@ -182,12 +185,12 @@
public void updateNotification(NotificationData.Entry headsUp, boolean alert) {
if (DEBUG) Log.v(TAG, "updateNotification");
- headsUp.row.setChildrenExpanded(false /* expanded */, false /* animated */);
headsUp.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
if (alert) {
HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(headsUp.key);
headsUpEntry.updateEntry();
+ mGroupManager.onEntryHeadsUped(headsUp);
setEntryPinned(headsUpEntry, shouldHeadsUpBecomePinned(headsUp));
}
}
@@ -247,6 +250,9 @@
return;
}
mHasPinnedNotification = hasPinnedNotification;
+ if (mHasPinnedNotification) {
+ MetricsLogger.count(mContext, "note_peek", 1);
+ }
updateTouchableRegionListener();
for (OnHeadsUpChangedListener listener : mListeners) {
listener.onHeadsUpPinnedModeChanged(hasPinnedNotification);
@@ -600,6 +606,13 @@
@Override
public int compareTo(HeadsUpEntry o) {
+ boolean selfFullscreen = hasFullScreenIntent(entry);
+ boolean otherFullscreen = hasFullScreenIntent(o.entry);
+ if (selfFullscreen && !otherFullscreen) {
+ return -1;
+ } else if (!selfFullscreen && otherFullscreen) {
+ return 1;
+ }
return postTime < o.postTime ? 1
: postTime == o.postTime ? entry.key.compareTo(o.entry.key)
: -1;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 4c99792..4d268ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -18,12 +18,14 @@
import android.app.ActivityManager;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.hardware.input.InputManager;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.SystemClock;
import android.util.AttributeSet;
+import android.util.TypedValue;
import android.view.HapticFeedbackConstants;
import android.view.InputDevice;
import android.view.KeyCharacterMap;
@@ -43,6 +45,7 @@
public class KeyButtonView extends ImageView {
+ private int mContentDescriptionRes;
private long mDownTime;
private int mCode;
private int mTouchSlop;
@@ -79,8 +82,14 @@
mSupportsLongpress = a.getBoolean(R.styleable.KeyButtonView_keyRepeat, true);
+ TypedValue value = new TypedValue();
+ if (a.getValue(R.styleable.KeyButtonView_android_contentDescription, value)) {
+ mContentDescriptionRes = value.resourceId;
+ }
+
a.recycle();
+
setClickable(true);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
@@ -88,6 +97,15 @@
}
@Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+
+ if (mContentDescriptionRes != 0) {
+ setContentDescription(mContext.getString(mContentDescriptionRes));
+ }
+ }
+
+ @Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
if (mCode != 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
index b2df40a..0e91b0b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
@@ -27,7 +27,6 @@
void removeCallback(Callback callback);
void setZen(int zen, Uri conditionId, String reason);
int getZen();
- void requestConditions(boolean request);
ZenRule getManualRule();
ZenModeConfig getConfig();
long getNextAlarm();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index c07f1a8..96efea1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -121,15 +121,6 @@
}
@Override
- public void requestConditions(boolean request) {
- mRequesting = request;
- mNoMan.requestZenModeConditions(mListener, request ? Condition.FLAG_RELEVANT_NOW : 0);
- if (!mRequesting) {
- mConditions.clear();
- }
- }
-
- @Override
public ZenRule getManualRule() {
return mConfig == null ? null : mConfig.manualRule;
}
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 3c9e8cf..c9ebc84 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -21,11 +21,11 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.Button;
import com.android.systemui.R;
import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.ExpandableView;
+import com.android.systemui.statusbar.notification.HybridNotificationView;
+import com.android.systemui.statusbar.notification.HybridNotificationViewManager;
import java.util.ArrayList;
import java.util.List;
@@ -35,15 +35,24 @@
*/
public class NotificationChildrenContainer extends ViewGroup {
+ private static final int NUMBER_OF_CHILDREN_WHEN_COLLAPSED = 2;
+ private static final int NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED = 5;
+ private static final int NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED = 8;
+
private final int mChildPadding;
private final int mDividerHeight;
private final int mMaxNotificationHeight;
private final List<View> mDividers = new ArrayList<>();
private final List<ExpandableNotificationRow> mChildren = new ArrayList<>();
- private final View mCollapseButton;
- private final View mCollapseDivider;
- private final int mCollapseButtonHeight;
+ private final int mNotificationHeaderHeight;
private final int mNotificationAppearDistance;
+ private final float mHeaderTopPaddingSubstraction;
+ private final HybridNotificationViewManager mHybridViewManager;
+ private final float mCollapsedBottompadding;
+ private boolean mChildrenExpanded;
+ private ExpandableNotificationRow mNotificationParent;
+ private HybridNotificationView mGroupOverflowContainer;
+ private ViewState mGroupOverFlowState;
public NotificationChildrenContainer(Context context) {
this(context, null);
@@ -68,14 +77,11 @@
R.dimen.notification_max_height);
mNotificationAppearDistance = getResources().getDimensionPixelSize(
R.dimen.notification_appear_distance);
- LayoutInflater inflater = mContext.getSystemService(LayoutInflater.class);
- mCollapseButton = inflater.inflate(R.layout.notification_collapse_button, this,
- false);
- mCollapseButtonHeight = getResources().getDimensionPixelSize(
- R.dimen.notification_bottom_decor_height);
- addView(mCollapseButton);
- mCollapseDivider = inflateDivider();
- addView(mCollapseDivider);
+ mNotificationHeaderHeight = getResources().getDimensionPixelSize(
+ R.dimen.notification_header_height);
+ mHeaderTopPaddingSubstraction = 2 * getResources().getDisplayMetrics().density;
+ mCollapsedBottompadding = 10 * getResources().getDisplayMetrics().density;
+ mHybridViewManager = new HybridNotificationViewManager(getContext(), this);
}
@Override
@@ -85,14 +91,6 @@
for (int i = 0; i < childCount; i++) {
View child = mChildren.get(i);
boolean viewGone = child.getVisibility() == View.GONE;
- if (i != 0) {
- View divider = mDividers.get(i - 1);
- int dividerVisibility = divider.getVisibility();
- int newVisibility = viewGone ? INVISIBLE : VISIBLE;
- if (dividerVisibility != newVisibility) {
- divider.setVisibility(newVisibility);
- }
- }
if (viewGone) {
continue;
}
@@ -103,9 +101,10 @@
firstChild = false;
}
}
- mCollapseButton.layout(0, 0, getWidth(), mCollapseButtonHeight);
- mCollapseDivider.layout(0, mCollapseButtonHeight - mDividerHeight, getWidth(),
- mCollapseButtonHeight);
+ if (mGroupOverflowContainer != null) {
+ mGroupOverflowContainer.layout(0, 0, getWidth(),
+ mGroupOverflowContainer.getMeasuredHeight());
+ }
}
@Override
@@ -120,18 +119,11 @@
}
int newHeightSpec = MeasureSpec.makeMeasureSpec(ownMaxHeight, MeasureSpec.AT_MOST);
int dividerHeightSpec = MeasureSpec.makeMeasureSpec(mDividerHeight, MeasureSpec.EXACTLY);
- int collapseButtonHeightSpec = MeasureSpec.makeMeasureSpec(mCollapseButtonHeight,
- MeasureSpec.EXACTLY);
- mCollapseButton.measure(widthMeasureSpec, collapseButtonHeightSpec);
- mCollapseDivider.measure(widthMeasureSpec, dividerHeightSpec);
- int height = mCollapseButtonHeight;
+ int height = mNotificationHeaderHeight;
int childCount = mChildren.size();
boolean firstChild = true;
for (int i = 0; i < childCount; i++) {
View child = mChildren.get(i);
- if (child.getVisibility() == View.GONE) {
- continue;
- }
child.measure(widthMeasureSpec, newHeightSpec);
height += child.getMeasuredHeight();
if (!firstChild) {
@@ -144,9 +136,9 @@
}
}
int width = MeasureSpec.getSize(widthMeasureSpec);
- height = hasFixedHeight ? ownMaxHeight
- : isHeightLimited ? Math.min(ownMaxHeight, height)
- : height;
+ if (mGroupOverflowContainer != null) {
+ mGroupOverflowContainer.measure(widthMeasureSpec, newHeightSpec);
+ }
setMeasuredDimension(width, height);
}
@@ -165,8 +157,7 @@
addView(divider);
mDividers.add(Math.max(newIndex - 1, 0), divider);
}
- // TODO: adapt background corners
- // TODO: fix overdraw
+ updateGroupOverflow();
}
public void removeNotification(ExpandableNotificationRow row) {
@@ -178,7 +169,26 @@
removeView(divider);
}
row.setSystemChildExpanded(false);
- // TODO: adapt background corners
+ updateGroupOverflow();
+ }
+
+ public void updateGroupOverflow() {
+ int childCount = mChildren.size();
+ int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
+ boolean hasOverflow = childCount > maxAllowedVisibleChildren;
+ int lastVisibleIndex = hasOverflow ? maxAllowedVisibleChildren - 2
+ : maxAllowedVisibleChildren - 1;
+ if (hasOverflow) {
+ mGroupOverflowContainer = mHybridViewManager.bindFromNotificationGroup(
+ mGroupOverflowContainer, mChildren, lastVisibleIndex + 1);
+ if (mGroupOverFlowState == null) {
+ mGroupOverFlowState = new ViewState();
+ }
+ } else if (mGroupOverflowContainer != null) {
+ removeView(mGroupOverflowContainer);
+ mGroupOverflowContainer = null;
+ mGroupOverFlowState = null;
+ }
}
private View inflateDivider() {
@@ -210,25 +220,42 @@
result = true;
}
}
-
- // Let's make the first child expanded!
- boolean first = true;
- for (int i = 0; i < childOrder.size(); i++) {
- ExpandableNotificationRow child = childOrder.get(i);
- child.setSystemChildExpanded(first);
- first = false;
- }
+ updateExpansionStates();
return result;
}
+ private void updateExpansionStates() {
+ // Let's make the first child expanded if the parent is
+ for (int i = 0; i < mChildren.size(); i++) {
+ ExpandableNotificationRow child = mChildren.get(i);
+ child.setSystemChildExpanded(false);
+ }
+ }
+
+ /**
+ *
+ * @return the intrinsic size of this children container, i.e the natural fully expanded state
+ */
public int getIntrinsicHeight() {
- int childCount = mChildren.size();
+ int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren();
+ return getIntrinsicHeight(maxAllowedVisibleChildren);
+ }
+
+ /**
+ * @return the intrinsic height with a number of children given
+ * in @param maxAllowedVisibleChildren
+ */
+ private int getIntrinsicHeight(float maxAllowedVisibleChildren) {
int intrinsicHeight = 0;
int visibleChildren = 0;
+ int childCount = mChildren.size();
for (int i = 0; i < childCount; i++) {
+ if (visibleChildren >= maxAllowedVisibleChildren) {
+ break;
+ }
ExpandableNotificationRow child = mChildren.get(i);
- if (child.getVisibility() == View.GONE) {
- continue;
+ if (i == 0 && child.hasSameBgColor(mNotificationParent)) {
+ intrinsicHeight -= mHeaderTopPaddingSubstraction;
}
intrinsicHeight += child.getIntrinsicHeight();
visibleChildren++;
@@ -236,6 +263,9 @@
if (visibleChildren > 0) {
intrinsicHeight += (visibleChildren - 1) * mDividerHeight;
}
+ if (!mChildrenExpanded) {
+ intrinsicHeight += mCollapsedBottompadding;
+ }
return intrinsicHeight;
}
@@ -247,17 +277,23 @@
*/
public void getState(StackScrollState resultState, StackViewState parentState) {
int childCount = mChildren.size();
- int yPosition = mCollapseButtonHeight;
+ int yPosition = mNotificationHeaderHeight;
boolean firstChild = true;
+ int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren();
+ boolean hasOverflow = !mChildrenExpanded && childCount > maxAllowedVisibleChildren
+ && maxAllowedVisibleChildren != NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED;
+ int lastVisibleIndex = hasOverflow
+ ? maxAllowedVisibleChildren - 2
+ : maxAllowedVisibleChildren - 1;
for (int i = 0; i < childCount; i++) {
ExpandableNotificationRow child = mChildren.get(i);
- if (child.getVisibility() == View.GONE) {
- continue;
- }
if (!firstChild) {
// There's a divider
yPosition += mChildPadding;
} else {
+ if (child.hasSameBgColor(mNotificationParent)) {
+ yPosition -= mHeaderTopPaddingSubstraction;
+ }
firstChild = false;
}
StackViewState childState = resultState.getViewStateForView(child);
@@ -269,41 +305,62 @@
childState.dark = parentState.dark;
childState.hideSensitive = parentState.hideSensitive;
childState.belowSpeedBump = parentState.belowSpeedBump;
- childState.scale = parentState.scale;
+ childState.scale = 1.0f;
childState.clipTopAmount = 0;
childState.topOverLap = 0;
+ boolean visible = i <= lastVisibleIndex;
+ childState.alpha = visible ? 1 : 0;
childState.location = parentState.location;
yPosition += intrinsicHeight;
}
+ if (mGroupOverflowContainer != null) {
+ mGroupOverFlowState.initFrom(mGroupOverflowContainer);
+ if (hasOverflow) {
+ StackViewState firstOverflowState =
+ resultState.getViewStateForView(mChildren.get(lastVisibleIndex + 1));
+ mGroupOverFlowState.yTranslation = firstOverflowState.yTranslation;
+ }
+ mGroupOverFlowState.alpha = mChildrenExpanded || !hasOverflow ? 0.0f : 1.0f;
+ }
+ }
+
+ private int getMaxAllowedVisibleChildren() {
+ return getMaxAllowedVisibleChildren(false /* likeCollapsed */);
+ }
+
+ private int getMaxAllowedVisibleChildren(boolean likeCollapsed) {
+ if (!likeCollapsed && (mChildrenExpanded || mNotificationParent.isUserLocked())) {
+ return NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED;
+ }
+ if (mNotificationParent.isExpanded()) {
+ return NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED;
+ }
+ return NUMBER_OF_CHILDREN_WHEN_COLLAPSED;
}
public void applyState(StackScrollState state) {
int childCount = mChildren.size();
boolean firstChild = true;
- ViewState dividerState = new ViewState();
+ ViewState tmpState = new ViewState();
for (int i = 0; i < childCount; i++) {
ExpandableNotificationRow child = mChildren.get(i);
StackViewState viewState = state.getViewStateForView(child);
- if (child.getVisibility() == View.GONE) {
- continue;
- }
if (!firstChild) {
// layout the divider
View divider = mDividers.get(i - 1);
- dividerState.initFrom(divider);
- dividerState.yTranslation = (int) (viewState.yTranslation
+ tmpState.initFrom(divider);
+ tmpState.yTranslation = (int) (viewState.yTranslation
- (mChildPadding + mDividerHeight) / 2.0f);
- dividerState.alpha = 1;
- state.applyViewState(divider, dividerState);
+ tmpState.alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0;
+ state.applyViewState(divider, tmpState);
} else {
firstChild = false;
}
state.applyState(child, viewState);
}
- }
-
- public void setCollapseClickListener(OnClickListener collapseClickListener) {
- mCollapseButton.setOnClickListener(collapseClickListener);
+ if (mGroupOverflowContainer != null) {
+ state.applyViewState(mGroupOverflowContainer, mGroupOverFlowState);
+ }
}
/**
@@ -313,6 +370,10 @@
* @param state the new state we animate to
*/
public void prepareExpansionChanged(StackScrollState state) {
+ if (true) {
+ // TODO: do something that makes sense
+ return;
+ }
int childCount = mChildren.size();
boolean firstChild = true;
StackViewState sourceState = new StackViewState();
@@ -320,9 +381,6 @@
for (int i = 0; i < childCount; i++) {
ExpandableNotificationRow child = mChildren.get(i);
StackViewState viewState = state.getViewStateForView(child);
- if (child.getVisibility() == View.GONE) {
- continue;
- }
if (!firstChild) {
// layout the divider
View divider = mDividers.get(i - 1);
@@ -339,23 +397,17 @@
sourceState.yTranslation += mNotificationAppearDistance;
state.applyState(child, sourceState);
}
- mCollapseButton.setAlpha(0);
- mCollapseDivider.setAlpha(0);
- mCollapseDivider.setTranslationY(mNotificationAppearDistance / 4);
}
public void startAnimationToState(StackScrollState state, StackStateAnimator stateAnimator,
boolean withDelays, long baseDelay, long duration) {
int childCount = mChildren.size();
boolean firstChild = true;
- ViewState dividerState = new ViewState();
+ ViewState tmpState = new ViewState();
int notGoneIndex = 0;
for (int i = 0; i < childCount; i++) {
ExpandableNotificationRow child = mChildren.get(i);
StackViewState viewState = state.getViewStateForView(child);
- if (child.getVisibility() == View.GONE) {
- continue;
- }
int difference = Math.min(StackStateAnimator.DELAY_EFFECT_MAX_INDEX_DIFFERENCE_CHILDREN,
notGoneIndex + 1);
long delay = withDelays
@@ -365,24 +417,20 @@
if (!firstChild) {
// layout the divider
View divider = mDividers.get(i - 1);
- dividerState.initFrom(divider);
- dividerState.yTranslation = viewState.yTranslation
+ tmpState.initFrom(divider);
+ tmpState.yTranslation = viewState.yTranslation
- (mChildPadding + mDividerHeight) / 2.0f;
- dividerState.alpha = 1;
- stateAnimator.startViewAnimations(divider, dividerState, delay, duration);
+ tmpState.alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0;;
+ stateAnimator.startViewAnimations(divider, tmpState, delay, duration);
} else {
firstChild = false;
}
stateAnimator.startStackAnimations(child, viewState, state, -1, delay);
notGoneIndex++;
}
- dividerState.initFrom(mCollapseButton);
- dividerState.alpha = 1.0f;
- stateAnimator.startViewAnimations(mCollapseButton, dividerState, baseDelay, duration);
- dividerState.initFrom(mCollapseDivider);
- dividerState.alpha = 1.0f;
- dividerState.yTranslation = 0.0f;
- stateAnimator.startViewAnimations(mCollapseDivider, dividerState, baseDelay, duration);
+ if (mGroupOverflowContainer != null) {
+ stateAnimator.startViewAnimations(mGroupOverflowContainer, mGroupOverFlowState, -1, 0);
+ }
}
public ExpandableNotificationRow getViewAtPosition(float y) {
@@ -400,7 +448,19 @@
return null;
}
- public void setTintColor(int color) {
- ExpandableNotificationRow.applyTint(mCollapseDivider, color);
+ public void setChildrenExpanded(boolean childrenExpanded) {
+ mChildrenExpanded = childrenExpanded;
+ }
+
+ public void setNotificationParent(ExpandableNotificationRow parent) {
+ mNotificationParent = parent;
+ }
+
+ public int getMaxContentHeight() {
+ return getIntrinsicHeight(NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED);
+ }
+
+ public int getMinHeight() {
+ return getIntrinsicHeight(getMaxAllowedVisibleChildren(true /* forceCollapsed */));
}
}
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 5e5f810..aeca97c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -747,7 +747,8 @@
public void setUserExpandedChild(View v, boolean userExpanded) {
if (v instanceof ExpandableNotificationRow) {
- ((ExpandableNotificationRow) v).setUserExpanded(userExpanded);
+ ((ExpandableNotificationRow) v).setUserExpanded(userExpanded,
+ true /* allowChildrenExpansion */);
}
}
@@ -1726,7 +1727,7 @@
ExpandableNotificationRow groupSummary =
mGroupManager.getGroupSummary(row.getStatusBarNotification());
if (groupSummary != null && groupSummary != row) {
- return !groupSummary.areChildrenExpanded();
+ return row.getVisibility() == View.INVISIBLE;
}
}
return false;
@@ -1789,6 +1790,7 @@
((ExpandableView) child).setOnHeightChangedListener(this);
generateAddAnimation(child, false /* fromMoreCard */);
updateAnimationState(child);
+ updateChronometerForChild(child);
if (canChildBeDismissed(child)) {
// Make sure the dismissButton is visible and not in the animated state.
// We need to do this to avoid a race where a clearable notification is added after the
@@ -2298,6 +2300,21 @@
mStackScrollAlgorithm.setIsExpanded(isExpanded);
if (changed) {
updateNotificationAnimationStates();
+ updateChronometers();
+ }
+ }
+
+ private void updateChronometers() {
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ updateChronometerForChild(getChildAt(i));
+ }
+ }
+
+ private void updateChronometerForChild(View child) {
+ if (child instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ row.setChronometerRunning(mIsExpanded);
}
}
@@ -2320,6 +2337,7 @@
}
mStackScrollAlgorithm.onReset(view);
updateAnimationState(view);
+ updateChronometerForChild(view);
}
private void updateScrollPositionOnExpandInBottom(ExpandableView view) {
@@ -2327,7 +2345,13 @@
ExpandableNotificationRow row = (ExpandableNotificationRow) view;
if (row.isUserLocked() && row != getFirstChildNotGone()) {
// We are actually expanding this view
- float endPosition = row.getTranslationY() + row.getActualHeight();
+ float endPosition;
+ if (row.isChildInGroup()) {
+ ExpandableNotificationRow parent = row.getNotificationParent();
+ endPosition = parent.getTranslationY() + parent.getActualHeight();
+ } else {
+ endPosition = row.getTranslationY() + row.getActualHeight();
+ }
int stackEnd = mMaxLayoutHeight - mBottomStackPeekSize -
mBottomStackSlowDownHeight + (int) mStackTranslation;
if (endPosition > stackEnd) {
@@ -2771,16 +2795,6 @@
return touchY > mTopPadding + mStackTranslation;
}
- private void updateExpandButtons() {
- for (int i = 0; i < getChildCount(); i++) {
- View child = getChildAt(i);
- if (child instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) child;
- row.updateExpandButton();
- }
- }
- }
-
@Override
public void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded) {
boolean animated = mAnimationsEnabled && mIsExpanded;
@@ -2793,11 +2807,6 @@
}
@Override
- public void onGroupsProhibitedChanged() {
- updateExpandButtons();
- }
-
- @Override
public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) {
for (NotificationData.Entry entry : group.children) {
ExpandableNotificationRow row = entry.row;
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 cf696a1..65ca95b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -361,7 +361,7 @@
// handle the notgoneIndex for the children as well
List<ExpandableNotificationRow> children =
row.getNotificationChildren();
- if (row.areChildrenExpanded() && children != null) {
+ if (row.isSummaryWithChildren() && children != null) {
for (ExpandableNotificationRow childRow : children) {
if (childRow.getVisibility() != View.GONE) {
StackViewState childState
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
index 3768ca4..e155d70 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
@@ -65,7 +65,7 @@
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
List<ExpandableNotificationRow> children =
row.getNotificationChildren();
- if (row.areChildrenExpanded() && children != null) {
+ if (row.isSummaryWithChildren() && children != null) {
for (ExpandableNotificationRow childRow : children) {
resetViewState(childRow);
}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java b/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java
index 3ac2a94..05e3fd5 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java
@@ -46,6 +46,7 @@
import com.android.systemui.qs.QSTile;
import com.android.systemui.qs.QSTile.Host.Callback;
import com.android.systemui.qs.QSTile.ResourceIcon;
+import com.android.systemui.qs.QSTileBaseView;
import com.android.systemui.qs.QSTileView;
import com.android.systemui.qs.tiles.IntentTile;
import com.android.systemui.statusbar.phone.QSTileHost;
@@ -211,7 +212,7 @@
}
@Override
- protected QSTile<?> createTile(String tileSpec) {
+ public QSTile<?> createTile(String tileSpec) {
return new DraggableTile(this, tileSpec);
}
@@ -381,7 +382,7 @@
private static class DraggableTile extends QSTile<QSTile.State>
implements DropListener {
private String mSpec;
- private QSTileView mView;
+ private QSTileBaseView mView;
protected DraggableTile(QSTile.Host host, String tileSpec) {
super(host);
@@ -390,7 +391,7 @@
}
@Override
- public QSTileView createTileView(Context context) {
+ public QSTileBaseView createTileView(Context context) {
mView = super.createTileView(context);
return mView;
}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
index 911d6a2..dc7c967 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
@@ -16,9 +16,10 @@
package com.android.systemui.tuner;
import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
import android.app.FragmentTransaction;
import android.content.DialogInterface;
-import android.content.DialogInterface.OnClickListener;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Bundle;
@@ -33,7 +34,6 @@
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
-
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.qs.QSPanel;
@@ -51,6 +51,8 @@
public static final String SETTING_SEEN_TUNER_WARNING = "seen_tuner_warning";
+ private static final String WARNING_TAG = "tuner_warning";
+
private static final int MENU_REMOVE = Menu.FIRST + 1;
private final SettingObserver mSettingObserver = new SettingObserver();
@@ -90,16 +92,9 @@
mBatteryPct = (SwitchPreference) findPreference(KEY_BATTERY_PCT);
if (Settings.Secure.getInt(getContext().getContentResolver(), SETTING_SEEN_TUNER_WARNING,
0) == 0) {
- new AlertDialog.Builder(getContext())
- .setTitle(R.string.tuner_warning_title)
- .setMessage(R.string.tuner_warning)
- .setPositiveButton(R.string.got_it, new OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- Settings.Secure.putInt(getContext().getContentResolver(),
- SETTING_SEEN_TUNER_WARNING, 1);
- }
- }).show();
+ if (getFragmentManager().findFragmentByTag(WARNING_TAG) == null) {
+ new TunerWarningFragment().show(getFragmentManager(), WARNING_TAG);
+ }
}
TunerService.get(getContext()).addTunable(mQsPaging, QSPanel.QS_THE_NEW_QS);
}
@@ -187,4 +182,20 @@
mQsTuner.setEnabled(newValue == null || Integer.parseInt(newValue) == 0);
}
};
+
+ public static class TunerWarningFragment extends DialogFragment {
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ return new AlertDialog.Builder(getContext())
+ .setTitle(R.string.tuner_warning_title)
+ .setMessage(R.string.tuner_warning)
+ .setPositiveButton(R.string.got_it, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Settings.Secure.putInt(getContext().getContentResolver(),
+ SETTING_SEEN_TUNER_WARNING, 1);
+ }
+ }).show();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
index 71559389..1e3b0f1 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
@@ -116,8 +116,12 @@
public void reloadSetting(Uri uri) {
String key = mListeningUris.get(uri);
+ List<Tunable> tunables = mTunableLookup.get(key);
+ if (tunables == null) {
+ return;
+ }
String value = Settings.Secure.getStringForUser(mContentResolver, key, mCurrentUser);
- for (Tunable tunable : mTunableLookup.get(key)) {
+ for (Tunable tunable : tunables) {
tunable.onTuningChanged(key, value);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java
index 1cf7a70f..673a30b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java
@@ -125,10 +125,6 @@
return mAudio;
}
- public ZenModeConfig getZenModeConfig() {
- return mNoMan.getZenModeConfig();
- }
-
public void dismiss() {
mCallbacks.onDismissRequested(Events.DISMISS_REASON_VOLUME_CONTROLLER);
}
@@ -348,7 +344,6 @@
updateRingerModeExternalW(mAudio.getRingerMode());
updateZenModeW();
updateEffectsSuppressorW(mNoMan.getEffectsSuppressor());
- updateZenModeConfigW();
mCallbacks.onStateChanged(mState);
}
@@ -401,13 +396,6 @@
return stream == AudioManager.STREAM_RING || stream == AudioManager.STREAM_NOTIFICATION;
}
- private boolean updateZenModeConfigW() {
- final ZenModeConfig zenModeConfig = getZenModeConfig();
- if (Objects.equals(mState.zenModeConfig, zenModeConfig)) return false;
- mState.zenModeConfig = zenModeConfig;
- return true;
- }
-
private boolean updateEffectsSuppressorW(ComponentName effectsSuppressor) {
if (Objects.equals(mState.effectsSuppressor, effectsSuppressor)) return false;
mState.effectsSuppressor = effectsSuppressor;
@@ -748,9 +736,6 @@
if (ZEN_MODE_URI.equals(uri)) {
changed = updateZenModeW();
}
- if (ZEN_MODE_CONFIG_URI.equals(uri)) {
- changed = updateZenModeConfigW();
- }
if (changed) {
mCallbacks.onStateChanged(mState);
}
@@ -947,7 +932,6 @@
public int zenMode;
public ComponentName effectsSuppressor;
public String effectsSuppressorName;
- public ZenModeConfig zenModeConfig;
public int activeStream = NO_ACTIVE_STREAM;
public State copy() {
@@ -960,7 +944,6 @@
rt.zenMode = zenMode;
if (effectsSuppressor != null) rt.effectsSuppressor = effectsSuppressor.clone();
rt.effectsSuppressorName = effectsSuppressorName;
- if (zenModeConfig != null) rt.zenModeConfig = zenModeConfig.copy();
rt.activeStream = activeStream;
return rt;
}
@@ -989,7 +972,6 @@
sep(sb, indent); sb.append("zenMode:").append(zenMode);
sep(sb, indent); sb.append("effectsSuppressor:").append(effectsSuppressor);
sep(sb, indent); sb.append("effectsSuppressorName:").append(effectsSuppressorName);
- sep(sb, indent); sb.append("zenModeConfig:").append(zenModeConfig);
sep(sb, indent); sb.append("activeStream:").append(activeStream);
if (indent > 0) sep(sb, indent);
return sb.append('}').toString();
@@ -1005,11 +987,6 @@
sb.append(',');
}
}
-
- public Condition getManualExitCondition() {
- return zenModeConfig != null && zenModeConfig.manualRule != null
- ? zenModeConfig.manualRule.condition : null;
- }
}
public interface Callbacks {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
index f156607..a03e7f7 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
@@ -141,6 +141,7 @@
public void onConfigurationChanged() {
mEndNowButton.setText(mContext.getString(R.string.volume_zen_end_now));
mSpTexts.update();
+ Util.setText(mEndNowButton, mContext.getString(R.string.volume_zen_end_now));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index 3c9a7fc..cd47ac1 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -1,4 +1,4 @@
-/*
+/**
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -58,6 +58,8 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Arrays;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.Objects;
@@ -68,14 +70,14 @@
private static final int SECONDS_MS = 1000;
private static final int MINUTES_MS = 60 * SECONDS_MS;
- private static final int[] MINUTE_BUCKETS = DEBUG
- ? new int[] { 0, 1, 2, 5, 15, 30, 45, 60, 120, 180, 240, 480 }
- : ZenModeConfig.MINUTE_BUCKETS;
+ private static final int[] MINUTE_BUCKETS = ZenModeConfig.MINUTE_BUCKETS;
private static final int MIN_BUCKET_MINUTES = MINUTE_BUCKETS[0];
private static final int MAX_BUCKET_MINUTES = MINUTE_BUCKETS[MINUTE_BUCKETS.length - 1];
private static final int DEFAULT_BUCKET_INDEX = Arrays.binarySearch(MINUTE_BUCKETS, 60);
private static final int FOREVER_CONDITION_INDEX = 0;
private static final int COUNTDOWN_CONDITION_INDEX = 1;
+ private static final int COUNTDOWN_ALARM_CONDITION_INDEX = 2;
+ private static final int COUNTDOWN_CONDITION_COUNT = 2;
public static final Intent ZEN_SETTINGS
= new Intent(Settings.ACTION_ZEN_MODE_SETTINGS);
@@ -86,7 +88,6 @@
private final LayoutInflater mInflater;
private final H mHandler = new H();
private final ZenPrefs mPrefs;
- private final IconPulser mIconPulser;
private final TransitionHelper mTransitionHelper = new TransitionHelper();
private final Uri mForeverId;
private final SpTexts mSpTexts;
@@ -104,9 +105,6 @@
private Callback mCallback;
private ZenModeController mController;
private boolean mCountdownConditionSupported;
- private int mMaxConditions;
- private int mMaxOptionalConditions;
- private int mFirstConditionIndex;
private boolean mRequestingConditions;
private Condition mExitCondition;
private int mBucketIndex = -1;
@@ -118,6 +116,7 @@
private Condition mSessionExitCondition;
private Condition[] mConditions;
private Condition mTimeCondition;
+ private Condition mTimeUntilAlarmCondition;
private boolean mVoiceCapable;
public ZenModePanel(Context context, AttributeSet attrs) {
@@ -125,7 +124,6 @@
mContext = context;
mPrefs = new ZenPrefs();
mInflater = LayoutInflater.from(mContext.getApplicationContext());
- mIconPulser = new IconPulser(mContext);
mForeverId = Condition.newId(mContext).appendPath("forever").build();
mSpTexts = new SpTexts(mContext);
mVoiceCapable = Util.isVoiceCapable(mContext);
@@ -135,7 +133,6 @@
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("ZenModePanel state:");
pw.print(" mCountdownConditionSupported="); pw.println(mCountdownConditionSupported);
- pw.print(" mMaxConditions="); pw.println(mMaxConditions);
pw.print(" mRequestingConditions="); pw.println(mRequestingConditions);
pw.print(" mAttached="); pw.println(mAttached);
pw.print(" mHidden="); pw.println(mHidden);
@@ -286,14 +283,6 @@
if (mRequestingConditions == requesting) return;
if (DEBUG) Log.d(mTag, "setRequestingConditions " + requesting);
mRequestingConditions = requesting;
- if (mController != null) {
- AsyncTask.execute(new Runnable() {
- @Override
- public void run() {
- mController.requestConditions(requesting);
- }
- });
- }
if (mRequestingConditions) {
mTimeCondition = parseExistingTimeCondition(mContext, mExitCondition);
if (mTimeCondition != null) {
@@ -304,6 +293,7 @@
MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser());
}
if (DEBUG) Log.d(mTag, "Initial bucket index: " + mBucketIndex);
+
mConditions = null; // reset conditions
handleUpdateConditions();
} else {
@@ -314,13 +304,9 @@
public void init(ZenModeController controller) {
mController = controller;
mCountdownConditionSupported = mController.isCountdownConditionSupported();
- final int countdownDelta = mCountdownConditionSupported ? 1 : 0;
- mFirstConditionIndex = COUNTDOWN_CONDITION_INDEX + countdownDelta;
+ final int countdownDelta = mCountdownConditionSupported ? COUNTDOWN_CONDITION_COUNT : 0;
final int minConditions = 1 /*forever*/ + countdownDelta;
- mMaxConditions = MathUtils.constrain(mContext.getResources()
- .getInteger(R.integer.zen_mode_max_conditions), minConditions, 100);
- mMaxOptionalConditions = mMaxConditions - minConditions;
- for (int i = 0; i < mMaxConditions; i++) {
+ for (int i = 0; i < minConditions; i++) {
mZenConditions.addView(mInflater.inflate(R.layout.zen_mode_condition, this, false));
}
mSessionZen = getSelectedZen(-1);
@@ -357,28 +343,10 @@
return condition == null ? null : condition.copy();
}
- public static String getExitConditionText(Context context, Condition exitCondition) {
- if (exitCondition == null) {
- return foreverSummary(context);
- } else if (isCountdown(exitCondition)) {
- final Condition condition = parseExistingTimeCondition(context, exitCondition);
- return condition != null ? condition.summary : foreverSummary(context);
- } else {
- return exitCondition.summary;
- }
- }
-
public void setCallback(Callback callback) {
mCallback = callback;
}
- public void showSilentHint() {
- if (DEBUG) Log.d(mTag, "showSilentHint");
- if (mZenButtons == null || mZenButtons.getChildCount() == 0) return;
- final View noneButton = mZenButtons.getChildAt(0);
- mIconPulser.start(noneButton);
- }
-
private void handleUpdateManualRule(ZenRule rule) {
final int zen = rule != null ? rule.zenMode : Global.ZEN_MODE_OFF;
handleUpdateZen(zen);
@@ -410,7 +378,7 @@
final ConditionTag tag = getConditionTagAt(i);
if (tag != null) {
if (sameConditionId(tag.condition, mExitCondition)) {
- bind(exitCondition, mZenConditions.getChildAt(i));
+ bind(exitCondition, mZenConditions.getChildAt(i), i);
}
}
}
@@ -495,64 +463,32 @@
final long span = time - now;
if (span <= 0 || span > MAX_BUCKET_MINUTES * MINUTES_MS) return null;
return ZenModeConfig.toTimeCondition(context,
- time, Math.round(span / (float) MINUTES_MS), now, ActivityManager.getCurrentUser(),
+ time, Math.round(span / (float) MINUTES_MS), ActivityManager.getCurrentUser(),
false /*shortVersion*/);
}
- private void handleUpdateConditions(Condition[] conditions) {
- conditions = trimConditions(conditions);
- if (Arrays.equals(conditions, mConditions)) {
- final int count = mConditions == null ? 0 : mConditions.length;
- if (DEBUG) Log.d(mTag, "handleUpdateConditions unchanged conditionCount=" + count);
- return;
- }
- mConditions = conditions;
- handleUpdateConditions();
- }
-
- private Condition[] trimConditions(Condition[] conditions) {
- if (conditions == null || conditions.length <= mMaxOptionalConditions) {
- // no need to trim
- return conditions;
- }
- // look for current exit condition, ensure it is included if found
- int found = -1;
- for (int i = 0; i < conditions.length; i++) {
- final Condition c = conditions[i];
- if (mSessionExitCondition != null && sameConditionId(mSessionExitCondition, c)) {
- found = i;
- break;
- }
- }
- final Condition[] rt = Arrays.copyOf(conditions, mMaxOptionalConditions);
- if (found >= mMaxOptionalConditions) {
- // found after the first N, promote to the end of the first N
- rt[mMaxOptionalConditions - 1] = conditions[found];
- }
- return rt;
- }
-
private void handleUpdateConditions() {
if (mTransitionHelper.isTransitioning()) {
- mTransitionHelper.pendingUpdateConditions();
return;
}
final int conditionCount = mConditions == null ? 0 : mConditions.length;
if (DEBUG) Log.d(mTag, "handleUpdateConditions conditionCount=" + conditionCount);
// forever
- bind(forever(), mZenConditions.getChildAt(FOREVER_CONDITION_INDEX));
+ bind(forever(), mZenConditions.getChildAt(FOREVER_CONDITION_INDEX),
+ FOREVER_CONDITION_INDEX);
// countdown
if (mCountdownConditionSupported && mTimeCondition != null) {
- bind(mTimeCondition, mZenConditions.getChildAt(COUNTDOWN_CONDITION_INDEX));
+ bind(mTimeCondition, mZenConditions.getChildAt(COUNTDOWN_CONDITION_INDEX),
+ COUNTDOWN_CONDITION_INDEX);
}
- // provider conditions
- for (int i = 0; i < conditionCount; i++) {
- bind(mConditions[i], mZenConditions.getChildAt(mFirstConditionIndex + i));
- }
- // hide the rest
- for (int i = mZenConditions.getChildCount() - 1; i > mFirstConditionIndex + conditionCount;
- i--) {
- mZenConditions.getChildAt(i).setVisibility(GONE);
+ // countdown until alarm
+ if (mCountdownConditionSupported) {
+ Condition nextAlarmCondition = getTimeUntilNextAlarmCondition();
+ if (nextAlarmCondition != null) {
+ bind(nextAlarmCondition,
+ mZenConditions.getChildAt(COUNTDOWN_ALARM_CONDITION_INDEX),
+ COUNTDOWN_ALARM_CONDITION_INDEX);
+ }
}
// ensure something is selected
if (mExpanded && isShown()) {
@@ -569,6 +505,33 @@
return context.getString(com.android.internal.R.string.zen_mode_forever);
}
+ // Returns a time condition if the next alarm is within the next week.
+ private Condition getTimeUntilNextAlarmCondition() {
+ GregorianCalendar weekRange = new GregorianCalendar();
+ final long now = weekRange.getTimeInMillis();
+ setToMidnight(weekRange);
+ weekRange.roll(Calendar.DATE, 6);
+ final long nextAlarmMs = mController.getNextAlarm();
+ if (nextAlarmMs > 0) {
+ GregorianCalendar nextAlarm = new GregorianCalendar();
+ nextAlarm.setTimeInMillis(nextAlarmMs);
+ setToMidnight(nextAlarm);
+
+ if (weekRange.compareTo(nextAlarm) >= 0) {
+ return ZenModeConfig.toNextAlarmCondition(mContext, now, nextAlarmMs,
+ ActivityManager.getCurrentUser());
+ }
+ }
+ return null;
+ }
+
+ private void setToMidnight(Calendar calendar) {
+ calendar.set(Calendar.HOUR_OF_DAY, 0);
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 0);
+ calendar.set(Calendar.MILLISECOND, 0);
+ }
+
private ConditionTag getConditionTagAt(int index) {
return (ConditionTag) mZenConditions.getChildAt(index).getTag();
}
@@ -610,7 +573,8 @@
mTimeCondition = ZenModeConfig.toTimeCondition(mContext,
MINUTE_BUCKETS[favoriteIndex], ActivityManager.getCurrentUser());
mBucketIndex = favoriteIndex;
- bind(mTimeCondition, mZenConditions.getChildAt(COUNTDOWN_CONDITION_INDEX));
+ bind(mTimeCondition, mZenConditions.getChildAt(COUNTDOWN_CONDITION_INDEX),
+ COUNTDOWN_CONDITION_INDEX);
getConditionTagAt(COUNTDOWN_CONDITION_INDEX).rb.setChecked(true);
}
}
@@ -623,7 +587,7 @@
return c != null && mForeverId.equals(c.id);
}
- private void bind(final Condition condition, final View row) {
+ private void bind(final Condition condition, final View row, final int rowId) {
if (condition == null) throw new IllegalArgumentException("condition must not be null");
final boolean enabled = condition.state == Condition.STATE_TRUE;
final ConditionTag tag =
@@ -638,14 +602,6 @@
if (DEBUG) Log.d(mTag, "bind i=" + mZenConditions.indexOfChild(row) + " first=" + first
+ " condition=" + conditionId);
tag.rb.setEnabled(enabled);
- final boolean checked = (mSessionExitCondition != null
- || mAttachedZen != Global.ZEN_MODE_OFF)
- && (sameConditionId(mSessionExitCondition, tag.condition)
- || isCountdown(mSessionExitCondition) && isCountdown(tag.condition));
- if (checked != tag.rb.isChecked()) {
- if (DEBUG) Log.d(mTag, "bind checked=" + checked + " condition=" + conditionId);
- tag.rb.setChecked(checked);
- }
tag.rb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
@@ -692,7 +648,7 @@
button1.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
- onClickTimeButton(row, tag, false /*down*/);
+ onClickTimeButton(row, tag, false /*down*/, rowId);
}
});
@@ -700,7 +656,7 @@
button2.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
- onClickTimeButton(row, tag, true /*up*/);
+ onClickTimeButton(row, tag, true /*up*/, rowId);
}
});
tag.lines.setOnClickListener(new OnClickListener() {
@@ -711,7 +667,7 @@
});
final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
- if (time > 0) {
+ if (rowId != COUNTDOWN_ALARM_CONDITION_INDEX && time > 0) {
button1.setVisibility(VISIBLE);
button2.setVisibility(VISIBLE);
if (mBucketIndex > -1) {
@@ -761,7 +717,7 @@
tag.line1.getText()));
}
- private void onClickTimeButton(View row, ConditionTag tag, boolean up) {
+ private void onClickTimeButton(View row, ConditionTag tag, boolean up, int rowId) {
MetricsLogger.action(mContext, MetricsLogger.QS_DND_TIME, up);
Condition newCondition = null;
final int N = MINUTE_BUCKETS.length;
@@ -777,7 +733,7 @@
if (up && bucketTime > time || !up && bucketTime < time) {
mBucketIndex = j;
newCondition = ZenModeConfig.toTimeCondition(mContext,
- bucketTime, bucketMinutes, now, ActivityManager.getCurrentUser(),
+ bucketTime, bucketMinutes, ActivityManager.getCurrentUser(),
false /*shortVersion*/);
break;
}
@@ -794,7 +750,7 @@
MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser());
}
mTimeCondition = newCondition;
- bind(mTimeCondition, row);
+ bind(mTimeCondition, row, rowId);
tag.rb.setChecked(true);
select(mTimeCondition);
announceConditionSelection(tag);
@@ -838,18 +794,12 @@
private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
@Override
- public void onConditionsChanged(Condition[] conditions) {
- mHandler.obtainMessage(H.UPDATE_CONDITIONS, conditions).sendToTarget();
- }
-
- @Override
public void onManualRuleChanged(ZenRule rule) {
mHandler.obtainMessage(H.MANUAL_RULE_CHANGED, rule).sendToTarget();
}
};
private final class H extends Handler {
- private static final int UPDATE_CONDITIONS = 1;
private static final int MANUAL_RULE_CHANGED = 2;
private static final int UPDATE_WIDGETS = 3;
@@ -860,7 +810,6 @@
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case UPDATE_CONDITIONS: handleUpdateConditions((Condition[]) msg.obj); break;
case MANUAL_RULE_CHANGED: handleUpdateManualRule((ZenRule) msg.obj); break;
case UPDATE_WIDGETS: updateWidgets(); break;
}
@@ -1005,26 +954,20 @@
private final ArraySet<View> mTransitioningViews = new ArraySet<View>();
private boolean mTransitioning;
- private boolean mPendingUpdateConditions;
private boolean mPendingUpdateWidgets;
public void clear() {
mTransitioningViews.clear();
- mPendingUpdateConditions = mPendingUpdateWidgets = false;
+ mPendingUpdateWidgets = false;
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println(" TransitionHelper state:");
- pw.print(" mPendingUpdateConditions="); pw.println(mPendingUpdateConditions);
pw.print(" mPendingUpdateWidgets="); pw.println(mPendingUpdateWidgets);
pw.print(" mTransitioning="); pw.println(mTransitioning);
pw.print(" mTransitioningViews="); pw.println(mTransitioningViews);
}
- public void pendingUpdateConditions() {
- mPendingUpdateConditions = true;
- }
-
public void pendingUpdateWidgets() {
mPendingUpdateWidgets = true;
}
@@ -1050,15 +993,11 @@
@Override
public void run() {
if (DEBUG) Log.d(mTag, "TransitionHelper run"
- + " mPendingUpdateWidgets=" + mPendingUpdateWidgets
- + " mPendingUpdateConditions=" + mPendingUpdateConditions);
+ + " mPendingUpdateWidgets=" + mPendingUpdateWidgets);
if (mPendingUpdateWidgets) {
updateWidgets();
}
- if (mPendingUpdateConditions) {
- handleUpdateConditions();
- }
- mPendingUpdateWidgets = mPendingUpdateConditions = false;
+ mPendingUpdateWidgets = false;
}
private void updateTransitioning() {
@@ -1067,10 +1006,10 @@
mTransitioning = transitioning;
if (DEBUG) Log.d(mTag, "TransitionHelper mTransitioning=" + mTransitioning);
if (!mTransitioning) {
- if (mPendingUpdateConditions || mPendingUpdateWidgets) {
+ if (mPendingUpdateWidgets) {
mHandler.post(this);
} else {
- mPendingUpdateConditions = mPendingUpdateWidgets = false;
+ mPendingUpdateWidgets = false;
}
}
}
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
index d6d17cb..fdc2543 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -22,7 +22,9 @@
LOCAL_PROTO_JAVA_OUTPUT_PARAMS := optional_field_style=accessors
LOCAL_AAPT_FLAGS := --auto-add-overlay --extra-packages com.android.systemui:com.android.keyguard
+
LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+ $(call all-Iaidl-files-under, src) \
$(call all-java-files-under, ../src) \
$(call all-proto-files-under, ../src) \
src/com/android/systemui/EventLogTags.logtags
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl b/packages/SystemUI/tests/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
new file mode 120000
index 0000000..0ea3e91
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
@@ -0,0 +1 @@
+../../../../../../src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl b/packages/SystemUI/tests/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
new file mode 120000
index 0000000..b1a0963
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
@@ -0,0 +1 @@
+../../../../../../src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
\ No newline at end of file
diff --git a/packages/VpnDialogs/res/values-ro/strings.xml b/packages/VpnDialogs/res/values-ro/strings.xml
index f7a6a6a..a77ef03 100644
--- a/packages/VpnDialogs/res/values-ro/strings.xml
+++ b/packages/VpnDialogs/res/values-ro/strings.xml
@@ -19,7 +19,7 @@
<string name="prompt" msgid="3183836924226407828">"Solicitare de conexiune"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> dorește să configureze o conexiune VPN care să îi permită să monitorizeze traficul în rețea. Acceptați numai dacă aveți încredere în sursă. Atunci când conexiunea VPN este activă, <br /> <br /> <img src=vpn_icon /> se afișează în partea de sus a ecranului."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN este conectat"</string>
- <string name="configure" msgid="4905518375574791375">"Configuraţi"</string>
+ <string name="configure" msgid="4905518375574791375">"Configurați"</string>
<string name="disconnect" msgid="971412338304200056">"Deconectaţi"</string>
<string name="session" msgid="6470628549473641030">"Sesiune:"</string>
<string name="duration" msgid="3584782459928719435">"Durată:"</string>
diff --git a/packages/services/Proxy/AndroidManifest.xml b/packages/services/Proxy/AndroidManifest.xml
index bbcd6b9..88f8381 100644
--- a/packages/services/Proxy/AndroidManifest.xml
+++ b/packages/services/Proxy/AndroidManifest.xml
@@ -7,7 +7,9 @@
<application
android:label="@string/app_label"
- android:process="com.android.proxyhandler">
+ android:process="com.android.proxyhandler"
+ android:forceDeviceEncrypted="true"
+ android:encryptionAware="true">
<service android:name=".ProxyService"
android:exported="true">
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index 3759c91..be7071e 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -64,6 +64,10 @@
case RS_TYPE_FLOAT_32: \
len = _env->GetArrayLength((jfloatArray)data); \
ptr = _env->GetFloatArrayElements((jfloatArray)data, flag); \
+ if (ptr == nullptr) { \
+ ALOGE("Failed to get Java array elements."); \
+ return; \
+ } \
typeBytes = 4; \
if (usePadding) { \
srcPtr = ptr; \
@@ -89,6 +93,10 @@
case RS_TYPE_FLOAT_64: \
len = _env->GetArrayLength((jdoubleArray)data); \
ptr = _env->GetDoubleArrayElements((jdoubleArray)data, flag); \
+ if (ptr == nullptr) { \
+ ALOGE("Failed to get Java array elements."); \
+ return; \
+ } \
typeBytes = 8; \
if (usePadding) { \
srcPtr = ptr; \
@@ -115,6 +123,10 @@
case RS_TYPE_UNSIGNED_8: \
len = _env->GetArrayLength((jbyteArray)data); \
ptr = _env->GetByteArrayElements((jbyteArray)data, flag); \
+ if (ptr == nullptr) { \
+ ALOGE("Failed to get Java array elements."); \
+ return; \
+ } \
typeBytes = 1; \
if (usePadding) { \
srcPtr = ptr; \
@@ -141,6 +153,10 @@
case RS_TYPE_UNSIGNED_16: \
len = _env->GetArrayLength((jshortArray)data); \
ptr = _env->GetShortArrayElements((jshortArray)data, flag); \
+ if (ptr == nullptr) { \
+ ALOGE("Failed to get Java array elements."); \
+ return; \
+ } \
typeBytes = 2; \
if (usePadding) { \
srcPtr = ptr; \
@@ -167,6 +183,10 @@
case RS_TYPE_UNSIGNED_32: \
len = _env->GetArrayLength((jintArray)data); \
ptr = _env->GetIntArrayElements((jintArray)data, flag); \
+ if (ptr == nullptr) { \
+ ALOGE("Failed to get Java array elements."); \
+ return; \
+ } \
typeBytes = 4; \
if (usePadding) { \
srcPtr = ptr; \
@@ -193,6 +213,10 @@
case RS_TYPE_UNSIGNED_64: \
len = _env->GetArrayLength((jlongArray)data); \
ptr = _env->GetLongArrayElements((jlongArray)data, flag); \
+ if (ptr == nullptr) { \
+ ALOGE("Failed to get Java array elements."); \
+ return; \
+ } \
typeBytes = 8; \
if (usePadding) { \
srcPtr = ptr; \
@@ -332,16 +356,40 @@
jlong* jFieldIDs = _env->GetLongArrayElements(fieldIDArray, nullptr);
jsize fieldIDs_length = _env->GetArrayLength(fieldIDArray);
+ if (jFieldIDs == nullptr) {
+ ALOGE("Failed to get Java array elements: fieldIDs.");
+ return ret;
+ }
+
jlong* jValues = _env->GetLongArrayElements(valueArray, nullptr);
jsize values_length = _env->GetArrayLength(valueArray);
+ if (jValues == nullptr) {
+ ALOGE("Failed to get Java array elements: values.");
+ return ret;
+ }
+
jint* jSizes = _env->GetIntArrayElements(sizeArray, nullptr);
jsize sizes_length = _env->GetArrayLength(sizeArray);
+ if (jSizes == nullptr) {
+ ALOGE("Failed to get Java array elements: sizes.");
+ return ret;
+ }
+
jlong* jDepClosures =
_env->GetLongArrayElements(depClosureArray, nullptr);
jsize depClosures_length = _env->GetArrayLength(depClosureArray);
+ if (jDepClosures == nullptr) {
+ ALOGE("Failed to get Java array elements: depClosures.");
+ return ret;
+ }
+
jlong* jDepFieldIDs =
_env->GetLongArrayElements(depFieldIDArray, nullptr);
jsize depFieldIDs_length = _env->GetArrayLength(depFieldIDArray);
+ if (jDepFieldIDs == nullptr) {
+ ALOGE("Failed to get Java array elements: depFieldIDs.");
+ return ret;
+ }
size_t numValues, numDependencies;
RsScriptFieldID* fieldIDs;
@@ -435,12 +483,31 @@
jbyte* jParams = _env->GetByteArrayElements(paramArray, nullptr);
jsize jParamLength = _env->GetArrayLength(paramArray);
+ if (jParams == nullptr) {
+ ALOGE("Failed to get Java array elements: params.");
+ return ret;
+ }
+
jlong* jFieldIDs = _env->GetLongArrayElements(fieldIDArray, nullptr);
jsize fieldIDs_length = _env->GetArrayLength(fieldIDArray);
+ if (jFieldIDs == nullptr) {
+ ALOGE("Failed to get Java array elements: fieldIDs.");
+ return ret;
+ }
+
jlong* jValues = _env->GetLongArrayElements(valueArray, nullptr);
jsize values_length = _env->GetArrayLength(valueArray);
+ if (jValues == nullptr) {
+ ALOGE("Failed to get Java array elements: values.");
+ return ret;
+ }
+
jint* jSizes = _env->GetIntArrayElements(sizeArray, nullptr);
jsize sizes_length = _env->GetArrayLength(sizeArray);
+ if (jSizes == nullptr) {
+ ALOGE("Failed to get Java array elements: sizes.");
+ return ret;
+ }
size_t numValues;
RsScriptFieldID* fieldIDs;
@@ -515,6 +582,10 @@
jlong* jClosures = _env->GetLongArrayElements(closureArray, nullptr);
jsize numClosures = _env->GetArrayLength(closureArray);
+ if (jClosures == nullptr) {
+ ALOGE("Failed to get Java array elements: closures.");
+ return ret;
+ }
RsClosure* closures;
@@ -720,6 +791,11 @@
}
jint len = _env->GetArrayLength(str);
jbyte * cptr = (jbyte *) _env->GetPrimitiveArrayCritical(str, 0);
+ if (cptr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return;
+ }
+
rsAssignName((RsContext)con, (void *)obj, (const char *)cptr, len);
_env->ReleasePrimitiveArrayCritical(str, cptr, JNI_ABORT);
}
@@ -916,6 +992,10 @@
ALOGD("nContextGetMessage, con(%p), len(%i)", (RsContext)con, len);
}
jint *ptr = _env->GetIntArrayElements(data, nullptr);
+ if (ptr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return 0;
+ }
size_t receiveLen;
uint32_t subID;
int id = rsContextGetMessage((RsContext)con,
@@ -936,6 +1016,10 @@
ALOGD("nContextPeekMessage, con(%p)", (RsContext)con);
}
jint *auxDataPtr = _env->GetIntArrayElements(auxData, nullptr);
+ if (auxDataPtr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return 0;
+ }
size_t receiveLen;
uint32_t subID;
int id = rsContextPeekMessage((RsContext)con, &receiveLen, sizeof(receiveLen),
@@ -970,6 +1054,10 @@
if (data) {
len = _env->GetArrayLength(data);
ptr = _env->GetIntArrayElements(data, nullptr);
+ if (ptr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return;
+ }
}
if (kLogApi) {
ALOGD("nContextSendMessage, con(%p), id(%i), len(%i)", (RsContext)con, id, len);
@@ -1004,7 +1092,15 @@
}
jlong *jIds = _env->GetLongArrayElements(_ids, nullptr);
+ if (jIds == nullptr) {
+ ALOGE("Failed to get Java array elements: ids");
+ return 0;
+ }
jint *jArraySizes = _env->GetIntArrayElements(_arraySizes, nullptr);
+ if (jArraySizes == nullptr) {
+ ALOGE("Failed to get Java array elements: arraySizes");
+ return 0;
+ }
RsElement *ids = (RsElement*)malloc(fieldCount * sizeof(RsElement));
uint32_t *arraySizes = (uint32_t *)malloc(fieldCount * sizeof(uint32_t));
@@ -1311,6 +1407,10 @@
sizeBytes);
}
jbyte *ptr = _env->GetByteArrayElements(data, nullptr);
+ if (ptr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return;
+ }
rsAllocationElementData((RsContext)con, (RsAllocation)alloc,
xoff, yoff, zoff,
lod, ptr, sizeBytes, compIdx);
@@ -1449,6 +1549,10 @@
sizeBytes);
}
jbyte *ptr = _env->GetByteArrayElements(data, nullptr);
+ if (ptr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return;
+ }
rsAllocationElementRead((RsContext)con, (RsAllocation)alloc,
xoff, yoff, zoff,
lod, ptr, sizeBytes, compIdx);
@@ -1775,6 +1879,10 @@
}
jint len = _env->GetArrayLength(data);
jbyte *ptr = _env->GetByteArrayElements(data, nullptr);
+ if (ptr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return;
+ }
rsScriptSetVarV((RsContext)con, (RsScript)script, slot, ptr, len);
_env->ReleaseByteArrayElements(data, ptr, JNI_ABORT);
}
@@ -1787,6 +1895,10 @@
}
jint len = _env->GetArrayLength(data);
jbyte *ptr = _env->GetByteArrayElements(data, nullptr);
+ if (ptr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return;
+ }
rsScriptGetVarV((RsContext)con, (RsScript)script, slot, ptr, len);
_env->ReleaseByteArrayElements(data, ptr, 0);
}
@@ -1800,8 +1912,16 @@
}
jint len = _env->GetArrayLength(data);
jbyte *ptr = _env->GetByteArrayElements(data, nullptr);
+ if (ptr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return;
+ }
jint dimsLen = _env->GetArrayLength(dims) * sizeof(int);
jint *dimsPtr = _env->GetIntArrayElements(dims, nullptr);
+ if (dimsPtr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return;
+ }
rsScriptSetVarVE((RsContext)con, (RsScript)script, slot, ptr, len, (RsElement)elem,
(const uint32_t*) dimsPtr, dimsLen);
_env->ReleaseByteArrayElements(data, ptr, JNI_ABORT);
@@ -1819,6 +1939,10 @@
jint length = _env->GetArrayLength(timeZone);
jbyte* timeZone_ptr;
timeZone_ptr = (jbyte *) _env->GetPrimitiveArrayCritical(timeZone, (jboolean *)0);
+ if (timeZone_ptr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return;
+ }
rsScriptSetTimeZone((RsContext)con, (RsScript)script, (const char *)timeZone_ptr, length);
@@ -1844,6 +1968,10 @@
}
jint len = _env->GetArrayLength(data);
jbyte *ptr = _env->GetByteArrayElements(data, nullptr);
+ if (ptr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return;
+ }
rsScriptInvokeV((RsContext)con, (RsScript)script, slot, ptr, len);
_env->ReleaseByteArrayElements(data, ptr, JNI_ABORT);
}
@@ -1870,8 +1998,12 @@
return;
}
- // TODO (b/20760800): Check in_ptr is not null
in_ptr = _env->GetLongArrayElements(ains, nullptr);
+ if (in_ptr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return;
+ }
+
if (sizeof(RsAllocation) == sizeof(jlong)) {
in_allocs = (RsAllocation*)in_ptr;
@@ -1897,6 +2029,10 @@
if (params != nullptr) {
param_len = _env->GetArrayLength(params);
param_ptr = _env->GetByteArrayElements(params, nullptr);
+ if (param_ptr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return;
+ }
}
RsScriptCall sc, *sca = nullptr;
@@ -1908,6 +2044,10 @@
if (limits != nullptr) {
limit_len = _env->GetArrayLength(limits);
limit_ptr = _env->GetIntArrayElements(limits, nullptr);
+ if (limit_ptr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return;
+ }
assert(limit_len == 6);
UNUSED(limit_len); // As the assert might not be compiled.
@@ -1966,6 +2106,10 @@
if (limits != nullptr) {
limit_len = _env->GetArrayLength(limits);
limit_ptr = _env->GetIntArrayElements(limits, nullptr);
+ if (limit_ptr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return;
+ }
// We expect to be passed an array [x1, x2] which specifies
// the sub-range for a 1-dimensional reduction.
@@ -2037,6 +2181,10 @@
}
script_ptr = (jbyte *)
_env->GetPrimitiveArrayCritical(scriptRef, (jboolean *)0);
+ if (script_ptr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return ret;
+ }
//rsScriptCSetText((RsContext)con, (const char *)script_ptr, length);
@@ -2104,6 +2252,10 @@
jint kernelsLen = _env->GetArrayLength(_kernels);
jlong *jKernelsPtr = _env->GetLongArrayElements(_kernels, nullptr);
+ if (jKernelsPtr == nullptr) {
+ ALOGE("Failed to get Java array elements: kernels");
+ return 0;
+ }
RsScriptKernelID* kernelsPtr = (RsScriptKernelID*) malloc(sizeof(RsScriptKernelID) * kernelsLen);
for(int i = 0; i < kernelsLen; ++i) {
kernelsPtr[i] = (RsScriptKernelID)jKernelsPtr[i];
@@ -2111,6 +2263,10 @@
jint srcLen = _env->GetArrayLength(_src);
jlong *jSrcPtr = _env->GetLongArrayElements(_src, nullptr);
+ if (jSrcPtr == nullptr) {
+ ALOGE("Failed to get Java array elements: src");
+ return 0;
+ }
RsScriptKernelID* srcPtr = (RsScriptKernelID*) malloc(sizeof(RsScriptKernelID) * srcLen);
for(int i = 0; i < srcLen; ++i) {
srcPtr[i] = (RsScriptKernelID)jSrcPtr[i];
@@ -2118,6 +2274,10 @@
jint dstkLen = _env->GetArrayLength(_dstk);
jlong *jDstkPtr = _env->GetLongArrayElements(_dstk, nullptr);
+ if (jDstkPtr == nullptr) {
+ ALOGE("Failed to get Java array elements: dstk");
+ return 0;
+ }
RsScriptKernelID* dstkPtr = (RsScriptKernelID*) malloc(sizeof(RsScriptKernelID) * dstkLen);
for(int i = 0; i < dstkLen; ++i) {
dstkPtr[i] = (RsScriptKernelID)jDstkPtr[i];
@@ -2125,6 +2285,10 @@
jint dstfLen = _env->GetArrayLength(_dstf);
jlong *jDstfPtr = _env->GetLongArrayElements(_dstf, nullptr);
+ if (jDstfPtr == nullptr) {
+ ALOGE("Failed to get Java array elements: dstf");
+ return 0;
+ }
RsScriptKernelID* dstfPtr = (RsScriptKernelID*) malloc(sizeof(RsScriptKernelID) * dstfLen);
for(int i = 0; i < dstfLen; ++i) {
dstfPtr[i] = (RsScriptKernelID)jDstfPtr[i];
@@ -2132,6 +2296,10 @@
jint typesLen = _env->GetArrayLength(_types);
jlong *jTypesPtr = _env->GetLongArrayElements(_types, nullptr);
+ if (jTypesPtr == nullptr) {
+ ALOGE("Failed to get Java array elements: types");
+ return 0;
+ }
RsType* typesPtr = (RsType*) malloc(sizeof(RsType) * typesLen);
for(int i = 0; i < typesLen; ++i) {
typesPtr[i] = (RsType)jTypesPtr[i];
@@ -2244,6 +2412,10 @@
AutoJavaStringToUTF8 shaderUTF(_env, shader);
jlong *jParamPtr = _env->GetLongArrayElements(params, nullptr);
jint paramLen = _env->GetArrayLength(params);
+ if (jParamPtr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return 0;
+ }
int texCount = _env->GetArrayLength(texNames);
AutoJavaStringArrayToUTF8 names(_env, texNames, texCount);
@@ -2277,6 +2449,10 @@
AutoJavaStringToUTF8 shaderUTF(_env, shader);
jlong *jParamPtr = _env->GetLongArrayElements(params, nullptr);
jint paramLen = _env->GetArrayLength(params);
+ if (jParamPtr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return 0;
+ }
if (kLogApi) {
ALOGD("nProgramVertexCreate, con(%p), paramLen(%i)", (RsContext)con, paramLen);
@@ -2392,6 +2568,10 @@
jint vtxLen = _env->GetArrayLength(_vtx);
jlong *jVtxPtr = _env->GetLongArrayElements(_vtx, nullptr);
+ if (jVtxPtr == nullptr) {
+ ALOGE("Failed to get Java array elements: vtx");
+ return 0;
+ }
RsAllocation* vtxPtr = (RsAllocation*) malloc(sizeof(RsAllocation) * vtxLen);
for(int i = 0; i < vtxLen; ++i) {
vtxPtr[i] = (RsAllocation)(uintptr_t)jVtxPtr[i];
@@ -2399,6 +2579,10 @@
jint idxLen = _env->GetArrayLength(_idx);
jlong *jIdxPtr = _env->GetLongArrayElements(_idx, nullptr);
+ if (jIdxPtr == nullptr) {
+ ALOGE("Failed to get Java array elements: idx");
+ return 0;
+ }
RsAllocation* idxPtr = (RsAllocation*) malloc(sizeof(RsAllocation) * idxLen);
for(int i = 0; i < idxLen; ++i) {
idxPtr[i] = (RsAllocation)(uintptr_t)jIdxPtr[i];
@@ -2406,6 +2590,10 @@
jint primLen = _env->GetArrayLength(_prim);
jint *primPtr = _env->GetIntArrayElements(_prim, nullptr);
+ if (primPtr == nullptr) {
+ ALOGE("Failed to get Java array elements: prim");
+ return 0;
+ }
jlong id = (jlong)(uintptr_t)rsMeshCreate((RsContext)con,
(RsAllocation *)vtxPtr, vtxLen,
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
index 7dff1a8..28121b4 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
@@ -17,53 +17,146 @@
package com.android.server.accessibility;
import android.content.Context;
+import android.gesture.Gesture;
+import android.gesture.GestureLibraries;
+import android.gesture.GestureLibrary;
+import android.gesture.GesturePoint;
+import android.gesture.GestureStore;
+import android.gesture.GestureStroke;
+import android.gesture.Prediction;
+import android.util.Slog;
import android.view.GestureDetector;
import android.view.MotionEvent;
+import com.android.internal.R;
+
+import java.util.ArrayList;
+
/**
* This class handles gesture detection for the Touch Explorer. It collects
* touch events, and sends events to mListener as gestures are recognized.
*/
class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListener {
- private final GestureDetector mGestureDetector;
+
+ private static final boolean DEBUG = false;
+
+ // Tag for logging received events.
+ private static final String LOG_TAG = "AccessibilityGestureDetector";
+
+ public interface Listener {
+ public void onDoubleTapAndHold(MotionEvent event, int policyFlags);
+ public boolean onDoubleTap(MotionEvent event, int policyFlags);
+ public boolean onGesture(int gestureId);
+ }
+
private final Listener mListener;
+ private final GestureDetector mGestureDetector;
+
+ // The library for gesture detection.
+ private final GestureLibrary mGestureLibrary;
+
+ // Indicates that a single tap has occurred.
private boolean mFirstTapDetected;
+
+ // Indicates that the down event of a double tap has occured.
private boolean mDoubleTapDetected;
+
+ // Indicates that motion events are being collected to match a gesture.
+ private boolean mRecognizingGesture;
+
+ // Policy flags of the previous event.
private int mPolicyFlags;
+ // The X of the previous event.
+ private float mPreviousX;
+
+ // The Y of the previous event.
+ private float mPreviousY;
+
+ // Buffer for storing points for gesture detection.
+ private final ArrayList<GesturePoint> mStrokeBuffer = new ArrayList<GesturePoint>(100);
+
+ // The minimal delta between moves to add a gesture point.
+ private static final int TOUCH_TOLERANCE = 3;
+
+ // The minimal score for accepting a predicted gesture.
+ private static final float MIN_PREDICTION_SCORE = 2.0f;
+
AccessibilityGestureDetector(Context context, Listener listener) {
mListener = listener;
mGestureDetector = new GestureDetector(context, this);
mGestureDetector.setOnDoubleTapListener(this);
+
+ mGestureLibrary = GestureLibraries.fromRawResource(context, R.raw.accessibility_gestures);
+ mGestureLibrary.setOrientationStyle(8 /* GestureStore.ORIENTATION_SENSITIVE_8 */);
+ mGestureLibrary.setSequenceType(GestureStore.SEQUENCE_SENSITIVE);
+ mGestureLibrary.load();
}
- public void onMotionEvent(MotionEvent event, int policyFlags) {
+ public boolean onMotionEvent(MotionEvent event, int policyFlags) {
+ final float x = event.getX();
+ final float y = event.getY();
+
mPolicyFlags = policyFlags;
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mDoubleTapDetected = false;
+ mRecognizingGesture = true;
+ mPreviousX = x;
+ mPreviousY = y;
+ mStrokeBuffer.clear();
+ mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ if (mRecognizingGesture) {
+ final float dX = Math.abs(x - mPreviousX);
+ final float dY = Math.abs(y - mPreviousY);
+ if (dX >= TOUCH_TOLERANCE || dY >= TOUCH_TOLERANCE) {
+ mPreviousX = x;
+ mPreviousY = y;
+ mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));
+ }
+ }
break;
case MotionEvent.ACTION_UP:
- maybeFinishDoubleTap(event, policyFlags);
+ if (maybeFinishDoubleTap(event, policyFlags)) {
+ return true;
+ }
+ if (mRecognizingGesture) {
+ mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));
+
+ if (recognizeGesture()) {
+ return true;
+ }
+ }
break;
}
- mGestureDetector.onTouchEvent(event);
+
+ if (!mRecognizingGesture) {
+ return false;
+ }
+
+ // Pass the event on to the standard gesture detector.
+ return mGestureDetector.onTouchEvent(event);
}
public void clear() {
mFirstTapDetected = false;
mDoubleTapDetected = false;
+ cancelGesture();
+ mStrokeBuffer.clear();
}
public boolean firstTapDetected() {
return mFirstTapDetected;
}
- @Override
- public boolean onDown(MotionEvent event) {
- return true;
+ public void cancelGesture() {
+ mRecognizingGesture = false;
+ mStrokeBuffer.clear();
}
@Override
@@ -101,18 +194,39 @@
mListener.onDoubleTapAndHold(event, policyFlags);
}
- private void maybeFinishDoubleTap(MotionEvent event, int policyFlags) {
+ private boolean maybeFinishDoubleTap(MotionEvent event, int policyFlags) {
if (!mDoubleTapDetected) {
- return;
+ return false;
}
clear();
- mListener.onDoubleTap(event, policyFlags);
+ return mListener.onDoubleTap(event, policyFlags);
}
- public interface Listener {
- public void onDoubleTapAndHold(MotionEvent event, int policyFlags);
- public void onDoubleTap(MotionEvent event, int policyFlags);
+ private boolean recognizeGesture() {
+ Gesture gesture = new Gesture();
+ gesture.addStroke(new GestureStroke(mStrokeBuffer));
+
+ ArrayList<Prediction> predictions = mGestureLibrary.recognize(gesture);
+ if (!predictions.isEmpty()) {
+ Prediction bestPrediction = predictions.get(0);
+ if (bestPrediction.score >= MIN_PREDICTION_SCORE) {
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "gesture: " + bestPrediction.name + " score: "
+ + bestPrediction.score);
+ }
+ try {
+ final int gestureId = Integer.parseInt(bestPrediction.name);
+ if (mListener.onGesture(gestureId)) {
+ return true;
+ }
+ } catch (NumberFormatException nfe) {
+ Slog.w(LOG_TAG, "Non numeric gesture id:" + bestPrediction.name);
+ }
+ }
+ }
+
+ return false;
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 749a080..535a8ef 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -91,7 +91,6 @@
import com.android.internal.R;
import com.android.internal.content.PackageMonitor;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.widget.LockPatternUtils;
import com.android.server.LocalServices;
import org.xmlpull.v1.XmlPullParserException;
@@ -180,6 +179,8 @@
private final MainHandler mMainHandler;
+ private MagnificationController mMagnificationController;
+
private InteractionBridge mInteractionBridge;
private AlertDialog mEnableTouchExplorationDialog;
@@ -1936,6 +1937,13 @@
}
}
+ MagnificationController getMagnificationController() {
+ if (mMagnificationController == null) {
+ mMagnificationController = new MagnificationController(mContext, this);
+ }
+ return mMagnificationController;
+ }
+
/**
* This class represents an accessibility service. It stores all per service
* data required for the service management, provides API for starting/stopping the
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
new file mode 100644
index 0000000..b4411cf
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
@@ -0,0 +1,367 @@
+/*
+ * 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.accessibility;
+
+import com.android.internal.R;
+import com.android.server.LocalServices;
+
+import android.animation.ObjectAnimator;
+import android.animation.TypeEvaluator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.util.Property;
+import android.util.Slog;
+import android.view.MagnificationSpec;
+import android.view.WindowManagerInternal;
+import android.view.animation.DecelerateInterpolator;
+
+/**
+ * This class is used to control and query the state of display magnification
+ * from the accessibility manager and related classes. It is responsible for
+ * holding the current state of magnification and animation, and it handles
+ * communication between the accessibility manager and window manager.
+ */
+class MagnificationController {
+ private static final String LOG_TAG = ScreenMagnifier.class.getSimpleName();
+
+ private static final boolean DEBUG_SET_MAGNIFICATION_SPEC = false;
+ private static final boolean DEBUG_MAGNIFICATION_CONTROLLER = false;
+
+ private static final String PROPERTY_NAME_MAGNIFICATION_SPEC = "magnificationSpec";
+
+ private final MagnificationSpec mSentMagnificationSpec = MagnificationSpec.obtain();
+ private final MagnificationSpec mCurrentMagnificationSpec = MagnificationSpec.obtain();
+
+ private final Region mMagnifiedBounds = new Region();
+ private final Rect mTempRect = new Rect();
+
+ private final AccessibilityManagerService mAms;
+ private final WindowManagerInternal mWindowManager;
+ private final ValueAnimator mTransformationAnimator;
+
+ public MagnificationController(Context context, AccessibilityManagerService ams) {
+ mAms = ams;
+ mWindowManager = LocalServices.getService(WindowManagerInternal.class);
+
+ final Property<MagnificationController, MagnificationSpec> property =
+ Property.of(MagnificationController.class, MagnificationSpec.class,
+ PROPERTY_NAME_MAGNIFICATION_SPEC);
+ final MagnificationSpecEvaluator evaluator = new MagnificationSpecEvaluator();
+ final long animationDuration = context.getResources().getInteger(
+ R.integer.config_longAnimTime);
+ mTransformationAnimator = ObjectAnimator.ofObject(this, property, evaluator,
+ mSentMagnificationSpec, mCurrentMagnificationSpec);
+ mTransformationAnimator.setDuration(animationDuration);
+ mTransformationAnimator.setInterpolator(new DecelerateInterpolator(2.5f));
+ }
+
+ /**
+ * @return {@code true} if magnification is active, e.g. the scale
+ * is > 1, {@code false} otherwise
+ */
+ public boolean isMagnifying() {
+ return mCurrentMagnificationSpec.scale > 1.0f;
+ }
+
+ /**
+ * Sets the magnified region.
+ *
+ * @param region the region to set
+ * @param updateSpec {@code true} to update the scale and center based on
+ * the region bounds, {@code false} to leave them as-is
+ */
+ public void setMagnifiedRegion(Region region, boolean updateSpec) {
+ mMagnifiedBounds.set(region);
+
+ if (updateSpec) {
+ final Rect magnifiedFrame = mTempRect;
+ region.getBounds(magnifiedFrame);
+ final float scale = mSentMagnificationSpec.scale;
+ final float offsetX = mSentMagnificationSpec.offsetX;
+ final float offsetY = mSentMagnificationSpec.offsetY;
+ final float centerX = (-offsetX + magnifiedFrame.width() / 2) / scale;
+ final float centerY = (-offsetY + magnifiedFrame.height() / 2) / scale;
+ setScaleAndMagnifiedRegionCenter(scale, centerX, centerY, false);
+ } else {
+ mAms.onMagnificationStateChanged();
+ }
+ }
+
+ /**
+ * Returns whether the magnified region contains the specified
+ * screen-relative coordinates.
+ *
+ * @param x the screen-relative X coordinate to check
+ * @param y the screen-relative Y coordinate to check
+ * @return {@code true} if the coordinate is contained within the
+ * magnified region, or {@code false} otherwise
+ */
+ public boolean magnifiedRegionContains(float x, float y) {
+ return mMagnifiedBounds.contains((int) x, (int) y);
+ }
+
+ /**
+ * Populates the specified rect with the bounds of the magnified
+ * region.
+ *
+ * @param outBounds rect to populate with the bounds of the magnified
+ * region
+ */
+ public void getMagnifiedBounds(Rect outBounds) {
+ mMagnifiedBounds.getBounds(outBounds);
+ }
+
+ /**
+ * Returns the magnification scale. If an animation is in progress,
+ * this reflects the end state of the animation.
+ *
+ * @return the scale
+ */
+ public float getScale() {
+ return mCurrentMagnificationSpec.scale;
+ }
+
+ /**
+ * Returns the X offset of the magnification viewport. If an animation
+ * is in progress, this reflects the end state of the animation.
+ *
+ * @return the X offset
+ */
+ public float getOffsetX() {
+ return mCurrentMagnificationSpec.offsetX;
+ }
+
+ /**
+ * Returns the Y offset of the magnification viewport. If an animation
+ * is in progress, this reflects the end state of the animation.
+ *
+ * @return the Y offset
+ */
+ public float getOffsetY() {
+ return mCurrentMagnificationSpec.offsetY;
+ }
+
+ /**
+ * Returns the scale currently used by the window manager. If an
+ * animation is in progress, this reflects the current state of the
+ * animation.
+ *
+ * @return the scale currently used by the window manager
+ */
+ public float getSentScale() {
+ return mSentMagnificationSpec.scale;
+ }
+
+ /**
+ * Returns the X offset currently used by the window manager. If an
+ * animation is in progress, this reflects the current state of the
+ * animation.
+ *
+ * @return the X offset currently used by the window manager
+ */
+ public float getSentOffsetX() {
+ return mSentMagnificationSpec.offsetX;
+ }
+
+ /**
+ * Returns the Y offset currently used by the window manager. If an
+ * animation is in progress, this reflects the current state of the
+ * animation.
+ *
+ * @return the Y offset currently used by the window manager
+ */
+ public float getSentOffsetY() {
+ return mSentMagnificationSpec.offsetY;
+ }
+
+ /**
+ * Resets the magnification scale and center, optionally animating the
+ * transition.
+ *
+ * @param animate {@code true} to animate the transition, {@code false}
+ * to transition immediately
+ */
+ public void reset(boolean animate) {
+ if (mTransformationAnimator.isRunning()) {
+ mTransformationAnimator.cancel();
+ }
+ mCurrentMagnificationSpec.clear();
+ if (animate) {
+ animateMagnificationSpec(mSentMagnificationSpec,
+ mCurrentMagnificationSpec);
+ } else {
+ setMagnificationSpec(mCurrentMagnificationSpec);
+ }
+ final Rect bounds = mTempRect;
+ bounds.setEmpty();
+ mAms.onMagnificationStateChanged();
+ }
+
+ /**
+ * Scales the magnified region around the specified pivot point,
+ * optionally animating the transition. If animation is disabled, the
+ * transition is immediate.
+ *
+ * @param scale the target scale, must be >= 1
+ * @param animate {@code true} to animate the transition, {@code false}
+ * to transition immediately
+ */
+ public void setScale(float scale, float pivotX, float pivotY, boolean animate) {
+ final Rect magnifiedFrame = mTempRect;
+ mMagnifiedBounds.getBounds(magnifiedFrame);
+ final MagnificationSpec spec = mCurrentMagnificationSpec;
+ final float oldScale = spec.scale;
+ final float oldCenterX = (-spec.offsetX + magnifiedFrame.width() / 2) / oldScale;
+ final float oldCenterY = (-spec.offsetY + magnifiedFrame.height() / 2) / oldScale;
+ final float normPivotX = (-spec.offsetX + pivotX) / oldScale;
+ final float normPivotY = (-spec.offsetY + pivotY) / oldScale;
+ final float offsetX = (oldCenterX - normPivotX) * (oldScale / scale);
+ final float offsetY = (oldCenterY - normPivotY) * (oldScale / scale);
+ final float centerX = normPivotX + offsetX;
+ final float centerY = normPivotY + offsetY;
+ setScaleAndMagnifiedRegionCenter(scale, centerX, centerY, animate);
+ }
+
+ /**
+ * Sets the center of the magnified region, optionally animating the
+ * transition. If animation is disabled, the transition is immediate.
+ *
+ * @param centerX the screen-relative X coordinate around which to
+ * center
+ * @param centerY the screen-relative Y coordinate around which to
+ * center
+ * @param animate {@code true} to animate the transition, {@code false}
+ * to transition immediately
+ */
+ public void setMagnifiedRegionCenter(float centerX, float centerY, boolean animate) {
+ setScaleAndMagnifiedRegionCenter(mCurrentMagnificationSpec.scale, centerX, centerY,
+ animate);
+ }
+
+ /**
+ * Sets the scale and center of the magnified region, optionally
+ * animating the transition. If animation is disabled, the transition
+ * is immediate.
+ *
+ * @param scale the target scale, must be >= 1
+ * @param centerX the screen-relative X coordinate around which to
+ * center and scale
+ * @param centerY the screen-relative Y coordinate around which to
+ * center and scale
+ * @param animate {@code true} to animate the transition, {@code false}
+ * to transition immediately
+ */
+ public void setScaleAndMagnifiedRegionCenter(float scale, float centerX, float centerY,
+ boolean animate) {
+ if (Float.compare(mCurrentMagnificationSpec.scale, scale) == 0
+ && Float.compare(mCurrentMagnificationSpec.offsetX, centerX) == 0
+ && Float.compare(mCurrentMagnificationSpec.offsetY, centerY) == 0) {
+ return;
+ }
+ if (mTransformationAnimator.isRunning()) {
+ mTransformationAnimator.cancel();
+ }
+ if (DEBUG_MAGNIFICATION_CONTROLLER) {
+ Slog.i(LOG_TAG, "scale: " + scale + " offsetX: " + centerX + " offsetY: " + centerY);
+ }
+ updateMagnificationSpec(scale, centerX, centerY);
+ if (animate) {
+ animateMagnificationSpec(mSentMagnificationSpec,
+ mCurrentMagnificationSpec);
+ } else {
+ setMagnificationSpec(mCurrentMagnificationSpec);
+ }
+ mAms.onMagnificationStateChanged();
+ }
+
+ /**
+ * Offsets the center of the magnified region.
+ *
+ * @param offsetX the amount in pixels to offset the X center
+ * @param offsetY the amount in pixels to offset the Y center
+ */
+ public void offsetMagnifiedRegionCenter(float offsetX, float offsetY) {
+ final float nonNormOffsetX = mCurrentMagnificationSpec.offsetX - offsetX;
+ mCurrentMagnificationSpec.offsetX = Math.min(Math.max(nonNormOffsetX,
+ getMinOffsetX()), 0);
+ final float nonNormOffsetY = mCurrentMagnificationSpec.offsetY - offsetY;
+ mCurrentMagnificationSpec.offsetY = Math.min(Math.max(nonNormOffsetY,
+ getMinOffsetY()), 0);
+ setMagnificationSpec(mCurrentMagnificationSpec);
+ }
+
+ private void updateMagnificationSpec(float scale, float magnifiedCenterX,
+ float magnifiedCenterY) {
+ final Rect magnifiedFrame = mTempRect;
+ mMagnifiedBounds.getBounds(magnifiedFrame);
+ mCurrentMagnificationSpec.scale = scale;
+ final int viewportWidth = magnifiedFrame.width();
+ final float nonNormOffsetX = viewportWidth / 2 - magnifiedCenterX * scale;
+ mCurrentMagnificationSpec.offsetX = Math.min(Math.max(nonNormOffsetX,
+ getMinOffsetX()), 0);
+ final int viewportHeight = magnifiedFrame.height();
+ final float nonNormOffsetY = viewportHeight / 2 - magnifiedCenterY * scale;
+ mCurrentMagnificationSpec.offsetY = Math.min(Math.max(nonNormOffsetY,
+ getMinOffsetY()), 0);
+ }
+
+ private float getMinOffsetX() {
+ final Rect magnifiedFrame = mTempRect;
+ mMagnifiedBounds.getBounds(magnifiedFrame);
+ final float viewportWidth = magnifiedFrame.width();
+ return viewportWidth - viewportWidth * mCurrentMagnificationSpec.scale;
+ }
+
+ private float getMinOffsetY() {
+ final Rect magnifiedFrame = mTempRect;
+ mMagnifiedBounds.getBounds(magnifiedFrame);
+ final float viewportHeight = magnifiedFrame.height();
+ return viewportHeight - viewportHeight * mCurrentMagnificationSpec.scale;
+ }
+
+ private void animateMagnificationSpec(MagnificationSpec fromSpec,
+ MagnificationSpec toSpec) {
+ mTransformationAnimator.setObjectValues(fromSpec, toSpec);
+ mTransformationAnimator.start();
+ }
+
+ private void setMagnificationSpec(MagnificationSpec spec) {
+ if (DEBUG_SET_MAGNIFICATION_SPEC) {
+ Slog.i(LOG_TAG, "Sending: " + spec);
+ }
+ mSentMagnificationSpec.scale = spec.scale;
+ mSentMagnificationSpec.offsetX = spec.offsetX;
+ mSentMagnificationSpec.offsetY = spec.offsetY;
+ mWindowManager.setMagnificationSpec(MagnificationSpec.obtain(spec));
+ }
+
+ private static class MagnificationSpecEvaluator implements TypeEvaluator<MagnificationSpec> {
+ private final MagnificationSpec mTempTransformationSpec = MagnificationSpec.obtain();
+
+ @Override
+ public MagnificationSpec evaluate(float fraction, MagnificationSpec fromSpec,
+ MagnificationSpec toSpec) {
+ final MagnificationSpec result = mTempTransformationSpec;
+ result.scale = fromSpec.scale + (toSpec.scale - fromSpec.scale) * fraction;
+ result.offsetX = fromSpec.offsetX + (toSpec.offsetX - fromSpec.offsetX) * fraction;
+ result.offsetY = fromSpec.offsetY + (toSpec.offsetY - fromSpec.offsetY) * fraction;
+ return result;
+ }
+ }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java b/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java
index 8845bc0..8feb167 100644
--- a/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java
+++ b/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java
@@ -16,9 +16,6 @@
package com.android.server.accessibility;
-import android.animation.ObjectAnimator;
-import android.animation.TypeEvaluator;
-import android.animation.ValueAnimator;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -31,14 +28,12 @@
import android.os.Message;
import android.provider.Settings;
import android.text.TextUtils;
-import android.util.Property;
import android.util.Slog;
import android.util.TypedValue;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.InputDevice;
import android.view.KeyEvent;
-import android.view.MagnificationSpec;
import android.view.MotionEvent;
import android.view.MotionEvent.PointerCoords;
import android.view.MotionEvent.PointerProperties;
@@ -48,7 +43,6 @@
import android.view.ViewConfiguration;
import android.view.WindowManagerInternal;
import android.view.accessibility.AccessibilityEvent;
-import android.view.animation.DecelerateInterpolator;
import com.android.internal.os.SomeArgs;
import com.android.server.LocalServices;
@@ -101,10 +95,8 @@
private static final boolean DEBUG_STATE_TRANSITIONS = false;
private static final boolean DEBUG_DETECTING = false;
- private static final boolean DEBUG_SET_MAGNIFICATION_SPEC = false;
private static final boolean DEBUG_PANNING = false;
private static final boolean DEBUG_SCALING = false;
- private static final boolean DEBUG_MAGNIFICATION_CONTROLLER = false;
private static final int STATE_DELEGATING = 1;
private static final int STATE_DETECTING = 2;
@@ -134,8 +126,6 @@
private final MagnifiedContentInteractonStateHandler mMagnifiedContentInteractonStateHandler;
private final StateViewportDraggingHandler mStateViewportDraggingHandler;
- private final AccessibilityManagerService mAms;
-
private final int mUserId;
private final int mTapTimeSlop = ViewConfiguration.getJumpTapTimeout();
@@ -143,10 +133,6 @@
private final int mTapDistanceSlop;
private final int mMultiTapDistanceSlop;
- private final long mLongAnimationDuration;
-
- private final Region mMagnifiedBounds = new Region();
-
private EventStreamTransformation mNext;
private int mCurrentState;
@@ -194,13 +180,10 @@
mContext = context;
mUserId = userId;
mWindowManager = LocalServices.getService(WindowManagerInternal.class);
- mAms = service;
mMultiTapTimeSlop = ViewConfiguration.getDoubleTapTimeout()
+ mContext.getResources().getInteger(
com.android.internal.R.integer.config_screen_magnification_multi_tap_adjustment);
- mLongAnimationDuration = context.getResources().getInteger(
- com.android.internal.R.integer.config_longAnimTime);
mTapDistanceSlop = ViewConfiguration.get(context).getScaledTouchSlop();
mMultiTapDistanceSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop();
@@ -209,7 +192,7 @@
mMagnifiedContentInteractonStateHandler = new MagnifiedContentInteractonStateHandler(
context);
- mMagnificationController = new MagnificationController(mLongAnimationDuration);
+ mMagnificationController = service.getMagnificationController();
mScreenStateObserver = new ScreenStateObserver(context, mMagnificationController);
mWindowManager.setMagnificationCallbacks(this);
@@ -230,19 +213,9 @@
// If there was a rotation we have to update the center of the magnified
// region since the old offset X/Y may be out of its acceptable range for
// the new display width and height.
- if (mUpdateMagnificationSpecOnNextBoundsChange) {
- mUpdateMagnificationSpecOnNextBoundsChange = false;
- MagnificationSpec spec = mMagnificationController.getMagnificationSpec();
- Rect magnifiedFrame = mTempRect;
- mMagnifiedBounds.getBounds(magnifiedFrame);
- final float scale = spec.scale;
- final float centerX = (-spec.offsetX + magnifiedFrame.width() / 2) / scale;
- final float centerY = (-spec.offsetY + magnifiedFrame.height() / 2) / scale;
- mMagnificationController.setScaleAndMagnifiedRegionCenter(scale, centerX,
- centerY, false);
- }
- mMagnifiedBounds.set(bounds);
- mAms.onMagnificationStateChanged();
+ mMagnificationController.setMagnifiedRegion(
+ bounds, mUpdateMagnificationSpecOnNextBoundsChange);
+ mUpdateMagnificationSpecOnNextBoundsChange = false;
}
@Override
@@ -257,7 +230,7 @@
private void handleOnRectangleOnScreenRequested(int left, int top, int right, int bottom) {
Rect magnifiedFrame = mTempRect;
- mMagnifiedBounds.getBounds(magnifiedFrame);
+ mMagnificationController.getMagnifiedBounds(magnifiedFrame);
if (!magnifiedFrame.intersects(left, top, right, bottom)) {
return;
}
@@ -314,10 +287,12 @@
}
private void getMagnifiedFrameInContentCoords(Rect rect) {
- MagnificationSpec spec = mMagnificationController.getMagnificationSpec();
- mMagnifiedBounds.getBounds(rect);
- rect.offset((int) -spec.offsetX, (int) -spec.offsetY);
- rect.scale(1.0f / spec.scale);
+ final float scale = mMagnificationController.getSentScale();
+ final float offsetX = mMagnificationController.getSentOffsetX();
+ final float offsetY = mMagnificationController.getSentOffsetY();
+ mMagnificationController.getMagnifiedBounds(rect);
+ rect.offset((int) -offsetX, (int) -offsetY);
+ rect.scale(1.0f / scale);
}
private void resetMagnificationIfNeeded() {
@@ -421,7 +396,7 @@
final float eventX = event.getX();
final float eventY = event.getY();
if (mMagnificationController.isMagnifying()
- && mMagnifiedBounds.contains((int) eventX, (int) eventY)) {
+ && mMagnificationController.magnifiedRegionContains(eventX, eventY)) {
final float scale = mMagnificationController.getScale();
final float scaledOffsetX = mMagnificationController.getOffsetX();
final float scaledOffsetY = mMagnificationController.getOffsetY();
@@ -623,7 +598,7 @@
}
final float eventX = event.getX();
final float eventY = event.getY();
- if (mMagnifiedBounds.contains((int) eventX, (int) eventY)) {
+ if (mMagnificationController.magnifiedRegionContains(eventX, eventY)) {
if (mLastMoveOutsideMagnifiedRegion) {
mLastMoveOutsideMagnifiedRegion = false;
mMagnificationController.setMagnifiedRegionCenter(eventX,
@@ -696,8 +671,8 @@
switch (action) {
case MotionEvent.ACTION_DOWN: {
mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
- if (!mMagnifiedBounds.contains((int) event.getX(),
- (int) event.getY())) {
+ if (!mMagnificationController.magnifiedRegionContains(
+ event.getX(), event.getY())) {
transitionToDelegatingStateAndClear();
return;
}
@@ -738,7 +713,8 @@
return;
}
mHandler.removeMessages(MESSAGE_ON_ACTION_TAP_AND_HOLD);
- if (!mMagnifiedBounds.contains((int) event.getX(), (int) event.getY())) {
+ if (!mMagnificationController.magnifiedRegionContains(
+ event.getX(), event.getY())) {
transitionToDelegatingStateAndClear();
return;
}
@@ -947,184 +923,6 @@
}
}
- private final class MagnificationController {
-
- private static final String PROPERTY_NAME_MAGNIFICATION_SPEC =
- "magnificationSpec";
-
- private final MagnificationSpec mSentMagnificationSpec = MagnificationSpec.obtain();
-
- private final MagnificationSpec mCurrentMagnificationSpec = MagnificationSpec.obtain();
-
- private final Rect mTempRect = new Rect();
-
- private final ValueAnimator mTransformationAnimator;
-
- public MagnificationController(long animationDuration) {
- Property<MagnificationController, MagnificationSpec> property =
- Property.of(MagnificationController.class, MagnificationSpec.class,
- PROPERTY_NAME_MAGNIFICATION_SPEC);
- TypeEvaluator<MagnificationSpec> evaluator = new TypeEvaluator<MagnificationSpec>() {
- private final MagnificationSpec mTempTransformationSpec =
- MagnificationSpec.obtain();
- @Override
- public MagnificationSpec evaluate(float fraction, MagnificationSpec fromSpec,
- MagnificationSpec toSpec) {
- MagnificationSpec result = mTempTransformationSpec;
- result.scale = fromSpec.scale
- + (toSpec.scale - fromSpec.scale) * fraction;
- result.offsetX = fromSpec.offsetX + (toSpec.offsetX - fromSpec.offsetX)
- * fraction;
- result.offsetY = fromSpec.offsetY + (toSpec.offsetY - fromSpec.offsetY)
- * fraction;
- return result;
- }
- };
- mTransformationAnimator = ObjectAnimator.ofObject(this, property,
- evaluator, mSentMagnificationSpec, mCurrentMagnificationSpec);
- mTransformationAnimator.setDuration((long) (animationDuration));
- mTransformationAnimator.setInterpolator(new DecelerateInterpolator(2.5f));
- }
-
- public boolean isMagnifying() {
- return mCurrentMagnificationSpec.scale > 1.0f;
- }
-
- public void reset(boolean animate) {
- if (mTransformationAnimator.isRunning()) {
- mTransformationAnimator.cancel();
- }
- mCurrentMagnificationSpec.clear();
- if (animate) {
- animateMangificationSpec(mSentMagnificationSpec,
- mCurrentMagnificationSpec);
- } else {
- setMagnificationSpec(mCurrentMagnificationSpec);
- }
- Rect bounds = mTempRect;
- bounds.setEmpty();
- mAms.onMagnificationStateChanged();
- }
-
- public float getScale() {
- return mCurrentMagnificationSpec.scale;
- }
-
- public float getOffsetX() {
- return mCurrentMagnificationSpec.offsetX;
- }
-
- public float getOffsetY() {
- return mCurrentMagnificationSpec.offsetY;
- }
-
- public void setScale(float scale, float pivotX, float pivotY, boolean animate) {
- Rect magnifiedFrame = mTempRect;
- mMagnifiedBounds.getBounds(magnifiedFrame);
- MagnificationSpec spec = mCurrentMagnificationSpec;
- final float oldScale = spec.scale;
- final float oldCenterX = (-spec.offsetX + magnifiedFrame.width() / 2) / oldScale;
- final float oldCenterY = (-spec.offsetY + magnifiedFrame.height() / 2) / oldScale;
- final float normPivotX = (-spec.offsetX + pivotX) / oldScale;
- final float normPivotY = (-spec.offsetY + pivotY) / oldScale;
- final float offsetX = (oldCenterX - normPivotX) * (oldScale / scale);
- final float offsetY = (oldCenterY - normPivotY) * (oldScale / scale);
- final float centerX = normPivotX + offsetX;
- final float centerY = normPivotY + offsetY;
- setScaleAndMagnifiedRegionCenter(scale, centerX, centerY, animate);
- }
-
- public void setMagnifiedRegionCenter(float centerX, float centerY, boolean animate) {
- setScaleAndMagnifiedRegionCenter(mCurrentMagnificationSpec.scale, centerX, centerY,
- animate);
- }
-
- public void offsetMagnifiedRegionCenter(float offsetX, float offsetY) {
- final float nonNormOffsetX = mCurrentMagnificationSpec.offsetX - offsetX;
- mCurrentMagnificationSpec.offsetX = Math.min(Math.max(nonNormOffsetX,
- getMinOffsetX()), 0);
- final float nonNormOffsetY = mCurrentMagnificationSpec.offsetY - offsetY;
- mCurrentMagnificationSpec.offsetY = Math.min(Math.max(nonNormOffsetY,
- getMinOffsetY()), 0);
- setMagnificationSpec(mCurrentMagnificationSpec);
- }
-
- public void setScaleAndMagnifiedRegionCenter(float scale, float centerX, float centerY,
- boolean animate) {
- if (Float.compare(mCurrentMagnificationSpec.scale, scale) == 0
- && Float.compare(mCurrentMagnificationSpec.offsetX,
- centerX) == 0
- && Float.compare(mCurrentMagnificationSpec.offsetY,
- centerY) == 0) {
- return;
- }
- if (mTransformationAnimator.isRunning()) {
- mTransformationAnimator.cancel();
- }
- if (DEBUG_MAGNIFICATION_CONTROLLER) {
- Slog.i(LOG_TAG, "scale: " + scale + " offsetX: " + centerX
- + " offsetY: " + centerY);
- }
- updateMagnificationSpec(scale, centerX, centerY);
- if (animate) {
- animateMangificationSpec(mSentMagnificationSpec,
- mCurrentMagnificationSpec);
- } else {
- setMagnificationSpec(mCurrentMagnificationSpec);
- }
- mAms.onMagnificationStateChanged();
- }
-
- public void updateMagnificationSpec(float scale, float magnifiedCenterX,
- float magnifiedCenterY) {
- Rect magnifiedFrame = mTempRect;
- mMagnifiedBounds.getBounds(magnifiedFrame);
- mCurrentMagnificationSpec.scale = scale;
- final int viewportWidth = magnifiedFrame.width();
- final float nonNormOffsetX = viewportWidth / 2 - magnifiedCenterX * scale;
- mCurrentMagnificationSpec.offsetX = Math.min(Math.max(nonNormOffsetX,
- getMinOffsetX()), 0);
- final int viewportHeight = magnifiedFrame.height();
- final float nonNormOffsetY = viewportHeight / 2 - magnifiedCenterY * scale;
- mCurrentMagnificationSpec.offsetY = Math.min(Math.max(nonNormOffsetY,
- getMinOffsetY()), 0);
- }
-
- private float getMinOffsetX() {
- Rect magnifiedFrame = mTempRect;
- mMagnifiedBounds.getBounds(magnifiedFrame);
- final float viewportWidth = magnifiedFrame.width();
- return viewportWidth - viewportWidth * mCurrentMagnificationSpec.scale;
- }
-
- private float getMinOffsetY() {
- Rect magnifiedFrame = mTempRect;
- mMagnifiedBounds.getBounds(magnifiedFrame);
- final float viewportHeight = magnifiedFrame.height();
- return viewportHeight - viewportHeight * mCurrentMagnificationSpec.scale;
- }
-
- private void animateMangificationSpec(MagnificationSpec fromSpec,
- MagnificationSpec toSpec) {
- mTransformationAnimator.setObjectValues(fromSpec, toSpec);
- mTransformationAnimator.start();
- }
-
- public MagnificationSpec getMagnificationSpec() {
- return mSentMagnificationSpec;
- }
-
- public void setMagnificationSpec(MagnificationSpec spec) {
- if (DEBUG_SET_MAGNIFICATION_SPEC) {
- Slog.i(LOG_TAG, "Sending: " + spec);
- }
- mSentMagnificationSpec.scale = spec.scale;
- mSentMagnificationSpec.offsetX = spec.offsetX;
- mSentMagnificationSpec.offsetY = spec.offsetY;
- mWindowManager.setMagnificationSpec(MagnificationSpec.obtain(spec));
- }
- }
-
private final class ScreenStateObserver extends BroadcastReceiver {
private static final int MESSAGE_ON_SCREEN_STATE_CHANGE = 1;
diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
index 954536f..ca30349 100644
--- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
@@ -17,13 +17,6 @@
package com.android.server.accessibility;
import android.content.Context;
-import android.gesture.Gesture;
-import android.gesture.GestureLibraries;
-import android.gesture.GestureLibrary;
-import android.gesture.GesturePoint;
-import android.gesture.GestureStore;
-import android.gesture.GestureStroke;
-import android.gesture.Prediction;
import android.graphics.Point;
import android.os.Handler;
import android.util.Slog;
@@ -38,8 +31,6 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
-import com.android.internal.R;
-
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -167,24 +158,6 @@
// Context in which this explorer operates.
private final Context mContext;
- // The X of the previous event.
- private float mPreviousX;
-
- // The Y of the previous event.
- private float mPreviousY;
-
- // Buffer for storing points for gesture detection.
- private final ArrayList<GesturePoint> mStrokeBuffer = new ArrayList<GesturePoint>(100);
-
- // The minimal delta between moves to add a gesture point.
- private static final int TOUCH_TOLERANCE = 3;
-
- // The minimal score for accepting a predicted gesture.
- private static final float MIN_PREDICTION_SCORE = 2.0f;
-
- // The library for gesture detection.
- private GestureLibrary mGestureLibrary;
-
// The long pressing pointer id if coordinate remapping is needed.
private int mLongPressingPointerId = -1;
@@ -215,10 +188,6 @@
mDoubleTapSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop();
mHandler = new Handler(context.getMainLooper());
mExitGestureDetectionModeDelayed = new ExitGestureDetectionModeDelayed();
- mGestureLibrary = GestureLibraries.fromRawResource(context, R.raw.accessibility_gestures);
- mGestureLibrary.setOrientationStyle(8);
- mGestureLibrary.setSequenceType(GestureStore.SEQUENCE_SENSITIVE);
- mGestureLibrary.load();
mSendHoverEnterAndMoveDelayed = new SendHoverEnterAndMoveDelayed();
mSendHoverExitDelayed = new SendHoverExitDelayed();
mSendTouchExplorationEndDelayed = new SendAccessibilityEventDelayed(
@@ -274,8 +243,7 @@
sendUpForInjectedDownPointers(event, policyFlags);
} break;
case STATE_GESTURE_DETECTING: {
- // Clear the current stroke.
- mStrokeBuffer.clear();
+ // No state specific cleanup required.
} break;
}
// Remove all pending callbacks.
@@ -321,6 +289,11 @@
mReceivedPointerTracker.onMotionEvent(rawEvent);
+ if (mGestureDetector.onMotionEvent(event, policyFlags)) {
+ // Event was handled by the gesture detector.
+ return;
+ }
+
switch(mCurrentState) {
case STATE_TOUCH_EXPLORING: {
handleMotionEventStateTouchExploring(event, rawEvent, policyFlags);
@@ -389,6 +362,11 @@
@Override
public void onDoubleTapAndHold(MotionEvent event, int policyFlags) {
+ // Ignore the event if we aren't touch exploring.
+ if (mCurrentState != STATE_TOUCH_EXPLORING) {
+ return;
+ }
+
// Pointers should not be zero when running this command.
if (mReceivedPointerTracker.getLastReceivedEvent().getPointerCount() == 0) {
return;
@@ -415,10 +393,10 @@
}
@Override
- public void onDoubleTap(MotionEvent event, int policyFlags) {
- // This should never be called when more than two pointers are down.
- if (event.getPointerCount() > 2) {
- return;
+ public boolean onDoubleTap(MotionEvent event, int policyFlags) {
+ // Ignore the event if we aren't touch exploring.
+ if (mCurrentState != STATE_TOUCH_EXPLORING) {
+ return false;
}
// Remove pending event deliveries.
@@ -438,7 +416,9 @@
Point clickLocation = mTempPoint;
final int result = computeClickLocation(clickLocation);
if (result == CLICK_LOCATION_NONE) {
- return;
+ // We can't send a click to no location, but the gesture was still
+ // consumed.
+ return true;
}
// Do the click.
@@ -456,6 +436,28 @@
final boolean targetAccessibilityFocus = (result == CLICK_LOCATION_ACCESSIBILITY_FOCUS);
sendActionDownAndUp(click_event, policyFlags, targetAccessibilityFocus);
click_event.recycle();
+ return true;
+ }
+
+ @Override
+ public boolean onGesture(int gestureId) {
+ if (mCurrentState != STATE_GESTURE_DETECTING) {
+ return false;
+ }
+
+ mAms.onTouchInteractionEnd();
+
+ // Announce the end of the gesture recognition.
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END);
+ // Announce the end of a the touch interaction.
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
+
+ mAms.onGesture(gestureId);
+
+ mExitGestureDetectionModeDelayed.cancel();
+ mCurrentState = STATE_TOUCH_EXPLORING;
+
+ return true;
}
/**
@@ -471,17 +473,10 @@
mVelocityTracker.addMovement(rawEvent);
- mGestureDetector.onMotionEvent(event, policyFlags);
-
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
mAms.onTouchInteractionStart();
- // Pre-feed the motion events to the gesture detector since we
- // have a distance slop before getting into gesture detection
- // mode and not using the points within this slop significantly
- // decreases the quality of gesture recognition.
- handleMotionEventGestureDetecting(rawEvent, policyFlags);
sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_START);
// If we still have not notified the user for the last
@@ -528,12 +523,6 @@
// We have not started sending events since we try to
// figure out what the user is doing.
if (mSendHoverEnterAndMoveDelayed.isPending()) {
- // Pre-feed the motion events to the gesture detector since we
- // have a distance slop before getting into gesture detection
- // mode and not using the points within this slop significantly
- // decreases the quality of gesture recognition.
- handleMotionEventGestureDetecting(rawEvent, policyFlags);
-
// Cache the event until we discern exploration from gesturing.
mSendHoverEnterAndMoveDelayed.addEvent(event);
@@ -568,6 +557,7 @@
} else {
// We have just decided that the user is touch,
// exploring so start sending events.
+ mGestureDetector.cancelGesture();
mSendHoverEnterAndMoveDelayed.forceSendAndRemove();
mSendHoverExitDelayed.cancel();
sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE,
@@ -613,9 +603,9 @@
}
// We know that a new state transition is to happen and the
- // new state will not be gesture recognition, so clear the
- // stashed gesture strokes.
- mStrokeBuffer.clear();
+ // new state will not be gesture recognition, so cancel
+ // the gesture.
+ mGestureDetector.cancelGesture();
if (isDraggingGesture(event)) {
// Two pointers moving in the same direction within
@@ -655,9 +645,6 @@
} break;
case MotionEvent.ACTION_UP: {
mAms.onTouchInteractionEnd();
- // We know that we do not need the pre-fed gesture points are not
- // needed anymore since the last pointer just went up.
- mStrokeBuffer.clear();
final int pointerId = event.getPointerId(event.getActionIndex());
final int pointerIdBits = (1 << pointerId);
@@ -820,24 +807,6 @@
private void handleMotionEventGestureDetecting(MotionEvent event, int policyFlags) {
switch (event.getActionMasked()) {
- case MotionEvent.ACTION_DOWN: {
- final float x = event.getX();
- final float y = event.getY();
- mPreviousX = x;
- mPreviousY = y;
- mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));
- } break;
- case MotionEvent.ACTION_MOVE: {
- final float x = event.getX();
- final float y = event.getY();
- final float dX = Math.abs(x - mPreviousX);
- final float dY = Math.abs(y - mPreviousY);
- if (dX >= TOUCH_TOLERANCE || dY >= TOUCH_TOLERANCE) {
- mPreviousX = x;
- mPreviousY = y;
- mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));
- }
- } break;
case MotionEvent.ACTION_UP: {
mAms.onTouchInteractionEnd();
// Announce the end of the gesture recognition.
@@ -845,31 +814,6 @@
// Announce the end of a the touch interaction.
sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
- float x = event.getX();
- float y = event.getY();
- mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));
-
- Gesture gesture = new Gesture();
- gesture.addStroke(new GestureStroke(mStrokeBuffer));
-
- ArrayList<Prediction> predictions = mGestureLibrary.recognize(gesture);
- if (!predictions.isEmpty()) {
- Prediction bestPrediction = predictions.get(0);
- if (bestPrediction.score >= MIN_PREDICTION_SCORE) {
- if (DEBUG) {
- Slog.i(LOG_TAG, "gesture: " + bestPrediction.name + " score: "
- + bestPrediction.score);
- }
- try {
- final int gestureId = Integer.parseInt(bestPrediction.name);
- mAms.onGesture(gestureId);
- } catch (NumberFormatException nfe) {
- Slog.w(LOG_TAG, "Non numeric gesture id:" + bestPrediction.name);
- }
- }
- }
-
- mStrokeBuffer.clear();
mExitGestureDetectionModeDelayed.cancel();
mCurrentState = STATE_TOUCH_EXPLORING;
} break;
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 882899e..a5ddc12 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -20,7 +20,10 @@
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.AlarmManager;
+import android.app.AppOpsManager;
import android.app.BroadcastOptions;
+import android.app.IAlarmCompleteListener;
+import android.app.IAlarmListener;
import android.app.IAlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
@@ -94,11 +97,13 @@
static final boolean DEBUG_BATCH = localLOGV || false;
static final boolean DEBUG_VALIDATE = localLOGV || false;
static final boolean DEBUG_ALARM_CLOCK = localLOGV || false;
+ static final boolean DEBUG_LISTENER_CALLBACK = localLOGV || false;
static final boolean RECORD_ALARMS_IN_HISTORY = true;
+ static final boolean RECORD_DEVICE_IDLE_ALARMS = false;
static final int ALARM_EVENT = 1;
static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
- static final Intent mBackgroundIntent
+ private final Intent mBackgroundIntent
= new Intent().addFlags(Intent.FLAG_FROM_BACKGROUND);
static final IncreasingTimeOrder sIncreasingTimeOrder = new IncreasingTimeOrder();
@@ -109,6 +114,8 @@
final LocalLog mLog = new LocalLog(TAG);
+ AppOpsManager mAppOps;
+
final Object mLock = new Object();
long mNativeData;
@@ -123,7 +130,7 @@
ClockReceiver mClockReceiver;
InteractiveStateReceiver mInteractiveStateReceiver;
private UninstallReceiver mUninstallReceiver;
- final ResultReceiver mResultReceiver = new ResultReceiver();
+ final DeliveryTracker mDeliveryTracker = new DeliveryTracker();
PendingIntent mTimeTickSender;
PendingIntent mDateChangeSender;
Random mRandom;
@@ -144,6 +151,16 @@
*/
final SparseLongArray mLastAllowWhileIdleDispatch = new SparseLongArray();
+ final static class IdleDispatchEntry {
+ int uid;
+ String pkg;
+ String tag;
+ String op;
+ long elapsedRealtime;
+ long argRealtime;
+ }
+ final ArrayList<IdleDispatchEntry> mAllowWhileIdleDispatches = new ArrayList();
+
/**
* Broadcast options to use for FLAG_ALLOW_WHILE_IDLE.
*/
@@ -174,13 +191,16 @@
private static final String KEY_ALLOW_WHILE_IDLE_LONG_TIME = "allow_while_idle_long_time";
private static final String KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION
= "allow_while_idle_whitelist_duration";
+ private static final String KEY_LISTENER_TIMEOUT = "listener_timeout";
private static final long DEFAULT_MIN_FUTURITY = 5 * 1000;
private static final long DEFAULT_MIN_INTERVAL = 60 * 1000;
private static final long DEFAULT_ALLOW_WHILE_IDLE_SHORT_TIME = DEFAULT_MIN_FUTURITY;
- private static final long DEFAULT_ALLOW_WHILE_IDLE_LONG_TIME = 15*60*1000;
+ private static final long DEFAULT_ALLOW_WHILE_IDLE_LONG_TIME = 9*60*1000;
private static final long DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION = 10*1000;
+ private static final long DEFAULT_LISTENER_TIMEOUT = 5 * 1000;
+
// Minimum futurity of a new alarm
public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY;
@@ -197,6 +217,9 @@
public long ALLOW_WHILE_IDLE_WHITELIST_DURATION
= DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION;
+ // Direct alarm listener callback timeout
+ public long LISTENER_TIMEOUT = DEFAULT_LISTENER_TIMEOUT;
+
private ContentResolver mResolver;
private final KeyValueListParser mParser = new KeyValueListParser(',');
private long mLastAllowWhileIdleWhitelistDuration = -1;
@@ -253,6 +276,8 @@
ALLOW_WHILE_IDLE_WHITELIST_DURATION = mParser.getLong(
KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION,
DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION);
+ LISTENER_TIMEOUT = mParser.getLong(KEY_LISTENER_TIMEOUT,
+ DEFAULT_LISTENER_TIMEOUT);
updateAllowWhileIdleMinTimeLocked();
updateAllowWhileIdleWhitelistDurationLocked();
@@ -270,6 +295,10 @@
TimeUtils.formatDuration(MIN_INTERVAL, pw);
pw.println();
+ pw.print(" "); pw.print(KEY_LISTENER_TIMEOUT); pw.print("=");
+ TimeUtils.formatDuration(LISTENER_TIMEOUT, pw);
+ pw.println();
+
pw.print(" "); pw.print(KEY_ALLOW_WHILE_IDLE_SHORT_TIME); pw.print("=");
TimeUtils.formatDuration(ALLOW_WHILE_IDLE_SHORT_TIME, pw);
pw.println();
@@ -377,14 +406,21 @@
return newStart;
}
- boolean remove(final PendingIntent operation) {
+ boolean remove(final PendingIntent operation, final IAlarmListener listener) {
+ if (operation == null && listener == null) {
+ if (localLOGV) {
+ Slog.w(TAG, "requested remove() of null operation",
+ new RuntimeException("here"));
+ }
+ return false;
+ }
boolean didRemove = false;
long newStart = 0; // recalculate endpoints as we go
long newEnd = Long.MAX_VALUE;
int newFlags = 0;
for (int i = 0; i < alarms.size(); ) {
Alarm alarm = alarms.get(i);
- if (alarm.operation.equals(operation)) {
+ if (alarm.matches(operation, listener)) {
alarms.remove(i);
didRemove = true;
if (alarm.alarmClock != null) {
@@ -411,13 +447,20 @@
}
boolean remove(final String packageName) {
+ if (packageName == null) {
+ if (localLOGV) {
+ Slog.w(TAG, "requested remove() of null packageName",
+ new RuntimeException("here"));
+ }
+ return false;
+ }
boolean didRemove = false;
long newStart = 0; // recalculate endpoints as we go
long newEnd = Long.MAX_VALUE;
int newFlags = 0;
for (int i = 0; i < alarms.size(); ) {
Alarm alarm = alarms.get(i);
- if (alarm.operation.getTargetPackage().equals(packageName)) {
+ if (alarm.matches(packageName)) {
alarms.remove(i);
didRemove = true;
if (alarm.alarmClock != null) {
@@ -449,7 +492,7 @@
long newEnd = Long.MAX_VALUE;
for (int i = 0; i < alarms.size(); ) {
Alarm alarm = alarms.get(i);
- if (UserHandle.getUserId(alarm.operation.getCreatorUid()) == userHandle) {
+ if (UserHandle.getUserId(alarm.creatorUid) == userHandle) {
alarms.remove(i);
didRemove = true;
if (alarm.alarmClock != null) {
@@ -477,7 +520,7 @@
final int N = alarms.size();
for (int i = 0; i < N; i++) {
Alarm a = alarms.get(i);
- if (a.operation.getTargetPackage().equals(packageName)) {
+ if (a.matches(packageName)) {
return true;
}
}
@@ -554,7 +597,8 @@
Alarm a = alarms.get(i);
final int alarmPrio;
- if (Intent.ACTION_TIME_TICK.equals(a.operation.getIntent().getAction())) {
+ if (a.operation != null
+ && Intent.ACTION_TIME_TICK.equals(a.operation.getIntent().getAction())) {
alarmPrio = PRIO_TICK;
} else if (a.wakeup) {
alarmPrio = PRIO_WAKEUP;
@@ -563,10 +607,13 @@
}
PriorityClass packagePrio = a.priorityClass;
- if (packagePrio == null) packagePrio = mPriorities.get(a.operation.getCreatorPackage());
+ String alarmPackage = (a.operation != null)
+ ? a.operation.getCreatorPackage()
+ : a.packageName;
+ if (packagePrio == null) packagePrio = mPriorities.get(alarmPackage);
if (packagePrio == null) {
packagePrio = a.priorityClass = new PriorityClass(); // lowest prio & stale sequence
- mPriorities.put(a.operation.getCreatorPackage(), packagePrio);
+ mPriorities.put(alarmPackage, packagePrio);
}
a.priorityClass = packagePrio;
@@ -700,6 +747,14 @@
}
void restorePendingWhileIdleAlarmsLocked() {
+ if (RECORD_DEVICE_IDLE_ALARMS) {
+ IdleDispatchEntry ent = new IdleDispatchEntry();
+ ent.uid = 0;
+ ent.pkg = "FINISH IDLE";
+ ent.elapsedRealtime = SystemClock.elapsedRealtime();
+ mAllowWhileIdleDispatches.add(ent);
+ }
+
// Bring pending alarms back into the main list.
if (mPendingWhileIdleAlarms.size() > 0) {
ArrayList<Alarm> alarms = mPendingWhileIdleAlarms;
@@ -725,20 +780,27 @@
}
}
- static final class InFlight extends Intent {
+ static final class InFlight {
final PendingIntent mPendingIntent;
+ final IBinder mListener;
final WorkSource mWorkSource;
+ final int mUid;
final String mTag;
final BroadcastStats mBroadcastStats;
final FilterStats mFilterStats;
final int mAlarmType;
- InFlight(AlarmManagerService service, PendingIntent pendingIntent, WorkSource workSource,
- int alarmType, String tag, long nowELAPSED) {
+ InFlight(AlarmManagerService service, PendingIntent pendingIntent, IAlarmListener listener,
+ WorkSource workSource, int uid, String alarmPkg, int alarmType, String tag,
+ long nowELAPSED) {
mPendingIntent = pendingIntent;
+ mListener = listener != null ? listener.asBinder() : null;
mWorkSource = workSource;
+ mUid = uid;
mTag = tag;
- mBroadcastStats = service.getStatsLocked(pendingIntent);
+ mBroadcastStats = (pendingIntent != null)
+ ? service.getStatsLocked(pendingIntent)
+ : service.getStatsLocked(uid, alarmPkg);
FilterStats fs = mBroadcastStats.filterStats.get(mTag);
if (fs == null) {
fs = new FilterStats(mBroadcastStats, mTag);
@@ -834,6 +896,7 @@
public void onBootPhase(int phase) {
if (phase == PHASE_SYSTEM_SERVICES_READY) {
mConstants.start(getContext().getContentResolver());
+ mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
}
}
@@ -886,15 +949,20 @@
return;
}
synchronized (mLock) {
- removeLocked(operation);
+ removeLocked(operation, null);
}
}
void setImpl(int type, long triggerAtTime, long windowLength, long interval,
- PendingIntent operation, int flags, WorkSource workSource,
- AlarmManager.AlarmClockInfo alarmClock, int callingUid) {
- if (operation == null) {
- Slog.w(TAG, "set/setRepeating ignored because there is no intent");
+ PendingIntent operation, IAlarmListener directReceiver, String listenerTag,
+ int flags, WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock,
+ int callingUid, String callingPackage) {
+ // must be *either* PendingIntent or AlarmReceiver, but not both
+ if ((operation == null && directReceiver == null)
+ || (operation != null && directReceiver != null)) {
+ Slog.w(TAG, "Alarms must either supply a PendingIntent or an AlarmReceiver");
+ // NB: previous releases failed silently here, so we are continuing to do the same
+ // rather than throw an IllegalArgumentException.
return;
}
@@ -952,17 +1020,19 @@
+ " interval=" + interval + " flags=0x" + Integer.toHexString(flags));
}
setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, maxElapsed,
- interval, operation, flags, true, workSource, alarmClock, callingUid);
+ interval, operation, directReceiver, listenerTag, flags, true, workSource,
+ alarmClock, callingUid, callingPackage);
}
}
private void setImplLocked(int type, long when, long whenElapsed, long windowLength,
- long maxWhen, long interval, PendingIntent operation, int flags,
- boolean doValidate, WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock,
- int uid) {
+ long maxWhen, long interval, PendingIntent operation, IAlarmListener directReceiver,
+ String listenerTag, int flags, boolean doValidate, WorkSource workSource,
+ AlarmManager.AlarmClockInfo alarmClock, int callingUid, String callingPackage) {
Alarm a = new Alarm(type, when, whenElapsed, windowLength, maxWhen, interval,
- operation, workSource, flags, alarmClock, uid);
- removeLocked(operation);
+ operation, directReceiver, listenerTag, workSource, flags, alarmClock,
+ callingUid, callingPackage);
+ removeLocked(operation, directReceiver);
setImplLocked(a, false, doValidate);
}
@@ -971,8 +1041,8 @@
// This is a special alarm that will put the system into idle until it goes off.
// The caller has given the time they want this to happen at, however we need
// to pull that earlier if there are existing alarms that have requested to
- // bring us out of idle.
- if (mNextWakeFromIdle != null) {
+ // bring us out of idle at an earlier time.
+ if (mNextWakeFromIdle != null && a.whenElapsed > mNextWakeFromIdle.whenElapsed) {
a.when = a.whenElapsed = a.maxWhenElapsed = mNextWakeFromIdle.whenElapsed;
}
// Add fuzz to make the alarm go off some time before the actual desired time.
@@ -1006,6 +1076,19 @@
}
}
+ if (RECORD_DEVICE_IDLE_ALARMS) {
+ if ((a.flags & AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) {
+ IdleDispatchEntry ent = new IdleDispatchEntry();
+ ent.uid = a.uid;
+ ent.pkg = a.operation.getCreatorPackage();
+ ent.tag = a.operation.getTag("");
+ ent.op = "SET";
+ ent.elapsedRealtime = SystemClock.elapsedRealtime();
+ ent.argRealtime = a.whenElapsed;
+ mAllowWhileIdleDispatches.add(ent);
+ }
+ }
+
int whichBatch = ((a.flags&AlarmManager.FLAG_STANDALONE) != 0)
? -1 : attemptCoalesceLocked(a.whenElapsed, a.maxWhenElapsed);
if (whichBatch < 0) {
@@ -1028,6 +1111,15 @@
boolean needRebatch = false;
if ((a.flags&AlarmManager.FLAG_IDLE_UNTIL) != 0) {
+ if (RECORD_DEVICE_IDLE_ALARMS) {
+ if (mPendingIdleUntil == null) {
+ IdleDispatchEntry ent = new IdleDispatchEntry();
+ ent.uid = 0;
+ ent.pkg = "START IDLE";
+ ent.elapsedRealtime = SystemClock.elapsedRealtime();
+ mAllowWhileIdleDispatches.add(ent);
+ }
+ }
mPendingIdleUntil = a;
mConstants.updateAllowWhileIdleMinTimeLocked();
needRebatch = true;
@@ -1068,10 +1160,31 @@
private final IBinder mService = new IAlarmManager.Stub() {
@Override
- public void set(int type, long triggerAtTime, long windowLength, long interval, int flags,
- PendingIntent operation, WorkSource workSource,
- AlarmManager.AlarmClockInfo alarmClock) {
+ public void set(String callingPackage,
+ int type, long triggerAtTime, long windowLength, long interval, int flags,
+ PendingIntent operation, IAlarmListener directReceiver, String listenerTag,
+ WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock) {
final int callingUid = Binder.getCallingUid();
+
+ // make sure the caller is not lying about which package should be blamed for
+ // wakelock time spent in alarm delivery
+ mAppOps.checkPackage(callingUid, callingPackage);
+
+ // Repeating alarms must use PendingIntent, not direct listener
+ if (interval != 0) {
+ if (directReceiver != null) {
+ throw new IllegalArgumentException("Repeating alarms cannot use AlarmReceivers");
+ }
+ }
+
+ // direct-callback alarms must be wakeup alarms (otherwise they should just be
+ // posting work to a Handler)
+ if (directReceiver != null) {
+ if (type != RTC_WAKEUP && type != ELAPSED_REALTIME_WAKEUP) {
+ throw new IllegalArgumentException("Only wakeup alarms can use AlarmReceivers");
+ }
+ }
+
if (workSource != null) {
getContext().enforcePermission(
android.Manifest.permission.UPDATE_DEVICE_STATS,
@@ -1108,8 +1221,8 @@
flags |= AlarmManager.FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_STANDALONE;
}
- setImpl(type, triggerAtTime, windowLength, interval, operation,
- flags, workSource, alarmClock, callingUid);
+ setImpl(type, triggerAtTime, windowLength, interval, operation, directReceiver,
+ listenerTag, flags, workSource, alarmClock, callingUid, callingPackage);
}
@Override
@@ -1143,9 +1256,15 @@
}
@Override
- public void remove(PendingIntent operation) {
- removeImpl(operation);
+ public void remove(PendingIntent operation, IAlarmListener listener) {
+ if (operation == null && listener == null) {
+ Slog.w(TAG, "remove() with no intent or listener");
+ return;
+ }
+ synchronized (mLock) {
+ removeLocked(operation, listener);
+ }
}
@Override
@@ -1256,7 +1375,7 @@
pw.print(" Idling until: ");
if (mPendingIdleUntil != null) {
pw.println(mPendingIdleUntil);
- mPendingIdleUntil.dump(pw, " ", nowRTC, nowELAPSED, sdf);
+ mPendingIdleUntil.dump(pw, " ", nowRTC, nowELAPSED, sdf);
} else {
pw.println("null");
}
@@ -1400,6 +1519,32 @@
}
}
+ if (RECORD_DEVICE_IDLE_ALARMS) {
+ pw.println();
+ pw.println(" Allow while idle dispatches:");
+ for (int i = 0; i < mAllowWhileIdleDispatches.size(); i++) {
+ IdleDispatchEntry ent = mAllowWhileIdleDispatches.get(i);
+ pw.print(" ");
+ TimeUtils.formatDuration(ent.elapsedRealtime, nowELAPSED, pw);
+ pw.print(": ");
+ UserHandle.formatUid(pw, ent.uid);
+ pw.print(":");
+ pw.println(ent.pkg);
+ if (ent.op != null) {
+ pw.print(" ");
+ pw.print(ent.op);
+ pw.print(" / ");
+ pw.print(ent.tag);
+ if (ent.argRealtime != 0) {
+ pw.print(" (");
+ TimeUtils.formatDuration(ent.argRealtime, nowELAPSED, pw);
+ pw.print(")");
+ }
+ pw.println();
+ }
+ }
+ }
+
if (WAKEUP_STATS) {
pw.println();
pw.println(" Recent Wakeup History:");
@@ -1630,17 +1775,17 @@
}
}
- private void removeLocked(PendingIntent operation) {
+ private void removeLocked(PendingIntent operation, IAlarmListener directReceiver) {
boolean didRemove = false;
for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
Batch b = mAlarmBatches.get(i);
- didRemove |= b.remove(operation);
+ didRemove |= b.remove(operation, directReceiver);
if (b.size() == 0) {
mAlarmBatches.remove(i);
}
}
for (int i = mPendingWhileIdleAlarms.size() - 1; i >= 0; i--) {
- if (mPendingWhileIdleAlarms.get(i).operation.equals(operation)) {
+ if (mPendingWhileIdleAlarms.get(i).matches(operation, directReceiver)) {
// Don't set didRemove, since this doesn't impact the scheduled alarms.
mPendingWhileIdleAlarms.remove(i);
}
@@ -1651,11 +1796,11 @@
Slog.v(TAG, "remove(operation) changed bounds; rebatching");
}
boolean restorePending = false;
- if (mPendingIdleUntil != null && mPendingIdleUntil.operation.equals(operation)) {
+ if (mPendingIdleUntil != null && mPendingIdleUntil.matches(operation, directReceiver)) {
mPendingIdleUntil = null;
restorePending = true;
}
- if (mNextWakeFromIdle != null && mNextWakeFromIdle.operation.equals(operation)) {
+ if (mNextWakeFromIdle != null && mNextWakeFromIdle.matches(operation, directReceiver)) {
mNextWakeFromIdle = null;
}
rebatchAllAlarmsLocked(true);
@@ -1676,7 +1821,8 @@
}
}
for (int i = mPendingWhileIdleAlarms.size() - 1; i >= 0; i--) {
- if (mPendingWhileIdleAlarms.get(i).operation.getTargetPackage().equals(packageName)) {
+ final Alarm a = mPendingWhileIdleAlarms.get(i);
+ if (a.matches(packageName)) {
// Don't set didRemove, since this doesn't impact the scheduled alarms.
mPendingWhileIdleAlarms.remove(i);
}
@@ -1702,7 +1848,7 @@
}
}
for (int i = mPendingWhileIdleAlarms.size() - 1; i >= 0; i--) {
- if (UserHandle.getUserId(mPendingWhileIdleAlarms.get(i).operation.getCreatorUid())
+ if (UserHandle.getUserId(mPendingWhileIdleAlarms.get(i).creatorUid)
== userHandle) {
// Don't set didRemove, since this doesn't impact the scheduled alarms.
mPendingWhileIdleAlarms.remove(i);
@@ -1758,7 +1904,8 @@
}
}
for (int i = 0; i < mPendingWhileIdleAlarms.size(); i++) {
- if (mPendingWhileIdleAlarms.get(i).operation.getTargetPackage().equals(packageName)) {
+ final Alarm a = mPendingWhileIdleAlarms.get(i);
+ if (a.matches(packageName)) {
return true;
}
}
@@ -1862,6 +2009,16 @@
if (alarm.maxWhenElapsed < minTime) {
alarm.maxWhenElapsed = minTime;
}
+ if (RECORD_DEVICE_IDLE_ALARMS) {
+ IdleDispatchEntry ent = new IdleDispatchEntry();
+ ent.uid = alarm.uid;
+ ent.pkg = alarm.operation.getCreatorPackage();
+ ent.tag = alarm.operation.getTag("");
+ ent.op = "RESCHEDULE";
+ ent.elapsedRealtime = nowELAPSED;
+ ent.argRealtime = lastTime;
+ mAllowWhileIdleDispatches.add(ent);
+ }
setImplLocked(alarm, true, false);
continue;
}
@@ -1871,7 +2028,7 @@
triggerList.add(alarm);
if ((alarm.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) {
EventLogTags.writeDeviceIdleWakeFromIdle(mPendingIdleUntil != null ? 1 : 0,
- alarm.tag);
+ alarm.statsTag);
}
if (mPendingIdleUntil == alarm) {
mPendingIdleUntil = null;
@@ -1895,8 +2052,8 @@
final long nextElapsed = alarm.whenElapsed + delta;
setImplLocked(alarm.type, alarm.when + delta, nextElapsed, alarm.windowLength,
maxTriggerTime(nowELAPSED, nextElapsed, alarm.repeatInterval),
- alarm.repeatInterval, alarm.operation, alarm.flags, true,
- alarm.workSource, alarm.alarmClock, alarm.uid);
+ alarm.repeatInterval, alarm.operation, null, null, alarm.flags, true,
+ alarm.workSource, alarm.alarmClock, alarm.uid, alarm.packageName);
}
if (alarm.wakeup) {
@@ -1947,11 +2104,15 @@
public final long origWhen;
public final boolean wakeup;
public final PendingIntent operation;
- public final String tag;
+ public final IAlarmListener listener;
+ public final String listenerTag;
+ public final String statsTag;
public final WorkSource workSource;
public final int flags;
public final AlarmManager.AlarmClockInfo alarmClock;
public final int uid;
+ public final int creatorUid;
+ public final String packageName;
public int count;
public long when;
public long windowLength;
@@ -1961,8 +2122,9 @@
public PriorityClass priorityClass;
public Alarm(int _type, long _when, long _whenElapsed, long _windowLength, long _maxWhen,
- long _interval, PendingIntent _op, WorkSource _ws, int _flags,
- AlarmManager.AlarmClockInfo _info, int _uid) {
+ long _interval, PendingIntent _op, IAlarmListener _rec, String _listenerTag,
+ WorkSource _ws, int _flags, AlarmManager.AlarmClockInfo _info,
+ int _uid, String _pkgName) {
type = _type;
origWhen = _when;
wakeup = _type == AlarmManager.ELAPSED_REALTIME_WAKEUP
@@ -1973,16 +2135,42 @@
maxWhenElapsed = _maxWhen;
repeatInterval = _interval;
operation = _op;
- tag = makeTag(_op, _type);
+ listener = _rec;
+ listenerTag = _listenerTag;
+ statsTag = makeTag(_op, _listenerTag, _type);
workSource = _ws;
flags = _flags;
alarmClock = _info;
uid = _uid;
+ packageName = _pkgName;
+
+ creatorUid = (operation != null) ? operation.getCreatorUid() : uid;
}
- public static String makeTag(PendingIntent pi, int type) {
- return pi.getTag(type == ELAPSED_REALTIME_WAKEUP || type == RTC_WAKEUP
- ? "*walarm*:" : "*alarm*:");
+ public static String makeTag(PendingIntent pi, String tag, int type) {
+ final String alarmString = type == ELAPSED_REALTIME_WAKEUP || type == RTC_WAKEUP
+ ? "*walarm*:" : "*alarm*:";
+ return (pi != null) ? pi.getTag(alarmString) : (alarmString + tag);
+ }
+
+ public WakeupEvent makeWakeupEvent(long nowRTC) {
+ return new WakeupEvent(nowRTC, creatorUid,
+ (operation != null)
+ ? operation.getIntent().getAction()
+ : ("<listener>:" + listenerTag));
+ }
+
+ // Returns true if either matches
+ public boolean matches(PendingIntent pi, IAlarmListener rec) {
+ return (operation != null)
+ ? operation.equals(pi)
+ : listener.asBinder().equals(rec.asBinder());
+ }
+
+ public boolean matches(String packageName) {
+ return (operation != null)
+ ? packageName.equals(operation.getTargetPackage())
+ : packageName.equals(this.packageName);
}
@Override
@@ -1995,7 +2183,11 @@
sb.append(" when ");
sb.append(when);
sb.append(" ");
- sb.append(operation.getTargetPackage());
+ if (operation != null) {
+ sb.append(operation.getTargetPackage());
+ } else {
+ sb.append(listener.asBinder().toString());
+ }
sb.append('}');
return sb.toString();
}
@@ -2003,14 +2195,15 @@
public void dump(PrintWriter pw, String prefix, long nowRTC, long nowELAPSED,
SimpleDateFormat sdf) {
final boolean isRtc = (type == RTC || type == RTC_WAKEUP);
- pw.print(prefix); pw.print("tag="); pw.println(tag);
+ pw.print(prefix); pw.print("tag="); pw.println(statsTag);
pw.print(prefix); pw.print("type="); pw.print(type);
pw.print(" whenElapsed="); TimeUtils.formatDuration(whenElapsed,
nowELAPSED, pw);
+ pw.print(" when=");
if (isRtc) {
- pw.print(" when="); pw.print(sdf.format(new Date(when)));
+ pw.print(sdf.format(new Date(when)));
} else {
- pw.print(" when="); TimeUtils.formatDuration(when, nowELAPSED, pw);
+ TimeUtils.formatDuration(when, nowELAPSED, pw);
}
pw.println();
pw.print(prefix); pw.print("window="); TimeUtils.formatDuration(windowLength, pw);
@@ -2024,6 +2217,9 @@
pw.print(prefix); pw.print(" showIntent="); pw.println(alarmClock.getShowIntent());
}
pw.print(prefix); pw.print("operation="); pw.println(operation);
+ if (listener != null) {
+ pw.print(prefix); pw.print("listener="); pw.println(listener.asBinder());
+ }
}
}
@@ -2038,10 +2234,7 @@
final int numAlarms = b.alarms.size();
for (int nextAlarm = 0; nextAlarm < numAlarms; nextAlarm++) {
Alarm a = b.alarms.get(nextAlarm);
- WakeupEvent e = new WakeupEvent(nowRTC,
- a.operation.getCreatorUid(),
- a.operation.getIntent().getAction());
- mRecentWakeups.add(e);
+ mRecentWakeups.add(a.makeWakeupEvent(nowRTC));
}
}
}
@@ -2104,71 +2297,14 @@
if (alarm.workSource != null && alarm.workSource.size() > 0) {
for (int wi=0; wi<alarm.workSource.size(); wi++) {
ActivityManagerNative.noteAlarmStart(
- alarm.operation, alarm.workSource.get(wi), alarm.tag);
+ alarm.operation, alarm.workSource.get(wi), alarm.statsTag);
}
} else {
ActivityManagerNative.noteAlarmStart(
- alarm.operation, -1, alarm.tag);
+ alarm.operation, alarm.uid, alarm.statsTag);
}
}
- alarm.operation.send(getContext(), 0,
- mBackgroundIntent.putExtra(
- Intent.EXTRA_ALARM_COUNT, alarm.count),
- mResultReceiver, mHandler, null, allowWhileIdle ? mIdleOptions : null);
-
- // we have an active broadcast so stay awake.
- if (mBroadcastRefCount == 0) {
- setWakelockWorkSource(alarm.operation, alarm.workSource,
- alarm.type, alarm.tag, true);
- mWakeLock.acquire();
- }
- final InFlight inflight = new InFlight(AlarmManagerService.this,
- alarm.operation, alarm.workSource, alarm.type, alarm.tag, nowELAPSED);
- mInFlight.add(inflight);
- mBroadcastRefCount++;
-
- if (allowWhileIdle) {
- // Record the last time this uid handled an ALLOW_WHILE_IDLE alarm.
- mLastAllowWhileIdleDispatch.put(alarm.uid, nowELAPSED);
- }
-
- final BroadcastStats bs = inflight.mBroadcastStats;
- bs.count++;
- if (bs.nesting == 0) {
- bs.nesting = 1;
- bs.startTime = nowELAPSED;
- } else {
- bs.nesting++;
- }
- final FilterStats fs = inflight.mFilterStats;
- fs.count++;
- if (fs.nesting == 0) {
- fs.nesting = 1;
- fs.startTime = nowELAPSED;
- } else {
- fs.nesting++;
- }
- if (alarm.type == ELAPSED_REALTIME_WAKEUP
- || alarm.type == RTC_WAKEUP) {
- bs.numWakeup++;
- fs.numWakeup++;
- if (alarm.workSource != null && alarm.workSource.size() > 0) {
- for (int wi=0; wi<alarm.workSource.size(); wi++) {
- ActivityManagerNative.noteWakeupAlarm(
- alarm.operation, alarm.workSource.get(wi),
- alarm.workSource.getName(wi), alarm.tag);
- }
- } else {
- ActivityManagerNative.noteWakeupAlarm(
- alarm.operation, -1, null, alarm.tag);
- }
- }
- } catch (PendingIntent.CanceledException e) {
- if (alarm.repeatInterval > 0) {
- // This IntentSender is no longer valid, but this
- // is a repeating alarm, so toss the hoser.
- removeImpl(alarm.operation);
- }
+ mDeliveryTracker.deliverLocked(alarm, nowELAPSED, allowWhileIdle);
} catch (RuntimeException e) {
Slog.w(TAG, "Failure sending alarm.", e);
}
@@ -2298,9 +2434,10 @@
* Attribute blame for a WakeLock.
* @param pi PendingIntent to attribute blame to if ws is null.
* @param ws WorkSource to attribute blame.
+ * @param knownUid attribution uid; < 0 if we need to derive it from the PendingIntent sender
*/
void setWakelockWorkSource(PendingIntent pi, WorkSource ws, int type, String tag,
- boolean first) {
+ int knownUid, boolean first) {
try {
final boolean unimportant = pi == mTimeTickSender;
mWakeLock.setUnimportantForLogging(unimportant);
@@ -2315,8 +2452,9 @@
return;
}
- final int uid = ActivityManagerNative.getDefault()
- .getUidForIntentSender(pi.getTarget());
+ final int uid = (knownUid >= 0)
+ ? knownUid
+ : ActivityManagerNative.getDefault().getUidForIntentSender(pi.getTarget());
if (uid >= 0) {
mWakeLock.setWorkSource(new WorkSource(uid));
return;
@@ -2333,35 +2471,49 @@
public static final int MINUTE_CHANGE_EVENT = 2;
public static final int DATE_CHANGE_EVENT = 3;
public static final int SEND_NEXT_ALARM_CLOCK_CHANGED = 4;
+ public static final int LISTENER_TIMEOUT = 5;
public AlarmHandler() {
}
public void handleMessage(Message msg) {
- if (msg.what == ALARM_EVENT) {
- ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
- synchronized (mLock) {
- final long nowRTC = System.currentTimeMillis();
- final long nowELAPSED = SystemClock.elapsedRealtime();
- triggerAlarmsLocked(triggerList, nowELAPSED, nowRTC);
- updateNextAlarmClockLocked();
- }
+ switch (msg.what) {
+ case ALARM_EVENT: {
+ ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
+ synchronized (mLock) {
+ final long nowRTC = System.currentTimeMillis();
+ final long nowELAPSED = SystemClock.elapsedRealtime();
+ triggerAlarmsLocked(triggerList, nowELAPSED, nowRTC);
+ updateNextAlarmClockLocked();
+ }
- // now trigger the alarms without the lock held
- for (int i=0; i<triggerList.size(); i++) {
- Alarm alarm = triggerList.get(i);
- try {
- alarm.operation.send();
- } catch (PendingIntent.CanceledException e) {
- if (alarm.repeatInterval > 0) {
- // This IntentSender is no longer valid, but this
- // is a repeating alarm, so toss the hoser.
- removeImpl(alarm.operation);
+ // now trigger the alarms without the lock held
+ for (int i=0; i<triggerList.size(); i++) {
+ Alarm alarm = triggerList.get(i);
+ try {
+ alarm.operation.send();
+ } catch (PendingIntent.CanceledException e) {
+ if (alarm.repeatInterval > 0) {
+ // This IntentSender is no longer valid, but this
+ // is a repeating alarm, so toss the hoser.
+ removeImpl(alarm.operation);
+ }
}
}
+ break;
}
- } else if (msg.what == SEND_NEXT_ALARM_CLOCK_CHANGED) {
- sendNextAlarmClockChanged();
+
+ case SEND_NEXT_ALARM_CLOCK_CHANGED:
+ sendNextAlarmClockChanged();
+ break;
+
+ case LISTENER_TIMEOUT:
+ mDeliveryTracker.alarmTimedOut((IBinder) msg.obj);
+ break;
+
+ default:
+ // nope, just ignore it
+ break;
}
}
}
@@ -2403,8 +2555,8 @@
final WorkSource workSource = null; // Let system take blame for time tick events.
setImpl(ELAPSED_REALTIME, SystemClock.elapsedRealtime() + tickEventDelay, 0,
- 0, mTimeTickSender, AlarmManager.FLAG_STANDALONE, workSource, null,
- Process.myUid());
+ 0, mTimeTickSender, null, null, AlarmManager.FLAG_STANDALONE, workSource,
+ null, Process.myUid(), "android");
}
public void scheduleDateChangedEvent() {
@@ -2417,8 +2569,9 @@
calendar.add(Calendar.DAY_OF_MONTH, 1);
final WorkSource workSource = null; // Let system take blame for date change events.
- setImpl(RTC, calendar.getTimeInMillis(), 0, 0, mDateChangeSender,
- AlarmManager.FLAG_STANDALONE, workSource, null, Process.myUid());
+ setImpl(RTC, calendar.getTimeInMillis(), 0, 0, mDateChangeSender, null, null,
+ AlarmManager.FLAG_STANDALONE, workSource, null,
+ Process.myUid(), "android");
}
}
@@ -2516,80 +2669,260 @@
private final BroadcastStats getStatsLocked(PendingIntent pi) {
String pkg = pi.getCreatorPackage();
int uid = pi.getCreatorUid();
+ return getStatsLocked(uid, pkg);
+ }
+
+ private final BroadcastStats getStatsLocked(int uid, String pkgName) {
ArrayMap<String, BroadcastStats> uidStats = mBroadcastStats.get(uid);
if (uidStats == null) {
uidStats = new ArrayMap<String, BroadcastStats>();
mBroadcastStats.put(uid, uidStats);
}
- BroadcastStats bs = uidStats.get(pkg);
+ BroadcastStats bs = uidStats.get(pkgName);
if (bs == null) {
- bs = new BroadcastStats(uid, pkg);
- uidStats.put(pkg, bs);
+ bs = new BroadcastStats(uid, pkgName);
+ uidStats.put(pkgName, bs);
}
return bs;
}
- class ResultReceiver implements PendingIntent.OnFinished {
+ class DeliveryTracker extends IAlarmCompleteListener.Stub implements PendingIntent.OnFinished {
+ private InFlight removeLocked(PendingIntent pi, Intent intent) {
+ for (int i = 0; i < mInFlight.size(); i++) {
+ if (mInFlight.get(i).mPendingIntent == pi) {
+ return mInFlight.remove(i);
+ }
+ }
+ mLog.w("No in-flight alarm for " + pi + " " + intent);
+ return null;
+ }
+
+ private InFlight removeLocked(IBinder listener) {
+ for (int i = 0; i < mInFlight.size(); i++) {
+ if (mInFlight.get(i).mListener == listener) {
+ return mInFlight.remove(i);
+ }
+ }
+ mLog.w("No in-flight alarm for listener " + listener);
+ return null;
+ }
+
+ private void updateStatsLocked(InFlight inflight) {
+ final long nowELAPSED = SystemClock.elapsedRealtime();
+ BroadcastStats bs = inflight.mBroadcastStats;
+ bs.nesting--;
+ if (bs.nesting <= 0) {
+ bs.nesting = 0;
+ bs.aggregateTime += nowELAPSED - bs.startTime;
+ }
+ FilterStats fs = inflight.mFilterStats;
+ fs.nesting--;
+ if (fs.nesting <= 0) {
+ fs.nesting = 0;
+ fs.aggregateTime += nowELAPSED - fs.startTime;
+ }
+ if (RECORD_ALARMS_IN_HISTORY) {
+ if (inflight.mWorkSource != null && inflight.mWorkSource.size() > 0) {
+ for (int wi=0; wi<inflight.mWorkSource.size(); wi++) {
+ ActivityManagerNative.noteAlarmFinish(
+ inflight.mPendingIntent, inflight.mWorkSource.get(wi), inflight.mTag);
+ }
+ } else {
+ ActivityManagerNative.noteAlarmFinish(
+ inflight.mPendingIntent, inflight.mUid, inflight.mTag);
+ }
+ }
+ }
+
+ private void updateTrackingLocked(InFlight inflight) {
+ if (inflight != null) {
+ updateStatsLocked(inflight);
+ }
+ mBroadcastRefCount--;
+ if (mBroadcastRefCount == 0) {
+ mWakeLock.release();
+ if (mInFlight.size() > 0) {
+ mLog.w("Finished all dispatches with " + mInFlight.size()
+ + " remaining inflights");
+ for (int i=0; i<mInFlight.size(); i++) {
+ mLog.w(" Remaining #" + i + ": " + mInFlight.get(i));
+ }
+ mInFlight.clear();
+ }
+ } else {
+ // the next of our alarms is now in flight. reattribute the wakelock.
+ if (mInFlight.size() > 0) {
+ InFlight inFlight = mInFlight.get(0);
+ setWakelockWorkSource(inFlight.mPendingIntent, inFlight.mWorkSource,
+ inFlight.mAlarmType, inFlight.mTag, -1, false);
+ } else {
+ // should never happen
+ mLog.w("Alarm wakelock still held but sent queue empty");
+ mWakeLock.setWorkSource(null);
+ }
+ }
+ }
+
+ /**
+ * Callback that arrives when a direct-call alarm reports that delivery has finished
+ */
+ @Override
+ public void alarmComplete(IBinder who) {
+ if (who == null) {
+ Slog.w(TAG, "Invalid alarmComplete: uid=" + Binder.getCallingUid()
+ + " pid=" + Binder.getCallingPid());
+ return;
+ }
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ mHandler.removeMessages(AlarmHandler.LISTENER_TIMEOUT, who);
+ InFlight inflight = removeLocked(who);
+ if (inflight != null) {
+ if (DEBUG_LISTENER_CALLBACK) {
+ Slog.i(TAG, "alarmComplete() from " + who);
+ }
+ updateTrackingLocked(inflight);
+ } else {
+ // Delivery timed out, and the timeout handling already took care of
+ // updating our tracking here, so we needn't do anything further.
+ if (DEBUG_LISTENER_CALLBACK) {
+ Slog.i(TAG, "Late alarmComplete() from " + who);
+ }
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ /**
+ * Callback that arrives when a PendingIntent alarm has finished delivery
+ */
+ @Override
public void onSendFinished(PendingIntent pi, Intent intent, int resultCode,
String resultData, Bundle resultExtras) {
synchronized (mLock) {
- InFlight inflight = null;
- for (int i=0; i<mInFlight.size(); i++) {
- if (mInFlight.get(i).mPendingIntent == pi) {
- inflight = mInFlight.remove(i);
- break;
- }
- }
+ updateTrackingLocked(removeLocked(pi, intent));
+ }
+ }
+
+ /**
+ * Timeout of a direct-call alarm delivery
+ */
+ public void alarmTimedOut(IBinder who) {
+ synchronized (mLock) {
+ InFlight inflight = removeLocked(who);
if (inflight != null) {
- final long nowELAPSED = SystemClock.elapsedRealtime();
- BroadcastStats bs = inflight.mBroadcastStats;
- bs.nesting--;
- if (bs.nesting <= 0) {
- bs.nesting = 0;
- bs.aggregateTime += nowELAPSED - bs.startTime;
+ // TODO: implement ANR policy for the target
+ if (DEBUG_LISTENER_CALLBACK) {
+ Slog.i(TAG, "Alarm listener " + who + " timed out in delivery");
}
- FilterStats fs = inflight.mFilterStats;
- fs.nesting--;
- if (fs.nesting <= 0) {
- fs.nesting = 0;
- fs.aggregateTime += nowELAPSED - fs.startTime;
- }
- if (RECORD_ALARMS_IN_HISTORY) {
- if (inflight.mWorkSource != null && inflight.mWorkSource.size() > 0) {
- for (int wi=0; wi<inflight.mWorkSource.size(); wi++) {
- ActivityManagerNative.noteAlarmFinish(
- pi, inflight.mWorkSource.get(wi), inflight.mTag);
- }
- } else {
- ActivityManagerNative.noteAlarmFinish(
- pi, -1, inflight.mTag);
- }
- }
+ updateTrackingLocked(inflight);
} else {
- mLog.w("No in-flight alarm for " + pi + " " + intent);
+ if (DEBUG_LISTENER_CALLBACK) {
+ Slog.i(TAG, "Spurious timeout of listener " + who);
+ }
}
- mBroadcastRefCount--;
- if (mBroadcastRefCount == 0) {
- mWakeLock.release();
- if (mInFlight.size() > 0) {
- mLog.w("Finished all broadcasts with " + mInFlight.size()
- + " remaining inflights");
- for (int i=0; i<mInFlight.size(); i++) {
- mLog.w(" Remaining #" + i + ": " + mInFlight.get(i));
- }
- mInFlight.clear();
+ }
+ }
+
+ /**
+ * Deliver an alarm and set up the post-delivery handling appropriately
+ */
+ public void deliverLocked(Alarm alarm, long nowELAPSED, boolean allowWhileIdle) {
+ if (alarm.operation != null) {
+ // PendingIntent alarm
+ try {
+ alarm.operation.send(getContext(), 0,
+ mBackgroundIntent.putExtra(
+ Intent.EXTRA_ALARM_COUNT, alarm.count),
+ mDeliveryTracker, mHandler, null,
+ allowWhileIdle ? mIdleOptions : null);
+ } catch (PendingIntent.CanceledException e) {
+ if (alarm.repeatInterval > 0) {
+ // This IntentSender is no longer valid, but this
+ // is a repeating alarm, so toss it
+ removeImpl(alarm.operation);
+ }
+ }
+ } else {
+ // Direct listener callback alarm
+ try {
+ if (DEBUG_LISTENER_CALLBACK) {
+ Slog.v(TAG, "Alarm to uid=" + alarm.uid
+ + " listener=" + alarm.listener.asBinder());
+ }
+ alarm.listener.doAlarm(this);
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(AlarmHandler.LISTENER_TIMEOUT,
+ alarm.listener.asBinder()),
+ mConstants.LISTENER_TIMEOUT);
+ } catch (Exception e) {
+ if (DEBUG_LISTENER_CALLBACK) {
+ Slog.i(TAG, "Alarm undeliverable to listener "
+ + alarm.listener.asBinder(), e);
+ }
+ }
+ }
+
+ // The alarm is now in flight; now arrange wakelock and stats tracking
+ if (mBroadcastRefCount == 0) {
+ setWakelockWorkSource(alarm.operation, alarm.workSource,
+ alarm.type, alarm.statsTag, (alarm.operation == null) ? alarm.uid : -1,
+ true);
+ mWakeLock.acquire();
+ }
+ final InFlight inflight = new InFlight(AlarmManagerService.this,
+ alarm.operation, alarm.listener, alarm.workSource, alarm.uid,
+ alarm.packageName, alarm.type, alarm.statsTag, nowELAPSED);
+ mInFlight.add(inflight);
+ mBroadcastRefCount++;
+
+ if (allowWhileIdle) {
+ // Record the last time this uid handled an ALLOW_WHILE_IDLE alarm.
+ mLastAllowWhileIdleDispatch.put(alarm.uid, nowELAPSED);
+ if (RECORD_DEVICE_IDLE_ALARMS) {
+ IdleDispatchEntry ent = new IdleDispatchEntry();
+ ent.uid = alarm.uid;
+ ent.pkg = alarm.packageName;
+ ent.tag = alarm.statsTag;
+ ent.op = "DELIVER";
+ ent.elapsedRealtime = nowELAPSED;
+ mAllowWhileIdleDispatches.add(ent);
+ }
+ }
+
+ final BroadcastStats bs = inflight.mBroadcastStats;
+ bs.count++;
+ if (bs.nesting == 0) {
+ bs.nesting = 1;
+ bs.startTime = nowELAPSED;
+ } else {
+ bs.nesting++;
+ }
+ final FilterStats fs = inflight.mFilterStats;
+ fs.count++;
+ if (fs.nesting == 0) {
+ fs.nesting = 1;
+ fs.startTime = nowELAPSED;
+ } else {
+ fs.nesting++;
+ }
+ if (alarm.type == ELAPSED_REALTIME_WAKEUP
+ || alarm.type == RTC_WAKEUP) {
+ bs.numWakeup++;
+ fs.numWakeup++;
+ if (alarm.workSource != null && alarm.workSource.size() > 0) {
+ for (int wi=0; wi<alarm.workSource.size(); wi++) {
+ ActivityManagerNative.noteWakeupAlarm(
+ alarm.operation, alarm.workSource.get(wi),
+ alarm.workSource.getName(wi), alarm.statsTag);
}
} else {
- // the next of our alarms is now in flight. reattribute the wakelock.
- if (mInFlight.size() > 0) {
- InFlight inFlight = mInFlight.get(0);
- setWakelockWorkSource(inFlight.mPendingIntent, inFlight.mWorkSource,
- inFlight.mAlarmType, inFlight.mTag, false);
- } else {
- // should never happen
- mLog.w("Alarm wakelock still held but sent queue empty");
- mWakeLock.setWorkSource(null);
- }
+ ActivityManagerNative.noteWakeupAlarm(
+ alarm.operation, -1, alarm.packageName, alarm.statsTag);
}
}
}
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index c373fb8..96c1e2a 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -39,7 +39,6 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.media.AudioAttributes;
import android.os.AsyncTask;
import android.os.Binder;
@@ -263,12 +262,12 @@
Iterator<Ops> it = pkgs.values().iterator();
while (it.hasNext()) {
Ops ops = it.next();
- int curUid;
+ int curUid = -1;
try {
- curUid = mContext.getPackageManager().getPackageUid(ops.packageName,
+ curUid = AppGlobals.getPackageManager().getPackageUidEtc(ops.packageName,
+ PackageManager.GET_UNINSTALLED_PACKAGES,
UserHandle.getUserId(ops.uidState.uid));
- } catch (NameNotFoundException e) {
- curUid = -1;
+ } catch (RemoteException ignored) {
}
if (curUid != ops.uidState.uid) {
Slog.i(TAG, "Pruning old package " + ops.packageName
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 2eeaec9..48e96aa 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -19,6 +19,8 @@
import android.database.ContentObserver;
import android.os.BatteryStats;
+import android.os.ResultReceiver;
+import android.os.ShellCommand;
import com.android.internal.app.IBatteryStats;
import com.android.server.am.BatteryStatsService;
import com.android.server.lights.Light;
@@ -96,7 +98,6 @@
// discharge stats before the device dies.
private int mCriticalBatteryLevel;
- private static final int DUMP_MAX_LENGTH = 24 * 1024;
private static final String[] DUMPSYS_ARGS = new String[] { "--checkin", "--unplugged" };
private static final String DUMPSYS_DATA_PATH = "/data/system/";
@@ -106,6 +107,7 @@
private final Context mContext;
private final IBatteryStats mBatteryStats;
+ BinderService mBinderService;
private final Handler mHandler;
private final Object mLock = new Object();
@@ -121,6 +123,7 @@
private int mLastBatteryTemperature;
private boolean mLastBatteryLevelCritical;
private int mLastMaxChargingCurrent;
+ private int mLastMaxChargingVoltage;
private int mInvalidCharger;
private int mLastInvalidCharger;
@@ -162,7 +165,18 @@
// watch for invalid charger messages if the invalid_charger switch exists
if (new File("/sys/devices/virtual/switch/invalid_charger/state").exists()) {
- mInvalidChargerObserver.startObserving(
+ UEventObserver invalidChargerObserver = new UEventObserver() {
+ @Override
+ public void onUEvent(UEvent event) {
+ final int invalidCharger = "1".equals(event.get("SWITCH_STATE")) ? 1 : 0;
+ synchronized (mLock) {
+ if (mInvalidCharger != invalidCharger) {
+ mInvalidCharger = invalidCharger;
+ }
+ }
+ }
+ };
+ invalidChargerObserver.startObserving(
"DEVPATH=/devices/virtual/switch/invalid_charger");
}
}
@@ -178,7 +192,8 @@
// Should never happen.
}
- publishBinderService("battery", new BinderService());
+ mBinderService = new BinderService();
+ publishBinderService("battery", mBinderService);
publishLocalService(BatteryManagerInternal.class, new LocalService());
}
@@ -325,6 +340,7 @@
+ ", chargerUsbOnline=" + mBatteryProps.chargerUsbOnline
+ ", chargerWirelessOnline=" + mBatteryProps.chargerWirelessOnline
+ ", maxChargingCurrent" + mBatteryProps.maxChargingCurrent
+ + ", maxChargingVoltage" + mBatteryProps.maxChargingVoltage
+ ", batteryStatus=" + mBatteryProps.batteryStatus
+ ", batteryHealth=" + mBatteryProps.batteryHealth
+ ", batteryPresent=" + mBatteryProps.batteryPresent
@@ -356,6 +372,7 @@
mBatteryProps.batteryVoltage != mLastBatteryVoltage ||
mBatteryProps.batteryTemperature != mLastBatteryTemperature ||
mBatteryProps.maxChargingCurrent != mLastMaxChargingCurrent ||
+ mBatteryProps.maxChargingVoltage != mLastMaxChargingVoltage ||
mInvalidCharger != mLastInvalidCharger)) {
if (mPlugType != mLastPlugType) {
@@ -483,6 +500,7 @@
mLastBatteryVoltage = mBatteryProps.batteryVoltage;
mLastBatteryTemperature = mBatteryProps.batteryTemperature;
mLastMaxChargingCurrent = mBatteryProps.maxChargingCurrent;
+ mLastMaxChargingVoltage = mBatteryProps.maxChargingVoltage;
mLastBatteryLevelCritical = mBatteryLevelCritical;
mLastInvalidCharger = mInvalidCharger;
}
@@ -508,7 +526,7 @@
intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mBatteryProps.batteryTechnology);
intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger);
intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_CURRENT, mBatteryProps.maxChargingCurrent);
-
+ intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE, mBatteryProps.maxChargingVoltage);
if (DEBUG) {
Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED. level:" + mBatteryProps.batteryLevel +
", scale:" + BATTERY_SCALE + ", status:" + mBatteryProps.batteryStatus +
@@ -521,7 +539,8 @@
", USB powered:" + mBatteryProps.chargerUsbOnline +
", Wireless powered:" + mBatteryProps.chargerWirelessOnline +
", icon:" + icon + ", invalid charger:" + mInvalidCharger +
- ", maxChargingCurrent:" + mBatteryProps.maxChargingCurrent);
+ ", maxChargingCurrent:" + mBatteryProps.maxChargingCurrent +
+ ", maxChargingVoltage:" + mBatteryProps.maxChargingVoltage);
}
mHandler.post(new Runnable() {
@@ -593,7 +612,6 @@
} catch (NumberFormatException e) {
Slog.e(TAG, "Invalid DischargeThresholds GService string: " +
durationThresholdString + " or " + dischargeThresholdString);
- return;
}
}
}
@@ -616,27 +634,40 @@
}
}
- private void dumpInternal(PrintWriter pw, String[] args) {
- synchronized (mLock) {
- if (args == null || args.length == 0 || "-a".equals(args[0])) {
- pw.println("Current Battery Service state:");
- if (mUpdatesStopped) {
- pw.println(" (UPDATES STOPPED -- use 'reset' to restart)");
- }
- pw.println(" AC powered: " + mBatteryProps.chargerAcOnline);
- pw.println(" USB powered: " + mBatteryProps.chargerUsbOnline);
- pw.println(" Wireless powered: " + mBatteryProps.chargerWirelessOnline);
- pw.println(" Max charging current: " + mBatteryProps.maxChargingCurrent);
- pw.println(" status: " + mBatteryProps.batteryStatus);
- pw.println(" health: " + mBatteryProps.batteryHealth);
- pw.println(" present: " + mBatteryProps.batteryPresent);
- pw.println(" level: " + mBatteryProps.batteryLevel);
- pw.println(" scale: " + BATTERY_SCALE);
- pw.println(" voltage: " + mBatteryProps.batteryVoltage);
- pw.println(" temperature: " + mBatteryProps.batteryTemperature);
- pw.println(" technology: " + mBatteryProps.batteryTechnology);
+ class Shell extends ShellCommand {
+ @Override
+ public int onCommand(String cmd) {
+ return onShellCommand(this, cmd);
+ }
- } else if ("unplug".equals(args[0])) {
+ @Override
+ public void onHelp() {
+ PrintWriter pw = getOutPrintWriter();
+ dumpHelp(pw);
+ }
+ }
+
+ static void dumpHelp(PrintWriter pw) {
+ pw.println("Battery service (battery) commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println(" set [ac|usb|wireless|status|level|invalid] <value>");
+ pw.println(" Force a battery property value, freezing battery state.");
+ pw.println(" unplug");
+ pw.println(" Force battery unplugged, freezing battery state.");
+ pw.println(" reset");
+ pw.println(" Unfreeze battery state, returning to current hardware values.");
+ }
+
+ int onShellCommand(Shell shell, String cmd) {
+ if (cmd == null) {
+ return shell.handleDefaultCommands(cmd);
+ }
+ PrintWriter pw = shell.getOutPrintWriter();
+ switch (cmd) {
+ case "unplug": {
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.DEVICE_POWER, null);
if (!mUpdatesStopped) {
mLastBatteryProps.set(mBatteryProps);
}
@@ -650,30 +681,50 @@
} finally {
Binder.restoreCallingIdentity(ident);
}
+ } break;
+ case "set": {
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.DEVICE_POWER, null);
+ final String key = shell.getNextArg();
+ if (key == null) {
+ pw.println("No property specified");
+ return -1;
- } else if (args.length == 3 && "set".equals(args[0])) {
- String key = args[1];
- String value = args[2];
+ }
+ final String value = shell.getNextArg();
+ if (value == null) {
+ pw.println("No value specified");
+ return -1;
+
+ }
try {
if (!mUpdatesStopped) {
mLastBatteryProps.set(mBatteryProps);
}
boolean update = true;
- if ("ac".equals(key)) {
- mBatteryProps.chargerAcOnline = Integer.parseInt(value) != 0;
- } else if ("usb".equals(key)) {
- mBatteryProps.chargerUsbOnline = Integer.parseInt(value) != 0;
- } else if ("wireless".equals(key)) {
- mBatteryProps.chargerWirelessOnline = Integer.parseInt(value) != 0;
- } else if ("status".equals(key)) {
- mBatteryProps.batteryStatus = Integer.parseInt(value);
- } else if ("level".equals(key)) {
- mBatteryProps.batteryLevel = Integer.parseInt(value);
- } else if ("invalid".equals(key)) {
- mInvalidCharger = Integer.parseInt(value);
- } else {
- pw.println("Unknown set option: " + key);
- update = false;
+ switch (key) {
+ case "ac":
+ mBatteryProps.chargerAcOnline = Integer.parseInt(value) != 0;
+ break;
+ case "usb":
+ mBatteryProps.chargerUsbOnline = Integer.parseInt(value) != 0;
+ break;
+ case "wireless":
+ mBatteryProps.chargerWirelessOnline = Integer.parseInt(value) != 0;
+ break;
+ case "status":
+ mBatteryProps.batteryStatus = Integer.parseInt(value);
+ break;
+ case "level":
+ mBatteryProps.batteryLevel = Integer.parseInt(value);
+ break;
+ case "invalid":
+ mInvalidCharger = Integer.parseInt(value);
+ break;
+ default:
+ pw.println("Unknown set option: " + key);
+ update = false;
+ break;
}
if (update) {
long ident = Binder.clearCallingIdentity();
@@ -686,9 +737,12 @@
}
} catch (NumberFormatException ex) {
pw.println("Bad value: " + value);
+ return -1;
}
-
- } else if (args.length == 1 && "reset".equals(args[0])) {
+ } break;
+ case "reset": {
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.DEVICE_POWER, null);
long ident = Binder.clearCallingIdentity();
try {
if (mUpdatesStopped) {
@@ -699,27 +753,40 @@
} finally {
Binder.restoreCallingIdentity(ident);
}
+ } break;
+ default:
+ return shell.handleDefaultCommands(cmd);
+ }
+ return 0;
+ }
+
+ private void dumpInternal(FileDescriptor fd, PrintWriter pw, String[] args) {
+ synchronized (mLock) {
+ if (args == null || args.length == 0 || "-a".equals(args[0])) {
+ pw.println("Current Battery Service state:");
+ if (mUpdatesStopped) {
+ pw.println(" (UPDATES STOPPED -- use 'reset' to restart)");
+ }
+ pw.println(" AC powered: " + mBatteryProps.chargerAcOnline);
+ pw.println(" USB powered: " + mBatteryProps.chargerUsbOnline);
+ pw.println(" Wireless powered: " + mBatteryProps.chargerWirelessOnline);
+ pw.println(" Max charging current: " + mBatteryProps.maxChargingCurrent);
+ pw.println(" Max charging voltage: " + mBatteryProps.maxChargingVoltage);
+ pw.println(" status: " + mBatteryProps.batteryStatus);
+ pw.println(" health: " + mBatteryProps.batteryHealth);
+ pw.println(" present: " + mBatteryProps.batteryPresent);
+ pw.println(" level: " + mBatteryProps.batteryLevel);
+ pw.println(" scale: " + BATTERY_SCALE);
+ pw.println(" voltage: " + mBatteryProps.batteryVoltage);
+ pw.println(" temperature: " + mBatteryProps.batteryTemperature);
+ pw.println(" technology: " + mBatteryProps.batteryTechnology);
} else {
- pw.println("Dump current battery state, or:");
- pw.println(" set [ac|usb|wireless|status|level|invalid] <value>");
- pw.println(" unplug");
- pw.println(" reset");
+ Shell shell = new Shell();
+ shell.exec(mBinderService, null, fd, null, args, new ResultReceiver(null));
}
}
}
- private final UEventObserver mInvalidChargerObserver = new UEventObserver() {
- @Override
- public void onUEvent(UEventObserver.UEvent event) {
- final int invalidCharger = "1".equals(event.get("SWITCH_STATE")) ? 1 : 0;
- synchronized (mLock) {
- if (mInvalidCharger != invalidCharger) {
- mInvalidCharger = invalidCharger;
- }
- }
- }
- };
-
private final class Led {
private final Light mBatteryLight;
@@ -776,8 +843,7 @@
}
private final class BatteryListener extends IBatteryPropertiesListener.Stub {
- @Override
- public void batteryPropertiesChanged(BatteryProperties props) {
+ @Override public void batteryPropertiesChanged(BatteryProperties props) {
final long identity = Binder.clearCallingIdentity();
try {
BatteryService.this.update(props);
@@ -788,8 +854,7 @@
}
private final class BinderService extends Binder {
- @Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
@@ -799,7 +864,12 @@
return;
}
- dumpInternal(pw, args);
+ dumpInternal(fd, pw, args);
+ }
+
+ @Override public void onShellCommand(FileDescriptor in, FileDescriptor out,
+ FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
+ (new Shell()).exec(this, in, out, err, args, resultReceiver);
}
}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index fd67b41..f329cff 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -95,8 +95,6 @@
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_SAVE_NAME_AND_ADDRESS=201;
private static final int MESSAGE_USER_SWITCHED = 300;
private static final int MESSAGE_ADD_PROXY_DELAYED = 400;
private static final int MESSAGE_BIND_PROFILE_SERVICE = 401;
@@ -587,15 +585,6 @@
}
}
- /** @hide*/
- public void getNameAndAddress() {
- if (DBG) {
- Log.d(TAG,"getNameAndAddress(): mBluetooth = " + mBluetooth +
- " mBinding = " + mBinding);
- }
- Message msg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
- mHandler.sendMessage(msg);
- }
public boolean enableNoAutoConnect()
{
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
@@ -681,14 +670,13 @@
if (mUnbinding) return;
mUnbinding = true;
if (mBluetooth != null) {
- if (!mConnection.isGetNameAddressOnly()) {
- //Unregister callback object
- try {
- mBluetooth.unregisterCallback(mBluetoothCallback);
- } catch (RemoteException re) {
- Log.e(TAG, "Unable to unregister BluetoothCallback",re);
- }
+ //Unregister callback object
+ try {
+ mBluetooth.unregisterCallback(mBluetoothCallback);
+ } catch (RemoteException re) {
+ Log.e(TAG, "Unable to unregister BluetoothCallback",re);
}
+
if (DBG) Log.d(TAG, "Sending unbind request.");
mBluetooth = null;
//Unbind
@@ -780,11 +768,6 @@
if (DBG) Log.d(TAG, "Auto-enabling Bluetooth.");
sendEnableMsg(mQuietEnableExternal);
}
- if (!isNameAndAddressSet()) {
- // Sync the Bluetooth name and address from the Bluetooth Adapter
- if (DBG) Log.d(TAG, "Retrieving Bluetooth Adapter name and address...");
- getNameAndAddress();
- }
}
/**
@@ -957,42 +940,38 @@
* Inform BluetoothAdapter instances that Adapter service is up
*/
private void sendBluetoothServiceUpCallback() {
- if (!mConnection.isGetNameAddressOnly()) {
- if (DBG) Log.d(TAG,"Calling onBluetoothServiceUp callbacks");
- try {
- int n = mCallbacks.beginBroadcast();
- Log.d(TAG,"Broadcasting onBluetoothServiceUp() to " + n + " receivers.");
- for (int i=0; i <n;i++) {
- try {
- mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth);
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e);
- }
+ if (DBG) Log.d(TAG,"Calling onBluetoothServiceUp callbacks");
+ try {
+ int n = mCallbacks.beginBroadcast();
+ Log.d(TAG,"Broadcasting onBluetoothServiceUp() to " + n + " receivers.");
+ for (int i=0; i <n;i++) {
+ try {
+ mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e);
}
- } finally {
- mCallbacks.finishBroadcast();
}
+ } finally {
+ mCallbacks.finishBroadcast();
}
}
/**
* Inform BluetoothAdapter instances that Adapter service is down
*/
private void sendBluetoothServiceDownCallback() {
- if (!mConnection.isGetNameAddressOnly()) {
- if (DBG) Log.d(TAG,"Calling onBluetoothServiceDown callbacks");
- try {
- int n = mCallbacks.beginBroadcast();
- Log.d(TAG,"Broadcasting onBluetoothServiceDown() to " + n + " receivers.");
- for (int i=0; i <n;i++) {
- try {
- mCallbacks.getBroadcastItem(i).onBluetoothServiceDown();
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to call onBluetoothServiceDown() on callback #" + i, e);
- }
+ if (DBG) Log.d(TAG,"Calling onBluetoothServiceDown callbacks");
+ try {
+ int n = mCallbacks.beginBroadcast();
+ Log.d(TAG,"Broadcasting onBluetoothServiceDown() to " + n + " receivers.");
+ for (int i=0; i <n;i++) {
+ try {
+ mCallbacks.getBroadcastItem(i).onBluetoothServiceDown();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to call onBluetoothServiceDown() on callback #" + i, e);
}
- } finally {
- mCallbacks.finishBroadcast();
}
+ } finally {
+ mCallbacks.finishBroadcast();
}
}
@@ -1052,17 +1031,6 @@
}
private class BluetoothServiceConnection implements ServiceConnection {
-
- private boolean mGetNameAddressOnly;
-
- public void setGetNameAddressOnly(boolean getOnly) {
- mGetNameAddressOnly = getOnly;
- }
-
- public boolean isGetNameAddressOnly() {
- return mGetNameAddressOnly;
- }
-
public void onServiceConnected(ComponentName className, IBinder service) {
if (DBG) Log.d(TAG, "BluetoothServiceConnection: " + className.getClassName());
Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_CONNECTED);
@@ -1108,104 +1076,6 @@
public void handleMessage(Message msg) {
if (DBG) Log.d (TAG, "Message: " + msg.what);
switch (msg.what) {
- case MESSAGE_GET_NAME_AND_ADDRESS: {
- if (DBG) Log.d(TAG,"MESSAGE_GET_NAME_AND_ADDRESS");
- synchronized(mConnection) {
- //Start bind request
- if ((mBluetooth == null) && (!mBinding)) {
- if (DBG) Log.d(TAG, "Binding to service to get name and address");
- mConnection.setGetNameAddressOnly(true);
- //Start bind timeout and bind
- 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 {
- Message saveMsg= mHandler.obtainMessage(MESSAGE_SAVE_NAME_AND_ADDRESS);
- saveMsg.arg1 = 0;
- if (mBluetooth != null) {
- mHandler.sendMessage(saveMsg);
- } else {
- // if enable is also called to bind the service
- // wait for MESSAGE_BLUETOOTH_SERVICE_CONNECTED
- mHandler.sendMessageDelayed(saveMsg, TIMEOUT_SAVE_MS);
- }
- }
- }
- break;
- }
- case MESSAGE_SAVE_NAME_AND_ADDRESS: {
- boolean unbind = false;
- if (DBG) Log.d(TAG,"MESSAGE_SAVE_NAME_AND_ADDRESS");
- synchronized(mConnection) {
- if (!mEnable && mBluetooth != null && !mConnection.isGetNameAddressOnly()) {
- try {
- mBluetooth.enable();
- } catch (RemoteException e) {
- Log.e(TAG,"Unable to call enable()",e);
- }
- }
- }
- if (mBluetooth != null && !mConnection.isGetNameAddressOnly()) waitForOnOff(true, false);
- synchronized(mConnection) {
- if (mBluetooth != null) {
- String name = null;
- String address = null;
- try {
- name = mBluetooth.getName();
- address = mBluetooth.getAddress();
- } catch (RemoteException re) {
- Log.e(TAG,"",re);
- }
-
- if (name != null && address != null) {
- storeNameAndAddress(name,address);
- if (mConnection.isGetNameAddressOnly()) {
- unbind = true;
- }
- } else {
- if (msg.arg1 < MAX_SAVE_RETRIES) {
- Message retryMsg = mHandler.obtainMessage(MESSAGE_SAVE_NAME_AND_ADDRESS);
- retryMsg.arg1= 1+msg.arg1;
- if (DBG) Log.d(TAG,"Retrying name/address remote retrieval and save.....Retry count =" + retryMsg.arg1);
- mHandler.sendMessageDelayed(retryMsg, TIMEOUT_SAVE_MS);
- } else {
- Log.w(TAG,"Maximum name/address remote retrieval retry exceeded");
- if (mConnection.isGetNameAddressOnly()) {
- unbind = true;
- }
- }
- }
- if (!mEnable && !mConnection.isGetNameAddressOnly()) {
- try {
- mBluetooth.disable();
- } catch (RemoteException e) {
- Log.e(TAG,"Unable to call disable()",e);
- }
- }
- } else {
- // rebind service by Request GET NAME AND ADDRESS
- // if service is unbinded by disable or
- // MESSAGE_BLUETOOTH_SERVICE_CONNECTED is not received
- Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
- mHandler.sendMessage(getMsg);
- }
- }
- if (!mEnable && mBluetooth != null && !mConnection.isGetNameAddressOnly()) {
- waitForOnOff(false, true);
- }
- if (unbind) {
- unbindAndFinish();
- }
- break;
- }
case MESSAGE_ENABLE:
if (DBG) {
Log.d(TAG, "MESSAGE_ENABLE: mBluetooth = " + mBluetooth);
@@ -1308,14 +1178,6 @@
Log.e(TAG,"Unable to call configHciSnoopLog", e);
}
- if (mConnection.isGetNameAddressOnly()) {
- //Request GET NAME AND ADDRESS
- Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
- mHandler.sendMessage(getMsg);
- if (!mEnable) return;
- }
-
- mConnection.setGetNameAddressOnly(false);
//Register callback object
try {
mBluetooth.registerCallback(mBluetoothCallback);
@@ -1412,25 +1274,23 @@
SERVICE_RESTART_TIME_MS);
}
- if (!mConnection.isGetNameAddressOnly()) {
- sendBluetoothServiceDownCallback();
+ sendBluetoothServiceDownCallback();
- // Send BT state broadcast to update
- // the BT icon correctly
- if ((mState == BluetoothAdapter.STATE_TURNING_ON) ||
- (mState == BluetoothAdapter.STATE_ON)) {
- bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON,
- BluetoothAdapter.STATE_TURNING_OFF);
- mState = BluetoothAdapter.STATE_TURNING_OFF;
- }
- if (mState == BluetoothAdapter.STATE_TURNING_OFF) {
- bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF,
- BluetoothAdapter.STATE_OFF);
- }
-
- mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
- mState = BluetoothAdapter.STATE_OFF;
+ // Send BT state broadcast to update
+ // the BT icon correctly
+ if ((mState == BluetoothAdapter.STATE_TURNING_ON) ||
+ (mState == BluetoothAdapter.STATE_ON)) {
+ bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON,
+ BluetoothAdapter.STATE_TURNING_OFF);
+ mState = BluetoothAdapter.STATE_TURNING_OFF;
}
+ if (mState == BluetoothAdapter.STATE_TURNING_OFF) {
+ bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF,
+ BluetoothAdapter.STATE_OFF);
+ }
+
+ mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
+ mState = BluetoothAdapter.STATE_OFF;
break;
}
case MESSAGE_RESTART_BLUETOOTH_SERVICE:
@@ -1539,7 +1399,6 @@
//Start bind timeout and bind
Message timeoutMsg=mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS);
- mConnection.setGetNameAddressOnly(false);
Intent i = new Intent(IBluetooth.class.getName());
if (!doBind(i, mConnection,Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
UserHandle.CURRENT)) {
@@ -1548,21 +1407,6 @@
mBinding = true;
}
} else if (mBluetooth != null) {
- if (mConnection.isGetNameAddressOnly()) {
- // if GetNameAddressOnly is set, we can clear this flag,
- // so the service won't be unbind
- // after name and address are saved
- mConnection.setGetNameAddressOnly(false);
- //Register callback object
- try {
- mBluetooth.registerCallback(mBluetoothCallback);
- } catch (RemoteException re) {
- Log.e(TAG, "Unable to register BluetoothCallback",re);
- }
- //Inform BluetoothAdapter instances that service is up
- sendBluetoothServiceUpCallback();
- }
-
//Enable bluetooth
try {
if (!mQuietEnable) {
@@ -1594,9 +1438,7 @@
private void handleDisable() {
synchronized(mConnection) {
- // don't need to disable if GetNameAddressOnly is set,
- // service will be unbinded after Name and Address are saved
- if ((mBluetooth != null) && (!mConnection.isGetNameAddressOnly())) {
+ if (mBluetooth != null) {
if (DBG) Log.d(TAG,"Sending off request.");
try {
diff --git a/services/core/java/com/android/server/BluetoothService.java b/services/core/java/com/android/server/BluetoothService.java
index 73e8c52..019d03d 100644
--- a/services/core/java/com/android/server/BluetoothService.java
+++ b/services/core/java/com/android/server/BluetoothService.java
@@ -31,14 +31,15 @@
@Override
public void onStart() {
- Log.d(TAG, "onStart: publishing BluetoothManagerService");
- publishBinderService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE, mBluetoothManagerService);
}
@Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
Log.d(TAG, "onBootPhase: PHASE_SYSTEM_SERVICES_READY");
+ publishBinderService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE, mBluetoothManagerService);
+ } else if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+ Log.d(TAG, "onBootPhase: PHASE_ACTIVITY_MANAGER_READY");
mBluetoothManagerService.handleOnBootPhase();
}
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index f914b20..c712a56 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1450,10 +1450,7 @@
}
private void enforceChangePermission() {
- int uid = Binder.getCallingUid();
- Settings.checkAndNoteChangeNetworkStateOperation(mContext, uid, Settings
- .getPackageNameForUid(mContext, uid), true);
-
+ ConnectivityManager.enforceChangePermission(mContext);
}
private void enforceTetherAccessPermission() {
@@ -3215,6 +3212,11 @@
final String profileName = new String(mKeyStore.get(Credentials.LOCKDOWN_VPN));
final VpnProfile profile = VpnProfile.decode(
profileName, mKeyStore.get(Credentials.VPN + profileName));
+ if (profile == null) {
+ Slog.e(TAG, "Lockdown VPN configured invalid profile " + profileName);
+ setLockdownTracker(null);
+ return true;
+ }
int user = UserHandle.getUserId(Binder.getCallingUid());
synchronized(mVpns) {
setLockdownTracker(new LockdownVpnTracker(mContext, mNetd, this, mVpns.get(user),
@@ -3361,7 +3363,7 @@
.setPriority(highPriority ?
Notification.PRIORITY_HIGH :
Notification.PRIORITY_DEFAULT)
- .setDefaults(Notification.DEFAULT_ALL)
+ .setDefaults(highPriority ? Notification.DEFAULT_ALL : 0)
.setOnlyAlertOnce(true)
.build();
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 87b490d..927b995 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -109,6 +109,9 @@
private static final String ACTION_STEP_IDLE_STATE =
"com.android.server.device_idle.STEP_IDLE_STATE";
+ private static final String ACTION_STEP_LIGHT_IDLE_STATE =
+ "com.android.server.device_idle.STEP_LIGHT_IDLE_STATE";
+
private AlarmManager mAlarmManager;
private IBatteryStats mBatteryStats;
private PowerManagerInternal mLocalPowerManager;
@@ -118,16 +121,16 @@
private Sensor mMotionSensor;
private LocationManager mLocationManager;
private LocationRequest mLocationRequest;
- private PendingIntent mSensingAlarmIntent;
private PendingIntent mAlarmIntent;
+ private PendingIntent mLightAlarmIntent;
private Intent mIdleIntent;
+ private Intent mLightIdleIntent;
private Display mCurDisplay;
private AnyMotionDetector mAnyMotionDetector;
private boolean mEnabled;
private boolean mForceIdle;
private boolean mScreenOn;
private boolean mCharging;
- private boolean mSensing;
private boolean mNotMoving;
private boolean mLocating;
private boolean mLocated;
@@ -138,7 +141,7 @@
/** Device is currently active. */
private static final int STATE_ACTIVE = 0;
- /** Device is inactve (screen off, no motion) and we are waiting to for idle. */
+ /** Device is inactive (screen off, no motion) and we are waiting to for idle. */
private static final int STATE_INACTIVE = 1;
/** Device is past the initial inactive period, and waiting for the next idle period. */
private static final int STATE_IDLE_PENDING = 2;
@@ -163,12 +166,35 @@
}
}
+ /** Device is currently active. */
+ 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 in the light idle state, trying to stay asleep as much as possible. */
+ private static final int LIGHT_STATE_IDLE = 2;
+ /** 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;
+ /** Device light idle state is overriden, now applying full doze state. */
+ private static final int LIGHT_STATE_OVERRIDE = 4;
+ private static String lightStateToString(int state) {
+ switch (state) {
+ case LIGHT_STATE_ACTIVE: return "ACTIVE";
+ case LIGHT_STATE_INACTIVE: return "INACTIVE";
+ case LIGHT_STATE_IDLE: return "IDLE";
+ case LIGHT_STATE_IDLE_MAINTENANCE: return "IDLE_MAINTENANCE";
+ case LIGHT_STATE_OVERRIDE: return "OVERRIDE";
+ default: return Integer.toString(state);
+ }
+ }
+
private int mState;
+ private int mLightState;
private long mInactiveTimeout;
private long mNextAlarmTime;
private long mNextIdlePendingDelay;
private long mNextIdleDelay;
+ private long mNextLightAlarmTime;
public final AtomicFile mConfigFile;
@@ -250,10 +276,14 @@
if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
Uri data = intent.getData();
String ssp;
- if (data != null && (ssp=data.getSchemeSpecificPart()) != null) {
+ if (data != null && (ssp = data.getSchemeSpecificPart()) != null) {
removePowerSaveWhitelistAppInternal(ssp);
}
}
+ } else if (ACTION_STEP_LIGHT_IDLE_STATE.equals(intent.getAction())) {
+ synchronized (DeviceIdleController.this) {
+ stepLightIdleStateLocked();
+ }
} else if (ACTION_STEP_IDLE_STATE.equals(intent.getAction())) {
synchronized (DeviceIdleController.this) {
stepIdleStateLocked();
@@ -305,7 +335,7 @@
public void onAccuracyChanged(Sensor sensor, int accuracy) {}
public boolean registerLocked() {
- boolean success = false;
+ boolean success;
if (mMotionSensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {
success = mSensorManager.requestTriggerSensor(mMotionListener, mMotionSensor);
} else {
@@ -380,6 +410,8 @@
*/
private final class Constants extends ContentObserver {
// Key names stored in the settings value.
+ private static final String KEY_LIGHT_IDLE_TIMEOUT = "light_idle_to";
+ private static final String KEY_LIGHT_IDLE_PENDING_TIMEOUT = "light_idle_pending_to";
private static final String KEY_INACTIVE_TIMEOUT = "inactive_to";
private static final String KEY_SENSING_TIMEOUT = "sensing_to";
private static final String KEY_LOCATING_TIMEOUT = "locating_to";
@@ -401,6 +433,22 @@
"sms_temp_app_whitelist_duration";
/**
+ * This is the time, after becoming inactive, that we will start going
+ * in to light-weight idle mode.
+ * @see Settings.Global#DEVICE_IDLE_CONSTANTS
+ * @see #KEY_LIGHT_IDLE_TIMEOUT
+ */
+ public long LIGHT_IDLE_TIMEOUT;
+
+ /**
+ * This is the initial time, after light idle idle, that we will will sit in the
+ * LIGHT_IDLE_MAINTENANCE period for the system to run normally before returning to idle.
+ * @see Settings.Global#DEVICE_IDLE_CONSTANTS
+ * @see #KEY_LIGHT_IDLE_PENDING_TIMEOUT
+ */
+ public long LIGHT_IDLE_PENDING_TIMEOUT;
+
+ /**
* This is the time, after becoming inactive, at which we start looking at the
* motion sensor to determine if the device is being left alone. We don't do this
* immediately after going inactive just because we don't want to be continually running
@@ -455,7 +503,8 @@
/**
* This is the initial time, after being idle, that we will allow ourself to be back
- * in the IDLE_PENDING state allowing the system to run normally until we return to idle.
+ * in the IDLE_MAINTENANCE state allowing the system to run normally until we return to
+ * idle.
* @see Settings.Global#DEVICE_IDLE_CONSTANTS
* @see #KEY_IDLE_PENDING_TIMEOUT
*/
@@ -555,6 +604,10 @@
Slog.e(TAG, "Bad device idle settings", e);
}
+ LIGHT_IDLE_TIMEOUT = mParser.getLong(KEY_LIGHT_IDLE_TIMEOUT,
+ !COMPRESS_TIME ? 15 * 60 * 1000L : 60 * 1000L);
+ LIGHT_IDLE_PENDING_TIMEOUT = mParser.getLong(KEY_LIGHT_IDLE_PENDING_TIMEOUT,
+ !COMPRESS_TIME ? 1 * 60 * 1000L : 15 * 1000L);
INACTIVE_TIMEOUT = mParser.getLong(KEY_INACTIVE_TIMEOUT,
!COMPRESS_TIME ? 30 * 60 * 1000L : 3 * 60 * 1000L);
SENSING_TIMEOUT = mParser.getLong(KEY_SENSING_TIMEOUT,
@@ -592,6 +645,14 @@
void dump(PrintWriter pw) {
pw.println(" Settings:");
+ 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_PENDING_TIMEOUT); pw.print("=");
+ TimeUtils.formatDuration(LIGHT_IDLE_PENDING_TIMEOUT, pw);
+ pw.println();
+
pw.print(" "); pw.print(KEY_INACTIVE_TIMEOUT); pw.print("=");
TimeUtils.formatDuration(INACTIVE_TIMEOUT, pw);
pw.println();
@@ -689,9 +750,10 @@
static final int MSG_WRITE_CONFIG = 1;
static final int MSG_REPORT_IDLE_ON = 2;
- static final int MSG_REPORT_IDLE_OFF = 3;
- static final int MSG_REPORT_ACTIVE = 4;
- static final int MSG_TEMP_APP_WHITELIST_TIMEOUT = 5;
+ static final int MSG_REPORT_IDLE_ON_LIGHT = 3;
+ static final int MSG_REPORT_IDLE_OFF = 4;
+ static final int MSG_REPORT_ACTIVE = 5;
+ static final int MSG_TEMP_APP_WHITELIST_TIMEOUT = 6;
final class MyHandler extends Handler {
MyHandler(Looper looper) {
@@ -704,43 +766,70 @@
case MSG_WRITE_CONFIG: {
handleWriteConfigFile();
} break;
- case MSG_REPORT_IDLE_ON: {
+ case MSG_REPORT_IDLE_ON:
+ case MSG_REPORT_IDLE_ON_LIGHT: {
EventLogTags.writeDeviceIdleOnStart();
- mLocalPowerManager.setDeviceIdleMode(true);
+ final boolean fullChanged;
+ final boolean lightChanged;
+ if (msg.what == MSG_REPORT_IDLE_ON) {
+ fullChanged = mLocalPowerManager.setDeviceIdleMode(true);
+ lightChanged = mLocalPowerManager.setLightDeviceIdleMode(false);
+ } else {
+ fullChanged = mLocalPowerManager.setDeviceIdleMode(false);
+ lightChanged = mLocalPowerManager.setLightDeviceIdleMode(true);
+ }
try {
mNetworkPolicyManager.setDeviceIdleMode(true);
- mBatteryStats.noteDeviceIdleMode(true, null, Process.myUid());
+ mBatteryStats.noteDeviceIdleMode(msg.what == MSG_REPORT_IDLE_ON
+ ? BatteryStats.DEVICE_IDLE_MODE_FULL
+ : BatteryStats.DEVICE_IDLE_MODE_LIGHT, null, Process.myUid());
} catch (RemoteException e) {
}
- getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL);
+ if (fullChanged) {
+ getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL);
+ }
+ if (lightChanged) {
+ getContext().sendBroadcastAsUser(mLightIdleIntent, UserHandle.ALL);
+ }
EventLogTags.writeDeviceIdleOnComplete();
} break;
case MSG_REPORT_IDLE_OFF: {
EventLogTags.writeDeviceIdleOffStart("unknown");
- mLocalPowerManager.setDeviceIdleMode(false);
+ final boolean fullChanged = mLocalPowerManager.setDeviceIdleMode(false);
+ final boolean lightChanged = mLocalPowerManager.setLightDeviceIdleMode(false);
try {
mNetworkPolicyManager.setDeviceIdleMode(false);
- mBatteryStats.noteDeviceIdleMode(false, null, Process.myUid());
+ mBatteryStats.noteDeviceIdleMode(BatteryStats.DEVICE_IDLE_MODE_OFF,
+ null, Process.myUid());
} catch (RemoteException e) {
}
- getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL);
+ if (fullChanged) {
+ getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL);
+ }
+ if (lightChanged) {
+ getContext().sendBroadcastAsUser(mLightIdleIntent, UserHandle.ALL);
+ }
EventLogTags.writeDeviceIdleOffComplete();
} break;
case MSG_REPORT_ACTIVE: {
String activeReason = (String)msg.obj;
int activeUid = msg.arg1;
- boolean needBroadcast = msg.arg2 != 0;
EventLogTags.writeDeviceIdleOffStart(
activeReason != null ? activeReason : "unknown");
- mLocalPowerManager.setDeviceIdleMode(false);
+ final boolean fullChanged = mLocalPowerManager.setDeviceIdleMode(false);
+ final boolean lightChanged = mLocalPowerManager.setLightDeviceIdleMode(false);
try {
mNetworkPolicyManager.setDeviceIdleMode(false);
- mBatteryStats.noteDeviceIdleMode(false, activeReason, activeUid);
+ mBatteryStats.noteDeviceIdleMode(BatteryStats.DEVICE_IDLE_MODE_OFF,
+ activeReason, activeUid);
} catch (RemoteException e) {
}
- if (needBroadcast) {
+ if (fullChanged) {
getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL);
}
+ if (lightChanged) {
+ getContext().sendBroadcastAsUser(mLightIdleIntent, UserHandle.ALL);
+ }
EventLogTags.writeDeviceIdleOffComplete();
} break;
case MSG_TEMP_APP_WHITELIST_TIMEOUT: {
@@ -909,6 +998,7 @@
// a battery update the next time the level drops.
mCharging = true;
mState = STATE_ACTIVE;
+ mLightState = LIGHT_STATE_ACTIVE;
mInactiveTimeout = mConstants.INACTIVE_TIMEOUT;
}
@@ -967,18 +1057,22 @@
.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
mAlarmIntent = PendingIntent.getBroadcast(getContext(), 0, intent, 0);
- Intent intentSensing = new Intent(ACTION_STEP_IDLE_STATE)
+ Intent intentLight = new Intent(ACTION_STEP_LIGHT_IDLE_STATE)
.setPackage("android")
.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- mSensingAlarmIntent = PendingIntent.getBroadcast(getContext(), 0, intentSensing, 0);
+ mLightAlarmIntent = PendingIntent.getBroadcast(getContext(), 0, intentLight, 0);
mIdleIntent = new Intent(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
mIdleIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
+ mLightIdleIntent = new Intent(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED);
+ mLightIdleIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
+ | Intent.FLAG_RECEIVER_FOREGROUND);
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
filter.addAction(ACTION_STEP_IDLE_STATE);
+ filter.addAction(ACTION_STEP_LIGHT_IDLE_STATE);
getContext().registerReceiver(mReceiver, filter);
filter = new IntentFilter();
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
@@ -1283,32 +1377,43 @@
}
void scheduleReportActiveLocked(String activeReason, int activeUid) {
- Message msg = mHandler.obtainMessage(MSG_REPORT_ACTIVE, activeUid,
- mState == STATE_IDLE ? 1 : 0, activeReason);
+ Message msg = mHandler.obtainMessage(MSG_REPORT_ACTIVE, activeUid, 0, activeReason);
mHandler.sendMessage(msg);
}
void becomeActiveLocked(String activeReason, int activeUid) {
if (DEBUG) Slog.i(TAG, "becomeActiveLocked, reason = " + activeReason);
- if (mState != STATE_ACTIVE) {
+ if (mState != STATE_ACTIVE || mLightState != STATE_ACTIVE) {
EventLogTags.writeDeviceIdle(STATE_ACTIVE, activeReason);
+ EventLogTags.writeDeviceIdleLight(LIGHT_STATE_ACTIVE, activeReason);
scheduleReportActiveLocked(activeReason, activeUid);
mState = STATE_ACTIVE;
+ mLightState = LIGHT_STATE_ACTIVE;
mInactiveTimeout = mConstants.INACTIVE_TIMEOUT;
resetIdleManagementLocked();
+ resetLightIdleManagementLocked();
}
}
void becomeInactiveIfAppropriateLocked() {
if (DEBUG) Slog.d(TAG, "becomeInactiveIfAppropriateLocked()");
- if (((!mScreenOn && !mCharging) || mForceIdle) && mEnabled && mState == STATE_ACTIVE) {
+ if (((!mScreenOn && !mCharging) || mForceIdle) && mEnabled) {
// Screen has turned off; we are now going to become inactive and start
// waiting to see if we will ultimately go idle.
- mState = STATE_INACTIVE;
- if (DEBUG) Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE");
- resetIdleManagementLocked();
- scheduleAlarmLocked(mInactiveTimeout, false);
- EventLogTags.writeDeviceIdle(mState, "no activity");
+ if (mState == STATE_ACTIVE) {
+ mState = STATE_INACTIVE;
+ if (DEBUG) Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE");
+ resetIdleManagementLocked();
+ scheduleAlarmLocked(mInactiveTimeout, false);
+ EventLogTags.writeDeviceIdle(mState, "no activity");
+ }
+ if (mLightState == LIGHT_STATE_ACTIVE) {
+ mLightState = LIGHT_STATE_INACTIVE;
+ if (DEBUG) Slog.d(TAG, "Moved from LIGHT_STATE_ACTIVE to LIGHT_STATE_INACTIVE");
+ resetLightIdleManagementLocked();
+ scheduleLightAlarmLocked(mConstants.LIGHT_IDLE_TIMEOUT);
+ EventLogTags.writeDeviceIdleLight(mLightState, "no activity");
+ }
}
}
@@ -1316,12 +1421,15 @@
mNextIdlePendingDelay = 0;
mNextIdleDelay = 0;
cancelAlarmLocked();
- cancelSensingAlarmLocked();
cancelLocatingLocked();
stopMonitoringMotionLocked();
mAnyMotionDetector.stop();
}
+ void resetLightIdleManagementLocked() {
+ cancelLightAlarmLocked();
+ }
+
void exitForceIdleLocked() {
if (mForceIdle) {
mForceIdle = false;
@@ -1331,6 +1439,37 @@
}
}
+ void stepLightIdleStateLocked() {
+ if (mLightState == LIGHT_STATE_OVERRIDE) {
+ // If we are already in full device idle mode, then
+ // there is nothing left to do for light mode.
+ return;
+ }
+
+ if (DEBUG) Slog.d(TAG, "stepLightIdleStateLocked: mLightState=" + mLightState);
+ EventLogTags.writeDeviceIdleLightStep();
+
+ switch (mLightState) {
+ case LIGHT_STATE_INACTIVE:
+ case LIGHT_STATE_IDLE_MAINTENANCE:
+ scheduleLightAlarmLocked(mConstants.LIGHT_IDLE_TIMEOUT);
+ if (DEBUG) Slog.d(TAG, "Moved to LIGHT_STATE_IDLE.");
+ mLightState = LIGHT_STATE_IDLE;
+ EventLogTags.writeDeviceIdleLight(mLightState, "step");
+ mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON_LIGHT);
+ break;
+ case LIGHT_STATE_IDLE:
+ // We have been idling long enough, now it is time to do some work.
+ scheduleLightAlarmLocked(mConstants.LIGHT_IDLE_PENDING_TIMEOUT);
+ if (DEBUG) Slog.d(TAG,
+ "Moved from LIGHT_STATE_IDLE to LIGHT_STATE_IDLE_MAINTENANCE.");
+ mLightState = LIGHT_STATE_IDLE_MAINTENANCE;
+ EventLogTags.writeDeviceIdleLight(mLightState, "step");
+ mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF);
+ break;
+ }
+ }
+
void stepIdleStateLocked() {
if (DEBUG) Slog.d(TAG, "stepIdleStateLocked: mState=" + mState);
EventLogTags.writeDeviceIdleStep();
@@ -1361,8 +1500,7 @@
mState = STATE_SENSING;
if (DEBUG) Slog.d(TAG, "Moved from STATE_IDLE_PENDING to STATE_SENSING.");
EventLogTags.writeDeviceIdle(mState, "step");
- scheduleSensingAlarmLocked(mConstants.SENSING_TIMEOUT);
- cancelSensingAlarmLocked();
+ scheduleAlarmLocked(mConstants.SENSING_TIMEOUT, false);
cancelLocatingLocked();
mAnyMotionDetector.checkForAnyMotion();
mNotMoving = false;
@@ -1374,8 +1512,7 @@
mState = STATE_LOCATING;
if (DEBUG) Slog.d(TAG, "Moved from STATE_SENSING to STATE_LOCATING.");
EventLogTags.writeDeviceIdle(mState, "step");
- cancelSensingAlarmLocked();
- scheduleSensingAlarmLocked(mConstants.LOCATING_TIMEOUT);
+ scheduleAlarmLocked(mConstants.LOCATING_TIMEOUT, false);
if (mLocationManager != null
&& mLocationManager.getProvider(LocationManager.NETWORK_PROVIDER) != null) {
mLocationManager.requestLocationUpdates(mLocationRequest,
@@ -1401,7 +1538,7 @@
// Otherwise, we have to move from locating into idle maintenance.
case STATE_LOCATING:
- cancelSensingAlarmLocked();
+ cancelAlarmLocked();
cancelLocatingLocked();
mAnyMotionDetector.stop();
case STATE_IDLE_MAINTENANCE:
@@ -1412,6 +1549,10 @@
if (DEBUG) Slog.d(TAG, "Setting mNextIdleDelay = " + mNextIdleDelay);
mNextIdleDelay = Math.min(mNextIdleDelay, mConstants.MAX_IDLE_TIMEOUT);
mState = STATE_IDLE;
+ if (mLightState != LIGHT_STATE_OVERRIDE) {
+ mLightState = LIGHT_STATE_OVERRIDE;
+ cancelLightAlarmLocked();
+ }
EventLogTags.writeDeviceIdle(mState, "step");
mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON);
break;
@@ -1439,11 +1580,22 @@
// The device is not yet active, so we want to go back to the pending idle
// state to wait again for no motion. Note that we only monitor for motion
// after moving out of the inactive state, so no need to worry about that.
+ boolean becomeInactive = false;
if (mState != STATE_ACTIVE) {
scheduleReportActiveLocked(type, Process.myUid());
mState = STATE_ACTIVE;
mInactiveTimeout = timeout;
EventLogTags.writeDeviceIdle(mState, type);
+ becomeInactive = true;
+ }
+ if (mLightState == LIGHT_STATE_OVERRIDE) {
+ // We went out of light idle mode because we had started full idle mode... let's
+ // now go back and reset things so we resume light idling if appropriate.
+ mLightState = STATE_ACTIVE;
+ EventLogTags.writeDeviceIdleLight(mLightState, type);
+ becomeInactive = true;
+ }
+ if (becomeInactive) {
becomeInactiveIfAppropriateLocked();
}
}
@@ -1501,11 +1653,10 @@
}
}
- void cancelSensingAlarmLocked() {
- if (mSensing) {
- if (DEBUG) Slog.d(TAG, "cancelSensingAlarmLocked()");
- mAlarmManager.cancel(mSensingAlarmIntent);
- mSensing = false;
+ void cancelLightAlarmLocked() {
+ if (mNextLightAlarmTime != 0) {
+ mNextLightAlarmTime = 0;
+ mAlarmManager.cancel(mLightAlarmIntent);
}
}
@@ -1536,13 +1687,18 @@
}
}
- void scheduleSensingAlarmLocked(long delay) {
- if (DEBUG) Slog.d(TAG, "scheduleSensingAlarmLocked(" + delay + ")");
- cancelSensingAlarmLocked();
- mNextAlarmTime = SystemClock.elapsedRealtime() + delay;
+ void scheduleLightAlarmLocked(long delay) {
+ if (DEBUG) Slog.d(TAG, "scheduleLightAlarmLocked(" + delay + ")");
+ if (mMotionSensor == null) {
+ // If there is no motion sensor on this device, then we won't schedule
+ // alarms, because we can't determine if the device is not moving. This effectively
+ // turns off normal execution of device idling, although it is still possible to
+ // manually poke it by pretending like the alarm is going off.
+ return;
+ }
+ mNextLightAlarmTime = SystemClock.elapsedRealtime() + delay;
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- mNextAlarmTime, mSensingAlarmIntent);
- mSensing = true;
+ mNextLightAlarmTime, mLightAlarmIntent);
}
private static int[] buildAppIdArray(ArrayMap<String, Integer> systemApps,
@@ -1778,7 +1934,21 @@
try {
exitForceIdleLocked();
stepIdleStateLocked();
- pw.print("Stepped to: "); pw.println(stateToString(mState));
+ pw.print("Stepped to: ");
+ pw.println(stateToString(mState));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ } else if ("light-step".equals(cmd)) {
+ getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
+ null);
+ synchronized (this) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ exitForceIdleLocked();
+ stepLightIdleStateLocked();
+ pw.print("Stepped to: "); pw.println(lightStateToString(mLightState));
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -2041,8 +2211,7 @@
pw.print(" mScreenOn="); pw.println(mScreenOn);
pw.print(" mCharging="); pw.println(mCharging);
pw.print(" mMotionActive="); pw.println(mMotionListener.active);
- pw.print(" mSensing="); pw.print(mSensing); pw.print(" mNotMoving=");
- pw.println(mNotMoving);
+ pw.print(" mNotMoving="); pw.println(mNotMoving);
pw.print(" mLocating="); pw.print(mLocating); pw.print(" mHasGps=");
pw.print(mHasGps); pw.print(" mHasNetwork=");
pw.print(mHasNetworkLocation); pw.print(" mLocated="); pw.println(mLocated);
@@ -2052,7 +2221,9 @@
if (mLastGpsLocation != null) {
pw.print(" mLastGpsLocation="); pw.println(mLastGpsLocation);
}
- pw.print(" mState="); pw.println(stateToString(mState));
+ pw.print(" mState="); pw.print(stateToString(mState));
+ pw.print(" mLightState=");
+ pw.println(lightStateToString(mLightState));
pw.print(" mInactiveTimeout="); TimeUtils.formatDuration(mInactiveTimeout, pw);
pw.println();
if (mNextAlarmTime != 0) {
@@ -2070,6 +2241,11 @@
TimeUtils.formatDuration(mNextIdleDelay, pw);
pw.println();
}
+ if (mNextLightAlarmTime != 0) {
+ pw.print(" mNextLightAlarmTime=");
+ TimeUtils.formatDuration(mNextLightAlarmTime, SystemClock.elapsedRealtime(), pw);
+ pw.println();
+ }
}
}
}
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index 9bf2aaa..516e2f4 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -194,6 +194,8 @@
34006 device_idle_off_start (reason|3)
34007 device_idle_off_phase (what|3)
34008 device_idle_off_complete
+34009 device_idle_light (state|1|5), (reason|3)
+34010 device_idle_light_step
# ---------------------------
# DisplayManagerService.java
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 087ddd6..9405d8e0 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -341,8 +341,10 @@
gpsProvider.disable();
}
- FlpHardwareProvider flpHardwareProvider = FlpHardwareProvider.getInstance(mContext);
- if (FlpHardwareProvider.isSupported() && flpHardwareProvider != null) {
+ // it is needed to check if FLP HW provider is supported before accessing the instance, this
+ // avoids an exception to be thrown by the singleton factory method
+ if (FlpHardwareProvider.isSupported()) {
+ FlpHardwareProvider flpHardwareProvider = FlpHardwareProvider.getInstance(mContext);
flpHardwareProvider.cleanup();
}
}
diff --git a/services/core/java/com/android/server/LockSettingsStorage.java b/services/core/java/com/android/server/LockSettingsStorage.java
index de48e71..6acec6b 100644
--- a/services/core/java/com/android/server/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/LockSettingsStorage.java
@@ -25,7 +25,9 @@
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Environment;
+import android.os.SystemProperties;
import android.os.UserManager;
+import android.os.storage.StorageManager;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
@@ -386,6 +388,12 @@
}
private int getUserParentOrSelfId(int userId) {
+ // Device supports per user encryption, so lock is applied to the given user.
+ if (mContext.getSystemService(StorageManager.class).isPerUserEncryptionEnabled()) {
+ return userId;
+ }
+ // Device uses Block Based Encryption, and the parent user's lock is used for the whole
+ // device.
if (userId != 0) {
final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
final UserInfo pi = um.getProfileParent(userId);
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index 4d32599..37dd884 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -96,6 +96,7 @@
import com.android.internal.os.Zygote;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.HexDump;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.server.NativeDaemonConnector.Command;
@@ -119,6 +120,7 @@
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -284,6 +286,8 @@
@GuardedBy("mLock")
private int[] mStartedUsers = EmptyArray.INT;
+ @GuardedBy("mLock")
+ private int[] mUnlockedUsers = EmptyArray.INT;
/** Map from disk ID to disk */
@GuardedBy("mLock")
@@ -402,6 +406,17 @@
}
}
+ private static String escapeNull(String arg) {
+ if (TextUtils.isEmpty(arg)) {
+ return "!";
+ } else {
+ if (arg.indexOf('\0') != -1 || arg.indexOf(' ') != -1) {
+ throw new IllegalArgumentException(arg);
+ }
+ return arg;
+ }
+ }
+
/** List of crypto types.
* These must match CRYPT_TYPE_XXX in cryptfs.h AND their
* corresponding commands in CommandListener.cpp */
@@ -562,6 +577,8 @@
private static final int H_VOLUME_BROADCAST = 6;
private static final int H_INTERNAL_BROADCAST = 7;
private static final int H_VOLUME_UNMOUNT = 8;
+ private static final int H_PARTITION_FORGET = 9;
+ private static final int H_RESET = 10;
class MountServiceHandler extends Handler {
public MountServiceHandler(Looper looper) {
@@ -669,6 +686,16 @@
final Intent intent = (Intent) msg.obj;
mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
android.Manifest.permission.WRITE_MEDIA_STORAGE);
+ break;
+ }
+ case H_PARTITION_FORGET: {
+ final String partGuid = (String) msg.obj;
+ forgetPartition(partGuid);
+ break;
+ }
+ case H_RESET: {
+ resetIfReadyAndConnected();
+ break;
}
}
}
@@ -753,9 +780,7 @@
}
private void handleSystemReady() {
- synchronized (mLock) {
- resetIfReadyAndConnectedLocked();
- }
+ resetIfReadyAndConnected();
// Start scheduling nominally-daily fstrim operations
MountServiceIdler.scheduleIdlePass(mContext);
@@ -793,7 +818,7 @@
}
}
- private void addInternalVolume() {
+ private void addInternalVolumeLocked() {
// Create a stub volume that represents internal storage
final VolumeInfo internal = new VolumeInfo(VolumeInfo.ID_PRIVATE_INTERNAL,
VolumeInfo.TYPE_PRIVATE, null, null);
@@ -802,18 +827,22 @@
mVolumes.put(internal.id, internal);
}
- private void resetIfReadyAndConnectedLocked() {
+ private void resetIfReadyAndConnected() {
Slog.d(TAG, "Thinking about reset, mSystemReady=" + mSystemReady
+ ", mDaemonConnected=" + mDaemonConnected);
if (mSystemReady && mDaemonConnected) {
- final UserManager um = UserManager.get(mContext);
- final List<UserInfo> users = um.getUsers();
+ final List<UserInfo> users = mContext.getSystemService(UserManager.class).getUsers();
killMediaProvider(users);
- mDisks.clear();
- mVolumes.clear();
+ final int[] startedUsers;
+ synchronized (mLock) {
+ startedUsers = mStartedUsers;
- addInternalVolume();
+ mDisks.clear();
+ mVolumes.clear();
+
+ addInternalVolumeLocked();
+ }
try {
mConnector.execute("volume", "reset");
@@ -822,7 +851,7 @@
for (UserInfo user : users) {
mConnector.execute("volume", "user_added", user.id, user.serialNumber);
}
- for (int userId : mStartedUsers) {
+ for (int userId : startedUsers) {
mConnector.execute("volume", "user_started", userId);
}
} catch (NativeDaemonConnectorException e) {
@@ -898,9 +927,7 @@
}
private void handleDaemonConnected() {
- synchronized (mLock) {
- resetIfReadyAndConnectedLocked();
- }
+ resetIfReadyAndConnected();
/*
* Now that we've done our initialization, release
@@ -1428,6 +1455,7 @@
mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25,
null);
mConnector.setDebug(true);
+ mConnector.setWarnIfHeld(mLock);
Thread thread = new Thread(mConnector, VOLD_TAG);
thread.start();
@@ -1445,7 +1473,9 @@
userFilter.addAction(Intent.ACTION_USER_REMOVED);
mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);
- addInternalVolume();
+ synchronized (mLock) {
+ addInternalVolumeLocked();
+ }
// Add ourself to the Watchdog monitors if enabled.
if (WATCHDOG_ENABLE) {
@@ -1791,10 +1821,11 @@
waitForReady();
Preconditions.checkNotNull(fsUuid);
+
synchronized (mLock) {
final VolumeRecord rec = mRecords.remove(fsUuid);
if (rec != null && !TextUtils.isEmpty(rec.partGuid)) {
- forgetPartition(rec.partGuid);
+ mHandler.obtainMessage(H_PARTITION_FORGET, rec.partGuid).sendToTarget();
}
mCallbacks.notifyVolumeForgotten(fsUuid);
@@ -1802,7 +1833,7 @@
// reset vold so we bind into new volume into place.
if (Objects.equals(mPrimaryStorageUuid, fsUuid)) {
mPrimaryStorageUuid = getDefaultPrimaryStorageUuid();
- resetIfReadyAndConnectedLocked();
+ mHandler.obtainMessage(H_RESET).sendToTarget();
}
writeSettingsLocked();
@@ -1819,7 +1850,7 @@
final String fsUuid = mRecords.keyAt(i);
final VolumeRecord rec = mRecords.valueAt(i);
if (!TextUtils.isEmpty(rec.partGuid)) {
- forgetPartition(rec.partGuid);
+ mHandler.obtainMessage(H_PARTITION_FORGET, rec.partGuid).sendToTarget();
}
mCallbacks.notifyVolumeForgotten(fsUuid);
}
@@ -1830,7 +1861,7 @@
}
writeSettingsLocked();
- resetIfReadyAndConnectedLocked();
+ mHandler.obtainMessage(H_RESET).sendToTarget();
}
}
@@ -1876,9 +1907,12 @@
if ((mask & StorageManager.DEBUG_FORCE_ADOPTABLE) != 0) {
mForceAdoptable = (flags & StorageManager.DEBUG_FORCE_ADOPTABLE) != 0;
}
+ if ((mask & StorageManager.DEBUG_EMULATE_FBE) != 0) {
+ // TODO: persist through vold and reboot
+ }
writeSettingsLocked();
- resetIfReadyAndConnectedLocked();
+ mHandler.obtainMessage(H_RESET).sendToTarget();
}
}
@@ -1915,7 +1949,7 @@
Slog.d(TAG, "Skipping move to/from primary physical");
onMoveStatusLocked(MOVE_STATUS_COPY_FINISHED);
onMoveStatusLocked(PackageManager.MOVE_SUCCEEDED);
- resetIfReadyAndConnectedLocked();
+ mHandler.obtainMessage(H_RESET).sendToTarget();
} else {
final VolumeInfo from = findStorageForUuid(mPrimaryStorageUuid);
@@ -2442,8 +2476,13 @@
}
try {
- mCryptConnector.execute("cryptfs", "enablecrypto", "inplace", CRYPTO_TYPES[type],
- new SensitiveArg(password));
+ if (type == StorageManager.CRYPT_TYPE_DEFAULT) {
+ mCryptConnector.execute("cryptfs", "enablecrypto", "inplace",
+ CRYPTO_TYPES[type]);
+ } else {
+ mCryptConnector.execute("cryptfs", "enablecrypto", "inplace",
+ CRYPTO_TYPES[type], new SensitiveArg(password));
+ }
} catch (NativeDaemonConnectorException e) {
// Encryption failed
return e.getCode();
@@ -2576,6 +2615,24 @@
}
}
+ /**
+ * Is userdata convertible to file based encryption?
+ * @return non zero for convertible
+ */
+ @Override
+ public boolean isConvertibleToFBE() throws RemoteException {
+
+ waitForReady();
+
+ final NativeDaemonEvent event;
+ try {
+ event = mCryptConnector.execute("cryptfs", "isConvertibleToFBE");
+ return Integer.parseInt(event.getMessage()) != 0;
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
@Override
public String getPassword() throws RemoteException {
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE,
@@ -2615,60 +2672,99 @@
}
@Override
- public void createNewUserDir(int userHandle, String path) {
- if (Binder.getCallingUid() != Process.SYSTEM_UID) {
- throw new SecurityException("Only SYSTEM_UID can create user directories");
- }
-
+ public void createUserKey(int userId, int serialNumber) {
+ enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
waitForReady();
- if (DEBUG_EVENTS) {
- Slog.i(TAG, "Creating new user dir");
- }
-
try {
- NativeDaemonEvent event = mCryptConnector.execute(
- "cryptfs", "createnewuserdir", userHandle, path);
- if (!"0".equals(event.getMessage())) {
- String error = "createnewuserdir sent unexpected message: "
- + event.getMessage();
- Slog.e(TAG, error);
- // ext4enc:TODO is this the right exception?
- throw new RuntimeException(error);
- }
+ mCryptConnector.execute("cryptfs", "create_user_key", userId, serialNumber);
} catch (NativeDaemonConnectorException e) {
- Slog.e(TAG, "createnewuserdir threw exception", e);
- throw new RuntimeException("createnewuserdir threw exception", e);
+ throw e.rethrowAsParcelableException();
}
}
- // ext4enc:TODO duplication between this and createNewUserDir is nasty
@Override
- public void deleteUserKey(int userHandle) {
- if (Binder.getCallingUid() != Process.SYSTEM_UID) {
- throw new SecurityException("Only SYSTEM_UID can delete user keys");
- }
-
+ public void destroyUserKey(int userId) {
+ enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
waitForReady();
- if (DEBUG_EVENTS) {
- Slog.i(TAG, "Deleting user key");
+ try {
+ mCryptConnector.execute("cryptfs", "destroy_user_key", userId);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
+ @Override
+ public void unlockUserKey(int userId, int serialNumber, byte[] token) {
+ enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
+ waitForReady();
+
+ final String encodedToken;
+ if (ArrayUtils.isEmpty(token)) {
+ encodedToken = "!";
+ } else {
+ encodedToken = HexDump.toHexString(token);
}
try {
- NativeDaemonEvent event = mCryptConnector.execute(
- "cryptfs", "deleteuserkey", userHandle);
- if (!"0".equals(event.getMessage())) {
- String error = "deleteuserkey sent unexpected message: "
- + event.getMessage();
- Slog.e(TAG, error);
- // ext4enc:TODO is this the right exception?
- throw new RuntimeException(error);
- }
+ mCryptConnector.execute("cryptfs", "unlock_user_key", userId, serialNumber,
+ new SensitiveArg(encodedToken));
} catch (NativeDaemonConnectorException e) {
- Slog.e(TAG, "deleteuserkey threw exception", e);
- throw new RuntimeException("deleteuserkey threw exception", e);
+ throw e.rethrowAsParcelableException();
}
+
+ synchronized (mLock) {
+ mUnlockedUsers = ArrayUtils.appendInt(mUnlockedUsers, userId);
+ }
+ }
+
+ @Override
+ public void lockUserKey(int userId) {
+ enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
+ waitForReady();
+
+ try {
+ mCryptConnector.execute("cryptfs", "lock_user_key", userId);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+
+ synchronized (mLock) {
+ mUnlockedUsers = ArrayUtils.removeInt(mUnlockedUsers, userId);
+ }
+ }
+
+ @Override
+ public boolean isUserKeyUnlocked(int userId) {
+ if (SystemProperties.getBoolean(StorageManager.PROP_HAS_FBE, false)) {
+ synchronized (mLock) {
+ return ArrayUtils.contains(mUnlockedUsers, userId);
+ }
+ } else {
+ return true;
+ }
+ }
+
+ @Override
+ public void prepareUserStorage(String volumeUuid, int userId, int serialNumber) {
+ enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
+ waitForReady();
+
+ try {
+ mCryptConnector.execute("cryptfs", "prepare_user_storage", escapeNull(volumeUuid),
+ userId, serialNumber);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
+ @Override
+ public boolean isPerUserEncryptionEnabled() {
+ // TODO: switch this over to a single property; currently using two to
+ // handle the emulated case
+ return "file".equals(SystemProperties.get("ro.crypto.type", "none"))
+ || SystemProperties.getBoolean(StorageManager.PROP_HAS_FBE, false);
}
@Override
@@ -3405,6 +3501,9 @@
pw.println();
pw.println("Primary storage UUID: " + mPrimaryStorageUuid);
pw.println("Force adoptable: " + mForceAdoptable);
+ pw.println();
+ pw.println("Started users: " + Arrays.toString(mStartedUsers));
+ pw.println("Unlocked users: " + Arrays.toString(mUnlockedUsers));
}
synchronized (mObbMounts) {
diff --git a/services/core/java/com/android/server/NativeDaemonConnector.java b/services/core/java/com/android/server/NativeDaemonConnector.java
index 519a2a3..e6b6074 100644
--- a/services/core/java/com/android/server/NativeDaemonConnector.java
+++ b/services/core/java/com/android/server/NativeDaemonConnector.java
@@ -28,6 +28,7 @@
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
import com.google.android.collect.Lists;
import java.io.FileDescriptor;
@@ -57,6 +58,7 @@
private LocalLog mLocalLog;
private volatile boolean mDebug = false;
+ private volatile Object mWarnIfHeld;
private final ResponseQueue mResponseQueue;
@@ -107,6 +109,15 @@
mDebug = debug;
}
+ /**
+ * Yell loudly if someone tries making future {@link #execute(Command)}
+ * calls while holding a lock on the given object.
+ */
+ public void setWarnIfHeld(Object warnIfHeld) {
+ Preconditions.checkState(mWarnIfHeld == null);
+ mWarnIfHeld = Preconditions.checkNotNull(warnIfHeld);
+ }
+
@Override
public void run() {
mCallbackHandler = new Handler(mLooper, this);
@@ -394,6 +405,11 @@
*/
public NativeDaemonEvent[] executeForList(long timeoutMs, String cmd, Object... args)
throws NativeDaemonConnectorException {
+ if (mWarnIfHeld != null && Thread.holdsLock(mWarnIfHeld)) {
+ Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName() + " is holding 0x"
+ + Integer.toHexString(System.identityHashCode(mWarnIfHeld)), new Throwable());
+ }
+
final long startTime = SystemClock.elapsedRealtime();
final ArrayList<NativeDaemonEvent> events = Lists.newArrayList();
diff --git a/services/core/java/com/android/server/NetworkTimeUpdateService.java b/services/core/java/com/android/server/NetworkTimeUpdateService.java
index a0d305c..3f0664d 100644
--- a/services/core/java/com/android/server/NetworkTimeUpdateService.java
+++ b/services/core/java/com/android/server/NetworkTimeUpdateService.java
@@ -30,6 +30,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
+import android.os.PowerManager;
import android.provider.Settings;
import android.util.Log;
import android.util.NtpTrustedTime;
@@ -75,6 +76,7 @@
private SettingsObserver mSettingsObserver;
// The last time that we successfully fetched the NTP time.
private long mLastNtpFetchTime = NOT_SET;
+ private final PowerManager.WakeLock mWakeLock;
// Normal polling frequency
private final long mPollingIntervalMs;
@@ -104,6 +106,9 @@
com.android.internal.R.integer.config_ntpRetry);
mTimeErrorThresholdMs = mContext.getResources().getInteger(
com.android.internal.R.integer.config_ntpThreshold);
+
+ mWakeLock = ((PowerManager) context.getSystemService(Context.POWER_SERVICE)).newWakeLock(
+ PowerManager.PARTIAL_WAKE_LOCK, TAG);
}
/** Initialize the receivers and initiate the first NTP request */
@@ -148,7 +153,15 @@
private void onPollNetworkTime(int event) {
// If Automatic time is not set, don't bother.
if (!isAutomaticTimeRequested()) return;
+ mWakeLock.acquire();
+ try {
+ onPollNetworkTimeUnderWakeLock(event);
+ } finally {
+ mWakeLock.release();
+ }
+ }
+ private void onPollNetworkTimeUnderWakeLock(int event) {
final long refTime = SystemClock.elapsedRealtime();
// If NITZ time was received less than mPollingIntervalMs time ago,
// no need to sync to NTP.
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 6b34612..9ac4ba3 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -980,7 +980,7 @@
if (user.isRestricted() && (parentUserId == user.restrictedProfileParentId)) {
addSharedAccountAsUser(account, user.id);
try {
- if (ActivityManagerNative.getDefault().isUserRunning(user.id, false)) {
+ if (ActivityManagerNative.getDefault().isUserRunning(user.id, 0)) {
mMessageHandler.sendMessage(mMessageHandler.obtainMessage(
MESSAGE_COPY_SHARED_ACCOUNT, parentUserId, user.id, account));
}
@@ -3824,7 +3824,6 @@
boolean isPermitted =
isPermitted(opPackageName, callingUid, Manifest.permission.GET_ACCOUNTS,
Manifest.permission.GET_ACCOUNTS_PRIVILEGED);
- Log.i(TAG, String.format("getTypesVisibleToCaller: isPermitted? %s", isPermitted));
return getTypesForCaller(callingUid, userId, isPermitted);
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 4949138..30565c6 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -337,7 +337,7 @@
ServiceRecord r = res.record;
- if (!mAm.getUserManagerLocked().exists(r.userId)) {
+ if (!mAm.mUserController.exists(r.userId)) {
Slog.d(TAG, "Trying to start service with non-existent user! " + r.userId);
return null;
}
@@ -355,7 +355,8 @@
final ServiceMap smap = getServiceMap(r.userId);
boolean addToStarting = false;
- if (!callerFg && r.app == null && mAm.mStartedUsers.get(r.userId) != null) {
+ if (!callerFg && r.app == null
+ && mAm.mUserController.hasStartedUserState(r.userId)) {
ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid, false);
if (proc == null || proc.curProcState > ActivityManager.PROCESS_STATE_RECEIVER) {
// If this is not coming from a foreground caller, then we may want
@@ -1029,8 +1030,8 @@
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "retrieveServiceLocked: " + service
+ " type=" + resolvedType + " callingUid=" + callingUid);
- userId = mAm.handleIncomingUser(callingPid, callingUid, userId,
- false, ActivityManagerService.ALLOW_NON_FULL_IN_PROFILE, "service", null);
+ userId = mAm.mUserController.handleIncomingUser(callingPid, callingUid, userId, false,
+ ActivityManagerService.ALLOW_NON_FULL_IN_PROFILE, "service", null);
ServiceMap smap = getServiceMap(userId);
final ComponentName comp = service.getComponent();
@@ -1314,6 +1315,15 @@
if (!mRestartingServices.contains(r)) {
return;
}
+ if (!isServiceNeeded(r, false, false)) {
+ // Paranoia: is this service actually needed? In theory a service that is not
+ // needed should never remain on the restart list. In practice... well, there
+ // have been bugs where this happens, and bad things happen because the process
+ // ends up just being cached, so quickly killed, then restarted again and again.
+ // Let's not let that happen.
+ Slog.wtf(TAG, "Restarting service that is not needed: " + r);
+ return;
+ }
try {
bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg, true);
} catch (TransactionTooLargeException e) {
@@ -1392,7 +1402,7 @@
// Make sure that the user who owns this service is started. If not,
// we don't want to allow it to run.
- if (mAm.mStartedUsers.get(r.userId) == null) {
+ if (!mAm.mUserController.hasStartedUserState(r.userId)) {
String msg = "Unable to launch app "
+ r.appInfo.packageName + "/"
+ r.appInfo.uid + " for service "
@@ -1517,7 +1527,7 @@
synchronized (r.stats.getBatteryStats()) {
r.stats.startLaunchedLocked();
}
- mAm.ensurePackageDexOpt(r.serviceInfo.packageName);
+ mAm.notifyPackageUse(r.serviceInfo.packageName);
app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
@@ -2043,6 +2053,13 @@
mAm.mProcessStats);
realStartServiceLocked(sr, proc, sr.createdFromFg);
didSomething = true;
+ if (!isServiceNeeded(sr, false, false)) {
+ // We were waiting for this service to start, but it is actually no
+ // longer needed. This could happen because bringDownServiceIfNeeded
+ // won't bring down a service that is pending... so now the pending
+ // is done, so let's drop it.
+ bringDownServiceLocked(sr);
+ }
}
} catch (RemoteException e) {
Slog.w(TAG, "Exception in new application when starting service "
@@ -2055,7 +2072,7 @@
// be weird to bring up the process but arbitrarily not let the services
// run at this point just because their restart time hasn't come up.
if (mRestartingServices.size() > 0) {
- ServiceRecord sr = null;
+ ServiceRecord sr;
for (int i=0; i<mRestartingServices.size(); i++) {
sr = mRestartingServices.get(i);
if (proc != sr.isolatedProc && (proc.uid != sr.appInfo.uid
@@ -2316,7 +2333,8 @@
EventLog.writeEvent(EventLogTags.AM_SERVICE_CRASHED_TOO_MUCH,
sr.userId, sr.crashCount, sr.shortName, app.pid);
bringDownServiceLocked(sr);
- } else if (!allowRestart || !mAm.isUserRunningLocked(sr.userId, false)) {
+ } else if (!allowRestart
+ || !mAm.mUserController.isUserRunningLocked(sr.userId, 0)) {
bringDownServiceLocked(sr);
} else {
boolean canceled = scheduleServiceRestartLocked(sr, true);
@@ -2429,7 +2447,7 @@
if (ActivityManager.checkUidPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
uid) == PackageManager.PERMISSION_GRANTED) {
- int[] users = mAm.getUsersLocked();
+ int[] users = mAm.mUserController.getUsers();
for (int ui=0; ui<users.length && res.size() < maxNum; ui++) {
ArrayMap<ComponentName, ServiceRecord> alls = getServices(users[ui]);
for (int i=0; i<alls.size() && res.size() < maxNum; i++) {
@@ -2563,7 +2581,7 @@
pw.print(mLastAnrDump);
pw.println();
}
- int[] users = mAm.getUsersLocked();
+ int[] users = mAm.mUserController.getUsers();
for (int user : users) {
ServiceMap smap = getServiceMap(user);
boolean printed = false;
@@ -2800,7 +2818,7 @@
ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
synchronized (mAm) {
- int[] users = mAm.getUsersLocked();
+ int[] users = mAm.mUserController.getUsers();
if ("all".equals(name)) {
for (int user : users) {
ServiceMap smap = mServiceMap.get(user);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e7601c2..0e5eadc 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -19,11 +19,11 @@
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
-import static android.app.ActivityManager.DOCKED_STACK_ID;
-import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.HOME_STACK_ID;
-import static android.app.ActivityManager.INVALID_STACK_ID;
+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.INVALID_STACK_ID;
import static android.app.ActivityManager.RESIZE_MODE_PRESERVE_WINDOW;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static com.android.internal.util.XmlUtils.readBooleanAttribute;
@@ -46,6 +46,7 @@
import static org.xmlpull.v1.XmlPullParser.START_TAG;
import android.Manifest;
+import android.app.ActivityManager.StackId;
import android.app.AppOpsManager;
import android.app.ApplicationThreadNative;
import android.app.BroadcastOptions;
@@ -68,18 +69,19 @@
import android.os.BatteryStats;
import android.os.PersistableBundle;
import android.os.PowerManager;
+import android.os.ResultReceiver;
import android.os.Trace;
import android.os.TransactionTooLargeException;
import android.os.WorkSource;
import android.os.storage.IMountService;
import android.os.storage.MountServiceInternal;
import android.os.storage.StorageManager;
+import android.provider.Settings.Global;
import android.service.voice.IVoiceInteractionSession;
import android.service.voice.VoiceInteractionSession;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DebugUtils;
-import android.util.SparseIntArray;
import android.view.Display;
import com.android.internal.R;
@@ -112,6 +114,7 @@
import com.android.server.SystemServiceManager;
import com.android.server.Watchdog;
import com.android.server.am.ActivityStack.ActivityState;
+import com.android.server.am.ActivityStackSupervisor.ActivityDisplay;
import com.android.server.firewall.IntentFirewall;
import com.android.server.pm.Installer;
import com.android.server.pm.UserManagerService;
@@ -132,6 +135,7 @@
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManager.StackInfo;
+import android.app.ActivityManager.TaskThumbnailInfo;
import android.app.ActivityManagerInternal;
import android.app.ActivityManagerInternal.SleepToken;
import android.app.ActivityManagerNative;
@@ -205,8 +209,6 @@
import android.os.IBinder;
import android.os.IPermissionController;
import android.os.IProcessInfoService;
-import android.os.IRemoteCallback;
-import android.os.IUserManager;
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
@@ -370,14 +372,14 @@
// How long we wait until we timeout on key dispatching during instrumentation.
static final int INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT = 60*1000;
- // Amount of time we wait for observers to handle a user switch before
- // giving up on them and unfreezing the screen.
- static final int USER_SWITCH_TIMEOUT = 2*1000;
-
// This is the amount of time an app needs to be running a foreground service before
// we will consider it to be doing interaction for usage stats.
static final int SERVICE_USAGE_INTERACTION_TIME = 30*60*1000;
+ // Maximum amount of time we will allow to elapse before re-reporting usage stats
+ // interaction with foreground processes.
+ static final long USAGE_STATS_INTERACTION_INTERVAL = 24*60*60*1000L;
+
// Maximum number of users we allow to be running at a time.
static final int MAX_RUNNING_USERS = 3;
@@ -505,6 +507,8 @@
*/
String mDeviceOwnerName;
+ final UserController mUserController;
+
public class PendingAssistExtras extends Binder implements Runnable {
public final ActivityRecord activity;
public final Bundle extras;
@@ -707,32 +711,6 @@
final SparseArray<UidRecord> mActiveUids = new SparseArray<>();
/**
- * Which users have been started, so are allowed to run code.
- */
- final SparseArray<UserState> mStartedUsers = new SparseArray<>();
-
- /**
- * LRU list of history of current users. Most recently current is at the end.
- */
- final ArrayList<Integer> mUserLru = new ArrayList<Integer>();
-
- /**
- * Constant array of the users that are currently started.
- */
- int[] mStartedUserArray = new int[] { 0 };
-
- /**
- * Registered observers of the user switching mechanics.
- */
- final RemoteCallbackList<IUserSwitchObserver> mUserSwitchObservers
- = new RemoteCallbackList<IUserSwitchObserver>();
-
- /**
- * Currently active user switch.
- */
- Object mCurUserSwitchCallback;
-
- /**
* Packages that the user has asked to have run in screen size
* compatibility mode instead of filling the screen.
*/
@@ -1221,6 +1199,7 @@
String mOrigDebugApp = null;
boolean mOrigWaitForDebugger = false;
boolean mAlwaysFinishActivities = false;
+ boolean mForceResizableActivites;
IActivityController mController = null;
String mProfileApp = null;
ProcessRecord mProfileProc = null;
@@ -1229,7 +1208,6 @@
int mSamplingInterval = 0;
boolean mAutoStopProfiler = false;
int mProfileType = 0;
- String mOpenGlTraceApp = null;
final ProcessMap<Pair<Long, String>> mMemWatchProcesses = new ProcessMap<>();
String mMemWatchDumpProcName;
String mMemWatchDumpFile;
@@ -1301,21 +1279,6 @@
final ActivityThread mSystemThread;
- // Holds the current foreground user's id
- int mCurrentUserId = 0;
- // Holds the target user's id during a user switch
- int mTargetUserId = UserHandle.USER_NULL;
- // If there are multiple profiles for the current user, their ids are here
- // Currently only the primary user can have managed profiles
- int[] mCurrentProfileIds = new int[] {}; // Accessed by ActivityStack
-
- /**
- * Mapping from each known user ID to the profile group ID it is associated with.
- */
- SparseIntArray mUserProfileGroupIdsSelfLocked = new SparseIntArray();
-
- private UserManagerService mUserManager;
-
private final class AppDeathRecipient implements IBinder.DeathRecipient {
final ProcessRecord mApp;
final int mPid;
@@ -1443,7 +1406,7 @@
boolean isBackground = (UserHandle.getAppId(proc.uid)
>= Process.FIRST_APPLICATION_UID
&& proc.pid != MY_PID);
- for (int userId : mCurrentProfileIds) {
+ for (int userId : mUserController.getCurrentProfileIdsLocked()) {
isBackground &= (proc.userId != userId);
}
if (isBackground && !showBackground) {
@@ -1608,7 +1571,7 @@
break;
}
case START_USER_SWITCH_MSG: {
- showUserSwitchDialog(msg.arg1, (String) msg.obj);
+ mUserController.showUserSwitchDialog(msg.arg1, (String) msg.obj);
break;
}
case DISMISS_DIALOG_MSG: {
@@ -1830,15 +1793,15 @@
break;
}
case REPORT_USER_SWITCH_MSG: {
- dispatchUserSwitch((UserState) msg.obj, msg.arg1, msg.arg2);
+ mUserController.dispatchUserSwitch((UserState) msg.obj, msg.arg1, msg.arg2);
break;
}
case CONTINUE_USER_SWITCH_MSG: {
- continueUserSwitch((UserState) msg.obj, msg.arg1, msg.arg2);
+ mUserController.continueUserSwitch((UserState) msg.obj, msg.arg1, msg.arg2);
break;
}
case USER_SWITCH_TIMEOUT_MSG: {
- timeoutUserSwitch((UserState) msg.obj, msg.arg1, msg.arg2);
+ mUserController.timeoutUserSwitch((UserState) msg.obj, msg.arg1, msg.arg2);
break;
}
case IMMERSIVE_MODE_LOCK_MSG: {
@@ -1867,7 +1830,7 @@
}
case START_PROFILES_MSG: {
synchronized (ActivityManagerService.this) {
- startProfilesLocked();
+ mUserController.startProfilesLocked();
}
break;
}
@@ -2058,14 +2021,14 @@
}
} break;
case FOREGROUND_PROFILE_CHANGED_MSG: {
- dispatchForegroundProfileChanged(msg.arg1);
+ mUserController.dispatchForegroundProfileChanged(msg.arg1);
} break;
case REPORT_TIME_TRACKER_MSG: {
AppTimeTracker tracker = (AppTimeTracker)msg.obj;
tracker.deliverResult(mContext);
} break;
case REPORT_USER_SWITCH_COMPLETE_MSG: {
- dispatchUserSwitchComplete(msg.arg1);
+ mUserController.dispatchUserSwitchComplete(msg.arg1);
} break;
case SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG: {
IUiAutomationConnection connection = (IUiAutomationConnection) msg.obj;
@@ -2387,10 +2350,7 @@
mGrantFile = new AtomicFile(new File(systemDir, "urigrants.xml"));
- // User 0 is the first and only user that runs at boot.
- mStartedUsers.put(UserHandle.USER_SYSTEM, new UserState(UserHandle.SYSTEM, true));
- mUserLru.add(UserHandle.USER_SYSTEM);
- updateStartedUserArrayLocked();
+ mUserController = new UserController(this);
GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
@@ -3102,12 +3062,10 @@
return proc;
}
- void ensurePackageDexOpt(String packageName) {
+ void notifyPackageUse(String packageName) {
IPackageManager pm = AppGlobals.getPackageManager();
try {
- if (pm.performDexOptIfNeeded(packageName, null /* instruction set */)) {
- mDidDexOpt = true;
- }
+ pm.notifyPackageUse(packageName);
} catch (RemoteException e) {
}
}
@@ -3637,8 +3595,7 @@
void enforceShellRestriction(String restriction, int userHandle) {
if (Binder.getCallingUid() == Process.SHELL_UID) {
- if (userHandle < 0
- || mUserManager.hasUserRestriction(restriction, userHandle)) {
+ if (userHandle < 0 || mUserController.hasUserRestriction(restriction, userHandle)) {
throw new SecurityException("Shell does not have permission to access user "
+ userHandle);
}
@@ -3889,29 +3846,29 @@
@Override
public final int startActivity(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
- int startFlags, ProfilerInfo profilerInfo, Bundle options) {
+ int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
- resultWho, requestCode, startFlags, profilerInfo, options,
- UserHandle.getCallingUserId());
+ resultWho, requestCode, startFlags, profilerInfo, bOptions,
+ UserHandle.getCallingUserId());
}
@Override
public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
- int startFlags, ProfilerInfo profilerInfo, Bundle options, int userId) {
+ int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
enforceNotIsolatedCaller("startActivity");
- userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
- false, ALLOW_FULL_ONLY, "startActivity", null);
+ userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, false, ALLOW_FULL_ONLY, "startActivity", null);
// TODO: Switch to user app stacks here.
return mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent,
resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
- profilerInfo, null, null, options, false, userId, null, null);
+ profilerInfo, null, null, bOptions, false, userId, null, null);
}
@Override
public final int startActivityAsCaller(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
- int startFlags, ProfilerInfo profilerInfo, Bundle options, boolean ignoreTargetSecurity,
+ int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, boolean ignoreTargetSecurity,
int userId) {
// This is very dangerous -- it allows you to perform a start activity (including
@@ -3968,7 +3925,7 @@
try {
int ret = mStackSupervisor.startActivityMayWait(null, targetUid, targetPackage, intent,
resolvedType, null, null, resultTo, resultWho, requestCode, startFlags, null,
- null, null, options, ignoreTargetSecurity, userId, null, null);
+ null, null, bOptions, ignoreTargetSecurity, userId, null, null);
return ret;
} catch (SecurityException e) {
// XXX need to figure out how to propagate to original app.
@@ -3989,36 +3946,36 @@
@Override
public final WaitResult startActivityAndWait(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
- int startFlags, ProfilerInfo profilerInfo, Bundle options, int userId) {
+ int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
enforceNotIsolatedCaller("startActivityAndWait");
- userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
- false, ALLOW_FULL_ONLY, "startActivityAndWait", null);
+ userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, false, ALLOW_FULL_ONLY, "startActivityAndWait", null);
WaitResult res = new WaitResult();
// TODO: Switch to user app stacks here.
mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent, resolvedType,
null, null, resultTo, resultWho, requestCode, startFlags, profilerInfo, res, null,
- options, false, userId, null, null);
+ bOptions, false, userId, null, null);
return res;
}
@Override
public final int startActivityWithConfig(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
- int startFlags, Configuration config, Bundle options, int userId) {
+ int startFlags, Configuration config, Bundle bOptions, int userId) {
enforceNotIsolatedCaller("startActivityWithConfig");
- userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
- false, ALLOW_FULL_ONLY, "startActivityWithConfig", null);
+ userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, false, ALLOW_FULL_ONLY, "startActivityWithConfig", null);
// TODO: Switch to user app stacks here.
int ret = mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent,
resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
- null, null, config, options, false, userId, null, null);
+ null, null, config, bOptions, false, userId, null, null);
return ret;
}
@Override
public int startActivityIntentSender(IApplicationThread caller, IntentSender intent,
Intent fillInIntent, String resolvedType, IBinder resultTo, String resultWho,
- int requestCode, int flagsMask, int flagsValues, Bundle options)
+ int requestCode, int flagsMask, int flagsValues, Bundle bOptions)
throws TransactionTooLargeException {
enforceNotIsolatedCaller("startActivityIntentSender");
// Refuse possible leaked file descriptors
@@ -4043,7 +4000,7 @@
}
}
int ret = pir.sendInner(0, fillInIntent, resolvedType, null, null,
- resultTo, resultWho, requestCode, flagsMask, flagsValues, options, null);
+ resultTo, resultWho, requestCode, flagsMask, flagsValues, bOptions, null);
return ret;
}
@@ -4051,7 +4008,7 @@
public int startVoiceActivity(String callingPackage, int callingPid, int callingUid,
Intent intent, String resolvedType, IVoiceInteractionSession session,
IVoiceInteractor interactor, int startFlags, ProfilerInfo profilerInfo,
- Bundle options, int userId) {
+ Bundle bOptions, int userId) {
if (checkCallingPermission(Manifest.permission.BIND_VOICE_INTERACTION)
!= PackageManager.PERMISSION_GRANTED) {
String msg = "Permission Denial: startVoiceActivity() from pid="
@@ -4064,12 +4021,12 @@
if (session == null || interactor == null) {
throw new NullPointerException("null session or interactor");
}
- userId = handleIncomingUser(callingPid, callingUid, userId,
- false, ALLOW_FULL_ONLY, "startVoiceActivity", null);
+ userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, false,
+ ALLOW_FULL_ONLY, "startVoiceActivity", null);
// TODO: Switch to user app stacks here.
return mStackSupervisor.startActivityMayWait(null, callingUid, callingPackage, intent,
resolvedType, session, interactor, null, null, 0, startFlags, profilerInfo, null,
- null, options, false, userId, null, null);
+ null, bOptions, false, userId, null, null);
}
@Override
@@ -4087,11 +4044,12 @@
@Override
public boolean startNextMatchingActivity(IBinder callingActivity,
- Intent intent, Bundle options) {
+ Intent intent, Bundle bOptions) {
// Refuse possible leaked file descriptors
if (intent != null && intent.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
+ ActivityOptions options = ActivityOptions.fromBundle(bOptions);
synchronized (this) {
final ActivityRecord r = ActivityRecord.isInStackLocked(callingActivity);
@@ -4135,8 +4093,8 @@
if (debug) {
Slog.v(TAG, "Next matching activity: found current " + r.packageName
+ "/" + r.info.name);
- Slog.v(TAG, "Next matching activity: next is " + aInfo.packageName
- + "/" + aInfo.name);
+ Slog.v(TAG, "Next matching activity: next is " + ((aInfo == null)
+ ? "null" : aInfo.packageName + "/" + aInfo.name));
}
break;
}
@@ -4192,17 +4150,22 @@
}
@Override
- public final int startActivityFromRecents(int taskId, int launchStackId, Bundle options) {
+ public final int startActivityFromRecents(int taskId, int launchStackId, Bundle bOptions) {
if (checkCallingPermission(START_TASKS_FROM_RECENTS) != PackageManager.PERMISSION_GRANTED) {
String msg = "Permission Denial: startActivityFromRecents called without " +
START_TASKS_FROM_RECENTS;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
- return startActivityFromRecentsInner(taskId, launchStackId, options);
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ return startActivityFromRecentsInner(taskId, launchStackId, bOptions);
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
}
- final int startActivityFromRecentsInner(int taskId, int launchStackId, Bundle options) {
+ final int startActivityFromRecentsInner(int taskId, int launchStackId, Bundle bOptions) {
final TaskRecord task;
final int callingUid;
final String callingPackage;
@@ -4220,13 +4183,19 @@
"startActivityFromRecentsInner: Task " + taskId + " not found.");
}
- if (launchStackId != INVALID_STACK_ID && task.stack.mStackId != launchStackId) {
- mStackSupervisor.moveTaskToStackUncheckedLocked(
- task, launchStackId, ON_TOP, FORCE_FOCUS, "startActivityFromRecents");
+ if (launchStackId != INVALID_STACK_ID) {
+ if (launchStackId == DOCKED_STACK_ID && bOptions != null) {
+ ActivityOptions activityOptions = new ActivityOptions(bOptions);
+ mWindowManager.setDockedStackCreateMode(activityOptions.getDockCreateMode());
+ }
+ if (task.stack.mStackId != launchStackId) {
+ mStackSupervisor.moveTaskToStackLocked(
+ taskId, launchStackId, ON_TOP, FORCE_FOCUS, "startActivityFromRecents");
+ }
}
if (task.getRootActivity() != null) {
- moveTaskToFrontLocked(task.taskId, 0, options);
+ moveTaskToFrontLocked(task.taskId, 0, bOptions);
return ActivityManager.START_TASK_TO_FRONT;
}
callingUid = task.mCallingUid;
@@ -4236,46 +4205,46 @@
userId = task.userId;
}
return startActivityInPackage(callingUid, callingPackage, intent, null, null, null, 0, 0,
- options, userId, null, task);
+ bOptions, userId, null, task);
}
final int startActivityInPackage(int uid, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo,
- String resultWho, int requestCode, int startFlags, Bundle options, int userId,
+ String resultWho, int requestCode, int startFlags, Bundle bOptions, int userId,
IActivityContainer container, TaskRecord inTask) {
- userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
- false, ALLOW_FULL_ONLY, "startActivityInPackage", null);
+ userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, false, ALLOW_FULL_ONLY, "startActivityInPackage", null);
// TODO: Switch to user app stacks here.
int ret = mStackSupervisor.startActivityMayWait(null, uid, callingPackage, intent,
resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
- null, null, null, options, false, userId, container, inTask);
+ null, null, null, bOptions, false, userId, container, inTask);
return ret;
}
@Override
public final int startActivities(IApplicationThread caller, String callingPackage,
- Intent[] intents, String[] resolvedTypes, IBinder resultTo, Bundle options,
+ Intent[] intents, String[] resolvedTypes, IBinder resultTo, Bundle bOptions,
int userId) {
enforceNotIsolatedCaller("startActivities");
- userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
- false, ALLOW_FULL_ONLY, "startActivity", null);
+ userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, false, ALLOW_FULL_ONLY, "startActivity", null);
// TODO: Switch to user app stacks here.
int ret = mStackSupervisor.startActivities(caller, -1, callingPackage, intents,
- resolvedTypes, resultTo, options, userId);
+ resolvedTypes, resultTo, bOptions, userId);
return ret;
}
final int startActivitiesInPackage(int uid, String callingPackage,
Intent[] intents, String[] resolvedTypes, IBinder resultTo,
- Bundle options, int userId) {
+ Bundle bOptions, int userId) {
- userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
- false, ALLOW_FULL_ONLY, "startActivityInPackage", null);
+ userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, false, ALLOW_FULL_ONLY, "startActivityInPackage", null);
// TODO: Switch to user app stacks here.
int ret = mStackSupervisor.startActivities(null, uid, callingPackage, intents, resolvedTypes,
- resultTo, options, userId);
+ resultTo, bOptions, userId);
return ret;
}
@@ -5173,8 +5142,8 @@
}
int uid = Binder.getCallingUid();
int pid = Binder.getCallingPid();
- userId = handleIncomingUser(pid, uid,
- userId, false, ALLOW_FULL_ONLY, "clearApplicationUserData", null);
+ userId = mUserController.handleIncomingUser(pid, uid, userId, false,
+ ALLOW_FULL_ONLY, "clearApplicationUserData", null);
long callingId = Binder.clearCallingIdentity();
try {
IPackageManager pm = AppGlobals.getPackageManager();
@@ -5253,7 +5222,7 @@
throw new SecurityException(msg);
}
- userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
userId, true, ALLOW_FULL_ONLY, "killBackgroundProcesses", null);
long callingId = Binder.clearCallingIdentity();
try {
@@ -5336,14 +5305,14 @@
throw new SecurityException(msg);
}
final int callingPid = Binder.getCallingPid();
- userId = handleIncomingUser(callingPid, Binder.getCallingUid(),
+ userId = mUserController.handleIncomingUser(callingPid, Binder.getCallingUid(),
userId, true, ALLOW_FULL_ONLY, "forceStopPackage", null);
long callingId = Binder.clearCallingIdentity();
try {
IPackageManager pm = AppGlobals.getPackageManager();
synchronized(this) {
int[] users = userId == UserHandle.USER_ALL
- ? getUsersLocked() : new int[] { userId };
+ ? mUserController.getUsers() : new int[] { userId };
for (int user : users) {
int pkgUid = -1;
try {
@@ -5361,7 +5330,7 @@
Slog.w(TAG, "Failed trying to unstop package "
+ packageName + ": " + e);
}
- if (isUserRunningLocked(user, false)) {
+ if (mUserController.isUserRunningLocked(user, 0)) {
forceStopPackageLocked(packageName, pkgUid, "from pid " + callingPid);
}
}
@@ -5567,16 +5536,6 @@
null, false, false, MY_PID, Process.SYSTEM_UID, UserHandle.getUserId(uid));
}
- private void forceStopUserLocked(int userId, String reason) {
- forceStopPackageLocked(null, -1, false, false, true, false, false, userId, reason);
- Intent intent = new Intent(Intent.ACTION_USER_STOPPED);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
- | Intent.FLAG_RECEIVER_FOREGROUND);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- broadcastIntentLocked(null, null, intent,
- null, null, 0, null, null, null, AppOpsManager.OP_NONE,
- null, false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
- }
private final boolean killPackageProcessesLocked(String packageName, int appId,
int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart,
@@ -5740,7 +5699,7 @@
}
- private final boolean forceStopPackageLocked(String packageName, int appId,
+ final boolean forceStopPackageLocked(String packageName, int appId,
boolean callerWillRestart, boolean purgeCache, boolean doit,
boolean evenPersistent, boolean uninstalling, int userId, String reason) {
int i;
@@ -5795,7 +5754,7 @@
}
boolean didSomething = killPackageProcessesLocked(packageName, appId, userId,
- -100, callerWillRestart, true, doit, evenPersistent,
+ ProcessList.INVALID_ADJ, callerWillRestart, true, doit, evenPersistent,
packageName == null ? ("stop user " + userId) : ("stop " + packageName));
if (mStackSupervisor.finishDisabledPackageActivitiesLocked(
@@ -6111,7 +6070,7 @@
EventLog.writeEvent(EventLogTags.AM_PROC_BOUND, app.userId, app.pid, app.processName);
app.makeActive(thread, mProcessStats);
- app.curAdj = app.setAdj = -100;
+ app.curAdj = app.setAdj = ProcessList.INVALID_ADJ;
app.curSchedGroup = app.setSchedGroup = Process.THREAD_GROUP_DEFAULT;
app.forcingToForeground = null;
updateProcessForegroundLocked(app, false, false);
@@ -6161,11 +6120,6 @@
samplingInterval = mSamplingInterval;
profileAutoStop = mAutoStopProfiler;
}
- boolean enableOpenGlTrace = false;
- if (mOpenGlTraceApp != null && mOpenGlTraceApp.equals(processName)) {
- enableOpenGlTrace = true;
- mOpenGlTraceApp = null;
- }
boolean enableTrackAllocation = false;
if (mTrackAllocationApp != null && mTrackAllocationApp.equals(processName)) {
enableTrackAllocation = true;
@@ -6180,11 +6134,11 @@
|| (mBackupTarget.backupMode == BackupRecord.BACKUP_FULL);
}
- ensurePackageDexOpt(app.instrumentationInfo != null
+ notifyPackageUse(app.instrumentationInfo != null
? app.instrumentationInfo.packageName
: app.info.packageName);
if (app.instrumentationClass != null) {
- ensurePackageDexOpt(app.instrumentationClass.getPackageName());
+ notifyPackageUse(app.instrumentationClass.getPackageName());
}
if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Binding proc "
+ processName + " with config " + mConfiguration);
@@ -6199,9 +6153,9 @@
thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,
profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,
app.instrumentationUiAutomationConnection, testMode,
- mBinderTransactionTrackingEnabled, enableOpenGlTrace,
- enableTrackAllocation, isRestrictedBackupMode || !normalMode,
- app.persistent, new Configuration(mConfiguration), app.compat,
+ mBinderTransactionTrackingEnabled, enableTrackAllocation,
+ isRestrictedBackupMode || !normalMode, app.persistent,
+ new Configuration(mConfiguration), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked());
updateLruProcessLocked(app, false, null);
@@ -6264,7 +6218,7 @@
if (!badApp && mBackupTarget != null && mBackupTarget.appInfo.uid == app.uid) {
if (DEBUG_BACKUP) Slog.v(TAG_BACKUP,
"New app is backup target, launching agent for " + app);
- ensurePackageDexOpt(mBackupTarget.appInfo.packageName);
+ notifyPackageUse(mBackupTarget.appInfo.packageName);
try {
thread.scheduleCreateBackupAgent(mBackupTarget.appInfo,
compatibilityInfoForPackageLocked(mBackupTarget.appInfo),
@@ -6466,32 +6420,18 @@
|| "".equals(SystemProperties.get("vold.encrypt_progress"))) {
SystemProperties.set("dev.bootcomplete", "1");
}
- for (int i=0; i<mStartedUsers.size(); i++) {
- UserState uss = mStartedUsers.valueAt(i);
- if (uss.mState == UserState.STATE_BOOTING) {
- uss.mState = UserState.STATE_RUNNING;
- final int userId = mStartedUsers.keyAt(i);
- Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT);
- 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) {
- synchronized (ActivityManagerService.this) {
- requestPssAllProcsLocked(SystemClock.uptimeMillis(),
- true, false);
- }
- }
- },
- 0, null, null,
- new String[] {android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
- AppOpsManager.OP_NONE, null, true, false,
- MY_PID, Process.SYSTEM_UID, userId);
- }
- }
+ mUserController.sendBootCompletedLocked(
+ new IIntentReceiver.Stub() {
+ @Override
+ public void performReceive(Intent intent, int resultCode,
+ String data, Bundle extras, boolean ordered,
+ boolean sticky, int sendingUser) {
+ synchronized (ActivityManagerService.this) {
+ requestPssAllProcsLocked(SystemClock.uptimeMillis(),
+ true, false);
+ }
+ }
+ });
scheduleStartProfilesLocked();
}
}
@@ -6593,7 +6533,7 @@
@Override
public void reportSizeConfigurations(IBinder token, int[] horizontalSizeConfiguration,
- int[] verticalSizeConfigurations) {
+ int[] verticalSizeConfigurations, int[] smallestSizeConfigurations) {
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Report configuration: " + token + " "
+ horizontalSizeConfiguration + " " + verticalSizeConfigurations);
synchronized (this) {
@@ -6603,7 +6543,7 @@
+ "found for: " + token);
}
record.setSizeConfigurations(horizontalSizeConfiguration,
- verticalSizeConfigurations);
+ verticalSizeConfigurations, smallestSizeConfigurations);
}
}
@@ -6693,7 +6633,7 @@
public IIntentSender getIntentSender(int type,
String packageName, IBinder token, String resultWho,
int requestCode, Intent[] intents, String[] resolvedTypes,
- int flags, Bundle options, int userId) {
+ int flags, Bundle bOptions, int userId) {
enforceNotIsolatedCaller("getIntentSender");
// Refuse possible leaked file descriptors
if (intents != null) {
@@ -6719,8 +6659,8 @@
"Intent array length does not match resolvedTypes length");
}
}
- if (options != null) {
- if (options.hasFileDescriptors()) {
+ if (bOptions != null) {
+ if (bOptions.hasFileDescriptors()) {
throw new IllegalArgumentException("File descriptors passed in options");
}
}
@@ -6728,7 +6668,7 @@
synchronized(this) {
int callingUid = Binder.getCallingUid();
int origUserId = userId;
- userId = handleIncomingUser(Binder.getCallingPid(), callingUid, userId,
+ userId = mUserController.handleIncomingUser(Binder.getCallingPid(), callingUid, userId,
type == ActivityManager.INTENT_SENDER_BROADCAST,
ALLOW_NON_FULL, "getIntentSender", null);
if (origUserId == UserHandle.USER_CURRENT) {
@@ -6753,7 +6693,7 @@
}
return getIntentSenderLocked(type, packageName, callingUid, userId,
- token, resultWho, requestCode, intents, resolvedTypes, flags, options);
+ token, resultWho, requestCode, intents, resolvedTypes, flags, bOptions);
} catch (RemoteException e) {
throw new SecurityException(e);
@@ -6764,7 +6704,7 @@
IIntentSender getIntentSenderLocked(int type, String packageName,
int callingUid, int userId, IBinder token, String resultWho,
int requestCode, Intent[] intents, String[] resolvedTypes, int flags,
- Bundle options) {
+ Bundle bOptions) {
if (DEBUG_MU) Slog.v(TAG_MU, "getIntentSenderLocked(): uid=" + callingUid);
ActivityRecord activity = null;
if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) {
@@ -6785,7 +6725,7 @@
PendingIntentRecord.Key key = new PendingIntentRecord.Key(
type, packageName, activity, resultWho,
- requestCode, intents, resolvedTypes, flags, options, userId);
+ requestCode, intents, resolvedTypes, flags, bOptions, userId);
WeakReference<PendingIntentRecord> ref;
ref = mIntentSenderRecords.get(key);
PendingIntentRecord rec = ref != null ? ref.get() : null;
@@ -7976,8 +7916,9 @@
@Override
public void grantUriPermissionFromOwner(IBinder token, int fromUid, String targetPkg, Uri uri,
final int modeFlags, int sourceUserId, int targetUserId) {
- targetUserId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
- targetUserId, false, ALLOW_FULL_ONLY, "grantUriPermissionFromOwner", null);
+ targetUserId = mUserController.handleIncomingUser(Binder.getCallingPid(),
+ Binder.getCallingUid(), targetUserId, false, ALLOW_FULL_ONLY,
+ "grantUriPermissionFromOwner", null);
synchronized(this) {
UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token);
if (owner == null) {
@@ -8428,6 +8369,9 @@
rti.affiliatedTaskId = tr.mAffiliatedTaskId;
rti.affiliatedTaskColor = tr.mAffiliatedTaskColor;
rti.numActivities = 0;
+ if (tr.mBounds != null) {
+ rti.bounds = new Rect(tr.mBounds);
+ }
ActivityRecord base = null;
ActivityRecord top = null;
@@ -8481,7 +8425,7 @@
@Override
public List<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags, int userId) {
final int callingUid = Binder.getCallingUid();
- userId = handleIncomingUser(Binder.getCallingPid(), callingUid, userId,
+ userId = mUserController.handleIncomingUser(Binder.getCallingPid(), callingUid, userId,
false, ALLOW_FULL_ONLY, "getRecentTasks", null);
final boolean includeProfiles = (flags & ActivityManager.RECENT_INCLUDE_PROFILES) != 0;
@@ -8499,7 +8443,7 @@
final Set<Integer> includedUsers;
if (includeProfiles) {
- includedUsers = getProfileIdsLocked(userId);
+ includedUsers = mUserController.getProfileIds(userId);
} else {
includedUsers = new HashSet<>();
}
@@ -8633,8 +8577,16 @@
}
}
+ // Use the full screen as the context for the task thumbnail
+ final Point displaySize = new Point();
+ final TaskThumbnailInfo thumbnailInfo = new TaskThumbnailInfo();
+ r.task.stack.getDisplaySize(displaySize);
+ thumbnailInfo.taskWidth = displaySize.x;
+ thumbnailInfo.taskHeight = displaySize.y;
+ thumbnailInfo.screenOrientation = mConfiguration.orientation;
+
TaskRecord task = new TaskRecord(this, mStackSupervisor.getNextTaskId(), ainfo,
- intent, description);
+ intent, description, thumbnailInfo);
int trimIdx = mRecentTasks.trimForTaskLocked(task, false);
if (trimIdx >= 0) {
@@ -8653,7 +8605,7 @@
mRecentTasks.add(task);
r.task.stack.addTask(task, false, false);
- task.setLastThumbnail(thumbnail);
+ task.setLastThumbnailLocked(thumbnail);
task.freeLastThumbnail();
return task.taskId;
@@ -8717,11 +8669,9 @@
// - a non-null bounds on a non-freeform (fullscreen OR docked) task moves
// that task to freeform
// - otherwise the task is not moved
- // Note it's not allowed to resize a home stack task, or a docked task.
int stackId = task.stack.mStackId;
- if (stackId == HOME_STACK_ID || stackId == DOCKED_STACK_ID) {
- throw new IllegalArgumentException("trying to resizeTask on a "
- + "home or docked task");
+ if (!StackId.isTaskResizeAllowed(stackId)) {
+ throw new IllegalArgumentException("resizeTask not allowed on task=" + task);
}
if (bounds == null && stackId == FREEFORM_WORKSPACE_STACK_ID) {
stackId = FULLSCREEN_WORKSPACE_STACK_ID;
@@ -8750,12 +8700,25 @@
Rect rect = new Rect();
try {
synchronized (this) {
- TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+ final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(
+ taskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
if (task == null) {
Slog.w(TAG, "getTaskBounds: taskId=" + taskId + " not found");
return rect;
}
- mWindowManager.getTaskBounds(task.taskId, rect);
+ if (task.stack != null) {
+ // Return the bounds from window manager since it will be adjusted for various
+ // things like the presense of a docked stack for tasks that aren't resizeable.
+ mWindowManager.getTaskBounds(task.taskId, rect);
+ } else {
+ // Task isn't in window manager yet since it isn't associated with a stack.
+ // Return the persist value from activity manager
+ if (task.mBounds != null) {
+ rect.set(task.mBounds);
+ } else if (task.mLastNonFullscreenBounds != null) {
+ rect.set(task.mLastNonFullscreenBounds);
+ }
+ }
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -8933,16 +8896,18 @@
* TODO: Add mController hook
*/
@Override
- public void moveTaskToFront(int taskId, int flags, Bundle options) {
+ public void moveTaskToFront(int taskId, int flags, Bundle bOptions) {
enforceCallingPermission(android.Manifest.permission.REORDER_TASKS, "moveTaskToFront()");
if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToFront: moving taskId=" + taskId);
synchronized(this) {
- moveTaskToFrontLocked(taskId, flags, options);
+ moveTaskToFrontLocked(taskId, flags, bOptions);
}
}
- void moveTaskToFrontLocked(int taskId, int flags, Bundle options) {
+ void moveTaskToFrontLocked(int taskId, int flags, Bundle bOptions) {
+ ActivityOptions options = ActivityOptions.fromBundle(bOptions);
+
if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(),
Binder.getCallingUid(), -1, -1, "Task to front")) {
ActivityOptions.abort(options);
@@ -9104,7 +9069,8 @@
}
if (DEBUG_STACK) Slog.d(TAG_STACK, "moveActivityToStack: moving r=" + r
+ " to stackId=" + stackId);
- mStackSupervisor.moveTaskToStackLocked(r.task.taskId, stackId, ON_TOP, !FORCE_FOCUS);
+ mStackSupervisor.moveTaskToStackLocked(r.task.taskId, stackId, ON_TOP, !FORCE_FOCUS,
+ "moveActivityToStack");
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -9124,7 +9090,8 @@
try {
if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToStack: moving task=" + taskId
+ " to stackId=" + stackId + " toTop=" + toTop);
- mStackSupervisor.moveTaskToStackLocked(taskId, stackId, toTop, !FORCE_FOCUS);
+ mStackSupervisor.moveTaskToStackLocked(taskId, stackId, toTop, !FORCE_FOCUS,
+ "moveTaskToStack");
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -9153,7 +9120,30 @@
+ " to createMode=" + createMode + " toTop=" + toTop);
mWindowManager.setDockedStackCreateMode(createMode);
mStackSupervisor.moveTaskToStackLocked(
- taskId, DOCKED_STACK_ID, toTop, !FORCE_FOCUS);
+ taskId, DOCKED_STACK_ID, toTop, !FORCE_FOCUS, "moveTaskToDockedStack");
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ /**
+ * Moves the top activity in the input stackId to the pinned stack.
+ *
+ * @param stackId Id of stack to move the top activity to pinned stack.
+ * @param bounds Bounds to use for pinned stack.
+ *
+ * @return True if the top activity of the input stack was successfully moved to the pinned
+ * stack.
+ */
+ @Override
+ public boolean moveTopActivityToPinnedStack(int stackId, Rect bounds) {
+ enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
+ "moveTopActivityToPinnedStack()");
+ synchronized (this) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ return mStackSupervisor.moveTopStackActivityToPinnedStackLocked(stackId, bounds);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -9480,7 +9470,7 @@
app.addPackage(cpi.applicationInfo.packageName, cpi.applicationInfo.versionCode,
mProcessStats);
}
- ensurePackageDexOpt(cpi.applicationInfo.packageName);
+ notifyPackageUse(cpi.applicationInfo.packageName);
}
}
return providers;
@@ -9498,16 +9488,15 @@
boolean checkedGrants = false;
if (checkUser) {
// Looking for cross-user grants before enforcing the typical cross-users permissions
- int tmpTargetUserId = unsafeConvertIncomingUser(userId);
+ int tmpTargetUserId = mUserController.unsafeConvertIncomingUserLocked(userId);
if (tmpTargetUserId != UserHandle.getUserId(callingUid)) {
if (checkAuthorityGrants(callingUid, cpi, tmpTargetUserId, checkUser)) {
return null;
}
checkedGrants = true;
}
- userId = handleIncomingUser(callingPid, callingUid, userId,
- false, ALLOW_NON_FULL,
- "checkContentProviderPermissionLocked " + cpi.authority, null);
+ userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, false,
+ ALLOW_NON_FULL, "checkContentProviderPermissionLocked " + cpi.authority, null);
if (userId != tmpTargetUserId) {
// When we actually went to determine the final targer user ID, this ended
// up different than our initial check for the authority. This is because
@@ -9846,7 +9835,7 @@
// Make sure that the user who owns this provider is running. If not,
// we don't want to allow it to run.
- if (!isUserRunningLocked(userId, false)) {
+ if (!mUserController.isUserRunningLocked(userId, 0)) {
Slog.w(TAG, "Unable to launch app "
+ cpi.applicationInfo.packageName + "/"
+ cpi.applicationInfo.uid + " for provider "
@@ -10032,8 +10021,8 @@
String name, int userId, IBinder token) {
enforceCallingPermission(android.Manifest.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY,
"Do not have permission in call getContentProviderExternal()");
- userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
- false, ALLOW_FULL_ONLY, "getContentProvider", null);
+ userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, false, ALLOW_FULL_ONLY, "getContentProvider", null);
return getContentProviderExternalUnchecked(name, token, userId);
}
@@ -10344,7 +10333,9 @@
int callingPid = Binder.getCallingPid();
long ident = 0;
boolean clearedIdentity = false;
- userId = unsafeConvertIncomingUser(userId);
+ synchronized (this) {
+ userId = mUserController.unsafeConvertIncomingUserLocked(userId);
+ }
if (canClearIdentity(callingPid, callingUid, userId)) {
clearedIdentity = true;
ident = Binder.clearCallingIdentity();
@@ -10809,19 +10800,6 @@
}
}
- void setOpenGlTraceApp(ApplicationInfo app, String processName) {
- synchronized (this) {
- boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
- if (!isDebuggable) {
- if ((app.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
- throw new SecurityException("Process not debuggable: " + app.packageName);
- }
- }
-
- mOpenGlTraceApp = processName;
- }
- }
-
void setTrackAllocationApp(ApplicationInfo app, String processName) {
synchronized (this) {
boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
@@ -11024,8 +11002,9 @@
@Override
public boolean isAssistDataAllowedOnCurrentActivity() {
- int userId = mCurrentUserId;
+ int userId;
synchronized (this) {
+ userId = mUserController.getCurrentUserIdLocked();
ActivityRecord activity = getFocusedStack().topActivity();
if (activity == null) {
return false;
@@ -11414,7 +11393,7 @@
}
public void noteWakeupAlarm(IIntentSender sender, int sourceUid, String sourcePkg, String tag) {
- if (!(sender instanceof PendingIntentRecord)) {
+ if (sender != null && !(sender instanceof PendingIntentRecord)) {
return;
}
final PendingIntentRecord rec = (PendingIntentRecord)sender;
@@ -11423,7 +11402,12 @@
if (mBatteryStatsService.isOnBattery()) {
mBatteryStatsService.enforceCallingPermission();
int MY_UID = Binder.getCallingUid();
- int uid = rec.uid == MY_UID ? Process.SYSTEM_UID : rec.uid;
+ final int uid;
+ if (sender == null) {
+ uid = sourceUid;
+ } else {
+ uid = rec.uid == MY_UID ? Process.SYSTEM_UID : rec.uid;
+ }
BatteryStatsImpl.Uid.Pkg pkg =
stats.getPackageStatsLocked(sourceUid >= 0 ? sourceUid : uid,
sourcePkg != null ? sourcePkg : rec.key.packageName);
@@ -11433,7 +11417,7 @@
}
public void noteAlarmStart(IIntentSender sender, int sourceUid, String tag) {
- if (!(sender instanceof PendingIntentRecord)) {
+ if (sender != null && !(sender instanceof PendingIntentRecord)) {
return;
}
final PendingIntentRecord rec = (PendingIntentRecord)sender;
@@ -11441,13 +11425,18 @@
synchronized (stats) {
mBatteryStatsService.enforceCallingPermission();
int MY_UID = Binder.getCallingUid();
- int uid = rec.uid == MY_UID ? Process.SYSTEM_UID : rec.uid;
+ final int uid;
+ if (sender == null) {
+ uid = sourceUid;
+ } else {
+ uid = rec.uid == MY_UID ? Process.SYSTEM_UID : rec.uid;
+ }
mBatteryStatsService.noteAlarmStart(tag, sourceUid >= 0 ? sourceUid : uid);
}
}
public void noteAlarmFinish(IIntentSender sender, int sourceUid, String tag) {
- if (!(sender instanceof PendingIntentRecord)) {
+ if (sender != null && !(sender instanceof PendingIntentRecord)) {
return;
}
final PendingIntentRecord rec = (PendingIntentRecord)sender;
@@ -11455,7 +11444,12 @@
synchronized (stats) {
mBatteryStatsService.enforceCallingPermission();
int MY_UID = Binder.getCallingUid();
- int uid = rec.uid == MY_UID ? Process.SYSTEM_UID : rec.uid;
+ final int uid;
+ if (sender == null) {
+ uid = sourceUid;
+ } else {
+ uid = rec.uid == MY_UID ? Process.SYSTEM_UID : rec.uid;
+ }
mBatteryStatsService.noteAlarmFinish(tag, sourceUid >= 0 ? sourceUid : uid);
}
}
@@ -11702,14 +11696,17 @@
private void retrieveSettings() {
final ContentResolver resolver = mContext.getContentResolver();
- String debugApp = Settings.Global.getString(
- resolver, Settings.Global.DEBUG_APP);
+ String debugApp = Settings.Global.getString(resolver, Settings.Global.DEBUG_APP);
boolean waitForDebugger = Settings.Global.getInt(
resolver, Settings.Global.WAIT_FOR_DEBUGGER, 0) != 0;
boolean alwaysFinishActivities = Settings.Global.getInt(
resolver, Settings.Global.ALWAYS_FINISH_ACTIVITIES, 0) != 0;
boolean forceRtl = Settings.Global.getInt(
resolver, Settings.Global.DEVELOPMENT_FORCE_RTL, 0) != 0;
+ int defaultForceResizable = Build.IS_DEBUGGABLE ? 1 : 0;
+ boolean forceResizable = Settings.Global.getInt(
+ resolver, Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES,
+ defaultForceResizable) != 0;
// Transfer any global setting for forcing RTL layout, into a System Property
SystemProperties.set(Settings.Global.DEVELOPMENT_FORCE_RTL, forceRtl ? "1":"0");
@@ -11724,6 +11721,7 @@
mDebugApp = mOrigDebugApp = debugApp;
mWaitForDebugger = mOrigWaitForDebugger = waitForDebugger;
mAlwaysFinishActivities = alwaysFinishActivities;
+ mForceResizableActivites = forceResizable;
// This happens before any activities are started, so we can
// change mConfiguration in-place.
updateConfigurationLocked(configuration, null, true);
@@ -11921,7 +11919,7 @@
}
// TODO: can we still do this with per user encryption?
- final int[] users = getUsersLocked();
+ final int[] users = mUserController.getUsers();
if (users.length <= 0) {
return false;
}
@@ -11948,11 +11946,10 @@
// Make sure we have the current profile info, since it is needed for
// security checks.
- updateCurrentProfileIdsLocked();
+ mUserController.updateCurrentProfileIdsLocked();
mRecentTasks.clear();
- mRecentTasks.addAll(mTaskPersister.restoreTasksLocked(
- getUserManagerLocked().getUserIds()));
+ mRecentTasks.addAll(mTaskPersister.restoreTasksLocked(mUserController.getUserIds()));
mRecentTasks.cleanupLocked(UserHandle.USER_ALL);
mTaskPersister.startPersisting();
@@ -12055,18 +12052,20 @@
retrieveSettings();
loadResourcesOnSystemReady();
-
+ final int currentUserId;
synchronized (this) {
+ currentUserId = mUserController.getCurrentUserIdLocked();
readGrantedUriPermissionsLocked();
}
if (goingCallback != null) goingCallback.run();
+
mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_USER_RUNNING_START,
- Integer.toString(mCurrentUserId), mCurrentUserId);
+ Integer.toString(currentUserId), currentUserId);
mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_START,
- Integer.toString(mCurrentUserId), mCurrentUserId);
- mSystemServiceManager.startUser(mCurrentUserId);
+ Integer.toString(currentUserId), currentUserId);
+ mSystemServiceManager.startUser(currentUserId);
synchronized (this) {
if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
@@ -12094,7 +12093,7 @@
// Start up initial activity.
mBooting = true;
- startHomeActivityLocked(mCurrentUserId, "systemReady");
+ startHomeActivityLocked(currentUserId, "systemReady");
try {
if (AppGlobals.getPackageManager().hasSystemUidErrors()) {
@@ -12115,13 +12114,14 @@
Intent intent = new Intent(Intent.ACTION_USER_STARTED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, mCurrentUserId);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, currentUserId);
broadcastIntentLocked(null, null, intent,
null, null, 0, null, null, null, AppOpsManager.OP_NONE,
- null, false, false, MY_PID, Process.SYSTEM_UID, mCurrentUserId);
+ null, false, false, MY_PID, Process.SYSTEM_UID,
+ currentUserId);
intent = new Intent(Intent.ACTION_USER_STARTING);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, mCurrentUserId);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, currentUserId);
broadcastIntentLocked(null, null, intent,
null, new IIntentReceiver.Stub() {
@Override
@@ -12138,7 +12138,7 @@
Binder.restoreCallingIdentity(ident);
}
mStackSupervisor.resumeTopActivitiesLocked();
- sendUserSwitchBroadcastsLocked(-1, mCurrentUserId);
+ mUserController.sendUserSwitchBroadcastsLocked(-1, currentUserId);
}
}
@@ -12150,7 +12150,8 @@
AppsQueryHelper queryHelper = new AppsQueryHelper(mContext);
Set<String> enableApps = new HashSet<>();
enableApps.addAll(queryHelper.queryApps(AppsQueryHelper.GET_NON_LAUNCHABLE_APPS
- | AppsQueryHelper.GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM,
+ | AppsQueryHelper.GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM
+ | AppsQueryHelper.GET_DEFAULT_IMES,
/* systemAppsOnly */ true, UserHandle.SYSTEM));
ArraySet<String> wlApps = SystemConfig.getInstance().getSystemUserWhitelistedApps();
enableApps.addAll(wlApps);
@@ -12337,7 +12338,7 @@
// launching the report UI under a different user.
app.errorReportReceiver = null;
- for (int userId : mCurrentProfileIds) {
+ for (int userId : mUserController.getCurrentProfileIdsLocked()) {
if (app.userId == userId) {
app.errorReportReceiver = ApplicationErrorReport.getErrorReportReceiver(
mContext, app.info.packageName, app.info.flags);
@@ -13126,6 +13127,13 @@
}
@Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out,
+ FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
+ (new ActivityManagerShellCommand(this, false)).exec(
+ this, in, out, err, args, resultReceiver);
+ }
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (checkCallingPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
@@ -13162,34 +13170,7 @@
}
dumpClient = true;
} else if ("-h".equals(opt)) {
- pw.println("Activity manager dump options:");
- pw.println(" [-a] [-c] [-p package] [-h] [cmd] ...");
- pw.println(" cmd may be one of:");
- pw.println(" a[ctivities]: activity stack state");
- pw.println(" r[recents]: recent activities state");
- pw.println(" b[roadcasts] [PACKAGE_NAME] [history [-s]]: broadcast state");
- pw.println(" i[ntents] [PACKAGE_NAME]: pending intent state");
- pw.println(" p[rocesses] [PACKAGE_NAME]: process state");
- pw.println(" o[om]: out of memory management");
- pw.println(" perm[issions]: URI permission grant state");
- pw.println(" prov[iders] [COMP_SPEC ...]: content provider state");
- pw.println(" provider [COMP_SPEC]: provider client-side state");
- pw.println(" s[ervices] [COMP_SPEC ...]: service state");
- pw.println(" as[sociations]: tracked app associations");
- pw.println(" service [COMP_SPEC]: service client-side state");
- pw.println(" package [PACKAGE_NAME]: all state related to given package");
- pw.println(" all: dump all activities");
- pw.println(" top: dump the top activity");
- pw.println(" write: write all pending state to storage");
- pw.println(" track-associations: enable association tracking");
- pw.println(" untrack-associations: disable and clear association tracking");
- pw.println(" cmd may also be a COMP_SPEC to dump activities.");
- pw.println(" COMP_SPEC may be a component name (com.foo/.myApp),");
- pw.println(" a partial substring in a component name, a");
- pw.println(" hex object identifier.");
- pw.println(" -a: include all available server state.");
- pw.println(" -c: include client state.");
- pw.println(" -p: limit output to given package.");
+ ActivityManagerShellCommand.dumpHelp(pw, true);
return;
} else {
pw.println("Unknown argument: " + opt + "; use -h for help");
@@ -13326,36 +13307,15 @@
synchronized (this) {
mServices.dumpServicesLocked(fd, pw, args, opti, true, dumpClient, dumpPackage);
}
- } else if ("write".equals(cmd)) {
- mTaskPersister.flush();
- pw.println("All tasks persisted.");
- return;
- } else if ("track-associations".equals(cmd)) {
- synchronized (this) {
- if (!mTrackingAssociations) {
- mTrackingAssociations = true;
- pw.println("Association tracking started.");
- } else {
- pw.println("Association tracking already enabled.");
- }
- }
- return;
- } else if ("untrack-associations".equals(cmd)) {
- synchronized (this) {
- if (mTrackingAssociations) {
- mTrackingAssociations = false;
- mAssociations.clear();
- pw.println("Association tracking stopped.");
- } else {
- pw.println("Association tracking not running.");
- }
- }
- return;
} else {
// Dumping a single activity?
if (!dumpActivity(fd, pw, cmd, args, opti, dumpAll)) {
- pw.println("Bad activity command, or no activities match: " + cmd);
- pw.println("Use -h for help.");
+ ActivityManagerShellCommand shell = new ActivityManagerShellCommand(this, true);
+ int res = shell.exec(this, null, fd, null, args, new ResultReceiver(null));
+ if (res < 0) {
+ pw.println("Bad activity command, or no activities match: " + cmd);
+ pw.println("Use -h for help.");
+ }
}
}
if (!more) {
@@ -13606,17 +13566,34 @@
}
if (mActiveUids.size() > 0) {
- if (needSep) {
- pw.println();
+ boolean printed = false;
+ int whichAppId = -1;
+ if (dumpPackage != null) {
+ try {
+ ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(
+ dumpPackage, 0);
+ whichAppId = UserHandle.getAppId(info.uid);
+ } catch (NameNotFoundException e) {
+ e.printStackTrace();
+ }
}
- pw.println(" UID states:");
for (int i=0; i<mActiveUids.size(); i++) {
UidRecord uidRec = mActiveUids.valueAt(i);
+ if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != whichAppId) {
+ continue;
+ }
+ if (!printed) {
+ printed = true;
+ if (needSep) {
+ pw.println();
+ }
+ pw.println(" UID states:");
+ needSep = true;
+ printedAnything = true;
+ }
pw.print(" UID "); UserHandle.formatUid(pw, uidRec.uid);
pw.print(": "); pw.println(uidRec);
}
- needSep = true;
- printedAnything = true;
}
if (mLruProcesses.size() > 0) {
@@ -13794,38 +13771,7 @@
if (dumpPackage == null) {
pw.println();
needSep = false;
- pw.println(" mStartedUsers:");
- for (int i=0; i<mStartedUsers.size(); i++) {
- UserState uss = mStartedUsers.valueAt(i);
- pw.print(" User #"); pw.print(uss.mHandle.getIdentifier());
- pw.print(": "); uss.dump("", pw);
- }
- pw.print(" mStartedUserArray: [");
- for (int i=0; i<mStartedUserArray.length; i++) {
- if (i > 0) pw.print(", ");
- pw.print(mStartedUserArray[i]);
- }
- pw.println("]");
- pw.print(" mUserLru: [");
- for (int i=0; i<mUserLru.size(); i++) {
- if (i > 0) pw.print(", ");
- pw.print(mUserLru.get(i));
- }
- pw.println("]");
- if (dumpAll) {
- pw.print(" mStartedUserArray: "); pw.println(Arrays.toString(mStartedUserArray));
- }
- synchronized (mUserProfileGroupIdsSelfLocked) {
- if (mUserProfileGroupIdsSelfLocked.size() > 0) {
- pw.println(" mUserProfileGroupIds:");
- for (int i=0; i<mUserProfileGroupIdsSelfLocked.size(); i++) {
- pw.print(" User #");
- pw.print(mUserProfileGroupIdsSelfLocked.keyAt(i));
- pw.print(" -> profile #");
- pw.println(mUserProfileGroupIdsSelfLocked.valueAt(i));
- }
- }
- }
+ mUserController.dump(pw, dumpAll);
}
if (mHomeProcess != null && (dumpPackage == null
|| mHomeProcess.pkgList.containsKey(dumpPackage))) {
@@ -13936,15 +13882,6 @@
pw.print(" mMemWatchDumpPid="); pw.print(mMemWatchDumpPid);
pw.print(" mMemWatchDumpUid="); pw.println(mMemWatchDumpUid);
}
- if (mOpenGlTraceApp != null) {
- if (dumpPackage == null || dumpPackage.equals(mOpenGlTraceApp)) {
- if (needSep) {
- pw.println();
- needSep = false;
- }
- pw.println(" mOpenGlTraceApp=" + mOpenGlTraceApp);
- }
- }
if (mTrackAllocationApp != null) {
if (dumpPackage == null || dumpPackage.equals(mTrackAllocationApp)) {
if (needSep) {
@@ -14054,8 +13991,8 @@
pw.print(": ");
pw.print(name);
pw.print(" (");
- pw.print(mProcessList.getMemLevel(adj)/1024);
- pw.println(" kB)");
+ pw.print(stringifySize(mProcessList.getMemLevel(adj), 1024));
+ pw.println(")");
}
boolean dumpOomLocked(FileDescriptor fd, PrintWriter pw, String[] args,
@@ -14836,7 +14773,7 @@
for (int i=0; i<items.size(); i++) {
MemItem mi = items.get(i);
if (!isCompact) {
- pw.print(prefix); pw.printf("%7d kB: ", mi.pss); pw.println(mi.label);
+ pw.printf("%s%s: %s\n", prefix, stringifyKBSize(mi.pss), mi.label);
} else if (mi.isProc) {
pw.print("proc,"); pw.print(tag); pw.print(","); pw.print(mi.shortLabel);
pw.print(","); pw.print(mi.id); pw.print(","); pw.print(mi.pss);
@@ -14912,7 +14849,7 @@
// short checkin version
pw.print("time,"); pw.print(uptime); pw.print(","); pw.println(realtime);
} else {
- pw.println("Applications Memory Usage (kB):");
+ pw.println("Applications Memory Usage (in Kilobytes):");
pw.println("Uptime: " + uptime + " Realtime: " + realtime);
}
}
@@ -14946,6 +14883,26 @@
return longOut;
}
+ private static String stringifySize(long size, int order) {
+ Locale locale = Locale.US;
+ switch (order) {
+ case 1:
+ return String.format(locale, "%,13d", size);
+ case 1024:
+ return String.format(locale, "%,9dK", size / 1024);
+ case 1024 * 1024:
+ return String.format(locale, "%,5dM", size / 1024 / 1024);
+ case 1024 * 1024 * 1024:
+ return String.format(locale, "%,1dG", size / 1024 / 1024 / 1024);
+ default:
+ throw new IllegalArgumentException("Invalid size order");
+ }
+ }
+
+ private static String stringifyKBSize(long size) {
+ return stringifySize(size * 1024, 1024);
+ }
+
final void dumpApplicationMemoryUsage(FileDescriptor fd,
PrintWriter pw, String prefix, String[] args, boolean brief, PrintWriter categoryPw) {
boolean dumpDetails = false;
@@ -15299,8 +15256,8 @@
}
if (!brief) {
if (!isCompact) {
- pw.print("Total RAM: "); pw.print(memInfo.getTotalSizeKb());
- pw.print(" kB (status ");
+ pw.print("Total RAM: "); pw.print(stringifyKBSize(memInfo.getTotalSizeKb()));
+ pw.print(" (status ");
switch (mLastMemoryLevel) {
case ProcessStats.ADJ_MEM_FACTOR_NORMAL:
pw.println("normal)");
@@ -15319,11 +15276,16 @@
pw.println(")");
break;
}
- pw.print(" Free RAM: "); pw.print(cachedPss + memInfo.getCachedSizeKb()
- + memInfo.getFreeSizeKb()); pw.print(" kB (");
- pw.print(cachedPss); pw.print(" cached pss + ");
- pw.print(memInfo.getCachedSizeKb()); pw.print(" cached kernel + ");
- pw.print(memInfo.getFreeSizeKb()); pw.println(" free)");
+ pw.print(" Free RAM: ");
+ pw.print(stringifyKBSize(cachedPss + memInfo.getCachedSizeKb()
+ + memInfo.getFreeSizeKb()));
+ pw.print(" (");
+ pw.print(stringifyKBSize(cachedPss));
+ pw.print(" cached pss + ");
+ pw.print(stringifyKBSize(memInfo.getCachedSizeKb()));
+ pw.print(" cached kernel + ");
+ pw.print(stringifyKBSize(memInfo.getFreeSizeKb()));
+ pw.println(" free)");
} else {
pw.print("ram,"); pw.print(memInfo.getTotalSizeKb()); pw.print(",");
pw.print(cachedPss + memInfo.getCachedSizeKb()
@@ -15332,24 +15294,25 @@
}
}
if (!isCompact) {
- pw.print(" Used RAM: "); pw.print(totalPss - cachedPss
- + memInfo.getKernelUsedSizeKb()); pw.print(" kB (");
- pw.print(totalPss - cachedPss); pw.print(" used pss + ");
- pw.print(memInfo.getKernelUsedSizeKb()); pw.print(" kernel)\n");
- pw.print(" Lost RAM: "); pw.print(memInfo.getTotalSizeKb()
+ pw.print(" Used RAM: "); pw.print(stringifyKBSize(totalPss - cachedPss
+ + memInfo.getKernelUsedSizeKb())); pw.print(" (");
+ pw.print(stringifyKBSize(totalPss - cachedPss)); pw.print(" used pss + ");
+ pw.print(stringifyKBSize(memInfo.getKernelUsedSizeKb())); pw.print(" kernel)\n");
+ pw.print(" Lost RAM: "); pw.println(stringifyKBSize(memInfo.getTotalSizeKb()
- totalPss - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
- - memInfo.getKernelUsedSizeKb()); pw.println(" kB");
+ - memInfo.getKernelUsedSizeKb()));
}
if (!brief) {
if (memInfo.getZramTotalSizeKb() != 0) {
if (!isCompact) {
- pw.print(" ZRAM: "); pw.print(memInfo.getZramTotalSizeKb());
- pw.print(" kB physical used for ");
- pw.print(memInfo.getSwapTotalSizeKb()
- - memInfo.getSwapFreeSizeKb());
- pw.print(" kB in swap (");
- pw.print(memInfo.getSwapTotalSizeKb());
- pw.println(" kB total swap)");
+ pw.print(" ZRAM: ");
+ pw.print(stringifyKBSize(memInfo.getZramTotalSizeKb()));
+ pw.print(" physical used for ");
+ pw.print(stringifyKBSize(memInfo.getSwapTotalSizeKb()
+ - memInfo.getSwapFreeSizeKb()));
+ pw.print(" in swap (");
+ pw.print(stringifyKBSize(memInfo.getSwapTotalSizeKb()));
+ pw.println(" total swap)");
} else {
pw.print("zram,"); pw.print(memInfo.getZramTotalSizeKb()); pw.print(",");
pw.print(memInfo.getSwapTotalSizeKb()); pw.print(",");
@@ -15360,23 +15323,23 @@
if (!isCompact) {
if (ksm[KSM_SHARING] != 0 || ksm[KSM_SHARED] != 0 || ksm[KSM_UNSHARED] != 0
|| ksm[KSM_VOLATILE] != 0) {
- pw.print(" KSM: "); pw.print(ksm[KSM_SHARING]);
- pw.print(" kB saved from shared ");
- pw.print(ksm[KSM_SHARED]); pw.println(" kB");
- pw.print(" "); pw.print(ksm[KSM_UNSHARED]);
- pw.print(" kB unshared; ");
- pw.print(ksm[KSM_VOLATILE]); pw.println(" kB volatile");
+ pw.print(" KSM: "); pw.print(stringifyKBSize(ksm[KSM_SHARING]));
+ pw.print(" saved from shared ");
+ pw.print(stringifyKBSize(ksm[KSM_SHARED]));
+ pw.print(" "); pw.print(stringifyKBSize(ksm[KSM_UNSHARED]));
+ pw.print(" unshared; ");
+ pw.print(stringifyKBSize(
+ ksm[KSM_VOLATILE])); pw.println(" volatile");
}
pw.print(" Tuning: ");
pw.print(ActivityManager.staticGetMemoryClass());
pw.print(" (large ");
pw.print(ActivityManager.staticGetLargeMemoryClass());
pw.print("), oom ");
- pw.print(mProcessList.getMemLevel(ProcessList.CACHED_APP_MAX_ADJ)/1024);
- pw.print(" kB");
+ pw.print(stringifySize(
+ mProcessList.getMemLevel(ProcessList.CACHED_APP_MAX_ADJ), 1024));
pw.print(", restore limit ");
- pw.print(mProcessList.getCachedRestoreThresholdKb());
- pw.print(" kB");
+ pw.print(stringifyKBSize(mProcessList.getCachedRestoreThresholdKb()));
if (ActivityManager.isLowRamDeviceStatic()) {
pw.print(" (low-ram)");
}
@@ -15414,12 +15377,12 @@
sb.append(ProcessList.makeProcStateString(procState));
sb.append(' ');
ProcessList.appendRamKb(sb, pss);
- sb.append(" kB: ");
+ sb.append(": ");
sb.append(name);
if (memtrack > 0) {
sb.append(" (");
- sb.append(memtrack);
- sb.append(" kB memtrack)");
+ sb.append(stringifyKBSize(memtrack));
+ sb.append(" memtrack)");
}
}
@@ -15573,11 +15536,11 @@
fullJavaBuilder.append(" ");
ProcessList.appendRamKb(fullJavaBuilder, totalPss);
- fullJavaBuilder.append(" kB: TOTAL");
+ fullJavaBuilder.append(": TOTAL");
if (totalMemtrack > 0) {
fullJavaBuilder.append(" (");
- fullJavaBuilder.append(totalMemtrack);
- fullJavaBuilder.append(" kB memtrack)");
+ fullJavaBuilder.append(stringifyKBSize(totalMemtrack));
+ fullJavaBuilder.append(" memtrack)");
} else {
}
fullJavaBuilder.append("\n");
@@ -15589,47 +15552,54 @@
StringBuilder memInfoBuilder = new StringBuilder(1024);
Debug.getMemInfo(infos);
memInfoBuilder.append(" MemInfo: ");
- memInfoBuilder.append(infos[Debug.MEMINFO_SLAB]).append(" kB slab, ");
- memInfoBuilder.append(infos[Debug.MEMINFO_SHMEM]).append(" kB shmem, ");
- memInfoBuilder.append(infos[Debug.MEMINFO_VM_ALLOC_USED]).append(" kB vm alloc, ");
- memInfoBuilder.append(infos[Debug.MEMINFO_PAGE_TABLES]).append(" kB page tables ");
- memInfoBuilder.append(infos[Debug.MEMINFO_KERNEL_STACK]).append(" kB kernel stack\n");
+ memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_SLAB])).append(" slab, ");
+ memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_SHMEM])).append(" shmem, ");
+ memInfoBuilder.append(stringifyKBSize(
+ infos[Debug.MEMINFO_VM_ALLOC_USED])).append(" vm alloc, ");
+ memInfoBuilder.append(stringifyKBSize(
+ infos[Debug.MEMINFO_PAGE_TABLES])).append(" page tables ");
+ memInfoBuilder.append(stringifyKBSize(
+ infos[Debug.MEMINFO_KERNEL_STACK])).append(" kernel stack\n");
memInfoBuilder.append(" ");
- memInfoBuilder.append(infos[Debug.MEMINFO_BUFFERS]).append(" kB buffers, ");
- memInfoBuilder.append(infos[Debug.MEMINFO_CACHED]).append(" kB cached, ");
- memInfoBuilder.append(infos[Debug.MEMINFO_MAPPED]).append(" kB mapped, ");
- memInfoBuilder.append(infos[Debug.MEMINFO_FREE]).append(" kB free\n");
+ memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_BUFFERS])).append(" buffers, ");
+ memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_CACHED])).append(" cached, ");
+ memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_MAPPED])).append(" mapped, ");
+ memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_FREE])).append(" free\n");
if (infos[Debug.MEMINFO_ZRAM_TOTAL] != 0) {
memInfoBuilder.append(" ZRAM: ");
- memInfoBuilder.append(infos[Debug.MEMINFO_ZRAM_TOTAL]);
- memInfoBuilder.append(" kB RAM, ");
- memInfoBuilder.append(infos[Debug.MEMINFO_SWAP_TOTAL]);
- memInfoBuilder.append(" kB swap total, ");
- memInfoBuilder.append(infos[Debug.MEMINFO_SWAP_FREE]);
- memInfoBuilder.append(" kB swap free\n");
+ memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_ZRAM_TOTAL]));
+ memInfoBuilder.append(" RAM, ");
+ memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_SWAP_TOTAL]));
+ memInfoBuilder.append(" swap total, ");
+ memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_SWAP_FREE]));
+ memInfoBuilder.append(" swap free\n");
}
final long[] ksm = getKsmInfo();
if (ksm[KSM_SHARING] != 0 || ksm[KSM_SHARED] != 0 || ksm[KSM_UNSHARED] != 0
|| ksm[KSM_VOLATILE] != 0) {
- memInfoBuilder.append(" KSM: "); memInfoBuilder.append(ksm[KSM_SHARING]);
- memInfoBuilder.append(" kB saved from shared ");
- memInfoBuilder.append(ksm[KSM_SHARED]); memInfoBuilder.append(" kB\n");
- memInfoBuilder.append(" "); memInfoBuilder.append(ksm[KSM_UNSHARED]);
- memInfoBuilder.append(" kB unshared; ");
- memInfoBuilder.append(ksm[KSM_VOLATILE]); memInfoBuilder.append(" kB volatile\n");
+ memInfoBuilder.append(" KSM: ");
+ memInfoBuilder.append(stringifyKBSize(ksm[KSM_SHARING]));
+ memInfoBuilder.append(" saved from shared ");
+ memInfoBuilder.append(stringifyKBSize(ksm[KSM_SHARED]));
+ memInfoBuilder.append("\n ");
+ memInfoBuilder.append(stringifyKBSize(ksm[KSM_UNSHARED]));
+ memInfoBuilder.append(" unshared; ");
+ memInfoBuilder.append(stringifyKBSize(ksm[KSM_VOLATILE]));
+ memInfoBuilder.append(" volatile\n");
}
memInfoBuilder.append(" Free RAM: ");
- memInfoBuilder.append(cachedPss + memInfo.getCachedSizeKb()
- + memInfo.getFreeSizeKb());
- memInfoBuilder.append(" kB\n");
+ memInfoBuilder.append(stringifyKBSize(cachedPss + memInfo.getCachedSizeKb()
+ + memInfo.getFreeSizeKb()));
+ memInfoBuilder.append("\n");
memInfoBuilder.append(" Used RAM: ");
- memInfoBuilder.append(totalPss - cachedPss + memInfo.getKernelUsedSizeKb());
- memInfoBuilder.append(" kB\n");
+ memInfoBuilder.append(stringifyKBSize(
+ totalPss - cachedPss + memInfo.getKernelUsedSizeKb()));
+ memInfoBuilder.append("\n");
memInfoBuilder.append(" Lost RAM: ");
- memInfoBuilder.append(memInfo.getTotalSizeKb()
+ memInfoBuilder.append(stringifyKBSize(memInfo.getTotalSizeKb()
- totalPss - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
- - memInfo.getKernelUsedSizeKb());
- memInfoBuilder.append(" kB\n");
+ - memInfo.getKernelUsedSizeKb()));
+ memInfoBuilder.append("\n");
Slog.i(TAG, "Low on memory:");
Slog.i(TAG, shortNativeBuilder.toString());
Slog.i(TAG, fullJavaBuilder.toString());
@@ -16089,104 +16059,10 @@
@Override
public int handleIncomingUser(int callingPid, int callingUid, int userId, boolean allowAll,
boolean requireFull, String name, String callerPackage) {
- return handleIncomingUser(callingPid, callingUid, userId, allowAll,
+ return mUserController.handleIncomingUser(callingPid, callingUid, userId, allowAll,
requireFull ? ALLOW_FULL_ONLY : ALLOW_NON_FULL, name, callerPackage);
}
- int unsafeConvertIncomingUser(int userId) {
- return (userId == UserHandle.USER_CURRENT || userId == UserHandle.USER_CURRENT_OR_SELF)
- ? mCurrentUserId : userId;
- }
-
- int handleIncomingUser(int callingPid, int callingUid, int userId, boolean allowAll,
- int allowMode, String name, String callerPackage) {
- final int callingUserId = UserHandle.getUserId(callingUid);
- if (callingUserId == userId) {
- return userId;
- }
-
- // Note that we may be accessing mCurrentUserId outside of a lock...
- // shouldn't be a big deal, if this is being called outside
- // of a locked context there is intrinsically a race with
- // the value the caller will receive and someone else changing it.
- // We assume that USER_CURRENT_OR_SELF will use the current user; later
- // we will switch to the calling user if access to the current user fails.
- int targetUserId = unsafeConvertIncomingUser(userId);
-
- if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
- final boolean allow;
- if (checkComponentPermission(INTERACT_ACROSS_USERS_FULL, callingPid,
- callingUid, -1, true) == PackageManager.PERMISSION_GRANTED) {
- // If the caller has this permission, they always pass go. And collect $200.
- allow = true;
- } else if (allowMode == ALLOW_FULL_ONLY) {
- // We require full access, sucks to be you.
- allow = false;
- } else if (checkComponentPermission(INTERACT_ACROSS_USERS, callingPid,
- callingUid, -1, true) != PackageManager.PERMISSION_GRANTED) {
- // If the caller does not have either permission, they are always doomed.
- allow = false;
- } else if (allowMode == ALLOW_NON_FULL) {
- // We are blanket allowing non-full access, you lucky caller!
- allow = true;
- } else if (allowMode == ALLOW_NON_FULL_IN_PROFILE) {
- // We may or may not allow this depending on whether the two users are
- // in the same profile.
- synchronized (mUserProfileGroupIdsSelfLocked) {
- int callingProfile = mUserProfileGroupIdsSelfLocked.get(callingUserId,
- UserInfo.NO_PROFILE_GROUP_ID);
- int targetProfile = mUserProfileGroupIdsSelfLocked.get(targetUserId,
- UserInfo.NO_PROFILE_GROUP_ID);
- allow = callingProfile != UserInfo.NO_PROFILE_GROUP_ID
- && callingProfile == targetProfile;
- }
- } else {
- throw new IllegalArgumentException("Unknown mode: " + allowMode);
- }
- if (!allow) {
- if (userId == UserHandle.USER_CURRENT_OR_SELF) {
- // In this case, they would like to just execute as their
- // owner user instead of failing.
- targetUserId = callingUserId;
- } else {
- StringBuilder builder = new StringBuilder(128);
- builder.append("Permission Denial: ");
- builder.append(name);
- if (callerPackage != null) {
- builder.append(" from ");
- builder.append(callerPackage);
- }
- builder.append(" asks to run as user ");
- builder.append(userId);
- builder.append(" but is calling from user ");
- builder.append(UserHandle.getUserId(callingUid));
- builder.append("; this requires ");
- builder.append(INTERACT_ACROSS_USERS_FULL);
- if (allowMode != ALLOW_FULL_ONLY) {
- builder.append(" or ");
- builder.append(INTERACT_ACROSS_USERS);
- }
- String msg = builder.toString();
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
- }
- }
- }
- if (!allowAll && targetUserId < 0) {
- throw new IllegalArgumentException(
- "Call does not support special user #" + targetUserId);
- }
- // Check shell permission
- if (callingUid == Process.SHELL_UID && targetUserId >= UserHandle.USER_SYSTEM) {
- if (mUserManager.hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES,
- targetUserId)) {
- throw new SecurityException("Shell does not have permission to access user "
- + targetUserId + "\n " + Debug.getCallers(3));
- }
- }
- return targetUserId;
- }
-
boolean isSingleton(String componentProcessName, ApplicationInfo aInfo,
String className, int flags) {
boolean result = false;
@@ -16499,8 +16375,8 @@
callingPid = Binder.getCallingPid();
}
- userId = handleIncomingUser(callingPid, callingUid, userId,
- true, ALLOW_FULL_ONLY, "registerReceiver", callerPackage);
+ userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
+ ALLOW_FULL_ONLY, "registerReceiver", callerPackage);
Iterator<String> actions = filter.actionsIterator();
if (actions == null) {
@@ -16690,8 +16566,8 @@
for (int user : users) {
// Skip users that have Shell restrictions
if (callingUid == Process.SHELL_UID
- && getUserManagerLocked().hasUserRestriction(
- UserManager.DISALLOW_DEBUGGING_FEATURES, user)) {
+ && mUserController.hasUserRestriction(
+ UserManager.DISALLOW_DEBUGGING_FEATURES, user)) {
continue;
}
List<ResolveInfo> newReceivers = AppGlobals.getPackageManager()
@@ -16758,10 +16634,10 @@
return receivers;
}
- private final int broadcastIntentLocked(ProcessRecord callerApp,
+ final int broadcastIntentLocked(ProcessRecord callerApp,
String callerPackage, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
- Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle options,
+ Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {
intent = new Intent(intent);
@@ -16780,14 +16656,14 @@
Slog.w(TAG, "Broadcast " + intent + " not ordered but result callback requested!");
}
- userId = handleIncomingUser(callingPid, callingUid, userId,
- true, ALLOW_NON_FULL, "broadcast", callerPackage);
+ userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
+ ALLOW_NON_FULL, "broadcast", callerPackage);
// Make sure that the user who is receiving this broadcast is running.
// If not, we will just skip it. Make an exception for shutdown broadcasts
// and upgrade steps.
- if (userId != UserHandle.USER_ALL && !isUserRunningLocked(userId, false)) {
+ if (userId != UserHandle.USER_ALL && !mUserController.isUserRunningLocked(userId, 0)) {
if ((callingUid != Process.SYSTEM_UID
|| (intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0)
&& !Intent.ACTION_SHUTDOWN.equals(intent.getAction())) {
@@ -16798,8 +16674,8 @@
}
BroadcastOptions brOptions = null;
- if (options != null) {
- brOptions = new BroadcastOptions(options);
+ if (bOptions != null) {
+ brOptions = new BroadcastOptions(bOptions);
if (brOptions.getTemporaryAppWhitelistDuration() > 0) {
// See if the caller is allowed to do this. Note we are checking against
// the actual real caller (not whoever provided the operation as say a
@@ -17075,7 +16951,7 @@
int[] users;
if (userId == UserHandle.USER_ALL) {
// Caller wants broadcast to go to all started users.
- users = mStartedUserArray;
+ users = mUserController.getStartedUserArrayLocked();
} else {
// Caller wants broadcast to go to one specific user.
users = new int[] {userId};
@@ -17092,9 +16968,8 @@
if (intent.getComponent() == null) {
if (userId == UserHandle.USER_ALL && callingUid == Process.SHELL_UID) {
// Query one target user at a time, excluding shell-restricted users
- UserManagerService ums = getUserManagerLocked();
for (int i = 0; i < users.length; i++) {
- if (ums.hasUserRestriction(
+ if (mUserController.hasUserRestriction(
UserManager.DISALLOW_DEBUGGING_FEATURES, users[i])) {
continue;
}
@@ -17265,7 +17140,7 @@
public final int broadcastIntent(IApplicationThread caller,
Intent intent, String resolvedType, IIntentReceiver resultTo,
int resultCode, String resultData, Bundle resultExtras,
- String[] requiredPermissions, int appOp, Bundle options,
+ String[] requiredPermissions, int appOp, Bundle bOptions,
boolean serialized, boolean sticky, int userId) {
enforceNotIsolatedCaller("broadcastIntent");
synchronized(this) {
@@ -17289,7 +17164,7 @@
int broadcastIntentInPackage(String packageName, int uid,
Intent intent, String resolvedType, IIntentReceiver resultTo,
int resultCode, String resultData, Bundle resultExtras,
- String requiredPermission, Bundle options, boolean serialized, boolean sticky,
+ String requiredPermission, Bundle bOptions, boolean serialized, boolean sticky,
int userId) {
synchronized(this) {
intent = verifyBroadcastLocked(intent);
@@ -17299,7 +17174,7 @@
: new String[] {requiredPermission};
int res = broadcastIntentLocked(null, packageName, intent, resolvedType,
resultTo, resultCode, resultData, resultExtras,
- requiredPermissions, AppOpsManager.OP_NONE, options, serialized,
+ requiredPermissions, AppOpsManager.OP_NONE, bOptions, serialized,
sticky, -1, uid, userId);
Binder.restoreCallingIdentity(origId);
return res;
@@ -17312,7 +17187,7 @@
throw new IllegalArgumentException("File descriptors passed in Intent");
}
- userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
userId, true, ALLOW_NON_FULL, "removeStickyBroadcast", null);
synchronized(this) {
@@ -17396,7 +17271,7 @@
IInstrumentationWatcher watcher, IUiAutomationConnection uiAutomationConnection,
int userId, String abiOverride) {
enforceNotIsolatedCaller("startInstrumentation");
- userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
userId, false, ALLOW_FULL_ONLY, "startInstrumentation", null);
// Refuse possible leaked file descriptors
if (arguments != null && arguments.hasFileDescriptors()) {
@@ -17592,14 +17467,16 @@
throw new IllegalArgumentException("Removing home stack is not allowed.");
}
synchronized (this) {
+ long origId = Binder.clearCallingIdentity();
ActivityStack stack = mStackSupervisor.getStack(stackId);
if (stack != null) {
ArrayList<TaskRecord> tasks = stack.getAllTasks();
for (int i = tasks.size() - 1; i >= 0; i--) {
- removeTaskByIdLocked(tasks.get(i).taskId, true /* killProcess */,
+ removeTaskByIdLocked(tasks.get(i).taskId, false /* killProcess */,
!REMOVE_FROM_RECENTS);
}
}
+ Binder.restoreCallingIdentity(origId);
}
}
@@ -17663,6 +17540,13 @@
}
}
+ void updateUserConfigurationLocked() {
+ Configuration configuration = new Configuration(mConfiguration);
+ Settings.System.getConfigurationForUser(mContext.getContentResolver(), configuration,
+ mUserController.getCurrentUserIdLocked());
+ updateConfigurationLocked(configuration, null, false);
+ }
+
boolean updateConfigurationLocked(Configuration values,
ActivityRecord starting, boolean initLocale) {
// pass UserHandle.USER_NULL as userId because we don't persist configuration for any user
@@ -17707,7 +17591,8 @@
newConfig.seq = mConfigurationSeq;
mConfiguration = newConfig;
Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + newConfig);
- mUsageStatsService.reportConfigurationChange(newConfig, mCurrentUserId);
+ mUsageStatsService.reportConfigurationChange(newConfig,
+ mUserController.getCurrentUserIdLocked());
//mUsageStatsService.noteStartConfig(newConfig);
final Configuration configCopy = new Configuration(mConfiguration);
@@ -18052,6 +17937,7 @@
// Examine all activities if not already foreground.
if (!foregroundActivities && activitiesSize > 0) {
+ int minLayer = ProcessList.VISIBLE_APP_LAYER_MAX;
for (int j = 0; j < activitiesSize; j++) {
final ActivityRecord r = app.activities.get(j);
if (r.app != app) {
@@ -18072,6 +17958,12 @@
app.cached = false;
app.empty = false;
foregroundActivities = true;
+ if (r.task != null && minLayer > 0) {
+ final int layer = r.task.mLayerRank;
+ if (layer >= 0 && minLayer > layer) {
+ minLayer = layer;
+ }
+ }
break;
} else if (r.state == ActivityState.PAUSING || r.state == ActivityState.PAUSED) {
if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
@@ -18112,6 +18004,9 @@
}
}
}
+ if (adj == ProcessList.VISIBLE_APP_ADJ) {
+ adj += minLayer;
+ }
}
if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
@@ -18321,11 +18216,11 @@
&& clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ
&& adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
adj = ProcessList.PERCEPTIBLE_APP_ADJ;
- } else if (clientAdj > ProcessList.VISIBLE_APP_ADJ) {
+ } else if (clientAdj >= ProcessList.PERCEPTIBLE_APP_ADJ) {
adj = clientAdj;
} else {
if (adj > ProcessList.VISIBLE_APP_ADJ) {
- adj = ProcessList.VISIBLE_APP_ADJ;
+ adj = Math.max(clientAdj, ProcessList.VISIBLE_APP_ADJ);
}
}
if (!client.cached) {
@@ -18966,7 +18861,8 @@
}
}
- private final boolean applyOomAdjLocked(ProcessRecord app, boolean doingAll, long now) {
+ private final boolean applyOomAdjLocked(ProcessRecord app, boolean doingAll, long now,
+ long nowElapsed) {
boolean success = true;
if (app.curRawAdj != app.setRawAdj) {
@@ -19070,7 +18966,7 @@
if (app.setProcState != app.curProcState) {
if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,
"Proc state change of " + app.processName
- + " to " + app.curProcState);
+ + " to " + app.curProcState);
boolean setImportant = app.setProcState < ActivityManager.PROCESS_STATE_SERVICE;
boolean curImportant = app.curProcState < ActivityManager.PROCESS_STATE_SERVICE;
if (setImportant && !curImportant) {
@@ -19081,14 +18977,14 @@
BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
synchronized (stats) {
app.lastWakeTime = stats.getProcessWakeTime(app.info.uid,
- app.pid, SystemClock.elapsedRealtime());
+ app.pid, nowElapsed);
}
app.lastCpuTime = app.curCpuTime;
}
// Inform UsageStats of important process state change
// Must be called before updating setProcState
- maybeUpdateUsageStatsLocked(app);
+ maybeUpdateUsageStatsLocked(app, nowElapsed);
app.setProcState = app.curProcState;
if (app.setProcState >= ActivityManager.PROCESS_STATE_HOME) {
@@ -19099,6 +18995,11 @@
} else {
app.procStateChanged = true;
}
+ } else if (app.reportedInteraction && (nowElapsed-app.interactionEventTime)
+ > USAGE_STATS_INTERACTION_INTERVAL) {
+ // For apps that sit around for a long time in the interactive state, we need
+ // to report this at least once a day so they don't go idle.
+ maybeUpdateUsageStatsLocked(app, nowElapsed);
}
if (changes != 0) {
@@ -19181,7 +19082,7 @@
String authority) {
if (app == null) return;
if (app.curProcState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
- UserState userState = mStartedUsers.get(app.userId);
+ UserState userState = mUserController.getStartedUserStateLocked(app.userId);
if (userState == null) return;
final long now = SystemClock.elapsedRealtime();
Long lastReported = userState.mProviderLastReportedFg.get(authority);
@@ -19193,7 +19094,7 @@
}
}
- private void maybeUpdateUsageStatsLocked(ProcessRecord app) {
+ private void maybeUpdateUsageStatsLocked(ProcessRecord app, long nowElapsed) {
if (DEBUG_USAGE_STATS) {
Slog.d(TAG, "Checking proc [" + Arrays.toString(app.getPackageList())
+ "] state changes: old = " + app.setProcState + ", new = "
@@ -19210,19 +19111,20 @@
isInteraction = true;
app.fgInteractionTime = 0;
} else if (app.curProcState <= ActivityManager.PROCESS_STATE_TOP_SLEEPING) {
- final long now = SystemClock.elapsedRealtime();
if (app.fgInteractionTime == 0) {
- app.fgInteractionTime = now;
+ app.fgInteractionTime = nowElapsed;
isInteraction = false;
} else {
- isInteraction = now > app.fgInteractionTime + SERVICE_USAGE_INTERACTION_TIME;
+ isInteraction = nowElapsed > app.fgInteractionTime + SERVICE_USAGE_INTERACTION_TIME;
}
} else {
isInteraction = app.curProcState
<= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
app.fgInteractionTime = 0;
}
- if (isInteraction && !app.reportedInteraction) {
+ if (isInteraction && (!app.reportedInteraction
+ || (nowElapsed-app.interactionEventTime) > USAGE_STATS_INTERACTION_INTERVAL)) {
+ app.interactionEventTime = nowElapsed;
String[] packages = app.getPackageList();
if (packages != null) {
for (int i = 0; i < packages.length; i++) {
@@ -19232,6 +19134,9 @@
}
}
app.reportedInteraction = isInteraction;
+ if (!isInteraction) {
+ app.interactionEventTime = 0;
+ }
}
private final void setProcessTrackerStateLocked(ProcessRecord proc, int memFactor, long now) {
@@ -19254,7 +19159,7 @@
computeOomAdjLocked(app, cachedAdj, TOP_APP, doingAll, now);
- return applyOomAdjLocked(app, doingAll, now);
+ return applyOomAdjLocked(app, doingAll, now, SystemClock.elapsedRealtime());
}
final void updateProcessForegroundLocked(ProcessRecord proc, boolean isForeground,
@@ -19346,6 +19251,7 @@
final ActivityRecord TOP_ACT = resumedAppLocked();
final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;
final long now = SystemClock.uptimeMillis();
+ final long nowElapsed = SystemClock.elapsedRealtime();
final long oldTime = now - ProcessList.MAX_EMPTY_TIME;
final int N = mLruProcesses.size();
@@ -19363,6 +19269,8 @@
uidRec.reset();
}
+ mStackSupervisor.rankTaskLayersIfNeeded();
+
mAdjSeq++;
mNewNumServiceProcs = 0;
mNewNumAServiceProcs = 0;
@@ -19472,7 +19380,7 @@
}
}
- applyOomAdjLocked(app, true, now);
+ applyOomAdjLocked(app, true, now, nowElapsed);
// Count the number of process types.
switch (app.curProcState) {
@@ -19901,7 +19809,7 @@
}
private ProcessRecord findProcessLocked(String process, int userId, String callName) {
- userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
userId, true, ALLOW_FULL_ONLY, callName, null);
ProcessRecord proc = null;
try {
@@ -20059,52 +19967,7 @@
*/
@Override
public boolean startUserInBackground(final int userId) {
- return startUser(userId, /* foreground */ false);
- }
-
- /**
- * Start user, if its not already running, and bring it to foreground.
- */
- boolean startUserInForeground(final int userId, Dialog dlg) {
- boolean result = startUser(userId, /* foreground */ true);
- dlg.dismiss();
- return result;
- }
-
- /**
- * Refreshes the list of users related to the current user when either a
- * user switch happens or when a new related user is started in the
- * background.
- */
- private void updateCurrentProfileIdsLocked() {
- final List<UserInfo> profiles = getUserManagerLocked().getProfiles(
- mCurrentUserId, false /* enabledOnly */);
- int[] currentProfileIds = new int[profiles.size()]; // profiles will not be null
- for (int i = 0; i < currentProfileIds.length; i++) {
- currentProfileIds[i] = profiles.get(i).id;
- }
- mCurrentProfileIds = currentProfileIds;
-
- synchronized (mUserProfileGroupIdsSelfLocked) {
- mUserProfileGroupIdsSelfLocked.clear();
- final List<UserInfo> users = getUserManagerLocked().getUsers(false);
- for (int i = 0; i < users.size(); i++) {
- UserInfo user = users.get(i);
- if (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID) {
- mUserProfileGroupIdsSelfLocked.put(user.id, user.profileGroupId);
- }
- }
- }
- }
-
- private Set<Integer> getProfileIdsLocked(int userId) {
- Set<Integer> userIds = new HashSet<Integer>();
- final List<UserInfo> profiles = getUserManagerLocked().getProfiles(
- userId, false /* enabledOnly */);
- for (UserInfo user : profiles) {
- userIds.add(Integer.valueOf(user.id));
- }
- return userIds;
+ return mUserController.startUser(userId, /* foreground */ false);
}
@Override
@@ -20112,7 +19975,7 @@
enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, userId);
String userName;
synchronized (this) {
- UserInfo userInfo = getUserManagerLocked().getUserInfo(userId);
+ UserInfo userInfo = mUserController.getUserInfo(userId);
if (userInfo == null) {
Slog.w(TAG, "No user info for user #" + userId);
return false;
@@ -20122,392 +19985,13 @@
return false;
}
userName = userInfo.name;
- mTargetUserId = userId;
+ mUserController.setTargetUserIdLocked(userId);
}
mUiHandler.removeMessages(START_USER_SWITCH_MSG);
mUiHandler.sendMessage(mUiHandler.obtainMessage(START_USER_SWITCH_MSG, userId, 0, userName));
return true;
}
- private void showUserSwitchDialog(int userId, String userName) {
- // The dialog will show and then initiate the user switch by calling startUserInForeground
- Dialog d = new UserSwitchingDialog(this, mContext, userId, userName,
- true /* above system */);
- d.show();
- }
-
- private boolean startUser(final int userId, final boolean foreground) {
- if (checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
- != PackageManager.PERMISSION_GRANTED) {
- String msg = "Permission Denial: switchUser() from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid()
- + " requires " + INTERACT_ACROSS_USERS_FULL;
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
- }
-
- if (DEBUG_MU) Slog.i(TAG_MU, "starting userid:" + userId + " fore:" + foreground);
-
- final long ident = Binder.clearCallingIdentity();
- try {
- synchronized (this) {
- final int oldUserId = mCurrentUserId;
- if (oldUserId == userId) {
- return true;
- }
-
- mStackSupervisor.setLockTaskModeLocked(null, ActivityManager.LOCK_TASK_MODE_NONE,
- "startUser", false);
-
- final UserInfo userInfo = getUserManagerLocked().getUserInfo(userId);
- if (userInfo == null) {
- Slog.w(TAG, "No user info for user #" + userId);
- return false;
- }
- if (foreground && userInfo.isManagedProfile()) {
- Slog.w(TAG, "Cannot switch to User #" + userId + ": not a full user");
- return false;
- }
-
- if (foreground) {
- mWindowManager.startFreezingScreen(R.anim.screen_user_exit,
- R.anim.screen_user_enter);
- }
-
- boolean needStart = false;
-
- // If the user we are switching to is not currently started, then
- // we need to start it now.
- if (mStartedUsers.get(userId) == null) {
- mStartedUsers.put(userId, new UserState(new UserHandle(userId), false));
- updateStartedUserArrayLocked();
- needStart = true;
- }
-
- final Integer userIdInt = Integer.valueOf(userId);
- mUserLru.remove(userIdInt);
- mUserLru.add(userIdInt);
-
- if (foreground) {
- mCurrentUserId = userId;
- updateUserConfigurationLocked();
- mTargetUserId = UserHandle.USER_NULL; // reset, mCurrentUserId has caught up
- updateCurrentProfileIdsLocked();
- mWindowManager.setCurrentUser(userId, mCurrentProfileIds);
- // Once the internal notion of the active user has switched, we lock the device
- // with the option to show the user switcher on the keyguard.
- mWindowManager.lockNow(null);
- } else {
- final Integer currentUserIdInt = Integer.valueOf(mCurrentUserId);
- updateCurrentProfileIdsLocked();
- mWindowManager.setCurrentProfileIds(mCurrentProfileIds);
- mUserLru.remove(currentUserIdInt);
- mUserLru.add(currentUserIdInt);
- }
-
- final UserState uss = mStartedUsers.get(userId);
-
- // Make sure user is in the started state. If it is currently
- // stopping, we need to knock that off.
- if (uss.mState == UserState.STATE_STOPPING) {
- // If we are stopping, we haven't sent ACTION_SHUTDOWN,
- // so we can just fairly silently bring the user back from
- // the almost-dead.
- uss.mState = UserState.STATE_RUNNING;
- updateStartedUserArrayLocked();
- needStart = true;
- } else if (uss.mState == UserState.STATE_SHUTDOWN) {
- // This means ACTION_SHUTDOWN has been sent, so we will
- // need to treat this as a new boot of the user.
- uss.mState = UserState.STATE_BOOTING;
- updateStartedUserArrayLocked();
- needStart = true;
- }
-
- if (uss.mState == UserState.STATE_BOOTING) {
- // Booting up a new user, need to tell system services about it.
- // Note that this is on the same handler as scheduling of broadcasts,
- // which is important because it needs to go first.
- mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_START_MSG, userId, 0));
- }
-
- if (foreground) {
- mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_CURRENT_MSG, userId,
- oldUserId));
- mHandler.removeMessages(REPORT_USER_SWITCH_MSG);
- mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
- mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_MSG,
- oldUserId, userId, uss));
- mHandler.sendMessageDelayed(mHandler.obtainMessage(USER_SWITCH_TIMEOUT_MSG,
- oldUserId, userId, uss), USER_SWITCH_TIMEOUT);
- }
-
- if (needStart) {
- // Send USER_STARTED broadcast
- Intent intent = new Intent(Intent.ACTION_USER_STARTED);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
- | Intent.FLAG_RECEIVER_FOREGROUND);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- broadcastIntentLocked(null, null, intent,
- null, null, 0, null, null, null, AppOpsManager.OP_NONE,
- null, false, false, MY_PID, Process.SYSTEM_UID, userId);
- }
-
- if ((userInfo.flags&UserInfo.FLAG_INITIALIZED) == 0) {
- if (userId != UserHandle.USER_SYSTEM) {
- Intent intent = new Intent(Intent.ACTION_USER_INITIALIZE);
- intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- broadcastIntentLocked(null, null, intent, null,
- new IIntentReceiver.Stub() {
- public void performReceive(Intent intent, int resultCode,
- String data, Bundle extras, boolean ordered,
- boolean sticky, int sendingUser) {
- onUserInitialized(uss, foreground, oldUserId, userId);
- }
- }, 0, null, null, null, AppOpsManager.OP_NONE,
- null, true, false, MY_PID, Process.SYSTEM_UID, userId);
- uss.initializing = true;
- } else {
- getUserManagerLocked().makeInitialized(userInfo.id);
- }
- }
-
- if (foreground) {
- if (!uss.initializing) {
- moveUserToForegroundLocked(uss, oldUserId, userId);
- }
- } else {
- mStackSupervisor.startBackgroundUserLocked(userId, uss);
- }
-
- if (needStart) {
- Intent intent = new Intent(Intent.ACTION_USER_STARTING);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- 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) throws RemoteException {
- }
- }, 0, null, null,
- new String[] {INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE,
- null, true, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
- }
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
-
- return true;
- }
-
- void dispatchForegroundProfileChanged(int userId) {
- final int N = mUserSwitchObservers.beginBroadcast();
- for (int i = 0; i < N; i++) {
- try {
- mUserSwitchObservers.getBroadcastItem(i).onForegroundProfileSwitch(userId);
- } catch (RemoteException e) {
- // Ignore
- }
- }
- mUserSwitchObservers.finishBroadcast();
- }
-
- void sendUserSwitchBroadcastsLocked(int oldUserId, int newUserId) {
- long ident = Binder.clearCallingIdentity();
- try {
- Intent intent;
- if (oldUserId >= 0) {
- // Send USER_BACKGROUND broadcast to all profiles of the outgoing user
- List<UserInfo> profiles = mUserManager.getProfiles(oldUserId, false);
- int count = profiles.size();
- for (int i = 0; i < count; i++) {
- int profileUserId = profiles.get(i).id;
- intent = new Intent(Intent.ACTION_USER_BACKGROUND);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
- | Intent.FLAG_RECEIVER_FOREGROUND);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, profileUserId);
- broadcastIntentLocked(null, null, intent,
- null, null, 0, null, null, null, AppOpsManager.OP_NONE,
- null, false, false, MY_PID, Process.SYSTEM_UID, profileUserId);
- }
- }
- if (newUserId >= 0) {
- // Send USER_FOREGROUND broadcast to all profiles of the incoming user
- List<UserInfo> profiles = mUserManager.getProfiles(newUserId, false);
- int count = profiles.size();
- for (int i = 0; i < count; i++) {
- int profileUserId = profiles.get(i).id;
- intent = new Intent(Intent.ACTION_USER_FOREGROUND);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
- | Intent.FLAG_RECEIVER_FOREGROUND);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, profileUserId);
- broadcastIntentLocked(null, null, intent,
- null, null, 0, null, null, null, AppOpsManager.OP_NONE,
- null, false, false, MY_PID, Process.SYSTEM_UID, profileUserId);
- }
- intent = new Intent(Intent.ACTION_USER_SWITCHED);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
- | Intent.FLAG_RECEIVER_FOREGROUND);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, newUserId);
- broadcastIntentLocked(null, null, intent,
- null, null, 0, null, null,
- new String[] {android.Manifest.permission.MANAGE_USERS},
- AppOpsManager.OP_NONE, null, false, false, MY_PID, Process.SYSTEM_UID,
- UserHandle.USER_ALL);
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- void dispatchUserSwitch(final UserState uss, final int oldUserId,
- final int newUserId) {
- final int N = mUserSwitchObservers.beginBroadcast();
- if (N > 0) {
- final IRemoteCallback callback = new IRemoteCallback.Stub() {
- int mCount = 0;
- @Override
- public void sendResult(Bundle data) throws RemoteException {
- synchronized (ActivityManagerService.this) {
- if (mCurUserSwitchCallback == this) {
- mCount++;
- if (mCount == N) {
- sendContinueUserSwitchLocked(uss, oldUserId, newUserId);
- }
- }
- }
- }
- };
- synchronized (this) {
- uss.switching = true;
- mCurUserSwitchCallback = callback;
- }
- for (int i=0; i<N; i++) {
- try {
- mUserSwitchObservers.getBroadcastItem(i).onUserSwitching(
- newUserId, callback);
- } catch (RemoteException e) {
- }
- }
- } else {
- synchronized (this) {
- sendContinueUserSwitchLocked(uss, oldUserId, newUserId);
- }
- }
- mUserSwitchObservers.finishBroadcast();
- }
-
- void timeoutUserSwitch(UserState uss, int oldUserId, int newUserId) {
- synchronized (this) {
- Slog.w(TAG, "User switch timeout: from " + oldUserId + " to " + newUserId);
- sendContinueUserSwitchLocked(uss, oldUserId, newUserId);
- }
- }
-
- void sendContinueUserSwitchLocked(UserState uss, int oldUserId, int newUserId) {
- mCurUserSwitchCallback = null;
- mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
- mHandler.sendMessage(mHandler.obtainMessage(CONTINUE_USER_SWITCH_MSG,
- oldUserId, newUserId, uss));
- }
-
- void onUserInitialized(UserState uss, boolean foreground, int oldUserId, int newUserId) {
- synchronized (this) {
- if (foreground) {
- moveUserToForegroundLocked(uss, oldUserId, newUserId);
- }
- }
-
- completeSwitchAndInitialize(uss, newUserId, true, false);
- }
-
- void moveUserToForegroundLocked(UserState uss, int oldUserId, int newUserId) {
- boolean homeInFront = mStackSupervisor.switchUserLocked(newUserId, uss);
- if (homeInFront) {
- startHomeActivityLocked(newUserId, "moveUserToFroreground");
- } else {
- mStackSupervisor.resumeTopActivitiesLocked();
- }
- EventLogTags.writeAmSwitchUser(newUserId);
- getUserManagerLocked().onUserForeground(newUserId);
- sendUserSwitchBroadcastsLocked(oldUserId, newUserId);
- }
-
- private void updateUserConfigurationLocked() {
- Configuration configuration = new Configuration(mConfiguration);
- Settings.System.getConfigurationForUser(mContext.getContentResolver(), configuration,
- mCurrentUserId);
- updateConfigurationLocked(configuration, null, false);
- }
-
- void continueUserSwitch(UserState uss, int oldUserId, int newUserId) {
- completeSwitchAndInitialize(uss, newUserId, false, true);
- }
-
- void completeSwitchAndInitialize(UserState uss, int newUserId,
- boolean clearInitializing, boolean clearSwitching) {
- boolean unfrozen = false;
- synchronized (this) {
- if (clearInitializing) {
- uss.initializing = false;
- getUserManagerLocked().makeInitialized(uss.mHandle.getIdentifier());
- }
- if (clearSwitching) {
- uss.switching = false;
- }
- if (!uss.switching && !uss.initializing) {
- mWindowManager.stopFreezingScreen();
- unfrozen = true;
- }
- }
- if (unfrozen) {
- mHandler.removeMessages(REPORT_USER_SWITCH_COMPLETE_MSG);
- mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_COMPLETE_MSG,
- newUserId, 0));
- }
- stopGuestUserIfBackground();
- }
-
- /** Called on handler thread */
- void dispatchUserSwitchComplete(int userId) {
- final int observerCount = mUserSwitchObservers.beginBroadcast();
- for (int i = 0; i < observerCount; i++) {
- try {
- mUserSwitchObservers.getBroadcastItem(i).onUserSwitchComplete(userId);
- } catch (RemoteException e) {
- }
- }
- mUserSwitchObservers.finishBroadcast();
- }
-
- /**
- * Stops the guest user if it has gone to the background.
- */
- private void stopGuestUserIfBackground() {
- synchronized (this) {
- final int num = mUserLru.size();
- for (int i = 0; i < num; i++) {
- Integer oldUserId = mUserLru.get(i);
- UserState oldUss = mStartedUsers.get(oldUserId);
- if (oldUserId == UserHandle.USER_SYSTEM || oldUserId == mCurrentUserId
- || oldUss.mState == UserState.STATE_STOPPING
- || oldUss.mState == UserState.STATE_SHUTDOWN) {
- continue;
- }
- UserInfo userInfo = mUserManager.getUserInfo(oldUserId);
- if (userInfo.isGuest()) {
- // This is a user to be stopped.
- stopUserLocked(oldUserId, null);
- break;
- }
- }
- }
- }
-
void scheduleStartProfilesLocked() {
if (!mHandler.hasMessages(START_PROFILES_MSG)) {
mHandler.sendMessageDelayed(mHandler.obtainMessage(START_PROFILES_MSG),
@@ -20515,229 +19999,9 @@
}
}
- void startProfilesLocked() {
- if (DEBUG_MU) Slog.i(TAG_MU, "startProfilesLocked");
- List<UserInfo> profiles = getUserManagerLocked().getProfiles(
- mCurrentUserId, false /* enabledOnly */);
- List<UserInfo> toStart = new ArrayList<UserInfo>(profiles.size());
- for (UserInfo user : profiles) {
- if ((user.flags & UserInfo.FLAG_INITIALIZED) == UserInfo.FLAG_INITIALIZED
- && user.id != mCurrentUserId) {
- toStart.add(user);
- }
- }
- final int n = toStart.size();
- int i = 0;
- for (; i < n && i < (MAX_RUNNING_USERS - 1); ++i) {
- startUserInBackground(toStart.get(i).id);
- }
- if (i < n) {
- Slog.w(TAG_MU, "More profiles than MAX_RUNNING_USERS");
- }
- }
-
- void finishUserBoot(UserState uss) {
- synchronized (this) {
- if (uss.mState == UserState.STATE_BOOTING
- && mStartedUsers.get(uss.mHandle.getIdentifier()) == uss) {
- uss.mState = UserState.STATE_RUNNING;
- final int userId = uss.mHandle.getIdentifier();
- Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT);
- broadcastIntentLocked(null, null, intent,
- null, null, 0, null, null,
- new String[] {android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
- AppOpsManager.OP_NONE, null, true, false, MY_PID, Process.SYSTEM_UID,
- userId);
- }
- }
- }
-
- void finishUserSwitch(UserState uss) {
- synchronized (this) {
- finishUserBoot(uss);
-
- startProfilesLocked();
-
- int num = mUserLru.size();
- int i = 0;
- while (num > MAX_RUNNING_USERS && i < mUserLru.size()) {
- Integer oldUserId = mUserLru.get(i);
- UserState oldUss = mStartedUsers.get(oldUserId);
- if (oldUss == null) {
- // Shouldn't happen, but be sane if it does.
- mUserLru.remove(i);
- num--;
- continue;
- }
- if (oldUss.mState == UserState.STATE_STOPPING
- || oldUss.mState == UserState.STATE_SHUTDOWN) {
- // This user is already stopping, doesn't count.
- num--;
- i++;
- continue;
- }
- if (oldUserId == UserHandle.USER_SYSTEM || oldUserId == mCurrentUserId) {
- // Owner/System user and current user can't be stopped. We count it as running
- // when it is not a pure system user.
- if (UserInfo.isSystemOnly(oldUserId)) {
- num--;
- }
- i++;
- continue;
- }
- // This is a user to be stopped.
- stopUserLocked(oldUserId, null);
- num--;
- i++;
- }
- }
- }
-
@Override
public int stopUser(final int userId, final IStopUserCallback callback) {
- if (checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
- != PackageManager.PERMISSION_GRANTED) {
- String msg = "Permission Denial: switchUser() from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid()
- + " requires " + INTERACT_ACROSS_USERS_FULL;
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
- }
- if (userId < 0 || userId == UserHandle.USER_SYSTEM) {
- throw new IllegalArgumentException("Can't stop system user " + userId);
- }
- enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, userId);
- synchronized (this) {
- return stopUserLocked(userId, callback);
- }
- }
-
- private int stopUserLocked(final int userId, final IStopUserCallback callback) {
- if (DEBUG_MU) Slog.i(TAG_MU, "stopUserLocked userId=" + userId);
- if (mCurrentUserId == userId && mTargetUserId == UserHandle.USER_NULL) {
- return ActivityManager.USER_OP_IS_CURRENT;
- }
-
- final UserState uss = mStartedUsers.get(userId);
- if (uss == null) {
- // User is not started, nothing to do... but we do need to
- // callback if requested.
- if (callback != null) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- try {
- callback.userStopped(userId);
- } catch (RemoteException e) {
- }
- }
- });
- }
- return ActivityManager.USER_OP_SUCCESS;
- }
-
- if (callback != null) {
- uss.mStopCallbacks.add(callback);
- }
-
- if (uss.mState != UserState.STATE_STOPPING
- && uss.mState != UserState.STATE_SHUTDOWN) {
- uss.mState = UserState.STATE_STOPPING;
- updateStartedUserArrayLocked();
-
- long ident = Binder.clearCallingIdentity();
- try {
- // We are going to broadcast ACTION_USER_STOPPING and then
- // once that is done send a final ACTION_SHUTDOWN and then
- // stop the user.
- final Intent stoppingIntent = new Intent(Intent.ACTION_USER_STOPPING);
- 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 (ActivityManagerService.this) {
- if (uss.mState != UserState.STATE_STOPPING) {
- // Whoops, we are being started back up. Abort, abort!
- return;
- }
- uss.mState = UserState.STATE_SHUTDOWN;
- }
- mBatteryStatsService.noteEvent(
- BatteryStats.HistoryItem.EVENT_USER_RUNNING_FINISH,
- Integer.toString(userId), userId);
- mSystemServiceManager.stopUser(userId);
- broadcastIntentLocked(null, null, shutdownIntent,
- null, shutdownReceiver, 0, null, null, null, AppOpsManager.OP_NONE,
- null, true, false, MY_PID, Process.SYSTEM_UID, userId);
- }
- };
- // Kick things off.
- broadcastIntentLocked(null, null, stoppingIntent,
- null, stoppingReceiver, 0, null, null,
- new String[] {INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE,
- null, true, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- return ActivityManager.USER_OP_SUCCESS;
- }
-
- void finishUserStop(UserState uss) {
- final int userId = uss.mHandle.getIdentifier();
- boolean stopped;
- ArrayList<IStopUserCallback> callbacks;
- synchronized (this) {
- callbacks = new ArrayList<IStopUserCallback>(uss.mStopCallbacks);
- if (mStartedUsers.get(userId) != uss) {
- stopped = false;
- } else if (uss.mState != UserState.STATE_SHUTDOWN) {
- stopped = false;
- } else {
- stopped = true;
- // User can no longer run.
- mStartedUsers.remove(userId);
- mUserLru.remove(Integer.valueOf(userId));
- updateStartedUserArrayLocked();
-
- // Clean up all state and processes associated with the user.
- // Kill all the processes for the user.
- forceStopUserLocked(userId, "finish user");
- }
- }
-
- for (int i=0; i<callbacks.size(); i++) {
- try {
- if (stopped) callbacks.get(i).userStopped(userId);
- else callbacks.get(i).userStopAborted(userId);
- } catch (RemoteException e) {
- }
- }
-
- if (stopped) {
- mSystemServiceManager.cleanupUser(userId);
- synchronized (this) {
- mStackSupervisor.removeUserLocked(userId);
- }
- }
+ return mUserController.stopUser(userId, callback);
}
void onUserRemovedLocked(int userId) {
@@ -20746,29 +20010,11 @@
@Override
public UserInfo getCurrentUser() {
- if ((checkCallingPermission(INTERACT_ACROSS_USERS)
- != PackageManager.PERMISSION_GRANTED) && (
- checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
- != PackageManager.PERMISSION_GRANTED)) {
- String msg = "Permission Denial: getCurrentUser() from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid()
- + " requires " + INTERACT_ACROSS_USERS;
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
- }
- synchronized (this) {
- int userId = mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId;
- return getUserManagerLocked().getUserInfo(userId);
- }
- }
-
- int getCurrentUserIdLocked() {
- return mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId;
+ return mUserController.getCurrentUser();
}
@Override
- public boolean isUserRunning(int userId, boolean orStopped) {
+ public boolean isUserRunning(int userId, int flags) {
if (checkCallingPermission(INTERACT_ACROSS_USERS)
!= PackageManager.PERMISSION_GRANTED) {
String msg = "Permission Denial: isUserRunning() from pid="
@@ -20779,22 +20025,10 @@
throw new SecurityException(msg);
}
synchronized (this) {
- return isUserRunningLocked(userId, orStopped);
+ return mUserController.isUserRunningLocked(userId, flags);
}
}
- boolean isUserRunningLocked(int userId, boolean orStopped) {
- UserState state = mStartedUsers.get(userId);
- if (state == null) {
- return false;
- }
- if (orStopped) {
- return true;
- }
- return state.mState != UserState.STATE_STOPPING
- && state.mState != UserState.STATE_SHUTDOWN;
- }
-
@Override
public int[] getRunningUserIds() {
if (checkCallingPermission(INTERACT_ACROSS_USERS)
@@ -20807,76 +20041,24 @@
throw new SecurityException(msg);
}
synchronized (this) {
- return mStartedUserArray;
- }
- }
-
- private void updateStartedUserArrayLocked() {
- int num = 0;
- for (int i=0; i<mStartedUsers.size(); i++) {
- UserState uss = mStartedUsers.valueAt(i);
- // This list does not include stopping users.
- if (uss.mState != UserState.STATE_STOPPING
- && uss.mState != UserState.STATE_SHUTDOWN) {
- num++;
- }
- }
- mStartedUserArray = new int[num];
- num = 0;
- for (int i=0; i<mStartedUsers.size(); i++) {
- UserState uss = mStartedUsers.valueAt(i);
- if (uss.mState != UserState.STATE_STOPPING
- && uss.mState != UserState.STATE_SHUTDOWN) {
- mStartedUserArray[num] = mStartedUsers.keyAt(i);
- num++;
- }
+ return mUserController.getStartedUserArrayLocked();
}
}
@Override
public void registerUserSwitchObserver(IUserSwitchObserver observer) {
- if (checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
- != PackageManager.PERMISSION_GRANTED) {
- String msg = "Permission Denial: registerUserSwitchObserver() from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid()
- + " requires " + INTERACT_ACROSS_USERS_FULL;
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
- }
-
- mUserSwitchObservers.register(observer);
+ mUserController.registerUserSwitchObserver(observer);
}
@Override
public void unregisterUserSwitchObserver(IUserSwitchObserver observer) {
- mUserSwitchObservers.unregister(observer);
- }
-
- int[] getUsersLocked() {
- UserManagerService ums = getUserManagerLocked();
- return ums != null ? ums.getUserIds() : new int[] { 0 };
- }
-
- UserManagerService getUserManagerLocked() {
- if (mUserManager == null) {
- IBinder b = ServiceManager.getService(Context.USER_SERVICE);
- mUserManager = (UserManagerService)IUserManager.Stub.asInterface(b);
- }
- return mUserManager;
- }
-
- private int applyUserId(int uid, int userId) {
- return UserHandle.getUid(userId, uid);
+ mUserController.unregisterUserSwitchObserver(observer);
}
ApplicationInfo getAppInfoForUser(ApplicationInfo info, int userId) {
if (info == null) return null;
ApplicationInfo newInfo = new ApplicationInfo(info);
- newInfo.uid = applyUserId(info.uid, userId);
- newInfo.dataDir = Environment
- .getDataUserPackageDirectory(info.volumeUuid, userId, info.packageName)
- .getAbsolutePath();
+ newInfo.initForUser(userId);
return newInfo;
}
@@ -21126,7 +20308,7 @@
@Override
public int startActivity(IBinder whoThread, String callingPackage,
- Intent intent, String resolvedType, Bundle options) {
+ Intent intent, String resolvedType, Bundle bOptions) {
checkCaller();
int callingUser = UserHandle.getCallingUserId();
@@ -21144,7 +20326,7 @@
}
return mStackSupervisor.startActivityMayWait(appThread, -1, callingPackage, intent,
resolvedType, null, null, null, null, 0, 0, null, null,
- null, options, false, callingUser, null, tr);
+ null, bOptions, false, callingUser, null, tr);
}
@Override
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
new file mode 100644
index 0000000..d1e7e85
--- /dev/null
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -0,0 +1,211 @@
+/*
+ * 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.am;
+
+import android.app.IActivityManager;
+import android.os.RemoteException;
+import android.os.ShellCommand;
+import android.os.UserHandle;
+
+import java.io.PrintWriter;
+
+class ActivityManagerShellCommand extends ShellCommand {
+ // IPC interface to activity manager -- don't need to do additional security checks.
+ final IActivityManager mInterface;
+
+ // Internal service impl -- must perform security checks before touching.
+ final ActivityManagerService mInternal;
+
+ final boolean mDumping;
+
+ ActivityManagerShellCommand(ActivityManagerService service, boolean dumping) {
+ mInterface = service;
+ mInternal = service;
+ mDumping = dumping;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+ PrintWriter pw = getOutPrintWriter();
+ try {
+ switch (cmd) {
+ case "force-stop":
+ return runForceStop(pw);
+ case "kill":
+ return runKill(pw);
+ case "kill-all":
+ return runKillAll(pw);
+ case "write":
+ return runWrite(pw);
+ case "track-associations":
+ return runTrackAssociations(pw);
+ case "untrack-associations":
+ return runUntrackAssociations(pw);
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ } catch (RemoteException e) {
+ pw.println("Remote exception: " + e);
+ }
+ return -1;
+ }
+
+ int runForceStop(PrintWriter pw) throws RemoteException {
+ int userId = UserHandle.USER_ALL;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ if (opt.equals("--user")) {
+ userId = parseUserArg(getNextArgRequired());
+ } else {
+ pw.println("Error: Unknown option: " + opt);
+ return -1;
+ }
+ }
+ mInterface.forceStopPackage(getNextArgRequired(), userId);
+ return 0;
+ }
+
+ int runKill(PrintWriter pw) throws RemoteException {
+ int userId = UserHandle.USER_ALL;
+
+ String opt;
+ while ((opt=getNextOption()) != null) {
+ if (opt.equals("--user")) {
+ userId = parseUserArg(getNextArgRequired());
+ } else {
+ pw.println("Error: Unknown option: " + opt);
+ return -1;
+ }
+ }
+ mInterface.killBackgroundProcesses(getNextArgRequired(), userId);
+ return 0;
+ }
+
+ int runKillAll(PrintWriter pw) throws RemoteException {
+ mInterface.killAllBackgroundProcesses();
+ return 0;
+ }
+
+ int runWrite(PrintWriter pw) {
+ mInternal.enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
+ "registerUidObserver()");
+ mInternal.mTaskPersister.flush();
+ pw.println("All tasks persisted.");
+ return 0;
+ }
+
+ int runTrackAssociations(PrintWriter pw) {
+ mInternal.enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
+ "registerUidObserver()");
+ synchronized (mInternal) {
+ if (!mInternal.mTrackingAssociations) {
+ mInternal.mTrackingAssociations = true;
+ pw.println("Association tracking started.");
+ } else {
+ pw.println("Association tracking already enabled.");
+ }
+ }
+ return 0;
+ }
+
+ int runUntrackAssociations(PrintWriter pw) {
+ mInternal.enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
+ "registerUidObserver()");
+ synchronized (mInternal) {
+ if (mInternal.mTrackingAssociations) {
+ mInternal.mTrackingAssociations = false;
+ mInternal.mAssociations.clear();
+ pw.println("Association tracking stopped.");
+ } else {
+ pw.println("Association tracking not running.");
+ }
+ }
+ return 0;
+ }
+
+ int parseUserArg(String arg) {
+ int userId;
+ if ("all".equals(arg)) {
+ userId = UserHandle.USER_ALL;
+ } else if ("current".equals(arg) || "cur".equals(arg)) {
+ userId = UserHandle.USER_CURRENT;
+ } else {
+ try {
+ userId = Integer.parseInt(arg);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Bad user number: " + arg);
+ }
+ }
+ return userId;
+ }
+
+ @Override
+ public void onHelp() {
+ PrintWriter pw = getOutPrintWriter();
+ dumpHelp(pw, mDumping);
+ }
+
+ static void dumpHelp(PrintWriter pw, boolean dumping) {
+ if (dumping) {
+ pw.println("Activity manager dump options:");
+ pw.println(" [-a] [-c] [-p PACKAGE] [-h] [WHAT] ...");
+ pw.println(" WHAT may be one of:");
+ pw.println(" a[ctivities]: activity stack state");
+ pw.println(" r[recents]: recent activities state");
+ pw.println(" b[roadcasts] [PACKAGE_NAME] [history [-s]]: broadcast state");
+ pw.println(" i[ntents] [PACKAGE_NAME]: pending intent state");
+ pw.println(" p[rocesses] [PACKAGE_NAME]: process state");
+ pw.println(" o[om]: out of memory management");
+ pw.println(" perm[issions]: URI permission grant state");
+ pw.println(" prov[iders] [COMP_SPEC ...]: content provider state");
+ pw.println(" provider [COMP_SPEC]: provider client-side state");
+ pw.println(" s[ervices] [COMP_SPEC ...]: service state");
+ pw.println(" as[sociations]: tracked app associations");
+ pw.println(" service [COMP_SPEC]: service client-side state");
+ pw.println(" package [PACKAGE_NAME]: all state related to given package");
+ pw.println(" all: dump all activities");
+ pw.println(" top: dump the top activity");
+ pw.println(" WHAT may also be a COMP_SPEC to dump activities.");
+ pw.println(" COMP_SPEC may be a component name (com.foo/.myApp),");
+ pw.println(" a partial substring in a component name, a");
+ pw.println(" hex object identifier.");
+ pw.println(" -a: include all available server state.");
+ pw.println(" -c: include client state.");
+ pw.println(" -p: limit output to given package.");
+ } else {
+ pw.println("Activity manager (activity) commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println(" force-stop [--user <USER_ID> | all | current] <PACKAGE>");
+ pw.println(" Complete stop the given application package.");
+ 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(" write");
+ pw.println(" Write all pending state to storage.");
+ pw.println(" track-associations");
+ pw.println(" Enable association tracking.");
+ pw.println(" untrack-associations");
+ pw.println(" Disable and clear association tracking.");
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 4671cb0..aa04bd7 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -53,6 +53,7 @@
import android.util.Log;
import android.util.Slog;
import android.util.TimeUtils;
+import android.view.AppTransitionAnimationSpec;
import android.view.IApplicationToken;
import android.view.WindowManager;
@@ -189,6 +190,7 @@
// and drawable-sw400dp will be added to both as 400.
private int[] mVerticalSizeConfigurations;
private int[] mHorizontalSizeConfigurations;
+ private int[] mSmallestSizeConfigurations;
void dump(PrintWriter pw, String prefix) {
final long now = SystemClock.uptimeMillis();
@@ -341,6 +343,10 @@
return crossesSizeThreshold(mVerticalSizeConfigurations, firstDp, secondDp);
}
+ public boolean crossesSmallestSizeThreshold(int firstDp, int secondDp) {
+ return crossesSizeThreshold(mSmallestSizeConfigurations, firstDp, secondDp);
+ }
+
/**
* The purpose of this method is to decide whether the activity needs to be relaunched upon
* changing its size. In most cases the activities don't need to be relaunched, if the resize
@@ -371,9 +377,10 @@
}
public void setSizeConfigurations(int[] horizontalSizeConfiguration,
- int[] verticalSizeConfigurations) {
+ int[] verticalSizeConfigurations, int[] smallestSizeConfigurations) {
mHorizontalSizeConfigurations = horizontalSizeConfiguration;
mVerticalSizeConfigurations = verticalSizeConfigurations;
+ mSmallestSizeConfigurations = smallestSizeConfigurations;
}
static class Token extends IApplicationToken.Stub {
@@ -487,7 +494,7 @@
ActivityRecord _resultTo, String _resultWho, int _reqCode,
boolean _componentSpecified, boolean _rootVoiceInteraction,
ActivityStackSupervisor supervisor,
- ActivityContainer container, Bundle options) {
+ ActivityContainer container, ActivityOptions options) {
service = _service;
appToken = new Token(this, service);
info = aInfo;
@@ -520,7 +527,7 @@
mStackSupervisor = supervisor;
mInitialActivityContainer = container;
if (options != null) {
- pendingOptions = new ActivityOptions(options);
+ pendingOptions = options;
mLaunchTaskBehind = pendingOptions.getLaunchTaskBehind();
PendingIntent usageReport = pendingOptions.getUsageTimeReport();
if (usageReport != null) {
@@ -792,15 +799,6 @@
}
}
- void updateOptionsLocked(Bundle options) {
- if (options != null) {
- if (pendingOptions != null) {
- pendingOptions.abort();
- }
- pendingOptions = new ActivityOptions(options);
- }
- }
-
void updateOptionsLocked(ActivityOptions options) {
if (options != null) {
if (pendingOptions != null) {
@@ -863,17 +861,25 @@
break;
case ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP:
case ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_DOWN:
- service.mWindowManager.overridePendingAppTransitionAspectScaledThumb(
- pendingOptions.getThumbnail(),
- pendingOptions.getStartX(), pendingOptions.getStartY(),
- pendingOptions.getWidth(), pendingOptions.getHeight(),
- pendingOptions.getOnAnimationStartListener(),
- (animationType == ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP));
- if (intent.getSourceBounds() == null) {
- intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
- pendingOptions.getStartY(),
- pendingOptions.getStartX() + pendingOptions.getWidth(),
- pendingOptions.getStartY() + pendingOptions.getHeight()));
+ final AppTransitionAnimationSpec[] specs = pendingOptions.getAnimSpecs();
+ if (animationType == ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_DOWN
+ && specs != null) {
+ service.mWindowManager.overridePendingAppTransitionMultiThumb(
+ specs, pendingOptions.getOnAnimationStartListener(),
+ pendingOptions.getAnimationFinishedListener(), false);
+ } else {
+ service.mWindowManager.overridePendingAppTransitionAspectScaledThumb(
+ pendingOptions.getThumbnail(),
+ pendingOptions.getStartX(), pendingOptions.getStartY(),
+ pendingOptions.getWidth(), pendingOptions.getHeight(),
+ pendingOptions.getOnAnimationStartListener(),
+ (animationType == ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP));
+ if (intent.getSourceBounds() == null) {
+ intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
+ pendingOptions.getStartY(),
+ pendingOptions.getStartX() + pendingOptions.getWidth(),
+ pendingOptions.getStartY() + pendingOptions.getHeight()));
+ }
}
break;
default:
@@ -926,7 +932,7 @@
if (newThumbnail != null) {
if (DEBUG_THUMBNAILS) Slog.i(TAG_THUMBNAILS,
"Setting thumbnail of " + this + " to " + newThumbnail);
- boolean thumbnailUpdated = task.setLastThumbnail(newThumbnail);
+ boolean thumbnailUpdated = task.setLastThumbnailLocked(newThumbnail);
if (thumbnailUpdated && isPersistable()) {
mStackSupervisor.mService.notifyTaskPersisterLocked(task, false);
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index cdb00ef..ba6e9b1c 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -16,12 +16,11 @@
package com.android.server.am;
-import static android.app.ActivityManager.DOCKED_STACK_ID;
-import static android.app.ActivityManager.FIRST_STATIC_STACK_ID;
-import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.HOME_STACK_ID;
-import static android.app.ActivityManager.LAST_STATIC_STACK_ID;
+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.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
import static com.android.server.am.ActivityManagerDebugConfig.*;
@@ -32,6 +31,7 @@
import static com.android.server.am.ActivityStackSupervisor.MOVING;
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import android.graphics.Point;
import android.graphics.Rect;
import android.util.ArraySet;
@@ -47,6 +47,7 @@
import android.app.Activity;
import android.app.ActivityManager;
+import android.app.ActivityManager.StackId;
import android.app.ActivityOptions;
import android.app.AppGlobals;
import android.app.IActivityController;
@@ -354,10 +355,6 @@
return count;
}
- int numTasks() {
- return mTaskHistory.size();
- }
-
ActivityStack(ActivityStackSupervisor.ActivityContainer activityContainer,
RecentTasks recentTasks) {
mActivityContainer = activityContainer;
@@ -366,7 +363,7 @@
mHandler = new ActivityStackHandler(mService.mHandler.getLooper());
mWindowManager = mService.mWindowManager;
mStackId = activityContainer.mStackId;
- mCurrentUser = mService.mCurrentUserId;
+ mCurrentUser = mService.mUserController.getCurrentUserIdLocked();
mRecentTasks = recentTasks;
mTaskPositioner = mStackId == FREEFORM_WORKSPACE_STACK_ID
? new LaunchingTaskPositioner() : null;
@@ -392,6 +389,10 @@
mWindowManager.detachStack(mStackId);
}
+ public void getDisplaySize(Point out) {
+ mActivityContainer.mActivityDisplay.mDisplay.getSize(out);
+ }
+
void setBounds(Rect bounds) {
mBounds = mFullscreen ? null : new Rect(bounds);
if (mTaskPositioner != null) {
@@ -528,25 +529,35 @@
* @param task If non-null, the task will be moved to the top of the stack.
* */
void moveToFront(String reason, TaskRecord task) {
- if (isAttached()) {
- final ActivityStack lastFocusStack = mStacks.get(mStacks.size() - 1);
- // Need to move this stack to the front before calling
- // {@link ActivityStackSupervisor#setFocusStack} below.
- mStacks.remove(this);
- mStacks.add(this);
+ if (!isAttached()) {
+ return;
+ }
- // TODO(multi-display): Needs to also work if focus is moving to the non-home display.
- if (isOnHomeDisplay()) {
- mStackSupervisor.setFocusStack(reason, lastFocusStack);
+ mStacks.remove(this);
+ int addIndex = mStacks.size();
+
+ if (addIndex > 0) {
+ final ActivityStack topStack = mStacks.get(addIndex - 1);
+ if (topStack.mStackId == PINNED_STACK_ID && topStack != this) {
+ // The pinned stack is always the top most stack (always-on-top).
+ // So, stack is moved just below the pinned stack.
+ addIndex--;
}
- if (task != null) {
- insertTaskAtTop(task, null);
- } else {
- task = topTask();
- }
- if (task != null) {
- mWindowManager.moveTaskToTop(task.taskId);
- }
+ }
+
+ mStacks.add(addIndex, this);
+
+ // TODO(multi-display): Needs to also work if focus is moving to the non-home display.
+ if (isOnHomeDisplay()) {
+ mStackSupervisor.setFocusStack(reason, this);
+ }
+ if (task != null) {
+ insertTaskAtTop(task, null);
+ } else {
+ task = topTask();
+ }
+ if (task != null) {
+ mWindowManager.moveTaskToTop(task.taskId);
}
}
@@ -901,14 +912,8 @@
prev.task.touchActiveTime();
clearLaunchTime(prev);
final ActivityRecord next = mStackSupervisor.topRunningActivityLocked();
- // In freeform mode we only update the thumbnail when there is no thumbnail yet since every
- // focus change will request a thumbnail to be taken.
- // Note furthermore that since windows can change their content in freeform mode all the
- // time a thumbnail is possibly constantly outdated.
- if (mService.mHasRecents &&
- (next == null || next.noDisplay || next.task != prev.task || uiSleeping) &&
- (!prev.task.hasThumbnail() ||
- prev.task.stack.mStackId != FREEFORM_WORKSPACE_STACK_ID)) {
+ if (mService.mHasRecents
+ && (next == null || next.noDisplay || next.task != prev.task || uiSleeping)) {
prev.updateThumbnailLocked(screenshotActivities(prev), null);
}
stopFullyDrawnTraceIfNeeded();
@@ -999,10 +1004,13 @@
r.userId, System.identityHashCode(r), r.shortComponentName,
mPausingActivity != null
? mPausingActivity.shortComponentName : "(none)");
- if (r.finishing && r.state == ActivityState.PAUSING) {
- if (DEBUG_PAUSE) Slog.v(TAG,
- "Executing finish of failed to pause activity: " + r);
- finishCurrentActivityLocked(r, FINISH_AFTER_VISIBLE, false);
+ if (r.state == ActivityState.PAUSING) {
+ r.state = ActivityState.PAUSED;
+ if (r.finishing) {
+ if (DEBUG_PAUSE) Slog.v(TAG,
+ "Executing finish of failed to pause activity: " + r);
+ finishCurrentActivityLocked(r, FINISH_AFTER_VISIBLE, false);
+ }
}
}
}
@@ -1305,7 +1313,7 @@
return false;
}
- if (mStackSupervisor.isFrontStack(this)) {
+ if (mStackSupervisor.isFrontStack(this) || mStackSupervisor.isFocusedStack(this)) {
return true;
}
@@ -1331,8 +1339,9 @@
}
final int belowFocusedIndex = mStacks.indexOf(focusedStack) - 1;
- if (focusedStackId == DOCKED_STACK_ID && stackIndex == belowFocusedIndex) {
- // Stacks directly behind the docked stack are always visible.
+ if ((focusedStackId == DOCKED_STACK_ID || focusedStackId == PINNED_STACK_ID)
+ && stackIndex == belowFocusedIndex) {
+ // Stacks directly behind the docked or pinned stack are always visible.
return true;
}
@@ -1346,15 +1355,16 @@
}
if (belowFocusedIndex >= 0) {
final ActivityStack stack = mStacks.get(belowFocusedIndex);
- if (stack.mStackId == DOCKED_STACK_ID && stackIndex == (belowFocusedIndex - 1)) {
- // The stack behind the docked stack is also visible so we can have a complete
- // backdrop to the translucent activity when the docked stack is up.
+ if ((stack.mStackId == DOCKED_STACK_ID || stack.mStackId == PINNED_STACK_ID)
+ && stackIndex == (belowFocusedIndex - 1)) {
+ // The stack behind the docked or pinned stack is also visible so we can have a
+ // complete backdrop to the translucent activity when the docked stack is up.
return true;
}
}
}
- if (mStackId >= FIRST_STATIC_STACK_ID && mStackId <= LAST_STATIC_STACK_ID) {
+ if (StackId.isStaticStack(mStackId)) {
// Visibility of any static stack should have been determined by the conditions above.
return false;
}
@@ -1366,9 +1376,7 @@
continue;
}
- if (stack.mStackId == FREEFORM_WORKSPACE_STACK_ID
- || stack.mStackId == HOME_STACK_ID
- || stack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID) {
+ if (!StackId.isDynamicStacksVisibleBehindAllowed(stack.mStackId)) {
// These stacks can't have any dynamic stacks visible behind them.
return false;
}
@@ -1381,6 +1389,20 @@
return true;
}
+ final int rankTaskLayers(int baseLayer) {
+ int layer = 0;
+ for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+ final TaskRecord task = mTaskHistory.get(taskNdx);
+ ActivityRecord r = task.topRunningActivityLocked();
+ if (r == null || r.finishing || !r.visible) {
+ task.mLayerRank = -1;
+ } else {
+ task.mLayerRank = baseLayer + layer++;
+ }
+ }
+ return layer;
+ }
+
/**
* Make sure that all activities that need to be visible (that is, they
* currently can be seen by the user) actually are.
@@ -1439,29 +1461,35 @@
}
if (r.app == null || r.app.thread == null) {
- // This activity needs to be visible, but isn't even running...
- // get it started and resume if no other stack in this stack is resumed.
- if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
- "Start and freeze screen for " + r);
- if (r != starting) {
- r.startFreezingScreenLocked(r.app, configChanges);
- }
- if (!r.visible || r.mLaunchTaskBehind) {
+ // We need to make sure the app is running if it's the top, or it is
+ // just made visible from invisible.
+ // If the app is already visible, it must have died while it was visible.
+ // In this case, we'll show the dead window but will not restart the app.
+ // Otherwise we could end up thrashing.
+ if (r == top || !r.visible) {
+ // This activity needs to be visible, but isn't even running...
+ // get it started and resume if no other stack in this stack is resumed.
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
- "Starting and making visible: " + r);
- setVisible(r, true);
- }
- if (r != starting) {
- mStackSupervisor.startSpecificActivityLocked(
- r, noStackActivityResumed, false);
- if (activityNdx >= activities.size()) {
- // Record may be removed if its process needs to restart.
- activityNdx = activities.size() - 1;
- } else {
- noStackActivityResumed = false;
+ "Start and freeze screen for " + r);
+ if (r != starting) {
+ r.startFreezingScreenLocked(r.app, configChanges);
+ }
+ if (!r.visible || r.mLaunchTaskBehind) {
+ if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
+ "Starting and making visible: " + r);
+ setVisible(r, true);
+ }
+ if (r != starting) {
+ mStackSupervisor.startSpecificActivityLocked(
+ r, noStackActivityResumed, false);
+ if (activityNdx >= activities.size()) {
+ // Record may be removed if its process needs to restart.
+ activityNdx = activities.size() - 1;
+ } else {
+ noStackActivityResumed = false;
+ }
}
}
-
} else if (r.visible) {
// If this activity is already visible, then there is nothing
// else to do here.
@@ -1679,7 +1707,7 @@
return resumeTopActivityLocked(prev, null);
}
- final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {
+ final boolean resumeTopActivityLocked(ActivityRecord prev, ActivityOptions options) {
if (mStackSupervisor.inResumeTopActivity) {
// Don't even start recursing.
return false;
@@ -1700,7 +1728,7 @@
return result;
}
- private boolean resumeTopActivityInnerLocked(ActivityRecord prev, Bundle options) {
+ private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
if (DEBUG_LOCKSCREEN) mService.logLockScreen("");
if (!mService.mBooting && !mService.mBooted) {
@@ -1808,7 +1836,7 @@
// Make sure that the user who owns this activity is started. If not,
// we will just leave it as is because someone should be bringing
// another user's activities to the top of the stack.
- if (mService.mStartedUsers.get(next.userId) == null) {
+ if (!mService.mUserController.hasStartedUserState(next.userId)) {
Slog.w(TAG, "Skipping resume of top activity " + next
+ ": user " + next.userId + " is stopped");
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
@@ -2021,7 +2049,7 @@
// Have the window manager re-evaluate the orientation of
// the screen based on the new activity order.
boolean notUpdated = true;
- if (mStackSupervisor.isFrontStack(this)) {
+ if (mStackSupervisor.isFocusedStack(this)) {
Configuration config = mWindowManager.updateOrientationFromAppTokens(
mService.mConfiguration,
next.mayFreezeScreenLocked(next.app) ? next.appToken : null);
@@ -2234,7 +2262,7 @@
}
final void startActivityLocked(ActivityRecord r, boolean newTask,
- boolean doResume, boolean keepCurTransition, Bundle options) {
+ boolean doResume, boolean keepCurTransition, ActivityOptions options) {
TaskRecord rTask = r.task;
final int taskId = rTask.taskId;
// mLaunchTaskBehind tasks get placed at the back of the task stack.
@@ -2336,7 +2364,7 @@
resetTaskIfNeededLocked(r, r);
doShow = topRunningNonDelayedActivityLocked(null) == r;
}
- } else if (options != null && new ActivityOptions(options).getAnimationType()
+ } else if (options != null && options.getAnimationType()
== ActivityOptions.ANIM_SCENE_TRANSITION) {
doShow = false;
}
@@ -2768,14 +2796,14 @@
}
private void adjustFocusedActivityLocked(ActivityRecord r, String reason) {
- if (mStackSupervisor.isFrontStack(this) && mService.mFocusedActivity == r) {
+ if (mStackSupervisor.isFocusedStack(this) && mService.mFocusedActivity == r) {
ActivityRecord next = topRunningActivityLocked();
final String myReason = reason + " adjustFocus";
if (next != r) {
- if (next != null && (mStackId == FREEFORM_WORKSPACE_STACK_ID
- || mStackId == DOCKED_STACK_ID)) {
- // For freeform and docked stacks we always keep the focus within the stack as
- // long as there is a running activity in the stack that we can adjust focus to.
+ if (next != null && StackId.keepFocusInStackIfPossible(mStackId)) {
+ // For freeform, docked, and pinned stacks we always keep the focus within the
+ // stack as long as there is a running activity in the stack that we can adjust
+ // focus to.
mService.setFocusedActivityLocked(next, myReason);
return;
} else {
@@ -3374,7 +3402,7 @@
if (task != null && task.removeActivity(r)) {
if (DEBUG_STACK) Slog.i(TAG_STACK,
"removeActivityFromHistoryLocked: last activity removed from " + this);
- if (mStackSupervisor.isFrontStack(this) && task == topTask() &&
+ if (mStackSupervisor.isFocusedStack(this) && task == topTask() &&
task.isOverHomeStack()) {
mStackSupervisor.moveHomeStackTaskToTop(task.getTaskToReturnTo(), reason);
}
@@ -3714,10 +3742,13 @@
// Don't currently have state for the activity, or
// it is finishing -- always remove it.
remove = true;
- } else if (r.launchCount > 2 &&
- r.lastLaunchTime > (SystemClock.uptimeMillis()-60000)) {
+ } else if (!r.visible && r.launchCount > 2 &&
+ r.lastLaunchTime > (SystemClock.uptimeMillis() - 60000)) {
// We have launched this activity too many times since it was
// able to run, so give up and remove it.
+ // (Note if the activity is visible, we don't remove the record.
+ // We leave the dead window on the screen but the process will
+ // not be restarted unless user explicitly tap on it.)
remove = true;
} else {
// The process may be gone, but the activity lives on!
@@ -3747,7 +3778,11 @@
if (DEBUG_APP) Slog.v(TAG_APP,
"Clearing app during removeHistory for activity " + r);
r.app = null;
- r.nowVisible = false;
+ // Set nowVisible to previous visible state. If the app was visible while
+ // it died, we leave the dead window on screen so it's basically visible.
+ // This is needed when user later tap on the dead window, we need to stop
+ // other apps when user transfers focus to the restarted activity.
+ r.nowVisible = r.visible;
if (!r.haveState) {
if (DEBUG_SAVED_STATE) Slog.i(TAG_SAVED_STATE,
"App died, clearing saved state of " + r);
@@ -3765,7 +3800,7 @@
return hasVisibleActivities;
}
- final void updateTransitLocked(int transit, Bundle options) {
+ final void updateTransitLocked(int transit, ActivityOptions options) {
if (options != null) {
ActivityRecord r = topRunningActivityLocked();
if (r != null && r.state != ActivityState.RESUMED) {
@@ -3787,6 +3822,7 @@
task.mLastTimeMoved *= -1;
}
}
+ mStackSupervisor.invalidateTaskLayers();
}
void moveHomeStackTaskToTop(int homeStackTaskType) {
@@ -3804,7 +3840,7 @@
}
}
- final void moveTaskToFrontLocked(TaskRecord tr, boolean noAnimation, Bundle options,
+ final void moveTaskToFrontLocked(TaskRecord tr, boolean noAnimation, ActivityOptions options,
AppTimeTracker timeTracker, String reason) {
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "moveTaskToFront: " + tr);
@@ -4126,9 +4162,7 @@
if ((taskChanges & ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE) != 0) {
final int oldSmallest = oldTaskOverride.smallestScreenWidthDp;
final int newSmallest = taskConfig.smallestScreenWidthDp;
- final boolean crosses = record.crossesHorizontalSizeThreshold(oldSmallest, newSmallest)
- || record.crossesVerticalSizeThreshold(oldSmallest, newSmallest);
- if (!crosses) {
+ if (!record.crossesSmallestSizeThreshold(oldSmallest, newSmallest)) {
taskChanges &= ~ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
}
}
@@ -4354,6 +4388,7 @@
RunningTaskInfo ci = new RunningTaskInfo();
ci.id = task.taskId;
+ ci.stackId = mStackId;
ci.baseActivity = r.intent.getComponent();
ci.topActivity = top.intent.getComponent();
ci.lastActiveTime = task.lastActiveTime;
@@ -4509,6 +4544,11 @@
if (!moving) {
mStackSupervisor.removeLockedTaskLocked(task);
mWindowManager.removeTask(task.taskId);
+ if (!StackId.persistTaskBounds(mStackId)) {
+ // Reset current bounds for task whose bounds shouldn't be persisted so it uses
+ // default configuration the next time it launches.
+ task.updateOverrideConfiguration(null);
+ }
}
final ActivityRecord r = mResumedActivity;
@@ -4546,7 +4586,7 @@
if (mTaskHistory.isEmpty()) {
if (DEBUG_STACK) Slog.i(TAG_STACK, "removeTask: removing stack=" + this);
// We only need to adjust focused stack if this stack is in focus.
- if (isOnHomeDisplay() && mStackSupervisor.isFrontStack(this)) {
+ if (isOnHomeDisplay() && mStackSupervisor.isFocusedStack(this)) {
String myReason = reason + " leftTaskHistoryEmpty";
if (mFullscreen || !adjustFocusToNextVisibleStackLocked(null, myReason)) {
mStackSupervisor.moveHomeStackToFront(myReason);
@@ -4571,14 +4611,20 @@
voiceInteractor);
// add the task to stack first, mTaskPositioner might need the stack association
addTask(task, toTop, false);
- if (mTaskPositioner != null) {
- mTaskPositioner.updateDefaultBounds(task, mTaskHistory, info.initialLayout);
- } else if (mBounds != null && task.mResizeable) {
+ if (!layoutTaskInStack(task, info.layout) && mBounds != null && task.mResizeable) {
task.updateOverrideConfiguration(mBounds);
}
return task;
}
+ boolean layoutTaskInStack(TaskRecord task, ActivityInfo.Layout layout) {
+ if (mTaskPositioner == null) {
+ return false;
+ }
+ mTaskPositioner.updateDefaultBounds(task, mTaskHistory, layout);
+ return true;
+ }
+
ArrayList<TaskRecord> getAllTasks() {
return new ArrayList<>(mTaskHistory);
}
@@ -4621,6 +4667,51 @@
r.taskConfigOverride = task.mOverrideConfig;
}
+ void setFocusAndResumeStateIfNeeded(
+ ActivityRecord r, boolean setFocus, boolean setResume, String reason) {
+ // If the activity had focus before move focus to this stack.
+ if (setFocus) {
+ // If the activity owns the last resumed activity, transfer that together,
+ // so that we don't resume the same activity again in the new stack.
+ // Apps may depend on onResume()/onPause() being called in pairs.
+ if (setResume) {
+ mResumedActivity = r;
+ // Move the stack in which we are placing the activity to the front. We don't use
+ // ActivityManagerService.setFocusedActivityLocked, because if the activity is
+ // already focused, the call will short-circuit and do nothing.
+ moveToFront(reason);
+ } else {
+ // We need to not only move the stack to the front, but also have the activity
+ // focused. This will achieve both goals.
+ mService.setFocusedActivityLocked(r, reason);
+ }
+ }
+ }
+
+ /**
+ * Moves the input activity from its current stack to this one.
+ * NOTE: The current task of the activity isn't moved to this stack. Instead a new task is
+ * created on this stack which the activity is added to.
+ * */
+ void moveActivityToStack(ActivityRecord r) {
+ final ActivityStack prevStack = r.task.stack;
+ if (prevStack.mStackId == mStackId) {
+ // You are already in the right stack silly...
+ return;
+ }
+
+ final boolean wasFocused = mStackSupervisor.isFocusedStack(prevStack)
+ && (mStackSupervisor.topRunningActivityLocked() == r);
+ final boolean wasResumed = wasFocused && (prevStack.mResumedActivity == r);
+
+ final TaskRecord task = createTaskRecord(
+ mStackSupervisor.getNextTaskId(), r.info, r.intent, null, null, true);
+ r.setTask(task, null);
+ task.addActivityToTop(r);
+ setAppTask(r, task);
+ setFocusAndResumeStateIfNeeded(r, wasFocused, wasResumed, "moveActivityToStack");
+ }
+
private void setAppTask(ActivityRecord r, TaskRecord task) {
final Rect bounds = task.getLaunchBounds();
task.updateOverrideConfiguration(bounds);
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 5df0490..4fc8454 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -18,6 +18,15 @@
import static android.Manifest.permission.START_ANY_ACTIVITY;
import static android.app.ActivityManager.*;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.FIRST_DYNAMIC_STACK_ID;
+import static android.app.ActivityManager.StackId.FIRST_STATIC_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.INVALID_STACK_ID;
+import static android.app.ActivityManager.StackId.LAST_STATIC_STACK_ID;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -40,6 +49,7 @@
import android.Manifest;
import android.app.Activity;
import android.app.ActivityManager;
+import android.app.ActivityManager.StackId;
import android.app.ActivityManager.StackInfo;
import android.app.ActivityOptions;
import android.app.AppGlobals;
@@ -48,6 +58,7 @@
import android.app.IActivityContainerCallback;
import android.app.IActivityManager;
import android.app.IApplicationThread;
+import android.app.KeyguardManager;
import android.app.PendingIntent;
import android.app.ProfilerInfo;
import android.app.ActivityManager.RunningTaskInfo;
@@ -65,6 +76,7 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
@@ -87,10 +99,13 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.SystemProperties;
import android.os.Trace;
import android.os.TransactionTooLargeException;
import android.os.UserHandle;
+import android.os.UserManager;
import android.os.WorkSource;
+import android.os.storage.StorageManager;
import android.provider.MediaStore;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
@@ -295,10 +310,6 @@
/** Set when we have taken too long waiting to go to sleep. */
boolean mSleepTimeout = false;
- /** Indicates if we are running on a Leanback-only (TV) device. Only initialized after
- * setWindowManager is called. **/
- private boolean mLeanbackOnlyDevice;
-
/**
* We don't want to allow the device to go to sleep while in the process
* of launching an activity. This is primarily to allow alarm intent
@@ -352,6 +363,13 @@
private final SparseArray<Configuration> mTmpConfigs = new SparseArray<>();
private final SparseArray<Rect> mTmpBounds = new SparseArray<>();
+ // The default minimal size that will be used if the activity doesn't specify its minimal size.
+ // It will be calculated when the default display gets added.
+ private int mDefaultMinimalSizeOfResizeableTask = -1;
+
+ // Whether tasks have moved and we need to rank the tasks before next OOM scoring
+ private boolean mTaskLayersChanged = true;
+
/**
* Description of a request to start a new activity, which has been held
* due to app switches being disabled.
@@ -361,13 +379,26 @@
final ActivityRecord sourceRecord;
final int startFlags;
final ActivityStack stack;
+ final ProcessRecord callerApp;
PendingActivityLaunch(ActivityRecord _r, ActivityRecord _sourceRecord,
- int _startFlags, ActivityStack _stack) {
+ int _startFlags, ActivityStack _stack, ProcessRecord _callerApp) {
r = _r;
sourceRecord = _sourceRecord;
startFlags = _startFlags;
stack = _stack;
+ callerApp = _callerApp;
+ }
+
+ void sendErrorResult(String message) {
+ try {
+ if (callerApp.thread != null) {
+ callerApp.thread.scheduleCrash(message);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception scheduling crash of failed "
+ + "activity launcher sourceRecord=" + sourceRecord, e);
+ }
}
}
@@ -432,15 +463,13 @@
throw new IllegalStateException("Default Display does not exist");
}
mActivityDisplays.put(displayId, activityDisplay);
+ calculateDefaultMinimalSizeOfResizeableTasks(activityDisplay);
}
createStackOnDisplay(HOME_STACK_ID, Display.DEFAULT_DISPLAY, true);
mHomeStack = mFocusedStack = mLastFocusedStack = getStack(HOME_STACK_ID);
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
-
- // Initialize this here, now that we can get a valid reference to PackageManager.
- mLeanbackOnlyDevice = isLeanbackOnlyDevice();
}
}
@@ -457,10 +486,7 @@
return mLastFocusedStack;
}
- /** Top of all visible stacks is/should always be equal to the focused stack.
- * Use {@link ActivityStack#isStackVisibleLocked} to determine if a specific
- * stack is visible or not. */
- boolean isFrontStack(ActivityStack stack) {
+ boolean isFocusedStack(ActivityStack stack) {
if (stack == null) {
return false;
}
@@ -472,18 +498,22 @@
return stack == mFocusedStack;
}
- void setFocusStack(String reason, ActivityStack lastFocusedStack) {
- ArrayList<ActivityStack> stacks = mHomeStack.mStacks;
- final int topNdx = stacks.size() - 1;
- if (topNdx <= 0) {
- return;
+ /** The top most stack. */
+ boolean isFrontStack(ActivityStack stack) {
+ if (stack == null) {
+ return false;
}
- final ActivityStack topStack = stacks.get(topNdx);
- mFocusedStack = topStack;
- if (lastFocusedStack != null) {
- mLastFocusedStack = lastFocusedStack;
+ final ActivityRecord parent = stack.mActivityContainer.mParentActivity;
+ if (parent != null) {
+ stack = parent.task.stack;
}
+ return stack == mHomeStack.mStacks.get((mHomeStack.mStacks.size() - 1));
+ }
+
+ void setFocusStack(String reason, ActivityStack focusedStack) {
+ mLastFocusedStack = mFocusedStack;
+ mFocusedStack = focusedStack;
EventLogTags.writeAmFocusedStack(
mCurrentUser, mFocusedStack == null ? -1 : mFocusedStack.getStackId(),
@@ -552,8 +582,8 @@
* @param id Id of the task we would like returned.
* @param restoreFromRecents If the id was not in the active list, but was found in recents,
* restore the task from recents to the active list.
- * @param stackId The stack to restore the task to (default launch stack will be used
- * if stackId is {@link android.app.ActivityManager#INVALID_STACK_ID}).
+ * @param stackId The stack to restore the task to (default launch stack will be used if
+ * stackId is {@link android.app.ActivityManager.StackId#INVALID_STACK_ID}).
*/
TaskRecord anyTaskForIdLocked(int id, boolean restoreFromRecents, int stackId) {
int numDisplays = mActivityDisplays.size();
@@ -641,7 +671,7 @@
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
- if (!isFrontStack(stack)) {
+ if (!isFocusedStack(stack)) {
continue;
}
ActivityRecord hr = stack.topRunningActivityLocked();
@@ -672,7 +702,7 @@
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
- if (!isFrontStack(stack) || stack.numActivities() == 0) {
+ if (!isFocusedStack(stack) || stack.numActivities() == 0) {
continue;
}
final ActivityRecord resumedActivity = stack.mResumedActivity;
@@ -691,7 +721,7 @@
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
- if (isFrontStack(stack)) {
+ if (isFocusedStack(stack)) {
final ActivityRecord r = stack.mResumedActivity;
if (r != null && r.state != RESUMED) {
return false;
@@ -736,7 +766,7 @@
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
- if (!isFrontStack(stack) && stack.mResumedActivity != null) {
+ if (!isFocusedStack(stack) && stack.mResumedActivity != null) {
if (DEBUG_STATES) Slog.d(TAG_STATES, "pauseBackStacks: stack=" + stack +
" mResumedActivity=" + stack.mResumedActivity);
someActivityPaused |= stack.startPausingLocked(userLeaving, false, resuming,
@@ -916,10 +946,6 @@
mService.setDebugApp(aInfo.processName, true, false);
}
- if ((startFlags & ActivityManager.START_FLAG_OPENGL_TRACES) != 0) {
- mService.setOpenGlTraceApp(aInfo.applicationInfo, aInfo.processName);
- }
-
if ((startFlags & ActivityManager.START_FLAG_TRACK_ALLOCATION) != 0) {
mService.setTrackAllocationApp(aInfo.applicationInfo, aInfo.processName);
}
@@ -954,7 +980,7 @@
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
IBinder resultTo, String resultWho, int requestCode, int startFlags,
ProfilerInfo profilerInfo, WaitResult outResult, Configuration config,
- Bundle options, boolean ignoreTargetSecurity, int userId,
+ Bundle bOptions, boolean ignoreTargetSecurity, int userId,
IActivityContainer iContainer, TaskRecord inTask) {
// Refuse possible leaked file descriptors
if (intent != null && intent.hasFileDescriptors()) {
@@ -969,6 +995,7 @@
ActivityInfo aInfo =
resolveActivity(intent, resolvedType, startFlags, profilerInfo, userId);
+ ActivityOptions options = ActivityOptions.fromBundle(bOptions);
ActivityContainer container = (ActivityContainer)iContainer;
synchronized (mService) {
if (container != null && container.mParentActivity != null &&
@@ -1126,7 +1153,7 @@
final int startActivities(IApplicationThread caller, int callingUid, String callingPackage,
Intent[] intents, String[] resolvedTypes, IBinder resultTo,
- Bundle options, int userId) {
+ Bundle bOptions, int userId) {
if (intents == null) {
throw new NullPointerException("intents is null");
}
@@ -1179,16 +1206,12 @@
"FLAG_CANT_SAVE_STATE not supported here");
}
- Bundle theseOptions;
- if (options != null && i == intents.length-1) {
- theseOptions = options;
- } else {
- theseOptions = null;
- }
+ ActivityOptions options = ActivityOptions.fromBundle(
+ i == intents.length - 1 ? bOptions : null);
int res = startActivityLocked(caller, intent, resolvedTypes[i],
aInfo, null, null, resultTo, null, -1, callingPid, callingUid,
callingPackage, callingPid, callingUid,
- 0, theseOptions, false, componentSpecified, outActivity, null, null);
+ 0, options, false, componentSpecified, outActivity, null, null);
if (res < 0) {
return res;
}
@@ -1271,7 +1294,7 @@
// Home process is the root process of the task.
mService.mHomeProcess = task.mActivities.get(0).app;
}
- mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName());
+ mService.notifyPackageUse(r.intent.getComponent().getPackageName());
r.sleeping = false;
r.forceNewConfig = false;
mService.showAskCompatModeDialogLocked(r);
@@ -1377,7 +1400,7 @@
// launching the initial activity (that is, home), so that it can have
// a chance to initialize itself while in the background, making the
// switch back to it faster and look better.
- if (isFrontStack(stack)) {
+ if (isFocusedStack(stack)) {
mService.startSetupActivityLocked();
}
@@ -1427,7 +1450,7 @@
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
IBinder resultTo, String resultWho, int requestCode,
int callingPid, int callingUid, String callingPackage,
- int realCallingPid, int realCallingUid, int startFlags, Bundle options,
+ int realCallingPid, int realCallingUid, int startFlags, ActivityOptions options,
boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
ActivityContainer container, TaskRecord inTask) {
int err = ActivityManager.START_SUCCESS;
@@ -1641,6 +1664,38 @@
}
}
+ UserInfo user = getUserInfo(userId);
+ // TODO: Timeout for work challenge
+ if (user.isManagedProfile()
+ && mService.mContext.getSystemService(StorageManager.class)
+ .isPerUserEncryptionEnabled()) {
+ KeyguardManager km = (KeyguardManager) mService.mContext
+ .getSystemService(Context.KEYGUARD_SERVICE);
+
+ IIntentSender target = mService.getIntentSenderLocked(
+ ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
+ Binder.getCallingUid(), userId, null, null, 0, new Intent[]{ intent },
+ new String[]{ resolvedType },
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
+ | PendingIntent.FLAG_IMMUTABLE, null);
+ int flags = intent.getFlags();
+ intent = km.createConfirmDeviceCredentialIntent(null, null);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, aInfo.packageName);
+ intent.putExtra(Intent.EXTRA_INTENT, new IntentSender(target));
+ intent.putExtra(Intent.EXTRA_USER_ID, userId);
+ intent.setFlags(flags);
+
+ resolvedType = null;
+ callingUid = realCallingUid;
+ callingPid = realCallingPid;
+
+ UserInfo parent = UserManager.get(mService.mContext).getProfileParent(userId);
+ aInfo = resolveActivity(intent, null, PackageManager.MATCH_DEFAULT_ONLY
+ | ActivityManagerService.STOCK_PM_FLAGS, null, parent.id);
+ }
+
if (abort) {
if (resultRecord != null) {
resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode,
@@ -1670,8 +1725,8 @@
|| stack.mResumedActivity.info.applicationInfo.uid != callingUid)) {
if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid,
realCallingPid, realCallingUid, "Activity start")) {
- PendingActivityLaunch pal =
- new PendingActivityLaunch(r, sourceRecord, startFlags, stack);
+ PendingActivityLaunch pal = new PendingActivityLaunch(r,
+ sourceRecord, startFlags, stack, callerApp);
mPendingActivityLaunches.add(pal);
ActivityOptions.abort(options);
return ActivityManager.START_SWITCHES_CANCELED;
@@ -1704,6 +1759,15 @@
return err;
}
+ private UserInfo getUserInfo(int userId) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return UserManager.get(mService.mContext).getUserInfo(userId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
private int getComponentRestrictionForCallingPackage(ActivityInfo activityInfo,
String callingPackage, int callingPid, int callingUid, boolean ignoreTargetSecurity) {
if (!ignoreTargetSecurity && mService.checkComponentPermission(activityInfo.permission,
@@ -1776,70 +1840,67 @@
ActivityStack computeStackFocus(ActivityRecord r, boolean newTask, Rect bounds) {
final TaskRecord task = r.task;
- // On leanback only devices we should keep all activities in the same stack.
- if (!mLeanbackOnlyDevice &&
- (r.isApplicationActivity() || (task != null && task.isApplicationTask()))) {
+ if (!(r.isApplicationActivity() || (task != null && task.isApplicationTask()))) {
+ return mHomeStack;
+ }
- ActivityStack stack;
+ ActivityStack stack;
- if (task != null && task.stack != null) {
- stack = task.stack;
- if (stack.isOnHomeDisplay()) {
- if (mFocusedStack != stack) {
- if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
- "computeStackFocus: Setting " + "focused stack to r=" + r
- + " task=" + task);
- } else {
- if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
- "computeStackFocus: Focused stack already=" + mFocusedStack);
- }
- }
- return stack;
- }
-
- final ActivityContainer container = r.mInitialActivityContainer;
- if (container != null) {
- // The first time put it on the desired stack, after this put on task stack.
- r.mInitialActivityContainer = null;
- return container.mStack;
- }
-
- // The fullscreen stack can contain any task regardless of if the task is resizeable
- // or not. So, we let the task go in the fullscreen task if it is the focus stack.
- // If the freeform stack has focus, and the activity to be launched is resizeable,
- // we can also put it in the focused stack.
- final boolean canUseFocusedStack =
- mFocusedStack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID
- || mFocusedStack.mStackId == FREEFORM_WORKSPACE_STACK_ID && r.info.resizeable;
- if (canUseFocusedStack
- && (!newTask || mFocusedStack.mActivityContainer.isEligibleForNewTasks())) {
- if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
- "computeStackFocus: Have a focused stack=" + mFocusedStack);
- return mFocusedStack;
- }
-
- // We first try to put the task in the first dynamic stack.
- final ArrayList<ActivityStack> homeDisplayStacks = mHomeStack.mStacks;
- for (int stackNdx = homeDisplayStacks.size() - 1; stackNdx >= 0; --stackNdx) {
- stack = homeDisplayStacks.get(stackNdx);
- final boolean isDynamicStack = stack.mStackId >= FIRST_DYNAMIC_STACK_ID;
- if (isDynamicStack) {
+ if (task != null && task.stack != null) {
+ stack = task.stack;
+ if (stack.isOnHomeDisplay()) {
+ if (mFocusedStack != stack) {
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
- "computeStackFocus: Setting focused stack=" + stack);
- return stack;
+ "computeStackFocus: Setting " + "focused stack to r=" + r
+ + " task=" + task);
+ } else {
+ if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
+ "computeStackFocus: Focused stack already=" + mFocusedStack);
}
}
-
- // If there is no suitable dynamic stack then we figure out which static stack to use.
- final int stackId = task != null ? task.getLaunchStackId() :
- bounds != null ? FREEFORM_WORKSPACE_STACK_ID :
- FULLSCREEN_WORKSPACE_STACK_ID;
- stack = getStack(stackId, CREATE_IF_NEEDED, ON_TOP);
- if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: New stack r="
- + r + " stackId=" + stack.mStackId);
return stack;
}
- return mHomeStack;
+
+ final ActivityContainer container = r.mInitialActivityContainer;
+ if (container != null) {
+ // The first time put it on the desired stack, after this put on task stack.
+ r.mInitialActivityContainer = null;
+ return container.mStack;
+ }
+
+ // The fullscreen stack can contain any task regardless of if the task is resizeable
+ // or not. So, we let the task go in the fullscreen task if it is the focus stack.
+ // If the freeform stack has focus, and the activity to be launched is resizeable,
+ // we can also put it in the focused stack.
+ final boolean canUseFocusedStack =
+ mFocusedStack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID
+ || mFocusedStack.mStackId == FREEFORM_WORKSPACE_STACK_ID && r.info.resizeable;
+ if (canUseFocusedStack
+ && (!newTask || mFocusedStack.mActivityContainer.isEligibleForNewTasks())) {
+ if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
+ "computeStackFocus: Have a focused stack=" + mFocusedStack);
+ return mFocusedStack;
+ }
+
+ // We first try to put the task in the first dynamic stack.
+ final ArrayList<ActivityStack> homeDisplayStacks = mHomeStack.mStacks;
+ for (int stackNdx = homeDisplayStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ stack = homeDisplayStacks.get(stackNdx);
+ if (!StackId.isStaticStack(stack.mStackId)) {
+ if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
+ "computeStackFocus: Setting focused stack=" + stack);
+ return stack;
+ }
+ }
+
+ // If there is no suitable dynamic stack then we figure out which static stack to use.
+ final int stackId = task != null ? task.getLaunchStackId() :
+ bounds != null ? FREEFORM_WORKSPACE_STACK_ID :
+ FULLSCREEN_WORKSPACE_STACK_ID;
+ stack = getStack(stackId, CREATE_IF_NEEDED, ON_TOP);
+ if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: New stack r="
+ + r + " stackId=" + stack.mStackId);
+ return stack;
}
boolean setFocusedStack(ActivityRecord r, String reason) {
@@ -1858,17 +1919,16 @@
final int startActivityUncheckedLocked(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags,
- boolean doResume, Bundle options, TaskRecord inTask) {
+ boolean doResume, ActivityOptions options, TaskRecord inTask) {
final Intent intent = r.intent;
final int callingUid = r.launchedFromUid;
boolean overrideBounds = false;
Rect newBounds = null;
if (options != null && (r.info.resizeable || (inTask != null && inTask.mResizeable))) {
- ActivityOptions opts = new ActivityOptions(options);
- if (opts.hasBounds()) {
+ if (options.hasBounds()) {
overrideBounds = true;
- newBounds = opts.getBounds();
+ newBounds = options.getBounds();
}
}
@@ -2537,8 +2597,14 @@
final void doPendingActivityLaunchesLocked(boolean doResume) {
while (!mPendingActivityLaunches.isEmpty()) {
PendingActivityLaunch pal = mPendingActivityLaunches.remove(0);
- startActivityUncheckedLocked(pal.r, pal.sourceRecord, null, null, pal.startFlags,
- doResume && mPendingActivityLaunches.isEmpty(), null, null);
+
+ try {
+ startActivityUncheckedLocked(pal.r, pal.sourceRecord, null, null, pal.startFlags,
+ doResume && mPendingActivityLaunches.isEmpty(), null, null);
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception during pending activity launch pal=" + pal, e);
+ pal.sendErrorResult(e.getMessage());
+ }
}
}
@@ -2621,7 +2687,7 @@
r.idle = true;
//Slog.i(TAG, "IDLE: mBooted=" + mBooted + ", fromTimeout=" + fromTimeout);
- if (isFrontStack(r.task.stack) || fromTimeout) {
+ if (isFocusedStack(r.task.stack) || fromTimeout) {
booting = checkFinishBootingLocked();
}
}
@@ -2683,7 +2749,7 @@
// Complete user switch
if (startingUsers != null) {
for (int i = 0; i < startingUsers.size(); i++) {
- mService.finishUserSwitch(startingUsers.get(i));
+ mService.mUserController.finishUserSwitch(startingUsers.get(i));
}
}
// Complete starting up of background users
@@ -2691,7 +2757,7 @@
startingUsers = new ArrayList<UserState>(mStartingBackgroundUsers);
mStartingBackgroundUsers.clear();
for (int i = 0; i < startingUsers.size(); i++) {
- mService.finishUserBoot(startingUsers.get(i));
+ mService.mUserController.finishUserBoot(startingUsers.get(i));
}
}
}
@@ -2749,8 +2815,7 @@
boolean didSomething = false;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
- final int numStacks = stacks.size();
- for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
if (stack.finishDisabledPackageActivitiesLocked(
packageName, filterByClasses, doit, evenPersistent, userId)) {
@@ -2774,7 +2839,7 @@
final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
- if (isFrontStack(stack)) {
+ if (isFocusedStack(stack)) {
if (stack.mResumedActivity != null) {
fgApp = stack.mResumedActivity.app;
} else if (stack.mPausingActivity != null) {
@@ -2799,14 +2864,14 @@
return resumeTopActivitiesLocked(null, null, null);
}
- boolean resumeTopActivitiesLocked(ActivityStack targetStack, ActivityRecord target,
- Bundle targetOptions) {
+ boolean resumeTopActivitiesLocked(
+ ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
if (targetStack == null) {
targetStack = mFocusedStack;
}
// Do targetStack first.
boolean result = false;
- if (isFrontStack(targetStack)) {
+ if (isFocusedStack(targetStack)) {
result = targetStack.resumeTopActivityLocked(target, targetOptions);
}
@@ -2818,7 +2883,7 @@
// Already started above.
continue;
}
- if (isFrontStack(stack)) {
+ if (isFocusedStack(stack)) {
stack.resumeTopActivityLocked(null);
}
}
@@ -2848,7 +2913,8 @@
}
}
- void findTaskToMoveToFrontLocked(TaskRecord task, int flags, Bundle options, String reason) {
+ void findTaskToMoveToFrontLocked(
+ TaskRecord task, int flags, ActivityOptions options, String reason) {
if ((flags & ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) {
mUserLeaving = true;
}
@@ -2863,25 +2929,27 @@
return;
}
- int stackId = task.stack.mStackId;
if (task.mResizeable && options != null) {
- ActivityOptions opts = new ActivityOptions(options);
- if (opts.hasBounds()) {
- Rect bounds = opts.getBounds();
+ if (options.hasBounds()) {
+ Rect bounds = options.getBounds();
task.updateOverrideConfiguration(bounds);
+ final int stackId = task.getLaunchStackId();
+ if (stackId != task.stack.mStackId) {
+ moveTaskToStackUncheckedLocked(task, stackId, ON_TOP, !FORCE_FOCUS, reason);
+ // moveTaskToStackUncheckedLocked() should already placed the task on top,
+ // still need moveTaskToFrontLocked() below for any transition settings.
+ }
+ // 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,
false /*relayout*/, false /*forced*/);
- stackId = task.getLaunchStackId();
}
}
- if (stackId != task.stack.mStackId) {
- moveTaskToStackUncheckedLocked(task, stackId, ON_TOP, FORCE_FOCUS, reason);
- } else {
- task.stack.moveTaskToFrontLocked(task, false /* noAnimation */, options,
- task.getTopActivity() == null ? null : task.getTopActivity().appTimeTracker,
- reason);
- }
+ final ActivityRecord r = task.getTopActivity();
+ task.stack.moveTaskToFrontLocked(task, false /* noAnimation */, options,
+ r == null ? null : r.appTimeTracker, reason);
if (DEBUG_STACK) Slog.d(TAG_STACK,
"findTaskToMoveToFront: moved to front of stack=" + task.stack);
@@ -2896,15 +2964,14 @@
if (activityContainer != null) {
return activityContainer.mStack;
}
- if (!createStaticStackIfNeeded
- || (stackId < FIRST_STATIC_STACK_ID || stackId > LAST_STATIC_STACK_ID)) {
+ if (!createStaticStackIfNeeded || !StackId.isStaticStack(stackId)) {
return null;
}
return createStackOnDisplay(stackId, Display.DEFAULT_DISPLAY, createOnTop);
}
ArrayList<ActivityStack> getStacks() {
- ArrayList<ActivityStack> allStacks = new ArrayList<ActivityStack>();
+ ArrayList<ActivityStack> allStacks = new ArrayList<>();
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
allStacks.addAll(mActivityDisplays.valueAt(displayNdx).mStacks);
}
@@ -2989,84 +3056,87 @@
}
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stackId);
+ mWindowManager.deferSurfaceLayout();
+ try {
+ ActivityRecord r = stack.topRunningActivityLocked();
- ActivityRecord r = stack.topRunningActivityLocked();
-
- mTmpBounds.clear();
- mTmpConfigs.clear();
- ArrayList<TaskRecord> tasks = stack.getAllTasks();
- for (int i = tasks.size() - 1; i >= 0; i--) {
- TaskRecord task = tasks.get(i);
- if (task.mResizeable) {
- if (stack.mStackId == FREEFORM_WORKSPACE_STACK_ID) {
- // For freeform stack we don't adjust the size of the tasks to match that
- // of the stack, but we do try to make sure the tasks are still contained
- // with the bounds of the stack.
- tempRect2.set(task.mBounds);
- fitWithinBounds(tempRect2, bounds);
- task.updateOverrideConfiguration(tempRect2);
- } else {
- task.updateOverrideConfiguration(bounds);
- }
- }
-
- mTmpConfigs.put(task.taskId, task.mOverrideConfig);
- mTmpBounds.put(task.taskId, task.mBounds);
- }
- stack.mFullscreen = mWindowManager.resizeStack(stackId, bounds, mTmpConfigs, mTmpBounds);
- if (stack.mStackId == DOCKED_STACK_ID) {
- // Dock stack funness...Yay!
- if (stack.mFullscreen) {
- // The dock stack went fullscreen which is kinda like dismissing it.
- // 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++) {
- if (i != DOCKED_STACK_ID && getStack(i) != null) {
- resizeStackLocked(i, null, preserveWindows, true);
+ mTmpBounds.clear();
+ mTmpConfigs.clear();
+ ArrayList<TaskRecord> tasks = stack.getAllTasks();
+ for (int i = tasks.size() - 1; i >= 0; i--) {
+ TaskRecord task = tasks.get(i);
+ if (task.mResizeable) {
+ if (stack.mStackId == FREEFORM_WORKSPACE_STACK_ID) {
+ // For freeform stack we don't adjust the size of the tasks to match that
+ // of the stack, but we do try to make sure the tasks are still contained
+ // with the bounds of the stack.
+ tempRect2.set(task.mBounds);
+ fitWithinBounds(tempRect2, bounds);
+ task.updateOverrideConfiguration(tempRect2);
+ } else {
+ task.updateOverrideConfiguration(bounds);
}
}
- final int count = tasks.size();
- for (int i = 0; i < count; i++) {
- moveTaskToStackLocked(tasks.get(i).taskId,
- FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP, FORCE_FOCUS);
- }
+ mTmpConfigs.put(task.taskId, task.mOverrideConfig);
+ mTmpBounds.put(task.taskId, task.mBounds);
+ }
+ stack.mFullscreen = mWindowManager.resizeStack(stackId, bounds, mTmpConfigs, mTmpBounds);
+ if (stack.mStackId == DOCKED_STACK_ID) {
+ // Dock stack funness...Yay!
+ if (stack.mFullscreen) {
+ // The dock stack went fullscreen which is kinda like dismissing it.
+ // 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++) {
+ if (StackId.isResizeableByDockedStack(i) && getStack(i) != null) {
+ resizeStackLocked(i, null, preserveWindows, true);
+ }
+ }
- // stack shouldn't contain anymore activities, so nothing to resume.
- r = null;
- } else {
- // Docked stacks occupy a dedicated region on screen so the size of all other
- // static stacks need to be adjusted so they don't overlap with the docked stack.
- // We get the bounds to use from window manager which has been adjusted for any
- // screen controls and is also the same for all stacks.
- mWindowManager.getStackDockedModeBounds(HOME_STACK_ID, tempRect);
+ final int count = tasks.size();
+ for (int i = 0; i < count; i++) {
+ moveTaskToStackLocked(tasks.get(i).taskId,
+ FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP, FORCE_FOCUS, "resizeStack");
+ }
- for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
- if (i != DOCKED_STACK_ID) {
- ActivityStack otherStack = getStack(i);
- if (otherStack != null) {
- resizeStackLocked(i, tempRect, PRESERVE_WINDOWS, true);
+ // stack shouldn't contain anymore activities, so nothing to resume.
+ r = null;
+ } else {
+ // Docked stacks occupy a dedicated region on screen so the size of all other
+ // static stacks need to be adjusted so they don't overlap with the docked stack.
+ // We get the bounds to use from window manager which has been adjusted for any
+ // screen controls and is also the same for all stacks.
+ mWindowManager.getStackDockedModeBounds(HOME_STACK_ID, tempRect);
+
+ 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, PRESERVE_WINDOWS, true);
+ }
}
}
}
+ // Since we are resizing the stack, all other operations should strive to preserve
+ // windows.
+ preserveWindows = true;
}
- // Since we are resizing the stack, all other operations should strive to preserve
- // windows.
- preserveWindows = true;
- }
- stack.setBounds(bounds);
+ stack.setBounds(bounds);
- if (r != null) {
- final boolean updated = stack.ensureActivityConfigurationLocked(r, 0, preserveWindows);
- // And we need to make sure at this point that all other activities
- // are made visible with the correct configuration.
- ensureActivitiesVisibleLocked(r, 0, preserveWindows);
- if (!updated) {
- resumeTopActivitiesLocked(stack, null, null);
+ if (r != null) {
+ final boolean updated = stack.ensureActivityConfigurationLocked(r, 0, preserveWindows);
+ // And we need to make sure at this point that all other activities
+ // are made visible with the correct configuration.
+ ensureActivitiesVisibleLocked(r, 0, preserveWindows);
+ if (!updated) {
+ resumeTopActivitiesLocked(stack, null, null);
+ }
}
+ } finally {
+ mWindowManager.continueSurfaceLayout();
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
-
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
void resizeTaskLocked(TaskRecord task, Rect bounds, int resizeMode, boolean preserveWindow) {
@@ -3075,6 +3145,8 @@
return;
}
+ adjustForMinimalTaskDimensions(task, bounds);
+
// If this is a forced resize, let it go through even if the bounds is not changing,
// as we might need a relayout due to surface size change (to/from fullscreen).
final boolean forced = (resizeMode & RESIZE_MODE_FORCED) != 0;
@@ -3123,6 +3195,38 @@
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
+ private void adjustForMinimalTaskDimensions(TaskRecord task, Rect bounds) {
+ if (bounds == null) {
+ return;
+ }
+ int minimalSize = task.mMinimalSize == -1 ? mDefaultMinimalSizeOfResizeableTask
+ : task.mMinimalSize;
+ final boolean adjustWidth = minimalSize > bounds.width();
+ final boolean adjustHeight = minimalSize > bounds.height();
+ if (!(adjustWidth || adjustHeight)) {
+ return;
+ }
+ Rect taskBounds = task.mBounds;
+ if (adjustWidth) {
+ if (taskBounds != null && bounds.right == taskBounds.right) {
+ bounds.left = bounds.right - minimalSize;
+ } 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;
+ }
+ }
+ if (adjustHeight) {
+ if (taskBounds != null && bounds.bottom == taskBounds.bottom) {
+ bounds.top = bounds.bottom - minimalSize;
+ } 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;
+ }
+ }
+ }
+
ActivityStack createStackOnDisplay(int stackId, int displayId, boolean onTop) {
ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
if (activityDisplay == null) {
@@ -3150,12 +3254,12 @@
* Restores a recent task to a stack
* @param task The recent task to be restored.
* @param stackId The stack to restore the task to (default launch stack will be used
- * if stackId is {@link android.app.ActivityManager#INVALID_STACK_ID}).
+ * if stackId is {@link android.app.ActivityManager.StackId#INVALID_STACK_ID}).
* @return true if the task has been restored successfully.
*/
private boolean restoreRecentTaskLocked(TaskRecord task, int stackId) {
if (stackId == INVALID_STACK_ID) {
- stackId = mLeanbackOnlyDevice ? mHomeStack.mStackId : task.getLaunchStackId();
+ stackId = task.getLaunchStackId();
}
if (task.stack != null) {
// Task has already been restored once. See if we need to do anything more
@@ -3168,7 +3272,7 @@
task.stack.removeTask(task, "restoreRecentTaskLocked", MOVING);
}
- ActivityStack stack =
+ final ActivityStack stack =
getStack(stackId, CREATE_IF_NEEDED, !ON_TOP);
if (stack == null) {
@@ -3204,7 +3308,7 @@
ActivityStack moveTaskToStackUncheckedLocked(
TaskRecord task, int stackId, boolean toTop, boolean forceFocus, String reason) {
final ActivityRecord r = task.getTopActivity();
- final boolean wasFocused = isFrontStack(task.stack) && (topRunningActivityLocked() == r);
+ final boolean wasFocused = isFocusedStack(task.stack) && (topRunningActivityLocked() == r);
final boolean wasResumed = wasFocused && (task.stack.mResumedActivity == r);
final boolean resizeable = task.mResizeable;
@@ -3222,39 +3326,33 @@
// If the task had focus before (or we're requested to move focus),
// move focus to the new stack.
- if (forceFocus || wasFocused) {
- // If the task owns the last resumed activity, transfer that together,
- // so that we don't resume the same activity again in the new stack.
- // Apps may depend on onResume()/onPause() being called in pairs.
- if (wasResumed) {
- stack.mResumedActivity = r;
- }
- // move the stack in which we are placing the task to the front.
- stack.moveToFront(reason);
- }
+ stack.setFocusAndResumeStateIfNeeded(
+ r, forceFocus || wasFocused, wasResumed, reason);
return stack;
}
- void moveTaskToStackLocked(int taskId, int stackId, boolean toTop, boolean forceFocus) {
+ void moveTaskToStackLocked(int taskId, int stackId, boolean toTop, boolean forceFocus,
+ String reason) {
final TaskRecord task = anyTaskForIdLocked(taskId);
if (task == null) {
Slog.w(TAG, "moveTaskToStack: no task for id=" + taskId);
return;
}
- final String reason = "moveTaskToStack";
- if (stackId == DOCKED_STACK_ID || stackId == FULLSCREEN_WORKSPACE_STACK_ID) {
+
+ final ActivityRecord topActivity = task.getTopActivity();
+ if (StackId.preserveWindowOnTaskMove(stackId) && topActivity != null) {
// We are about to relaunch the activity because its configuration changed due to
// being maximized, i.e. size change. The activity will first remove the old window
// and then add a new one. This call will tell window manager about this, so it can
// preserve the old window until the new one is drawn. This prevents having a gap
// between the removal and addition, in which no window is visible. We also want the
// entrance of the new window to be properly animated.
- ActivityRecord r = task.getTopActivity();
- mWindowManager.setReplacingWindow(r.appToken, true /* animate */);
+ mWindowManager.setReplacingWindow(topActivity.appToken, true /* animate */);
}
final ActivityStack stack =
- moveTaskToStackUncheckedLocked(task, stackId, toTop, forceFocus, reason);
+ moveTaskToStackUncheckedLocked(task, stackId, toTop, forceFocus,
+ "moveTaskToStack:" + reason);
// Make sure the task has the appropriate bounds/size for the stack it is in.
if (stackId == FULLSCREEN_WORKSPACE_STACK_ID && task.mBounds != null) {
@@ -3264,7 +3362,7 @@
&& task.mBounds == null && task.mLastNonFullscreenBounds != null) {
resizeTaskLocked(task, task.mLastNonFullscreenBounds,
RESIZE_MODE_SYSTEM, !PRESERVE_WINDOWS);
- } else if (stackId == DOCKED_STACK_ID) {
+ } else if (stackId == DOCKED_STACK_ID || stackId == PINNED_STACK_ID) {
resizeTaskLocked(task, stack.mBounds,
RESIZE_MODE_SYSTEM, !PRESERVE_WINDOWS);
}
@@ -3275,15 +3373,59 @@
resumeTopActivitiesLocked();
}
+ boolean moveTopStackActivityToPinnedStackLocked(int stackId, Rect bounds) {
+ final ActivityStack stack = getStack(stackId, !CREATE_IF_NEEDED, !ON_TOP);
+ if (stack == null) {
+ throw new IllegalArgumentException(
+ "moveTopStackActivityToPinnedStackLocked: Unknown stackId=" + stackId);
+ }
+
+ final ActivityRecord r = stack.topRunningActivityLocked();
+ if (r == null) {
+ Slog.w(TAG, "moveTopStackActivityToPinnedStackLocked: No top running activity"
+ + " in stack=" + stack);
+ return false;
+ }
+
+ if (!r.info.supportsPip) {
+ Slog.w(TAG,
+ "moveTopStackActivityToPinnedStackLocked: Picture-In-Picture not supported for "
+ + " r=" + r);
+ return false;
+ }
+
+ final TaskRecord task = r.task;
+ if (task.mActivities.size() == 1) {
+ // There is only one activity in the task. So, we can just move the task over to the
+ // pinned stack without re-parenting the activity in a different task.
+ moveTaskToStackLocked(task.taskId, PINNED_STACK_ID, ON_TOP, FORCE_FOCUS,
+ "moveTopActivityToPinnedStack");
+ } else {
+ final ActivityStack pinnedStack = getStack(PINNED_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
+ pinnedStack.moveActivityToStack(r);
+ }
+
+ resizeStackLocked(PINNED_STACK_ID, bounds, PRESERVE_WINDOWS, true);
+
+ // The task might have already been running and its visibility needs to be synchronized with
+ // the visibility of the stack / windows.
+ ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+ resumeTopActivitiesLocked();
+ return true;
+ }
+
void positionTaskInStackLocked(int taskId, int stackId, int position) {
final TaskRecord task = anyTaskForIdLocked(taskId);
if (task == null) {
Slog.w(TAG, "positionTaskInStackLocked: no task for id=" + taskId);
return;
}
- ActivityStack stack =
- getStack(stackId, CREATE_IF_NEEDED, !ON_TOP);
- mWindowManager.positionTaskInStack(taskId, stackId, position);
+ final ActivityStack stack = getStack(stackId, CREATE_IF_NEEDED, !ON_TOP);
+
+ task.updateOverrideConfigurationForStack(stack);
+
+ mWindowManager.positionTaskInStack(
+ taskId, stackId, position, task.mBounds, task.mOverrideConfig);
final boolean stackChanged = task.stack != null && task.stack != stack;
if (stackChanged) {
task.stack.removeTask(task, "moveTaskToStack", MOVING);
@@ -3395,7 +3537,7 @@
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
stack.awakeFromSleepingLocked();
- if (isFrontStack(stack)) {
+ if (isFocusedStack(stack)) {
resumeTopActivitiesLocked();
}
}
@@ -3462,7 +3604,7 @@
boolean reportResumedActivityLocked(ActivityRecord r) {
final ActivityStack stack = r.task.stack;
- if (isFrontStack(stack)) {
+ if (isFocusedStack(stack)) {
mService.updateUsageStats(r, true);
}
if (allResumedActivitiesComplete()) {
@@ -3542,7 +3684,7 @@
void handleLaunchTaskBehindCompleteLocked(ActivityRecord r) {
r.mLaunchTaskBehind = false;
final TaskRecord task = r.task;
- task.setLastThumbnail(task.stack.screenshotActivities(r));
+ task.setLastThumbnailLocked(task.stack.screenshotActivities(r));
mRecentTasks.addLocked(task);
mService.notifyTaskStackChangedLocked();
mWindowManager.setAppVisibility(r.appToken, false);
@@ -3565,6 +3707,24 @@
}
}
+ void invalidateTaskLayers() {
+ mTaskLayersChanged = true;
+ }
+
+ void rankTaskLayersIfNeeded() {
+ if (!mTaskLayersChanged) {
+ return;
+ }
+ mTaskLayersChanged = false;
+ for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); displayNdx++) {
+ final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+ int baseLayer = 0;
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ baseLayer += stacks.get(stackNdx).rankTaskLayers(baseLayer);
+ }
+ }
+ }
+
void clearOtherAppTimeTrackers(AppTimeTracker except) {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
@@ -3687,10 +3847,7 @@
/** Checks whether the userid is a profile of the current user. */
boolean isCurrentProfileLocked(int userId) {
if (userId == mCurrentUser) return true;
- for (int i = 0; i < mService.mCurrentProfileIds.length; i++) {
- if (mService.mCurrentProfileIds[i] == userId) return true;
- }
- return false;
+ return mService.mUserController.isCurrentProfileLocked(userId);
}
/** Checks whether the activity should be shown for current user. */
@@ -3740,7 +3897,7 @@
final ActivityStack stack = stacks.get(stackNdx);
final ActivityRecord r = stack.topRunningActivityLocked();
final ActivityState state = r == null ? DESTROYED : r.state;
- if (isFrontStack(stack)) {
+ if (isFocusedStack(stack)) {
if (r == null) Slog.e(TAG,
"validateTop...: null top activity, stack=" + stack);
else {
@@ -4026,6 +4183,7 @@
return;
}
mActivityDisplays.put(displayId, activityDisplay);
+ calculateDefaultMinimalSizeOfResizeableTasks(activityDisplay);
}
}
if (newDisplay) {
@@ -4033,6 +4191,16 @@
}
}
+ private void calculateDefaultMinimalSizeOfResizeableTasks(ActivityDisplay display) {
+ if (display.mDisplayId != Display.DEFAULT_DISPLAY) {
+ return;
+ }
+ final float fraction = mService.mContext.getResources().getFraction(com.android.internal.R.
+ fraction.config_displayFractionForDefaultMinimalSizeOfResizeableTask, 1, 1);
+ mDefaultMinimalSizeOfResizeableTask = (int) (fraction * Math.min(
+ display.mDisplayInfo.logicalWidth, display.mDisplayInfo.logicalHeight));
+ }
+
private void handleDisplayRemoved(int displayId) {
synchronized (mService) {
ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
@@ -4563,7 +4731,7 @@
@Override
public final int startActivity(Intent intent) {
mService.enforceNotIsolatedCaller("ActivityContainer.startActivity");
- final int userId = mService.handleIncomingUser(Binder.getCallingPid(),
+ final int userId = mService.mUserController.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), mCurrentUser, false,
ActivityManagerService.ALLOW_FULL_ONLY, "ActivityContainer", null);
@@ -4589,7 +4757,7 @@
throw new IllegalArgumentException("Bad PendingIntent object");
}
- final int userId = mService.handleIncomingUser(Binder.getCallingPid(),
+ final int userId = mService.mUserController.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), mCurrentUser, false,
ActivityManagerService.ALLOW_FULL_ONLY, "ActivityContainer", null);
@@ -4848,18 +5016,6 @@
}
}
- private boolean isLeanbackOnlyDevice() {
- boolean onLeanbackOnly = false;
- try {
- onLeanbackOnly = AppGlobals.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_LEANBACK_ONLY);
- } catch (RemoteException e) {
- // noop
- }
-
- return onLeanbackOnly;
- }
-
/**
* Adjust bounds to stay within stack bounds.
*
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 62768c3..c7228ce 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -834,10 +834,10 @@
}
@Override
- public void noteDeviceIdleMode(boolean enabled, String activeReason, int activeUid) {
+ public void noteDeviceIdleMode(int mode, String activeReason, int activeUid) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.noteDeviceIdleModeLocked(enabled, activeReason, activeUid);
+ mStats.noteDeviceIdleModeLocked(mode, activeReason, activeUid);
}
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 960cbf1..d317791 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -44,6 +44,7 @@
import android.os.UserHandle;
import android.util.EventLog;
import android.util.Slog;
+import android.util.TimeUtils;
import com.android.server.DeviceIdleController;
import static com.android.server.am.ActivityManagerDebugConfig.*;
@@ -268,7 +269,7 @@
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
"Delivering to component " + r.curComponent
+ ": " + r);
- mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName());
+ mService.notifyPackageUse(r.intent.getComponent().getPackageName());
app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo),
r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
@@ -1284,6 +1285,7 @@
final boolean dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, String dumpPackage, boolean needSep) {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
if (mParallelBroadcasts.size() > 0 || mOrderedBroadcasts.size() > 0
|| mPendingBroadcast != null) {
boolean printed = false;
@@ -1301,7 +1303,7 @@
pw.println(" Active broadcasts [" + mQueueName + "]:");
}
pw.println(" Active Broadcast " + mQueueName + " #" + i + ":");
- br.dump(pw, " ");
+ br.dump(pw, " ", sdf);
}
printed = false;
needSep = true;
@@ -1319,7 +1321,7 @@
pw.println(" Active ordered broadcasts [" + mQueueName + "]:");
}
pw.println(" Active Ordered Broadcast " + mQueueName + " #" + i + ":");
- mOrderedBroadcasts.get(i).dump(pw, " ");
+ mOrderedBroadcasts.get(i).dump(pw, " ", sdf);
}
if (dumpPackage == null || (mPendingBroadcast != null
&& dumpPackage.equals(mPendingBroadcast.callerPackage))) {
@@ -1328,7 +1330,7 @@
}
pw.println(" Pending broadcast [" + mQueueName + "]:");
if (mPendingBroadcast != null) {
- mPendingBroadcast.dump(pw, " ");
+ mPendingBroadcast.dump(pw, " ", sdf);
} else {
pw.println(" (null)");
}
@@ -1366,7 +1368,7 @@
if (dumpAll) {
pw.print(" Historical Broadcast " + mQueueName + " #");
pw.print(i); pw.println(":");
- r.dump(pw, " ");
+ r.dump(pw, " ", sdf);
} else {
pw.print(" #"); pw.print(i); pw.print(": "); pw.println(r);
pw.print(" ");
@@ -1400,7 +1402,6 @@
}
// done skipping; dump the remainder of the ring. 'i' is still the ordinal within
// the overall broadcast history.
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
do {
ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_SUMMARY_HISTORY);
Intent intent = mBroadcastSummaryHistory[ringIndex];
@@ -1422,9 +1423,19 @@
i++;
pw.print(" #"); pw.print(i); pw.print(": ");
pw.println(intent.toShortString(false, true, true, false));
- pw.print(" enq="); pw.print(sdf.format(new Date(mSummaryHistoryEnqueueTime[ringIndex])));
- pw.print(" disp="); pw.print(sdf.format(new Date(mSummaryHistoryDispatchTime[ringIndex])));
- pw.print(" fin="); pw.println(sdf.format(new Date(mSummaryHistoryFinishTime[ringIndex])));
+ pw.print(" ");
+ TimeUtils.formatDuration(mSummaryHistoryDispatchTime[ringIndex]
+ - mSummaryHistoryEnqueueTime[ringIndex], pw);
+ pw.print(" dispatch ");
+ TimeUtils.formatDuration(mSummaryHistoryFinishTime[ringIndex]
+ - mSummaryHistoryDispatchTime[ringIndex], pw);
+ pw.println(" finish");
+ pw.print(" enq=");
+ pw.print(sdf.format(new Date(mSummaryHistoryEnqueueTime[ringIndex])));
+ pw.print(" disp=");
+ pw.print(sdf.format(new Date(mSummaryHistoryDispatchTime[ringIndex])));
+ pw.print(" fin=");
+ pw.println(sdf.format(new Date(mSummaryHistoryFinishTime[ringIndex])));
Bundle bundle = intent.getExtras();
if (bundle != null) {
pw.print(" extras: "); pw.println(bundle.toString());
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index 1fbfd9f..b42bcff 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -32,6 +32,7 @@
import android.util.TimeUtils;
import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
@@ -88,7 +89,7 @@
ComponentName curComponent; // the receiver class that is currently running.
ActivityInfo curReceiver; // info about the receiver that is currently running.
- void dump(PrintWriter pw, String prefix) {
+ void dump(PrintWriter pw, String prefix, SimpleDateFormat sdf) {
final long now = SystemClock.uptimeMillis();
pw.print(prefix); pw.print(this); pw.print(" to user "); pw.println(userId);
@@ -114,13 +115,19 @@
pw.print(prefix); pw.print("options="); pw.println(options.toBundle());
}
pw.print(prefix); pw.print("enqueueClockTime=");
- pw.print(new Date(enqueueClockTime));
+ pw.print(sdf.format(new Date(enqueueClockTime)));
pw.print(" dispatchClockTime=");
- pw.println(new Date(dispatchClockTime));
+ pw.println(sdf.format(new Date(dispatchClockTime)));
pw.print(prefix); pw.print("dispatchTime=");
TimeUtils.formatDuration(dispatchTime, now, pw);
+ pw.print(" (");
+ TimeUtils.formatDuration(dispatchClockTime-enqueueClockTime, pw);
+ pw.print(" since enq)");
if (finishTime != 0) {
pw.print(" finishTime="); TimeUtils.formatDuration(finishTime, now, pw);
+ pw.print(" (");
+ TimeUtils.formatDuration(finishTime-dispatchTime, pw);
+ pw.print(" since disp)");
} else {
pw.print(" receiverTime="); TimeUtils.formatDuration(receiverTime, now, pw);
}
diff --git a/services/core/java/com/android/server/am/LaunchingTaskPositioner.java b/services/core/java/com/android/server/am/LaunchingTaskPositioner.java
index 3d45915..4ba1d0d 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 initialLayout Optional information from the client about how it would like to be sized
+ * @param layout Optional information from the client about how it would like to be sized
* and positioned.
*/
void updateDefaultBounds(TaskRecord task, ArrayList<TaskRecord> tasks,
- @Nullable ActivityInfo.InitialLayout initialLayout) {
+ @Nullable ActivityInfo.Layout layout) {
if (!mDefaultStartBoundsConfigurationSet) {
return;
}
- if (initialLayout == null) {
+ if (layout == null) {
positionCenter(task, tasks, mDefaultFreeformWidth, mDefaultFreeformHeight);
return;
}
- int width = getFinalWidth(initialLayout);
- int height = getFinalHeight(initialLayout);
- int verticalGravity = initialLayout.gravity & Gravity.VERTICAL_GRAVITY_MASK;
- int horizontalGravity = initialLayout.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
+ int width = getFinalWidth(layout);
+ int height = getFinalHeight(layout);
+ int verticalGravity = layout.gravity & Gravity.VERTICAL_GRAVITY_MASK;
+ int horizontalGravity = layout.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: " + initialLayout.gravity
+ Slog.w(TAG, "Received unsupported gravity: " + layout.gravity
+ ", positioning in the center instead.");
positionCenter(task, tasks, width, height);
}
}
- private int getFinalWidth(ActivityInfo.InitialLayout initialLayout) {
+ private int getFinalWidth(ActivityInfo.Layout layout) {
int width = mDefaultFreeformWidth;
- if (initialLayout.width > 0) {
- width = initialLayout.width;
+ if (layout.width > 0) {
+ width = layout.width;
}
- if (initialLayout.widthFraction > 0) {
- width = (int) (mAvailableRect.width() * initialLayout.widthFraction);
+ if (layout.widthFraction > 0) {
+ width = (int) (mAvailableRect.width() * layout.widthFraction);
}
return width;
}
- private int getFinalHeight(ActivityInfo.InitialLayout initialLayout) {
+ private int getFinalHeight(ActivityInfo.Layout layout) {
int height = mDefaultFreeformHeight;
- if (initialLayout.height > 0) {
- height = initialLayout.height;
+ if (layout.height > 0) {
+ height = layout.height;
}
- if (initialLayout.heightFraction > 0) {
- height = (int) (mAvailableRect.height() * initialLayout.heightFraction);
+ if (layout.heightFraction > 0) {
+ height = (int) (mAvailableRect.height() * layout.heightFraction);
}
return height;
}
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 5b46799..8039072 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -248,7 +248,7 @@
boolean sendFinish = finishedReceiver != null;
int userId = key.userId;
if (userId == UserHandle.USER_CURRENT) {
- userId = owner.getCurrentUserIdLocked();
+ userId = owner.mUserController.getCurrentOrTargetUserIdLocked();
}
switch (key.type) {
case ActivityManager.INTENT_SENDER_ACTIVITY:
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 0e24952..b49370b 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -49,19 +49,22 @@
// OOM adjustments for processes in various states:
+ // Uninitialized value for any major or minor adj fields
+ static final int INVALID_ADJ = -10000;
+
// Adjustment used in certain places where we don't know it yet.
// (Generally this is something that is going to be cached, but we
// don't know the exact value in the cached range to assign yet.)
- static final int UNKNOWN_ADJ = 16;
+ static final int UNKNOWN_ADJ = 1001;
// This is a process only hosting activities that are not visible,
// so it can be killed without any disruption.
- static final int CACHED_APP_MAX_ADJ = 15;
- static final int CACHED_APP_MIN_ADJ = 9;
+ static final int CACHED_APP_MAX_ADJ = 906;
+ static final int CACHED_APP_MIN_ADJ = 900;
// The B list of SERVICE_ADJ -- these are the old and decrepit
// services that aren't as shiny and interesting as the ones in the A list.
- static final int SERVICE_B_ADJ = 8;
+ static final int SERVICE_B_ADJ = 800;
// This is the process of the previous application that the user was in.
// This process is kept above other things, because it is very common to
@@ -69,34 +72,35 @@
// task switch (toggling between the two top recent apps) as well as normal
// UI flow such as clicking on a URI in the e-mail app to view in the browser,
// and then pressing back to return to e-mail.
- static final int PREVIOUS_APP_ADJ = 7;
+ static final int PREVIOUS_APP_ADJ = 700;
// This is a process holding the home application -- we want to try
// avoiding killing it, even if it would normally be in the background,
// because the user interacts with it so much.
- static final int HOME_APP_ADJ = 6;
+ static final int HOME_APP_ADJ = 600;
// This is a process holding an application service -- killing it will not
// have much of an impact as far as the user is concerned.
- static final int SERVICE_ADJ = 5;
+ static final int SERVICE_ADJ = 500;
// This is a process with a heavy-weight application. It is in the
// background, but we want to try to avoid killing it. Value set in
// system/rootdir/init.rc on startup.
- static final int HEAVY_WEIGHT_APP_ADJ = 4;
+ static final int HEAVY_WEIGHT_APP_ADJ = 400;
// This is a process currently hosting a backup operation. Killing it
// is not entirely fatal but is generally a bad idea.
- static final int BACKUP_APP_ADJ = 3;
+ static final int BACKUP_APP_ADJ = 300;
// This is a process only hosting components that are perceptible to the
// user, and we really want to avoid killing them, but they are not
// immediately visible. An example is background music playback.
- static final int PERCEPTIBLE_APP_ADJ = 2;
+ static final int PERCEPTIBLE_APP_ADJ = 200;
// This is a process only hosting activities that are visible to the
// user, so we'd prefer they don't disappear.
- static final int VISIBLE_APP_ADJ = 1;
+ static final int VISIBLE_APP_ADJ = 100;
+ static final int VISIBLE_APP_LAYER_MAX = PERCEPTIBLE_APP_ADJ - VISIBLE_APP_ADJ - 1;
// This is the process running the current foreground app. We'd really
// rather not kill it!
@@ -104,18 +108,18 @@
// This is a process that the system or a persistent process has bound to,
// and indicated it is important.
- static final int PERSISTENT_SERVICE_ADJ = -11;
+ static final int PERSISTENT_SERVICE_ADJ = -700;
// This is a system persistent process, such as telephony. Definitely
// don't want to kill it, but doing so is not completely fatal.
- static final int PERSISTENT_PROC_ADJ = -12;
+ static final int PERSISTENT_PROC_ADJ = -800;
// The system process runs at the default adjustment.
- static final int SYSTEM_ADJ = -16;
+ static final int SYSTEM_ADJ = -900;
// Special code for native processes that are not being managed by the system (so
// don't have an oom adj assigned by the system).
- static final int NATIVE_ADJ = -17;
+ static final int NATIVE_ADJ = -1000;
// Memory pages are 4K.
static final int PAGE_SIZE = 4*1024;
@@ -159,7 +163,7 @@
// These must be kept in sync with the definitions in lmkd.c
//
// LMK_TARGET <minfree> <minkillprio> ... (up to 6 pairs)
- // LMK_PROCPRIO <pid> <prio>
+ // LMK_PROCPRIO <pid> <uid> <prio>
// LMK_PROCREMOVE <pid>
static final byte LMK_TARGET = 0;
static final byte LMK_PROCPRIO = 1;
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index bd31a21..b77eec8 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -114,6 +114,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
+ 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
IBinder forcingToForeground;// Token that is forcing this process to be foreground
@@ -171,10 +172,10 @@
boolean debugging; // was app launched for debugging?
boolean waitedForDebugger; // has process show wait for debugger dialog?
Dialog waitDialog; // current wait for debugger dialog
-
+
String shortStringName; // caching of toShortString() result.
String stringName; // caching of toString() result.
-
+
// These reports are generated & stored when an app gets into an error condition.
// They will be "null" when all is OK.
ActivityManager.ProcessErrorStateInfo crashingReport;
@@ -297,6 +298,10 @@
if (reportedInteraction || fgInteractionTime != 0) {
pw.print(prefix); pw.print("reportedInteraction=");
pw.print(reportedInteraction);
+ if (interactionEventTime != 0) {
+ pw.print(" time=");
+ TimeUtils.formatDuration(interactionEventTime, SystemClock.elapsedRealtime(), pw);
+ }
if (fgInteractionTime != 0) {
pw.print(" fgInteractionTime=");
TimeUtils.formatDuration(fgInteractionTime, SystemClock.elapsedRealtime(), pw);
@@ -402,7 +407,7 @@
}
}
}
-
+
ProcessRecord(BatteryStatsImpl _batteryStats, ApplicationInfo _info,
String _processName, int _uid) {
mBatteryStats = _batteryStats;
@@ -413,8 +418,8 @@
processName = _processName;
pkgList.put(_info.packageName, new ProcessStats.ProcessStateHolder(_info.versionCode));
maxAdj = ProcessList.UNKNOWN_ADJ;
- curRawAdj = setRawAdj = -100;
- curAdj = setAdj = -100;
+ curRawAdj = setRawAdj = ProcessList.INVALID_ADJ;
+ curAdj = setAdj = ProcessList.INVALID_ADJ;
persistent = false;
removed = false;
lastStateTime = lastPssTime = nextPssTime = SystemClock.uptimeMillis();
@@ -560,7 +565,7 @@
toShortString(sb);
return shortStringName = sb.toString();
}
-
+
void toShortString(StringBuilder sb) {
sb.append(pid);
sb.append(':');
@@ -585,7 +590,7 @@
}
}
}
-
+
public String toString() {
if (stringName != null) {
return stringName;
@@ -695,7 +700,7 @@
pkgList.put(info.packageName, new ProcessStats.ProcessStateHolder(info.versionCode));
}
}
-
+
public String[] getPackageList() {
int size = pkgList.size();
if (size == 0) {
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index edd16ef..c63eaac 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -108,7 +108,7 @@
final IPackageManager pm = AppGlobals.getPackageManager();
final int[] users = (userId == UserHandle.USER_ALL)
- ? mService.getUsersLocked() : new int[] { userId };
+ ? mService.mUserController.getUsers() : new int[] { userId };
for (int userIdx = 0; userIdx < users.length; userIdx++) {
final int user = users[userIdx];
recentsCount = size() - 1;
@@ -462,10 +462,12 @@
final boolean sameActivity = task.realActivity != null
&& tr.realActivity != null
&& task.realActivity.equals(tr.realActivity);
- if (!sameActivity) {
+ // If the document is open in another app or is not the same
+ // document, we don't need to trim it.
+ if (!sameActivity || !sameIntent) {
continue;
- }
- if (maxRecents > 0 && !doTrim) {
+ // Otherwise only trim if we are over our max recents for this task
+ } else if (maxRecents > 0 && !doTrim) {
--maxRecents;
continue;
}
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index 871331b..150baf0 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -16,22 +16,14 @@
package com.android.server.am;
-import android.app.ActivityManager;
-import android.app.AppGlobals;
-import android.content.ComponentName;
import android.content.pm.IPackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Debug;
-import android.os.RemoteException;
import android.os.SystemClock;
-import android.os.UserHandle;
-import android.text.format.DateUtils;
-import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Slog;
-import android.util.SparseArray;
import android.util.Xml;
import android.os.Process;
@@ -51,14 +43,10 @@
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.Comparator;
-import java.util.List;
import libcore.io.IoUtils;
-import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
-
public class TaskPersister {
static final String TAG = "TaskPersister";
static final boolean DEBUG = false;
@@ -104,6 +92,7 @@
private static class WriteQueueItem {}
private static class TaskWriteQueueItem extends WriteQueueItem {
final TaskRecord mTask;
+
TaskWriteQueueItem(TaskRecord task) {
mTask = task;
}
@@ -111,6 +100,7 @@
private static class ImageWriteQueueItem extends WriteQueueItem {
final String mFilename;
Bitmap mImage;
+
ImageWriteQueueItem(String filename, Bitmap image) {
mFilename = filename;
mImage = image;
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 77dbad4..b214080 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -16,10 +16,10 @@
package com.android.server.am;
-import static android.app.ActivityManager.DOCKED_STACK_ID;
-import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.HOME_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.INVALID_STACK_ID;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
@@ -34,8 +34,11 @@
import android.app.Activity;
import android.app.ActivityManager;
+import android.app.ActivityManager.StackId;
import android.app.ActivityManager.TaskThumbnail;
import android.app.ActivityManager.TaskDescription;
+import android.app.ActivityManager.TaskThumbnail;
+import android.app.ActivityManager.TaskThumbnailInfo;
import android.app.ActivityOptions;
import android.app.AppGlobals;
import android.content.ComponentName;
@@ -46,6 +49,7 @@
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
+import android.graphics.Point;
import android.graphics.Rect;
import android.os.Debug;
import android.os.ParcelFileDescriptor;
@@ -193,6 +197,7 @@
private Bitmap mLastThumbnail; // Last thumbnail captured for this item.
private final File mLastThumbnailFile; // File containing last thumbnail.
private final String mFilename;
+ private TaskThumbnailInfo mLastThumbnailInfo;
CharSequence lastDescription; // Last description captured for this item.
int mAffiliatedTaskId; // taskId of parent affiliation or self if no parent.
@@ -217,6 +222,13 @@
// 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;
+
+ // 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.
+ int mLayerRank = -1;
Configuration mOverrideConfig = Configuration.EMPTY;
@@ -226,6 +238,7 @@
mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
TaskPersister.IMAGE_EXTENSION;
mLastThumbnailFile = new File(TaskPersister.sImagesDir, mFilename);
+ mLastThumbnailInfo = new TaskThumbnailInfo();
taskId = _taskId;
mAffiliatedTaskId = _taskId;
voiceSession = _voiceSession;
@@ -235,14 +248,16 @@
mCallingUid = info.applicationInfo.uid;
mCallingPackage = info.packageName;
setIntent(_intent, info);
+ mMinimalSize = info != null && info.layout != null ? info.layout.minimalSize : -1;
}
TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent,
- TaskDescription _taskDescription) {
+ TaskDescription _taskDescription, TaskThumbnailInfo thumbnailInfo) {
mService = service;
mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
TaskPersister.IMAGE_EXTENSION;
mLastThumbnailFile = new File(TaskPersister.sImagesDir, mFilename);
+ mLastThumbnailInfo = thumbnailInfo;
taskId = _taskId;
mAffiliatedTaskId = _taskId;
voiceSession = null;
@@ -263,6 +278,7 @@
mTaskToReturnTo = HOME_ACTIVITY_TYPE;
userId = UserHandle.getUserId(info.applicationInfo.uid);
lastTaskDescription = _taskDescription;
+ mMinimalSize = info != null && info.layout != null ? info.layout.minimalSize : -1;
}
private TaskRecord(ActivityManagerService service, int _taskId, Intent _intent,
@@ -272,12 +288,14 @@
int _effectiveUid, String _lastDescription, ArrayList<ActivityRecord> activities,
long _firstActiveTime, long _lastActiveTime, long lastTimeMoved,
boolean neverRelinquishIdentity, TaskDescription _lastTaskDescription,
- int taskAffiliation, int prevTaskId, int nextTaskId, int taskAffiliationColor,
- int callingUid, String callingPackage, boolean resizeable, boolean privileged) {
+ TaskThumbnailInfo lastThumbnailInfo, int taskAffiliation, int prevTaskId,
+ int nextTaskId, int taskAffiliationColor, int callingUid, String callingPackage,
+ boolean resizeable, boolean privileged) {
mService = service;
mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
TaskPersister.IMAGE_EXTENSION;
mLastThumbnailFile = new File(TaskPersister.sImagesDir, mFilename);
+ mLastThumbnailInfo = lastThumbnailInfo;
taskId = _taskId;
intent = _intent;
affinityIntent = _affinityIntent;
@@ -308,8 +326,10 @@
mNextAffiliateTaskId = nextTaskId;
mCallingUid = callingUid;
mCallingPackage = callingPackage;
- mResizeable = resizeable;
+ mResizeable = resizeable || mService.mForceResizableActivites;
mPrivileged = privileged;
+ ActivityInfo info = (mActivities.size() > 0) ? mActivities.get(0).info : null;
+ mMinimalSize = info != null && info.layout != null ? info.layout.minimalSize : -1;
}
void touchActiveTime() {
@@ -408,7 +428,7 @@
} else {
autoRemoveRecents = false;
}
- mResizeable = info.resizeable;
+ mResizeable = info.resizeable || mService.mForceResizableActivites;
mLockTaskMode = info.lockTaskLaunchMode;
mPrivileged = (info.applicationInfo.privateFlags & PRIVATE_FLAG_PRIVILEGED) != 0;
setLockTaskAuth();
@@ -478,20 +498,40 @@
}
/**
- * Returns true when we have a thumbnail.
- * @return Returns true if there is a thumbnail.
+ * Sets the last thumbnail with the current task bounds and the system orientation.
+ * @return whether the thumbnail was set
*/
- boolean hasThumbnail() {
- return mLastThumbnail != null;
+ boolean setLastThumbnailLocked(Bitmap thumbnail) {
+ final Configuration serviceConfig = mService.mConfiguration;
+ int taskWidth = 0;
+ int taskHeight = 0;
+ if (mBounds != null) {
+ // Non-fullscreen tasks
+ taskWidth = mBounds.width();
+ taskHeight = mBounds.height();
+ } else if (stack != null) {
+ // Fullscreen tasks
+ final Point displaySize = new Point();
+ stack.getDisplaySize(displaySize);
+ taskWidth = displaySize.x;
+ taskHeight = displaySize.y;
+ } else {
+ Slog.e(TAG, "setLastThumbnailLocked() called on Task without stack");
+ }
+ return setLastThumbnailLocked(thumbnail, taskWidth, taskHeight, serviceConfig.orientation);
}
/**
- * Sets the last thumbnail.
+ * Sets the last thumbnail with the current task bounds.
* @return whether the thumbnail was set
*/
- boolean setLastThumbnail(Bitmap thumbnail) {
+ private boolean setLastThumbnailLocked(Bitmap thumbnail, int taskWidth, int taskHeight,
+ int screenOrientation) {
if (mLastThumbnail != thumbnail) {
mLastThumbnail = thumbnail;
+ mLastThumbnailInfo.taskWidth = taskWidth;
+ mLastThumbnailInfo.taskHeight = taskHeight;
+ mLastThumbnailInfo.screenOrientation = screenOrientation;
if (thumbnail == null) {
if (mLastThumbnailFile != null) {
mLastThumbnailFile.delete();
@@ -506,6 +546,7 @@
void getLastThumbnail(TaskThumbnail thumbs) {
thumbs.mainThumbnail = mLastThumbnail;
+ thumbs.thumbnailInfo = mLastThumbnailInfo;
thumbs.thumbnailFileDescriptor = null;
if (mLastThumbnail == null) {
thumbs.mainThumbnail = mService.mTaskPersister.getImageFromWriteQueue(mFilename);
@@ -520,11 +561,18 @@
}
}
+ /**
+ * Removes in-memory thumbnail data when the max number of in-memory task thumbnails is reached.
+ */
void freeLastThumbnail() {
mLastThumbnail = null;
}
+ /**
+ * Removes all associated thumbnail data when a task is removed or pruned from recents.
+ */
void disposeThumbnail() {
+ mLastThumbnailInfo.reset();
mLastThumbnail = null;
lastDescription = null;
}
@@ -569,9 +617,18 @@
return null;
}
+ void setFrontOfTask() {
+ setFrontOfTask(null);
+ }
+
/** Call after activity movement or finish to make sure that frontOfTask is set correctly */
- final void setFrontOfTask() {
- boolean foundFront = false;
+ void setFrontOfTask(ActivityRecord newTop) {
+ // If a top candidate is suggested by the caller, go ahead and use it and mark all others
+ // as not front. This is needed in situations where the current front activity in the
+ // task isn't finished yet and we want to set the front to the activity moved to the front
+ // of the task.
+ boolean foundFront = newTop != null ? true : false;
+
final int numActivities = mActivities.size();
for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
final ActivityRecord r = mActivities.get(activityNdx);
@@ -588,6 +645,9 @@
// activity, make the bottom activity front.
mActivities.get(0).frontOfTask = true;
}
+ if (newTop != null) {
+ newTop.frontOfTask = true;
+ }
}
/**
@@ -602,7 +662,7 @@
mActivities.add(newTop);
updateEffectiveIntent();
- setFrontOfTask();
+ setFrontOfTask(newTop);
}
void addActivityAtBottom(ActivityRecord r) {
@@ -622,6 +682,9 @@
// Only set this based on the first activity
if (mActivities.isEmpty()) {
taskType = r.mActivityType;
+ if (taskType == HOME_ACTIVITY_TYPE && mService.mForceResizableActivites) {
+ mResizeable = r.info.resizeable;
+ }
isPersistable = r.isPersistable();
mCallingUid = r.launchedFromUid;
mCallingPackage = r.launchedFromPackage;
@@ -760,7 +823,7 @@
final ActivityRecord resumedActivity = stack.mResumedActivity;
if (resumedActivity != null && resumedActivity.task == this) {
final Bitmap thumbnail = stack.screenshotActivities(resumedActivity);
- setLastThumbnail(thumbnail);
+ setLastThumbnailLocked(thumbnail);
}
}
final TaskThumbnail taskThumbnail = new TaskThumbnail();
@@ -972,6 +1035,7 @@
if (lastTaskDescription != null) {
lastTaskDescription.saveToXml(out);
}
+ mLastThumbnailInfo.saveToXml(out);
out.attribute(null, ATTR_TASK_AFFILIATION_COLOR, String.valueOf(mAffiliatedTaskColor));
out.attribute(null, ATTR_TASK_AFFILIATION, String.valueOf(mAffiliatedTaskId));
out.attribute(null, ATTR_PREV_AFFILIATION, String.valueOf(mPrevAffiliateTaskId));
@@ -1016,7 +1080,7 @@
throws IOException, XmlPullParserException {
Intent intent = null;
Intent affinityIntent = null;
- ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>();
+ ArrayList<ActivityRecord> activities = new ArrayList<>();
ComponentName realActivity = null;
ComponentName origActivity = null;
String affinity = null;
@@ -1036,6 +1100,7 @@
int taskId = INVALID_TASK_ID;
final int outerDepth = in.getDepth();
TaskDescription taskDescription = new TaskDescription();
+ TaskThumbnailInfo thumbnailInfo = new TaskThumbnailInfo();
int taskAffiliation = INVALID_TASK_ID;
int taskAffiliationColor = 0;
int prevTaskId = INVALID_TASK_ID;
@@ -1084,6 +1149,8 @@
lastTimeOnTop = Long.valueOf(attrValue);
} else if (ATTR_NEVERRELINQUISH.equals(attrName)) {
neverRelinquishIdentity = Boolean.valueOf(attrValue);
+ } else if (attrName.startsWith(TaskThumbnailInfo.ATTR_TASK_THUMBNAILINFO_PREFIX)) {
+ thumbnailInfo.restoreFromXml(attrName, attrValue);
} else if (attrName.startsWith(TaskDescription.ATTR_TASKDESCRIPTION_PREFIX)) {
taskDescription.restoreFromXml(attrName, attrValue);
} else if (ATTR_TASK_AFFILIATION.equals(attrName)) {
@@ -1162,8 +1229,8 @@
affinityIntent, affinity, rootAffinity, realActivity, origActivity, rootHasReset,
autoRemoveRecents, askedCompatMode, taskType, userId, effectiveUid, lastDescription,
activities, firstActiveTime, lastActiveTime, lastTimeOnTop, neverRelinquishIdentity,
- taskDescription, taskAffiliation, prevTaskId, nextTaskId, taskAffiliationColor,
- callingUid, callingPackage, resizeable, privileged);
+ taskDescription, thumbnailInfo, taskAffiliation, prevTaskId, nextTaskId,
+ taskAffiliationColor, callingUid, callingPackage, resizeable, privileged);
task.updateOverrideConfiguration(bounds);
for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
@@ -1186,14 +1253,14 @@
mFullscreen = bounds == null;
if (mFullscreen) {
- if (mBounds != null && stack.mStackId != DOCKED_STACK_ID) {
+ if (mBounds != null && StackId.persistTaskBounds(stack.mStackId)) {
mLastNonFullscreenBounds = mBounds;
}
mBounds = null;
mOverrideConfig = Configuration.EMPTY;
} else {
mBounds = new Rect(bounds);
- if (stack == null || stack.mStackId != DOCKED_STACK_ID) {
+ if (stack == null || StackId.persistTaskBounds(stack.mStackId)) {
mLastNonFullscreenBounds = mBounds;
}
@@ -1215,6 +1282,31 @@
return !mOverrideConfig.equals(oldConfig) ? mOverrideConfig : null;
}
+ /** Updates the task's bounds and override configuration to match what is expected for the
+ * input stack. */
+ void updateOverrideConfigurationForStack(ActivityStack inStack) {
+ if (stack != null && stack == inStack) {
+ return;
+ }
+
+ if (inStack.mStackId == FREEFORM_WORKSPACE_STACK_ID) {
+ if (!mResizeable) {
+ throw new IllegalArgumentException("Can not position non-resizeable task="
+ + this + " in stack=" + inStack);
+ }
+ if (mBounds != null) {
+ return;
+ }
+ if (mLastNonFullscreenBounds != null) {
+ updateOverrideConfiguration(mLastNonFullscreenBounds);
+ } else {
+ inStack.layoutTaskInStack(this, null);
+ }
+ } else {
+ updateOverrideConfiguration(inStack.mBounds);
+ }
+ }
+
/**
* Returns the correct stack to use based on task type and currently set bounds,
* regardless of the focused stack and current stack association of the task.
@@ -1232,11 +1324,12 @@
/** Returns the bounds that should be used to launch this task. */
Rect getLaunchBounds() {
+ final int stackId = stack.mStackId;
if (stack == null
- || stack.mStackId == HOME_STACK_ID
- || stack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID) {
+ || stackId == HOME_STACK_ID
+ || stackId == FULLSCREEN_WORKSPACE_STACK_ID) {
return (mResizeable && stack != null) ? stack.mBounds : null;
- } else if (stack.mStackId == DOCKED_STACK_ID) {
+ } else if (!StackId.persistTaskBounds(stackId)) {
return stack.mBounds;
}
return mLastNonFullscreenBounds;
@@ -1347,6 +1440,8 @@
sb.append(stringName);
sb.append(" U=");
sb.append(userId);
+ sb.append(" StackId=");
+ sb.append(stack != null ? stack.mStackId : INVALID_STACK_ID);
sb.append(" sz=");
sb.append(mActivities.size());
sb.append('}');
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
new file mode 100644
index 0000000..d6fced6
--- /dev/null
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -0,0 +1,1107 @@
+/*
+ * 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.am;
+
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.app.ActivityManager.USER_OP_IS_CURRENT;
+import static android.app.ActivityManager.USER_OP_SUCCESS;
+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;
+import static com.android.server.am.ActivityManagerService.ALLOW_FULL_ONLY;
+import static com.android.server.am.ActivityManagerService.ALLOW_NON_FULL;
+import static com.android.server.am.ActivityManagerService.ALLOW_NON_FULL_IN_PROFILE;
+import static com.android.server.am.ActivityManagerService.MY_PID;
+import static com.android.server.am.ActivityManagerService.REPORT_USER_SWITCH_COMPLETE_MSG;
+import static com.android.server.am.ActivityManagerService.REPORT_USER_SWITCH_MSG;
+import static com.android.server.am.ActivityManagerService.SYSTEM_USER_CURRENT_MSG;
+import static com.android.server.am.ActivityManagerService.SYSTEM_USER_START_MSG;
+import static com.android.server.am.ActivityManagerService.USER_SWITCH_TIMEOUT_MSG;
+
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.app.Dialog;
+import android.app.IStopUserCallback;
+import android.app.IUserSwitchObserver;
+import android.content.Context;
+import android.content.IIntentReceiver;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.os.BatteryStats;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Debug;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IRemoteCallback;
+import android.os.IUserManager;
+import android.os.Process;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.storage.IMountService;
+import android.os.storage.StorageManager;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+
+import com.android.internal.R;
+import com.android.internal.util.ArrayUtils;
+import com.android.server.pm.UserManagerService;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Helper class for {@link ActivityManagerService} responsible for multi-user functionality.
+ */
+final class UserController {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "UserController" : TAG_AM;
+ // Maximum number of users we allow to be running at a time.
+ static final int MAX_RUNNING_USERS = 3;
+
+ // Amount of time we wait for observers to handle a user switch before
+ // giving up on them and unfreezing the screen.
+ static final int USER_SWITCH_TIMEOUT = 2 * 1000;
+
+ private final ActivityManagerService mService;
+ private final Handler mHandler;
+
+ // Holds the current foreground user's id
+ private int mCurrentUserId = UserHandle.USER_SYSTEM;
+ // Holds the target user's id during a user switch
+ private int mTargetUserId = UserHandle.USER_NULL;
+
+ /**
+ * Which users have been started, so are allowed to run code.
+ */
+ private final SparseArray<UserState> mStartedUsers = new SparseArray<>();
+ /**
+ * LRU list of history of current users. Most recently current is at the end.
+ */
+ private final ArrayList<Integer> mUserLru = new ArrayList<>();
+
+ /**
+ * Constant array of the users that are currently started.
+ */
+ private int[] mStartedUserArray = new int[] { 0 };
+
+ // If there are multiple profiles for the current user, their ids are here
+ // Currently only the primary user can have managed profiles
+ private int[] mCurrentProfileIds = new int[] {};
+
+ /**
+ * Mapping from each known user ID to the profile group ID it is associated with.
+ */
+ private final SparseIntArray mUserProfileGroupIdsSelfLocked = new SparseIntArray();
+
+ /**
+ * Registered observers of the user switching mechanics.
+ */
+ private final RemoteCallbackList<IUserSwitchObserver> mUserSwitchObservers
+ = new RemoteCallbackList<>();
+
+ /**
+ * Currently active user switch.
+ */
+ Object mCurUserSwitchCallback;
+
+ private volatile UserManagerService mUserManager;
+
+ UserController(ActivityManagerService service) {
+ mService = service;
+ mHandler = mService.mHandler;
+ // User 0 is the first and only user that runs at boot.
+ final UserState uss = new UserState(UserHandle.SYSTEM);
+ mStartedUsers.put(UserHandle.USER_SYSTEM, uss);
+ updateUserUnlockedState(uss);
+ mUserLru.add(UserHandle.USER_SYSTEM);
+ updateStartedUserArrayLocked();
+ }
+
+ void finishUserSwitch(UserState uss) {
+ synchronized (mService) {
+ finishUserBoot(uss);
+
+ startProfilesLocked();
+
+ int num = mUserLru.size();
+ int i = 0;
+ while (num > MAX_RUNNING_USERS && i < mUserLru.size()) {
+ Integer oldUserId = mUserLru.get(i);
+ UserState oldUss = mStartedUsers.get(oldUserId);
+ if (oldUss == null) {
+ // Shouldn't happen, but be sane if it does.
+ mUserLru.remove(i);
+ num--;
+ continue;
+ }
+ if (oldUss.mState == UserState.STATE_STOPPING
+ || oldUss.mState == UserState.STATE_SHUTDOWN) {
+ // This user is already stopping, doesn't count.
+ num--;
+ i++;
+ continue;
+ }
+ if (oldUserId == UserHandle.USER_SYSTEM || oldUserId == mCurrentUserId) {
+ // Owner/System user and current user can't be stopped. We count it as running
+ // when it is not a pure system user.
+ if (UserInfo.isSystemOnly(oldUserId)) {
+ num--;
+ }
+ i++;
+ continue;
+ }
+ // This is a user to be stopped.
+ stopUserLocked(oldUserId, null);
+ num--;
+ i++;
+ }
+ }
+ }
+
+ void finishUserBoot(UserState uss) {
+ synchronized (mService) {
+ if (uss.mState == UserState.STATE_BOOTING
+ && mStartedUsers.get(uss.mHandle.getIdentifier()) == uss) {
+ uss.mState = UserState.STATE_RUNNING;
+ final int userId = uss.mHandle.getIdentifier();
+ Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT);
+ mService.broadcastIntentLocked(null, null, intent,
+ null, null, 0, null, null,
+ new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
+ AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId);
+ }
+ }
+ }
+
+ int stopUser(final int userId, final IStopUserCallback callback) {
+ if (mService.checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
+ != PackageManager.PERMISSION_GRANTED) {
+ String msg = "Permission Denial: switchUser() from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " requires " + INTERACT_ACROSS_USERS_FULL;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ if (userId < 0 || userId == UserHandle.USER_SYSTEM) {
+ throw new IllegalArgumentException("Can't stop system user " + userId);
+ }
+ mService.enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES,
+ userId);
+ synchronized (mService) {
+ return stopUserLocked(userId, callback);
+ }
+ }
+
+ private int stopUserLocked(final int userId, final IStopUserCallback callback) {
+ if (DEBUG_MU) Slog.i(TAG, "stopUserLocked userId=" + userId);
+ if (mCurrentUserId == userId && mTargetUserId == UserHandle.USER_NULL) {
+ return USER_OP_IS_CURRENT;
+ }
+
+ final UserState uss = mStartedUsers.get(userId);
+ if (uss == null) {
+ // User is not started, nothing to do... but we do need to
+ // callback if requested.
+ if (callback != null) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ callback.userStopped(userId);
+ } catch (RemoteException e) {
+ }
+ }
+ });
+ }
+ return USER_OP_SUCCESS;
+ }
+
+ if (callback != null) {
+ uss.mStopCallbacks.add(callback);
+ }
+
+ if (uss.mState != UserState.STATE_STOPPING
+ && uss.mState != UserState.STATE_SHUTDOWN) {
+ uss.mState = UserState.STATE_STOPPING;
+ updateStartedUserArrayLocked();
+
+ long ident = Binder.clearCallingIdentity();
+ try {
+ // We are going to broadcast ACTION_USER_STOPPING and then
+ // once that is done send a final ACTION_SHUTDOWN and then
+ // stop the user.
+ final Intent stoppingIntent = new Intent(Intent.ACTION_USER_STOPPING);
+ 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.mState != UserState.STATE_STOPPING) {
+ // Whoops, we are being started back up. Abort, abort!
+ return;
+ }
+ uss.mState = 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.
+ mService.broadcastIntentLocked(null, null, stoppingIntent,
+ null, stoppingReceiver, 0, null, null,
+ new String[]{INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE,
+ null, true, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ return USER_OP_SUCCESS;
+ }
+
+ void finishUserStop(UserState uss) {
+ final int userId = uss.mHandle.getIdentifier();
+ boolean stopped;
+ ArrayList<IStopUserCallback> callbacks;
+ synchronized (mService) {
+ callbacks = new ArrayList<>(uss.mStopCallbacks);
+ if (mStartedUsers.get(userId) != uss) {
+ stopped = false;
+ } else if (uss.mState != UserState.STATE_SHUTDOWN) {
+ stopped = false;
+ } else {
+ stopped = true;
+ // User can no longer run.
+ mStartedUsers.remove(userId);
+ mUserLru.remove(Integer.valueOf(userId));
+ updateStartedUserArrayLocked();
+
+ // Clean up all state and processes associated with the user.
+ // Kill all the processes for the user.
+ forceStopUserLocked(userId, "finish user");
+ }
+ }
+
+ for (int i = 0; i < callbacks.size(); i++) {
+ try {
+ if (stopped) callbacks.get(i).userStopped(userId);
+ else callbacks.get(i).userStopAborted(userId);
+ } catch (RemoteException e) {
+ }
+ }
+
+ if (stopped) {
+ mService.mSystemServiceManager.cleanupUser(userId);
+ synchronized (mService) {
+ mService.mStackSupervisor.removeUserLocked(userId);
+ }
+ }
+ }
+
+ private void forceStopUserLocked(int userId, String reason) {
+ mService.forceStopPackageLocked(null, -1, false, false, true, false, false,
+ userId, reason);
+ Intent intent = new Intent(Intent.ACTION_USER_STOPPED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
+ | Intent.FLAG_RECEIVER_FOREGROUND);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ mService.broadcastIntentLocked(null, null, intent,
+ null, null, 0, null, null, null, AppOpsManager.OP_NONE,
+ null, false, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL);
+ }
+
+
+ /**
+ * Stops the guest user if it has gone to the background.
+ */
+ private void stopGuestUserIfBackground() {
+ synchronized (mService) {
+ final int num = mUserLru.size();
+ for (int i = 0; i < num; i++) {
+ Integer oldUserId = mUserLru.get(i);
+ UserState oldUss = mStartedUsers.get(oldUserId);
+ if (oldUserId == UserHandle.USER_SYSTEM || oldUserId == mCurrentUserId
+ || oldUss.mState == UserState.STATE_STOPPING
+ || oldUss.mState == UserState.STATE_SHUTDOWN) {
+ continue;
+ }
+ UserInfo userInfo = getUserInfo(oldUserId);
+ if (userInfo.isGuest()) {
+ // This is a user to be stopped.
+ stopUserLocked(oldUserId, null);
+ break;
+ }
+ }
+ }
+ }
+
+ void startProfilesLocked() {
+ if (DEBUG_MU) Slog.i(TAG, "startProfilesLocked");
+ List<UserInfo> profiles = getUserManager().getProfiles(
+ mCurrentUserId, false /* enabledOnly */);
+ List<UserInfo> profilesToStart = new ArrayList<>(profiles.size());
+ for (UserInfo user : profiles) {
+ if ((user.flags & UserInfo.FLAG_INITIALIZED) == UserInfo.FLAG_INITIALIZED
+ && user.id != mCurrentUserId) {
+ profilesToStart.add(user);
+ }
+ }
+ final int profilesToStartSize = profilesToStart.size();
+ int i = 0;
+ for (; i < profilesToStartSize && i < (MAX_RUNNING_USERS - 1); ++i) {
+ startUser(profilesToStart.get(i).id, /* foreground= */ false);
+ }
+ if (i < profilesToStartSize) {
+ Slog.w(TAG, "More profiles than MAX_RUNNING_USERS");
+ }
+ }
+
+ private UserManagerService getUserManager() {
+ UserManagerService userManager = mUserManager;
+ if (userManager == null) {
+ IBinder b = ServiceManager.getService(Context.USER_SERVICE);
+ userManager = mUserManager = (UserManagerService) IUserManager.Stub.asInterface(b);
+ }
+ return userManager;
+ }
+
+ private void updateUserUnlockedState(UserState uss) {
+ final IMountService mountService = IMountService.Stub
+ .asInterface(ServiceManager.getService(Context.STORAGE_SERVICE));
+ if (mountService != null) {
+ try {
+ uss.unlocked = mountService.isUserKeyUnlocked(uss.mHandle.getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ } else {
+ // System isn't fully booted yet, so guess based on property
+ uss.unlocked = !SystemProperties.getBoolean(StorageManager.PROP_HAS_FBE, false);
+ }
+ }
+
+ boolean startUser(final int userId, final boolean foreground) {
+ if (mService.checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
+ != PackageManager.PERMISSION_GRANTED) {
+ String msg = "Permission Denial: switchUser() from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " requires " + INTERACT_ACROSS_USERS_FULL;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+
+ if (DEBUG_MU) Slog.i(TAG, "starting userid:" + userId + " fore:" + foreground);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mService) {
+ final int oldUserId = mCurrentUserId;
+ if (oldUserId == userId) {
+ return true;
+ }
+
+ mService.mStackSupervisor.setLockTaskModeLocked(null,
+ ActivityManager.LOCK_TASK_MODE_NONE, "startUser", false);
+
+ final UserInfo userInfo = getUserInfo(userId);
+ if (userInfo == null) {
+ Slog.w(TAG, "No user info for user #" + userId);
+ return false;
+ }
+ if (foreground && userInfo.isManagedProfile()) {
+ Slog.w(TAG, "Cannot switch to User #" + userId + ": not a full user");
+ return false;
+ }
+
+ if (foreground) {
+ mService.mWindowManager.startFreezingScreen(
+ R.anim.screen_user_exit, R.anim.screen_user_enter);
+ }
+
+ boolean needStart = false;
+
+ // If the user we are switching to is not currently started, then
+ // we need to start it now.
+ if (mStartedUsers.get(userId) == null) {
+ mStartedUsers.put(userId, new UserState(new UserHandle(userId)));
+ updateStartedUserArrayLocked();
+ needStart = true;
+ }
+
+ final UserState uss = mStartedUsers.get(userId);
+ updateUserUnlockedState(uss);
+
+ final Integer userIdInt = userId;
+ mUserLru.remove(userIdInt);
+ mUserLru.add(userIdInt);
+
+ if (foreground) {
+ mCurrentUserId = userId;
+ mService.updateUserConfigurationLocked();
+ mTargetUserId = UserHandle.USER_NULL; // reset, mCurrentUserId has caught up
+ updateCurrentProfileIdsLocked();
+ mService.mWindowManager.setCurrentUser(userId, mCurrentProfileIds);
+ // Once the internal notion of the active user has switched, we lock the device
+ // with the option to show the user switcher on the keyguard.
+ mService.mWindowManager.lockNow(null);
+ } else {
+ final Integer currentUserIdInt = mCurrentUserId;
+ updateCurrentProfileIdsLocked();
+ mService.mWindowManager.setCurrentProfileIds(mCurrentProfileIds);
+ mUserLru.remove(currentUserIdInt);
+ mUserLru.add(currentUserIdInt);
+ }
+
+ // Make sure user is in the started state. If it is currently
+ // stopping, we need to knock that off.
+ if (uss.mState == UserState.STATE_STOPPING) {
+ // If we are stopping, we haven't sent ACTION_SHUTDOWN,
+ // so we can just fairly silently bring the user back from
+ // the almost-dead.
+ uss.mState = UserState.STATE_RUNNING;
+ updateStartedUserArrayLocked();
+ needStart = true;
+ } else if (uss.mState == UserState.STATE_SHUTDOWN) {
+ // This means ACTION_SHUTDOWN has been sent, so we will
+ // need to treat this as a new boot of the user.
+ uss.mState = UserState.STATE_BOOTING;
+ updateStartedUserArrayLocked();
+ needStart = true;
+ }
+
+ if (uss.mState == UserState.STATE_BOOTING) {
+ // Booting up a new user, need to tell system services about it.
+ // Note that this is on the same handler as scheduling of broadcasts,
+ // which is important because it needs to go first.
+ mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_START_MSG, userId, 0));
+ }
+
+ if (foreground) {
+ mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_CURRENT_MSG, userId,
+ oldUserId));
+ mHandler.removeMessages(REPORT_USER_SWITCH_MSG);
+ mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
+ mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_MSG,
+ oldUserId, userId, uss));
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(USER_SWITCH_TIMEOUT_MSG,
+ oldUserId, userId, uss), USER_SWITCH_TIMEOUT);
+ }
+
+ if (needStart) {
+ // Send USER_STARTED broadcast
+ Intent intent = new Intent(Intent.ACTION_USER_STARTED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
+ | Intent.FLAG_RECEIVER_FOREGROUND);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ mService.broadcastIntentLocked(null, null, intent,
+ null, null, 0, null, null, null, AppOpsManager.OP_NONE,
+ null, false, false, MY_PID, SYSTEM_UID, userId);
+ }
+
+ if ((userInfo.flags&UserInfo.FLAG_INITIALIZED) == 0) {
+ if (userId != UserHandle.USER_SYSTEM) {
+ Intent intent = new Intent(Intent.ACTION_USER_INITIALIZE);
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ mService.broadcastIntentLocked(null, null, intent, null,
+ new IIntentReceiver.Stub() {
+ public void performReceive(Intent intent, int resultCode,
+ String data, Bundle extras, boolean ordered,
+ boolean sticky, int sendingUser) {
+ onUserInitialized(uss, foreground, oldUserId, userId);
+ }
+ }, 0, null, null, null, AppOpsManager.OP_NONE,
+ null, true, false, MY_PID, SYSTEM_UID, userId);
+ uss.initializing = true;
+ } else {
+ getUserManager().makeInitialized(userInfo.id);
+ }
+ }
+
+ if (foreground) {
+ if (!uss.initializing) {
+ moveUserToForegroundLocked(uss, oldUserId, userId);
+ }
+ } else {
+ mService.mStackSupervisor.startBackgroundUserLocked(userId, uss);
+ }
+
+ if (needStart) {
+ Intent intent = new Intent(Intent.ACTION_USER_STARTING);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ 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) throws RemoteException {
+ }
+ }, 0, null, null,
+ new String[] {INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE,
+ null, true, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+
+ return true;
+ }
+
+ /**
+ * Start user, if its not already running, and bring it to foreground.
+ */
+ boolean startUserInForeground(final int userId, Dialog dlg) {
+ boolean result = startUser(userId, /* foreground */ true);
+ dlg.dismiss();
+ return result;
+ }
+
+ void showUserSwitchDialog(int userId, String userName) {
+ // The dialog will show and then initiate the user switch by calling startUserInForeground
+ Dialog d = new UserSwitchingDialog(mService, mService.mContext, userId, userName,
+ true /* above system */);
+ d.show();
+ }
+
+ void dispatchForegroundProfileChanged(int userId) {
+ final int observerCount = mUserSwitchObservers.beginBroadcast();
+ for (int i = 0; i < observerCount; i++) {
+ try {
+ mUserSwitchObservers.getBroadcastItem(i).onForegroundProfileSwitch(userId);
+ } catch (RemoteException e) {
+ // Ignore
+ }
+ }
+ mUserSwitchObservers.finishBroadcast();
+ }
+
+ /** Called on handler thread */
+ void dispatchUserSwitchComplete(int userId) {
+ final int observerCount = mUserSwitchObservers.beginBroadcast();
+ for (int i = 0; i < observerCount; i++) {
+ try {
+ mUserSwitchObservers.getBroadcastItem(i).onUserSwitchComplete(userId);
+ } catch (RemoteException e) {
+ }
+ }
+ mUserSwitchObservers.finishBroadcast();
+ }
+
+ void timeoutUserSwitch(UserState uss, int oldUserId, int newUserId) {
+ synchronized (mService) {
+ Slog.w(TAG, "User switch timeout: from " + oldUserId + " to " + newUserId);
+ sendContinueUserSwitchLocked(uss, oldUserId, newUserId);
+ }
+ }
+
+ void dispatchUserSwitch(final UserState uss, final int oldUserId,
+ final int newUserId) {
+ final int observerCount = mUserSwitchObservers.beginBroadcast();
+ if (observerCount > 0) {
+ final IRemoteCallback callback = new IRemoteCallback.Stub() {
+ int mCount = 0;
+ @Override
+ public void sendResult(Bundle data) throws RemoteException {
+ synchronized (mService) {
+ if (mCurUserSwitchCallback == this) {
+ mCount++;
+ if (mCount == observerCount) {
+ sendContinueUserSwitchLocked(uss, oldUserId, newUserId);
+ }
+ }
+ }
+ }
+ };
+ synchronized (mService) {
+ uss.switching = true;
+ mCurUserSwitchCallback = callback;
+ }
+ for (int i = 0; i < observerCount; i++) {
+ try {
+ mUserSwitchObservers.getBroadcastItem(i).onUserSwitching(
+ newUserId, callback);
+ } catch (RemoteException e) {
+ }
+ }
+ } else {
+ synchronized (mService) {
+ sendContinueUserSwitchLocked(uss, oldUserId, newUserId);
+ }
+ }
+ mUserSwitchObservers.finishBroadcast();
+ }
+
+ void sendContinueUserSwitchLocked(UserState uss, int oldUserId, int newUserId) {
+ mCurUserSwitchCallback = null;
+ mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
+ mHandler.sendMessage(mHandler.obtainMessage(ActivityManagerService.CONTINUE_USER_SWITCH_MSG,
+ oldUserId, newUserId, uss));
+ }
+
+ void continueUserSwitch(UserState uss, int oldUserId, int newUserId) {
+ completeSwitchAndInitialize(uss, newUserId, false, true);
+ }
+
+ void onUserInitialized(UserState uss, boolean foreground, int oldUserId, int newUserId) {
+ synchronized (mService) {
+ if (foreground) {
+ moveUserToForegroundLocked(uss, oldUserId, newUserId);
+ }
+ }
+ completeSwitchAndInitialize(uss, newUserId, true, false);
+ }
+
+ void completeSwitchAndInitialize(UserState uss, int newUserId,
+ boolean clearInitializing, boolean clearSwitching) {
+ boolean unfrozen = false;
+ synchronized (mService) {
+ if (clearInitializing) {
+ uss.initializing = false;
+ getUserManager().makeInitialized(uss.mHandle.getIdentifier());
+ }
+ if (clearSwitching) {
+ uss.switching = false;
+ }
+ if (!uss.switching && !uss.initializing) {
+ mService.mWindowManager.stopFreezingScreen();
+ unfrozen = true;
+ }
+ }
+ if (unfrozen) {
+ mHandler.removeMessages(REPORT_USER_SWITCH_COMPLETE_MSG);
+ mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_COMPLETE_MSG,
+ newUserId, 0));
+ }
+ stopGuestUserIfBackground();
+ }
+
+ void moveUserToForegroundLocked(UserState uss, int oldUserId, int newUserId) {
+ boolean homeInFront = mService.mStackSupervisor.switchUserLocked(newUserId, uss);
+ if (homeInFront) {
+ mService.startHomeActivityLocked(newUserId, "moveUserToForeground");
+ } else {
+ mService.mStackSupervisor.resumeTopActivitiesLocked();
+ }
+ EventLogTags.writeAmSwitchUser(newUserId);
+ getUserManager().onUserForeground(newUserId);
+ sendUserSwitchBroadcastsLocked(oldUserId, newUserId);
+ }
+
+ void sendUserSwitchBroadcastsLocked(int oldUserId, int newUserId) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ Intent intent;
+ if (oldUserId >= 0) {
+ // Send USER_BACKGROUND broadcast to all profiles of the outgoing user
+ List<UserInfo> profiles = getUserManager().getProfiles(oldUserId, false);
+ int count = profiles.size();
+ for (int i = 0; i < count; i++) {
+ int profileUserId = profiles.get(i).id;
+ intent = new Intent(Intent.ACTION_USER_BACKGROUND);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
+ | Intent.FLAG_RECEIVER_FOREGROUND);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, profileUserId);
+ mService.broadcastIntentLocked(null, null, intent,
+ null, null, 0, null, null, null, AppOpsManager.OP_NONE,
+ null, false, false, MY_PID, SYSTEM_UID, profileUserId);
+ }
+ }
+ if (newUserId >= 0) {
+ // Send USER_FOREGROUND broadcast to all profiles of the incoming user
+ List<UserInfo> profiles = getUserManager().getProfiles(newUserId, false);
+ int count = profiles.size();
+ for (int i = 0; i < count; i++) {
+ int profileUserId = profiles.get(i).id;
+ intent = new Intent(Intent.ACTION_USER_FOREGROUND);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
+ | Intent.FLAG_RECEIVER_FOREGROUND);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, profileUserId);
+ mService.broadcastIntentLocked(null, null, intent,
+ null, null, 0, null, null, null, AppOpsManager.OP_NONE,
+ null, false, false, MY_PID, SYSTEM_UID, profileUserId);
+ }
+ intent = new Intent(Intent.ACTION_USER_SWITCHED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
+ | Intent.FLAG_RECEIVER_FOREGROUND);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, newUserId);
+ mService.broadcastIntentLocked(null, null, intent,
+ null, null, 0, null, null,
+ new String[] {android.Manifest.permission.MANAGE_USERS},
+ AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
+ UserHandle.USER_ALL);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+
+ int handleIncomingUser(int callingPid, int callingUid, int userId, boolean allowAll,
+ int allowMode, String name, String callerPackage) {
+ final int callingUserId = UserHandle.getUserId(callingUid);
+ if (callingUserId == userId) {
+ return userId;
+ }
+
+ // Note that we may be accessing mCurrentUserId outside of a lock...
+ // shouldn't be a big deal, if this is being called outside
+ // of a locked context there is intrinsically a race with
+ // the value the caller will receive and someone else changing it.
+ // We assume that USER_CURRENT_OR_SELF will use the current user; later
+ // we will switch to the calling user if access to the current user fails.
+ int targetUserId = unsafeConvertIncomingUserLocked(userId);
+
+ if (callingUid != 0 && callingUid != SYSTEM_UID) {
+ final boolean allow;
+ if (mService.checkComponentPermission(INTERACT_ACROSS_USERS_FULL, callingPid,
+ callingUid, -1, true) == PackageManager.PERMISSION_GRANTED) {
+ // If the caller has this permission, they always pass go. And collect $200.
+ allow = true;
+ } else if (allowMode == ALLOW_FULL_ONLY) {
+ // We require full access, sucks to be you.
+ allow = false;
+ } else if (mService.checkComponentPermission(INTERACT_ACROSS_USERS, callingPid,
+ callingUid, -1, true) != PackageManager.PERMISSION_GRANTED) {
+ // If the caller does not have either permission, they are always doomed.
+ allow = false;
+ } else if (allowMode == ALLOW_NON_FULL) {
+ // We are blanket allowing non-full access, you lucky caller!
+ allow = true;
+ } else if (allowMode == ALLOW_NON_FULL_IN_PROFILE) {
+ // We may or may not allow this depending on whether the two users are
+ // in the same profile.
+ allow = isSameProfileGroup(callingUserId, targetUserId);
+ } else {
+ throw new IllegalArgumentException("Unknown mode: " + allowMode);
+ }
+ if (!allow) {
+ if (userId == UserHandle.USER_CURRENT_OR_SELF) {
+ // In this case, they would like to just execute as their
+ // owner user instead of failing.
+ targetUserId = callingUserId;
+ } else {
+ StringBuilder builder = new StringBuilder(128);
+ builder.append("Permission Denial: ");
+ builder.append(name);
+ if (callerPackage != null) {
+ builder.append(" from ");
+ builder.append(callerPackage);
+ }
+ builder.append(" asks to run as user ");
+ builder.append(userId);
+ builder.append(" but is calling from user ");
+ builder.append(UserHandle.getUserId(callingUid));
+ builder.append("; this requires ");
+ builder.append(INTERACT_ACROSS_USERS_FULL);
+ if (allowMode != ALLOW_FULL_ONLY) {
+ builder.append(" or ");
+ builder.append(INTERACT_ACROSS_USERS);
+ }
+ String msg = builder.toString();
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ }
+ }
+ if (!allowAll && targetUserId < 0) {
+ throw new IllegalArgumentException(
+ "Call does not support special user #" + targetUserId);
+ }
+ // Check shell permission
+ if (callingUid == Process.SHELL_UID && targetUserId >= UserHandle.USER_SYSTEM) {
+ if (hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, targetUserId)) {
+ throw new SecurityException("Shell does not have permission to access user "
+ + targetUserId + "\n " + Debug.getCallers(3));
+ }
+ }
+ return targetUserId;
+ }
+
+ int unsafeConvertIncomingUserLocked(int userId) {
+ return (userId == UserHandle.USER_CURRENT || userId == UserHandle.USER_CURRENT_OR_SELF)
+ ? getCurrentUserIdLocked(): userId;
+ }
+
+ void registerUserSwitchObserver(IUserSwitchObserver observer) {
+ if (mService.checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
+ != PackageManager.PERMISSION_GRANTED) {
+ final String msg = "Permission Denial: registerUserSwitchObserver() from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " requires " + INTERACT_ACROSS_USERS_FULL;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+
+ mUserSwitchObservers.register(observer);
+ }
+
+ void unregisterUserSwitchObserver(IUserSwitchObserver observer) {
+ mUserSwitchObservers.unregister(observer);
+ }
+
+ UserState getStartedUserStateLocked(int userId) {
+ return mStartedUsers.get(userId);
+ }
+
+ boolean hasStartedUserState(int userId) {
+ return mStartedUsers.get(userId) != null;
+ }
+
+ private void updateStartedUserArrayLocked() {
+ int num = 0;
+ for (int i = 0; i < mStartedUsers.size(); i++) {
+ UserState uss = mStartedUsers.valueAt(i);
+ // This list does not include stopping users.
+ if (uss.mState != UserState.STATE_STOPPING
+ && uss.mState != UserState.STATE_SHUTDOWN) {
+ num++;
+ }
+ }
+ mStartedUserArray = new int[num];
+ num = 0;
+ for (int i = 0; i < mStartedUsers.size(); i++) {
+ UserState uss = mStartedUsers.valueAt(i);
+ if (uss.mState != UserState.STATE_STOPPING
+ && uss.mState != UserState.STATE_SHUTDOWN) {
+ mStartedUserArray[num] = mStartedUsers.keyAt(i);
+ num++;
+ }
+ }
+ }
+
+ void sendBootCompletedLocked(IIntentReceiver resultTo) {
+ for (int i = 0; i < mStartedUsers.size(); i++) {
+ UserState uss = mStartedUsers.valueAt(i);
+ if (uss.mState == UserState.STATE_BOOTING) {
+ uss.mState = UserState.STATE_RUNNING;
+ final int userId = mStartedUsers.keyAt(i);
+ Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT);
+ mService.broadcastIntentLocked(null, null, intent, null,
+ resultTo, 0, null, null,
+ new String[] {android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
+ AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId);
+ }
+ }
+ }
+
+ /**
+ * Refreshes the list of users related to the current user when either a
+ * user switch happens or when a new related user is started in the
+ * background.
+ */
+ void updateCurrentProfileIdsLocked() {
+ final List<UserInfo> profiles = getUserManager().getProfiles(mCurrentUserId,
+ false /* enabledOnly */);
+ int[] currentProfileIds = new int[profiles.size()]; // profiles will not be null
+ for (int i = 0; i < currentProfileIds.length; i++) {
+ currentProfileIds[i] = profiles.get(i).id;
+ }
+ mCurrentProfileIds = currentProfileIds;
+
+ synchronized (mUserProfileGroupIdsSelfLocked) {
+ mUserProfileGroupIdsSelfLocked.clear();
+ final List<UserInfo> users = getUserManager().getUsers(false);
+ for (int i = 0; i < users.size(); i++) {
+ UserInfo user = users.get(i);
+ if (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID) {
+ mUserProfileGroupIdsSelfLocked.put(user.id, user.profileGroupId);
+ }
+ }
+ }
+ }
+
+ int[] getStartedUserArrayLocked() {
+ return mStartedUserArray;
+ }
+
+ boolean isUserRunningLocked(int userId, int flags) {
+ UserState state = getStartedUserStateLocked(userId);
+ if (state == null) {
+ return false;
+ }
+ if ((flags & ActivityManager.FLAG_OR_STOPPED) != 0) {
+ return true;
+ }
+ if ((flags & ActivityManager.FLAG_WITH_AMNESIA) != 0) {
+ // If user is currently locked, we fall through to default "running"
+ // behavior below
+ if (state.unlocked) {
+ return false;
+ }
+ }
+ return state.mState != UserState.STATE_STOPPING
+ && state.mState != UserState.STATE_SHUTDOWN;
+ }
+
+ UserInfo getCurrentUser() {
+ if ((mService.checkCallingPermission(INTERACT_ACROSS_USERS)
+ != PackageManager.PERMISSION_GRANTED) && (
+ mService.checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
+ != PackageManager.PERMISSION_GRANTED)) {
+ String msg = "Permission Denial: getCurrentUser() from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " requires " + INTERACT_ACROSS_USERS;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ synchronized (mService) {
+ return getCurrentUserLocked();
+ }
+ }
+
+ UserInfo getCurrentUserLocked() {
+ int userId = mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId;
+ return getUserInfo(userId);
+ }
+
+ int getCurrentOrTargetUserIdLocked() {
+ return mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId;
+ }
+
+ int getCurrentUserIdLocked() {
+ return mCurrentUserId;
+ }
+
+ int setTargetUserIdLocked(int targetUserId) {
+ return mTargetUserId = targetUserId;
+ }
+
+ int[] getUsers() {
+ UserManagerService ums = getUserManager();
+ return ums != null ? ums.getUserIds() : new int[] { 0 };
+ }
+
+ UserInfo getUserInfo(int userId) {
+ return getUserManager().getUserInfo(userId);
+ }
+
+ int[] getUserIds() {
+ return getUserManager().getUserIds();
+ }
+
+ boolean exists(int userId) {
+ return getUserManager().exists(userId);
+ }
+
+ boolean hasUserRestriction(String restriction, int userId) {
+ return getUserManager().hasUserRestriction(restriction, userId);
+ }
+
+ Set<Integer> getProfileIds(int userId) {
+ Set<Integer> userIds = new HashSet<>();
+ final List<UserInfo> profiles = getUserManager().getProfiles(userId,
+ false /* enabledOnly */);
+ for (UserInfo user : profiles) {
+ userIds.add(user.id);
+ }
+ return userIds;
+ }
+
+ boolean isSameProfileGroup(int callingUserId, int targetUserId) {
+ synchronized (mUserProfileGroupIdsSelfLocked) {
+ int callingProfile = mUserProfileGroupIdsSelfLocked.get(callingUserId,
+ UserInfo.NO_PROFILE_GROUP_ID);
+ int targetProfile = mUserProfileGroupIdsSelfLocked.get(targetUserId,
+ UserInfo.NO_PROFILE_GROUP_ID);
+ return callingProfile != UserInfo.NO_PROFILE_GROUP_ID
+ && callingProfile == targetProfile;
+ }
+ }
+
+ boolean isCurrentProfileLocked(int userId) {
+ return ArrayUtils.contains(mCurrentProfileIds, userId);
+ }
+
+ int[] getCurrentProfileIdsLocked() {
+ return mCurrentProfileIds;
+ }
+
+ void dump(PrintWriter pw, boolean dumpAll) {
+ pw.println(" mStartedUsers:");
+ for (int i = 0; i < mStartedUsers.size(); i++) {
+ UserState uss = mStartedUsers.valueAt(i);
+ pw.print(" User #"); pw.print(uss.mHandle.getIdentifier());
+ pw.print(": "); uss.dump("", pw);
+ }
+ pw.print(" mStartedUserArray: [");
+ for (int i = 0; i < mStartedUserArray.length; i++) {
+ if (i > 0) pw.print(", ");
+ pw.print(mStartedUserArray[i]);
+ }
+ pw.println("]");
+ pw.print(" mUserLru: [");
+ for (int i = 0; i < mUserLru.size(); i++) {
+ if (i > 0) pw.print(", ");
+ pw.print(mUserLru.get(i));
+ }
+ pw.println("]");
+ if (dumpAll) {
+ pw.print(" mStartedUserArray: "); pw.println(Arrays.toString(mStartedUserArray));
+ }
+ synchronized (mUserProfileGroupIdsSelfLocked) {
+ if (mUserProfileGroupIdsSelfLocked.size() > 0) {
+ pw.println(" mUserProfileGroupIds:");
+ for (int i=0; i<mUserProfileGroupIdsSelfLocked.size(); i++) {
+ pw.print(" User #");
+ pw.print(mUserProfileGroupIdsSelfLocked.keyAt(i));
+ pw.print(" -> profile #");
+ pw.println(mUserProfileGroupIdsSelfLocked.valueAt(i));
+ }
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/UserState.java b/services/core/java/com/android/server/am/UserState.java
index b3d82bc..b5b5c1d 100644
--- a/services/core/java/com/android/server/am/UserState.java
+++ b/services/core/java/com/android/server/am/UserState.java
@@ -40,6 +40,7 @@
public int mState = STATE_BOOTING;
public boolean switching;
public boolean initializing;
+ public boolean unlocked;
/**
* The last time that a provider was reported to usage stats as being brought to important
@@ -47,7 +48,7 @@
*/
public final ArrayMap<String,Long> mProviderLastReportedFg = new ArrayMap<>();
- public UserState(UserHandle handle, boolean initial) {
+ public UserState(UserHandle handle) {
mHandle = handle;
}
@@ -62,6 +63,11 @@
}
if (switching) pw.print(" SWITCHING");
if (initializing) pw.print(" INITIALIZING");
+ if (unlocked) {
+ pw.print(" UNLOCKED");
+ } else {
+ pw.print(" LOCKED");
+ }
pw.println();
}
}
diff --git a/services/core/java/com/android/server/am/UserSwitchingDialog.java b/services/core/java/com/android/server/am/UserSwitchingDialog.java
index 5a66f4a..28b4096 100644
--- a/services/core/java/com/android/server/am/UserSwitchingDialog.java
+++ b/services/core/java/com/android/server/am/UserSwitchingDialog.java
@@ -97,7 +97,7 @@
void startUser() {
synchronized (this) {
if (!mStartedUser) {
- mService.startUserInForeground(mUserId, this);
+ mService.mUserController.startUserInForeground(mUserId, this);
mStartedUser = true;
final View decorView = getWindow().getDecorView();
if (decorView != null) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index c4b57f1..058d681 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -76,6 +76,7 @@
import android.media.audiopolicy.IAudioPolicyCallback;
import android.os.Binder;
import android.os.Build;
+import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
@@ -88,6 +89,8 @@
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.UserManagerInternal;
+import android.os.UserManagerInternal.UserRestrictionsListener;
import android.os.Vibrator;
import android.provider.Settings;
import android.provider.Settings.System;
@@ -109,6 +112,7 @@
import com.android.internal.util.XmlUtils;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
+import com.android.server.SystemService;
import com.android.server.pm.UserManagerService;
import org.xmlpull.v1.XmlPullParserException;
@@ -279,7 +283,7 @@
0, // STREAM_MUSIC
0, // STREAM_ALARM
0, // STREAM_NOTIFICATION
- 1, // STREAM_BLUETOOTH_SCO
+ 0, // STREAM_BLUETOOTH_SCO
0, // STREAM_SYSTEM_ENFORCED
0, // STREAM_DTMF
0 // STREAM_TTS
@@ -396,6 +400,12 @@
// Broadcast receiver for device connections intent broadcasts
private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver();
+ /** Interface for UserManagerService. */
+ private final UserManagerInternal mUserManagerInternal;
+
+ private final UserRestrictionsListener mUserRestrictionsListener =
+ new AudioServiceUserRestrictionsListener();
+
// Devices currently connected
// Use makeDeviceListKey() to make a unique key for this list.
private class DeviceListSpec {
@@ -564,6 +574,27 @@
return "card=" + card + ";device=" + device + ";";
}
+ public static final class Lifecycle extends SystemService {
+ private AudioService mService;
+
+ public Lifecycle(Context context) {
+ super(context);
+ mService = new AudioService(context);
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.AUDIO_SERVICE, mService);
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+ mService.systemReady();
+ }
+ }
+ }
+
///////////////////////////////////////////////////////////////////////////
// Construction
///////////////////////////////////////////////////////////////////////////
@@ -576,6 +607,8 @@
mPlatformType = AudioSystem.getPlatformType(context);
+ mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
+
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mAudioEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleAudioEvent");
@@ -672,6 +705,8 @@
context.registerReceiverAsUser(mReceiver, UserHandle.ALL, intentFilter, null, null);
LocalServices.addService(AudioManagerInternal.class, new AudioServiceInternal());
+
+ mUserManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener);
}
public void systemReady() {
@@ -781,7 +816,8 @@
int numStreamTypes = AudioSystem.getNumStreamTypes();
for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
VolumeStreamState streamState = mStreamStates[streamType];
- AudioSystem.initStreamVolume(streamType, 0, (streamState.mIndexMax + 5) / 10);
+ AudioSystem.initStreamVolume(
+ streamType, streamState.mIndexMin / 10, streamState.mIndexMax / 10);
streamState.applyAllVolumes();
}
@@ -1016,17 +1052,32 @@
System.MUTE_STREAMS_AFFECTED, AudioSystem.DEFAULT_MUTE_STREAMS_AFFECTED,
UserHandle.USER_CURRENT);
- boolean masterMute = System.getIntForUser(cr, System.VOLUME_MASTER_MUTE,
- 0, UserHandle.USER_CURRENT) == 1;
+ final int currentUser = getCurrentUserId();
+
+ // In addition to checking the system setting, also check the current user restriction.
+ // Because of the delay before persisting VOLUME_MASTER_MUTE, there's a window where
+ // DISALLOW_ADJUST_VOLUME will be ignored when it's set right before switching users.
+ boolean masterMute = (System.getIntForUser(cr, System.VOLUME_MASTER_MUTE,
+ 0, UserHandle.USER_CURRENT) == 1)
+ || mUserManagerInternal.getUserRestriction(
+ currentUser, UserManager.DISALLOW_ADJUST_VOLUME);
if (mUseFixedVolume) {
masterMute = false;
AudioSystem.setMasterVolume(1.0f);
}
+ if (DEBUG_VOL) {
+ Log.d(TAG, String.format("Master mute %s, user=%d", masterMute, currentUser));
+ }
AudioSystem.setMasterMute(masterMute);
broadcastMasterMuteStatus(masterMute);
boolean microphoneMute =
- System.getIntForUser(cr, System.MICROPHONE_MUTE, 0, UserHandle.USER_CURRENT) == 1;
+ (System.getIntForUser(cr, System.MICROPHONE_MUTE, 0, UserHandle.USER_CURRENT) == 1)
+ || mUserManagerInternal.getUserRestriction(
+ currentUser, UserManager.DISALLOW_UNMUTE_MICROPHONE);
+ if (DEBUG_VOL) {
+ Log.d(TAG, String.format("Mic mute %s, user=%d", microphoneMute, currentUser));
+ }
AudioSystem.muteMicrophone(microphoneMute);
// Each stream will read its own persisted settings
@@ -1733,7 +1784,8 @@
if (uid == android.os.Process.SYSTEM_UID) {
uid = UserHandle.getUid(userId, UserHandle.getAppId(uid));
}
- if (mAppOps.noteOp(AppOpsManager.OP_AUDIO_MASTER_VOLUME, uid, callingPackage)
+ // If OP_AUDIO_MASTER_VOLUME is set, disallow unmuting.
+ if (!mute && mAppOps.noteOp(AppOpsManager.OP_AUDIO_MASTER_VOLUME, uid, callingPackage)
!= AppOpsManager.MODE_ALLOWED) {
return;
}
@@ -1743,6 +1795,16 @@
!= PackageManager.PERMISSION_GRANTED) {
return;
}
+ setMasterMuteInternalNoCallerCheck(mute, flags, userId);
+ }
+
+ private void setMasterMuteInternalNoCallerCheck(boolean mute, int flags, int userId) {
+ if (DEBUG_VOL) {
+ Log.d(TAG, String.format("Master mute %s, %d, user=%d", mute, flags, userId));
+ }
+ if (mUseFixedVolume) {
+ return; // If using fixed volume, we don't mute.
+ }
if (getCurrentUserId() == userId) {
if (mute != AudioSystem.getMasterMute()) {
setSystemAudioMute(mute);
@@ -1817,7 +1879,8 @@
return mStreamVolumeAlias[AudioSystem.STREAM_SYSTEM];
}
- /** @see AudioManager#setMicrophoneMute(boolean, int) */
+ /** @see AudioManager#setMicrophoneMute(boolean) */
+ @Override
public void setMicrophoneMute(boolean on, String callingPackage, int userId) {
// If we are being called by the system check for user we are going to change
// so we handle user restrictions correctly.
@@ -1825,7 +1888,8 @@
if (uid == android.os.Process.SYSTEM_UID) {
uid = UserHandle.getUid(userId, UserHandle.getAppId(uid));
}
- if (mAppOps.noteOp(AppOpsManager.OP_MUTE_MICROPHONE, uid, callingPackage)
+ // If OP_MUTE_MICROPHONE is set, disallow unmuting.
+ if (!on && mAppOps.noteOp(AppOpsManager.OP_MUTE_MICROPHONE, uid, callingPackage)
!= AppOpsManager.MODE_ALLOWED) {
return;
}
@@ -1838,7 +1902,13 @@
!= PackageManager.PERMISSION_GRANTED) {
return;
}
+ setMicrophoneMuteNoCallerCheck(on, userId);
+ }
+ private void setMicrophoneMuteNoCallerCheck(boolean on, int userId) {
+ if (DEBUG_VOL) {
+ Log.d(TAG, String.format("Mic mute %s, user=%d", on, userId));
+ }
// If mute is for current user actually mute, else just persist the setting
// which will be loaded on user switch.
if (getCurrentUserId() == userId) {
@@ -5091,6 +5161,35 @@
}
} // end class AudioServiceBroadcastReceiver
+ private class AudioServiceUserRestrictionsListener implements UserRestrictionsListener {
+
+ @Override
+ public void onUserRestrictionsChanged(int userId, Bundle newRestrictions,
+ Bundle prevRestrictions) {
+ // Update mic mute state.
+ {
+ final boolean wasRestricted =
+ prevRestrictions.getBoolean(UserManager.DISALLOW_UNMUTE_MICROPHONE);
+ final boolean isRestricted =
+ newRestrictions.getBoolean(UserManager.DISALLOW_UNMUTE_MICROPHONE);
+ if (wasRestricted != isRestricted) {
+ setMicrophoneMuteNoCallerCheck(isRestricted, userId);
+ }
+ }
+
+ // Update speaker mute state.
+ {
+ final boolean wasRestricted =
+ prevRestrictions.getBoolean(UserManager.DISALLOW_ADJUST_VOLUME);
+ final boolean isRestricted =
+ newRestrictions.getBoolean(UserManager.DISALLOW_ADJUST_VOLUME);
+ if (wasRestricted != isRestricted) {
+ setMasterMuteInternalNoCallerCheck(isRestricted, /* flags =*/ 0, userId);
+ }
+ }
+ }
+ } // end class AudioServiceUserRestrictionsListener
+
private void killBackgroundUserProcessesWithRecordAudioPermission(UserInfo oldUser) {
PackageManager pm = mContext.getPackageManager();
// Find the home activity of the user. It should not be killed to avoid expensive restart,
diff --git a/services/core/java/com/android/server/content/AppIdleMonitor.java b/services/core/java/com/android/server/content/AppIdleMonitor.java
index fe5c2da..2d768d8 100644
--- a/services/core/java/com/android/server/content/AppIdleMonitor.java
+++ b/services/core/java/com/android/server/content/AppIdleMonitor.java
@@ -50,8 +50,8 @@
}
}
- boolean isAppIdle(String packageName, int userId) {
- return !mAppIdleParoleOn && mUsageStats.isAppIdle(packageName, userId);
+ boolean isAppIdle(String packageName, int uidForAppId, int userId) {
+ return !mAppIdleParoleOn && mUsageStats.isAppIdle(packageName, uidForAppId, userId);
}
@Override
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index e6dc895..82e0eaf 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -104,6 +104,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Random;
import java.util.Set;
@@ -258,7 +259,8 @@
private BroadcastReceiver mDeviceIdleReceiver = new BroadcastReceiver() {
@Override public void onReceive(Context context, Intent intent) {
- boolean idle = mPowerManager.isDeviceIdleMode();
+ boolean idle = mPowerManager.isDeviceIdleMode()
+ || mPowerManager.isLightDeviceIdleMode();
mDeviceIsIdle = idle;
if (idle) {
cancelActiveSync(
@@ -478,6 +480,7 @@
context.registerReceiver(mStorageIntentReceiver, intentFilter);
intentFilter = new IntentFilter(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
+ intentFilter.addAction(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED);
context.registerReceiver(mDeviceIdleReceiver, intentFilter);
intentFilter = new IntentFilter(Intent.ACTION_SHUTDOWN);
@@ -2622,9 +2625,18 @@
continue;
}
String packageName = getPackageName(op.target);
+ ApplicationInfo ai = null;
+ if (packageName != null) {
+ try {
+ ai = mContext.getPackageManager().getApplicationInfo(packageName,
+ PackageManager.GET_UNINSTALLED_PACKAGES
+ | PackageManager.GET_DISABLED_COMPONENTS);
+ } catch (NameNotFoundException e) {
+ }
+ }
// If app is considered idle, then skip for now and backoff
- if (packageName != null
- && mAppIdleMonitor.isAppIdle(packageName, op.target.userId)) {
+ if (ai != null
+ && mAppIdleMonitor.isAppIdle(packageName, ai.uid, op.target.userId)) {
increaseBackoffSetting(op);
op.appIdle = true;
if (isLoggable) {
@@ -3370,7 +3382,7 @@
if (!smaller.containsKey(key)) {
return false;
}
- if (!bigger.get(key).equals(smaller.get(key))) {
+ if (!Objects.equals(bigger.get(key), smaller.get(key))) {
return false;
}
}
@@ -3378,7 +3390,6 @@
}
/**
- * TODO: Get rid of this when we separate sync settings extras from dev specified extras.
* @return true if the provided key is used by the SyncManager in scheduling the sync.
*/
private static boolean isSyncSetting(String key) {
diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index bb4dbc3..835ba17 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -74,6 +74,7 @@
// Set to true when the animation context has been fully prepared.
private boolean mPrepared;
+ private boolean mCreatedResources;
private int mMode;
private final DisplayManagerInternal mDisplayManagerInternal;
@@ -169,6 +170,7 @@
}
// Done.
+ mCreatedResources = true;
mPrepared = true;
// Dejanking optimization.
@@ -313,6 +315,34 @@
}
/**
+ * Dismisses the color fade animation resources.
+ *
+ * This function destroys the resources that are created for the color fade
+ * animation but does not clean up the surface.
+ */
+ public void dismissResources() {
+ if (DEBUG) {
+ Slog.d(TAG, "dismissResources");
+ }
+
+ if (mCreatedResources) {
+ attachEglContext();
+ try {
+ destroyScreenshotTexture();
+ destroyGLShaders();
+ destroyGLBuffers();
+ destroyEglSurface();
+ } finally {
+ detachEglContext();
+ }
+ // This is being called with no active context so shouldn't be
+ // needed but is safer to not change for now.
+ GLES20.glFlush();
+ mCreatedResources = false;
+ }
+ }
+
+ /**
* Dismisses the color fade animation surface and cleans up.
*
* To prevent stray photons from leaking out after the color fade has been
@@ -325,17 +355,8 @@
}
if (mPrepared) {
- attachEglContext();
- try {
- destroyScreenshotTexture();
- destroyGLShaders();
- destroyGLBuffers();
- destroyEglSurface();
- } finally {
- detachEglContext();
- }
+ dismissResources();
destroySurface();
- GLES20.glFlush();
mPrepared = false;
}
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 452378f..7b49530 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -837,6 +837,7 @@
if (mPendingScreenOff && target != Display.STATE_OFF) {
setScreenState(Display.STATE_OFF);
mPendingScreenOff = false;
+ mPowerState.dismissColorFadeResources();
}
if (target == Display.STATE_ON) {
@@ -910,6 +911,7 @@
// A black surface is already hiding the contents of the screen.
setScreenState(Display.STATE_OFF);
mPendingScreenOff = false;
+ mPowerState.dismissColorFadeResources();
} else if (performScreenOffTransition
&& mPowerState.prepareColorFade(mContext,
mColorFadeFadesConfig ?
diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java
index 2eabd32..9862516 100644
--- a/services/core/java/com/android/server/display/DisplayPowerState.java
+++ b/services/core/java/com/android/server/display/DisplayPowerState.java
@@ -187,7 +187,7 @@
}
/**
- * Dismisses the electron beam surface.
+ * Dismisses the color fade surface.
*/
public void dismissColorFade() {
mColorFade.dismiss();
@@ -195,6 +195,13 @@
mColorFadeReady = true;
}
+ /**
+ * Dismisses the color fade resources.
+ */
+ public void dismissColorFadeResources() {
+ mColorFade.dismissResources();
+ }
+
/**
* Sets the level of the electron beam steering current.
*
diff --git a/services/core/java/com/android/server/hdmi/DevicePowerStatusAction.java b/services/core/java/com/android/server/hdmi/DevicePowerStatusAction.java
index 3dd1522..b52ab76 100644
--- a/services/core/java/com/android/server/hdmi/DevicePowerStatusAction.java
+++ b/services/core/java/com/android/server/hdmi/DevicePowerStatusAction.java
@@ -23,6 +23,9 @@
import android.os.RemoteException;
import android.util.Slog;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Feature action that queries the power status of other device. This action is initiated via
* {@link HdmiPlaybackClient#queryDisplayStatus(DisplayStatusCallback)} from the Android system
@@ -37,7 +40,7 @@
private static final int STATE_WAITING_FOR_REPORT_POWER_STATUS = 1;
private final int mTargetAddress;
- private final IHdmiControlCallback mCallback;
+ private final List<IHdmiControlCallback> mCallbacks = new ArrayList<>();
static DevicePowerStatusAction create(HdmiCecLocalDevice source,
int targetAddress, IHdmiControlCallback callback) {
@@ -52,7 +55,7 @@
int targetAddress, IHdmiControlCallback callback) {
super(localDevice);
mTargetAddress = targetAddress;
- mCallback = callback;
+ addCallback(callback);
}
@Override
@@ -95,9 +98,15 @@
}
}
+ public void addCallback(IHdmiControlCallback callback) {
+ mCallbacks.add(callback);
+ }
+
private void invokeCallback(int result) {
try {
- mCallback.onComplete(result);
+ for (IHdmiControlCallback callback : mCallbacks) {
+ callback.onComplete(result);
+ }
} catch (RemoteException e) {
Slog.e(TAG, "Callback failed:" + e);
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 3c35f5e..39c6732 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -35,6 +35,8 @@
import java.util.List;
import java.util.Locale;
+import java.util.List;
+
/**
* Represent a logical device of type Playback residing in Android system.
*/
@@ -97,14 +99,12 @@
@ServiceThreadOnly
void oneTouchPlay(IHdmiControlCallback callback) {
assertRunOnServiceThread();
- if (hasAction(OneTouchPlayAction.class)) {
- Slog.w(TAG, "oneTouchPlay already in progress");
- invokeCallback(callback, HdmiControlManager.RESULT_ALREADY_IN_PROGRESS);
+ List<OneTouchPlayAction> actions = getActions(OneTouchPlayAction.class);
+ if (!actions.isEmpty()) {
+ Slog.i(TAG, "oneTouchPlay already in progress");
+ actions.get(0).addCallback(callback);
return;
}
-
- // TODO: Consider the case of multiple TV sets. For now we always direct the command
- // to the primary one.
OneTouchPlayAction action = OneTouchPlayAction.create(this, Constants.ADDR_TV,
callback);
if (action == null) {
@@ -118,13 +118,14 @@
@ServiceThreadOnly
void queryDisplayStatus(IHdmiControlCallback callback) {
assertRunOnServiceThread();
- if (hasAction(DevicePowerStatusAction.class)) {
- Slog.w(TAG, "queryDisplayStatus already in progress");
- invokeCallback(callback, HdmiControlManager.RESULT_ALREADY_IN_PROGRESS);
+ List<DevicePowerStatusAction> actions = getActions(DevicePowerStatusAction.class);
+ if (!actions.isEmpty()) {
+ Slog.i(TAG, "queryDisplayStatus already in progress");
+ actions.get(0).addCallback(callback);
return;
}
- DevicePowerStatusAction action = DevicePowerStatusAction.create(this,
- Constants.ADDR_TV, callback);
+ DevicePowerStatusAction action = DevicePowerStatusAction.create(this, Constants.ADDR_TV,
+ callback);
if (action == null) {
Slog.w(TAG, "Cannot initiate queryDisplayStatus");
invokeCallback(callback, HdmiControlManager.RESULT_EXCEPTION);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 37afd08..cd8484f 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -138,6 +138,7 @@
@Override
public void onInputAdded(String inputId) {
TvInputInfo tvInfo = mService.getTvInputManager().getTvInputInfo(inputId);
+ if (tvInfo == null) return;
HdmiDeviceInfo info = tvInfo.getHdmiDeviceInfo();
if (info == null) return;
addTvInput(inputId, info.getId());
@@ -731,6 +732,14 @@
@ServiceThreadOnly
protected boolean handleTextViewOn(HdmiCecMessage message) {
assertRunOnServiceThread();
+
+ // Note that <Text View On> (and <Image View On>) command won't be handled here in
+ // most cases. A dedicated microcontroller should be in charge while Android system
+ // is in sleep mode, and the command need not be passed up to this service.
+ // The only situation where the command reaches this handler is that sleep mode is
+ // implemented in such a way that Android system is not really put to standby mode
+ // but only the display is set to blank. Then the command leads to the effect of
+ // turning on the display by the invocation of PowerManager.wakeUp().
if (mService.isPowerStandbyOrTransient() && mAutoWakeup) {
mService.wakeUp();
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
index fe50666..8c00be5 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
@@ -140,7 +140,7 @@
// Allow unregistered source for all vendor specific commands, because we don't know
// how to use the commands at this moment.
addValidationInfo(Constants.MESSAGE_VENDOR_COMMAND,
- maxLengthValidator, DEST_DIRECT | SRC_UNREGISTERED);
+ new VariableLengthValidator(1, 14), DEST_DIRECT | SRC_UNREGISTERED);
addValidationInfo(Constants.MESSAGE_VENDOR_COMMAND_WITH_ID,
new VariableLengthValidator(4, 14), DEST_ALL | SRC_UNREGISTERED);
addValidationInfo(Constants.MESSAGE_VENDOR_REMOTE_BUTTON_DOWN,
diff --git a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
index a711102..5c66316 100644
--- a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
+++ b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
@@ -21,6 +21,9 @@
import android.os.RemoteException;
import android.util.Slog;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Feature action that performs one touch play against TV/Display device. This action is initiated
* via {@link android.hardware.hdmi.HdmiPlaybackClient#oneTouchPlay(OneTouchPlayCallback)} from the
@@ -47,7 +50,7 @@
private static final int LOOP_COUNTER_MAX = 10;
private final int mTargetAddress;
- private final IHdmiControlCallback mCallback;
+ private final List<IHdmiControlCallback> mCallbacks = new ArrayList<>();
private int mPowerStatusCounter = 0;
@@ -66,7 +69,7 @@
IHdmiControlCallback callback) {
super(localDevice);
mTargetAddress = targetAddress;
- mCallback = callback;
+ addCallback(callback);
}
@Override
@@ -125,9 +128,15 @@
}
}
+ public void addCallback(IHdmiControlCallback callback) {
+ mCallbacks.add(callback);
+ }
+
private void invokeCallback(int result) {
try {
- mCallback.onComplete(result);
+ for (IHdmiControlCallback callback : mCallbacks) {
+ callback.onComplete(result);
+ }
} catch (RemoteException e) {
Slog.e(TAG, "Callback failed:" + e);
}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 5a13672..81ae8ac 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -206,6 +206,7 @@
private static native void nativeReloadDeviceAliases(long ptr);
private static native String nativeDump(long ptr);
private static native void nativeMonitor(long ptr);
+ private static native void nativeSetPointerIconShape(long ptr, int iconId);
// Input event injection constants defined in InputDispatcher.h.
private static final int INPUT_EVENT_INJECTION_SUCCEEDED = 0;
@@ -792,8 +793,17 @@
}
@Override // Binder call
+ public int isInTabletMode() {
+ if (!checkCallingPermission(android.Manifest.permission.TABLET_MODE,
+ "isInTabletMode()")) {
+ throw new SecurityException("Requires TABLET_MODE permission");
+ }
+ return getSwitchState(-1, InputDevice.SOURCE_ANY, SW_TABLET_MODE);
+ }
+
+ @Override // Binder call
public void registerTabletModeChangedListener(ITabletModeChangedListener listener) {
- if (!checkCallingPermission(android.Manifest.permission.TABLET_MODE_LISTENER,
+ if (!checkCallingPermission(android.Manifest.permission.TABLET_MODE,
"registerTabletModeChangedListener()")) {
throw new SecurityException("Requires TABLET_MODE_LISTENER permission");
}
@@ -1407,6 +1417,12 @@
}
}
+ // Binder call
+ @Override
+ public void setPointerIconShape(int iconId) {
+ nativeSetPointerIconShape(mPtr, iconId);
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
@@ -1488,7 +1504,7 @@
switchMask);
}
- if ((switchMask & SW_TABLET_MODE) != 0) {
+ if ((switchMask & SW_TABLET_MODE_BIT) != 0) {
SomeArgs args = SomeArgs.obtain();
args.argi1 = (int) (whenNanos & 0xFFFFFFFF);
args.argi2 = (int) (whenNanos >> 32);
diff --git a/services/core/java/com/android/server/input/InputWindowHandle.java b/services/core/java/com/android/server/input/InputWindowHandle.java
index 9149fcc..207c05d 100644
--- a/services/core/java/com/android/server/input/InputWindowHandle.java
+++ b/services/core/java/com/android/server/input/InputWindowHandle.java
@@ -100,6 +100,17 @@
}
@Override
+ public String toString() {
+ return new StringBuilder(name)
+ .append(", layer=").append(layer)
+ .append(", frame=[").append(frameLeft).append(",").append(frameTop).append(",")
+ .append(frameRight).append(",").append(frameBottom).append("]")
+ .append(", touchableRegion=").append(touchableRegion)
+ .toString();
+
+ }
+
+ @Override
protected void finalize() throws Throwable {
try {
nativeDispose();
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 06bd583..2b535b9 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -73,7 +73,7 @@
*/
public class JobSchedulerService extends com.android.server.SystemService
implements StateChangedListener, JobCompletedListener {
- static final boolean DEBUG = false;
+ public static final boolean DEBUG = false;
/** The number of concurrent jobs we run at one time. */
private static final int MAX_JOB_CONTEXTS_COUNT
= ActivityManager.isLowRamDeviceStatic() ? 1 : 3;
@@ -99,7 +99,7 @@
* Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule
* things early.
*/
- static final int MIN_CONNECTIVITY_COUNT = 2;
+ static final int MIN_CONNECTIVITY_COUNT = 1; // Run connectivity jobs as soon as ready.
/**
* Minimum # of jobs (with no particular constraints) for which the JMS will be happy running
* some work early.
@@ -162,8 +162,12 @@
Slog.d(TAG, "Removing jobs for user: " + userId);
}
cancelJobsForUser(userId);
- } else if (PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(intent.getAction())) {
- updateIdleMode(mPowerManager != null ? mPowerManager.isDeviceIdleMode() : false);
+ } else if (PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED.equals(intent.getAction())
+ || PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(intent.getAction())) {
+ updateIdleMode(mPowerManager != null
+ ? (mPowerManager.isDeviceIdleMode()
+ || mPowerManager.isLightDeviceIdleMode())
+ : false);
}
}
};
@@ -340,6 +344,7 @@
mBroadcastReceiver, UserHandle.ALL, filter, null, null);
final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
userFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
+ userFilter.addAction(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED);
getContext().registerReceiverAsUser(
mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
mPowerManager = (PowerManager)getContext().getSystemService(Context.POWER_SERVICE);
diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java
index d7fafe3..5376043 100644
--- a/services/core/java/com/android/server/job/JobServiceContext.java
+++ b/services/core/java/com/android/server/job/JobServiceContext.java
@@ -62,7 +62,7 @@
*
*/
public class JobServiceContext extends IJobCallback.Stub implements ServiceConnection {
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = JobSchedulerService.DEBUG;
private static final String TAG = "JobServiceContext";
/** Define the maximum # of jobs allowed to run on a service at once. */
private static final int defaultMaxActiveJobsPerService =
@@ -109,7 +109,13 @@
int mVerb;
private AtomicBoolean mCancelled = new AtomicBoolean();
- /** All the information maintained about the job currently being executed. */
+ /**
+ * All the information maintained about the job currently being executed.
+ *
+ * Any reads (dereferences) not done from the handler thread must be synchronized on
+ * {@link #mLock}.
+ * Writes can only be done from the handler thread, or {@link #executeRunnableJob(JobStatus)}.
+ */
private JobStatus mRunningJob;
/** Binder to the client service. */
IJobService service;
@@ -194,7 +200,8 @@
*/
JobStatus getRunningJob() {
synchronized (mLock) {
- return mRunningJob;
+ return mRunningJob == null ?
+ null : new JobStatus(mRunningJob);
}
}
@@ -255,15 +262,22 @@
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
- if (!name.equals(mRunningJob.getServiceComponent())) {
+ JobStatus runningJob;
+ synchronized (mLock) {
+ // This isn't strictly necessary b/c the JobServiceHandler is running on the main
+ // looper and at this point we can't get any binder callbacks from the client. Better
+ // safe than sorry.
+ runningJob = mRunningJob;
+ }
+ if (runningJob == null || !name.equals(runningJob.getServiceComponent())) {
mCallbackHandler.obtainMessage(MSG_SHUTDOWN_EXECUTION).sendToTarget();
return;
}
this.service = IJobService.Stub.asInterface(service);
final PowerManager pm =
(PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, mRunningJob.getTag());
- mWakeLock.setWorkSource(new WorkSource(mRunningJob.getUid()));
+ mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, runningJob.getTag());
+ mWakeLock.setWorkSource(new WorkSource(runningJob.getUid()));
mWakeLock.setReferenceCounted(false);
mWakeLock.acquire();
mCallbackHandler.obtainMessage(MSG_SERVICE_BOUND).sendToTarget();
@@ -281,13 +295,15 @@
* @return True if the binder calling is coming from the client we expect.
*/
private boolean verifyCallingUid() {
- if (mRunningJob == null || Binder.getCallingUid() != mRunningJob.getUid()) {
- if (DEBUG) {
- Slog.d(TAG, "Stale callback received, ignoring.");
+ synchronized (mLock) {
+ if (mRunningJob == null || Binder.getCallingUid() != mRunningJob.getUid()) {
+ if (DEBUG) {
+ Slog.d(TAG, "Stale callback received, ignoring.");
+ }
+ return false;
}
- return false;
+ return true;
}
- return true;
}
/**
diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java
index 11e7605..0004c42 100644
--- a/services/core/java/com/android/server/job/JobStore.java
+++ b/services/core/java/com/android/server/job/JobStore.java
@@ -24,6 +24,7 @@
import android.os.PersistableBundle;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.text.format.DateUtils;
import android.util.AtomicFile;
import android.util.ArraySet;
import android.util.Pair;
@@ -552,9 +553,10 @@
return null;
}
- Pair<Long, Long> runtimes;
+ // Tuple of (earliest runtime, latest runtime) in elapsed realtime after disk load.
+ Pair<Long, Long> elapsedRuntimes;
try {
- runtimes = buildExecutionTimesFromXml(parser);
+ elapsedRuntimes = buildExecutionTimesFromXml(parser);
} catch (NumberFormatException e) {
if (DEBUG) {
Slog.d(TAG, "Error parsing execution time parameters, skipping.");
@@ -562,22 +564,45 @@
return null;
}
+ final long elapsedNow = SystemClock.elapsedRealtime();
if (XML_TAG_PERIODIC.equals(parser.getName())) {
try {
String val = parser.getAttributeValue(null, "period");
- jobBuilder.setPeriodic(Long.valueOf(val));
+ final long periodMillis = Long.valueOf(val);
+ jobBuilder.setPeriodic(periodMillis);
+ // As a sanity check, cap the recreated run time to be no later than 2 periods
+ // from now. This is the latest the periodic could be pushed out. This could
+ // happen if the periodic ran early (at the start of its period), and then the
+ // device rebooted.
+ if (elapsedRuntimes.second > elapsedNow + 2 * periodMillis) {
+ final long clampedEarlyRuntimeElapsed = elapsedNow + periodMillis;
+ final long clampedLateRuntimeElapsed = elapsedNow + 2 * periodMillis;
+ Slog.w(TAG,
+ String.format("Periodic job for uid='%d' persisted run-time is" +
+ " too big [%s, %s]. Clamping to [%s,%s]",
+ uid,
+ DateUtils.formatElapsedTime(elapsedRuntimes.first / 1000),
+ DateUtils.formatElapsedTime(elapsedRuntimes.second / 1000),
+ DateUtils.formatElapsedTime(
+ clampedEarlyRuntimeElapsed / 1000),
+ DateUtils.formatElapsedTime(
+ clampedLateRuntimeElapsed / 1000))
+ );
+ elapsedRuntimes =
+ Pair.create(clampedEarlyRuntimeElapsed, clampedLateRuntimeElapsed);
+ }
} catch (NumberFormatException e) {
Slog.d(TAG, "Error reading periodic execution criteria, skipping.");
return null;
}
} else if (XML_TAG_ONEOFF.equals(parser.getName())) {
try {
- if (runtimes.first != JobStatus.NO_EARLIEST_RUNTIME) {
- jobBuilder.setMinimumLatency(runtimes.first - SystemClock.elapsedRealtime());
+ if (elapsedRuntimes.first != JobStatus.NO_EARLIEST_RUNTIME) {
+ jobBuilder.setMinimumLatency(elapsedRuntimes.first - elapsedNow);
}
- if (runtimes.second != JobStatus.NO_LATEST_RUNTIME) {
+ if (elapsedRuntimes.second != JobStatus.NO_LATEST_RUNTIME) {
jobBuilder.setOverrideDeadline(
- runtimes.second - SystemClock.elapsedRealtime());
+ elapsedRuntimes.second - elapsedNow);
}
} catch (NumberFormatException e) {
Slog.d(TAG, "Error reading job execution criteria, skipping.");
@@ -598,7 +623,8 @@
do {
eventType = parser.next();
} while (eventType == XmlPullParser.TEXT);
- if (!(eventType == XmlPullParser.START_TAG && XML_TAG_EXTRAS.equals(parser.getName()))) {
+ if (!(eventType == XmlPullParser.START_TAG
+ && XML_TAG_EXTRAS.equals(parser.getName()))) {
if (DEBUG) {
Slog.d(TAG, "Error reading extras, skipping.");
}
@@ -609,7 +635,8 @@
jobBuilder.setExtras(extras);
parser.nextTag(); // Consume </extras>
- return new JobStatus(jobBuilder.build(), uid, runtimes.first, runtimes.second);
+ return new JobStatus(
+ jobBuilder.build(), uid, elapsedRuntimes.first, elapsedRuntimes.second);
}
private JobInfo.Builder buildBuilderFromXml(XmlPullParser parser) throws NumberFormatException {
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 02d4f40..6fc02f6 100644
--- a/services/core/java/com/android/server/job/controllers/AppIdleController.java
+++ b/services/core/java/com/android/server/job/controllers/AppIdleController.java
@@ -67,7 +67,7 @@
mTrackedTasks.add(jobStatus);
String packageName = jobStatus.job.getService().getPackageName();
final boolean appIdle = !mAppIdleParoleOn && mUsageStatsInternal.isAppIdle(packageName,
- jobStatus.getUserId());
+ jobStatus.uId, jobStatus.getUserId());
if (DEBUG) {
Slog.d(LOG_TAG, "Start tracking, setting idle state of "
+ packageName + " to " + appIdle);
@@ -108,7 +108,7 @@
for (JobStatus task : mTrackedTasks) {
String packageName = task.job.getService().getPackageName();
final boolean appIdle = !mAppIdleParoleOn && mUsageStatsInternal.isAppIdle(packageName,
- task.getUserId());
+ task.uId, task.getUserId());
if (DEBUG) {
Slog.d(LOG_TAG, "Setting idle state of " + packageName + " to " + appIdle);
}
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index 69c63f3..c02611f 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -82,6 +82,13 @@
this.numFailures = numFailures;
}
+ /** Copy constructor. */
+ public JobStatus(JobStatus jobStatus) {
+ this(jobStatus.getJob(), jobStatus.getUid(), jobStatus.getNumFailures());
+ this.earliestRunTimeElapsedMillis = jobStatus.getEarliestRunTime();
+ this.latestRunTimeElapsedMillis = jobStatus.getLatestRunTimeElapsed();
+ }
+
/** Create a newly scheduled job. */
public JobStatus(JobInfo job, int uId) {
this(job, uId, 0);
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 cda7c32..21c30c7 100644
--- a/services/core/java/com/android/server/job/controllers/StateController.java
+++ b/services/core/java/com/android/server/job/controllers/StateController.java
@@ -18,6 +18,7 @@
import android.content.Context;
+import com.android.server.job.JobSchedulerService;
import com.android.server.job.StateChangedListener;
import java.io.PrintWriter;
@@ -28,7 +29,7 @@
* are ready to run, or whether they must be stopped.
*/
public abstract class StateController {
- protected static final boolean DEBUG = false;
+ protected static final boolean DEBUG = JobSchedulerService.DEBUG;
protected Context mContext;
protected StateChangedListener mStateChangedListener;
protected boolean mDeviceIdleMode;
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 17ae6dc..41aea04 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -2227,7 +2227,7 @@
final int userId = UserHandle.getUserId(uid);
for (String packageName : packages) {
- if (!mUsageStats.isAppIdle(packageName, userId)) {
+ if (!mUsageStats.isAppIdle(packageName, uid, userId)) {
return false;
}
}
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index 19d8538..1987214 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -17,6 +17,7 @@
package com.android.server.notification;
import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.Context;
import android.net.Uri;
import android.os.Handler;
@@ -29,6 +30,7 @@
import android.service.notification.ConditionProviderService;
import android.service.notification.IConditionListener;
import android.service.notification.IConditionProvider;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
@@ -79,7 +81,7 @@
final Config c = new Config();
c.caption = "condition provider";
c.serviceInterface = ConditionProviderService.SERVICE_INTERFACE;
- c.secureSettingName = Settings.Secure.ENABLED_CONDITION_PROVIDERS;
+ c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES;
c.bindPermission = android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE;
c.settingsAction = Settings.ACTION_CONDITION_PROVIDER_SETTINGS;
c.clientLabel = R.string.condition_provider_service_binding_label;
@@ -166,24 +168,6 @@
}
}
- public void requestConditions(IConditionListener callback, int relevance) {
- synchronized(mMutex) {
- if (DEBUG) Slog.d(TAG, "requestConditions callback=" + callback
- + " relevance=" + Condition.relevanceToString(relevance));
- if (callback == null) return;
- relevance = relevance & (Condition.FLAG_RELEVANT_NOW | Condition.FLAG_RELEVANT_ALWAYS);
- if (relevance != 0) {
- mListeners.put(callback.asBinder(), callback);
- requestConditionsLocked(relevance);
- } else {
- mListeners.remove(callback.asBinder());
- if (mListeners.isEmpty()) {
- requestConditionsLocked(0);
- }
- }
- }
- }
-
private Condition[] validateConditions(String pkg, Condition[] conditions) {
if (conditions == null || conditions.length == 0) return null;
final int N = conditions.length;
@@ -280,6 +264,26 @@
}
}
+ @Override
+ protected ArraySet<ComponentName> loadComponentNamesFromSetting(String settingName,
+ int userId) {
+ final ContentResolver cr = mContext.getContentResolver();
+ String settingValue = Settings.Secure.getStringForUser(
+ cr,
+ settingName,
+ userId);
+ if (TextUtils.isEmpty(settingValue))
+ return null;
+ String[] packages = settingValue.split(ENABLED_SERVICES_SEPARATOR);
+ ArraySet<ComponentName> result = new ArraySet<>(packages.length);
+ for (int i = 0; i < packages.length; i++) {
+ if (!TextUtils.isEmpty(packages[i])) {
+ result.addAll(queryPackageForServices(packages[i], userId));
+ }
+ }
+ return result;
+ }
+
public boolean subscribeIfNecessary(ComponentName component, Uri conditionId) {
synchronized (mMutex) {
final ConditionRecord r = getRecordLocked(conditionId, component, false /*create*/);
@@ -360,25 +364,6 @@
return info == null ? null : (IConditionProvider) info.service;
}
- private void requestConditionsLocked(int flags) {
- for (ManagedServiceInfo info : mServices) {
- final IConditionProvider provider = provider(info);
- if (provider == null) continue;
- // clear all stored conditions from this provider that we no longer care about
- for (int i = mRecords.size() - 1; i >= 0; i--) {
- final ConditionRecord r = mRecords.get(i);
- if (r.info != info) continue;
- if (r.subscribed) continue;
- mRecords.remove(i);
- }
- try {
- provider.onRequestConditions(flags);
- } catch (RemoteException e) {
- Slog.w(TAG, "Error requesting conditions from " + info.component, e);
- }
- }
- }
-
private static class ConditionRecord {
public final Uri id;
public final ComponentName component;
diff --git a/services/core/java/com/android/server/notification/EventConditionProvider.java b/services/core/java/com/android/server/notification/EventConditionProvider.java
index 88ef366..a4d5bce 100644
--- a/services/core/java/com/android/server/notification/EventConditionProvider.java
+++ b/services/core/java/com/android/server/notification/EventConditionProvider.java
@@ -176,7 +176,7 @@
}
mTrackers.clear();
for (UserHandle user : UserManager.get(mContext).getUserProfiles()) {
- final Context context = user.isOwner() ? mContext : getContextForUser(mContext, user);
+ final Context context = user.isSystem() ? mContext : getContextForUser(mContext, user);
if (context == null) {
Slog.w(TAG, "Unable to create context for user " + user.getIdentifier());
continue;
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index a54a61a..c7551c50 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -70,7 +70,7 @@
protected final String TAG = getClass().getSimpleName();
protected final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final String ENABLED_SERVICES_SEPARATOR = ":";
+ protected static final String ENABLED_SERVICES_SEPARATOR = ":";
protected final Context mContext;
protected final Object mMutex;
@@ -279,7 +279,8 @@
}
- private ArraySet<ComponentName> loadComponentNamesFromSetting(String settingName, int userId) {
+ protected ArraySet<ComponentName> loadComponentNamesFromSetting(String settingName,
+ int userId) {
final ContentResolver cr = mContext.getContentResolver();
String settingValue = Settings.Secure.getStringForUser(
cr,
@@ -319,7 +320,6 @@
userId);
}
-
/**
* Remove access for any services that no longer exist.
*/
@@ -332,6 +332,38 @@
rebuildRestoredPackages();
}
+ protected Set<ComponentName> queryPackageForServices(String packageName, int userId) {
+ Set<ComponentName> installed = new ArraySet<>();
+ final PackageManager pm = mContext.getPackageManager();
+ Intent queryIntent = new Intent(mConfig.serviceInterface);
+ if (!TextUtils.isEmpty(packageName)) {
+ queryIntent.setPackage(packageName);
+ }
+ List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser(
+ queryIntent,
+ PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
+ userId);
+ if (DEBUG)
+ Slog.v(TAG, mConfig.serviceInterface + " services: " + installedServices);
+ if (installedServices != null) {
+ for (int i = 0, count = installedServices.size(); i < count; i++) {
+ ResolveInfo resolveInfo = installedServices.get(i);
+ ServiceInfo info = resolveInfo.serviceInfo;
+
+ ComponentName component = new ComponentName(info.packageName, info.name);
+ if (!mConfig.bindPermission.equals(info.permission)) {
+ Slog.w(TAG, "Skipping " + getCaption() + " service "
+ + info.packageName + "/" + info.name
+ + ": it does not require the permission "
+ + mConfig.bindPermission);
+ continue;
+ }
+ installed.add(component);
+ }
+ }
+ return installed;
+ }
+
private void updateSettingsAccordingToInstalledServices(int userId) {
boolean restoredChanged = false;
boolean currentChanged = false;
@@ -339,30 +371,8 @@
loadComponentNamesFromSetting(restoredSettingName(mConfig), userId);
Set<ComponentName> current =
loadComponentNamesFromSetting(mConfig.secureSettingName, userId);
- Set<ComponentName> installed = new ArraySet<>();
-
- final PackageManager pm = mContext.getPackageManager();
- List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser(
- new Intent(mConfig.serviceInterface),
- PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
- userId);
- if (DEBUG)
- Slog.v(TAG, mConfig.serviceInterface + " services: " + installedServices);
-
- for (int i = 0, count = installedServices.size(); i < count; i++) {
- ResolveInfo resolveInfo = installedServices.get(i);
- ServiceInfo info = resolveInfo.serviceInfo;
-
- ComponentName component = new ComponentName(info.packageName, info.name);
- if (!mConfig.bindPermission.equals(info.permission)) {
- Slog.w(TAG, "Skipping " + getCaption() + " service "
- + info.packageName + "/" + info.name
- + ": it does not require the permission "
- + mConfig.bindPermission);
- continue;
- }
- installed.add(component);
- }
+ // Load all services for all packages.
+ Set<ComponentName> installed = queryPackageForServices(null, userId);
ArraySet<ComponentName> retained = new ArraySet<>();
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index e8e46ef..b84811f 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1579,12 +1579,6 @@
}
@Override
- public boolean setZenModeConfig(ZenModeConfig config, String reason) {
- checkCallerIsSystem();
- return mZenModeHelper.setConfig(config, reason);
- }
-
- @Override
public void setZenMode(int mode, Uri conditionId, String reason) throws RemoteException {
enforceSystemOrSystemUIOrVolume("INotificationManager.setZenMode");
final long identity = Binder.clearCallingIdentity();
@@ -1669,12 +1663,6 @@
});
}
- @Override
- public void requestZenModeConditions(IConditionListener callback, int relevance) {
- enforceSystemOrSystemUIOrVolume("INotificationManager.requestZenModeConditions");
- mZenModeHelper.requestZenModeConditions(callback, relevance);
- }
-
private void enforceSystemOrSystemUIOrVolume(String message) {
if (mAudioManagerInternal != null) {
final int vcuid = mAudioManagerInternal.getVolumeControllerUid();
@@ -2146,13 +2134,6 @@
+ " id=" + id + " notification=" + notification);
}
- if (notification.getSmallIcon() != null) {
- if (!notification.isValid()) {
- throw new IllegalArgumentException("Invalid notification (): pkg=" + pkg
- + " id=" + id + " notification=" + notification);
- }
- }
-
mHandler.post(new Runnable() {
@Override
public void run() {
diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java
index 0d6e8d6..1cdc6db 100644
--- a/services/core/java/com/android/server/notification/NotificationUsageStats.java
+++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java
@@ -26,6 +26,7 @@
import android.os.HandlerThread;
import android.os.Message;
import android.os.SystemClock;
+import android.text.TextUtils;
import android.util.Log;
import com.android.internal.logging.MetricsLogger;
@@ -41,6 +42,7 @@
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Map;
+import java.util.Set;
/**
* Keeps track of notification activity, display, and user interaction.
@@ -367,26 +369,37 @@
break;
}
- for (String Key : n.extras.keySet()) {
- if (Notification.EXTRA_BIG_TEXT.equals(key)) {
- numWithBigText++;
- } else if (Notification.EXTRA_PICTURE.equals(key)) {
- numWithBigPicture++;
- } else if (Notification.EXTRA_LARGE_ICON.equals(key)) {
- numWithLargeIcon++;
- } else if (Notification.EXTRA_TEXT_LINES.equals(key)) {
- numWithInbox++;
- } else if (Notification.EXTRA_MEDIA_SESSION.equals(key)) {
- numWithMediaSession++;
- } else if (Notification.EXTRA_TITLE.equals(key)) {
- numWithTitle++;
- } else if (Notification.EXTRA_TEXT.equals(key)) {
- numWithText++;
- } else if (Notification.EXTRA_SUB_TEXT.equals(key)) {
- numWithSubText++;
- } else if (Notification.EXTRA_INFO_TEXT.equals(key)) {
- numWithInfoText++;
- }
+ final Set<String> names = n.extras.keySet();
+ if (names.contains(Notification.EXTRA_BIG_TEXT)) {
+ numWithBigText++;
+ }
+ if (names.contains(Notification.EXTRA_PICTURE)) {
+ numWithBigPicture++;
+ }
+ if (names.contains(Notification.EXTRA_LARGE_ICON)) {
+ numWithLargeIcon++;
+ }
+ if (names.contains(Notification.EXTRA_TEXT_LINES)) {
+ numWithInbox++;
+ }
+ if (names.contains(Notification.EXTRA_MEDIA_SESSION)) {
+ numWithMediaSession++;
+ }
+ if (names.contains(Notification.EXTRA_TITLE) &&
+ !TextUtils.isEmpty(n.extras.getCharSequence(Notification.EXTRA_TITLE))) {
+ numWithTitle++;
+ }
+ if (names.contains(Notification.EXTRA_TEXT) &&
+ !TextUtils.isEmpty(n.extras.getCharSequence(Notification.EXTRA_TEXT))) {
+ numWithText++;
+ }
+ if (names.contains(Notification.EXTRA_SUB_TEXT) &&
+ !TextUtils.isEmpty(n.extras.getCharSequence(Notification.EXTRA_SUB_TEXT))) {
+ numWithSubText++;
+ }
+ if (names.contains(Notification.EXTRA_INFO_TEXT) &&
+ !TextUtils.isEmpty(n.extras.getCharSequence(Notification.EXTRA_INFO_TEXT))) {
+ numWithInfoText++;
}
}
diff --git a/services/core/java/com/android/server/notification/ZenModeConditions.java b/services/core/java/com/android/server/notification/ZenModeConditions.java
index c2e4349..cee9ec8 100644
--- a/services/core/java/com/android/server/notification/ZenModeConditions.java
+++ b/services/core/java/com/android/server/notification/ZenModeConditions.java
@@ -59,10 +59,6 @@
pw.print(prefix); pw.print("mSubscriptions="); pw.println(mSubscriptions);
}
- public void requestConditions(IConditionListener callback, int relevance) {
- mConditionProviders.requestConditions(callback, relevance);
- }
-
public void evaluateConfig(ZenModeConfig config, boolean processSubscriptions) {
if (config == null) return;
if (config.manualRule != null && config.manualRule.condition != null
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 76c6443..a1f8c41 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -121,8 +121,11 @@
public boolean matchesCallFilter(UserHandle userHandle, Bundle extras,
ValidateNotificationPeople validator, int contactsTimeoutMs, float timeoutAffinity) {
- return ZenModeFiltering.matchesCallFilter(mContext, mZenMode, mConfig, userHandle, extras,
- validator, contactsTimeoutMs, timeoutAffinity);
+ synchronized (mConfig) {
+ return ZenModeFiltering.matchesCallFilter(mContext, mZenMode, mConfig, userHandle,
+ extras,
+ validator, contactsTimeoutMs, timeoutAffinity);
+ }
}
public boolean isCall(NotificationRecord record) {
@@ -130,7 +133,9 @@
}
public boolean shouldIntercept(NotificationRecord record) {
- return mFiltering.shouldIntercept(mZenMode, mConfig, record);
+ synchronized (mConfig) {
+ return mFiltering.shouldIntercept(mZenMode, mConfig, record);
+ }
}
public void addCallback(Callback callback) {
@@ -175,10 +180,6 @@
mConfigs.remove(user);
}
- public void requestZenModeConditions(IConditionListener callback, int relevance) {
- mConditions.requestConditions(callback, relevance);
- }
-
public int getZenModeListenerInterruptionFilter() {
return NotificationManager.zenModeToInterruptionFilter(mZenMode);
}
@@ -203,18 +204,23 @@
public List<AutomaticZenRule> getAutomaticZenRules() {
List<AutomaticZenRule> rules = new ArrayList<>();
- if (mConfig == null) return rules;
- for(ZenRule rule : mConfig.automaticRules.values()) {
- if (canManageAutomaticZenRule(rule)) {
- rules.add(createAutomaticZenRule(rule));
+ synchronized (mConfig) {
+ if (mConfig == null) return rules;
+ for (ZenRule rule : mConfig.automaticRules.values()) {
+ if (canManageAutomaticZenRule(rule)) {
+ rules.add(createAutomaticZenRule(rule));
+ }
}
}
return rules;
}
public AutomaticZenRule getAutomaticZenRule(String id) {
- if (mConfig == null) return null;
- ZenRule rule = mConfig.automaticRules.get(id);
+ ZenRule rule;
+ synchronized (mConfig) {
+ if (mConfig == null) return null;
+ rule = mConfig.automaticRules.get(id);
+ }
if (rule == null) return null;
if (canManageAutomaticZenRule(rule)) {
return createAutomaticZenRule(rule);
@@ -223,14 +229,18 @@
}
public AutomaticZenRule addAutomaticZenRule(AutomaticZenRule automaticZenRule, String reason) {
- if (mConfig == null) return null;
- if (DEBUG) {
- Log.d(TAG, "addAutomaticZenRule zenRule= " + automaticZenRule + " reason=" +reason);
+ ZenModeConfig newConfig;
+ synchronized (mConfig) {
+ if (mConfig == null) return null;
+ if (DEBUG) {
+ Log.d(TAG,
+ "addAutomaticZenRule zenRule= " + automaticZenRule + " reason=" + reason);
+ }
+ if (!TextUtils.isEmpty(automaticZenRule.getId())) {
+ throw new IllegalArgumentException("Rule already exists");
+ }
+ newConfig = mConfig.copy();
}
- if (!TextUtils.isEmpty(automaticZenRule.getId())) {
- throw new IllegalArgumentException("Rule already exists");
- }
- final ZenModeConfig newConfig = mConfig.copy();
ZenRule rule = new ZenRule();
populateZenRule(automaticZenRule, rule, true);
newConfig.automaticRules.put(rule.id, rule);
@@ -242,12 +252,15 @@
}
public boolean updateAutomaticZenRule(AutomaticZenRule automaticZenRule, String reason) {
- if (mConfig == null) return false;
- if (DEBUG) {
- Log.d(TAG, "updateAutomaticZenRule zenRule=" + automaticZenRule
- + " reason=" + reason);
+ ZenModeConfig newConfig;
+ synchronized (mConfig) {
+ if (mConfig == null) return false;
+ if (DEBUG) {
+ Log.d(TAG, "updateAutomaticZenRule zenRule=" + automaticZenRule
+ + " reason=" + reason);
+ }
+ newConfig = mConfig.copy();
}
- final ZenModeConfig newConfig = mConfig.copy();
final String ruleId = automaticZenRule.getId();
ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
if (ruleId == null) {
@@ -265,8 +278,11 @@
}
public boolean removeAutomaticZenRule(String id, String reason) {
- if (mConfig == null) return false;
- final ZenModeConfig newConfig = mConfig.copy();
+ ZenModeConfig newConfig;
+ synchronized (mConfig) {
+ if (mConfig == null) return false;
+ newConfig = mConfig.copy();
+ }
ZenRule rule = newConfig.automaticRules.get(id);
if (rule == null) return false;
if (canManageAutomaticZenRule(rule)) {
@@ -328,12 +344,15 @@
private void setManualZenMode(int zenMode, Uri conditionId, String reason,
boolean setRingerMode) {
- if (mConfig == null) return;
- if (!Global.isValidZenMode(zenMode)) return;
- if (DEBUG) Log.d(TAG, "setManualZenMode " + Global.zenModeToString(zenMode)
- + " conditionId=" + conditionId + " reason=" + reason
- + " setRingerMode=" + setRingerMode);
- final ZenModeConfig newConfig = mConfig.copy();
+ ZenModeConfig newConfig;
+ synchronized (mConfig) {
+ if (mConfig == null) return;
+ if (!Global.isValidZenMode(zenMode)) return;
+ if (DEBUG) Log.d(TAG, "setManualZenMode " + Global.zenModeToString(zenMode)
+ + " conditionId=" + conditionId + " reason=" + reason
+ + " setRingerMode=" + setRingerMode);
+ newConfig = mConfig.copy();
+ }
if (zenMode == Global.ZEN_MODE_OFF) {
newConfig.manualRule = null;
for (ZenRule automaticRule : newConfig.automaticRules.values()) {
@@ -360,7 +379,9 @@
dump(pw, prefix, "mConfigs[u=" + mConfigs.keyAt(i) + "]", mConfigs.valueAt(i));
}
pw.print(prefix); pw.print("mUser="); pw.println(mUser);
- dump(pw, prefix, "mConfig", mConfig);
+ synchronized (mConfig) {
+ dump(pw, prefix, "mConfig", mConfig);
+ }
pw.print(prefix); pw.print("mEffectsSuppressed="); pw.println(mEffectsSuppressed);
mFiltering.dump(pw, prefix);
mConditions.dump(pw, prefix);
@@ -437,7 +458,9 @@
}
public ZenModeConfig getConfig() {
- return mConfig;
+ synchronized (mConfig) {
+ return mConfig.copy();
+ }
}
public boolean setConfig(ZenModeConfig config, String reason) {
@@ -462,19 +485,21 @@
return true;
}
mConditions.evaluateConfig(config, false /*processSubscriptions*/); // may modify config
- mConfigs.put(config.user, config);
- if (DEBUG) Log.d(TAG, "setConfig reason=" + reason, new Throwable());
- ZenLog.traceConfig(reason, mConfig, config);
- final boolean policyChanged = !Objects.equals(getNotificationPolicy(mConfig),
- getNotificationPolicy(config));
- mConfig = config;
- if (config.equals(mConfig)) {
- dispatchOnConfigChanged();
+ synchronized (mConfig) {
+ mConfigs.put(config.user, config);
+ if (DEBUG) Log.d(TAG, "setConfig reason=" + reason, new Throwable());
+ ZenLog.traceConfig(reason, mConfig, config);
+ final boolean policyChanged = !Objects.equals(getNotificationPolicy(mConfig),
+ getNotificationPolicy(config));
+ mConfig = config;
+ if (config.equals(mConfig)) {
+ dispatchOnConfigChanged();
+ }
+ if (policyChanged) {
+ dispatchOnPolicyChanged();
+ }
}
- if (policyChanged){
- dispatchOnPolicyChanged();
- }
- final String val = Integer.toString(mConfig.hashCode());
+ final String val = Integer.toString(config.hashCode());
Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val);
if (!evaluateZenMode(reason, setRingerMode)) {
applyRestrictions(); // evaluateZenMode will also apply restrictions if changed
@@ -529,17 +554,19 @@
}
private int computeZenMode() {
- if (mConfig == null) return Global.ZEN_MODE_OFF;
- if (mConfig.manualRule != null) return mConfig.manualRule.zenMode;
- int zen = Global.ZEN_MODE_OFF;
- for (ZenRule automaticRule : mConfig.automaticRules.values()) {
- if (automaticRule.isAutomaticActive()) {
- if (zenSeverity(automaticRule.zenMode) > zenSeverity(zen)) {
- zen = automaticRule.zenMode;
+ synchronized (mConfig) {
+ if (mConfig == null) return Global.ZEN_MODE_OFF;
+ if (mConfig.manualRule != null) return mConfig.manualRule.zenMode;
+ int zen = Global.ZEN_MODE_OFF;
+ for (ZenRule automaticRule : mConfig.automaticRules.values()) {
+ if (automaticRule.isAutomaticActive()) {
+ if (zenSeverity(automaticRule.zenMode) > zenSeverity(zen)) {
+ zen = automaticRule.zenMode;
+ }
}
}
+ return zen;
}
- return zen;
}
private void applyRestrictions() {
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 7b1ac5ca..af20679 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -28,6 +28,7 @@
import android.util.Log;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.TimeUnit;
/**
* {@hide}
@@ -55,6 +56,7 @@
.setRequiresDeviceIdle(true)
.setRequiresCharging(true)
.setMinimumLatency(minLatency)
+ .setPeriodic(TimeUnit.DAYS.toMillis(1))
.build();
js.schedule(job);
}
@@ -89,7 +91,7 @@
// skip previously failing package
continue;
}
- if (!pm.performDexOpt(pkg, null /* instruction set */, true)) {
+ if (!pm.performDexOpt(pkg, null /* instruction set */)) {
// 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 8176aff..e4dbf65 100644
--- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
@@ -20,6 +20,7 @@
import android.app.DownloadManager;
import android.app.admin.DevicePolicyManager;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal.PackagesProvider;
@@ -586,6 +587,8 @@
grantRuntimePermissionsLPw(wearHomePackage, PHONE_PERMISSIONS, true, userId);
grantRuntimePermissionsLPw(wearHomePackage, MICROPHONE_PERMISSIONS, false,
userId);
+ grantRuntimePermissionsLPw(wearHomePackage, LOCATION_PERMISSIONS, false,
+ userId);
}
}
@@ -596,14 +599,16 @@
private void grantDefaultPermissionsToDefaultSystemDialerAppLPr(
PackageParser.Package dialerPackage, int userId) {
if (doesPackageSupportRuntimePermissions(dialerPackage)) {
- grantRuntimePermissionsLPw(dialerPackage, PHONE_PERMISSIONS, userId);
+ boolean isPhonePermFixed =
+ mService.hasSystemFeature(PackageManager.FEATURE_WATCH);
+ grantRuntimePermissionsLPw(
+ dialerPackage, PHONE_PERMISSIONS, isPhonePermFixed, userId);
grantRuntimePermissionsLPw(dialerPackage, CONTACTS_PERMISSIONS, userId);
grantRuntimePermissionsLPw(dialerPackage, SMS_PERMISSIONS, userId);
grantRuntimePermissionsLPw(dialerPackage, MICROPHONE_PERMISSIONS, userId);
}
}
-
private void grantDefaultPermissionsToDefaultSystemSmsAppLPr(
PackageParser.Package smsPackage, int userId) {
if (doesPackageSupportRuntimePermissions(smsPackage)) {
@@ -613,7 +618,6 @@
}
}
-
public void grantDefaultPermissionsToDefaultSmsAppLPr(String packageName, int userId) {
Log.i(TAG, "Granting permissions to default sms app for user:" + userId);
if (packageName == null) {
@@ -691,29 +695,23 @@
private PackageParser.Package getDefaultSystemHandlerActivityPackageLPr(
Intent intent, int userId) {
- List<ResolveInfo> handlers = mService.mActivities.queryIntent(intent,
- intent.resolveType(mService.mContext.getContentResolver()),
- PackageManager.GET_DISABLED_COMPONENTS, userId);
- if (handlers == null) {
+ ResolveInfo handler = mService.resolveIntent(intent,
+ intent.resolveType(mService.mContext.getContentResolver()), 0, userId);
+ if (handler == null || handler.activityInfo == null) {
return null;
}
- final int handlerCount = handlers.size();
- for (int i = 0; i < handlerCount; i++) {
- ResolveInfo handler = handlers.get(i);
- PackageParser.Package handlerPackage = getSystemPackageLPr(
- handler.activityInfo.packageName);
- if (handlerPackage != null) {
- return handlerPackage;
- }
+ ActivityInfo activityInfo = handler.activityInfo;
+ if (activityInfo.packageName.equals(mService.mResolveActivity.packageName)
+ && activityInfo.name.equals(mService.mResolveActivity.name)) {
+ return null;
}
- return null;
+ return getSystemPackageLPr(handler.activityInfo.packageName);
}
private PackageParser.Package getDefaultSystemHandlerServicePackageLPr(
Intent intent, int userId) {
List<ResolveInfo> handlers = mService.queryIntentServices(intent,
- intent.resolveType(mService.mContext.getContentResolver()),
- PackageManager.GET_DISABLED_COMPONENTS, userId);
+ intent.resolveType(mService.mContext.getContentResolver()), 0, userId);
if (handlers == null) {
return null;
}
@@ -739,10 +737,9 @@
for (String syncAdapterPackageName : syncAdapterPackageNames) {
homeIntent.setPackage(syncAdapterPackageName);
- List<ResolveInfo> homeActivities = mService.mActivities.queryIntent(homeIntent,
- homeIntent.resolveType(mService.mContext.getContentResolver()),
- PackageManager.GET_DISABLED_COMPONENTS, userId);
- if (!homeActivities.isEmpty()) {
+ ResolveInfo homeActivity = mService.resolveIntent(homeIntent,
+ homeIntent.resolveType(mService.mContext.getContentResolver()), 0, userId);
+ if (homeActivity != null) {
continue;
}
@@ -837,8 +834,8 @@
mService.grantRuntimePermission(pkg.packageName, permission, userId);
if (DEBUG) {
- Log.i(TAG, "Granted " + permission + " to default handler "
- + pkg.packageName);
+ Log.i(TAG, "Granted " + (systemFixed ? "fixed " : "not fixed ")
+ + permission + " to default handler " + pkg.packageName);
}
int newFlags = PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
@@ -849,6 +846,19 @@
mService.updatePermissionFlags(permission, pkg.packageName,
newFlags, newFlags, userId);
}
+
+ // If a component gets a permission for being the default handler A
+ // and also default handler B, we grant the weaker grant form.
+ if ((flags & PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT) != 0
+ && (flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0
+ && !systemFixed) {
+ if (DEBUG) {
+ Log.i(TAG, "Granted not fixed " + permission + " to default handler "
+ + pkg.packageName);
+ }
+ mService.updatePermissionFlags(permission, pkg.packageName,
+ PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, 0, userId);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index d867616..99a051a 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -53,6 +53,14 @@
mInstaller = new InstallerConnection();
}
+ /**
+ * Yell loudly if someone tries making future calls while holding a lock on
+ * the given object.
+ */
+ public void setWarnIfHeld(Object warnIfHeld) {
+ mInstaller.setWarnIfHeld(warnIfHeld);
+ }
+
@Override
public void onStart() {
Slog.i(TAG, "Waiting for installd to be ready.");
@@ -177,15 +185,6 @@
return mInstaller.execute(builder.toString());
}
- public int rename(String oldname, String newname) {
- StringBuilder builder = new StringBuilder("rename");
- builder.append(' ');
- builder.append(oldname);
- builder.append(' ');
- builder.append(newname);
- return mInstaller.execute(builder.toString());
- }
-
@Deprecated
public int fixUid(String name, int uid, int gid) {
return fixUid(null, name, uid, gid);
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 6c6871f..d29a623 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -28,7 +28,6 @@
import android.util.Slog;
import java.io.File;
-import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@@ -56,7 +55,6 @@
static final int DEX_OPT_FAILED = -1;
private final PackageManagerService mPackageManagerService;
- private ArraySet<PackageParser.Package> mDeferredDexOpt;
private final PowerManager.WakeLock mDexoptWakeLock;
private volatile boolean mSystemReady;
@@ -76,8 +74,7 @@
* {@link PackageManagerService#mInstallLock}.
*/
int performDexOpt(PackageParser.Package pkg, String[] instructionSets,
- boolean forceDex, boolean defer, boolean inclDependencies,
- boolean bootComplete, boolean useJit) {
+ boolean inclDependencies) {
ArraySet<String> done;
if (inclDependencies && (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null)) {
done = new ArraySet<String>();
@@ -92,8 +89,7 @@
mDexoptWakeLock.acquire();
}
try {
- return performDexOptLI(pkg, instructionSets, forceDex, defer, bootComplete,
- useJit, done);
+ return performDexOptLI(pkg, instructionSets, done);
} finally {
if (useLock) {
mDexoptWakeLock.release();
@@ -103,7 +99,6 @@
}
private int performDexOptLI(PackageParser.Package pkg, String[] targetInstructionSets,
- boolean forceDex, boolean defer, boolean bootComplete, boolean useJit,
ArraySet<String> done) {
final String[] instructionSets = targetInstructionSets != null ?
targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo);
@@ -111,12 +106,10 @@
if (done != null) {
done.add(pkg.packageName);
if (pkg.usesLibraries != null) {
- performDexOptLibsLI(pkg.usesLibraries, instructionSets, forceDex, defer,
- bootComplete, useJit, done);
+ performDexOptLibsLI(pkg.usesLibraries, instructionSets, done);
}
if (pkg.usesOptionalLibraries != null) {
- performDexOptLibsLI(pkg.usesOptionalLibraries, instructionSets, forceDex, defer,
- bootComplete, useJit, done);
+ performDexOptLibsLI(pkg.usesOptionalLibraries, instructionSets, done);
}
}
@@ -135,30 +128,18 @@
// 3.) we are skipping an unneeded dexopt
final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
for (String dexCodeInstructionSet : dexCodeInstructionSets) {
- if (!forceDex && pkg.mDexOptPerformed.contains(dexCodeInstructionSet)) {
+ if (pkg.mDexOptPerformed.contains(dexCodeInstructionSet)) {
continue;
}
for (String path : paths) {
final int dexoptNeeded;
- if (forceDex) {
- dexoptNeeded = DexFile.DEX2OAT_NEEDED;
- } else {
- try {
- dexoptNeeded = DexFile.getDexOptNeeded(path, pkg.packageName,
- dexCodeInstructionSet, defer);
- } catch (IOException ioe) {
- Slog.w(TAG, "IOException reading apk: " + path, ioe);
- return DEX_OPT_FAILED;
- }
- }
-
- if (!forceDex && defer && dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
- // We're deciding to defer a needed dexopt. Don't bother dexopting for other
- // paths and instruction sets. We'll deal with them all together when we process
- // our list of deferred dexopts.
- addPackageForDeferredDexopt(pkg);
- return DEX_OPT_DEFERRED;
+ try {
+ dexoptNeeded = DexFile.getDexOptNeeded(path, pkg.packageName,
+ dexCodeInstructionSet, /* defer */false);
+ } catch (IOException ioe) {
+ Slog.w(TAG, "IOException reading apk: " + path, ioe);
+ return DEX_OPT_FAILED;
}
if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
@@ -166,12 +147,7 @@
String oatDir = null;
if (dexoptNeeded == DexFile.DEX2OAT_NEEDED) {
dexoptType = "dex2oat";
- try {
- oatDir = createOatDirIfSupported(pkg, dexCodeInstructionSet);
- } catch (IOException ioe) {
- Slog.w(TAG, "Unable to create oatDir for package: " + pkg.packageName);
- return DEX_OPT_FAILED;
- }
+ oatDir = createOatDirIfSupported(pkg, dexCodeInstructionSet);
} else if (dexoptNeeded == DexFile.PATCHOAT_NEEDED) {
dexoptType = "patchoat";
} else if (dexoptNeeded == DexFile.SELF_PATCHOAT_NEEDED) {
@@ -183,20 +159,17 @@
Log.i(TAG, "Running dexopt (" + dexoptType + ") on: " + path + " pkg="
+ pkg.applicationInfo.packageName + " isa=" + dexCodeInstructionSet
+ " vmSafeMode=" + vmSafeMode + " debuggable=" + debuggable
- + " oatDir = " + oatDir + " bootComplete=" + bootComplete
- + " useJit=" + useJit);
+ + " oatDir = " + oatDir);
final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
final int dexFlags =
(!pkg.isForwardLocked() ? DEXOPT_PUBLIC : 0)
| (vmSafeMode ? DEXOPT_SAFEMODE : 0)
| (debuggable ? DEXOPT_DEBUGGABLE : 0)
- | (bootComplete ? DEXOPT_BOOTCOMPLETE : 0)
- | (useJit ? DEXOPT_USEJIT : 0);
+ | DEXOPT_BOOTCOMPLETE;
final int ret = mPackageManagerService.mInstaller.dexopt(path, sharedGid,
pkg.packageName, dexCodeInstructionSet, dexoptNeeded, oatDir, dexFlags);
- // Dex2oat might fail due to compiler / verifier errors. We soldier on
- // regardless, and attempt to interpret the app as a safety net.
+ // Dex2oat might fail due to compiler / verifier errors.
if (ret == 0) {
performedDexOpt = true;
}
@@ -230,8 +203,7 @@
* cannot be created.
*/
@Nullable
- private String createOatDirIfSupported(PackageParser.Package pkg, String dexInstructionSet)
- throws IOException {
+ private String createOatDirIfSupported(PackageParser.Package pkg, String dexInstructionSet) {
if (!pkg.canHaveOatDir()) {
return null;
}
@@ -250,34 +222,16 @@
}
private void performDexOptLibsLI(ArrayList<String> libs, String[] instructionSets,
- boolean forceDex, boolean defer, boolean bootComplete, boolean useJit,
ArraySet<String> done) {
for (String libName : libs) {
PackageParser.Package libPkg = mPackageManagerService.findSharedNonSystemLibrary(
libName);
if (libPkg != null && !done.contains(libName)) {
- performDexOptLI(libPkg, instructionSets, forceDex, defer, bootComplete, useJit, done);
+ performDexOptLI(libPkg, instructionSets, done);
}
}
}
- /**
- * Clears set of deferred dexopt packages.
- * @return content of dexopt set if it was not empty
- */
- public ArraySet<PackageParser.Package> clearDeferredDexOptPackages() {
- ArraySet<PackageParser.Package> result = mDeferredDexOpt;
- mDeferredDexOpt = null;
- return result;
- }
-
- public void addPackageForDeferredDexopt(PackageParser.Package pkg) {
- if (mDeferredDexOpt == null) {
- mDeferredDexOpt = new ArraySet<>();
- }
- mDeferredDexOpt.add(pkg);
- }
-
void systemReady() {
mSystemReady = true;
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 6e32e5c..b0e43a5 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -481,6 +481,7 @@
throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
"Failed to resolve stage location", e);
}
+ final boolean quickInstall = (params.installFlags & PackageManager.INSTALL_QUICK) != 0;
// Verify that stage looks sane with respect to existing application.
// This currently only ensures packageName, versionCode, and certificate
@@ -488,7 +489,10 @@
validateInstallLocked();
Preconditions.checkNotNull(mPackageName);
- Preconditions.checkNotNull(mSignatures);
+ // TODO: fix b/25118622; don't bypass signature check
+ if (!quickInstall) {
+ Preconditions.checkNotNull(mSignatures);
+ }
Preconditions.checkNotNull(mResolvedBaseFile);
if (!mPermissionsAccepted) {
@@ -598,6 +602,7 @@
* {@link PackageManagerService}.
*/
private void validateInstallLocked() throws PackageManagerException {
+ final boolean quickInstall = (params.installFlags & PackageManager.INSTALL_QUICK) != 0;
mPackageName = null;
mVersionCode = -1;
mSignatures = null;
@@ -621,7 +626,9 @@
final ApkLite apk;
try {
- apk = PackageParser.parseApkLite(file, PackageParser.PARSE_COLLECT_CERTIFICATES);
+ // TODO: fix b/25118622; always use PARSE_COLLECT_CERTIFICATES
+ final int parseFlags = quickInstall ? 0 : PackageParser.PARSE_COLLECT_CERTIFICATES;
+ apk = PackageParser.parseApkLite(file, parseFlags);
} catch (PackageParserException e) {
throw PackageManagerException.from(e);
}
@@ -742,6 +749,7 @@
}
private void assertApkConsistent(String tag, ApkLite apk) throws PackageManagerException {
+ final boolean quickInstall = (params.installFlags & PackageManager.INSTALL_QUICK) != 0;
if (!mPackageName.equals(apk.packageName)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " package "
+ apk.packageName + " inconsistent with " + mPackageName);
@@ -751,7 +759,8 @@
+ " version code " + apk.versionCode + " inconsistent with "
+ mVersionCode);
}
- if (!Signature.areExactMatch(mSignatures, apk.signatures)) {
+ // TODO: fix b/25118622; don't bypass signature check
+ if (!quickInstall && !Signature.areExactMatch(mSignatures, apk.signatures)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
tag + " signatures are inconsistent");
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f7f38db..64628aa 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -162,6 +162,7 @@
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.os.SELinux;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -428,8 +429,6 @@
final Context mContext;
final boolean mFactoryTest;
final boolean mOnlyCore;
- final boolean mLazyDexOpt;
- final long mDexOptLRUThresholdInMills;
final DisplayMetrics mMetrics;
final int mDefParseFlags;
final String[] mSeparateProcesses;
@@ -1753,11 +1752,21 @@
PermissionsState permissionsState = sb.getPermissionsState();
- for (String permission : pkg.requestedPermissions) {
- BasePermission bp = mSettings.mPermissions.get(permission);
- if (bp != null && bp.isRuntime() && (grantedPermissions == null
- || ArrayUtils.contains(grantedPermissions, permission))) {
- permissionsState.grantRuntimePermission(bp, userId);
+ final int immutableFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED
+ | PackageManager.FLAG_PERMISSION_POLICY_FIXED;
+
+ synchronized (mPackages) {
+ for (String permission : pkg.requestedPermissions) {
+ BasePermission bp = mSettings.mPermissions.get(permission);
+ if (bp != null && (bp.isRuntime() || bp.isDevelopment())
+ && (grantedPermissions == null
+ || ArrayUtils.contains(grantedPermissions, permission))) {
+ final int flags = permissionsState.getPermissionFlags(permission, userId);
+ // Installer cannot change immutable permissions.
+ if ((flags & immutableFlags) == 0) {
+ grantRuntimePermission(pkg.packageName, permission, userId);
+ }
+ }
}
}
}
@@ -1845,7 +1854,6 @@
mContext = context;
mFactoryTest = factoryTest;
mOnlyCore = onlyCore;
- mLazyDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
mMetrics = new DisplayMetrics();
mSettings = new Settings(mPackages);
mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
@@ -1861,15 +1869,6 @@
mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
- // TODO: add a property to control this?
- long dexOptLRUThresholdInMinutes;
- if (mLazyDexOpt) {
- dexOptLRUThresholdInMinutes = 30; // only last 30 minutes of apps for eng builds.
- } else {
- dexOptLRUThresholdInMinutes = 7 * 24 * 60; // apps used in the 7 days for users.
- }
- mDexOptLRUThresholdInMills = dexOptLRUThresholdInMinutes * 60 * 1000;
-
String separateProcesses = SystemProperties.get("debug.separate_processes");
if (separateProcesses != null && separateProcesses.length() > 0) {
if ("*".equals(separateProcesses)) {
@@ -1964,31 +1963,14 @@
// scanning install directories.
final int scanFlags = SCAN_NO_PATHS | SCAN_DEFER_DEX | SCAN_BOOTING | SCAN_INITIAL;
- final ArraySet<String> alreadyDexOpted = new ArraySet<String>();
-
- /**
- * Add everything in the in the boot class path to the
- * list of process files because dexopt will have been run
- * if necessary during zygote startup.
- */
final String bootClassPath = System.getenv("BOOTCLASSPATH");
final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH");
- if (bootClassPath != null) {
- String[] bootClassPathElements = splitString(bootClassPath, ':');
- for (String element : bootClassPathElements) {
- alreadyDexOpted.add(element);
- }
- } else {
+ if (bootClassPath == null) {
Slog.w(TAG, "No BOOTCLASSPATH found!");
}
- if (systemServerClassPath != null) {
- String[] systemServerClassPathElements = splitString(systemServerClassPath, ':');
- for (String element : systemServerClassPathElements) {
- alreadyDexOpted.add(element);
- }
- } else {
+ if (systemServerClassPath == null) {
Slog.w(TAG, "No SYSTEMSERVERCLASSPATH found!");
}
@@ -2015,7 +1997,6 @@
try {
int dexoptNeeded = DexFile.getDexOptNeeded(lib, null, dexCodeInstructionSet, false);
if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
- alreadyDexOpted.add(lib);
mInstaller.dexopt(lib, Process.SYSTEM_UID, dexCodeInstructionSet,
dexoptNeeded, DEXOPT_PUBLIC /*dexFlags*/);
}
@@ -2031,52 +2012,6 @@
File frameworkDir = new File(Environment.getRootDirectory(), "framework");
- // Gross hack for now: we know this file doesn't contain any
- // code, so don't dexopt it to avoid the resulting log spew.
- alreadyDexOpted.add(frameworkDir.getPath() + "/framework-res.apk");
-
- // Gross hack for now: we know this file is only part of
- // the boot class path for art, so don't dexopt it to
- // avoid the resulting log spew.
- alreadyDexOpted.add(frameworkDir.getPath() + "/core-libart.jar");
-
- /**
- * There are a number of commands implemented in Java, which
- * we currently need to do the dexopt on so that they can be
- * run from a non-root shell.
- */
- String[] frameworkFiles = frameworkDir.list();
- if (frameworkFiles != null) {
- // TODO: We could compile these only for the most preferred ABI. We should
- // first double check that the dex files for these commands are not referenced
- // by other system apps.
- for (String dexCodeInstructionSet : dexCodeInstructionSets) {
- for (int i=0; i<frameworkFiles.length; i++) {
- File libPath = new File(frameworkDir, frameworkFiles[i]);
- String path = libPath.getPath();
- // Skip the file if we already did it.
- if (alreadyDexOpted.contains(path)) {
- continue;
- }
- // Skip the file if it is not a type we want to dexopt.
- if (!path.endsWith(".apk") && !path.endsWith(".jar")) {
- continue;
- }
- try {
- int dexoptNeeded = DexFile.getDexOptNeeded(path, null, dexCodeInstructionSet, false);
- if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
- mInstaller.dexopt(path, Process.SYSTEM_UID, dexCodeInstructionSet,
- dexoptNeeded, DEXOPT_PUBLIC /*dexFlags*/);
- }
- } catch (FileNotFoundException e) {
- Slog.w(TAG, "Jar not found: " + path);
- } catch (IOException e) {
- Slog.w(TAG, "Exception reading jar: " + path, e);
- }
- }
- }
- }
-
final VersionInfo ver = mSettings.getInternalVersion();
mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint);
// when upgrading from pre-M, promote system app permissions from install to runtime
@@ -2276,7 +2211,7 @@
mSettings.enableSystemPackageLPw(packageName);
try {
- scanPackageTracedLI(scanFile, reparseFlags, scanFlags, 0, UserHandle.SYSTEM);
+ scanPackageTracedLI(scanFile, reparseFlags, scanFlags, 0, null);
} catch (PackageManagerException e) {
Slog.e(TAG, "Failed to parse original system package: "
+ e.getMessage());
@@ -2295,7 +2230,6 @@
// the rest of the commands above) because there's precious little we
// can do about it. A settings error is reported, though.
adjustCpuAbisForSharedUserLPw(setting.packages, null /* scanned package */,
- false /* force dexopt */, false /* defer dexopt */,
false /* boot complete */);
}
@@ -2321,7 +2255,7 @@
+ mSdkVersion + "; regranting permissions for internal storage");
updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL;
}
- updatePermissionsLPw(null, null, updateFlags);
+ updatePermissionsLPw(null, null, StorageManager.UUID_PRIVATE_INTERNAL, updateFlags);
ver.sdkVersion = mSdkVersion;
// If this is the first boot or an update from pre-M, and it is a normal
@@ -2380,6 +2314,11 @@
// tidy.
Runtime.getRuntime().gc();
+ // The initial scanning above does many calls into installd while
+ // holding the mPackages lock, but we're mostly interested in yelling
+ // once we have a booted system.
+ mInstaller.setWarnIfHeld(mPackages);
+
// Expose private service for system components to use.
LocalServices.addService(PackageManagerInternal.class, new PackageManagerInternalImpl());
}
@@ -2772,25 +2711,38 @@
@Override
public int getPackageUid(String packageName, int userId) {
+ return getPackageUidEtc(packageName, 0, userId);
+ }
+
+ @Override
+ public int getPackageUidEtc(String packageName, int flags, int userId) {
if (!sUserManager.exists(userId)) return -1;
enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get package uid");
// reader
synchronized (mPackages) {
- PackageParser.Package p = mPackages.get(packageName);
- if(p != null) {
+ final PackageParser.Package p = mPackages.get(packageName);
+ if (p != null) {
return UserHandle.getUid(userId, p.applicationInfo.uid);
}
- PackageSetting ps = mSettings.mPackages.get(packageName);
- if((ps == null) || (ps.pkg == null) || (ps.pkg.applicationInfo == null)) {
- return -1;
+ if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) {
+ final PackageSetting ps = mSettings.mPackages.get(packageName);
+ if (ps != null) {
+ return UserHandle.getUid(userId, ps.appId);
+ }
}
- return UserHandle.getUid(userId, ps.pkg.applicationInfo.uid);
}
+
+ return -1;
}
@Override
- public int[] getPackageGids(String packageName, int userId) throws RemoteException {
+ public int[] getPackageGids(String packageName, int userId) {
+ return getPackageGidsEtc(packageName, 0, userId);
+ }
+
+ @Override
+ public int[] getPackageGidsEtc(String packageName, int flags, int userId) {
if (!sUserManager.exists(userId)) {
return null;
}
@@ -2800,14 +2752,17 @@
// reader
synchronized (mPackages) {
- PackageParser.Package p = mPackages.get(packageName);
- if (DEBUG_PACKAGE_INFO) {
- Log.v(TAG, "getPackageGids" + packageName + ": " + p);
- }
+ final PackageParser.Package p = mPackages.get(packageName);
if (p != null) {
PackageSetting ps = (PackageSetting) p.mExtras;
return ps.getPermissionsState().computeGids(userId);
}
+ if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) {
+ final PackageSetting ps = mSettings.mPackages.get(packageName);
+ if (ps != null) {
+ return ps.getPermissionsState().computeGids(userId);
+ }
+ }
}
return null;
@@ -2919,9 +2874,8 @@
pkg.applicationInfo.packageName = packageName;
pkg.applicationInfo.flags = ps.pkgFlags | ApplicationInfo.FLAG_IS_DATA_ONLY;
pkg.applicationInfo.privateFlags = ps.pkgPrivateFlags;
- pkg.applicationInfo.dataDir = Environment
- .getDataUserPackageDirectory(ps.volumeUuid, userId, packageName)
- .getAbsolutePath();
+ pkg.applicationInfo.uid = ps.appId;
+ pkg.applicationInfo.initForUser(userId);
pkg.applicationInfo.primaryCpuAbi = ps.primaryCpuAbiString;
pkg.applicationInfo.secondaryCpuAbi = ps.secondaryCpuAbiString;
}
@@ -3022,15 +2976,43 @@
}
}
+ /**
+ * Augment the given flags depending on current user running state. This is
+ * purposefully done before acquiring {@link #mPackages} lock.
+ */
+ private int augmentFlagsForUser(int flags, int userId) {
+ if (SystemProperties.getBoolean(StorageManager.PROP_HAS_FBE, false)) {
+ final IMountService mount = IMountService.Stub
+ .asInterface(ServiceManager.getService(Context.STORAGE_SERVICE));
+ if (mount == null) {
+ // We must be early in boot, so the best we can do is assume the
+ // user is fully running.
+ return flags;
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (!mount.isUserKeyUnlocked(userId)) {
+ flags |= PackageManager.FLAG_USER_RUNNING_WITH_AMNESIA;
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ return flags;
+ }
+
@Override
public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
+ flags = augmentFlagsForUser(flags, userId);
enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get activity info");
synchronized (mPackages) {
PackageParser.Activity a = mActivities.mActivities.get(component);
if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a);
- if (a != null && mSettings.isEnabledLPr(a.info, flags, userId)) {
+ if (a != null && mSettings.isEnabledAndVisibleLPr(a.info, flags, userId)) {
PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
if (ps == null) return null;
return PackageParser.generateActivityInfo(a, flags, ps.readUserState(userId),
@@ -3069,12 +3051,13 @@
@Override
public ActivityInfo getReceiverInfo(ComponentName component, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
+ flags = augmentFlagsForUser(flags, userId);
enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get receiver info");
synchronized (mPackages) {
PackageParser.Activity a = mReceivers.mActivities.get(component);
if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getReceiverInfo " + component + ": " + a);
- if (a != null && mSettings.isEnabledLPr(a.info, flags, userId)) {
+ if (a != null && mSettings.isEnabledAndVisibleLPr(a.info, flags, userId)) {
PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
if (ps == null) return null;
return PackageParser.generateActivityInfo(a, flags, ps.readUserState(userId),
@@ -3087,12 +3070,13 @@
@Override
public ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
+ flags = augmentFlagsForUser(flags, userId);
enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get service info");
synchronized (mPackages) {
PackageParser.Service s = mServices.mServices.get(component);
if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getServiceInfo " + component + ": " + s);
- if (s != null && mSettings.isEnabledLPr(s.info, flags, userId)) {
+ if (s != null && mSettings.isEnabledAndVisibleLPr(s.info, flags, userId)) {
PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
if (ps == null) return null;
return PackageParser.generateServiceInfo(s, flags, ps.readUserState(userId),
@@ -3105,12 +3089,13 @@
@Override
public ProviderInfo getProviderInfo(ComponentName component, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
+ flags = augmentFlagsForUser(flags, userId);
enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get provider info");
synchronized (mPackages) {
PackageParser.Provider p = mProviders.mProviders.get(component);
if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getProviderInfo " + component + ": " + p);
- if (p != null && mSettings.isEnabledLPr(p.info, flags, userId)) {
+ if (p != null && mSettings.isEnabledAndVisibleLPr(p.info, flags, userId)) {
PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
if (ps == null) return null;
return PackageParser.generateProviderInfo(p, flags, ps.readUserState(userId),
@@ -3558,7 +3543,8 @@
killUid(appId, userId, KILL_APP_REASON_GIDS_CHANGED);
}
});
- } break;
+ }
+ break;
}
mOnPermissionChangeListeners.onPermissionsChanged(uid);
@@ -3752,12 +3738,6 @@
PermissionsState permissionsState = sb.getPermissionsState();
- // Only the package manager can change flags for system component permissions.
- final int flags = permissionsState.getPermissionFlags(bp.name, userId);
- if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
- return;
- }
-
boolean hadState = permissionsState.getRuntimePermissionState(name, userId) != null;
if (permissionsState.updatePermissionFlags(bp, userId, flagMask, flagValues)) {
@@ -4207,6 +4187,7 @@
public ResolveInfo resolveIntent(Intent intent, String resolvedType,
int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
+ flags = augmentFlagsForUser(flags, userId);
enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "resolve intent");
List<ResolveInfo> query = queryIntentActivities(intent, resolvedType, flags, userId);
return chooseBestActivity(intent, resolvedType, flags, query, userId);
@@ -4348,10 +4329,12 @@
return null;
}
+ // TODO: handle preferred activities missing while user has amnesia
ResolveInfo findPreferredActivity(Intent intent, String resolvedType, int flags,
List<ResolveInfo> query, int priority, boolean always,
boolean removeMatches, boolean debug, int userId) {
if (!sUserManager.exists(userId)) return null;
+ flags = augmentFlagsForUser(flags, userId);
// writer
synchronized (mPackages) {
if (intent.getSelector() != null) {
@@ -4550,6 +4533,7 @@
public List<ResolveInfo> queryIntentActivities(Intent intent,
String resolvedType, int flags, int userId) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
+ flags = augmentFlagsForUser(flags, userId);
enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "query intent activities");
ComponentName comp = intent.getComponent();
if (comp == null) {
@@ -4811,18 +4795,13 @@
// First try to add the "always" resolution(s) for the current user, if any
if (alwaysList.size() > 0) {
result.addAll(alwaysList);
- // if there is an "always" for the parent user, add it.
- } else if (xpDomainInfo != null && xpDomainInfo.bestDomainVerificationStatus
- == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) {
- result.add(xpDomainInfo.resolveInfo);
} else {
// Add all undefined apps as we want them to appear in the disambiguation dialog.
result.addAll(undefinedList);
+ // Maybe add one for the other profile.
if (xpDomainInfo != null && (
xpDomainInfo.bestDomainVerificationStatus
- == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED
- || xpDomainInfo.bestDomainVerificationStatus
- == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK)) {
+ != INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER)) {
result.add(xpDomainInfo.resolveInfo);
}
includeBrowser = true;
@@ -5020,6 +4999,7 @@
Intent[] specifics, String[] specificTypes, Intent intent,
String resolvedType, int flags, int userId) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
+ flags = augmentFlagsForUser(flags, userId);
enforceCrossUserPermission(Binder.getCallingUid(), userId, false,
false, "query intent activity options");
final String resultsAction = intent.getAction();
@@ -5192,6 +5172,7 @@
public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags,
int userId) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
+ flags = augmentFlagsForUser(flags, userId);
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
@@ -5227,8 +5208,9 @@
@Override
public ResolveInfo resolveService(Intent intent, String resolvedType, int flags, int userId) {
- List<ResolveInfo> query = queryIntentServices(intent, resolvedType, flags, userId);
if (!sUserManager.exists(userId)) return null;
+ flags = augmentFlagsForUser(flags, userId);
+ List<ResolveInfo> query = queryIntentServices(intent, resolvedType, flags, userId);
if (query != null) {
if (query.size() >= 1) {
// If there is more than one service with the same priority,
@@ -5243,6 +5225,7 @@
public List<ResolveInfo> queryIntentServices(Intent intent, String resolvedType, int flags,
int userId) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
+ flags = augmentFlagsForUser(flags, userId);
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
@@ -5280,6 +5263,7 @@
public List<ResolveInfo> queryIntentContentProviders(
Intent intent, String resolvedType, int flags, int userId) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
+ flags = augmentFlagsForUser(flags, userId);
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
@@ -5396,6 +5380,7 @@
public ParceledListSlice<PackageInfo> getPackagesHoldingPermissions(
String[] permissions, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
+ flags = augmentFlagsForUser(flags, userId);
final boolean listUninstalled = (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0;
// writer
@@ -5423,6 +5408,7 @@
@Override
public ParceledListSlice<ApplicationInfo> getInstalledApplications(int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
+ flags = augmentFlagsForUser(flags, userId);
final boolean listUninstalled = (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0;
// writer
@@ -5489,6 +5475,7 @@
@Override
public ProviderInfo resolveContentProvider(String name, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
+ flags = augmentFlagsForUser(flags, userId);
// reader
synchronized (mPackages) {
final PackageParser.Provider provider = mProvidersByAuthority.get(name);
@@ -5496,7 +5483,7 @@
? mSettings.mPackages.get(provider.owner.packageName)
: null;
return ps != null
- && mSettings.isEnabledLPr(provider.info, flags, userId)
+ && mSettings.isEnabledAndVisibleLPr(provider.info, flags, userId)
&& (!mSafeMode || (provider.info.applicationInfo.flags
&ApplicationInfo.FLAG_SYSTEM) != 0)
? PackageParser.generateProviderInfo(provider, flags,
@@ -5537,12 +5524,15 @@
@Override
public ParceledListSlice<ProviderInfo> queryContentProviders(String processName,
int uid, int flags) {
+ final int userId = processName != null ? UserHandle.getUserId(uid)
+ : UserHandle.getCallingUserId();
+ if (!sUserManager.exists(userId)) return null;
+ flags = augmentFlagsForUser(flags, userId);
+
ArrayList<ProviderInfo> finalList = null;
// reader
synchronized (mPackages) {
final Iterator<PackageParser.Provider> i = mProviders.mProviders.values().iterator();
- final int userId = processName != null ?
- UserHandle.getUserId(uid) : UserHandle.getCallingUserId();
while (i.hasNext()) {
final PackageParser.Provider p = i.next();
PackageSetting ps = mSettings.mPackages.get(p.owner.packageName);
@@ -5550,7 +5540,7 @@
&& (processName == null
|| (p.info.processName.equals(processName)
&& UserHandle.isSameApp(p.info.applicationInfo.uid, uid)))
- && mSettings.isEnabledLPr(p.info, flags, userId)
+ && mSettings.isEnabledAndVisibleLPr(p.info, flags, userId)
&& (!mSafeMode
|| (p.info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0)) {
if (finalList == null) {
@@ -5690,7 +5680,7 @@
}
try {
scanPackageTracedLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,
- scanFlags, currentTime, UserHandle.SYSTEM);
+ scanFlags, currentTime, null);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage());
@@ -5796,8 +5786,6 @@
*/
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
long currentTime, UserHandle user) throws PackageManagerException {
- Preconditions.checkNotNull(user);
-
if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
parseFlags |= mDefParseFlags;
PackageParser pp = new PackageParser();
@@ -6085,8 +6073,8 @@
}
@Override
- public void performBootDexOpt() {
- enforceSystemOrRoot("Only the system can request dexopt be performed");
+ public void performFstrimIfNeeded() {
+ enforceSystemOrRoot("Only the system can request fstrim");
// Before everything else, see whether we need to fstrim.
try {
@@ -6127,98 +6115,6 @@
} catch (RemoteException e) {
// Can't happen; MountService is local
}
-
- final ArraySet<PackageParser.Package> pkgs;
- synchronized (mPackages) {
- pkgs = mPackageDexOptimizer.clearDeferredDexOptPackages();
- }
-
- if (pkgs != null) {
- // Sort apps by importance for dexopt ordering. Important apps are given more priority
- // in case the device runs out of space.
- ArrayList<PackageParser.Package> sortedPkgs = new ArrayList<PackageParser.Package>();
- // Give priority to core apps.
- for (Iterator<PackageParser.Package> it = pkgs.iterator(); it.hasNext();) {
- PackageParser.Package pkg = it.next();
- if (pkg.coreApp) {
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Adding core app " + sortedPkgs.size() + ": " + pkg.packageName);
- }
- sortedPkgs.add(pkg);
- it.remove();
- }
- }
- // Give priority to system apps that listen for pre boot complete.
- Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
- ArraySet<String> pkgNames = getPackageNamesForIntent(intent, UserHandle.USER_SYSTEM);
- for (Iterator<PackageParser.Package> it = pkgs.iterator(); it.hasNext();) {
- PackageParser.Package pkg = it.next();
- if (pkgNames.contains(pkg.packageName)) {
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Adding pre boot system app " + sortedPkgs.size() + ": " + pkg.packageName);
- }
- sortedPkgs.add(pkg);
- it.remove();
- }
- }
- // Filter out packages that aren't recently used.
- filterRecentlyUsedApps(pkgs);
- // Add all remaining apps.
- for (PackageParser.Package pkg : pkgs) {
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Adding app " + sortedPkgs.size() + ": " + pkg.packageName);
- }
- sortedPkgs.add(pkg);
- }
-
- // If we want to be lazy, filter everything that wasn't recently used.
- if (mLazyDexOpt) {
- filterRecentlyUsedApps(sortedPkgs);
- }
-
- int i = 0;
- int total = sortedPkgs.size();
- File dataDir = Environment.getDataDirectory();
- long lowThreshold = StorageManager.from(mContext).getStorageLowBytes(dataDir);
- if (lowThreshold == 0) {
- throw new IllegalStateException("Invalid low memory threshold");
- }
- for (PackageParser.Package pkg : sortedPkgs) {
- long usableSpace = dataDir.getUsableSpace();
- if (usableSpace < lowThreshold) {
- Log.w(TAG, "Not running dexopt on remaining apps due to low memory: " + usableSpace);
- break;
- }
- performBootDexOpt(pkg, ++i, total);
- }
- }
- }
-
- private void filterRecentlyUsedApps(Collection<PackageParser.Package> pkgs) {
- // Filter out packages that aren't recently used.
- //
- // The exception is first boot of a non-eng device (aka !mLazyDexOpt), which
- // should do a full dexopt.
- if (mLazyDexOpt || (!isFirstBoot() && mPackageUsage.isHistoricalPackageUsageAvailable())) {
- int total = pkgs.size();
- int skipped = 0;
- long now = System.currentTimeMillis();
- for (Iterator<PackageParser.Package> i = pkgs.iterator(); i.hasNext();) {
- PackageParser.Package pkg = i.next();
- long then = pkg.mLastPackageUsageTimeInMills;
- if (then + mDexOptLRUThresholdInMills < now) {
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Skipping dexopt of " + pkg.packageName + " last resumed: " +
- ((then == 0) ? "never" : new Date(then)));
- }
- i.remove();
- skipped++;
- }
- }
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Skipped optimizing " + skipped + " of " + total);
- }
- }
}
private ArraySet<String> getPackageNamesForIntent(Intent intent, int userId) {
@@ -6237,54 +6133,36 @@
return pkgNames;
}
- private void performBootDexOpt(PackageParser.Package pkg, int curr, int total) {
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Optimizing app " + curr + " of " + total + ": " + pkg.packageName);
- }
- if (!isFirstBoot()) {
- try {
- ActivityManagerNative.getDefault().showBootMessage(
- mContext.getResources().getString(R.string.android_upgrading_apk,
- curr, total), true);
- } catch (RemoteException e) {
+ @Override
+ public void notifyPackageUse(String packageName) {
+ synchronized (mPackages) {
+ PackageParser.Package p = mPackages.get(packageName);
+ if (p == null) {
+ return;
}
- }
- PackageParser.Package p = pkg;
- synchronized (mInstallLock) {
- mPackageDexOptimizer.performDexOpt(p, null /* instruction sets */,
- false /* force dex */, false /* defer */, true /* include dependencies */,
- false /* boot complete */, false /*useJit*/);
+ p.mLastPackageUsageTimeInMills = System.currentTimeMillis();
}
}
@Override
public boolean performDexOptIfNeeded(String packageName, String instructionSet) {
- return performDexOptTraced(packageName, instructionSet, false);
+ return performDexOptTraced(packageName, instructionSet);
}
- public boolean performDexOpt(
- String packageName, String instructionSet, boolean backgroundDexopt) {
- return performDexOptTraced(packageName, instructionSet, backgroundDexopt);
+ public boolean performDexOpt(String packageName, String instructionSet) {
+ return performDexOptTraced(packageName, instructionSet);
}
- private boolean performDexOptTraced(
- String packageName, String instructionSet, boolean backgroundDexopt) {
+ private boolean performDexOptTraced(String packageName, String instructionSet) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
try {
- return performDexOptInternal(packageName, instructionSet, backgroundDexopt);
+ return performDexOptInternal(packageName, instructionSet);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
- private boolean performDexOptInternal(
- String packageName, String instructionSet, boolean backgroundDexopt) {
- boolean dexopt = mLazyDexOpt || backgroundDexopt;
- boolean updateUsage = !backgroundDexopt; // Don't update usage if this is just a backgroundDexopt
- if (!dexopt && !updateUsage) {
- // We aren't going to dexopt or update usage, so bail early.
- return false;
- }
+ private boolean performDexOptInternal(String packageName, String instructionSet) {
PackageParser.Package p;
final String targetInstructionSet;
synchronized (mPackages) {
@@ -6292,14 +6170,7 @@
if (p == null) {
return false;
}
- if (updateUsage) {
- p.mLastPackageUsageTimeInMills = System.currentTimeMillis();
- }
mPackageUsage.write(false);
- if (!dexopt) {
- // We aren't going to dexopt, so bail early.
- return false;
- }
targetInstructionSet = instructionSet != null ? instructionSet :
getPrimaryInstructionSet(p.applicationInfo);
@@ -6312,8 +6183,7 @@
synchronized (mInstallLock) {
final String[] instructionSets = new String[] { targetInstructionSet };
int result = mPackageDexOptimizer.performDexOpt(p, instructionSets,
- false /* forceDex */, false /* defer */, true /* inclDependencies */,
- true /* boot complete */, false /*useJit*/);
+ true /* inclDependencies */);
return result == PackageDexOptimizer.DEX_OPT_PERFORMED;
}
} finally {
@@ -6363,8 +6233,7 @@
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
final int res = mPackageDexOptimizer.performDexOpt(pkg, instructionSets,
- true /*forceDex*/, false /* defer */, true /* inclDependencies */,
- true /* boot complete */, false /*useJit*/);
+ true /* inclDependencies */);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) {
@@ -6924,17 +6793,10 @@
pkg.applicationInfo.processName,
pkg.applicationInfo.uid);
- File dataPath;
- if (mPlatformPackage == pkg) {
- // The system package is special.
- dataPath = new File(Environment.getDataDirectory(), "system");
-
- pkg.applicationInfo.dataDir = dataPath.getPath();
-
- } else {
+ if (pkg != mPlatformPackage) {
// This is a normal package, need to make its data directory.
- dataPath = Environment.getDataUserPackageDirectory(pkg.volumeUuid,
- UserHandle.USER_SYSTEM, pkg.packageName);
+ final File dataPath = Environment.getDataUserCredentialEncryptedPackageDirectory(
+ pkg.volumeUuid, UserHandle.USER_SYSTEM, pkg.packageName);
boolean uidError = false;
if (dataPath.exists()) {
@@ -7023,7 +6885,7 @@
}
}
}
- pkg.applicationInfo.dataDir = dataPath.getPath();
+
if (mShouldRestoreconData) {
Slog.i(TAG, "SELinux relabeling of " + pkg.packageName + " issued.");
mInstaller.restoreconData(pkg.volumeUuid, pkg.packageName,
@@ -7042,15 +6904,11 @@
throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
"Unable to create data dirs [errorCode=" + ret + "]");
}
-
- if (dataPath.exists()) {
- pkg.applicationInfo.dataDir = dataPath.getPath();
- } else {
- Slog.w(TAG, "Unable to create data directory: " + dataPath);
- pkg.applicationInfo.dataDir = null;
- }
}
+ // Get all of our default paths setup
+ pkg.applicationInfo.initForUser(UserHandle.USER_SYSTEM);
+
pkgSetting.uidError = uidError;
}
@@ -7165,7 +7023,7 @@
" secondary=" + pkg.applicationInfo.secondaryCpuAbi);
}
- if ((scanFlags&SCAN_BOOTING) == 0 && pkgSetting.sharedUser != null) {
+ if ((scanFlags & SCAN_BOOTING) == 0 && pkgSetting.sharedUser != null) {
// We don't do this here during boot because we can do it all
// at once after scanning all existing packages.
//
@@ -7173,21 +7031,9 @@
// we can avoid redundant dexopts, and also to make sure we've got the
// code and package path correct.
adjustCpuAbisForSharedUserLPw(pkgSetting.sharedUser.packages,
- pkg, forceDex, (scanFlags & SCAN_DEFER_DEX) != 0, true /* boot complete */);
+ pkg, true /* boot complete */);
}
- if ((scanFlags & SCAN_NO_DEX) == 0) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
-
- int result = mPackageDexOptimizer.performDexOpt(pkg, null /* instruction sets */,
- forceDex, (scanFlags & SCAN_DEFER_DEX) != 0, false /* inclDependencies */,
- (scanFlags & SCAN_BOOTING) == 0, false /*useJit*/);
-
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
- throw new PackageManagerException(INSTALL_FAILED_DEXOPT, "scanPackageLI");
- }
- }
if (mFactoryTest && pkg.requestedPermissions.contains(
android.Manifest.permission.FACTORY_TEST)) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_FACTORY_TEST;
@@ -7239,7 +7085,7 @@
+ name + " that is not declared on system image; skipping");
}
}
- if ((scanFlags&SCAN_BOOTING) == 0) {
+ if ((scanFlags & SCAN_BOOTING) == 0) {
// If we are not booting, we need to update any applications
// that are clients of our shared library. If we are booting,
// this will all be done once the scan is complete.
@@ -7249,30 +7095,6 @@
}
}
- // We also need to dexopt any apps that are dependent on this library. Note that
- // if these fail, we should abort the install since installing the library will
- // result in some apps being broken.
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
- try {
- if (clientLibPkgs != null) {
- if ((scanFlags & SCAN_NO_DEX) == 0) {
- for (int i = 0; i < clientLibPkgs.size(); i++) {
- PackageParser.Package clientPkg = clientLibPkgs.get(i);
- int result = mPackageDexOptimizer.performDexOpt(clientPkg,
- null /* instruction sets */, forceDex,
- (scanFlags & SCAN_DEFER_DEX) != 0, false,
- (scanFlags & SCAN_BOOTING) == 0, false /*useJit*/);
- if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
- throw new PackageManagerException(INSTALL_FAILED_DEXOPT,
- "scanPackageLI failed to dexopt clientLibPkgs");
- }
- }
- }
- }
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
-
// Request the ActivityManager to kill the process(only for existing packages)
// so that we do not end up in a confused state while the user is still using the older
// version of the application while the new one gets installed.
@@ -7604,6 +7426,8 @@
a.info.splitSourceDirs = pkg.applicationInfo.splitSourceDirs;
a.info.splitPublicSourceDirs = pkg.applicationInfo.splitPublicSourceDirs;
a.info.dataDir = pkg.applicationInfo.dataDir;
+ a.info.deviceEncryptedDataDir = pkg.applicationInfo.deviceEncryptedDataDir;
+ a.info.credentialEncryptedDataDir = pkg.applicationInfo.credentialEncryptedDataDir;
// TODO: Update instrumentation.nativeLibraryDir as well ? Does it
// need other information about the application, like the ABI and what not ?
@@ -7685,8 +7509,8 @@
// We would never need to extract libs for forward-locked and external packages,
// since the container service will do it for us. We shouldn't attempt to
// extract libs from system app when it was not updated.
- if (pkg.isForwardLocked() || isExternal(pkg) ||
- (isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) ) {
+ if (pkg.isForwardLocked() || pkg.applicationInfo.isExternalAsec() ||
+ (isSystemApp(pkg) && !pkg.isUpdatedSystemApp())) {
extractLibs = false;
}
@@ -7817,8 +7641,7 @@
* adds unnecessary complexity.
*/
private void adjustCpuAbisForSharedUserLPw(Set<PackageSetting> packagesForUser,
- PackageParser.Package scannedPackage, boolean forceDexOpt, boolean deferDexOpt,
- boolean bootComplete) {
+ PackageParser.Package scannedPackage, boolean bootComplete) {
String requiredInstructionSet = null;
if (scannedPackage != null && scannedPackage.applicationInfo.primaryCpuAbi != null) {
requiredInstructionSet = VMRuntime.getInstructionSet(
@@ -7880,22 +7703,8 @@
if (ps.pkg != null && ps.pkg.applicationInfo != null) {
ps.pkg.applicationInfo.primaryCpuAbi = adjustedAbi;
Slog.i(TAG, "Adjusting ABI for : " + ps.name + " to " + adjustedAbi);
-
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
-
- int result = mPackageDexOptimizer.performDexOpt(ps.pkg,
- null /* instruction sets */, forceDexOpt, deferDexOpt, true,
- bootComplete, false /*useJit*/);
-
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
- ps.primaryCpuAbiString = null;
- ps.pkg.applicationInfo.primaryCpuAbi = null;
- return;
- } else {
- mInstaller.rmdex(ps.codePathString,
- getDexCodeInstructionSet(getPreferredInstructionSet()));
- }
+ mInstaller.rmdex(ps.codePathString,
+ getDexCodeInstructionSet(getPreferredInstructionSet()));
}
}
}
@@ -7967,7 +7776,7 @@
final String codePath = pkg.codePath;
final File codeFile = new File(codePath);
final boolean bundledApp = info.isSystemApp() && !info.isUpdatedSystemApp();
- final boolean asecApp = info.isForwardLocked() || isExternal(info);
+ final boolean asecApp = info.isForwardLocked() || info.isExternalAsec();
info.nativeLibraryRootDir = null;
info.nativeLibraryRootRequiresIsa = false;
@@ -8365,8 +8174,14 @@
static final int UPDATE_PERMISSIONS_REPLACE_PKG = 1<<1;
static final int UPDATE_PERMISSIONS_REPLACE_ALL = 1<<2;
+ private void updatePermissionsLPw(String changingPkg, PackageParser.Package pkgInfo,
+ int flags) {
+ final String volumeUuid = (pkgInfo != null) ? getVolumeUuidForPackage(pkgInfo) : null;
+ updatePermissionsLPw(changingPkg, pkgInfo, volumeUuid, flags);
+ }
+
private void updatePermissionsLPw(String changingPkg,
- PackageParser.Package pkgInfo, int flags) {
+ PackageParser.Package pkgInfo, String replaceVolumeUuid, int flags) {
// Make sure there are no dangling permission trees.
Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator();
while (it.hasNext()) {
@@ -8435,14 +8250,21 @@
if ((flags&UPDATE_PERMISSIONS_ALL) != 0) {
for (PackageParser.Package pkg : mPackages.values()) {
if (pkg != pkgInfo) {
- grantPermissionsLPw(pkg, (flags&UPDATE_PERMISSIONS_REPLACE_ALL) != 0,
- changingPkg);
+ // Only replace for packages on requested volume
+ final String volumeUuid = getVolumeUuidForPackage(pkg);
+ final boolean replace = ((flags & UPDATE_PERMISSIONS_REPLACE_ALL) != 0)
+ && Objects.equals(replaceVolumeUuid, volumeUuid);
+ grantPermissionsLPw(pkg, replace, changingPkg);
}
}
}
if (pkgInfo != null) {
- grantPermissionsLPw(pkgInfo, (flags&UPDATE_PERMISSIONS_REPLACE_PKG) != 0, changingPkg);
+ // Only replace for packages on requested volume
+ final String volumeUuid = getVolumeUuidForPackage(pkgInfo);
+ final boolean replace = ((flags & UPDATE_PERMISSIONS_REPLACE_PKG) != 0)
+ && Objects.equals(replaceVolumeUuid, volumeUuid);
+ grantPermissionsLPw(pkgInfo, replace, changingPkg);
}
}
@@ -8952,7 +8774,7 @@
protected ResolveInfo newResult(PackageParser.ActivityIntentInfo info,
int match, int userId) {
if (!sUserManager.exists(userId)) return null;
- if (!mSettings.isEnabledLPr(info.activity.info, mFlags, userId)) {
+ if (!mSettings.isEnabledAndVisibleLPr(info.activity.info, mFlags, userId)) {
return null;
}
final PackageParser.Activity activity = info.activity;
@@ -9176,7 +8998,7 @@
int match, int userId) {
if (!sUserManager.exists(userId)) return null;
final PackageParser.ServiceIntentInfo info = (PackageParser.ServiceIntentInfo)filter;
- if (!mSettings.isEnabledLPr(info.service.info, mFlags, userId)) {
+ if (!mSettings.isEnabledAndVisibleLPr(info.service.info, mFlags, userId)) {
return null;
}
final PackageParser.Service service = info.service;
@@ -9399,7 +9221,7 @@
if (!sUserManager.exists(userId))
return null;
final PackageParser.ProviderIntentInfo info = filter;
- if (!mSettings.isEnabledLPr(info.provider.info, mFlags, userId)) {
+ if (!mSettings.isEnabledAndVisibleLPr(info.provider.info, mFlags, userId)) {
return null;
}
final PackageParser.Provider provider = info.provider;
@@ -9738,7 +9560,7 @@
IActivityManager am = ActivityManagerNative.getDefault();
final boolean isSystem =
isSystemApp(pkgSetting) || isUpdatedSystemApp(pkgSetting);
- if (isSystem && am.isUserRunning(userId, false)) {
+ if (isSystem && am.isUserRunning(userId, 0)) {
// The just-installed/enabled app is bundled on the system, so presumed
// to be able to run automatically without needing an explicit launch.
// Send it a BOOT_COMPLETED if it would ordinarily have gotten one.
@@ -10087,6 +9909,10 @@
if (!DEFAULT_VERIFY_ENABLE) {
return false;
}
+ // TODO: fix b/25118622; don't bypass verification
+ if (Build.IS_DEBUGGABLE && (installFlags & PackageManager.INSTALL_QUICK) != 0) {
+ return false;
+ }
boolean ensureVerifyAppsEnabled = isUserRestricted(userId, UserManager.ENSURE_VERIFY_APPS);
@@ -12129,7 +11955,7 @@
int oldScanFlags = SCAN_UPDATE_SIGNATURE | SCAN_UPDATE_TIME;
try {
scanPackageTracedLI(restoreFile, oldParseFlags, oldScanFlags, origUpdateTime,
- UserHandle.SYSTEM);
+ null);
} catch (PackageManagerException e) {
Slog.e(TAG, "Failed to restore package : " + pkgName + " after failed upgrade: "
+ e.getMessage());
@@ -12412,6 +12238,7 @@
final boolean forwardLocked = ((installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
final boolean onExternal = (((installFlags & PackageManager.INSTALL_EXTERNAL) != 0)
|| (args.volumeUuid != null));
+ final boolean quickInstall = ((installFlags & PackageManager.INSTALL_QUICK) != 0);
boolean replace = false;
int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE;
if (args.move != null) {
@@ -12427,7 +12254,8 @@
final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
| PackageParser.PARSE_ENFORCE_CODE
| (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)
- | (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0);
+ | (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0)
+ | (quickInstall ? PackageParser.PARSE_SKIP_VERIFICATION : 0);
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setDisplayMetrics(mMetrics);
@@ -12457,7 +12285,6 @@
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
try {
pp.collectCertificates(pkg, parseFlags);
- pp.collectManifestDigest(pkg);
} catch (PackageParserException e) {
res.setError("Failed collect during installPackageLI", e);
return;
@@ -12467,6 +12294,16 @@
/* If the installer passed in a manifest digest, compare it now. */
if (args.manifestDigest != null) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectManifestDigest");
+ try {
+ pp.collectManifestDigest(pkg);
+ } catch (PackageParserException e) {
+ res.setError("Failed collect during installPackageLI", e);
+ return;
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+
if (DEBUG_INSTALL) {
final String parsedManifest = pkg.manifestDigest == null ? "null"
: pkg.manifestDigest.toString();
@@ -12637,19 +12474,6 @@
res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Error deriving application ABI");
return;
}
-
- // Run dexopt before old package gets removed, to minimize time when app is unavailable
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
-
- int result = mPackageDexOptimizer
- .performDexOpt(pkg, null /* instruction sets */, false /* forceDex */,
- false /* defer */, false /* inclDependencies */,
- true /*bootComplete*/, false /*useJit*/);
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
- res.setError(INSTALL_FAILED_DEXOPT, "Dexopt failed for " + pkg.codePath);
- return;
- }
}
if (!args.doRename(res.returnCode, pkg, oldCodePath)) {
@@ -12846,6 +12670,18 @@
return installFlags;
}
+ private String getVolumeUuidForPackage(PackageParser.Package pkg) {
+ if (isExternal(pkg)) {
+ if (TextUtils.isEmpty(pkg.volumeUuid)) {
+ return StorageManager.UUID_PRIMARY_PHYSICAL;
+ } else {
+ return pkg.volumeUuid;
+ }
+ } else {
+ return StorageManager.UUID_PRIVATE_INTERNAL;
+ }
+ }
+
private VersionInfo getSettingsVersionForPackage(PackageParser.Package pkg) {
if (isExternal(pkg)) {
if (TextUtils.isEmpty(pkg.volumeUuid)) {
@@ -13241,8 +13077,7 @@
final PackageParser.Package newPkg;
try {
- newPkg = scanPackageTracedLI(disabledPs.codePath, parseFlags, SCAN_NO_PATHS, 0,
- UserHandle.SYSTEM);
+ newPkg = scanPackageTracedLI(disabledPs.codePath, parseFlags, SCAN_NO_PATHS, 0, null);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to restore system package:" + newPs.name + ": " + e.getMessage());
return false;
@@ -13862,7 +13697,7 @@
if (ps != null) {
libDirRoot = ps.legacyNativeLibraryPathString;
}
- if (p != null && (isExternal(p) || p.isForwardLocked())) {
+ if (p != null && (p.isForwardLocked() || p.applicationInfo.isExternalAsec())) {
final long token = Binder.clearCallingIdentity();
try {
String secureContainerId = cidFromCodePath(p.applicationInfo.getBaseCodePath());
@@ -15027,6 +14862,13 @@
}
@Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out,
+ FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
+ (new PackageManagerShellCommand(this)).exec(
+ this, in, out, err, args, resultReceiver);
+ }
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
@@ -15716,7 +15558,7 @@
if (isMounted) {
if (DEBUG_SD_INSTALL)
Log.i(TAG, "Loading packages");
- loadMediaPackages(processCids, uidArr);
+ loadMediaPackages(processCids, uidArr, externalStorage);
startCleaningPackages();
mInstallerService.onSecureContainersAvailable();
} else {
@@ -15771,7 +15613,8 @@
* the cid is added to list of removeCids. We currently don't delete stale
* containers.
*/
- private void loadMediaPackages(ArrayMap<AsecInstallArgs, String> processCids, int[] uidArr) {
+ private void loadMediaPackages(ArrayMap<AsecInstallArgs, String> processCids, int[] uidArr,
+ boolean externalStorage) {
ArrayList<String> pkgList = new ArrayList<String>();
Set<AsecInstallArgs> keys = processCids.keySet();
@@ -15805,8 +15648,7 @@
synchronized (mInstallLock) {
PackageParser.Package pkg = null;
try {
- pkg = scanPackageTracedLI(new File(codePath), parseFlags, 0, 0,
- UserHandle.SYSTEM);
+ pkg = scanPackageTracedLI(new File(codePath), parseFlags, 0, 0, null);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to scan " + codePath + ": " + e.getMessage());
}
@@ -15844,7 +15686,10 @@
// cases get permissions that the user didn't initially explicitly
// allow... it would be nice to have some better way to handle
// this situation.
- final VersionInfo ver = mSettings.getExternalVersion();
+ final VersionInfo ver = externalStorage ? mSettings.getExternalVersion()
+ : mSettings.getInternalVersion();
+ final String volumeUuid = externalStorage ? StorageManager.UUID_PRIMARY_PHYSICAL
+ : StorageManager.UUID_PRIVATE_INTERNAL;
int updateFlags = UPDATE_PERMISSIONS_ALL;
if (ver.sdkVersion != mSdkVersion) {
@@ -15852,7 +15697,7 @@
+ mSdkVersion + "; regranting permissions for external");
updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL;
}
- updatePermissionsLPw(null, null, updateFlags);
+ updatePermissionsLPw(null, null, volumeUuid, updateFlags);
// Yay, everything is now upgraded
ver.forceCurrent();
@@ -15966,8 +15811,7 @@
synchronized (mInstallLock) {
final PackageParser.Package pkg;
try {
- pkg = scanPackageTracedLI(ps.codePath, parseFlags, SCAN_INITIAL, 0,
- UserHandle.SYSTEM);
+ pkg = scanPackageTracedLI(ps.codePath, parseFlags, SCAN_INITIAL, 0, null);
loaded.add(pkg.applicationInfo);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to scan " + ps.codePath + ": " + e.getMessage());
@@ -15986,7 +15830,7 @@
+ mSdkVersion + "; regranting permissions for " + vol.fsUuid);
updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL;
}
- updatePermissionsLPw(null, null, updateFlags);
+ updatePermissionsLPw(null, null, vol.fsUuid, updateFlags);
// Yay, everything is now upgraded
ver.forceCurrent();
@@ -16076,13 +15920,14 @@
}
}
+ final StorageManager sm = mContext.getSystemService(StorageManager.class);
final UserManager um = mContext.getSystemService(UserManager.class);
for (UserInfo user : um.getUsers()) {
final File userDir = Environment.getDataUserDirectory(volumeUuid, user.id);
if (userDir.exists()) continue;
try {
- UserManagerService.prepareUserDirectory(mContext, volumeUuid, user.id);
+ sm.prepareUserStorage(volumeUuid, user.id, user.serialNumber);
UserManagerService.enforceSerialNumber(userDir, user.serialNumber);
} catch (IOException e) {
Log.wtf(TAG, "Failed to create user directory on " + volumeUuid, e);
@@ -16160,13 +16005,18 @@
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MOVE_PACKAGE, null);
final int moveId = mNextMoveId.getAndIncrement();
- try {
- movePackageInternal(packageName, volumeUuid, moveId);
- } catch (PackageManagerException e) {
- Slog.w(TAG, "Failed to move " + packageName, e);
- mMoveCallbacks.notifyStatusChanged(moveId,
- PackageManager.MOVE_FAILED_INTERNAL_ERROR);
- }
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ movePackageInternal(packageName, volumeUuid, moveId);
+ } catch (PackageManagerException e) {
+ Slog.w(TAG, "Failed to move " + packageName, e);
+ mMoveCallbacks.notifyStatusChanged(moveId,
+ PackageManager.MOVE_FAILED_INTERNAL_ERROR);
+ }
+ }
+ });
return moveId;
}
@@ -16484,7 +16334,7 @@
*/
private void removeUnusedPackagesLILPw(UserManagerService userManager, final int userHandle) {
final boolean DEBUG_CLEAN_APKS = false;
- int [] users = userManager.getUserIdsLPr();
+ int [] users = userManager.getUserIds();
Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
while (psit.hasNext()) {
PackageSetting ps = psit.next();
@@ -16703,27 +16553,6 @@
}
}
- public void getUsageStatsIfNoPackageUsageInfo() {
- if (!mPackageUsage.isHistoricalPackageUsageAvailable()) {
- UsageStatsManager usm = (UsageStatsManager) mContext.getSystemService(Context.USAGE_STATS_SERVICE);
- if (usm == null) {
- throw new IllegalStateException("UsageStatsManager must be initialized");
- }
- long now = System.currentTimeMillis();
- Map<String, UsageStats> stats = usm.queryAndAggregateUsageStats(now - mDexOptLRUThresholdInMills, now);
- for (Map.Entry<String, UsageStats> entry : stats.entrySet()) {
- String packageName = entry.getKey();
- PackageParser.Package pkg = mPackages.get(packageName);
- if (pkg == null) {
- continue;
- }
- UsageStats usage = entry.getValue();
- pkg.mLastPackageUsageTimeInMills = usage.getLastTimeUsed();
- mPackageUsage.mIsHistoricalPackageUsageAvailable = true;
- }
- }
- }
-
/**
* Check and throw if the given before/after packages would be considered a
* downgrade.
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
new file mode 100644
index 0000000..d7176fd
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -0,0 +1,943 @@
+/*
+ * 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.pm;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.IIntentReceiver;
+import android.content.IIntentSender;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.FeatureInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.InstrumentationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.PermissionGroupInfo;
+import android.content.pm.PermissionInfo;
+import android.content.pm.PackageInstaller.SessionInfo;
+import android.content.pm.PackageInstaller.SessionParams;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ShellCommand;
+import android.os.UserHandle;
+import android.text.TextUtils;
+
+import com.android.internal.util.SizedInputStream;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.WeakHashMap;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.TimeUnit;
+
+class PackageManagerShellCommand extends ShellCommand {
+ final IPackageManager mInterface;
+ final private WeakHashMap<String, Resources> mResourceCache =
+ new WeakHashMap<String, Resources>();
+
+ PackageManagerShellCommand(PackageManagerService service) {
+ mInterface = service;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+
+ final PrintWriter pw = getOutPrintWriter();
+ try {
+ switch(cmd) {
+ case "install":
+ return runInstall();
+ case "install-abandon":
+ case "install-destroy":
+ return runInstallAbandon();
+ case "install-commit":
+ return runInstallCommit();
+ case "install-create":
+ return runInstallCreate();
+ case "install-write":
+ return runInstallWrite();
+ case "list":
+ return runList();
+ case "uninstall":
+ return runUninstall();
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ } catch (RemoteException e) {
+ pw.println("Remote exception: " + e);
+ }
+ return -1;
+ }
+
+ private int runInstall() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ final InstallParams params = makeInstallParams();
+ final int sessionId = doCreateSession(params.sessionParams,
+ params.installerPackageName, params.userId);
+
+ final String inPath = getNextArg();
+ if (inPath == null && params.sessionParams.sizeBytes == 0) {
+ pw.println("Error: must either specify a package size or an APK file");
+ return 1;
+ }
+ if (doWriteSession(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk") != 0) {
+ return 1;
+ }
+ if (doCommitSession(sessionId) != 0) {
+ return 1;
+ }
+ return 0;
+ }
+
+ private int runInstallAbandon() throws RemoteException {
+ final int sessionId = Integer.parseInt(getNextArg());
+ return doAbandonSession(sessionId);
+ }
+
+ private int runInstallCommit() throws RemoteException {
+ final int sessionId = Integer.parseInt(getNextArg());
+ return doCommitSession(sessionId);
+ }
+
+ private int runInstallCreate() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ final InstallParams installParams = makeInstallParams();
+ final int sessionId = doCreateSession(installParams.sessionParams,
+ installParams.installerPackageName, installParams.userId);
+
+ // NOTE: adb depends on parsing this string
+ pw.println("Success: created install session [" + sessionId + "]");
+ return 0;
+ }
+
+ private int runInstallWrite() throws RemoteException {
+ long sizeBytes = -1;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ if (opt.equals("-S")) {
+ sizeBytes = Long.parseLong(getNextArg());
+ } else {
+ throw new IllegalArgumentException("Unknown option: " + opt);
+ }
+ }
+
+ final int sessionId = Integer.parseInt(getNextArg());
+ final String splitName = getNextArg();
+ final String path = getNextArg();
+ return doWriteSession(sessionId, path, sizeBytes, splitName);
+ }
+
+ private int runList() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ final String type = getNextArg();
+ if (type == null) {
+ pw.println("Error: didn't specify type of data to list");
+ return -1;
+ }
+ switch(type) {
+ case "features":
+ return runListFeatures();
+ case "instrumentation":
+ return runListInstrumentation();
+ case "libraries":
+ return runListLibraries();
+ case "package":
+ case "packages":
+ return runListPackages(false /*showSourceDir*/);
+ case "permission-groups":
+ return runListPermissionGroups();
+ case "permissions":
+ return runListPermissions();
+ }
+ pw.println("Error: unknown list type '" + type + "'");
+ return -1;
+ }
+
+ private int runListFeatures() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ final List<FeatureInfo> list = new ArrayList<FeatureInfo>();
+ final FeatureInfo[] rawList = mInterface.getSystemAvailableFeatures();
+ for (int i=0; i<rawList.length; i++) {
+ list.add(rawList[i]);
+ }
+
+ // sort by name
+ Collections.sort(list, new Comparator<FeatureInfo>() {
+ public int compare(FeatureInfo o1, FeatureInfo o2) {
+ if (o1.name == o2.name) return 0;
+ if (o1.name == null) return -1;
+ if (o2.name == null) return 1;
+ return o1.name.compareTo(o2.name);
+ }
+ });
+
+ final int count = (list != null) ? list.size() : 0;
+ for (int p = 0; p < count; p++) {
+ FeatureInfo fi = list.get(p);
+ pw.print("feature:");
+ if (fi.name != null) pw.println(fi.name);
+ else pw.println("reqGlEsVersion=0x"
+ + Integer.toHexString(fi.reqGlEsVersion));
+ }
+ return 0;
+ }
+
+ private int runListInstrumentation() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ boolean showSourceDir = false;
+ String targetPackage = null;
+
+ try {
+ String opt;
+ while ((opt = getNextArg()) != null) {
+ switch (opt) {
+ case "-f":
+ showSourceDir = true;
+ break;
+ default:
+ if (opt.charAt(0) != '-') {
+ targetPackage = opt;
+ } else {
+ pw.println("Error: Unknown option: " + opt);
+ return -1;
+ }
+ break;
+ }
+ }
+ } catch (RuntimeException ex) {
+ pw.println("Error: " + ex.toString());
+ return -1;
+ }
+
+ final List<InstrumentationInfo> list =
+ mInterface.queryInstrumentation(targetPackage, 0 /*flags*/);
+
+ // sort by target package
+ Collections.sort(list, new Comparator<InstrumentationInfo>() {
+ public int compare(InstrumentationInfo o1, InstrumentationInfo o2) {
+ return o1.targetPackage.compareTo(o2.targetPackage);
+ }
+ });
+
+ final int count = (list != null) ? list.size() : 0;
+ for (int p = 0; p < count; p++) {
+ final InstrumentationInfo ii = list.get(p);
+ pw.print("instrumentation:");
+ if (showSourceDir) {
+ pw.print(ii.sourceDir);
+ pw.print("=");
+ }
+ final ComponentName cn = new ComponentName(ii.packageName, ii.name);
+ pw.print(cn.flattenToShortString());
+ pw.print(" (target=");
+ pw.print(ii.targetPackage);
+ pw.println(")");
+ }
+ return 0;
+ }
+
+ private int runListLibraries() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ final List<String> list = new ArrayList<String>();
+ final String[] rawList = mInterface.getSystemSharedLibraryNames();
+ for (int i = 0; i < rawList.length; i++) {
+ list.add(rawList[i]);
+ }
+
+ // sort by name
+ Collections.sort(list, new Comparator<String>() {
+ public int compare(String o1, String o2) {
+ if (o1 == o2) return 0;
+ if (o1 == null) return -1;
+ if (o2 == null) return 1;
+ return o1.compareTo(o2);
+ }
+ });
+
+ final int count = (list != null) ? list.size() : 0;
+ for (int p = 0; p < count; p++) {
+ String lib = list.get(p);
+ pw.print("library:");
+ pw.println(lib);
+ }
+ return 0;
+ }
+
+ private int runListPackages(boolean showSourceDir) throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ int getFlags = 0;
+ boolean listDisabled = false, listEnabled = false;
+ boolean listSystem = false, listThirdParty = false;
+ boolean listInstaller = false;
+ int userId = UserHandle.USER_SYSTEM;
+ try {
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "-d":
+ listDisabled = true;
+ break;
+ case "-e":
+ listEnabled = true;
+ break;
+ case "-f":
+ showSourceDir = true;
+ break;
+ case "-i":
+ listInstaller = true;
+ break;
+ case "-l":
+ // old compat
+ break;
+ case "-lf":
+ showSourceDir = true;
+ break;
+ case "-s":
+ listSystem = true;
+ break;
+ case "-u":
+ getFlags |= PackageManager.GET_UNINSTALLED_PACKAGES;
+ break;
+ case "-3":
+ listThirdParty = true;
+ break;
+ case "--user":
+ userId = Integer.parseInt(getNextArg());
+ break;
+ default:
+ pw.println("Error: Unknown option: " + opt);
+ return -1;
+ }
+ }
+ } catch (RuntimeException ex) {
+ pw.println("Error: " + ex.toString());
+ return -1;
+ }
+
+ final String filter = getNextArg();
+
+ @SuppressWarnings("unchecked")
+ final ParceledListSlice<PackageInfo> slice =
+ mInterface.getInstalledPackages(getFlags, userId);
+ final List<PackageInfo> packages = slice.getList();
+
+ final int count = packages.size();
+ for (int p = 0; p < count; p++) {
+ final PackageInfo info = packages.get(p);
+ if (filter != null && !info.packageName.contains(filter)) {
+ continue;
+ }
+ final boolean isSystem =
+ (info.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0;
+ if ((!listDisabled || !info.applicationInfo.enabled) &&
+ (!listEnabled || info.applicationInfo.enabled) &&
+ (!listSystem || isSystem) &&
+ (!listThirdParty || !isSystem)) {
+ pw.print("package:");
+ if (showSourceDir) {
+ pw.print(info.applicationInfo.sourceDir);
+ pw.print("=");
+ }
+ pw.print(info.packageName);
+ if (listInstaller) {
+ pw.print(" installer=");
+ pw.print(mInterface.getInstallerPackageName(info.packageName));
+ }
+ pw.println();
+ }
+ }
+ return 0;
+ }
+
+ private int runListPermissionGroups() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ final List<PermissionGroupInfo> pgs = mInterface.getAllPermissionGroups(0);
+
+ final int count = pgs.size();
+ for (int p = 0; p < count ; p++) {
+ final PermissionGroupInfo pgi = pgs.get(p);
+ pw.print("permission group:");
+ pw.println(pgi.name);
+ }
+ return 0;
+ }
+
+ private int runListPermissions() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ boolean labels = false;
+ boolean groups = false;
+ boolean userOnly = false;
+ boolean summary = false;
+ boolean dangerousOnly = false;
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "-d":
+ dangerousOnly = true;
+ break;
+ case "-f":
+ labels = true;
+ break;
+ case "-g":
+ groups = true;
+ break;
+ case "-s":
+ groups = true;
+ labels = true;
+ summary = true;
+ break;
+ case "-u":
+ userOnly = true;
+ break;
+ default:
+ pw.println("Error: Unknown option: " + opt);
+ return 1;
+ }
+ }
+
+ final ArrayList<String> groupList = new ArrayList<String>();
+ if (groups) {
+ final List<PermissionGroupInfo> infos =
+ mInterface.getAllPermissionGroups(0 /*flags*/);
+ final int count = infos.size();
+ for (int i = 0; i < count; i++) {
+ groupList.add(infos.get(i).name);
+ }
+ groupList.add(null);
+ } else {
+ final String grp = getNextArg();
+ groupList.add(grp);
+ }
+
+ if (dangerousOnly) {
+ pw.println("Dangerous Permissions:");
+ pw.println("");
+ doListPermissions(groupList, groups, labels, summary,
+ PermissionInfo.PROTECTION_DANGEROUS,
+ PermissionInfo.PROTECTION_DANGEROUS);
+ if (userOnly) {
+ pw.println("Normal Permissions:");
+ pw.println("");
+ doListPermissions(groupList, groups, labels, summary,
+ PermissionInfo.PROTECTION_NORMAL,
+ PermissionInfo.PROTECTION_NORMAL);
+ }
+ } else if (userOnly) {
+ pw.println("Dangerous and Normal Permissions:");
+ pw.println("");
+ doListPermissions(groupList, groups, labels, summary,
+ PermissionInfo.PROTECTION_NORMAL,
+ PermissionInfo.PROTECTION_DANGEROUS);
+ } else {
+ pw.println("All Permissions:");
+ pw.println("");
+ doListPermissions(groupList, groups, labels, summary,
+ -10000, 10000);
+ }
+ return 0;
+ }
+
+ private int runUninstall() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ int flags = 0;
+ int userId = UserHandle.USER_ALL;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "-k":
+ flags |= PackageManager.DELETE_KEEP_DATA;
+ break;
+ case "--user":
+ userId = Integer.parseInt(getNextArg());
+ break;
+ default:
+ pw.println("Error: Unknown option: " + opt);
+ return 1;
+ }
+ }
+
+ String packageName = getNextArg();
+ if (packageName == null) {
+ pw.println("Error: package name not specified");
+ return 1;
+ }
+
+ userId = translateUserId(userId, "runUninstall");
+ if (userId == UserHandle.USER_ALL) {
+ userId = UserHandle.USER_SYSTEM;
+ flags |= PackageManager.DELETE_ALL_USERS;
+ } else {
+ final PackageInfo info = mInterface.getPackageInfo(packageName, 0, userId);
+ if (info == null) {
+ pw.println("Failure - not installed for " + userId);
+ return 1;
+ }
+ final boolean isSystem =
+ (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+ // If we are being asked to delete a system app for just one
+ // user set flag so it disables rather than reverting to system
+ // version of the app.
+ if (isSystem) {
+ flags |= PackageManager.DELETE_SYSTEM_APP;
+ }
+ }
+
+ final LocalIntentReceiver receiver = new LocalIntentReceiver();
+ mInterface.getPackageInstaller().uninstall(packageName, null /*callerPackageName*/, flags,
+ receiver.getIntentSender(), userId);
+
+ final Intent result = receiver.getResult();
+ final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_FAILURE);
+ if (status == PackageInstaller.STATUS_SUCCESS) {
+ pw.println("Success");
+ return 0;
+ } else {
+ pw.println("Failure ["
+ + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
+ return 1;
+ }
+ }
+
+ private static class InstallParams {
+ SessionParams sessionParams;
+ String installerPackageName;
+ int userId = UserHandle.USER_ALL;
+ }
+
+ private InstallParams makeInstallParams() {
+ final SessionParams sessionParams = new SessionParams(SessionParams.MODE_FULL_INSTALL);
+ final InstallParams params = new InstallParams();
+ params.sessionParams = sessionParams;
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "-l":
+ sessionParams.installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
+ break;
+ case "-r":
+ sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
+ break;
+ case "-i":
+ params.installerPackageName = getNextArg();
+ if (params.installerPackageName == null) {
+ throw new IllegalArgumentException("Missing installer package");
+ }
+ break;
+ case "-t":
+ sessionParams.installFlags |= PackageManager.INSTALL_ALLOW_TEST;
+ break;
+ case "-s":
+ sessionParams.installFlags |= PackageManager.INSTALL_EXTERNAL;
+ break;
+ case "-f":
+ sessionParams.installFlags |= PackageManager.INSTALL_INTERNAL;
+ break;
+ case "-d":
+ sessionParams.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
+ break;
+ case "-g":
+ sessionParams.installFlags |= PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS;
+ break;
+ case "--originating-uri":
+ sessionParams.originatingUri = Uri.parse(getNextArg());
+ break;
+ case "--referrer":
+ sessionParams.referrerUri = Uri.parse(getNextArg());
+ break;
+ case "-p":
+ sessionParams.mode = SessionParams.MODE_INHERIT_EXISTING;
+ sessionParams.appPackageName = getNextArg();
+ if (sessionParams.appPackageName == null) {
+ throw new IllegalArgumentException("Missing inherit package name");
+ }
+ break;
+ case "-S":
+ sessionParams.setSize(Long.parseLong(getNextArg()));
+ break;
+ case "--abi":
+ sessionParams.abiOverride = checkAbiArgument(getNextArg());
+ break;
+ case "--user":
+ params.userId = Integer.parseInt(getNextArg());
+ break;
+ case "--install-location":
+ sessionParams.installLocation = Integer.parseInt(getNextArg());
+ break;
+ case "--force-uuid":
+ sessionParams.installFlags |= PackageManager.INSTALL_FORCE_VOLUME_UUID;
+ sessionParams.volumeUuid = getNextArg();
+ if ("internal".equals(sessionParams.volumeUuid)) {
+ sessionParams.volumeUuid = null;
+ }
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown option " + opt);
+ }
+ }
+ return params;
+ }
+
+ private static String checkAbiArgument(String abi) {
+ if (TextUtils.isEmpty(abi)) {
+ throw new IllegalArgumentException("Missing ABI argument");
+ }
+
+ if ("-".equals(abi)) {
+ return abi;
+ }
+
+ final String[] supportedAbis = Build.SUPPORTED_ABIS;
+ for (String supportedAbi : supportedAbis) {
+ if (supportedAbi.equals(abi)) {
+ return abi;
+ }
+ }
+
+ throw new IllegalArgumentException("ABI " + abi + " not supported on this device");
+ }
+
+ private int translateUserId(int userId, String logContext) {
+ return ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, true, true, logContext, "pm command");
+ }
+
+ private int doCreateSession(SessionParams params, String installerPackageName, int userId)
+ throws RemoteException {
+ userId = translateUserId(userId, "runInstallCreate");
+ if (userId == UserHandle.USER_ALL) {
+ userId = UserHandle.USER_SYSTEM;
+ params.installFlags |= PackageManager.INSTALL_ALL_USERS;
+ }
+
+ final int sessionId = mInterface.getPackageInstaller()
+ .createSession(params, installerPackageName, userId);
+ return sessionId;
+ }
+
+ private int doWriteSession(int sessionId, String inPath, long sizeBytes, String splitName)
+ throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ if ("-".equals(inPath)) {
+ inPath = null;
+ } else if (inPath != null) {
+ final File file = new File(inPath);
+ if (file.isFile()) {
+ sizeBytes = file.length();
+ }
+ }
+
+ final SessionInfo info = mInterface.getPackageInstaller().getSessionInfo(sessionId);
+
+ PackageInstaller.Session session = null;
+ InputStream in = null;
+ OutputStream out = null;
+ try {
+ session = new PackageInstaller.Session(
+ mInterface.getPackageInstaller().openSession(sessionId));
+
+ if (inPath != null) {
+ in = new FileInputStream(inPath);
+ } else {
+ in = new SizedInputStream(getInputStream(), sizeBytes);
+ }
+ out = session.openWrite(splitName, 0, sizeBytes);
+
+ int total = 0;
+ byte[] buffer = new byte[65536];
+ int c;
+ while ((c = in.read(buffer)) != -1) {
+ total += c;
+ out.write(buffer, 0, c);
+
+ if (info.sizeBytes > 0) {
+ final float fraction = ((float) c / (float) info.sizeBytes);
+ session.addProgress(fraction);
+ }
+ }
+ session.fsync(out);
+
+ pw.println("Success: streamed " + total + " bytes");
+ return 0;
+ } catch (IOException e) {
+ pw.println("Error: failed to write; " + e.getMessage());
+ return 1;
+ } finally {
+ IoUtils.closeQuietly(out);
+ IoUtils.closeQuietly(in);
+ IoUtils.closeQuietly(session);
+ }
+ }
+
+ private int doCommitSession(int sessionId) throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ PackageInstaller.Session session = null;
+ try {
+ session = new PackageInstaller.Session(
+ mInterface.getPackageInstaller().openSession(sessionId));
+
+ final LocalIntentReceiver receiver = new LocalIntentReceiver();
+ session.commit(receiver.getIntentSender());
+
+ final Intent result = receiver.getResult();
+ final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_FAILURE);
+ if (status == PackageInstaller.STATUS_SUCCESS) {
+ pw.println("Success");
+ } else {
+ pw.println("Failure ["
+ + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
+ pw.println("Failure details: " + result.getExtras());
+ }
+ return status;
+ } finally {
+ IoUtils.closeQuietly(session);
+ }
+ }
+
+ private int doAbandonSession(int sessionId) throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ PackageInstaller.Session session = null;
+ try {
+ session = new PackageInstaller.Session(
+ mInterface.getPackageInstaller().openSession(sessionId));
+ session.abandon();
+ pw.println("Success");
+ return 0;
+ } finally {
+ IoUtils.closeQuietly(session);
+ }
+ }
+
+ private void doListPermissions(ArrayList<String> groupList, boolean groups, boolean labels,
+ boolean summary, int startProtectionLevel, int endProtectionLevel)
+ throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ final int groupCount = groupList.size();
+ for (int i = 0; i < groupCount; i++) {
+ String groupName = groupList.get(i);
+ String prefix = "";
+ if (groups) {
+ if (i > 0) {
+ pw.println("");
+ }
+ if (groupName != null) {
+ PermissionGroupInfo pgi =
+ mInterface.getPermissionGroupInfo(groupName, 0 /*flags*/);
+ if (summary) {
+ Resources res = getResources(pgi);
+ if (res != null) {
+ pw.print(loadText(pgi, pgi.labelRes, pgi.nonLocalizedLabel) + ": ");
+ } else {
+ pw.print(pgi.name + ": ");
+
+ }
+ } else {
+ pw.println((labels ? "+ " : "") + "group:" + pgi.name);
+ if (labels) {
+ pw.println(" package:" + pgi.packageName);
+ Resources res = getResources(pgi);
+ if (res != null) {
+ pw.println(" label:"
+ + loadText(pgi, pgi.labelRes, pgi.nonLocalizedLabel));
+ pw.println(" description:"
+ + loadText(pgi, pgi.descriptionRes,
+ pgi.nonLocalizedDescription));
+ }
+ }
+ }
+ } else {
+ pw.println(((labels && !summary) ? "+ " : "") + "ungrouped:");
+ }
+ prefix = " ";
+ }
+ List<PermissionInfo> ps =
+ mInterface.queryPermissionsByGroup(groupList.get(i), 0 /*flags*/);
+ final int count = ps.size();
+ boolean first = true;
+ for (int p = 0 ; p < count ; p++) {
+ PermissionInfo pi = ps.get(p);
+ if (groups && groupName == null && pi.group != null) {
+ continue;
+ }
+ final int base = pi.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
+ if (base < startProtectionLevel
+ || base > endProtectionLevel) {
+ continue;
+ }
+ if (summary) {
+ if (first) {
+ first = false;
+ } else {
+ pw.print(", ");
+ }
+ Resources res = getResources(pi);
+ if (res != null) {
+ pw.print(loadText(pi, pi.labelRes,
+ pi.nonLocalizedLabel));
+ } else {
+ pw.print(pi.name);
+ }
+ } else {
+ pw.println(prefix + (labels ? "+ " : "")
+ + "permission:" + pi.name);
+ if (labels) {
+ pw.println(prefix + " package:" + pi.packageName);
+ Resources res = getResources(pi);
+ if (res != null) {
+ pw.println(prefix + " label:"
+ + loadText(pi, pi.labelRes,
+ pi.nonLocalizedLabel));
+ pw.println(prefix + " description:"
+ + loadText(pi, pi.descriptionRes,
+ pi.nonLocalizedDescription));
+ }
+ pw.println(prefix + " protectionLevel:"
+ + PermissionInfo.protectionToString(pi.protectionLevel));
+ }
+ }
+ }
+
+ if (summary) {
+ pw.println("");
+ }
+ }
+ }
+
+ private String loadText(PackageItemInfo pii, int res, CharSequence nonLocalized)
+ throws RemoteException {
+ if (nonLocalized != null) {
+ return nonLocalized.toString();
+ }
+ if (res != 0) {
+ Resources r = getResources(pii);
+ if (r != null) {
+ try {
+ return r.getString(res);
+ } catch (Resources.NotFoundException e) {
+ }
+ }
+ }
+ return null;
+ }
+
+ private Resources getResources(PackageItemInfo pii) throws RemoteException {
+ Resources res = mResourceCache.get(pii.packageName);
+ if (res != null) return res;
+
+ ApplicationInfo ai = mInterface.getApplicationInfo(pii.packageName, 0, 0);
+ AssetManager am = new AssetManager();
+ am.addAssetPath(ai.publicSourceDir);
+ res = new Resources(am, null, null);
+ mResourceCache.put(pii.packageName, res);
+ return res;
+ }
+
+ @Override
+ public void onHelp() {
+ final PrintWriter pw = getOutPrintWriter();
+ pw.println("Package manager (package) commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println("");
+ pw.println(" list features");
+ pw.println(" Prints all features of the system.");
+ pw.println(" list instrumentation [-f] [TARGET-PACKAGE]");
+ pw.println(" Prints all test packages; optionally only those targetting TARGET-PACKAGE");
+ pw.println(" Options:");
+ pw.println(" -f: dump the name of the .apk file containing the test package");
+ pw.println(" list libraries");
+ pw.println(" Prints all system libraries.");
+ pw.println(" list packages [-f] [-d] [-e] [-s] [-3] [-i] [-u] [--user USER_ID] [FILTER]");
+ pw.println(" Prints all packages; optionally only those whose name contains");
+ pw.println(" the text in FILTER.");
+ pw.println(" Options:");
+ pw.println(" -f: see their associated file");
+ pw.println(" -d: filter to only show disbled packages");
+ pw.println(" -e: filter to only show enabled packages");
+ pw.println(" -s: filter to only show system packages");
+ pw.println(" -3: filter to only show third party packages");
+ pw.println(" -i: see the installer for the packages");
+ pw.println(" -u: also include uninstalled packages");
+ pw.println(" list permission-groups");
+ pw.println(" Prints all known permission groups.");
+ pw.println(" list permissions [-g] [-f] [-d] [-u] [GROUP]");
+ pw.println(" Prints all known permissions; optionally only those in GROUP.");
+ pw.println(" Options:");
+ pw.println(" -g: organize by group");
+ pw.println(" -f: print all information");
+ pw.println(" -s: short summary");
+ pw.println(" -d: only list dangerous permissions");
+ pw.println(" -u: list only the permissions users will see");
+ pw.println("");
+ }
+
+ private static class LocalIntentReceiver {
+ private final SynchronousQueue<Intent> mResult = new SynchronousQueue<>();
+
+ private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
+ @Override
+ public int send(int code, Intent intent, String resolvedType,
+ IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
+ try {
+ mResult.offer(intent, 5, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ return 0;
+ }
+ };
+
+ public IntentSender getIntentSender() {
+ return new IntentSender((IIntentSender) mLocalSender);
+ }
+
+ public Intent getResult() {
+ try {
+ return mResult.take();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+}
+
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index bbdfe31..78328f5 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -239,6 +239,7 @@
keySetData = base.keySetData;
verificationInfo = base.verificationInfo;
installerPackageName = base.installerPackageName;
+ volumeUuid = base.volumeUuid;
}
private PackageUserState modifyUserState(int userId) {
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index 66170d4..5d8b1d2 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -100,6 +100,9 @@
private static final String SEAPP_HASH_FILE =
Environment.getDataDirectory().toString() + "/system/seapp_hash";
+ // Append privapp to existing seinfo label
+ private static final String PRIVILEGED_APP_STR = ":privapp";
+
/**
* Load the mac_permissions.xml file containing all seinfo assignments used to
* label apps. The loaded mac_permissions.xml file is determined by the
@@ -313,6 +316,9 @@
}
}
+ if (pkg.applicationInfo.isPrivilegedApp())
+ pkg.applicationInfo.seinfo += PRIVILEGED_APP_STR;
+
if (DEBUG_POLICY_INSTALL) {
Slog.i(TAG, "package (" + pkg.packageName + ") labeled with " +
"seinfo=" + pkg.applicationInfo.seinfo);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 61d2676..de14739 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -514,7 +514,18 @@
ArrayList<String> removeStage = new ArrayList<String>();
for (Map.Entry<String,SharedUserSetting> entry : mSharedUsers.entrySet()) {
final SharedUserSetting sus = entry.getValue();
- if (sus == null || sus.packages.size() == 0) {
+ if (sus == null) {
+ removeStage.add(entry.getKey());
+ continue;
+ }
+ // remove packages that are no longer installed
+ for (Iterator<PackageSetting> iter = sus.packages.iterator(); iter.hasNext();) {
+ PackageSetting ps = iter.next();
+ if (mPackages.get(ps.name) == null) {
+ iter.remove();
+ }
+ }
+ if (sus.packages.size() == 0) {
removeStage.add(entry.getKey());
}
}
@@ -2237,7 +2248,8 @@
StringBuilder sb = new StringBuilder();
for (final PackageSetting pkg : mPackages.values()) {
- if (pkg.pkg == null || pkg.pkg.applicationInfo == null) {
+ if (pkg.pkg == null || pkg.pkg.applicationInfo == null
+ || pkg.pkg.applicationInfo.dataDir == null) {
Slog.w(TAG, "Skipping " + pkg + " due to missing metadata");
continue;
}
@@ -2264,10 +2276,8 @@
//
// DO NOT MODIFY THIS FORMAT UNLESS YOU CAN ALSO MODIFY ITS USERS
// FROM NATIVE CODE. AT THE MOMENT, LOOK AT THE FOLLOWING SOURCES:
- // system/core/logd/LogStatistics.cpp
+ // frameworks/base/libs/packagelistparser
// system/core/run-as/run-as.c
- // system/core/sdcard/sdcard.c
- // external/libselinux/src/android.c:package_info_init()
//
sb.setLength(0);
sb.append(ai.packageName);
@@ -3741,8 +3751,13 @@
private String compToString(ArraySet<String> cmp) {
return cmp != null ? Arrays.toString(cmp.toArray()) : "[]";
}
-
- boolean isEnabledLPr(ComponentInfo componentInfo, int flags, int userId) {
+
+ boolean isEnabledAndVisibleLPr(ComponentInfo componentInfo, int flags, int userId) {
+ return isEnabledLPr(componentInfo, flags, userId)
+ && isVisibleLPr(componentInfo, flags);
+ }
+
+ private boolean isEnabledLPr(ComponentInfo componentInfo, int flags, int userId) {
if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
return true;
}
@@ -3783,6 +3798,17 @@
return componentInfo.enabled;
}
+ private boolean isVisibleLPr(ComponentInfo componentInfo, int flags) {
+ if ((flags & PackageManager.GET_ENCRYPTION_UNAWARE_COMPONENTS) != 0) {
+ return true;
+ }
+ if ((flags & PackageManager.FLAG_USER_RUNNING_WITH_AMNESIA) != 0) {
+ // When running with amnesia, we can only run encryption-aware apps
+ return componentInfo.encryptionAware;
+ }
+ return true;
+ }
+
String getInstallerPackageNameLPr(String packageName) {
final PackageSetting pkg = mPackages.get(packageName);
if (pkg == null) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 341410d..3a1d2de 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -16,14 +16,16 @@
package com.android.server.pm;
-import android.accounts.Account;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
import android.app.IStopUserCallback;
import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManagerInternal;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -43,9 +45,14 @@
import android.os.Parcelable;
import android.os.Process;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.os.ServiceManager;
+import android.os.ShellCommand;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.UserManagerInternal;
+import android.os.UserManagerInternal.UserRestrictionsListener;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.system.ErrnoException;
@@ -59,9 +66,11 @@
import android.util.TimeUtils;
import android.util.Xml;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IAppOpsService;
import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.server.LocalServices;
@@ -83,11 +92,20 @@
import libcore.io.IoUtils;
+/**
+ * Service for {@link UserManager}.
+ *
+ * Method naming convention:
+ * <ul>
+ * <li> Methods suffixed with "LILP" should be called within {@link #mInstallLock} and
+ * {@link #mPackagesLock} locks obtained in the respective order.
+ * <li> Methods suffixed with "LR" should be called within the {@link #mRestrictionsLock} lock.
+ * <li> Methods suffixed with "LU" should be called within the {@link #mUsersLock} lock.
+ * </ul>
+ */
public class UserManagerService extends IUserManager.Stub {
-
private static final String LOG_TAG = "UserManagerService";
-
- private static final boolean DBG = false;
+ private static final boolean DBG = false; // DO NOT SUBMIT WITH TRUE
private static final String TAG_NAME = "name";
private static final String ATTR_FLAGS = "flags";
@@ -139,11 +157,6 @@
// without first making sure that the rest of the framework is prepared for it.
private static final int MAX_MANAGED_PROFILES = 1;
- /**
- * Flag indicating whether device credentials are shared among same-user profiles.
- */
- private static final boolean CONFIG_PROFILES_SHARE_CREDENTIAL = true;
-
static final int WRITE_USER_MSG = 1;
static final int WRITE_USER_DELAY = 2*1000; // 2 seconds
@@ -153,29 +166,77 @@
private final PackageManagerService mPm;
private final Object mInstallLock;
private final Object mPackagesLock;
+ // Short-term lock for internal state, when interaction/sync with PM is not required
+ private final Object mUsersLock = new Object();
+ private final Object mRestrictionsLock = new Object();
private final Handler mHandler;
private final File mUsersDir;
private final File mUserListFile;
- private final SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>();
- private final SparseArray<Bundle> mUserRestrictions = new SparseArray<Bundle>();
+ @GuardedBy("mUsersLock")
+ private final SparseArray<UserInfo> mUsers = new SparseArray<>();
+
+ /**
+ * User restrictions set via UserManager. This doesn't include restrictions set by
+ * device owner / profile owners.
+ *
+ * DO NOT Change existing {@link Bundle} in it. When changing a restriction for a user,
+ * a new {@link Bundle} should always be created and set. This is because a {@link Bundle}
+ * maybe shared between {@link #mBaseUserRestrictions} and
+ * {@link #mCachedEffectiveUserRestrictions}, but they should always updated separately.
+ * (Otherwise we won't be able to detect what restrictions have changed in
+ * {@link #updateUserRestrictionsInternalLR}.
+ */
+ @GuardedBy("mRestrictionsLock")
+ private final SparseArray<Bundle> mBaseUserRestrictions = new SparseArray<>();
+
+ /**
+ * Cached user restrictions that are in effect -- i.e. {@link #mBaseUserRestrictions} combined
+ * with device / profile owner restrictions. We'll initialize it lazily; use
+ * {@link #getEffectiveUserRestrictions} to access it.
+ *
+ * DO NOT Change existing {@link Bundle} in it. When changing a restriction for a user,
+ * a new {@link Bundle} should always be created and set. This is because a {@link Bundle}
+ * maybe shared between {@link #mBaseUserRestrictions} and
+ * {@link #mCachedEffectiveUserRestrictions}, but they should always updated separately.
+ * (Otherwise we won't be able to detect what restrictions have changed in
+ * {@link #updateUserRestrictionsInternalLR}.
+ */
+ @GuardedBy("mRestrictionsLock")
+ private final SparseArray<Bundle> mCachedEffectiveUserRestrictions = new SparseArray<>();
+
+ /**
+ * User restrictions that have already been applied in {@link #applyUserRestrictionsLR}. We
+ * use it to detect restrictions that have changed since the last
+ * {@link #applyUserRestrictionsLR} call.
+ */
+ @GuardedBy("mRestrictionsLock")
+ private final SparseArray<Bundle> mAppliedUserRestrictions = new SparseArray<>();
+
private final Bundle mGuestRestrictions = new Bundle();
/**
* Set of user IDs being actively removed. Removed IDs linger in this set
* for several seconds to work around a VFS caching issue.
*/
- // @GuardedBy("mPackagesLock")
+ @GuardedBy("mUsersLock")
private final SparseBooleanArray mRemovingUserIds = new SparseBooleanArray();
+ @GuardedBy("mUsersLock")
private int[] mUserIds;
private int mNextSerialNumber;
private int mUserVersion = 0;
private IAppOpsService mAppOpsService;
+ private final LocalService mLocalService;
+
+ @GuardedBy("mUserRestrictionsListeners")
+ private final ArrayList<UserRestrictionsListener> mUserRestrictionsListeners =
+ new ArrayList<>();
+
private static UserManagerService sInstance;
public static UserManagerService getInstance() {
@@ -227,30 +288,34 @@
-1, -1);
mUserListFile = new File(mUsersDir, USER_LIST_FILENAME);
initDefaultGuestRestrictions();
- readUserListLocked();
+ readUserListLILP();
sInstance = this;
}
}
+ mLocalService = new LocalService();
+ LocalServices.addService(UserManagerInternal.class, mLocalService);
}
void systemReady() {
synchronized (mInstallLock) {
synchronized (mPackagesLock) {
- // Prune out any partially created/partially removed users.
- ArrayList<UserInfo> partials = new ArrayList<UserInfo>();
- final int userSize = mUsers.size();
- for (int i = 0; i < userSize; i++) {
- UserInfo ui = mUsers.valueAt(i);
- if ((ui.partial || ui.guestToRemove) && i != 0) {
- partials.add(ui);
+ synchronized (mUsersLock) {
+ // Prune out any partially created/partially removed users.
+ ArrayList<UserInfo> partials = new ArrayList<UserInfo>();
+ final int userSize = mUsers.size();
+ for (int i = 0; i < userSize; i++) {
+ UserInfo ui = mUsers.valueAt(i);
+ if ((ui.partial || ui.guestToRemove) && i != 0) {
+ partials.add(ui);
+ }
}
- }
- final int partialsSize = partials.size();
- for (int i = 0; i < partialsSize; i++) {
- UserInfo ui = partials.get(i);
- Slog.w(LOG_TAG, "Removing partially created user " + ui.id
- + " (name=" + ui.name + ")");
- removeUserStateLocked(ui.id);
+ final int partialsSize = partials.size();
+ for (int i = 0; i < partialsSize; i++) {
+ UserInfo ui = partials.get(i);
+ Slog.w(LOG_TAG, "Removing partially created user " + ui.id
+ + " (name=" + ui.name + ")");
+ removeUserStateLILP(ui.id);
+ }
}
}
}
@@ -258,8 +323,9 @@
mAppOpsService = IAppOpsService.Stub.asInterface(
ServiceManager.getService(Context.APP_OPS_SERVICE));
for (int i = 0; i < mUserIds.length; ++i) {
+ final int userId = mUserIds[i];
try {
- mAppOpsService.setUserRestrictions(mUserRestrictions.get(mUserIds[i]), mUserIds[i]);
+ mAppOpsService.setUserRestrictions(getEffectiveUserRestrictions(userId), userId);
} catch (RemoteException e) {
Log.w(LOG_TAG, "Unable to notify AppOpsService of UserRestrictions");
}
@@ -269,7 +335,7 @@
@Override
public UserInfo getPrimaryUser() {
checkManageUsersPermission("query users");
- synchronized (mPackagesLock) {
+ synchronized (mUsersLock) {
final int userSize = mUsers.size();
for (int i = 0; i < userSize; i++) {
UserInfo ui = mUsers.valueAt(i);
@@ -284,7 +350,7 @@
@Override
public @NonNull List<UserInfo> getUsers(boolean excludeDying) {
checkManageUsersPermission("query users");
- synchronized (mPackagesLock) {
+ synchronized (mUsersLock) {
ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
final int userSize = mUsers.size();
for (int i = 0; i < userSize; i++) {
@@ -307,8 +373,8 @@
}
final long ident = Binder.clearCallingIdentity();
try {
- synchronized (mPackagesLock) {
- return getProfilesLocked(userId, enabledOnly);
+ synchronized (mUsersLock) {
+ return getProfilesLU(userId, enabledOnly);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -316,8 +382,8 @@
}
/** Assume permissions already checked and caller's identity cleared */
- private List<UserInfo> getProfilesLocked(int userId, boolean enabledOnly) {
- UserInfo user = getUserInfoLocked(userId);
+ private List<UserInfo> getProfilesLU(int userId, boolean enabledOnly) {
+ UserInfo user = getUserInfoLU(userId);
ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
if (user == null) {
// Probably a dying user
@@ -343,9 +409,9 @@
@Override
public int getCredentialOwnerProfile(int userHandle) {
checkManageUsersPermission("get the credential owner");
- if (CONFIG_PROFILES_SHARE_CREDENTIAL) {
- synchronized (mPackagesLock) {
- UserInfo profileParent = getProfileParentLocked(userHandle);
+ if (!mContext.getSystemService(StorageManager.class).isPerUserEncryptionEnabled()) {
+ synchronized (mUsersLock) {
+ UserInfo profileParent = getProfileParentLU(userHandle);
if (profileParent != null) {
return profileParent.id;
}
@@ -356,15 +422,39 @@
}
@Override
- public UserInfo getProfileParent(int userHandle) {
- checkManageUsersPermission("get the profile parent");
+ public boolean isSameProfileGroup(int userId, int otherUserId) {
+ if (userId == otherUserId) return true;
+ checkManageUsersPermission("check if in the same profile group");
synchronized (mPackagesLock) {
- return getProfileParentLocked(userHandle);
+ return isSameProfileGroupLP(userId, otherUserId);
}
}
- private UserInfo getProfileParentLocked(int userHandle) {
- UserInfo profile = getUserInfoLocked(userHandle);
+ private boolean isSameProfileGroupLP(int userId, int otherUserId) {
+ synchronized (mUsersLock) {
+ UserInfo userInfo = getUserInfoLU(userId);
+ if (userInfo == null || userInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID) {
+ return false;
+ }
+ UserInfo otherUserInfo = getUserInfoLU(otherUserId);
+ if (otherUserInfo == null
+ || otherUserInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID) {
+ return false;
+ }
+ return userInfo.profileGroupId == otherUserInfo.profileGroupId;
+ }
+ }
+
+ @Override
+ public UserInfo getProfileParent(int userHandle) {
+ checkManageUsersPermission("get the profile parent");
+ synchronized (mUsersLock) {
+ return getProfileParentLU(userHandle);
+ }
+ }
+
+ private UserInfo getProfileParentLU(int userHandle) {
+ UserInfo profile = getUserInfoLU(userHandle);
if (profile == null) {
return null;
}
@@ -372,11 +462,11 @@
if (parentUserId == UserInfo.NO_PROFILE_GROUP_ID) {
return null;
} else {
- return getUserInfoLocked(parentUserId);
+ return getUserInfoLU(parentUserId);
}
}
- private boolean isProfileOf(UserInfo user, UserInfo profile) {
+ private static boolean isProfileOf(UserInfo user, UserInfo profile) {
return user.id == profile.id ||
(user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
&& user.profileGroupId == profile.profileGroupId);
@@ -386,10 +476,13 @@
public void setUserEnabled(int userId) {
checkManageUsersPermission("enable user");
synchronized (mPackagesLock) {
- UserInfo info = getUserInfoLocked(userId);
+ UserInfo info;
+ synchronized (mUsersLock) {
+ info = getUserInfoLU(userId);
+ }
if (info != null && !info.isEnabled()) {
info.flags ^= UserInfo.FLAG_DISABLED;
- writeUserLocked(info);
+ writeUserLP(info);
}
}
}
@@ -397,23 +490,23 @@
@Override
public UserInfo getUserInfo(int userId) {
checkManageUsersPermission("query user");
- synchronized (mPackagesLock) {
- return getUserInfoLocked(userId);
+ synchronized (mUsersLock) {
+ return getUserInfoLU(userId);
}
}
@Override
public boolean isRestricted() {
- synchronized (mPackagesLock) {
- return getUserInfoLocked(UserHandle.getCallingUserId()).isRestricted();
+ synchronized (mUsersLock) {
+ return getUserInfoLU(UserHandle.getCallingUserId()).isRestricted();
}
}
@Override
public boolean canHaveRestrictedProfile(int userId) {
checkManageUsersPermission("canHaveRestrictedProfile");
- synchronized (mPackagesLock) {
- final UserInfo userInfo = getUserInfoLocked(userId);
+ synchronized (mUsersLock) {
+ final UserInfo userInfo = getUserInfoLU(userId);
if (userInfo == null || !userInfo.canHaveProfile()) {
return false;
}
@@ -430,7 +523,7 @@
/*
* Should be locked on mUsers before calling this.
*/
- private UserInfo getUserInfoLocked(int userId) {
+ private UserInfo getUserInfoLU(int userId) {
UserInfo ui = mUsers.get(userId);
// If it is partial and not in the process of being removed, return as unknown user.
if (ui != null && ui.partial && !mRemovingUserIds.get(userId)) {
@@ -440,11 +533,19 @@
return ui;
}
+ /**
+ * Obtains {@link #mUsersLock} and return UserInfo from mUsers.
+ * <p>No permissions checking or any addition checks are made</p>
+ */
+ private UserInfo getUserInfoNoChecks(int userId) {
+ synchronized (mUsersLock) {
+ return mUsers.get(userId);
+ }
+ }
+
/** Called by PackageManagerService */
public boolean exists(int userId) {
- synchronized (mPackagesLock) {
- return mUsers.get(userId) != null;
- }
+ return getUserInfoNoChecks(userId) != null;
}
@Override
@@ -452,14 +553,14 @@
checkManageUsersPermission("rename users");
boolean changed = false;
synchronized (mPackagesLock) {
- UserInfo info = mUsers.get(userId);
+ UserInfo info = getUserInfoNoChecks(userId);
if (info == null || info.partial) {
Slog.w(LOG_TAG, "setUserName: unknown user #" + userId);
return;
}
if (name != null && !name.equals(info.name)) {
info.name = name;
- writeUserLocked(info);
+ writeUserLP(info);
changed = true;
}
}
@@ -474,13 +575,13 @@
long ident = Binder.clearCallingIdentity();
try {
synchronized (mPackagesLock) {
- UserInfo info = mUsers.get(userId);
+ UserInfo info = getUserInfoNoChecks(userId);
if (info == null || info.partial) {
Slog.w(LOG_TAG, "setUserIcon: unknown user #" + userId);
return;
}
- writeBitmapLocked(info, bitmap);
- writeUserLocked(info);
+ writeBitmapLP(info, bitmap);
+ writeUserLP(info);
}
sendUserInfoChangedBroadcast(userId);
} finally {
@@ -499,12 +600,12 @@
public ParcelFileDescriptor getUserIcon(int userId) {
String iconPath;
synchronized (mPackagesLock) {
- UserInfo info = mUsers.get(userId);
+ UserInfo info = getUserInfoNoChecks(userId);
if (info == null || info.partial) {
Slog.w(LOG_TAG, "getUserIcon: unknown user #" + userId);
return null;
}
- int callingGroupId = mUsers.get(UserHandle.getCallingUserId()).profileGroupId;
+ int callingGroupId = getUserInfoNoChecks(UserHandle.getCallingUserId()).profileGroupId;
if (callingGroupId == UserInfo.NO_PROFILE_GROUP_ID
|| callingGroupId != info.profileGroupId) {
checkManageUsersPermission("get the icon of a user who is not related");
@@ -527,13 +628,14 @@
public void makeInitialized(int userId) {
checkManageUsersPermission("makeInitialized");
synchronized (mPackagesLock) {
- UserInfo info = mUsers.get(userId);
+ UserInfo info = getUserInfoNoChecks(userId);
if (info == null || info.partial) {
Slog.w(LOG_TAG, "makeInitialized: unknown user #" + userId);
+ // TODO Check if we should return here instead of a null check below
}
- if ((info.flags&UserInfo.FLAG_INITIALIZED) == 0) {
+ if (info != null && (info.flags&UserInfo.FLAG_INITIALIZED) == 0) {
info.flags |= UserInfo.FLAG_INITIALIZED;
- scheduleWriteUserLocked(info);
+ scheduleWriteUser(info);
}
}
}
@@ -552,6 +654,7 @@
@Override
public Bundle getDefaultGuestRestrictions() {
checkManageUsersPermission("getDefaultGuestRestrictions");
+ // TODO Switch to mGuestRestrictions for locking
synchronized (mPackagesLock) {
return new Bundle(mGuestRestrictions);
}
@@ -560,95 +663,234 @@
@Override
public void setDefaultGuestRestrictions(Bundle restrictions) {
checkManageUsersPermission("setDefaultGuestRestrictions");
- synchronized (mPackagesLock) {
- mGuestRestrictions.clear();
- mGuestRestrictions.putAll(restrictions);
- writeUserListLocked();
+ synchronized (mInstallLock) {
+ synchronized (mPackagesLock) {
+ mGuestRestrictions.clear();
+ mGuestRestrictions.putAll(restrictions);
+ writeUserListLILP();
+ }
}
}
+ @GuardedBy("mRestrictionsLock")
+ private Bundle computeEffectiveUserRestrictionsLR(int userId) {
+ final DevicePolicyManagerInternal dpmi =
+ LocalServices.getService(DevicePolicyManagerInternal.class);
+ final Bundle systemRestrictions = mBaseUserRestrictions.get(userId);
+
+ final Bundle effective;
+ if (dpmi == null) {
+ // TODO Make sure it's because DPMS is disabled and not because we called it too early.
+ effective = systemRestrictions;
+ } else {
+ effective = dpmi.getComposedUserRestrictions(userId, systemRestrictions);
+ }
+ return effective;
+ }
+
+ @GuardedBy("mRestrictionsLock")
+ private void invalidateEffectiveUserRestrictionsLR(int userId) {
+ if (DBG) {
+ Log.d(LOG_TAG, "invalidateEffectiveUserRestrictions userId=" + userId);
+ }
+ mCachedEffectiveUserRestrictions.remove(userId);
+ }
+
+ private Bundle getEffectiveUserRestrictions(int userId) {
+ synchronized (mRestrictionsLock) {
+ Bundle restrictions = mCachedEffectiveUserRestrictions.get(userId);
+ if (restrictions == null) {
+ restrictions = computeEffectiveUserRestrictionsLR(userId);
+ mCachedEffectiveUserRestrictions.put(userId, restrictions);
+ }
+ return restrictions;
+ }
+ }
+
+ /** @return a specific user restriction that's in effect currently. */
@Override
public boolean hasUserRestriction(String restrictionKey, int userId) {
- synchronized (mPackagesLock) {
- Bundle restrictions = mUserRestrictions.get(userId);
- return restrictions != null && restrictions.getBoolean(restrictionKey);
- }
+ Bundle restrictions = getEffectiveUserRestrictions(userId);
+ return restrictions != null && restrictions.getBoolean(restrictionKey);
}
+ /**
+ * @return UserRestrictions that are in effect currently. This always returns a new
+ * {@link Bundle}.
+ */
@Override
public Bundle getUserRestrictions(int userId) {
- // checkManageUsersPermission("getUserRestrictions");
-
- synchronized (mPackagesLock) {
- Bundle restrictions = mUserRestrictions.get(userId);
- return restrictions != null ? new Bundle(restrictions) : new Bundle();
- }
+ Bundle restrictions = getEffectiveUserRestrictions(userId);
+ return restrictions != null ? new Bundle(restrictions) : new Bundle();
}
@Override
public void setUserRestriction(String key, boolean value, int userId) {
checkManageUsersPermission("setUserRestriction");
- synchronized (mPackagesLock) {
- if (!UserRestrictionsUtils.SYSTEM_CONTROLLED_USER_RESTRICTIONS.contains(key)) {
- Bundle restrictions = getUserRestrictions(userId);
- restrictions.putBoolean(key, value);
- setUserRestrictionsInternalLocked(restrictions, userId);
- }
+ if (!UserRestrictionsUtils.SYSTEM_CONTROLLED_USER_RESTRICTIONS.contains(key)) {
+ setUserRestrictionNoCheck(key, value, userId);
}
}
@Override
public void setSystemControlledUserRestriction(String key, boolean value, int userId) {
checkSystemOrRoot("setSystemControlledUserRestriction");
- synchronized (mPackagesLock) {
- Bundle restrictions = getUserRestrictions(userId);
- restrictions.putBoolean(key, value);
- setUserRestrictionsInternalLocked(restrictions, userId);
+ setUserRestrictionNoCheck(key, value, userId);
+ }
+
+ private void setUserRestrictionNoCheck(String key, boolean value, int userId) {
+ synchronized (mRestrictionsLock) {
+ // Note we can't modify Bundles stored in mBaseUserRestrictions directly, so create
+ // a copy.
+ final Bundle newRestrictions = new Bundle();
+ UserRestrictionsUtils.merge(newRestrictions, mBaseUserRestrictions.get(userId));
+ newRestrictions.putBoolean(key, value);
+
+ updateUserRestrictionsInternalLR(newRestrictions, userId);
}
}
- @Override
- public void setUserRestrictions(Bundle restrictions, int userId) {
- checkManageUsersPermission("setUserRestrictions");
- if (restrictions == null) return;
-
- synchronized (mPackagesLock) {
- final Bundle oldUserRestrictions = mUserRestrictions.get(userId);
- // Restore the original state of system controlled restrictions from oldUserRestrictions
- for (String key : UserRestrictionsUtils.SYSTEM_CONTROLLED_USER_RESTRICTIONS) {
- restrictions.remove(key);
- if (oldUserRestrictions.containsKey(key)) {
- restrictions.putBoolean(key, oldUserRestrictions.getBoolean(key));
- }
- }
- setUserRestrictionsInternalLocked(restrictions, userId);
+ /**
+ * Optionally updating user restrictions, calculate the effective user restrictions by
+ * consulting {@link com.android.server.devicepolicy.DevicePolicyManagerService} and also
+ * apply it to {@link com.android.server.AppOpsService}.
+ * TODO applyUserRestrictionsLocked() should also apply to system settings.
+ *
+ * @param newRestrictions User restrictions to set. If null, only the effective restrictions
+ * will be updated. Note don't pass an existing Bundle in {@link #mBaseUserRestrictions}
+ * or {@link #mCachedEffectiveUserRestrictions}; that'll most likely cause a sub
+ * @param userId target user ID.
+ */
+ @GuardedBy("mRestrictionsLock")
+ private void updateUserRestrictionsInternalLR(
+ @Nullable Bundle newRestrictions, int userId) {
+ if (DBG) {
+ Log.d(LOG_TAG, "updateUserRestrictionsInternalLocked userId=" + userId
+ + " bundle=" + newRestrictions);
}
+ // Update system restrictions.
+ if (newRestrictions != null) {
+ // If newRestrictions == the current one, it's probably a bug.
+ Preconditions.checkState(mBaseUserRestrictions.get(userId) != newRestrictions);
+ Preconditions.checkState(mCachedEffectiveUserRestrictions.get(userId)
+ != newRestrictions);
+ mBaseUserRestrictions.put(userId, newRestrictions);
+ scheduleWriteUser(mUsers.get(userId));
+ }
+
+ final Bundle effective = computeEffectiveUserRestrictionsLR(userId);
+
+ mCachedEffectiveUserRestrictions.put(userId, effective);
+
+ applyUserRestrictionsLR(userId, effective);
}
- private void setUserRestrictionsInternalLocked(Bundle restrictions, int userId) {
- final Bundle userRestrictions = mUserRestrictions.get(userId);
- userRestrictions.clear();
- userRestrictions.putAll(restrictions);
- long token = Binder.clearCallingIdentity();
+ @GuardedBy("mRestrictionsLock")
+ private void applyUserRestrictionsLR(int userId, Bundle newRestrictions) {
+ if (newRestrictions == null) {
+ newRestrictions = Bundle.EMPTY;
+ }
+
+ Bundle prevRestrictions = mAppliedUserRestrictions.get(userId);
+ if (prevRestrictions == null) {
+ prevRestrictions = Bundle.EMPTY;
+ }
+
+ if (DBG) {
+ Log.d(LOG_TAG, "applyUserRestrictionsRL userId=" + userId
+ + " new=" + newRestrictions + " prev=" + prevRestrictions);
+ }
+
+ final long token = Binder.clearCallingIdentity();
try {
- mAppOpsService.setUserRestrictions(userRestrictions, userId);
+ mAppOpsService.setUserRestrictions(newRestrictions, userId);
} catch (RemoteException e) {
Log.w(LOG_TAG, "Unable to notify AppOpsService of UserRestrictions");
} finally {
Binder.restoreCallingIdentity(token);
}
- scheduleWriteUserLocked(mUsers.get(userId));
+
+ UserRestrictionsUtils.applyUserRestrictionsLR(
+ mContext, userId, newRestrictions, prevRestrictions);
+
+ notifyUserRestrictionsListeners(userId, newRestrictions, prevRestrictions);
+
+ mAppliedUserRestrictions.put(userId, new Bundle(newRestrictions));
+ }
+
+ private void notifyUserRestrictionsListeners(final int userId,
+ Bundle newRestrictions, Bundle prevRestrictions) {
+
+ final Bundle newRestrictionsFinal = new Bundle(newRestrictions);
+ final Bundle prevRestrictionsFinal = new Bundle(prevRestrictions);
+
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ final UserRestrictionsListener[] listeners;
+ synchronized (mUserRestrictionsListeners) {
+ listeners = new UserRestrictionsListener[mUserRestrictionsListeners.size()];
+ mUserRestrictionsListeners.toArray(listeners);
+ }
+ for (int i = 0; i < listeners.length; i++) {
+ listeners[i].onUserRestrictionsChanged(userId,
+ newRestrictionsFinal, prevRestrictionsFinal);
+ }
+ }
+ });
+ }
+
+ @GuardedBy("mRestrictionsLock")
+ private void updateEffectiveUserRestrictionsLR(int userId) {
+ updateUserRestrictionsInternalLR(null, userId);
+ }
+
+ @GuardedBy("mRestrictionsLock")
+ private void updateEffectiveUserRestrictionsForAllUsersLR() {
+ // First, invalidate all cached values.
+ mCachedEffectiveUserRestrictions.clear();
+
+ // We don't want to call into ActivityManagerNative while taking a lock, so we'll call
+ // it on a handler.
+ final Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ // Then get the list of running users.
+ final int[] runningUsers;
+ try {
+ runningUsers = ActivityManagerNative.getDefault().getRunningUserIds();
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Unable to access ActivityManagerNative");
+ return;
+ }
+ // Then re-calculate the effective restrictions and apply, only for running users.
+ // It's okay if a new user has started after the getRunningUserIds() call,
+ // because we'll do the same thing (re-calculate the restrictions and apply)
+ // when we start a user.
+ // TODO: "Apply restrictions upon user start hasn't been implemented. Implement it.
+ synchronized (mRestrictionsLock) {
+ for (int i = 0; i < runningUsers.length; i++) {
+ updateUserRestrictionsInternalLR(null, runningUsers[i]);
+ }
+ }
+ }
+ };
+ mHandler.post(r);
}
/**
* Check if we've hit the limit of how many users can be created.
*/
- private boolean isUserLimitReachedLocked() {
- return getAliveUsersExcludingGuestsCountLocked() >= UserManager.getMaxSupportedUsers();
+ private boolean isUserLimitReached() {
+ int count;
+ synchronized (mUsersLock) {
+ count = getAliveUsersExcludingGuestsCountLU();
+ }
+ return count >= UserManager.getMaxSupportedUsers();
}
@Override
- public boolean canAddMoreManagedProfiles(int userId) {
+ public boolean canAddMoreManagedProfiles(int userId, boolean allowedToRemoveOne) {
checkManageUsersPermission("check if more managed profiles can be added.");
if (ActivityManager.isLowRamDeviceStatic()) {
return false;
@@ -658,22 +900,25 @@
return false;
}
// Limit number of managed profiles that can be created
- int managedProfilesCount = getProfiles(userId, true).size() - 1;
- if (managedProfilesCount >= MAX_MANAGED_PROFILES) {
+ final int managedProfilesCount = getProfiles(userId, true).size() - 1;
+ final int profilesRemovedCount = managedProfilesCount > 0 && allowedToRemoveOne ? 1 : 0;
+ if (managedProfilesCount - profilesRemovedCount >= MAX_MANAGED_PROFILES) {
return false;
}
- synchronized(mPackagesLock) {
- UserInfo userInfo = getUserInfoLocked(userId);
+ synchronized(mUsersLock) {
+ UserInfo userInfo = getUserInfoLU(userId);
if (!userInfo.canHaveProfile()) {
return false;
}
- int usersCount = getAliveUsersExcludingGuestsCountLocked();
+ int usersCountAfterRemoving = getAliveUsersExcludingGuestsCountLU()
+ - profilesRemovedCount;
// We allow creating a managed profile in the special case where there is only one user.
- return usersCount == 1 || usersCount < UserManager.getMaxSupportedUsers();
+ return usersCountAfterRemoving == 1
+ || usersCountAfterRemoving < UserManager.getMaxSupportedUsers();
}
}
- private int getAliveUsersExcludingGuestsCountLocked() {
+ private int getAliveUsersExcludingGuestsCountLU() {
int aliveUserCount = 0;
final int totalUserCount = mUsers.size();
// Skip over users being removed
@@ -712,7 +957,7 @@
}
}
- private void writeBitmapLocked(UserInfo info, Bitmap bitmap) {
+ private void writeBitmapLP(UserInfo info, Bitmap bitmap) {
try {
File dir = new File(mUsersDir, Integer.toString(info.id));
File file = new File(dir, USER_PHOTO_FILENAME);
@@ -746,18 +991,14 @@
* @return the array of user ids.
*/
public int[] getUserIds() {
- synchronized (mPackagesLock) {
+ synchronized (mUsersLock) {
return mUserIds;
}
}
- int[] getUserIdsLPr() {
- return mUserIds;
- }
-
- private void readUserListLocked() {
+ private void readUserListLILP() {
if (!mUserListFile.exists()) {
- fallbackToSingleUserLocked();
+ fallbackToSingleUserLILP();
return;
}
FileInputStream fis = null;
@@ -774,7 +1015,7 @@
if (type != XmlPullParser.START_TAG) {
Slog.e(LOG_TAG, "Unable to read user list");
- fallbackToSingleUserLocked();
+ fallbackToSingleUserLILP();
return;
}
@@ -795,12 +1036,14 @@
final String name = parser.getName();
if (name.equals(TAG_USER)) {
String id = parser.getAttributeValue(null, ATTR_ID);
- UserInfo user = readUserLocked(Integer.parseInt(id));
+ UserInfo user = readUserLILP(Integer.parseInt(id));
if (user != null) {
- mUsers.put(user.id, user);
- if (mNextSerialNumber < 0 || mNextSerialNumber <= user.id) {
- mNextSerialNumber = user.id + 1;
+ synchronized (mUsersLock) {
+ mUsers.put(user.id, user);
+ if (mNextSerialNumber < 0 || mNextSerialNumber <= user.id) {
+ mNextSerialNumber = user.id + 1;
+ }
}
}
} else if (name.equals(TAG_GUEST_RESTRICTIONS)) {
@@ -817,12 +1060,12 @@
}
}
}
- updateUserIdsLocked();
- upgradeIfNecessaryLocked();
+ updateUserIds();
+ upgradeIfNecessaryLILP();
} catch (IOException ioe) {
- fallbackToSingleUserLocked();
+ fallbackToSingleUserLILP();
} catch (XmlPullParserException pe) {
- fallbackToSingleUserLocked();
+ fallbackToSingleUserLILP();
} finally {
if (fis != null) {
try {
@@ -836,24 +1079,24 @@
/**
* Upgrade steps between versions, either for fixing bugs or changing the data format.
*/
- private void upgradeIfNecessaryLocked() {
+ private void upgradeIfNecessaryLILP() {
int userVersion = mUserVersion;
if (userVersion < 1) {
// Assign a proper name for the owner, if not initialized correctly before
- UserInfo user = mUsers.get(UserHandle.USER_SYSTEM);
+ UserInfo user = getUserInfoNoChecks(UserHandle.USER_SYSTEM);
if ("Primary".equals(user.name)) {
user.name = mContext.getResources().getString(com.android.internal.R.string.owner_name);
- scheduleWriteUserLocked(user);
+ scheduleWriteUser(user);
}
userVersion = 1;
}
if (userVersion < 2) {
// Owner should be marked as initialized
- UserInfo user = mUsers.get(UserHandle.USER_SYSTEM);
+ UserInfo user = getUserInfoNoChecks(UserHandle.USER_SYSTEM);
if ((user.flags & UserInfo.FLAG_INITIALIZED) == 0) {
user.flags |= UserInfo.FLAG_INITIALIZED;
- scheduleWriteUserLocked(user);
+ scheduleWriteUser(user);
}
userVersion = 2;
}
@@ -870,13 +1113,15 @@
if (userVersion < 6) {
final boolean splitSystemUser = UserManager.isSplitSystemUser();
- for (int i = 0; i < mUsers.size(); i++) {
- UserInfo user = mUsers.valueAt(i);
- // In non-split mode, only user 0 can have restricted profiles
- if (!splitSystemUser && user.isRestricted()
- && (user.restrictedProfileParentId == UserInfo.NO_PROFILE_GROUP_ID)) {
- user.restrictedProfileParentId = UserHandle.USER_SYSTEM;
- scheduleWriteUserLocked(user);
+ synchronized (mUsersLock) {
+ for (int i = 0; i < mUsers.size(); i++) {
+ UserInfo user = mUsers.valueAt(i);
+ // In non-split mode, only user 0 can have restricted profiles
+ if (!splitSystemUser && user.isRestricted()
+ && (user.restrictedProfileParentId == UserInfo.NO_PROFILE_GROUP_ID)) {
+ user.restrictedProfileParentId = UserHandle.USER_SYSTEM;
+ scheduleWriteUser(user);
+ }
}
}
userVersion = 6;
@@ -887,11 +1132,11 @@
+ USER_VERSION);
} else {
mUserVersion = userVersion;
- writeUserListLocked();
+ writeUserListLILP();
}
}
- private void fallbackToSingleUserLocked() {
+ private void fallbackToSingleUserLILP() {
int flags = UserInfo.FLAG_INITIALIZED;
// In split system user mode, the admin and primary flags are assigned to the first human
// user.
@@ -902,21 +1147,27 @@
UserInfo system = new UserInfo(UserHandle.USER_SYSTEM,
mContext.getResources().getString(com.android.internal.R.string.owner_name), null,
flags);
- mUsers.put(system.id, system);
+ synchronized (mUsersLock) {
+ mUsers.put(system.id, system);
+ }
mNextSerialNumber = MIN_USER_ID;
mUserVersion = USER_VERSION;
Bundle restrictions = new Bundle();
- mUserRestrictions.append(UserHandle.USER_SYSTEM, restrictions);
+ synchronized (mRestrictionsLock) {
+ mBaseUserRestrictions.append(UserHandle.USER_SYSTEM, restrictions);
+ }
- updateUserIdsLocked();
+ updateUserIds();
initDefaultGuestRestrictions();
- writeUserListLocked();
- writeUserLocked(system);
+ writeUserListLILP();
+ writeUserLP(system);
}
- private void scheduleWriteUserLocked(UserInfo userInfo) {
+ private void scheduleWriteUser(UserInfo userInfo) {
+ // No need to wrap it within a lock -- worst case, we'll just post the same message
+ // twice.
if (!mHandler.hasMessages(WRITE_USER_MSG, userInfo)) {
Message msg = mHandler.obtainMessage(WRITE_USER_MSG, userInfo);
mHandler.sendMessageDelayed(msg, WRITE_USER_DELAY);
@@ -930,7 +1181,7 @@
* <name>Primary</name>
* </user>
*/
- private void writeUserLocked(UserInfo userInfo) {
+ private void writeUserLP(UserInfo userInfo) {
FileOutputStream fos = null;
AtomicFile userFile = new AtomicFile(new File(mUsersDir, userInfo.id + XML_SUFFIX));
try {
@@ -970,9 +1221,13 @@
serializer.startTag(null, TAG_NAME);
serializer.text(userInfo.name);
serializer.endTag(null, TAG_NAME);
- Bundle restrictions = mUserRestrictions.get(userInfo.id);
+ Bundle restrictions;
+ synchronized (mRestrictionsLock) {
+ restrictions = mBaseUserRestrictions.get(userInfo.id);
+ }
if (restrictions != null) {
- UserRestrictionsUtils.writeRestrictions(serializer, restrictions, TAG_RESTRICTIONS);
+ UserRestrictionsUtils
+ .writeRestrictions(serializer, restrictions, TAG_RESTRICTIONS);
}
serializer.endTag(null, TAG_USER);
@@ -992,7 +1247,8 @@
* <user id="2"></user>
* </users>
*/
- private void writeUserListLocked() {
+ private void writeUserListLILP() {
+ // TODO Investigate removing a dependency on mInstallLock
FileOutputStream fos = null;
AtomicFile userListFile = new AtomicFile(mUserListFile);
try {
@@ -1013,11 +1269,17 @@
UserRestrictionsUtils
.writeRestrictions(serializer, mGuestRestrictions, TAG_RESTRICTIONS);
serializer.endTag(null, TAG_GUEST_RESTRICTIONS);
- final int userSize = mUsers.size();
- for (int i = 0; i < userSize; i++) {
- UserInfo user = mUsers.valueAt(i);
+ int[] userIdsToWrite;
+ synchronized (mUsersLock) {
+ userIdsToWrite = new int[mUsers.size()];
+ for (int i = 0; i < userIdsToWrite.length; i++) {
+ UserInfo user = mUsers.valueAt(i);
+ userIdsToWrite[i] = user.id;
+ }
+ }
+ for (int id : userIdsToWrite) {
serializer.startTag(null, TAG_USER);
- serializer.attribute(null, ATTR_ID, Integer.toString(user.id));
+ serializer.attribute(null, ATTR_ID, Integer.toString(id));
serializer.endTag(null, TAG_USER);
}
@@ -1031,7 +1293,7 @@
}
}
- private UserInfo readUserLocked(int id) {
+ private UserInfo readUserLILP(int id) {
int flags = 0;
int serialNumber = id;
String name = null;
@@ -1112,7 +1374,9 @@
userInfo.guestToRemove = guestToRemove;
userInfo.profileGroupId = profileGroupId;
userInfo.restrictedProfileParentId = restrictedProfileParentId;
- mUserRestrictions.append(id, restrictions);
+ synchronized (mRestrictionsLock) {
+ mBaseUserRestrictions.append(id, restrictions);
+ }
return userInfo;
} catch (IOException ioe) {
@@ -1223,20 +1487,22 @@
synchronized (mPackagesLock) {
UserInfo parent = null;
if (parentId != UserHandle.USER_NULL) {
- parent = getUserInfoLocked(parentId);
+ synchronized (mUsersLock) {
+ parent = getUserInfoLU(parentId);
+ }
if (parent == null) return null;
}
- if (isManagedProfile && !canAddMoreManagedProfiles(parentId)) {
+ if (isManagedProfile && !canAddMoreManagedProfiles(parentId, false)) {
Log.e(LOG_TAG, "Cannot add more managed profiles for user " + parentId);
return null;
}
- if (!isGuest && !isManagedProfile && isUserLimitReachedLocked()) {
+ if (!isGuest && !isManagedProfile && isUserLimitReached()) {
// If we're not adding a guest user or a managed profile and the limit has
// been reached, cannot add a user.
return null;
}
// If we're adding a guest and there already exists one, bail.
- if (isGuest && findCurrentGuestUserLocked() != null) {
+ if (isGuest && findCurrentGuestUser() != null) {
return null;
}
// In legacy mode, restricted profile's parent can only be the owner user
@@ -1270,7 +1536,7 @@
flags |= UserInfo.FLAG_ADMIN;
}
}
- userId = getNextAvailableIdLocked();
+ userId = getNextAvailableId();
userInfo = new UserInfo(userId, name, null, flags);
userInfo.serialNumber = mNextSerialNumber++;
long now = System.currentTimeMillis();
@@ -1278,12 +1544,12 @@
userInfo.partial = true;
Environment.getUserSystemDirectory(userInfo.id).mkdirs();
mUsers.put(userId, userInfo);
- writeUserListLocked();
+ writeUserListLILP();
if (parent != null) {
if (isManagedProfile) {
if (parent.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID) {
parent.profileGroupId = parent.id;
- scheduleWriteUserLocked(parent);
+ scheduleWriteUser(parent);
}
userInfo.profileGroupId = parent.profileGroupId;
} else if (isRestricted) {
@@ -1292,18 +1558,20 @@
}
if (parent.restrictedProfileParentId == UserInfo.NO_PROFILE_GROUP_ID) {
parent.restrictedProfileParentId = parent.id;
- scheduleWriteUserLocked(parent);
+ scheduleWriteUser(parent);
}
userInfo.restrictedProfileParentId = parent.restrictedProfileParentId;
}
}
+
final StorageManager storage = mContext.getSystemService(StorageManager.class);
+ storage.createUserKey(userId, userInfo.serialNumber);
for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
final String volumeUuid = vol.getFsUuid();
try {
final File userDir = Environment.getDataUserDirectory(volumeUuid,
userId);
- prepareUserDirectory(mContext, volumeUuid, userId);
+ storage.prepareUserStorage(volumeUuid, userId, userInfo.serialNumber);
enforceSerialNumber(userDir, userInfo.serialNumber);
} catch (IOException e) {
Log.wtf(LOG_TAG, "Failed to create user directory on " + volumeUuid, e);
@@ -1311,10 +1579,12 @@
}
mPm.createNewUserLILPw(userId);
userInfo.partial = false;
- scheduleWriteUserLocked(userInfo);
- updateUserIdsLocked();
+ scheduleWriteUser(userInfo);
+ updateUserIds();
Bundle restrictions = new Bundle();
- mUserRestrictions.append(userId, restrictions);
+ synchronized (mRestrictionsLock) {
+ mBaseUserRestrictions.append(userId, restrictions);
+ }
}
}
mPm.newUserCreated(userId);
@@ -1353,12 +1623,14 @@
* Find the current guest user. If the Guest user is partial,
* then do not include it in the results as it is about to die.
*/
- private UserInfo findCurrentGuestUserLocked() {
- final int size = mUsers.size();
- for (int i = 0; i < size; i++) {
- final UserInfo user = mUsers.valueAt(i);
- if (user.isGuest() && !user.guestToRemove && !mRemovingUserIds.get(user.id)) {
- return user;
+ private UserInfo findCurrentGuestUser() {
+ synchronized (mUsersLock) {
+ final int size = mUsers.size();
+ for (int i = 0; i < size; i++) {
+ final UserInfo user = mUsers.valueAt(i);
+ if (user.isGuest() && !user.guestToRemove && !mRemovingUserIds.get(user.id)) {
+ return user;
+ }
}
}
return null;
@@ -1382,9 +1654,11 @@
try {
final UserInfo user;
synchronized (mPackagesLock) {
- user = mUsers.get(userHandle);
- if (userHandle == 0 || user == null || mRemovingUserIds.get(userHandle)) {
- return false;
+ synchronized (mUsersLock) {
+ user = mUsers.get(userHandle);
+ if (userHandle == 0 || user == null || mRemovingUserIds.get(userHandle)) {
+ return false;
+ }
}
if (!user.isGuest()) {
return false;
@@ -1398,7 +1672,7 @@
// Mark it as disabled, so that it isn't returned any more when
// profiles are queried.
user.flags |= UserInfo.FLAG_DISABLED;
- writeUserLocked(user);
+ writeUserLP(user);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -1428,15 +1702,17 @@
return false;
}
synchronized (mPackagesLock) {
- user = mUsers.get(userHandle);
- if (userHandle == 0 || user == null || mRemovingUserIds.get(userHandle)) {
- return false;
- }
+ synchronized (mUsersLock) {
+ user = mUsers.get(userHandle);
+ if (userHandle == 0 || user == null || mRemovingUserIds.get(userHandle)) {
+ return false;
+ }
- // We remember deleted user IDs to prevent them from being
- // reused during the current boot; they can still be reused
- // after a reboot.
- mRemovingUserIds.put(userHandle, true);
+ // We remember deleted user IDs to prevent them from being
+ // reused during the current boot; they can still be reused
+ // after a reboot.
+ mRemovingUserIds.put(userHandle, true);
+ }
try {
mAppOpsService.removeUser(userHandle);
@@ -1450,7 +1726,7 @@
// Mark it as disabled, so that it isn't returned any more when
// profiles are queried.
user.flags |= UserInfo.FLAG_DISABLED;
- writeUserLocked(user);
+ writeUserLP(user);
}
if (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
@@ -1508,7 +1784,7 @@
.onUserRemoved(userHandle);
synchronized (mInstallLock) {
synchronized (mPackagesLock) {
- removeUserStateLocked(userHandle);
+ removeUserStateLILP(userHandle);
}
}
}
@@ -1522,20 +1798,21 @@
}
}
- private void removeUserStateLocked(final int userHandle) {
- mContext.getSystemService(StorageManager.class)
- .deleteUserKey(userHandle);
+ private void removeUserStateLILP(final int userHandle) {
+ mContext.getSystemService(StorageManager.class).destroyUserKey(userHandle);
// Cleanup package manager settings
mPm.cleanUpUserLILPw(this, userHandle);
// Remove this user from the list
- mUsers.remove(userHandle);
+ synchronized (mUsersLock) {
+ mUsers.remove(userHandle);
+ }
// Remove user file
AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + XML_SUFFIX));
userFile.delete();
// Update the user list
- writeUserListLocked();
- updateUserIdsLocked();
+ writeUserListLILP();
+ updateUserIds();
removeDirectoryRecursive(Environment.getUserSystemDirectory(userHandle));
}
@@ -1567,27 +1844,24 @@
public Bundle getApplicationRestrictionsForUser(String packageName, int userId) {
if (UserHandle.getCallingUserId() != userId
|| !UserHandle.isSameApp(Binder.getCallingUid(), getUidForPackage(packageName))) {
- checkManageUsersPermission("Only system can get restrictions for other users/apps");
+ checkManageUsersPermission("get application restrictions for other users/apps");
}
synchronized (mPackagesLock) {
// Read the restrictions from XML
- return readApplicationRestrictionsLocked(packageName, userId);
+ return readApplicationRestrictionsLP(packageName, userId);
}
}
@Override
public void setApplicationRestrictions(String packageName, Bundle restrictions,
int userId) {
- if (UserHandle.getCallingUserId() != userId
- || !UserHandle.isSameApp(Binder.getCallingUid(), getUidForPackage(packageName))) {
- checkManageUsersPermission("Only system can set restrictions for other users/apps");
- }
+ checkManageUsersPermission("set application restrictions");
synchronized (mPackagesLock) {
if (restrictions == null || restrictions.isEmpty()) {
cleanAppRestrictionsForPackage(packageName, userId);
} else {
// Write the restrictions to XML
- writeApplicationRestrictionsLocked(packageName, restrictions, userId);
+ writeApplicationRestrictionsLP(packageName, restrictions, userId);
}
}
@@ -1600,25 +1874,6 @@
}
}
- @Override
- public void removeRestrictions() {
- checkManageUsersPermission("Only system can remove restrictions");
- final int userHandle = UserHandle.getCallingUserId();
- removeRestrictionsForUser(userHandle, true);
- }
-
- private void removeRestrictionsForUser(final int userHandle, boolean unhideApps) {
- synchronized (mPackagesLock) {
- // Remove all user restrictions
- setUserRestrictions(new Bundle(), userHandle);
- // Remove any app restrictions
- cleanAppRestrictions(userHandle);
- }
- if (unhideApps) {
- unhideAllInstalledAppsForUser(userHandle);
- }
- }
-
private void unhideAllInstalledAppsForUser(final int userHandle) {
mHandler.post(new Runnable() {
@Override
@@ -1654,16 +1909,15 @@
}
}
- private Bundle readApplicationRestrictionsLocked(String packageName,
- int userId) {
+ private Bundle readApplicationRestrictionsLP(String packageName, int userId) {
AtomicFile restrictionsFile =
new AtomicFile(new File(Environment.getUserSystemDirectory(userId),
packageToRestrictionsFileName(packageName)));
- return readApplicationRestrictionsLocked(restrictionsFile);
+ return readApplicationRestrictionsLP(restrictionsFile);
}
@VisibleForTesting
- static Bundle readApplicationRestrictionsLocked(AtomicFile restrictionsFile) {
+ static Bundle readApplicationRestrictionsLP(AtomicFile restrictionsFile) {
final Bundle restrictions = new Bundle();
final ArrayList<String> values = new ArrayList<>();
if (!restrictionsFile.getBaseFile().exists()) {
@@ -1746,17 +2000,16 @@
return childBundle;
}
- private void writeApplicationRestrictionsLocked(String packageName,
+ private void writeApplicationRestrictionsLP(String packageName,
Bundle restrictions, int userId) {
AtomicFile restrictionsFile = new AtomicFile(
new File(Environment.getUserSystemDirectory(userId),
packageToRestrictionsFileName(packageName)));
- writeApplicationRestrictionsLocked(restrictions, restrictionsFile);
+ writeApplicationRestrictionsLP(restrictions, restrictionsFile);
}
@VisibleForTesting
- static void writeApplicationRestrictionsLocked(Bundle restrictions,
- AtomicFile restrictionsFile) {
+ static void writeApplicationRestrictionsLP(Bundle restrictions, AtomicFile restrictionsFile) {
FileOutputStream fos = null;
try {
fos = restrictionsFile.startWrite();
@@ -1826,17 +2079,17 @@
@Override
public int getUserSerialNumber(int userHandle) {
- synchronized (mPackagesLock) {
+ synchronized (mUsersLock) {
if (!exists(userHandle)) return -1;
- return getUserInfoLocked(userHandle).serialNumber;
+ return getUserInfoLU(userHandle).serialNumber;
}
}
@Override
public int getUserHandle(int userSerialNumber) {
- synchronized (mPackagesLock) {
+ synchronized (mUsersLock) {
for (int userId : mUserIds) {
- UserInfo info = getUserInfoLocked(userId);
+ UserInfo info = getUserInfoLU(userId);
if (info != null && info.serialNumber == userSerialNumber) return userId;
}
// Not found
@@ -1848,13 +2101,13 @@
public long getUserCreationTime(int userHandle) {
int callingUserId = UserHandle.getCallingUserId();
UserInfo userInfo = null;
- synchronized (mPackagesLock) {
+ synchronized (mUsersLock) {
if (callingUserId == userHandle) {
- userInfo = getUserInfoLocked(userHandle);
+ userInfo = getUserInfoLU(userHandle);
} else {
- UserInfo parent = getProfileParentLocked(userHandle);
+ UserInfo parent = getProfileParentLU(userHandle);
if (parent != null && parent.id == callingUserId) {
- userInfo = getUserInfoLocked(userHandle);
+ userInfo = getUserInfoLU(userHandle);
}
}
}
@@ -1868,22 +2121,24 @@
/**
* Caches the list of user ids in an array, adjusting the array size when necessary.
*/
- private void updateUserIdsLocked() {
+ private void updateUserIds() {
int num = 0;
- final int userSize = mUsers.size();
- for (int i = 0; i < userSize; i++) {
- if (!mUsers.valueAt(i).partial) {
- num++;
+ synchronized (mUsersLock) {
+ final int userSize = mUsers.size();
+ for (int i = 0; i < userSize; i++) {
+ if (!mUsers.valueAt(i).partial) {
+ num++;
+ }
}
- }
- final int[] newUsers = new int[num];
- int n = 0;
- for (int i = 0; i < userSize; i++) {
- if (!mUsers.valueAt(i).partial) {
- newUsers[n++] = mUsers.keyAt(i);
+ final int[] newUsers = new int[num];
+ int n = 0;
+ for (int i = 0; i < userSize; i++) {
+ if (!mUsers.valueAt(i).partial) {
+ newUsers[n++] = mUsers.keyAt(i);
+ }
}
+ mUserIds = newUsers;
}
- mUserIds = newUsers;
}
/**
@@ -1892,7 +2147,7 @@
*/
public void onUserForeground(int userId) {
synchronized (mPackagesLock) {
- UserInfo user = mUsers.get(userId);
+ UserInfo user = getUserInfoNoChecks(userId);
long now = System.currentTimeMillis();
if (user == null || user.partial) {
Slog.w(LOG_TAG, "userForeground: unknown user #" + userId);
@@ -1900,7 +2155,7 @@
}
if (now > EPOCH_PLUS_30_YEARS) {
user.lastLoggedInTime = now;
- scheduleWriteUserLocked(user);
+ scheduleWriteUser(user);
}
}
}
@@ -1911,8 +2166,8 @@
* for data and battery stats collection, or unexpected cross-talk.
* @return
*/
- private int getNextAvailableIdLocked() {
- synchronized (mPackagesLock) {
+ private int getNextAvailableId() {
+ synchronized (mUsersLock) {
int i = MIN_USER_ID;
while (i < MAX_USER_ID) {
if (mUsers.indexOfKey(i) < 0 && !mRemovingUserIds.get(i)) {
@@ -1929,16 +2184,6 @@
}
/**
- * Create new {@code /data/user/[id]} directory and sets default
- * permissions.
- */
- public static void prepareUserDirectory(Context context, String volumeUuid, int userId) {
- final StorageManager storage = context.getSystemService(StorageManager.class);
- final File userDir = Environment.getDataUserDirectory(volumeUuid, userId);
- storage.createNewUserDir(userId, userDir);
- }
-
- /**
* Enforce that serial number stored in user directory inode matches the
* given expected value. Gracefully sets the serial number if currently
* undefined.
@@ -2004,6 +2249,45 @@
}
@Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out,
+ FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
+ (new Shell()).exec(this, in, out, err, args, resultReceiver);
+ }
+
+ int onShellCommand(Shell shell, String cmd) {
+ if (cmd == null) {
+ return shell.handleDefaultCommands(cmd);
+ }
+
+ final PrintWriter pw = shell.getOutPrintWriter();
+ try {
+ switch(cmd) {
+ case "list":
+ return runList(pw);
+ }
+ } catch (RemoteException e) {
+ pw.println("Remote exception: " + e);
+ }
+ return -1;
+ }
+
+ private int runList(PrintWriter pw) throws RemoteException {
+ final IActivityManager am = ActivityManagerNative.getDefault();
+ final List<UserInfo> users = getUsers(false);
+ if (users == null) {
+ pw.println("Error: couldn't get users");
+ return 1;
+ } else {
+ pw.println("Users:");
+ for (int i = 0; i < users.size(); i++) {
+ String running = am.isUserRunning(users.get(i).id, 0) ? " running" : "";
+ pw.println("\t" + users.get(i).toString() + running);
+ }
+ return 0;
+ }
+ }
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
@@ -2018,35 +2302,49 @@
long now = System.currentTimeMillis();
StringBuilder sb = new StringBuilder();
synchronized (mPackagesLock) {
- pw.println("Users:");
- for (int i = 0; i < mUsers.size(); i++) {
- UserInfo user = mUsers.valueAt(i);
- if (user == null) continue;
- pw.print(" "); pw.print(user); pw.print(" serialNo="); pw.print(user.serialNumber);
- if (mRemovingUserIds.get(mUsers.keyAt(i))) pw.print(" <removing> ");
- if (user.partial) pw.print(" <partial>");
- pw.println();
- pw.print(" Created: ");
- if (user.creationTime == 0) {
- pw.println("<unknown>");
- } else {
- sb.setLength(0);
- TimeUtils.formatDuration(now - user.creationTime, sb);
- sb.append(" ago");
- pw.println(sb);
+ synchronized (mUsersLock) {
+ pw.println("Users:");
+ for (int i = 0; i < mUsers.size(); i++) {
+ UserInfo user = mUsers.valueAt(i);
+ if (user == null) {
+ continue;
+ }
+ pw.print(" "); pw.print(user);
+ pw.print(" serialNo="); pw.print(user.serialNumber);
+ if (mRemovingUserIds.get(mUsers.keyAt(i))) {
+ pw.print(" <removing> ");
+ }
+ if (user.partial) {
+ pw.print(" <partial>");
+ }
+ pw.println();
+ pw.print(" Created: ");
+ if (user.creationTime == 0) {
+ pw.println("<unknown>");
+ } else {
+ sb.setLength(0);
+ TimeUtils.formatDuration(now - user.creationTime, sb);
+ sb.append(" ago");
+ pw.println(sb);
+ }
+ pw.print(" Last logged in: ");
+ if (user.lastLoggedInTime == 0) {
+ pw.println("<unknown>");
+ } else {
+ sb.setLength(0);
+ TimeUtils.formatDuration(now - user.lastLoggedInTime, sb);
+ sb.append(" ago");
+ pw.println(sb);
+ }
+ pw.println(" Restrictions:");
+ synchronized (mRestrictionsLock) {
+ UserRestrictionsUtils.dumpRestrictions(
+ pw, " ", mBaseUserRestrictions.get(user.id));
+ pw.println(" Effective restrictions:");
+ UserRestrictionsUtils.dumpRestrictions(
+ pw, " ", mCachedEffectiveUserRestrictions.get(user.id));
+ }
}
- pw.print(" Last logged in: ");
- if (user.lastLoggedInTime == 0) {
- pw.println("<unknown>");
- } else {
- sb.setLength(0);
- TimeUtils.formatDuration(now - user.lastLoggedInTime, sb);
- sb.append(" ago");
- pw.println(sb);
- }
- pw.println(" Restrictions:");
- UserRestrictionsUtils.dumpRestrictions(
- pw, " ", mUserRestrictions.get(user.id));
}
pw.println();
pw.println("Guest restrictions:");
@@ -2063,9 +2361,9 @@
removeMessages(WRITE_USER_MSG, msg.obj);
synchronized (mPackagesLock) {
int userId = ((UserInfo) msg.obj).id;
- UserInfo userInfo = mUsers.get(userId);
+ UserInfo userInfo = getUserInfoNoChecks(userId);
if (userInfo != null) {
- writeUserLocked(userInfo);
+ writeUserLP(userInfo);
}
}
}
@@ -2079,4 +2377,86 @@
boolean isInitialized(int userId) {
return (getUserInfo(userId).flags & UserInfo.FLAG_INITIALIZED) != 0;
}
+
+ private class LocalService extends UserManagerInternal {
+
+ @Override
+ public Object getUserRestrictionsLock() {
+ return mRestrictionsLock;
+ }
+
+ @Override
+ @GuardedBy("mRestrictionsLock")
+ public void updateEffectiveUserRestrictionsLR(int userId) {
+ UserManagerService.this.updateEffectiveUserRestrictionsLR(userId);
+ }
+
+ @Override
+ @GuardedBy("mRestrictionsLock")
+ public void updateEffectiveUserRestrictionsForAllUsersLR() {
+ UserManagerService.this.updateEffectiveUserRestrictionsForAllUsersLR();
+ }
+
+ @Override
+ public Bundle getBaseUserRestrictions(int userId) {
+ synchronized (mRestrictionsLock) {
+ return mBaseUserRestrictions.get(userId);
+ }
+ }
+
+ @Override
+ public void setBaseUserRestrictionsByDpmsForMigration(
+ int userId, Bundle baseRestrictions) {
+ synchronized (mRestrictionsLock) {
+ mBaseUserRestrictions.put(userId, new Bundle(baseRestrictions));
+ invalidateEffectiveUserRestrictionsLR(userId);
+ }
+
+ final UserInfo userInfo = getUserInfoNoChecks(userId);
+ synchronized (mPackagesLock) {
+ if (userInfo != null) {
+ writeUserLP(userInfo);
+ } else {
+ Slog.w(LOG_TAG, "UserInfo not found for " + userId);
+ }
+ }
+ }
+
+ @Override
+ public boolean getUserRestriction(int userId, String key) {
+ return getUserRestrictions(userId).getBoolean(key);
+ }
+
+ @Override
+ public void addUserRestrictionsListener(UserRestrictionsListener listener) {
+ synchronized (mUserRestrictionsListeners) {
+ mUserRestrictionsListeners.add(listener);
+ }
+ }
+
+ @Override
+ public void removeUserRestrictionsListener(UserRestrictionsListener listener) {
+ synchronized (mUserRestrictionsListeners) {
+ mUserRestrictionsListeners.remove(listener);
+ }
+ }
+ }
+
+ private class Shell extends ShellCommand {
+ @Override
+ public int onCommand(String cmd) {
+ return onShellCommand(this, cmd);
+ }
+
+ @Override
+ public void onHelp() {
+ final PrintWriter pw = getOutPrintWriter();
+ pw.println("User manager (user) commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println("");
+ pw.println(" list");
+ pw.println(" Prints all users on the system.");
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index db1fd2e..56e8b3e 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -18,9 +18,13 @@
import com.google.android.collect.Sets;
-import com.android.internal.util.Preconditions;
-
+import android.content.ContentResolver;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Binder;
import android.os.Bundle;
+import android.os.SystemProperties;
+import android.os.UserHandle;
import android.os.UserManager;
import org.xmlpull.v1.XmlPullParser;
@@ -30,7 +34,14 @@
import java.io.PrintWriter;
import java.util.Set;
+/**
+ * Utility methods for uesr restrictions.
+ *
+ * <p>See {@link UserManagerService} for the method suffixes.
+ */
public class UserRestrictionsUtils {
+ private static final String TAG = "UserRestrictionsUtils";
+
private UserRestrictionsUtils() {
}
@@ -86,7 +97,6 @@
String tag) throws IOException {
serializer.startTag(null, tag);
for (String key : USER_RESTRICTIONS) {
- //
if (restrictions.getBoolean(key)
&& !NON_PERSIST_USER_RESTRICTIONS.contains(key)) {
serializer.attribute(null, key, "true");
@@ -105,6 +115,124 @@
}
}
+ public static void merge(Bundle dest, Bundle in) {
+ if (in == null) {
+ return;
+ }
+ for (String key : in.keySet()) {
+ if (in.getBoolean(key, false)) {
+ dest.putBoolean(key, true);
+ }
+ }
+ }
+
+ /**
+ * Takes a new use restriction set and the previous set, and apply the restrictions that have
+ * changed.
+ *
+ * <p>Note this method is called by {@link UserManagerService} while holding
+ * {@code mRestrictionLock}. Be aware when calling into other services, which could cause
+ * a deadlock.
+ */
+ public static void applyUserRestrictionsLR(Context context, int userId,
+ Bundle newRestrictions, Bundle prevRestrictions) {
+ for (String key : USER_RESTRICTIONS) {
+ final boolean newValue = newRestrictions.getBoolean(key);
+ final boolean prevValue = prevRestrictions.getBoolean(key);
+
+ if (newValue != prevValue) {
+ applyUserRestrictionLR(context, userId, key, newValue);
+ }
+ }
+ }
+
+ /**
+ * Apply each user restriction.
+ *
+ * <p>Note this method is called by {@link UserManagerService} while holding
+ * {@code mRestrictionLock}. Be aware when calling into other services, which could cause
+ * a deadlock.
+ */
+ private static void applyUserRestrictionLR(Context context, int userId, String key,
+ boolean newValue) {
+ // When certain restrictions are cleared, we don't update the system settings,
+ // because these settings are changeable on the Settings UI and we don't know the original
+ // value -- for example LOCATION_MODE might have been off already when the restriction was
+ // set, and in that case even if the restriction is lifted, changing it to ON would be
+ // wrong. So just don't do anything in such a case. If the user hopes to enable location
+ // later, they can do it on the Settings UI.
+
+ final ContentResolver cr = context.getContentResolver();
+ final long id = Binder.clearCallingIdentity();
+ try {
+ switch (key) {
+ case UserManager.DISALLOW_CONFIG_WIFI:
+ if (newValue) {
+ android.provider.Settings.Secure.putIntForUser(cr,
+ android.provider.Settings.Secure
+ .WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 0, userId);
+ }
+ break;
+ case UserManager.DISALLOW_SHARE_LOCATION:
+ if (newValue) {
+ android.provider.Settings.Secure.putIntForUser(cr,
+ android.provider.Settings.Secure.LOCATION_MODE,
+ android.provider.Settings.Secure.LOCATION_MODE_OFF,
+ userId);
+ android.provider.Settings.Secure.putStringForUser(cr,
+ android.provider.Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "",
+ userId);
+ }
+ // Send out notifications as some clients may want to reread the
+ // value which actually changed due to a restriction having been
+ // applied.
+ final String property =
+ android.provider.Settings.Secure.SYS_PROP_SETTING_VERSION;
+ long version = SystemProperties.getLong(property, 0) + 1;
+ SystemProperties.set(property, Long.toString(version));
+
+ final String name = android.provider.Settings.Secure.LOCATION_PROVIDERS_ALLOWED;
+ final Uri url = Uri.withAppendedPath(
+ android.provider.Settings.Secure.CONTENT_URI, name);
+ context.getContentResolver().notifyChange(url, null, true, userId);
+
+ break;
+ case UserManager.DISALLOW_DEBUGGING_FEATURES:
+ if (newValue) {
+ // Only disable adb if changing for system user, since it is global
+ // TODO: should this be admin user?
+ if (userId == UserHandle.USER_SYSTEM) {
+ android.provider.Settings.Global.putStringForUser(cr,
+ android.provider.Settings.Global.ADB_ENABLED, "0",
+ userId);
+ }
+ }
+ break;
+ case UserManager.ENSURE_VERIFY_APPS:
+ if (newValue) {
+ android.provider.Settings.Global.putStringForUser(
+ context.getContentResolver(),
+ android.provider.Settings.Global.PACKAGE_VERIFIER_ENABLE, "1",
+ userId);
+ android.provider.Settings.Global.putStringForUser(
+ context.getContentResolver(),
+ android.provider.Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, "1",
+ userId);
+ }
+ break;
+ case UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES:
+ if (newValue) {
+ android.provider.Settings.Secure.putIntForUser(cr,
+ android.provider.Settings.Secure.INSTALL_NON_MARKET_APPS, 0,
+ userId);
+ }
+ break;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(id);
+ }
+ }
+
public static void dumpRestrictions(PrintWriter pw, String prefix, Bundle restrictions) {
boolean noneSet = true;
if (restrictions != null) {
@@ -114,9 +242,11 @@
noneSet = false;
}
}
- }
- if (noneSet) {
- pw.println(prefix + "none");
+ if (noneSet) {
+ pw.println(prefix + "none");
+ }
+ } else {
+ pw.println(prefix + "null");
}
}
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 16add37..121ef21 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -16,8 +16,8 @@
package com.android.server.policy;
-import static android.app.ActivityManager.DOCKED_STACK_ID;
-import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.view.WindowManager.LayoutParams.*;
import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
@@ -2532,6 +2532,12 @@
return R.anim.dock_right_enter;
}
}
+ } else if (win.getAttrs().type == TYPE_DOCK_DIVIDER) {
+ if (transit == TRANSIT_ENTER) {
+ return R.anim.fade_in;
+ } else if (transit == TRANSIT_EXIT) {
+ return R.anim.fade_out;
+ }
}
if (transit == TRANSIT_PREVIEW_DONE) {
@@ -3016,7 +3022,7 @@
// Display task switcher for ALT-TAB.
if (down && repeatCount == 0 && keyCode == KeyEvent.KEYCODE_TAB) {
- if (mRecentAppsHeldModifiers == 0 && !keyguardOn) {
+ if (mRecentAppsHeldModifiers == 0 && !keyguardOn && isUserSetupComplete()) {
final int shiftlessModifiers = event.getModifiers() & ~KeyEvent.META_SHIFT_MASK;
if (KeyEvent.metaStateHasModifiers(shiftlessModifiers, KeyEvent.META_ALT_ON)) {
mRecentAppsHeldModifiers = shiftlessModifiers;
@@ -4614,7 +4620,8 @@
final int fl = PolicyControl.getWindowFlags(null, lp);
if (localLOGV) {
Slog.d(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrameLw()
- + " shown frame: " + mTopFullscreenOpaqueWindowState.getShownFrameLw());
+ + " shown position: "
+ + mTopFullscreenOpaqueWindowState.getShownPositionLw());
Slog.d(TAG, "attr: " + mTopFullscreenOpaqueWindowState.getAttrs()
+ " lp.flags=0x" + Integer.toHexString(fl));
}
diff --git a/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java b/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java
index 4ae3154..b06fe58 100644
--- a/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java
+++ b/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java
@@ -22,6 +22,7 @@
import android.os.SystemClock;
import android.util.Slog;
import android.view.GestureDetector;
+import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.WindowManagerPolicy.PointerEventListener;
import android.widget.OverScroller;
@@ -130,8 +131,7 @@
}
break;
case MotionEvent.ACTION_HOVER_MOVE:
- if (event.getPointerCount() == 1
- && event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE) {
+ if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
if (!mMouseHoveringAtEdge && event.getY() == 0) {
mCallbacks.onMouseHoverAtTop();
mMouseHoveringAtEdge = true;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index ced0433..6498dd9 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -444,6 +444,9 @@
// True if we are currently in device idle mode.
private boolean mDeviceIdleMode;
+ // True if we are currently in light device idle mode.
+ private boolean mLightDeviceIdleMode;
+
// Set of app ids that we will always respect the wake locks for.
int[] mDeviceIdleWhitelist = new int[0];
@@ -2292,12 +2295,18 @@
}
}
- private boolean isDeviceIdleModeInternal() {
+ boolean isDeviceIdleModeInternal() {
synchronized (mLock) {
return mDeviceIdleMode;
}
}
+ boolean isLightDeviceIdleModeInternal() {
+ synchronized (mLock) {
+ return mLightDeviceIdleMode;
+ }
+ }
+
private void handleBatteryStateChangedLocked() {
mDirty |= DIRTY_BATTERY_STATE;
updatePowerStateLocked();
@@ -2368,7 +2377,7 @@
}
}
- void setDeviceIdleModeInternal(boolean enabled) {
+ boolean setDeviceIdleModeInternal(boolean enabled) {
synchronized (mLock) {
if (mDeviceIdleMode != enabled) {
mDeviceIdleMode = enabled;
@@ -2378,7 +2387,19 @@
} else {
EventLogTags.writeDeviceIdleOffPhase("power");
}
+ return true;
}
+ return false;
+ }
+ }
+
+ boolean setLightDeviceIdleModeInternal(boolean enabled) {
+ synchronized (mLock) {
+ if (mLightDeviceIdleMode != enabled) {
+ mLightDeviceIdleMode = enabled;
+ return true;
+ }
+ return false;
}
}
@@ -2671,6 +2692,7 @@
pw.println(" mSandmanSummoned=" + mSandmanSummoned);
pw.println(" mLowPowerModeEnabled=" + mLowPowerModeEnabled);
pw.println(" mBatteryLevelLow=" + mBatteryLevelLow);
+ pw.println(" mLightDeviceIdleMode=" + mLightDeviceIdleMode);
pw.println(" mDeviceIdleMode=" + mDeviceIdleMode);
pw.println(" mDeviceIdleWhitelist=" + Arrays.toString(mDeviceIdleWhitelist));
pw.println(" mDeviceIdleTempWhitelist=" + Arrays.toString(mDeviceIdleTempWhitelist));
@@ -3308,6 +3330,16 @@
}
}
+ @Override // Binder call
+ public boolean isLightDeviceIdleMode() {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return isLightDeviceIdleModeInternal();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
/**
* Reboots the device.
*
@@ -3576,8 +3608,13 @@
}
@Override
- public void setDeviceIdleMode(boolean enabled) {
- setDeviceIdleModeInternal(enabled);
+ public boolean setDeviceIdleMode(boolean enabled) {
+ return setDeviceIdleModeInternal(enabled);
+ }
+
+ @Override
+ public boolean setLightDeviceIdleMode(boolean enabled) {
+ return setLightDeviceIdleModeInternal(enabled);
}
@Override
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index d4c5f87..ac79b36 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -40,6 +40,8 @@
private boolean mRelroReady32Bit = false;
private boolean mRelroReady64Bit = false;
+ private String oldWebViewPackageName = null;
+
private BroadcastReceiver mWebViewUpdatedReceiver;
public WebViewUpdateService(Context context) {
@@ -51,9 +53,22 @@
mWebViewUpdatedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- String webviewPackage = "package:" + WebViewFactory.getWebViewPackageName();
- if (webviewPackage.equals(intent.getDataString())) {
- onWebViewUpdateInstalled();
+
+ for (String packageName : WebViewFactory.getWebViewPackageNames()) {
+ String webviewPackage = "package:" + packageName;
+
+ if (webviewPackage.equals(intent.getDataString())) {
+ String usedPackageName =
+ WebViewFactory.findPreferredWebViewPackage().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.
+ if (packageName.equals(usedPackageName) ||
+ packageName.equals(oldWebViewPackageName)) {
+ onWebViewUpdateInstalled();
+ oldWebViewPackageName = usedPackageName;
+ }
+ return;
+ }
}
}
};
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 66ae9ef..d713751 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -206,8 +206,8 @@
sTempFloats[Matrix.MSKEW_Y] = windowState.mWinAnimator.mDtDx;
sTempFloats[Matrix.MSKEW_X] = windowState.mWinAnimator.mDsDy;
sTempFloats[Matrix.MSCALE_Y] = windowState.mWinAnimator.mDtDy;
- sTempFloats[Matrix.MTRANS_X] = windowState.mShownFrame.left;
- sTempFloats[Matrix.MTRANS_Y] = windowState.mShownFrame.top;
+ sTempFloats[Matrix.MTRANS_X] = windowState.mShownPosition.x;
+ sTempFloats[Matrix.MTRANS_Y] = windowState.mShownPosition.y;
sTempFloats[Matrix.MPERSP_0] = 0;
sTempFloats[Matrix.MPERSP_1] = 0;
sTempFloats[Matrix.MPERSP_2] = 1;
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index cc51d20..89f5658 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -16,39 +16,6 @@
package com.android.server.wm;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.Bitmap;
-import android.graphics.Rect;
-import android.os.Debug;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.IRemoteCallback;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.view.AppTransitionAnimationSpec;
-import android.view.WindowManager;
-import android.view.animation.AlphaAnimation;
-import android.view.animation.Animation;
-import android.view.animation.AnimationSet;
-import android.view.animation.AnimationUtils;
-import android.view.animation.ClipRectAnimation;
-import android.view.animation.ClipRectLRAnimation;
-import android.view.animation.ClipRectTBAnimation;
-import android.view.animation.Interpolator;
-import android.view.animation.PathInterpolator;
-import android.view.animation.ScaleAnimation;
-import android.view.animation.TranslateAnimation;
-import android.view.animation.TranslateYAnimation;
-
-import com.android.internal.util.DumpUtils.Dump;
-import com.android.server.AttributeCache;
-import com.android.server.wm.WindowManagerService.H;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-
import static android.view.WindowManagerInternal.AppTransitionListener;
import static com.android.internal.R.styleable.WindowAnimation_activityCloseEnterAnimation;
import static com.android.internal.R.styleable.WindowAnimation_activityCloseExitAnimation;
@@ -73,6 +40,42 @@
import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenEnterAnimation;
import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenExitAnimation;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.os.Debug;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IRemoteCallback;
+import android.os.RemoteException;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.AppTransitionAnimationSpec;
+import android.view.IAppTransitionAnimationSpecsFuture;
+import android.view.WindowManager;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.AnimationSet;
+import android.view.animation.AnimationUtils;
+import android.view.animation.ClipRectAnimation;
+import android.view.animation.ClipRectLRAnimation;
+import android.view.animation.ClipRectTBAnimation;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+import android.view.animation.ScaleAnimation;
+import android.view.animation.TranslateAnimation;
+
+import com.android.internal.util.DumpUtils.Dump;
+import com.android.server.AttributeCache;
+import com.android.server.wm.WindowManagerService.H;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
// State management of app transitions. When we are preparing for a
// transition, mNextAppTransition will be the kind of transition to
// perform or TRANSIT_NONE if we are not waiting. If we are waiting,
@@ -161,6 +164,8 @@
// Used for thumbnail transitions. True if we're scaling up, false if scaling down
private boolean mNextAppTransitionScaleUp;
private IRemoteCallback mNextAppTransitionCallback;
+ private IRemoteCallback mNextAppTransitionFutureCallback;
+ private IRemoteCallback mAnimationFinishedCallback;
private int mNextAppTransitionEnter;
private int mNextAppTransitionExit;
private int mNextAppTransitionInPlace;
@@ -168,6 +173,8 @@
// Keyed by task id.
private final SparseArray<AppTransitionAnimationSpec> mNextAppTransitionAnimationsSpecs
= new SparseArray<>();
+ private IAppTransitionAnimationSpecsFuture mNextAppTransitionAnimationsSpecsFuture;
+ private boolean mNextAppTransitionAnimationsSpecsPending;
private AppTransitionAnimationSpec mDefaultNextAppTransitionAnimationSpec;
private Rect mNextAppTransitionInsets = new Rect();
@@ -175,7 +182,7 @@
private Rect mTmpFromClipRect = new Rect();
private Rect mTmpToClipRect = new Rect();
- private final Rect mTmpStartRect = new Rect();
+ private final Rect mTmpRect = new Rect();
private final static int APP_STATE_IDLE = 0;
private final static int APP_STATE_READY = 1;
@@ -200,10 +207,16 @@
private int mCurrentUserId = 0;
private final ArrayList<AppTransitionListener> mListeners = new ArrayList<>();
+ private final ExecutorService mDefaultExecutor = Executors.newSingleThreadExecutor();
+ private final Object mServiceLock;
+ private final WindowSurfacePlacer mWindowSurfacePlacer;
- AppTransition(Context context, Handler h) {
+ AppTransition(Context context, Handler h, Object serviceLock,
+ WindowSurfacePlacer windowSurfacePlacer) {
mContext = context;
mH = h;
+ mServiceLock = serviceLock;
+ mWindowSurfacePlacer = windowSurfacePlacer;
mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
com.android.internal.R.interpolator.linear_out_slow_in);
mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
@@ -262,6 +275,7 @@
void setReady() {
mAppTransitionState = APP_STATE_READY;
+ fetchAppTransitionSpecsFromFuture();
}
boolean isRunning() {
@@ -282,6 +296,9 @@
Bitmap getAppTransitionThumbnailHeader(int taskId) {
AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(taskId);
+ if (spec == null) {
+ spec = mDefaultNextAppTransitionAnimationSpec;
+ }
return spec != null ? spec.bitmap : null;
}
@@ -296,6 +313,14 @@
return mNextAppTransitionScaleUp;
}
+ /**
+ * @return true if and only if we are currently fetching app transition specs from the future
+ * passed into {@link #overridePendingAppTransitionMultiThumbFuture}
+ */
+ boolean isFetchingAppTransitionsSpecs() {
+ return mNextAppTransitionAnimationsSpecsPending;
+ }
+
private boolean prepare() {
if (!isRunning()) {
mAppTransitionState = APP_STATE_IDLE;
@@ -456,17 +481,19 @@
return -startPos / denom;
}
- private Animation createScaleUpAnimationLocked(
- int transit, boolean enter, int appWidth, int appHeight) {
- Animation a = null;
- getDefaultNextAppTransitionStartRect(mTmpStartRect);
+ private Animation createScaleUpAnimationLocked(int transit, boolean enter,
+ Rect containingFrame) {
+ Animation a;
+ getDefaultNextAppTransitionStartRect(mTmpRect);
+ final int appWidth = containingFrame.width();
+ final int appHeight = containingFrame.height();
if (enter) {
// Entering app zooms out from the center of the initial rect.
- float scaleW = mTmpStartRect.width() / (float) appWidth;
- float scaleH = mTmpStartRect.height() / (float) appHeight;
+ float scaleW = mTmpRect.width() / (float) appWidth;
+ float scaleH = mTmpRect.height() / (float) appHeight;
Animation scale = new ScaleAnimation(scaleW, 1, scaleH, 1,
- computePivot(mTmpStartRect.left, scaleW),
- computePivot(mTmpStartRect.right, scaleH));
+ computePivot(mTmpRect.left, scaleW),
+ computePivot(mTmpRect.right, scaleH));
scale.setInterpolator(mDecelerateInterpolator);
Animation alpha = new AlphaAnimation(0, 1);
@@ -522,6 +549,9 @@
void getNextAppTransitionStartRect(int taskId, Rect rect) {
AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(taskId);
+ if (spec == null) {
+ spec = mDefaultNextAppTransitionAnimationSpec;
+ }
if (spec == null || spec.rect == null) {
Slog.wtf(TAG, "Starting rect for task: " + taskId + " requested, but not available",
new Throwable());
@@ -531,9 +561,10 @@
}
}
- private void putDefaultNextAppTransitionCoordinates(int left, int top, int width, int height) {
+ private void putDefaultNextAppTransitionCoordinates(int left, int top, int width, int height,
+ Bitmap bitmap) {
mDefaultNextAppTransitionAnimationSpec = new AppTransitionAnimationSpec(-1 /* taskId */,
- null /* bitmap */, new Rect(left, top, left + width, top + height));
+ bitmap, new Rect(left, top, left + width, top + height));
}
private Animation createClipRevealAnimationLocked(int transit, boolean enter, Rect appFrame) {
@@ -543,34 +574,40 @@
final int appWidth = appFrame.width();
final int appHeight = appFrame.height();
- getDefaultNextAppTransitionStartRect(mTmpStartRect);
+ // mTmpRect will contain an area around the launcher icon that was pressed. We will
+ // clip reveal from that area in the final area of the app.
+ getDefaultNextAppTransitionStartRect(mTmpRect);
float t = 0f;
if (appHeight > 0) {
- t = (float) mTmpStartRect.left / appHeight;
+ t = (float) mTmpRect.left / appHeight;
}
- int translationY = mClipRevealTranslationY
- + (int)(appHeight / 7f * t);
+ int translationY = mClipRevealTranslationY + (int)(appHeight / 7f * t);
- int centerX = mTmpStartRect.centerX();
- int centerY = mTmpStartRect.centerY();
- int halfWidth = mTmpStartRect.width() / 2;
- int halfHeight = mTmpStartRect.height() / 2;
+ int centerX = mTmpRect.centerX();
+ int centerY = mTmpRect.centerY();
+ int halfWidth = mTmpRect.width() / 2;
+ int halfHeight = mTmpRect.height() / 2;
// Clip third of the from size of launch icon, expand to full width/height
Animation clipAnimLR = new ClipRectLRAnimation(
centerX - halfWidth, centerX + halfWidth, 0, appWidth);
clipAnimLR.setInterpolator(mClipHorizontalInterpolator);
clipAnimLR.setDuration((long) (DEFAULT_APP_TRANSITION_DURATION / 2.5f));
+
Animation clipAnimTB = new ClipRectTBAnimation(centerY - halfHeight - translationY,
centerY + halfHeight/ 2 - translationY, 0, appHeight);
clipAnimTB.setInterpolator(mTouchResponseInterpolator);
clipAnimTB.setDuration(DEFAULT_APP_TRANSITION_DURATION);
- TranslateYAnimation translateY = new TranslateYAnimation(
- Animation.ABSOLUTE, translationY, Animation.ABSOLUTE, 0);
- translateY.setInterpolator(mLinearOutSlowInInterpolator);
- translateY.setDuration(DEFAULT_APP_TRANSITION_DURATION);
+ // We might be animating entrance of a docked task, so we need the translate to account
+ // for the app frame in which the window will reside. Every other calculation here
+ // is performed as if the window started at 0,0.
+ translationY -= appFrame.top;
+ TranslateAnimation translate = new TranslateAnimation(-appFrame.left, 0, translationY,
+ 0);
+ translate.setInterpolator(mLinearOutSlowInInterpolator);
+ translate.setDuration(DEFAULT_APP_TRANSITION_DURATION);
// Quick fade-in from icon to app window
final int alphaDuration = DEFAULT_APP_TRANSITION_DURATION / 4;
@@ -581,7 +618,7 @@
AnimationSet set = new AnimationSet(false);
set.addAnimation(clipAnimLR);
set.addAnimation(clipAnimTB);
- set.addAnimation(translateY);
+ set.addAnimation(translate);
set.addAnimation(alpha);
set.setZAdjustment(Animation.ZORDER_TOP);
set.initialize(appWidth, appHeight, appWidth, appHeight);
@@ -685,19 +722,19 @@
float scaleW = appWidth / thumbWidth;
float unscaledHeight = thumbHeight * scaleW;
- getNextAppTransitionStartRect(taskId, mTmpStartRect);
- float unscaledStartY = mTmpStartRect.top - (unscaledHeight - thumbHeight) / 2f;
+ getNextAppTransitionStartRect(taskId, mTmpRect);
+ float unscaledStartY = mTmpRect.top - (unscaledHeight - thumbHeight) / 2f;
if (mNextAppTransitionScaleUp) {
// Animation up from the thumbnail to the full screen
Animation scale = new ScaleAnimation(1f, scaleW, 1f, scaleW,
- mTmpStartRect.left + (thumbWidth / 2f), mTmpStartRect.top + (thumbHeight / 2f));
+ mTmpRect.left + (thumbWidth / 2f), mTmpRect.top + (thumbHeight / 2f));
scale.setInterpolator(mTouchResponseInterpolator);
scale.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
Animation alpha = new AlphaAnimation(1, 0);
alpha.setInterpolator(mThumbnailFadeOutInterpolator);
alpha.setDuration(THUMBNAIL_APP_TRANSITION_ALPHA_DURATION);
final float toX = appRect.left + appRect.width() / 2 -
- (mTmpStartRect.left + thumbWidth / 2);
+ (mTmpRect.left + thumbWidth / 2);
final float toY = appRect.top + mNextAppTransitionInsets.top + -unscaledStartY;
Animation translate = new TranslateAnimation(0, toX, 0, toY);
translate.setInterpolator(mTouchResponseInterpolator);
@@ -712,7 +749,7 @@
} else {
// Animation down from the full screen to the thumbnail
Animation scale = new ScaleAnimation(scaleW, 1f, scaleW, 1f,
- mTmpStartRect.left + (thumbWidth / 2f), mTmpStartRect.top + (thumbHeight / 2f));
+ mTmpRect.left + (thumbWidth / 2f), mTmpRect.top + (thumbHeight / 2f));
scale.setInterpolator(mTouchResponseInterpolator);
scale.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
Animation alpha = new AlphaAnimation(0f, 1f);
@@ -740,14 +777,15 @@
* activity that is leaving, and the activity that is entering.
*/
Animation createAspectScaledThumbnailEnterExitAnimationLocked(int thumbTransitState,
- int appWidth, int appHeight, int orientation, int transit, Rect containingFrame,
- Rect contentInsets, @Nullable Rect surfaceInsets, boolean resizedWindow,
- int taskId) {
+ int orientation, int transit, Rect containingFrame, Rect contentInsets,
+ @Nullable Rect surfaceInsets, boolean freeform, int taskId) {
Animation a;
- getDefaultNextAppTransitionStartRect(mTmpStartRect);
- final int thumbWidthI = mTmpStartRect.width();
+ final int appWidth = containingFrame.width();
+ final int appHeight = containingFrame.height();
+ getDefaultNextAppTransitionStartRect(mTmpRect);
+ final int thumbWidthI = mTmpRect.width();
final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
- final int thumbHeightI = mTmpStartRect.height();
+ final int thumbHeightI = mTmpRect.height();
final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
// Used for the ENTER_SCALE_UP and EXIT_SCALE_DOWN transitions
@@ -756,36 +794,40 @@
switch (thumbTransitState) {
case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: {
- if (resizedWindow) {
- a = createAspectScaledThumbnailEnterNonFullscreenAnimationLocked(
+ if (freeform) {
+ a = createAspectScaledThumbnailEnterFreeformAnimationLocked(
containingFrame, surfaceInsets, taskId);
} else {
+ mTmpFromClipRect.set(containingFrame);
+ // exclude top screen decor (status bar) region from the source clip.
+ mTmpFromClipRect.top = contentInsets.top;
// App window scaling up to become full screen
+ mTmpToClipRect.set(containingFrame);
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
// In portrait, we scale the width and clip to the top/left square
scale = thumbWidth / appWidth;
scaledTopDecor = (int) (scale * contentInsets.top);
int unscaledThumbHeight = (int) (thumbHeight / scale);
- mTmpFromClipRect.set(containingFrame);
- mTmpFromClipRect.bottom = (mTmpFromClipRect.top + unscaledThumbHeight);
- mTmpToClipRect.set(containingFrame);
+ mTmpFromClipRect.bottom = mTmpFromClipRect.top + unscaledThumbHeight;
} else {
- // In landscape, we scale the height and clip to the top/left square
- scale = thumbHeight / (appHeight - contentInsets.top);
+ // In landscape, we scale the height and clip to the top/left square. We
+ // only scale the part that is not covered by status bar and the nav bar.
+ scale = thumbHeight / (appHeight - contentInsets.top
+ - contentInsets.bottom);
scaledTopDecor = (int) (scale * contentInsets.top);
int unscaledThumbWidth = (int) (thumbWidth / scale);
- mTmpFromClipRect.set(containingFrame);
- mTmpFromClipRect.right = (mTmpFromClipRect.left + unscaledThumbWidth);
- mTmpToClipRect.set(containingFrame);
+ mTmpFromClipRect.right = mTmpFromClipRect.left + unscaledThumbWidth;
+ // This removes the navigation bar from the first frame, so it better
+ // matches the thumbnail. We need to do this explicitly in landscape,
+ // because in portrait we already crop vertically.
+ mTmpFromClipRect.bottom = mTmpFromClipRect.bottom - contentInsets.bottom;
}
- // exclude top screen decor (status bar) region from the source clip.
- mTmpFromClipRect.top = contentInsets.top;
mNextAppTransitionInsets.set(contentInsets);
Animation scaleAnim = new ScaleAnimation(scale, 1, scale, 1,
- computePivot(mTmpStartRect.left, scale),
- computePivot(mTmpStartRect.top, scale));
+ computePivot(mTmpRect.left, scale),
+ computePivot(mTmpRect.top, scale));
Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
Animation translateAnim = new TranslateAnimation(0, 0, -scaledTopDecor, 0);
@@ -821,41 +863,49 @@
}
case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
// App window scaling down from full screen
- if (orientation == Configuration.ORIENTATION_PORTRAIT) {
- // In portrait, we scale the width and clip to the top/left square
- scale = thumbWidth / appWidth;
- scaledTopDecor = (int) (scale * contentInsets.top);
- int unscaledThumbHeight = (int) (thumbHeight / scale);
- mTmpFromClipRect.set(containingFrame);
- mTmpToClipRect.set(containingFrame);
- mTmpToClipRect.bottom = (mTmpToClipRect.top + unscaledThumbHeight);
+ if (freeform) {
+ a = createAspectScaledThumbnailExitFreeformAnimationLocked(
+ containingFrame, surfaceInsets, taskId);
} else {
- // In landscape, we scale the height and clip to the top/left square
- scale = thumbHeight / (appHeight - contentInsets.top);
- scaledTopDecor = (int) (scale * contentInsets.top);
- int unscaledThumbWidth = (int) (thumbWidth / scale);
mTmpFromClipRect.set(containingFrame);
mTmpToClipRect.set(containingFrame);
- mTmpToClipRect.right = (mTmpToClipRect.left + unscaledThumbWidth);
+ // exclude top screen decor (status bar) region from the destination clip.
+ mTmpToClipRect.top = contentInsets.top;
+ if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+ // In portrait, we scale the width and clip to the top/left square
+ scale = thumbWidth / appWidth;
+ scaledTopDecor = (int) (scale * contentInsets.top);
+ int unscaledThumbHeight = (int) (thumbHeight / scale);
+ mTmpToClipRect.bottom = mTmpToClipRect.top + unscaledThumbHeight;
+ } else {
+ // In landscape, we scale the height and clip to the top/left square. We only
+ // scale the part that is not covered by status bar and the nav bar.
+ scale = thumbHeight / (appHeight - contentInsets.top - contentInsets.bottom);
+ scaledTopDecor = (int) (scale * contentInsets.top);
+ int unscaledThumbWidth = (int) (thumbWidth / scale);
+ mTmpToClipRect.right = mTmpToClipRect.left + unscaledThumbWidth;
+ // This removes the navigation bar from the last frame, so it better matches the
+ // thumbnail. We need to do this explicitly in landscape, because in portrait we
+ // already crop vertically.
+ mTmpToClipRect.bottom = mTmpToClipRect.bottom - contentInsets.bottom;
+ }
+
+ mNextAppTransitionInsets.set(contentInsets);
+
+ Animation scaleAnim = new ScaleAnimation(1, scale, 1, scale,
+ computePivot(mTmpRect.left, scale),
+ computePivot(mTmpRect.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);
}
- // exclude top screen decor (status bar) region from the destination clip.
- mTmpToClipRect.top = contentInsets.top;
-
- mNextAppTransitionInsets.set(contentInsets);
-
- Animation scaleAnim = new ScaleAnimation(1, scale, 1, scale,
- computePivot(mTmpStartRect.left, scale),
- computePivot(mTmpStartRect.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:
@@ -868,29 +918,66 @@
mTouchResponseInterpolator);
}
- private Animation createAspectScaledThumbnailEnterNonFullscreenAnimationLocked(
- Rect frame, @Nullable Rect surfaceInsets, int taskId) {
- getNextAppTransitionStartRect(taskId, mTmpStartRect);
- float width = frame.width();
- float height = frame.height();
- float scaleWidth = mTmpStartRect.width() / width;
- float scaleHeight = mTmpStartRect.height() / height;
+ private Animation createAspectScaledThumbnailEnterFreeformAnimationLocked(Rect frame,
+ @Nullable Rect surfaceInsets, int taskId) {
+ getNextAppTransitionStartRect(taskId, mTmpRect);
+ return createAspectScaledThumbnailFreeformAnimationLocked(mTmpRect, frame, surfaceInsets,
+ true);
+ }
+
+ private Animation createAspectScaledThumbnailExitFreeformAnimationLocked(Rect frame,
+ @Nullable Rect surfaceInsets, int taskId) {
+ getNextAppTransitionStartRect(taskId, mTmpRect);
+ return createAspectScaledThumbnailFreeformAnimationLocked(frame, mTmpRect, surfaceInsets,
+ false);
+ }
+
+ private AnimationSet createAspectScaledThumbnailFreeformAnimationLocked(Rect sourceFrame,
+ Rect destFrame, @Nullable Rect surfaceInsets, boolean enter) {
+ final float sourceWidth = sourceFrame.width();
+ final float sourceHeight = sourceFrame.height();
+ final float destWidth = destFrame.width();
+ final float destHeight = destFrame.height();
+ final float scaleH = enter ? sourceWidth / destWidth : destWidth / sourceWidth;
+ final float scaleV = enter ? sourceHeight / destHeight : destHeight / sourceHeight;
AnimationSet set = new AnimationSet(true);
- int surfaceInsetsHorizontal = surfaceInsets == null
+ final int surfaceInsetsH = surfaceInsets == null
? 0 : surfaceInsets.left + surfaceInsets.right;
- int surfaceInsetsVertical = surfaceInsets == null
+ final int surfaceInsetsV = surfaceInsets == null
? 0 : surfaceInsets.top + surfaceInsets.bottom;
// We want the scaling to happen from the center of the surface. In order to achieve that,
// we need to account for surface insets that will be used to enlarge the surface.
- ScaleAnimation scale = new ScaleAnimation(scaleWidth, 1, scaleHeight, 1,
- (width + surfaceInsetsHorizontal) / 2, (height + surfaceInsetsVertical) / 2);
- int fromX = mTmpStartRect.left + mTmpStartRect.width() / 2
- - (frame.left + frame.width() / 2);
- int fromY = mTmpStartRect.top + mTmpStartRect.height() / 2
- - (frame.top + frame.height() / 2);
- TranslateAnimation translation = new TranslateAnimation(fromX, 0, fromY, 0);
+ final float scaleHCenter = ((enter ? destWidth : sourceWidth) + surfaceInsetsH) / 2;
+ final float scaleVCenter = ((enter ? destHeight : sourceHeight) + surfaceInsetsV) / 2;
+ final ScaleAnimation scale = enter ?
+ new ScaleAnimation(scaleH, 1, scaleV, 1, scaleHCenter, scaleVCenter)
+ : new ScaleAnimation(1, scaleH, 1, scaleV, scaleHCenter, scaleVCenter);
+ final int sourceHCenter = sourceFrame.left + sourceFrame.width() / 2;
+ final int sourceVCenter = sourceFrame.top + sourceFrame.height() / 2;
+ final int destHCenter = destFrame.left + destFrame.width() / 2;
+ final int destVCenter = destFrame.top + destFrame.height() / 2;
+ final int fromX = enter ? sourceHCenter - destHCenter : destHCenter - sourceHCenter;
+ final int fromY = enter ? sourceVCenter - destVCenter : destVCenter - sourceVCenter;
+ final TranslateAnimation translation = enter ? new TranslateAnimation(fromX, 0, fromY, 0)
+ : new TranslateAnimation(0, fromX, 0, fromY);
set.addAnimation(scale);
set.addAnimation(translation);
+
+ final IRemoteCallback callback = mAnimationFinishedCallback;
+ if (callback != null) {
+ set.setAnimationListener(new Animation.AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) { }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ mH.obtainMessage(H.DO_ANIMATION_CALLBACK, callback).sendToTarget();
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) { }
+ });
+ }
return set;
}
@@ -901,7 +988,7 @@
Animation createThumbnailScaleAnimationLocked(int appWidth, int appHeight, int transit,
Bitmap thumbnailHeader) {
Animation a;
- getDefaultNextAppTransitionStartRect(mTmpStartRect);
+ getDefaultNextAppTransitionStartRect(mTmpRect);
final int thumbWidthI = thumbnailHeader.getWidth();
final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
final int thumbHeightI = thumbnailHeader.getHeight();
@@ -912,8 +999,8 @@
float scaleW = appWidth / thumbWidth;
float scaleH = appHeight / thumbHeight;
Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
- computePivot(mTmpStartRect.left, 1 / scaleW),
- computePivot(mTmpStartRect.top, 1 / scaleH));
+ computePivot(mTmpRect.left, 1 / scaleW),
+ computePivot(mTmpRect.top, 1 / scaleH));
scale.setInterpolator(mDecelerateInterpolator);
Animation alpha = new AlphaAnimation(1, 0);
@@ -929,8 +1016,8 @@
float scaleW = appWidth / thumbWidth;
float scaleH = appHeight / thumbHeight;
a = new ScaleAnimation(scaleW, 1, scaleH, 1,
- computePivot(mTmpStartRect.left, 1 / scaleW),
- computePivot(mTmpStartRect.top, 1 / scaleH));
+ computePivot(mTmpRect.left, 1 / scaleW),
+ computePivot(mTmpRect.top, 1 / scaleH));
}
return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
@@ -940,11 +1027,13 @@
* This animation is created when we are doing a thumbnail transition, for the activity that is
* leaving, and the activity that is entering.
*/
- Animation createThumbnailEnterExitAnimationLocked(int thumbTransitState, int appWidth,
- int appHeight, int transit, int taskId) {
+ Animation createThumbnailEnterExitAnimationLocked(int thumbTransitState, Rect containingFrame,
+ int transit, int taskId) {
+ final int appWidth = containingFrame.width();
+ final int appHeight = containingFrame.height();
Bitmap thumbnailHeader = getAppTransitionThumbnailHeader(taskId);
Animation a;
- getDefaultNextAppTransitionStartRect(mTmpStartRect);
+ getDefaultNextAppTransitionStartRect(mTmpRect);
final int thumbWidthI = thumbnailHeader != null ? thumbnailHeader.getWidth() : appWidth;
final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
final int thumbHeightI = thumbnailHeader != null ? thumbnailHeader.getHeight() : appHeight;
@@ -956,8 +1045,8 @@
float scaleW = thumbWidth / appWidth;
float scaleH = thumbHeight / appHeight;
a = new ScaleAnimation(scaleW, 1, scaleH, 1,
- computePivot(mTmpStartRect.left, scaleW),
- computePivot(mTmpStartRect.top, scaleH));
+ computePivot(mTmpRect.left, scaleW),
+ computePivot(mTmpRect.top, scaleH));
break;
}
case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: {
@@ -984,8 +1073,8 @@
float scaleW = thumbWidth / appWidth;
float scaleH = thumbHeight / appHeight;
Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
- computePivot(mTmpStartRect.left, scaleW),
- computePivot(mTmpStartRect.top, scaleH));
+ computePivot(mTmpRect.left, scaleW),
+ computePivot(mTmpRect.top, scaleH));
Animation alpha = new AlphaAnimation(1, 0);
@@ -1003,18 +1092,22 @@
return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
}
- private Animation createRelaunchAnimation(int appWidth, int appHeight,
- Rect containingFrame) {
+ private Animation createRelaunchAnimation(Rect containingFrame, Rect contentInsets) {
getDefaultNextAppTransitionStartRect(mTmpFromClipRect);
final int left = mTmpFromClipRect.left;
final int top = mTmpFromClipRect.top;
mTmpFromClipRect.offset(-left, -top);
- mTmpToClipRect.set(0, 0, appWidth, appHeight);
+ // TODO: Isn't that strange that we ignore exact position of the containingFrame?
+ mTmpToClipRect.set(0, 0, containingFrame.width(), containingFrame.height());
AnimationSet set = new AnimationSet(true);
float fromWidth = mTmpFromClipRect.width();
float toWidth = mTmpToClipRect.width();
float fromHeight = mTmpFromClipRect.height();
- float toHeight = mTmpToClipRect.height();
+ // While the window might span the whole display, the actual content will be cropped to the
+ // system decoration frame, for example when the window is docked. We need to take into
+ // account the visible height when constructing the animation.
+ float toHeight = mTmpToClipRect.height() - contentInsets.top - contentInsets.bottom;
+ int translateAdjustment = 0;
if (fromWidth <= toWidth && fromHeight <= toHeight) {
// The final window is larger in both dimensions than current window (e.g. we are
// maximizing), so we can simply unclip the new window and there will be no disappearing
@@ -1024,12 +1117,17 @@
// The disappearing window has one larger dimension. We need to apply scaling, so the
// first frame of the entry animation matches the old window.
set.addAnimation(new ScaleAnimation(fromWidth / toWidth, 1, fromHeight / toHeight, 1));
+ // We might not be going exactly full screen, but instead be aligned under the status
+ // bar using cropping. We still need to account for the cropped part, which will also
+ // be scaled.
+ translateAdjustment = (int) (contentInsets.top * fromHeight / toHeight);
}
- // We might not be going exactly full screen, but instead be aligned under the status bar.
- // We need to take this into account when creating the translate animation.
+ // We animate the translation from the old position of the removed window, to the new
+ // position of the added window. The latter might not be full screen, for example docked for
+ // docked windows.
TranslateAnimation translate = new TranslateAnimation(left - containingFrame.left,
- 0, top - containingFrame.top, 0);
+ 0, top - containingFrame.top - translateAdjustment, 0);
set.addAnimation(translate);
set.setDuration(DEFAULT_APP_TRANSITION_DURATION);
set.setZAdjustment(Animation.ZORDER_TOP);
@@ -1047,10 +1145,30 @@
&& mNextAppTransitionType != NEXT_TRANSIT_TYPE_CLIP_REVEAL;
}
+ /**
+ *
+ * @param frame These are the bounds of the window when it finishes the animation. This is where
+ * the animation must usually finish in entrance animation, as the next frame will
+ * display the window at these coordinates. In case of exit animation, this is
+ * where the animation must start, as the frame before the animation is displaying
+ * the window at these bounds.
+ * @param insets Knowing where the window will be positioned is not enough. Some parts of the
+ * window might be obscured, usually by the system windows (status bar and
+ * navigation bar) and we use content insets to convey that information. This
+ * usually affects the animation aspects vertically, as the system decoration is
+ * at the top and the bottom. For example when we animate from full screen to
+ * recents, we want to exclude the covered parts, because they won't match the
+ * thumbnail after the last frame is executed.
+ * @param surfaceInsets In rare situation the surface is larger than the content and we need to
+ * know about this to make the animation frames match. We currently use
+ * this for freeform windows, which have larger surfaces to display
+ * shadows. When we animate them from recents, we want to match the content
+ * to the recents thumbnail and hence need to account for the surface being
+ * bigger.
+ */
Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
- int appWidth, int appHeight, int orientation, Rect containingFrame, Rect contentInsets,
- @Nullable Rect surfaceInsets, Rect appFrame, boolean isVoiceInteraction,
- boolean resizedWindow, int taskId) {
+ int orientation, Rect frame, Rect insets, @Nullable Rect surfaceInsets,
+ boolean isVoiceInteraction, boolean freeform, int taskId) {
Animation a;
if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_OPEN
|| transit == TRANSIT_TASK_OPEN
@@ -1073,7 +1191,7 @@
+ " anim=" + a + " transit=" + appTransitionToString(transit)
+ " isEntrance=" + enter + " Callers=" + Debug.getCallers(3));
} else if (transit == TRANSIT_ACTIVITY_RELAUNCH) {
- a = createRelaunchAnimation(appWidth, appHeight, containingFrame);
+ a = createRelaunchAnimation(frame, insets);
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
"applyAnimation:"
+ " anim=" + a + " nextAppTransition=" + mNextAppTransition
@@ -1095,14 +1213,14 @@
+ " transit=" + appTransitionToString(transit)
+ " Callers=" + Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL) {
- a = createClipRevealAnimationLocked(transit, enter, appFrame);
+ a = createClipRevealAnimationLocked(transit, enter, frame);
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
"applyAnimation:"
+ " anim=" + a + " nextAppTransition=ANIM_CLIP_REVEAL"
+ " transit=" + appTransitionToString(transit)
+ " Callers=" + Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_SCALE_UP) {
- a = createScaleUpAnimationLocked(transit, enter, appWidth, appHeight);
+ a = createScaleUpAnimationLocked(transit, enter, frame);
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
"applyAnimation:"
+ " anim=" + a + " nextAppTransition=ANIM_SCALE_UP"
@@ -1113,7 +1231,7 @@
mNextAppTransitionScaleUp =
(mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP);
a = createThumbnailEnterExitAnimationLocked(getThumbnailTransitionState(enter),
- appWidth, appHeight, transit, taskId);
+ frame, transit, taskId);
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
String animName = mNextAppTransitionScaleUp ?
"ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN";
@@ -1127,8 +1245,8 @@
mNextAppTransitionScaleUp =
(mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP);
a = createAspectScaledThumbnailEnterExitAnimationLocked(
- getThumbnailTransitionState(enter), appWidth, appHeight, orientation, transit,
- containingFrame, contentInsets, surfaceInsets, resizedWindow, taskId);
+ getThumbnailTransitionState(enter), orientation, transit, frame,
+ insets, surfaceInsets, freeform, taskId);
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
String animName = mNextAppTransitionScaleUp ?
"ANIM_THUMBNAIL_ASPECT_SCALE_UP" : "ANIM_THUMBNAIL_ASPECT_SCALE_DOWN";
@@ -1213,12 +1331,19 @@
}
}
+ private void clearAppTransitionState() {
+ mNextAppTransitionPackage = null;
+ mNextAppTransitionAnimationsSpecs.clear();
+ mDefaultNextAppTransitionAnimationSpec = null;
+ mAnimationFinishedCallback = null;
+ }
+
void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim,
IRemoteCallback startedCallback) {
if (isTransitionSet()) {
+ clearAppTransitionState();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM;
mNextAppTransitionPackage = packageName;
- mNextAppTransitionAnimationsSpecs.clear();
mNextAppTransitionEnter = enterAnim;
mNextAppTransitionExit = exitAnim;
postAnimationCallback();
@@ -1231,35 +1356,32 @@
void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth,
int startHeight) {
if (isTransitionSet()) {
+ clearAppTransitionState();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_SCALE_UP;
- mNextAppTransitionPackage = null;
- mNextAppTransitionAnimationsSpecs.clear();
putDefaultNextAppTransitionCoordinates(startX, startY, startX + startWidth,
- startY + startHeight);
+ startY + startHeight, null);
postAnimationCallback();
- mNextAppTransitionCallback = null;
}
}
void overridePendingAppTransitionClipReveal(int startX, int startY,
int startWidth, int startHeight) {
if (isTransitionSet()) {
+ clearAppTransitionState();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_CLIP_REVEAL;
- putDefaultNextAppTransitionCoordinates(startX, startY, startWidth, startHeight);
+ putDefaultNextAppTransitionCoordinates(startX, startY, startWidth, startHeight, null);
postAnimationCallback();
- mNextAppTransitionCallback = null;
}
}
void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX, int startY,
IRemoteCallback startedCallback, boolean scaleUp) {
if (isTransitionSet()) {
+ clearAppTransitionState();
mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP
: NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN;
- mNextAppTransitionPackage = null;
- mNextAppTransitionAnimationsSpecs.clear();
mNextAppTransitionScaleUp = scaleUp;
- putDefaultNextAppTransitionCoordinates(startX, startY, 0 ,0);
+ putDefaultNextAppTransitionCoordinates(startX, startY, 0, 0, srcThumb);
postAnimationCallback();
mNextAppTransitionCallback = startedCallback;
} else {
@@ -1270,12 +1392,12 @@
void overridePendingAppTransitionAspectScaledThumb(Bitmap srcThumb, int startX, int startY,
int targetWidth, int targetHeight, IRemoteCallback startedCallback, boolean scaleUp) {
if (isTransitionSet()) {
+ clearAppTransitionState();
mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
: NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
- mNextAppTransitionPackage = null;
- mNextAppTransitionAnimationsSpecs.clear();
mNextAppTransitionScaleUp = scaleUp;
- putDefaultNextAppTransitionCoordinates(startX, startY, targetWidth, targetHeight);
+ putDefaultNextAppTransitionCoordinates(startX, startY, targetWidth, targetHeight,
+ srcThumb);
postAnimationCallback();
mNextAppTransitionCallback = startedCallback;
} else {
@@ -1284,36 +1406,52 @@
}
public void overridePendingAppTransitionMultiThumb(AppTransitionAnimationSpec[] specs,
- IRemoteCallback callback, boolean scaleUp) {
+ IRemoteCallback onAnimationStartedCallback, IRemoteCallback onAnimationFinishedCallback,
+ boolean scaleUp) {
if (isTransitionSet()) {
+ clearAppTransitionState();
mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
: NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
- mNextAppTransitionPackage = null;
- mDefaultNextAppTransitionAnimationSpec = null;
- mNextAppTransitionAnimationsSpecs.clear();
mNextAppTransitionScaleUp = scaleUp;
- for (int i = 0; i < specs.length; i++) {
- AppTransitionAnimationSpec spec = specs[i];
- if (spec != null) {
- mNextAppTransitionAnimationsSpecs.put(spec.taskId, spec);
- if (i == 0) {
- // In full screen mode, the transition code depends on the default spec to
- // be set.
- Rect rect = spec.rect;
- putDefaultNextAppTransitionCoordinates(rect.left, rect.top, rect.width(),
- rect.height());
+ if (specs != null) {
+ for (int i = 0; i < specs.length; i++) {
+ AppTransitionAnimationSpec spec = specs[i];
+ if (spec != null) {
+ mNextAppTransitionAnimationsSpecs.put(spec.taskId, spec);
+ if (i == 0) {
+ // In full screen mode, the transition code depends on the default spec
+ // to be set.
+ Rect rect = spec.rect;
+ putDefaultNextAppTransitionCoordinates(rect.left, rect.top,
+ rect.width(), rect.height(), null);
+ }
}
}
}
postAnimationCallback();
- mNextAppTransitionCallback = callback;
+ mNextAppTransitionCallback = onAnimationStartedCallback;
+ mAnimationFinishedCallback = onAnimationFinishedCallback;
} else {
postAnimationCallback();
}
}
+ void overridePendingAppTransitionMultiThumbFuture(
+ IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback callback,
+ boolean scaleUp) {
+ if (isTransitionSet()) {
+ clearAppTransitionState();
+ mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
+ : NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
+ mNextAppTransitionAnimationsSpecsFuture = specsFuture;
+ mNextAppTransitionScaleUp = scaleUp;
+ mNextAppTransitionFutureCallback = callback;
+ }
+ }
+
void overrideInPlaceAppTransition(String packageName, int anim) {
if (isTransitionSet()) {
+ clearAppTransitionState();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE;
mNextAppTransitionPackage = packageName;
mNextAppTransitionInPlace = anim;
@@ -1322,6 +1460,37 @@
}
}
+ /**
+ * If a future is set for the app transition specs, fetch it in another thread.
+ */
+ private void fetchAppTransitionSpecsFromFuture() {
+ if (mNextAppTransitionAnimationsSpecsFuture != null) {
+ mNextAppTransitionAnimationsSpecsPending = true;
+ final IAppTransitionAnimationSpecsFuture future
+ = mNextAppTransitionAnimationsSpecsFuture;
+ mNextAppTransitionAnimationsSpecsFuture = null;
+ mDefaultExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ AppTransitionAnimationSpec[] specs = null;
+ try {
+ specs = future.get();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to fetch app transition specs: " + e);
+ }
+ synchronized (mServiceLock) {
+ mNextAppTransitionAnimationsSpecsPending = false;
+ overridePendingAppTransitionMultiThumb(specs,
+ mNextAppTransitionFutureCallback, null /* finishedCallback */,
+ mNextAppTransitionScaleUp);
+ mNextAppTransitionFutureCallback = null;
+ mWindowSurfacePlacer.requestTraversal();
+ }
+ }
+ });
+ }
+ }
+
@Override
public String toString() {
return "mNextAppTransition=" + appTransitionToString(mNextAppTransition);
@@ -1445,15 +1614,15 @@
pw.print(Integer.toHexString(mNextAppTransitionInPlace));
break;
case NEXT_TRANSIT_TYPE_SCALE_UP: {
- getDefaultNextAppTransitionStartRect(mTmpStartRect);
+ getDefaultNextAppTransitionStartRect(mTmpRect);
pw.print(prefix); pw.print("mNextAppTransitionStartX=");
- pw.print(mTmpStartRect.left);
+ pw.print(mTmpRect.left);
pw.print(" mNextAppTransitionStartY=");
- pw.println(mTmpStartRect.top);
+ pw.println(mTmpRect.top);
pw.print(prefix); pw.print("mNextAppTransitionStartWidth=");
- pw.print(mTmpStartRect.width());
+ pw.print(mTmpRect.width());
pw.print(" mNextAppTransitionStartHeight=");
- pw.println(mTmpStartRect.height());
+ pw.println(mTmpRect.height());
break;
}
case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP:
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index 2828cd0..2905269 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -82,6 +82,14 @@
// This flag is set if the animator has deferThumbnailDestruction set and has reached the final
// frame of animation. It will extend the animation by one frame and then clean up afterwards.
boolean deferFinalFrameCleanup;
+ // If true when the animation hits the last frame, it will keep running on that last frame.
+ // This is used to synchronize animation with Recents and we wait for Recents to tell us to
+ // finish or for a new animation be set as fail-safe mechanism.
+ private boolean mProlongAnimation;
+ // Whether the prolong animation can be removed when animation is set. The purpose of this is
+ // that if recents doesn't tell us to remove the prolonged animation, we will get rid of it
+ // when new animation is set.
+ private boolean mClearProlongedAnimation;
/** WindowStateAnimator from mAppAnimator.allAppWindows as of last performLayout */
ArrayList<WindowStateAnimator> mAllAppWinAnimators = new ArrayList<>();
@@ -133,6 +141,11 @@
if (!mAppToken.appFullscreen) {
anim.setBackgroundColor(0);
}
+ if (mClearProlongedAnimation) {
+ mProlongAnimation = false;
+ } else {
+ mClearProlongedAnimation = true;
+ }
}
public void setDummyAnimation() {
@@ -263,9 +276,13 @@
} else {
if (false && DEBUG_ANIM) Slog.v(TAG,
"Stepped animation in " + mAppToken + ": more=" + hasMoreFrames +
- ", xform=" + transformation);
+ ", xform=" + transformation + ", mProlongAnimation=" + mProlongAnimation);
deferFinalFrameCleanup = false;
- animation = null;
+ if (mProlongAnimation) {
+ hasMoreFrames = true;
+ } else {
+ animation = null;
+ }
clearThumbnail();
if (DEBUG_ANIM) Slog.v(TAG,
"Finished animation in " + mAppToken + " @ " + currentTime);
@@ -300,7 +317,8 @@
return false;
}
- if ((mAppToken.allDrawn || animating || mAppToken.startingDisplayed)
+ if ((mAppToken.allDrawn || mAppToken.mAnimatingWithSavedSurface
+ || animating || mAppToken.startingDisplayed)
&& animation != null) {
if (!animating) {
if (DEBUG_ANIM) Slog.v(TAG,
@@ -367,6 +385,7 @@
return false;
}
+ // This must be called while inside a transaction.
boolean showAllWindowsLocked() {
boolean isAnimating = false;
final int NW = mAllAppWinAnimators.size();
@@ -415,6 +434,15 @@
}
}
+ void startProlongAnimation() {
+ mProlongAnimation = true;
+ mClearProlongedAnimation = false;
+ }
+
+ void endProlongedAnimation() {
+ mProlongAnimation = false;
+ }
+
// This is an animation that does nothing: it just immediately finishes
// itself every time it is called. It is used as a stub animation in cases
// where we want to synchronize multiple things that may be animating.
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 5a1ed58..425ff9b 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -17,6 +17,9 @@
package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+import static com.android.server.wm.WindowManagerService.DEBUG_ANIM;
+import static com.android.server.wm.WindowManagerService.DEBUG_APP_TRANSITIONS;
+import static com.android.server.wm.WindowManagerService.TAG;
import com.android.server.input.InputApplicationHandle;
import com.android.server.wm.WindowManagerService.H;
@@ -52,6 +55,13 @@
final boolean voiceInteraction;
+ // Whether the window has a saved surface from last pause, which can be
+ // used to start an entering animation earlier.
+ boolean mHasSavedSurface;
+
+ // Whether we're performing an entering animation with a saved surface.
+ boolean mAnimatingWithSavedSurface;
+
Task mTask;
boolean appFullscreen;
int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -92,6 +102,7 @@
// Set to true when the token has been removed from the window mgr.
boolean removed;
+ boolean appDied;
// Information about an application starting window if displayed.
StartingData startingData;
WindowState startingWindow;
@@ -114,11 +125,17 @@
// This application will have its window replaced due to relaunch. This allows window manager
// to differentiate between simple removal of a window and replacement. In the latter case it
// will preserve the old window until the new one is drawn.
- boolean mReplacingWindow;
+ boolean mWillReplaceWindow;
// If true, the replaced window was already requested to be removed.
boolean mReplacingRemoveRequested;
// Whether the replacement of the window should trigger app transition animation.
boolean mAnimateReplacingWindow;
+ // If not null, the window that will be used to replace the old one. This is being set when
+ // the window is added and unset when this window reports its first draw.
+ WindowState mReplacingWindow;
+ // Whether the new window has replaced the old one, so the old one can be removed without
+ // blinking.
+ boolean mHasReplacedWindow;
AppWindowToken(WindowManagerService _service, IApplicationToken _token,
boolean _voiceInteraction) {
@@ -175,7 +192,8 @@
+ win.isDrawnLw()
+ ", isAnimating=" + win.mWinAnimator.isAnimating());
if (!win.isDrawnLw()) {
- Slog.v(WindowManagerService.TAG, "Not displayed: s=" + win.mWinAnimator.mSurfaceControl
+ Slog.v(WindowManagerService.TAG, "Not displayed: s=" +
+ win.mWinAnimator.mSurfaceController
+ " pv=" + win.mPolicyVisibility
+ " mDrawState=" + win.mWinAnimator.mDrawState
+ " ah=" + win.mAttachedHidden
@@ -257,11 +275,14 @@
final int N = allAppWindows.size();
for (int i=0; i<N; i++) {
WindowState win = allAppWindows.get(i);
+ // If we're animating with a saved surface, we're already visible.
+ // Return true so that the alpha doesn't get cleared.
if (!win.mAppFreezing
- && (win.mViewVisibility == View.VISIBLE ||
- (win.mWinAnimator.isAnimating() &&
- !service.mAppTransition.isTransitionSet()))
- && !win.mDestroying && win.isDrawnLw()) {
+ && (win.mViewVisibility == View.VISIBLE || mAnimatingWithSavedSurface
+ || (win.mWinAnimator.isAnimating()
+ && !service.mAppTransition.isTransitionSet()))
+ && !win.mDestroying
+ && win.isDrawnLw()) {
return true;
}
}
@@ -283,6 +304,52 @@
}
}
+ /**
+ * Checks whether we should save surfaces for this app.
+ *
+ * @return true if the surfaces should be saved, false otherwise.
+ */
+ boolean shouldSaveSurface() {
+ // We want to save surface if the app's windows are "allDrawn", or if we're
+ // currently animating with save surfaces. (If the app didn't even finish
+ // drawing when the user exits, but we have a saved surface from last time,
+ // we still want to keep that surface.)
+ mHasSavedSurface = allDrawn || mAnimatingWithSavedSurface;
+ if (mHasSavedSurface) {
+ if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
+ "Saving surface: " + this);
+ return true;
+ }
+ return false;
+ }
+
+ void restoreSavedSurfaces() {
+ if (!mHasSavedSurface) {
+ return;
+ }
+
+ if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
+ "Restoring saved surfaces: " + this + ", allDrawn=" + allDrawn);
+
+ mHasSavedSurface = false;
+ mAnimatingWithSavedSurface = true;
+ for (int i = windows.size() - 1; i >= 0; i--) {
+ WindowState ws = windows.get(i);
+ ws.mWinAnimator.mDrawState = WindowStateAnimator.READY_TO_SHOW;
+ }
+ }
+
+ void destroySavedSurfaces() {
+ if (mHasSavedSurface) {
+ if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
+ "Destroying saved surface: " + this);
+ for (int i = windows.size() - 1; i >= 0; i--) {
+ final WindowState win = windows.get(i);
+ win.mWinAnimator.destroySurfaceLocked();
+ }
+ }
+ }
+
@Override
void removeAllWindows() {
for (int winNdx = allAppWindows.size() - 1; winNdx >= 0;
@@ -303,6 +370,26 @@
windows.clear();
}
+ void removeAllDeadWindows() {
+ for (int winNdx = allAppWindows.size() - 1; winNdx >= 0;
+ // removeWindowLocked at bottom of loop may remove multiple entries from
+ // allAppWindows if the window to be removed has child windows. It also may
+ // not remove any windows from allAppWindows at all if win is exiting and
+ // currently animating away. This ensures that winNdx is monotonically decreasing
+ // and never beyond allAppWindows bounds.
+ winNdx = Math.min(winNdx - 1, allAppWindows.size() - 1)) {
+ WindowState win = allAppWindows.get(winNdx);
+ if (win.mAppDied) {
+ if (WindowManagerService.DEBUG_WINDOW_MOVEMENT) {
+ Slog.w(WindowManagerService.TAG, "removeAllDeadWindows: " + win);
+ }
+ // Set mDestroying, we don't want any animation or delayed removal here.
+ win.mDestroying = true;
+ service.removeWindowLocked(win);
+ }
+ }
+ }
+
@Override
void dump(PrintWriter pw, String prefix) {
super.dump(pw, prefix);
diff --git a/services/core/java/com/android/server/wm/BlackFrame.java b/services/core/java/com/android/server/wm/BlackFrame.java
index 5aa266d..7b08354 100644
--- a/services/core/java/com/android/server/wm/BlackFrame.java
+++ b/services/core/java/com/android/server/wm/BlackFrame.java
@@ -45,7 +45,7 @@
int h = b-t;
if (WindowManagerService.DEBUG_SURFACE_TRACE) {
- surface = new WindowStateAnimator.SurfaceTrace(session, "BlackSurface("
+ surface = new WindowSurfaceController.SurfaceTrace(session, "BlackSurface("
+ l + ", " + t + ")",
w, h, PixelFormat.OPAQUE, SurfaceControl.FX_SURFACE_DIM | SurfaceControl.HIDDEN);
} else {
diff --git a/services/core/java/com/android/server/wm/CircularDisplayMask.java b/services/core/java/com/android/server/wm/CircularDisplayMask.java
index 7c2da2d..33cec64 100644
--- a/services/core/java/com/android/server/wm/CircularDisplayMask.java
+++ b/services/core/java/com/android/server/wm/CircularDisplayMask.java
@@ -56,7 +56,7 @@
int screenOffset, int maskThickness) {
mScreenSize = new Point();
display.getSize(mScreenSize);
- if (mScreenSize.x != mScreenSize.y) {
+ if (mScreenSize.x != mScreenSize.y + screenOffset) {
Slog.w(TAG, "Screen dimensions of displayId = " + display.getDisplayId() +
"are not equal, circularMask will not be drawn.");
mDimensionsUnequal = true;
@@ -65,7 +65,7 @@
SurfaceControl ctrl = null;
try {
if (WindowManagerService.DEBUG_SURFACE_TRACE) {
- ctrl = new WindowStateAnimator.SurfaceTrace(session, "CircularDisplayMask",
+ ctrl = new WindowSurfaceController.SurfaceTrace(session, "CircularDisplayMask",
mScreenSize.x, mScreenSize.y, PixelFormat.TRANSLUCENT,
SurfaceControl.HIDDEN);
} else {
diff --git a/services/core/java/com/android/server/wm/DimBehindController.java b/services/core/java/com/android/server/wm/DimBehindController.java
deleted file mode 100644
index 8870dd1..0000000
--- a/services/core/java/com/android/server/wm/DimBehindController.java
+++ /dev/null
@@ -1,299 +0,0 @@
-package com.android.server.wm;
-
-import static com.android.server.wm.WindowManagerService.DEBUG_DIM_LAYER;
-
-import android.graphics.Rect;
-import android.util.ArrayMap;
-import android.util.Slog;
-import android.util.TypedValue;
-
-import java.io.PrintWriter;
-
-/**
- * Centralizes the control of dim layers used for
- * {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND}.
- */
-class DimBehindController {
- private static final String TAG = "DimBehindController";
-
- /** Amount of time in milliseconds to animate the dim surface from one value to another,
- * when no window animation is driving it. */
- private static final int DEFAULT_DIM_DURATION = 200;
-
- // Shared dim layer for fullscreen users. {@link DimBehindState#dimLayer} will point to this
- // instead of creating a new object per fullscreen task on a display.
- private DimLayer mSharedFullScreenDimLayer;
-
- private ArrayMap<DimLayer.DimLayerUser, DimBehindState> mState = new ArrayMap<>();
-
- private DisplayContent mDisplayContent;
-
- private Rect mTmpBounds = new Rect();
-
- DimBehindController(DisplayContent displayContent) {
- mDisplayContent = displayContent;
- }
-
- /** Updates the dim layer bounds, recreating it if needed. */
- void updateDimLayer(DimLayer.DimLayerUser dimLayerUser) {
- DimBehindState state = getOrCreateDimBehindState(dimLayerUser);
- final boolean previousFullscreen = state.dimLayer != null
- && state.dimLayer == mSharedFullScreenDimLayer;
- DimLayer newDimLayer;
- final int displayId = mDisplayContent.getDisplayId();
- if (dimLayerUser.isFullscreen()) {
- if (previousFullscreen) {
- // Nothing to do here...
- return;
- }
- // Use shared fullscreen dim layer
- newDimLayer = mSharedFullScreenDimLayer;
- if (newDimLayer == null) {
- if (state.dimLayer != null) {
- // Re-purpose the previous dim layer.
- newDimLayer = state.dimLayer;
- } else {
- // Create new full screen dim layer.
- newDimLayer = new DimLayer(mDisplayContent.mService, dimLayerUser, displayId);
- }
- dimLayerUser.getBounds(mTmpBounds);
- newDimLayer.setBounds(mTmpBounds);
- mSharedFullScreenDimLayer = newDimLayer;
- } else if (state.dimLayer != null) {
- state.dimLayer. destroySurface();
- }
- } else {
- newDimLayer = (state.dimLayer == null || previousFullscreen)
- ? new DimLayer(mDisplayContent.mService, dimLayerUser, displayId)
- : state.dimLayer;
- dimLayerUser.getBounds(mTmpBounds);
- newDimLayer.setBounds(mTmpBounds);
- }
- state.dimLayer = newDimLayer;
- }
-
- private DimBehindState getOrCreateDimBehindState(DimLayer.DimLayerUser dimLayerUser) {
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "getDimBehindState, dimLayerUser="
- + dimLayerUser.toShortString());
- DimBehindState state = mState.get(dimLayerUser);
- if (state == null) {
- state = new DimBehindState();
- mState.put(dimLayerUser, state);
- }
- return state;
- }
-
- private void setContinueDimming(DimLayer.DimLayerUser dimLayerUser) {
- DimBehindState state = mState.get(dimLayerUser);
- if (state == null) {
- if (DEBUG_DIM_LAYER) Slog.w(TAG, "setContinueDimming, no state for: "
- + dimLayerUser.toShortString());
- return;
- }
- state.continueDimming = true;
- }
-
- boolean isDimming() {
- for (int i = mState.size() - 1; i >= 0; i--) {
- DimBehindState state = mState.valueAt(i);
- if (state.dimLayer != null && state.dimLayer.isDimming()) {
- return true;
- }
- }
- return false;
- }
-
- void resetDimming() {
- for (int i = mState.size() - 1; i >= 0; i--) {
- mState.valueAt(i).continueDimming = false;
- }
- }
-
- private boolean getContinueDimming(DimLayer.DimLayerUser dimLayerUser) {
- DimBehindState state = mState.get(dimLayerUser);
- return state != null && state.continueDimming;
- }
-
- void startDimmingIfNeeded(DimLayer.DimLayerUser dimLayerUser,
- WindowStateAnimator newWinAnimator) {
- // Only set dim params on the highest dimmed layer.
- // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer.
- DimBehindState state = getOrCreateDimBehindState(dimLayerUser);
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "startDimmingIfNeeded,"
- + " dimLayerUser=" + dimLayerUser.toShortString()
- + " newWinAnimator=" + newWinAnimator
- + " state.animator=" + state.animator);
- if (newWinAnimator.mSurfaceShown && (state.animator == null
- || !state.animator.mSurfaceShown
- || state.animator.mAnimLayer <= newWinAnimator.mAnimLayer)) {
- state.animator = newWinAnimator;
- if (state.animator.mWin.mAppToken == null && !dimLayerUser.isFullscreen()) {
- // Dim should cover the entire screen for system windows.
- mDisplayContent.getLogicalDisplayRect(mTmpBounds);
- state.dimLayer.setBounds(mTmpBounds);
- }
- }
- }
-
- void stopDimmingIfNeeded() {
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "stopDimmingIfNeeded, mState.size()=" + mState.size());
- for (int i = mState.size() - 1; i >= 0; i--) {
- DimLayer.DimLayerUser dimLayerUser = mState.keyAt(i);
- stopDimmingIfNeeded(dimLayerUser);
- }
- }
-
- private void stopDimmingIfNeeded(DimLayer.DimLayerUser dimLayerUser) {
- // No need to check if state is null, we know the key has a value.
- DimBehindState state = mState.get(dimLayerUser);
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "stopDimmingIfNeeded,"
- + " dimLayerUser=" + dimLayerUser.toShortString()
- + " state.continueDimming=" + state.continueDimming
- + " state.dimLayer.isDimming=" + state.dimLayer.isDimming());
- if (!state.continueDimming && state.dimLayer.isDimming()) {
- state.animator = null;
- dimLayerUser.getBounds(mTmpBounds);
- state.dimLayer.setBounds(mTmpBounds);
- }
- }
-
- boolean animateDimLayers() {
- int fullScreen = -1;
- int fullScreenAndDimming = -1;
- boolean result = false;
-
- for (int i = mState.size() - 1; i >= 0; i--) {
- DimLayer.DimLayerUser user = mState.keyAt(i);
- if (user.isFullscreen()) {
- fullScreen = i;
- if (mState.valueAt(i).continueDimming) {
- fullScreenAndDimming = i;
- }
- } else {
- // We always want to animate the non fullscreen windows, they don't share their
- // dim layers.
- result |= animateDimLayers(user);
- }
- }
- // For the shared, full screen dim layer, we prefer the animation that is causing it to
- // appear.
- if (fullScreenAndDimming != -1) {
- result |= animateDimLayers(mState.keyAt(fullScreenAndDimming));
- } else if (fullScreen != -1) {
- // If there is no animation for the full screen dim layer to appear, we can use any of
- // the animators that will cause it to disappear.
- result |= animateDimLayers(mState.keyAt(fullScreen));
- }
- return result;
- }
-
- private boolean animateDimLayers(DimLayer.DimLayerUser dimLayerUser) {
- DimBehindState state = mState.get(dimLayerUser);
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "animateDimLayers,"
- + " dimLayerUser=" + dimLayerUser.toShortString()
- + " state.animator=" + state.animator
- + " state.continueDimming=" + state.continueDimming);
- final int dimLayer;
- final float dimAmount;
- if (state.animator == null) {
- dimLayer = state.dimLayer.getLayer();
- dimAmount = 0;
- } else {
- dimLayer = state.animator.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM;
- dimAmount = state.animator.mWin.mAttrs.dimAmount;
- }
- final float targetAlpha = state.dimLayer.getTargetAlpha();
- if (targetAlpha != dimAmount) {
- if (state.animator == null) {
- state.dimLayer.hide(DEFAULT_DIM_DURATION);
- } else {
- long duration = (state.animator.mAnimating && state.animator.mAnimation != null)
- ? state.animator.mAnimation.computeDurationHint()
- : DEFAULT_DIM_DURATION;
- if (targetAlpha > dimAmount) {
- duration = getDimBehindFadeDuration(duration);
- }
- state.dimLayer.show(dimLayer, dimAmount, duration);
- }
- } else if (state.dimLayer.getLayer() != dimLayer) {
- state.dimLayer.setLayer(dimLayer);
- }
- if (state.dimLayer.isAnimating()) {
- if (!mDisplayContent.mService.okToDisplay()) {
- // Jump to the end of the animation.
- state.dimLayer.show();
- } else {
- return state.dimLayer.stepAnimation();
- }
- }
- return false;
- }
-
- boolean isDimming(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator winAnimator) {
- DimBehindState state = mState.get(dimLayerUser);
- return state != null && state.animator == winAnimator && state.dimLayer.isDimming();
- }
-
- private long getDimBehindFadeDuration(long duration) {
- TypedValue tv = new TypedValue();
- mDisplayContent.mService.mContext.getResources().getValue(
- com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true);
- if (tv.type == TypedValue.TYPE_FRACTION) {
- duration = (long) tv.getFraction(duration, duration);
- } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) {
- duration = tv.data;
- }
- return duration;
- }
-
- void close() {
- for (int i = mState.size() - 1; i >= 0; i--) {
- DimBehindState state = mState.valueAt(i);
- state.dimLayer.destroySurface();
- }
- mState.clear();
- mSharedFullScreenDimLayer = null;
- }
-
- void removeDimLayerUser(DimLayer.DimLayerUser dimLayerUser) {
- mState.remove(dimLayerUser);
- }
-
- void applyDimBehind(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator) {
- if (dimLayerUser == null) {
- Slog.e(TAG, "Trying to apply dim layer for: " + this
- + ", but no dim layer user found.");
- return;
- }
- if (!getContinueDimming(dimLayerUser)) {
- setContinueDimming(dimLayerUser);
- if (!isDimming(dimLayerUser, animator)) {
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "Win " + this + " start dimming.");
- startDimmingIfNeeded(dimLayerUser, animator);
- }
- }
- }
-
- private static class DimBehindState {
- // The particular window with FLAG_DIM_BEHIND set. If null, hide dimLayer.
- WindowStateAnimator animator;
- // Set to false at the start of performLayoutAndPlaceSurfaces. If it is still false by the
- // end then stop any dimming.
- boolean continueDimming;
- DimLayer dimLayer;
- }
-
- void dump(String prefix, PrintWriter pw) {
- pw.println(prefix + "DimBehindController");
- for (int i = 0, n = mState.size(); i < n; i++) {
- pw.println(prefix + " " + mState.keyAt(i).toShortString());
- pw.print(prefix + " ");
- DimBehindState state = mState.valueAt(i);
- pw.print("dimLayer=" + (state.dimLayer == mSharedFullScreenDimLayer ? "shared" :
- state.dimLayer));
- pw.print(", animator=" + state.animator);
- pw.println(", continueDimming=" + state.continueDimming + "}");
-
- }
- }
-}
diff --git a/services/core/java/com/android/server/wm/DimLayer.java b/services/core/java/com/android/server/wm/DimLayer.java
index bc31274..4b3620f 100644
--- a/services/core/java/com/android/server/wm/DimLayer.java
+++ b/services/core/java/com/android/server/wm/DimLayer.java
@@ -70,7 +70,7 @@
/** Returns the display info. of the dim layer user. */
DisplayInfo getDisplayInfo();
/** Gets the bounds of the dim layer user. */
- void getBounds(Rect outBounds);
+ void getDimBounds(Rect outBounds);
String toShortString();
}
/** The user of this dim layer. */
@@ -82,7 +82,7 @@
SurfaceControl.openTransaction();
try {
if (WindowManagerService.DEBUG_SURFACE_TRACE) {
- mDimSurface = new WindowStateAnimator.SurfaceTrace(service.mFxSession,
+ mDimSurface = new WindowSurfaceController.SurfaceTrace(service.mFxSession,
"DimSurface",
16, 16, PixelFormat.OPAQUE,
SurfaceControl.FX_SURFACE_DIM | SurfaceControl.HIDDEN);
diff --git a/services/core/java/com/android/server/wm/DimLayerController.java b/services/core/java/com/android/server/wm/DimLayerController.java
new file mode 100644
index 0000000..bd30bd5
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DimLayerController.java
@@ -0,0 +1,329 @@
+package com.android.server.wm;
+
+import static com.android.server.wm.WindowManagerService.DEBUG_DIM_LAYER;
+import static com.android.server.wm.WindowManagerService.LAYER_OFFSET_DIM;
+
+import android.graphics.Rect;
+import android.util.ArrayMap;
+import android.util.Slog;
+import android.util.TypedValue;
+
+import java.io.PrintWriter;
+
+/**
+ * Centralizes the control of dim layers used for
+ * {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND}
+ * as well as other use cases (such as dimming above a dead window).
+ */
+class DimLayerController {
+ private static final String TAG = "DimLayerController";
+
+ /** Amount of time in milliseconds to animate the dim surface from one value to another,
+ * when no window animation is driving it. */
+ private static final int DEFAULT_DIM_DURATION = 200;
+
+ /**
+ * The default amount of dim applied over a dead window
+ */
+ private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
+
+ // Shared dim layer for fullscreen users. {@link DimLayerState#dimLayer} will point to this
+ // instead of creating a new object per fullscreen task on a display.
+ private DimLayer mSharedFullScreenDimLayer;
+
+ private ArrayMap<DimLayer.DimLayerUser, DimLayerState> mState = new ArrayMap<>();
+
+ private DisplayContent mDisplayContent;
+
+ private Rect mTmpBounds = new Rect();
+
+ DimLayerController(DisplayContent displayContent) {
+ mDisplayContent = displayContent;
+ }
+
+ /** Updates the dim layer bounds, recreating it if needed. */
+ void updateDimLayer(DimLayer.DimLayerUser dimLayerUser) {
+ DimLayerState state = getOrCreateDimLayerState(dimLayerUser);
+ final boolean previousFullscreen = state.dimLayer != null
+ && state.dimLayer == mSharedFullScreenDimLayer;
+ DimLayer newDimLayer;
+ final int displayId = mDisplayContent.getDisplayId();
+ if (dimLayerUser.isFullscreen()) {
+ if (previousFullscreen) {
+ // Nothing to do here...
+ return;
+ }
+ // Use shared fullscreen dim layer
+ newDimLayer = mSharedFullScreenDimLayer;
+ if (newDimLayer == null) {
+ if (state.dimLayer != null) {
+ // Re-purpose the previous dim layer.
+ newDimLayer = state.dimLayer;
+ } else {
+ // Create new full screen dim layer.
+ newDimLayer = new DimLayer(mDisplayContent.mService, dimLayerUser, displayId);
+ }
+ dimLayerUser.getDimBounds(mTmpBounds);
+ newDimLayer.setBounds(mTmpBounds);
+ mSharedFullScreenDimLayer = newDimLayer;
+ } else if (state.dimLayer != null) {
+ state.dimLayer.destroySurface();
+ }
+ } else {
+ newDimLayer = (state.dimLayer == null || previousFullscreen)
+ ? new DimLayer(mDisplayContent.mService, dimLayerUser, displayId)
+ : state.dimLayer;
+ dimLayerUser.getDimBounds(mTmpBounds);
+ newDimLayer.setBounds(mTmpBounds);
+ }
+ state.dimLayer = newDimLayer;
+ }
+
+ private DimLayerState getOrCreateDimLayerState(DimLayer.DimLayerUser dimLayerUser) {
+ if (DEBUG_DIM_LAYER) Slog.v(TAG, "getOrCreateDimLayerState, dimLayerUser="
+ + dimLayerUser.toShortString());
+ DimLayerState state = mState.get(dimLayerUser);
+ if (state == null) {
+ state = new DimLayerState();
+ mState.put(dimLayerUser, state);
+ }
+ return state;
+ }
+
+ private void setContinueDimming(DimLayer.DimLayerUser dimLayerUser) {
+ DimLayerState state = mState.get(dimLayerUser);
+ if (state == null) {
+ if (DEBUG_DIM_LAYER) Slog.w(TAG, "setContinueDimming, no state for: "
+ + dimLayerUser.toShortString());
+ return;
+ }
+ state.continueDimming = true;
+ }
+
+ boolean isDimming() {
+ for (int i = mState.size() - 1; i >= 0; i--) {
+ DimLayerState state = mState.valueAt(i);
+ if (state.dimLayer != null && state.dimLayer.isDimming()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void resetDimming() {
+ for (int i = mState.size() - 1; i >= 0; i--) {
+ mState.valueAt(i).continueDimming = false;
+ }
+ }
+
+ private boolean getContinueDimming(DimLayer.DimLayerUser dimLayerUser) {
+ DimLayerState state = mState.get(dimLayerUser);
+ return state != null && state.continueDimming;
+ }
+
+ void startDimmingIfNeeded(DimLayer.DimLayerUser dimLayerUser,
+ WindowStateAnimator newWinAnimator, boolean aboveApp) {
+ // Only set dim params on the highest dimmed layer.
+ // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer.
+ DimLayerState state = getOrCreateDimLayerState(dimLayerUser);
+ state.dimAbove = aboveApp;
+ if (DEBUG_DIM_LAYER) Slog.v(TAG, "startDimmingIfNeeded,"
+ + " dimLayerUser=" + dimLayerUser.toShortString()
+ + " newWinAnimator=" + newWinAnimator
+ + " state.animator=" + state.animator);
+ if (newWinAnimator.getShown() && (state.animator == null
+ || !state.animator.getShown()
+ || state.animator.mAnimLayer <= newWinAnimator.mAnimLayer)) {
+ state.animator = newWinAnimator;
+ if (state.animator.mWin.mAppToken == null && !dimLayerUser.isFullscreen()) {
+ // Dim should cover the entire screen for system windows.
+ mDisplayContent.getLogicalDisplayRect(mTmpBounds);
+ state.dimLayer.setBounds(mTmpBounds);
+ }
+ }
+ }
+
+ void stopDimmingIfNeeded() {
+ if (DEBUG_DIM_LAYER) Slog.v(TAG, "stopDimmingIfNeeded, mState.size()=" + mState.size());
+ for (int i = mState.size() - 1; i >= 0; i--) {
+ DimLayer.DimLayerUser dimLayerUser = mState.keyAt(i);
+ stopDimmingIfNeeded(dimLayerUser);
+ }
+ }
+
+ private void stopDimmingIfNeeded(DimLayer.DimLayerUser dimLayerUser) {
+ // No need to check if state is null, we know the key has a value.
+ DimLayerState state = mState.get(dimLayerUser);
+ if (DEBUG_DIM_LAYER) Slog.v(TAG, "stopDimmingIfNeeded,"
+ + " dimLayerUser=" + dimLayerUser.toShortString()
+ + " state.continueDimming=" + state.continueDimming
+ + " state.dimLayer.isDimming=" + state.dimLayer.isDimming());
+ if (!state.continueDimming && state.dimLayer.isDimming()) {
+ state.animator = null;
+ dimLayerUser.getDimBounds(mTmpBounds);
+ state.dimLayer.setBounds(mTmpBounds);
+ }
+ }
+
+ boolean animateDimLayers() {
+ int fullScreen = -1;
+ int fullScreenAndDimming = -1;
+ boolean result = false;
+
+ for (int i = mState.size() - 1; i >= 0; i--) {
+ DimLayer.DimLayerUser user = mState.keyAt(i);
+ if (user.isFullscreen()) {
+ fullScreen = i;
+ if (mState.valueAt(i).continueDimming) {
+ fullScreenAndDimming = i;
+ }
+ } else {
+ // We always want to animate the non fullscreen windows, they don't share their
+ // dim layers.
+ result |= animateDimLayers(user);
+ }
+ }
+ // For the shared, full screen dim layer, we prefer the animation that is causing it to
+ // appear.
+ if (fullScreenAndDimming != -1) {
+ result |= animateDimLayers(mState.keyAt(fullScreenAndDimming));
+ } else if (fullScreen != -1) {
+ // If there is no animation for the full screen dim layer to appear, we can use any of
+ // the animators that will cause it to disappear.
+ result |= animateDimLayers(mState.keyAt(fullScreen));
+ }
+ return result;
+ }
+
+ private boolean animateDimLayers(DimLayer.DimLayerUser dimLayerUser) {
+ DimLayerState state = mState.get(dimLayerUser);
+ if (DEBUG_DIM_LAYER) Slog.v(TAG, "animateDimLayers,"
+ + " dimLayerUser=" + dimLayerUser.toShortString()
+ + " state.animator=" + state.animator
+ + " state.continueDimming=" + state.continueDimming);
+ final int dimLayer;
+ final float dimAmount;
+ if (state.animator == null) {
+ dimLayer = state.dimLayer.getLayer();
+ dimAmount = 0;
+ } else {
+ if (state.dimAbove) {
+ dimLayer = state.animator.mAnimLayer + LAYER_OFFSET_DIM;
+ dimAmount = DEFAULT_DIM_AMOUNT_DEAD_WINDOW;
+ } else {
+ dimLayer = state.animator.mAnimLayer - LAYER_OFFSET_DIM;
+ dimAmount = state.animator.mWin.mAttrs.dimAmount;
+ }
+ }
+ final float targetAlpha = state.dimLayer.getTargetAlpha();
+ if (targetAlpha != dimAmount) {
+ if (state.animator == null) {
+ state.dimLayer.hide(DEFAULT_DIM_DURATION);
+ } else {
+ long duration = (state.animator.mAnimating && state.animator.mAnimation != null)
+ ? state.animator.mAnimation.computeDurationHint()
+ : DEFAULT_DIM_DURATION;
+ if (targetAlpha > dimAmount) {
+ duration = getDimLayerFadeDuration(duration);
+ }
+ state.dimLayer.show(dimLayer, dimAmount, duration);
+ }
+ } else if (state.dimLayer.getLayer() != dimLayer) {
+ state.dimLayer.setLayer(dimLayer);
+ }
+ if (state.dimLayer.isAnimating()) {
+ if (!mDisplayContent.mService.okToDisplay()) {
+ // Jump to the end of the animation.
+ state.dimLayer.show();
+ } else {
+ return state.dimLayer.stepAnimation();
+ }
+ }
+ return false;
+ }
+
+ boolean isDimming(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator winAnimator) {
+ DimLayerState state = mState.get(dimLayerUser);
+ return state != null && state.animator == winAnimator && state.dimLayer.isDimming();
+ }
+
+ private long getDimLayerFadeDuration(long duration) {
+ TypedValue tv = new TypedValue();
+ mDisplayContent.mService.mContext.getResources().getValue(
+ com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true);
+ if (tv.type == TypedValue.TYPE_FRACTION) {
+ duration = (long) tv.getFraction(duration, duration);
+ } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) {
+ duration = tv.data;
+ }
+ return duration;
+ }
+
+ void close() {
+ for (int i = mState.size() - 1; i >= 0; i--) {
+ DimLayerState state = mState.valueAt(i);
+ state.dimLayer.destroySurface();
+ }
+ mState.clear();
+ mSharedFullScreenDimLayer = null;
+ }
+
+ void removeDimLayerUser(DimLayer.DimLayerUser dimLayerUser) {
+ DimLayerState state = mState.get(dimLayerUser);
+ if (state != null) {
+ // Destroy the surface, unless it's the shared fullscreen dim.
+ if (state.dimLayer != mSharedFullScreenDimLayer) {
+ state.dimLayer.destroySurface();
+ }
+ mState.remove(dimLayerUser);
+ }
+ }
+
+ void applyDimBehind(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator) {
+ applyDim(dimLayerUser, animator, false /* aboveApp */);
+ }
+
+ void applyDimAbove(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator) {
+ applyDim(dimLayerUser, animator, true /* aboveApp */);
+ }
+
+ private void applyDim(
+ DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator, boolean aboveApp) {
+ if (dimLayerUser == null) {
+ Slog.e(TAG, "Trying to apply dim layer for: " + this
+ + ", but no dim layer user found.");
+ return;
+ }
+ if (!getContinueDimming(dimLayerUser)) {
+ setContinueDimming(dimLayerUser);
+ if (!isDimming(dimLayerUser, animator)) {
+ if (DEBUG_DIM_LAYER) Slog.v(TAG, "Win " + this + " start dimming.");
+ startDimmingIfNeeded(dimLayerUser, animator, aboveApp);
+ }
+ }
+ }
+
+ private static class DimLayerState {
+ // The particular window requesting a dim layer. If null, hide dimLayer.
+ WindowStateAnimator animator;
+ // Set to false at the start of performLayoutAndPlaceSurfaces. If it is still false by the
+ // end then stop any dimming.
+ boolean continueDimming;
+ DimLayer dimLayer;
+ boolean dimAbove;
+ }
+
+ void dump(String prefix, PrintWriter pw) {
+ pw.println(prefix + "DimLayerController");
+ for (int i = 0, n = mState.size(); i < n; i++) {
+ pw.println(prefix + " " + mState.keyAt(i).toShortString());
+ pw.print(prefix + " ");
+ DimLayerState state = mState.valueAt(i);
+ pw.print("dimLayer=" + (state.dimLayer == mSharedFullScreenDimLayer ? "shared" :
+ state.dimLayer));
+ pw.print(", animator=" + state.animator);
+ pw.println(", continueDimming=" + state.continueDimming + "}");
+
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 39479c1..f54fd83 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -16,14 +16,15 @@
package com.android.server.wm;
-import static android.app.ActivityManager.DOCKED_STACK_ID;
-import static android.app.ActivityManager.HOME_STACK_ID;
-
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.HOME_STACK_ID;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static com.android.server.wm.WindowManagerService.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerService.TAG;
import static com.android.server.wm.WindowState.RESIZE_HANDLE_WIDTH_IN_DP;
import static com.android.server.wm.WindowState.BOUNDS_FOR_TOUCH;
+import android.app.ActivityManager.StackId;
import android.graphics.Rect;
import android.graphics.Region;
import android.util.DisplayMetrics;
@@ -115,7 +116,7 @@
final DockedStackDividerController mDividerControllerLocked;
- final DimBehindController mDimBehindController;
+ final DimLayerController mDimLayerController;
/**
* @param display May not be null.
@@ -130,7 +131,7 @@
mService = service;
initializeDisplayBaseInfo();
mDividerControllerLocked = new DockedStackDividerController(service.mContext, this);
- mDimBehindController = new DimBehindController(this);
+ mDimLayerController = new DimLayerController(this);
}
int getDisplayId() {
@@ -153,6 +154,10 @@
return mDisplayMetrics;
}
+ DockedStackDividerController getDockedDividerController() {
+ return mDividerControllerLocked;
+ }
+
/**
* Returns true if the specified UID has access to this display.
*/
@@ -191,7 +196,6 @@
void updateDisplayInfo() {
mDisplay.getDisplayInfo(mDisplayInfo);
mDisplay.getMetrics(mDisplayMetrics);
- mDividerControllerLocked.updateDisplayInfo();
for (int i = mStacks.size() - 1; i >= 0; --i) {
mStacks.get(i).updateDisplayInfo(null);
}
@@ -243,14 +247,34 @@
}
void moveStack(TaskStack stack, boolean toTop) {
+ if (stack.mStackId == PINNED_STACK_ID && !toTop) {
+ // Pinned stack is always-on-top silly...
+ Slog.w(TAG, "Ignoring move of always-on-top stack=" + stack + " to bottom");
+ return;
+ }
+
if (!mStacks.remove(stack)) {
Slog.wtf(TAG, "moving stack that was not added: " + stack, new Throwable());
}
- mStacks.add(toTop ? mStacks.size() : 0, stack);
+
+ int addIndex = toTop ? mStacks.size() : 0;
+
+ if (toTop
+ && mService.isStackVisibleLocked(PINNED_STACK_ID)
+ && stack.mStackId != PINNED_STACK_ID) {
+ // The pinned stack is always the top most stack (always-on-top) when it is visible.
+ // So, stack is moved just below the pinned stack.
+ addIndex--;
+ TaskStack topStack = mStacks.get(addIndex);
+ if (topStack.mStackId != PINNED_STACK_ID) {
+ throw new IllegalStateException("Pinned stack isn't top stack??? " + mStacks);
+ }
+ }
+ mStacks.add(addIndex, stack);
}
void detachStack(TaskStack stack) {
- mDimBehindController.removeDimLayerUser(stack);
+ mDimLayerController.removeDimLayerUser(stack);
mStacks.remove(stack);
}
@@ -273,7 +297,7 @@
// windows frames when the app window is the IME target.
final WindowState win = task.getTopAppMainWindow();
if (win != null) {
- win.getVisibleBounds(mTmpRect, !BOUNDS_FOR_TOUCH);
+ win.getVisibleBounds(mTmpRect);
if (mTmpRect.contains(x, y)) {
return task.mTaskId;
}
@@ -291,7 +315,7 @@
final int delta = mService.dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
TaskStack stack = mStacks.get(stackNdx);
- if (!stack.allowTaskResize()) {
+ if (!StackId.isTaskResizeAllowed(stack.mStackId)) {
break;
}
final ArrayList<Task> tasks = stack.getTasks();
@@ -308,7 +332,7 @@
// start at (0,0) after it's adjusted for the status bar.)
final WindowState win = task.getTopAppMainWindow();
if (win != null) {
- win.getVisibleBounds(mTmpRect, !BOUNDS_FOR_TOUCH);
+ win.getVisibleBounds(mTmpRect);
mTmpRect.inset(-delta, -delta);
if (mTmpRect.contains(x, y)) {
mTmpRect.inset(delta, delta);
@@ -352,6 +376,12 @@
*/
if (isFreeformed) {
mTmpRect.inset(-delta, -delta);
+ // Intersect with display content rect. If we have system decor (status bar/
+ // navigation bar), we want to exclude that from the tap detection.
+ // Otherwise, if the app is partially placed under some system button (eg.
+ // Recents, Home), pressing that button would cause a full series of
+ // unwanted transfer focus/resume/pause, before we could go home.
+ mTmpRect.intersect(mContentRect);
}
mTouchExcludeRegion.op(mTmpRect, Region.Op.DIFFERENCE);
}
@@ -394,23 +424,23 @@
}
boolean animateDimLayers() {
- return mDimBehindController.animateDimLayers();
+ return mDimLayerController.animateDimLayers();
}
void resetDimming() {
- mDimBehindController.resetDimming();
+ mDimLayerController.resetDimming();
}
boolean isDimming() {
- return mDimBehindController.isDimming();
+ return mDimLayerController.isDimming();
}
void stopDimmingIfNeeded() {
- mDimBehindController.stopDimmingIfNeeded();
+ mDimLayerController.stopDimmingIfNeeded();
}
void close() {
- mDimBehindController.close();
+ mDimLayerController.close();
for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
mStacks.get(stackNdx).close();
}
@@ -557,7 +587,7 @@
}
}
pw.println();
- mDimBehindController.dump(prefix + " ", pw);
+ mDimLayerController.dump(prefix + " ", pw);
}
@Override
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 2d87123..6b62467 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -16,118 +16,64 @@
package com.android.server.wm;
-import static android.app.ActivityManager.DOCKED_STACK_ID;
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
-import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
-import static android.view.WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING;
-import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
-import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
-import static com.android.server.wm.DimLayer.RESIZING_HINT_ALPHA;
-import static com.android.server.wm.DimLayer.RESIZING_HINT_DURATION_MS;
-import static com.android.server.wm.TaskPositioner.SIDE_MARGIN_DIP;
-import static com.android.server.wm.TaskStack.DOCKED_BOTTOM;
-import static com.android.server.wm.TaskStack.DOCKED_LEFT;
-import static com.android.server.wm.TaskStack.DOCKED_RIGHT;
-import static com.android.server.wm.TaskStack.DOCKED_TOP;
-import static com.android.server.wm.WindowManagerService.dipToPixel;
-
import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.PixelFormat;
import android.graphics.Rect;
-import android.os.RemoteException;
import android.util.Slog;
-import android.view.DisplayInfo;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
+
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.view.WindowManager.DOCKED_BOTTOM;
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_RIGHT;
+import static android.view.WindowManager.DOCKED_TOP;
/**
- * Controls showing and hiding of a docked stack divider on the display.
+ * Keeps information about the docked stack divider.
*/
-public class DockedStackDividerController implements View.OnTouchListener, DimLayer.DimLayerUser {
- private static final String TAG = "DockedStackDivider";
- private final Context mContext;
- private final int mDividerWidth;
+public class DockedStackDividerController {
+
+ private static final String TAG = "DockedStackDividerController";
+
private final DisplayContent mDisplayContent;
- private final int mSideMargin;
- private final DimLayer mDimLayer;
- private int mDisplayWidth;
- private int mDisplayHeight;
- private View mView;
- private Rect mTmpRect = new Rect();
- private Rect mLastResizeRect = new Rect();
- private int mStartX;
- private int mStartY;
- private TaskStack mTaskStack;
- private Rect mOriginalRect = new Rect();
- private int mDockSide;
- private boolean mDimLayerVisible;
+ private final int mDividerWindowWidth;
+ private final int mDividerInsets;
+ private boolean mResizing;
+ private WindowState mWindow;
+ private final Rect mTmpRect = new Rect();
+ private final Rect mLastRect = new Rect();
DockedStackDividerController(Context context, DisplayContent displayContent) {
- mContext = context;
mDisplayContent = displayContent;
- updateDisplayInfo();
- mDividerWidth = context.getResources().getDimensionPixelSize(
+ mDividerWindowWidth = context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.docked_stack_divider_thickness);
- mSideMargin = dipToPixel(SIDE_MARGIN_DIP, mDisplayContent.getDisplayMetrics());
- mDimLayer = new DimLayer(displayContent.mService, this, displayContent.getDisplayId());
+ mDividerInsets = context.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.docked_stack_divider_insets);
}
- private void addDivider(Configuration configuration) {
- View view = LayoutInflater.from(mContext).inflate(
- com.android.internal.R.layout.docked_stack_divider, null);
- view.setOnTouchListener(this);
- WindowManagerGlobal manager = WindowManagerGlobal.getInstance();
- final boolean landscape = configuration.orientation == ORIENTATION_LANDSCAPE;
- final int width = landscape ? mDividerWidth : MATCH_PARENT;
- final int height = landscape ? MATCH_PARENT : mDividerWidth;
- WindowManager.LayoutParams params = new WindowManager.LayoutParams(
- width, height, TYPE_DOCK_DIVIDER,
- FLAG_TOUCHABLE_WHEN_WAKING | FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL
- | FLAG_WATCH_OUTSIDE_TOUCH | FLAG_SPLIT_TOUCH,
- PixelFormat.OPAQUE);
- params.setTitle(TAG);
- manager.addView(view, params, mDisplayContent.getDisplay(), null);
- mView = view;
+ boolean isResizing() {
+ return mResizing;
}
- private void removeDivider() {
- mView.setOnTouchListener(null);
- WindowManagerGlobal manager = WindowManagerGlobal.getInstance();
- manager.removeView(mView, true /* immediate */);
- mView = null;
+ int getContentWidth() {
+ return mDividerWindowWidth - 2 * mDividerInsets;
}
- boolean hasDivider() {
- return mView != null;
+ void setResizing(boolean resizing) {
+ mResizing = resizing;
}
- void updateDisplayInfo() {
- final DisplayInfo info = mDisplayContent.getDisplayInfo();
- mDisplayWidth = info.logicalWidth;
- mDisplayHeight = info.logicalHeight;
+ void setWindow(WindowState window) {
+ mWindow = window;
+ reevaluateVisibility();
}
- void update(Configuration configuration, boolean forceUpdate) {
- if (forceUpdate && mView != null) {
- removeDivider();
+ void reevaluateVisibility() {
+ if (mWindow == null) return;
+ TaskStack stack = mDisplayContent.mService.mStackIdToStack.get(DOCKED_STACK_ID);
+ if (stack != null && stack.isVisibleLocked()) {
+ mWindow.showLw(true /* doAnimation */);
+ } else {
+ mWindow.hideLw(true /* doAnimation */);
}
- TaskStack stack = mDisplayContent.getDockedStackLocked();
- if (stack != null && mView == null) {
- addDivider(configuration);
- } else if (stack == null && mView != null) {
- removeDivider();
- }
- }
-
- int getWidth() {
- return mDividerWidth;
}
void positionDockedStackedDivider(Rect frame) {
@@ -135,268 +81,33 @@
if (stack == null) {
// Unfortunately we might end up with still having a divider, even though the underlying
// stack was already removed. This is because we are on AM thread and the removal of the
- // divider was deferred to WM thread and hasn't happened yet.
+ // divider was deferred to WM thread and hasn't happened yet. In that case let's just
+ // keep putting it in the same place it was before the stack was removed to have
+ // continuity and prevent it from jumping to the center. It will get hidden soon.
+ frame.set(mLastRect);
return;
+ } else {
+ stack.getDimBounds(mTmpRect);
}
- final @TaskStack.DockSide int side = stack.getDockSide();
- stack.getBounds(mTmpRect);
+ int side = stack.getDockSide();
switch (side) {
case DOCKED_LEFT:
- frame.set(mTmpRect.right, frame.top, mTmpRect.right + frame.width(), frame.bottom);
+ frame.set(mTmpRect.right - mDividerInsets, frame.top,
+ mTmpRect.right + frame.width() - mDividerInsets, frame.bottom);
break;
case DOCKED_TOP:
- frame.set(frame.left, mTmpRect.bottom, mTmpRect.right,
- mTmpRect.bottom + frame.height());
+ frame.set(frame.left, mTmpRect.bottom - mDividerInsets,
+ mTmpRect.right, mTmpRect.bottom + frame.height() - mDividerInsets);
break;
case DOCKED_RIGHT:
- frame.set(mTmpRect.left - frame.width(), frame.top, mTmpRect.left, frame.bottom);
+ frame.set(mTmpRect.left - frame.width() + mDividerInsets, frame.top,
+ mTmpRect.left + mDividerInsets, frame.bottom);
break;
case DOCKED_BOTTOM:
- frame.set(frame.left, mTmpRect.top - frame.height(), frame.right, mTmpRect.top);
+ frame.set(frame.left, mTmpRect.top - frame.height() + mDividerInsets,
+ frame.right, mTmpRect.top + mDividerInsets);
break;
}
- }
-
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- final int action = event.getAction() & MotionEvent.ACTION_MASK;
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- // We use raw values, because getX/Y() would give us results relative to the
- // dock divider bounds.
- mStartX = (int) event.getRawX();
- mStartY = (int) event.getRawY();
- synchronized (mDisplayContent.mService.mWindowMap) {
- mTaskStack = mDisplayContent.getDockedStackLocked();
- if (mTaskStack != null) {
- mTaskStack.getBounds(mOriginalRect);
- mDockSide = mTaskStack.getDockSide();
- }
- }
- break;
- case MotionEvent.ACTION_MOVE:
- if (mTaskStack != null) {
- final int x = (int) event.getRawX();
- final int y = (int) event.getRawY();
- resizeStack(x, y);
- }
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- if (mTaskStack != null) {
- final int x = (int) event.getRawX();
- final int y = (int) event.getRawY();
- // At most one of these will be executed, the other one will exit early.
- maybeDismissTaskStack(x, y);
- maybeMaximizeTaskStack(x, y);
- mTaskStack = null;
- }
- setDimLayerVisible(false);
- mDockSide = TaskStack.DOCKED_INVALID;
- break;
- }
- return true;
- }
-
- private void maybeMaximizeTaskStack(int x, int y) {
- final int distance = distanceFromFullScreen(mDockSide, x, y);
- if (distance == -1) {
- Slog.wtf(TAG, "maybeMaximizeTaskStack: Unknown dock side=" + mDockSide);
- return;
- }
- if (distance <= mSideMargin) {
- try {
- mDisplayContent.mService.mActivityManager.resizeStack(
- mTaskStack.mStackId, null, true);
- } catch (RemoteException e) {
- // This can't happen because we are in the same process.
- }
- }
- }
-
- private void maybeDismissTaskStack(int x, int y) {
- final int distance = distanceFromDockSide(mDockSide, mOriginalRect, x, y);
- if (distance == -1) {
- Slog.wtf(TAG, "maybeDismissTaskStack: Unknown dock side=" + mDockSide);
- return;
- }
- if (distance <= mSideMargin) {
- try {
- mDisplayContent.mService.mActivityManager.removeStack(mTaskStack.mStackId);
- } catch (RemoteException e) {
- // This can't happen because we are in the same process.
- }
- }
- }
-
- private void updateDimLayer(int x, int y) {
- final int dismissDistance = distanceFromDockSide(mDockSide, mOriginalRect, x, y);
- final int maximizeDistance = distanceFromFullScreen(mDockSide, x, y);
- if (dismissDistance == -1 || maximizeDistance == -1) {
- Slog.wtf(TAG, "updateDimLayer: Unknown dock side=" + mDockSide);
- return;
- }
- if (dismissDistance <= mSideMargin && maximizeDistance <= mSideMargin) {
- Slog.wtf(TAG, "Both dismiss and maximize distances would trigger dim layer.");
- return;
- }
- if (dismissDistance <= mSideMargin) {
- setDismissDimLayerVisible(x, y);
- } else if (maximizeDistance <= mSideMargin) {
- setMaximizeDimLayerVisible(x, y);
- } else {
- setDimLayerVisible(false);
- }
- }
-
- /**
- * Provides the distance from the point to the docked side of a rectangle.
- *
- * @return non negative distance or -1 on error
- */
- private static int distanceFromDockSide(int dockSide, Rect bounds, int x, int y) {
- switch (dockSide) {
- case DOCKED_LEFT:
- return x - bounds.left;
- case DOCKED_TOP:
- return y - bounds.top;
- case DOCKED_RIGHT:
- return bounds.right - x;
- case DOCKED_BOTTOM:
- return bounds.bottom - y;
- }
- return -1;
- }
-
- private int distanceFromFullScreen(int dockSide, int x, int y) {
- switch (dockSide) {
- case DOCKED_LEFT:
- return mDisplayWidth - x;
- case DOCKED_TOP:
- return mDisplayHeight - y;
- case DOCKED_RIGHT:
- return x;
- case DOCKED_BOTTOM:
- return y;
- }
- return -1;
- }
-
- private void setDismissDimLayerVisible(int x, int y) {
- mTmpRect.set(mOriginalRect);
- switch (mDockSide) {
- case DOCKED_LEFT:
- mTmpRect.right = x;
- break;
- case DOCKED_TOP:
- mTmpRect.bottom = y;
- break;
- case DOCKED_RIGHT:
- mTmpRect.left = x;
- break;
- case DOCKED_BOTTOM:
- mTmpRect.top = y;
- break;
- default:
- Slog.wtf(TAG, "setDismissDimLayerVisible: Unknown dock side when setting dim "
- + "layer=" + mDockSide);
- return;
- }
- mDimLayer.setBounds(mTmpRect);
- setDimLayerVisible(true);
- }
-
- private void setMaximizeDimLayerVisible(int x, int y) {
- mTmpRect.set(0, 0, mDisplayWidth, mDisplayHeight);
- switch (mDockSide) {
- case DOCKED_LEFT:
- mTmpRect.left = x;
- break;
- case DOCKED_TOP:
- mTmpRect.top = y;
- break;
- case DOCKED_RIGHT:
- mTmpRect.right = x;
- break;
- case DOCKED_BOTTOM:
- mTmpRect.top = y;
- break;
- default:
- Slog.wtf(TAG, "setMaximizeDimLayerVisible: Unknown dock side when setting dim "
- + "layer=" + mDockSide);
- }
- mDimLayer.setBounds(mTmpRect);
- setDimLayerVisible(true);
- }
-
- private void setDimLayerVisible(boolean visible) {
- if (mDimLayerVisible == visible) {
- return;
- }
- mDimLayerVisible = visible;
- if (mDimLayerVisible) {
- mDimLayer.show(mDisplayContent.mService.getDragLayerLocked(), RESIZING_HINT_ALPHA,
- RESIZING_HINT_DURATION_MS);
- } else {
- mDimLayer.hide();
- }
- }
-
- private void resizeStack(int x, int y) {
- mTmpRect.set(mOriginalRect);
- final int deltaX = x - mStartX;
- final int deltaY = y - mStartY;
- switch (mDockSide) {
- case DOCKED_LEFT:
- mTmpRect.right += deltaX;
- break;
- case DOCKED_TOP:
- mTmpRect.bottom += deltaY;
- break;
- case DOCKED_RIGHT:
- mTmpRect.left += deltaX;
- break;
- case DOCKED_BOTTOM:
- mTmpRect.top += deltaY;
- break;
- }
- if (mTmpRect.equals(mLastResizeRect)) {
- return;
- }
- mLastResizeRect.set(mTmpRect);
- try {
- mDisplayContent.mService.mActivityManager.resizeStack(DOCKED_STACK_ID, mTmpRect, true);
- } catch (RemoteException e) {
- // This can't happen because we are in the same process.
- }
- updateDimLayer(x, y);
- }
-
- boolean isResizing() {
- return mTaskStack != null;
- }
-
- int getWidthAdjustment() {
- return getWidth() / 2;
- }
-
- @Override
- public boolean isFullscreen() {
- return false;
- }
-
- @Override
- public DisplayInfo getDisplayInfo() {
- return mDisplayContent.getDisplayInfo();
- }
-
- @Override
- public void getBounds(Rect outBounds) {
- // This dim layer user doesn't need this.
- }
-
- @Override
- public String toShortString() {
- return TAG;
+ mLastRect.set(frame);
}
}
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 3521682..2be7ab8 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -27,7 +27,6 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
-import android.os.Binder;
import android.os.IBinder;
import android.os.Message;
import android.os.Process;
@@ -52,6 +51,7 @@
SurfaceControl mSurfaceControl;
int mFlags;
IBinder mLocalWin;
+ int mPid;
int mUid;
ClipData mData;
ClipDescription mDataDescription;
@@ -77,7 +77,6 @@
mSurfaceControl = surface;
mFlags = flags;
mLocalWin = localWin;
- mUid = Binder.getCallingUid();
mNotifiedWindows = new ArrayList<WindowState>();
}
@@ -265,21 +264,34 @@
}
void broadcastDragEndedLw() {
+ final int myPid = Process.myPid();
+
if (WindowManagerService.DEBUG_DRAG) {
Slog.d(WindowManagerService.TAG, "broadcasting DRAG_ENDED");
}
- DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED,
- 0, 0, null, null, null, null, mDragResult);
- for (WindowState ws: mNotifiedWindows) {
+ for (WindowState ws : mNotifiedWindows) {
+ float x = 0;
+ float y = 0;
+ if (!mDragResult && (ws.mSession.mPid == mPid)) {
+ // Report unconsumed drop location back to the app that started the drag.
+ x = mCurrentX;
+ y = mCurrentY;
+ }
+ DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED,
+ x, y, null, null, null, null, mDragResult);
try {
ws.mClient.dispatchDragEvent(evt);
} catch (RemoteException e) {
Slog.w(WindowManagerService.TAG, "Unable to drag-end window " + ws);
}
+ // if the current window is in the same process,
+ // the dispatch has already recycled the event
+ if (myPid != ws.mSession.mPid) {
+ evt.recycle();
+ }
}
mNotifiedWindows.clear();
mDragInProgress = false;
- evt.recycle();
}
void endDragLw() {
@@ -423,7 +435,7 @@
continue;
}
- child.getVisibleBounds(mTmpRect, !BOUNDS_FOR_TOUCH);
+ child.getVisibleBounds(mTmpRect);
if (!mTmpRect.contains(x, y)) {
// outside of this window's activity stack == don't tell about drags
continue;
diff --git a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
index 4c8a6f9..0a372d8 100644
--- a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
+++ b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
@@ -54,7 +54,7 @@
SurfaceControl ctrl = null;
try {
if (WindowManagerService.DEBUG_SURFACE_TRACE) {
- ctrl = new WindowStateAnimator.SurfaceTrace(session, "EmulatorDisplayOverlay",
+ ctrl = new WindowSurfaceController.SurfaceTrace(session, "EmulatorDisplayOverlay",
mScreenSize.x, mScreenSize.y, PixelFormat.TRANSLUCENT,
SurfaceControl.HIDDEN);
} else {
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 6c391ad..3c3123f 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -17,7 +17,9 @@
package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
-import static com.android.server.wm.WindowState.BOUNDS_FOR_TOUCH;
+import static com.android.server.wm.WindowManagerService.DEBUG_FOCUS_LIGHT;
+import static com.android.server.wm.WindowManagerService.DEBUG_INPUT;
+
import android.app.ActivityManagerNative;
import android.graphics.Rect;
import android.os.RemoteException;
@@ -60,8 +62,6 @@
private final Object mInputDevicesReadyMonitor = new Object();
private boolean mInputDevicesReady;
- Rect mTmpRect = new Rect();
-
public InputMonitor(WindowManagerService service) {
mService = service;
}
@@ -174,17 +174,7 @@
final boolean hasFocus, final boolean hasWallpaper, DisplayContent displayContent) {
// Add a window to our list of input windows.
inputWindowHandle.name = child.toString();
- final boolean modal = (flags & (WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)) == 0;
- if (modal && child.mAppToken != null) {
- // Limit the outer touch to the activity stack region.
- flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
- child.getVisibleBounds(mTmpRect, BOUNDS_FOR_TOUCH);
- inputWindowHandle.touchableRegion.set(mTmpRect);
- } else {
- // Not modal or full screen modal
- child.getTouchableRegion(inputWindowHandle.touchableRegion);
- }
+ flags = child.getTouchableRegion(inputWindowHandle.touchableRegion, flags, this);
inputWindowHandle.layoutParamsFlags = flags;
inputWindowHandle.layoutParamsType = type;
inputWindowHandle.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos();
@@ -203,20 +193,6 @@
inputWindowHandle.frameTop = frame.top;
inputWindowHandle.frameRight = frame.right;
inputWindowHandle.frameBottom = frame.bottom;
- if (child.mAttrs.type == TYPE_DOCK_DIVIDER) {
- // We need to determine if the divider is horizontal or vertical and adjust its handle
- // frame accordingly.
- int adjustment = displayContent.mDividerControllerLocked.getWidthAdjustment();
- if (inputWindowHandle.frameRight - inputWindowHandle.frameLeft >
- inputWindowHandle.frameTop - inputWindowHandle.frameBottom) {
- // Horizontal divider.
- inputWindowHandle.frameTop -= adjustment;
- inputWindowHandle.frameBottom += adjustment;
- } else {
- inputWindowHandle.frameLeft -= adjustment;
- inputWindowHandle.frameRight += adjustment;
- }
- }
if (child.mGlobalScale != 1) {
// If we are scaling the window, input coordinates need
@@ -227,7 +203,9 @@
inputWindowHandle.scaleFactor = 1;
}
-
+ if (DEBUG_INPUT) {
+ Slog.d(WindowManagerService.TAG, "addInputWindowHandle: " + inputWindowHandle);
+ }
addInputWindowHandleLw(inputWindowHandle);
}
@@ -428,7 +406,7 @@
* Layer assignment is assumed to be complete by the time this is called.
*/
public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) {
- if (WindowManagerService.DEBUG_FOCUS_LIGHT || WindowManagerService.DEBUG_INPUT) {
+ if (DEBUG_FOCUS_LIGHT || DEBUG_INPUT) {
Slog.d(WindowManagerService.TAG, "Input focus has changed to " + newWindow);
}
@@ -464,7 +442,7 @@
public void pauseDispatchingLw(WindowToken window) {
if (! window.paused) {
- if (WindowManagerService.DEBUG_INPUT) {
+ if (DEBUG_INPUT) {
Slog.v(WindowManagerService.TAG, "Pausing WindowToken " + window);
}
@@ -475,7 +453,7 @@
public void resumeDispatchingLw(WindowToken window) {
if (window.paused) {
- if (WindowManagerService.DEBUG_INPUT) {
+ if (DEBUG_INPUT) {
Slog.v(WindowManagerService.TAG, "Resuming WindowToken " + window);
}
@@ -486,7 +464,7 @@
public void freezeInputDispatchingLw() {
if (! mInputDispatchFrozen) {
- if (WindowManagerService.DEBUG_INPUT) {
+ if (DEBUG_INPUT) {
Slog.v(WindowManagerService.TAG, "Freezing input dispatching");
}
@@ -497,7 +475,7 @@
public void thawInputDispatchingLw() {
if (mInputDispatchFrozen) {
- if (WindowManagerService.DEBUG_INPUT) {
+ if (DEBUG_INPUT) {
Slog.v(WindowManagerService.TAG, "Thawing input dispatching");
}
@@ -508,7 +486,7 @@
public void setEventDispatchingLw(boolean enabled) {
if (mInputDispatchEnabled != enabled) {
- if (WindowManagerService.DEBUG_INPUT) {
+ if (DEBUG_INPUT) {
Slog.v(WindowManagerService.TAG, "Setting event dispatching to " + enabled);
}
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index 283d498..3158c47 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -19,7 +19,7 @@
import java.io.PrintWriter;
import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
-import static com.android.server.wm.WindowStateAnimator.SurfaceTrace;
+import static com.android.server.wm.WindowSurfaceController.SurfaceTrace;
import static com.android.server.wm.WindowStateAnimator.WINDOW_FREEZE_LAYER;
import android.content.Context;
import android.graphics.Matrix;
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 1f62bc1..1caeca0 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -187,6 +187,13 @@
mService.removeWindow(this, window);
}
+ @Override
+ public void repositionChild(IWindow window, int left, int top, int right, int bottom,
+ long deferTransactionUntilFrame, Rect outFrame) {
+ mService.repositionChild(this, window, left, top, right, bottom,
+ deferTransactionUntilFrame, outFrame);
+ }
+
public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewFlags,
int flags, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index c4600e0..1b86488 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -16,19 +16,21 @@
package com.android.server.wm;
-import static android.app.ActivityManager.DOCKED_STACK_ID;
+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.HOME_STACK_ID;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.app.ActivityManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION;
import static com.android.server.wm.WindowManagerService.TAG;
import static com.android.server.wm.WindowManagerService.DEBUG_RESIZE;
import static com.android.server.wm.WindowManagerService.DEBUG_STACK;
import static com.android.server.wm.WindowManagerService.H.RESIZE_TASK;
-import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
+import android.app.ActivityManager.StackId;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.util.EventLog;
import android.util.Slog;
-import android.util.SparseArray;
import android.view.DisplayInfo;
import android.view.Surface;
@@ -114,7 +116,7 @@
mDeferRemoval = false;
DisplayContent content = getDisplayContent();
if (content != null) {
- content.mDimBehindController.removeDimLayerUser(this);
+ content.mDimLayerController.removeDimLayerUser(this);
}
mStack.removeTask(this);
mService.mTaskIdToTask.delete(mTaskId);
@@ -133,7 +135,7 @@
stack.addTask(this, toTop);
}
- void positionTaskInStack(TaskStack stack, int position) {
+ void positionTaskInStack(TaskStack stack, int position, Rect bounds, Configuration config) {
if (mStack != null && stack != mStack) {
if (DEBUG_STACK) Slog.i(TAG, "positionTaskInStack: removing taskId=" + mTaskId
+ " from stack=" + mStack);
@@ -141,6 +143,7 @@
mStack.removeTask(this);
}
stack.positionTask(this, position, showForAllUsers());
+ setBounds(bounds, config);
}
boolean removeAppToken(AppWindowToken wtoken) {
@@ -187,7 +190,8 @@
bounds = mTmpRect;
mFullscreen = true;
} else {
- if (mStack.mStackId != FREEFORM_WORKSPACE_STACK_ID || bounds.isEmpty()) {
+ if ((mStack.mStackId != FREEFORM_WORKSPACE_STACK_ID
+ && mStack.mStackId != PINNED_STACK_ID) || bounds.isEmpty()) {
// ensure bounds are entirely within the display rect
if (!bounds.intersect(mTmpRect)) {
// Can't set bounds outside the containing display...Sorry!
@@ -217,7 +221,7 @@
mBounds.set(bounds);
mRotation = rotation;
if (displayContent != null) {
- displayContent.mDimBehindController.updateDimLayer(this);
+ displayContent.mDimLayerController.updateDimLayer(this);
}
mOverrideConfig = mFullscreen ? Configuration.EMPTY : config;
return boundsChange;
@@ -241,8 +245,7 @@
private boolean useCurrentBounds() {
final DisplayContent displayContent = mStack.getDisplayContent();
if (mFullscreen
- || mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID
- || mStack.mStackId == DOCKED_STACK_ID
+ || !StackId.isTaskResizeableByDockedStack(mStack.mStackId)
|| displayContent == null
|| displayContent.getDockedStackLocked() != null) {
return true;
@@ -250,8 +253,7 @@
return false;
}
- /** Bounds of the task with other system factors taken into consideration. */
- @Override
+ /** Original bounds of the task if applicable, otherwise fullscreen rect. */
public void getBounds(Rect out) {
if (useCurrentBounds()) {
// No need to adjust the output bounds if fullscreen or the docked stack is visible
@@ -266,6 +268,65 @@
mStack.getDisplayContent().getLogicalDisplayRect(out);
}
+
+ /**
+ * Calculate the maximum visible area of this task. If the task has only one app,
+ * the result will be visible frame of that app. If the task has more than one apps,
+ * we search from top down if the next app got different visible area.
+ *
+ * This effort is to handle the case where some task (eg. GMail composer) might pop up
+ * a dialog that's different in size from the activity below, in which case we should
+ * be dimming the entire task area behind the dialog.
+ *
+ * @param out Rect containing the max visible bounds.
+ * @return true if the task has some visible app windows; false otherwise.
+ */
+ boolean getMaxVisibleBounds(Rect out) {
+ boolean foundTop = false;
+ for (int i = mAppTokens.size() - 1; i >= 0; i--) {
+ final WindowState win = mAppTokens.get(i).findMainWindow();
+ if (win == null) {
+ continue;
+ }
+ if (!foundTop) {
+ out.set(win.mVisibleFrame);
+ foundTop = true;
+ continue;
+ }
+ if (win.mVisibleFrame.left < out.left) {
+ out.left = win.mVisibleFrame.left;
+ }
+ if (win.mVisibleFrame.top < out.top) {
+ out.top = win.mVisibleFrame.top;
+ }
+ if (win.mVisibleFrame.right > out.right) {
+ out.right = win.mVisibleFrame.right;
+ }
+ if (win.mVisibleFrame.bottom > out.bottom) {
+ out.bottom = win.mVisibleFrame.bottom;
+ }
+ }
+ return foundTop;
+ }
+
+ /** Bounds of the task to be used for dimming, as well as touch related tests. */
+ @Override
+ public void getDimBounds(Rect out) {
+ if (useCurrentBounds()) {
+ if (inFreeformWorkspace() && getMaxVisibleBounds(out)) {
+ return;
+ }
+
+ out.set(mBounds);
+ return;
+ }
+
+ // The bounds has been adjusted to accommodate for a docked stack, but the docked stack
+ // is not currently visible. Go ahead a represent it as fullscreen to the rest of the
+ // system.
+ mStack.getDisplayContent().getLogicalDisplayRect(out);
+ }
+
void setDragResizing(boolean dragResizing) {
mDragResizing = dragResizing;
}
@@ -317,20 +378,51 @@
}
}
+ /**
+ * Cancels any running app transitions associated with the task.
+ */
+ void cancelTaskWindowTransition() {
+ for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
+ mAppTokens.get(activityNdx).mAppAnimator.clearAnimation();
+ }
+ }
+
+ /**
+ * Cancels any running thumbnail transitions associated with the task.
+ */
+ void cancelTaskThumbnailTransition() {
+ for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
+ mAppTokens.get(activityNdx).mAppAnimator.clearThumbnail();
+ }
+ }
+
boolean showForAllUsers() {
final int tokensCount = mAppTokens.size();
return (tokensCount != 0) && mAppTokens.get(tokensCount - 1).showForAllUsers;
}
+ boolean inHomeStack() {
+ return mStack != null && mStack.mStackId == HOME_STACK_ID;
+ }
+
boolean inFreeformWorkspace() {
return mStack != null && mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID;
}
+ boolean inDockedWorkspace() {
+ return mStack != null && mStack.mStackId == DOCKED_STACK_ID;
+ }
+
WindowState getTopAppMainWindow() {
final int tokensCount = mAppTokens.size();
return tokensCount > 0 ? mAppTokens.get(tokensCount - 1).findMainWindow() : null;
}
+ AppWindowToken getTopAppWindowToken() {
+ final int tokensCount = mAppTokens.size();
+ return tokensCount > 0 ? mAppTokens.get(tokensCount - 1) : null;
+ }
+
@Override
public boolean isFullscreen() {
if (useCurrentBounds()) {
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 227b3f0..dd47e7a 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -18,7 +18,7 @@
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
-import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.RESIZE_MODE_USER;
import static android.app.ActivityManager.RESIZE_MODE_USER_FORCED;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
@@ -149,7 +149,7 @@
}
synchronized (mService.mWindowMap) {
mDragEnded = notifyMoveLocked(newX, newY);
- mTask.getBounds(mTmpRect);
+ mTask.getDimBounds(mTmpRect);
}
if (!mTmpRect.equals(mWindowDragBounds)) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
@@ -353,7 +353,9 @@
mStartDragX = startX;
mStartDragY = startY;
- mService.getTaskBounds(mTask.mTaskId, mWindowOriginalBounds);
+ // Use the visible bounds, not the original task bounds. The cursor
+ // movement should be calculated relative to the visible bounds.
+ mWindowOriginalBounds.set(win.mVisibleFrame);
}
private void endDragLocked() {
@@ -393,7 +395,7 @@
}
// This is a moving operation.
- mTask.mStack.getBounds(mTmpRect);
+ mTask.mStack.getDimBounds(mTmpRect);
mTmpRect.inset(mMinVisibleWidth, mMinVisibleHeight);
if (!mTmpRect.contains((int) x, (int) y)) {
// We end the moving operation if position is outside the stack bounds.
@@ -438,7 +440,7 @@
return CTRL_NONE;
}
- mTask.mStack.getBounds(mTmpRect);
+ mTask.mStack.getDimBounds(mTmpRect);
if (x - mSideMargin <= mTmpRect.left) {
return CTRL_LEFT;
}
@@ -450,7 +452,7 @@
}
private void showDimLayer() {
- mTask.mStack.getBounds(mTmpRect);
+ mTask.mStack.getDimBounds(mTmpRect);
if (mCurrentDimSide == CTRL_LEFT) {
mTmpRect.right = mTmpRect.centerX();
} else if (mCurrentDimSide == CTRL_RIGHT) {
@@ -473,7 +475,7 @@
}
@Override
- public void getBounds(Rect out) {
+ public void getDimBounds(Rect out) {
// This dim layer user doesn't need this.
}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index b409cea..8085f13 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -16,13 +16,7 @@
package com.android.server.wm;
-import static android.app.ActivityManager.*;
-import static com.android.server.wm.WindowManagerService.DEBUG_TASK_MOVEMENT;
-import static com.android.server.wm.WindowManagerService.H.RESIZE_STACK;
-import static com.android.server.wm.WindowManagerService.H.UNUSED;
-import static com.android.server.wm.WindowManagerService.TAG;
-
-import android.annotation.IntDef;
+import android.app.ActivityManager.StackId;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Debug;
@@ -30,15 +24,25 @@
import android.util.Slog;
import android.util.SparseArray;
import android.view.DisplayInfo;
-
import android.view.Surface;
+
import com.android.server.EventLogTags;
import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+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.view.WindowManager.DOCKED_BOTTOM;
+import static android.view.WindowManager.DOCKED_INVALID;
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_RIGHT;
+import static android.view.WindowManager.DOCKED_TOP;
+import static com.android.server.wm.WindowManagerService.DEBUG_TASK_MOVEMENT;
+import static com.android.server.wm.WindowManagerService.H.RESIZE_STACK;
+import static com.android.server.wm.WindowManagerService.TAG;
+
public class TaskStack implements DimLayer.DimLayerUser {
// If the stack should be resized to fullscreen.
@@ -82,21 +86,6 @@
/** Detach this stack from its display when animation completes. */
boolean mDeferDetach;
- static final int DOCKED_INVALID = -1;
- static final int DOCKED_LEFT = 1;
- static final int DOCKED_TOP = 2;
- static final int DOCKED_RIGHT = 3;
- static final int DOCKED_BOTTOM = 4;
-
- @IntDef({
- DOCKED_INVALID,
- DOCKED_LEFT,
- DOCKED_TOP,
- DOCKED_RIGHT,
- DOCKED_BOTTOM})
- @Retention(RetentionPolicy.SOURCE)
- @interface DockSide {}
-
TaskStack(WindowManagerService service, int stackId) {
mService = service;
mStackId = stackId;
@@ -117,11 +106,6 @@
}
}
- boolean allowTaskResize() {
- return mStackId == FREEFORM_WORKSPACE_STACK_ID
- || mStackId == DOCKED_STACK_ID;
- }
-
/**
* Set the bounds of the stack and its containing tasks.
* @param stackBounds New stack bounds. Passing in null sets the bounds to fullscreen.
@@ -177,7 +161,7 @@
}
if (mDisplayContent != null) {
- mDisplayContent.mDimBehindController.updateDimLayer(this);
+ mDisplayContent.mDimLayerController.updateDimLayer(this);
mAnimationBackgroundSurface.setBounds(bounds);
}
@@ -197,7 +181,7 @@
/** Return true if the current bound can get outputted to the rest of the system as-is. */
private boolean useCurrentBounds() {
if (mFullscreen
- || mStackId == DOCKED_STACK_ID
+ || !StackId.isResizeableByDockedStack(mStackId)
|| mDisplayContent == null
|| mDisplayContent.getDockedStackLocked() != null) {
return true;
@@ -205,8 +189,6 @@
return false;
}
- /** Bounds of the stack with other system factors taken into consideration. */
- @Override
public void getBounds(Rect out) {
if (useCurrentBounds()) {
// No need to adjust the output bounds if fullscreen or the docked stack is visible
@@ -221,6 +203,12 @@
mDisplayContent.getLogicalDisplayRect(out);
}
+ /** Bounds of the stack with other system factors taken into consideration. */
+ @Override
+ public void getDimBounds(Rect out) {
+ getBounds(out);
+ }
+
void updateDisplayInfo(Rect bounds) {
if (mDisplayContent != null) {
for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
@@ -389,9 +377,9 @@
Rect bounds = null;
final TaskStack dockedStack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
- if (mStackId == DOCKED_STACK_ID || (dockedStack != null
- && mStackId >= FIRST_STATIC_STACK_ID && mStackId <= LAST_STATIC_STACK_ID)) {
- // The existence of a docked stack affects the size of any static stack created since
+ if (mStackId == DOCKED_STACK_ID
+ || (dockedStack != null && StackId.isResizeableByDockedStack(mStackId))) {
+ // The existence of a docked stack affects the size of other static stack created since
// the docked stack occupies a dedicated region on screen.
bounds = new Rect();
displayContent.getLogicalDisplayRect(mTmpRect);
@@ -402,7 +390,7 @@
final boolean dockedOnTopOrLeft = WindowManagerService.sDockedStackCreateMode
== DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
getStackDockedModeBounds(mTmpRect, bounds, mStackId, mTmpRect2,
- mDisplayContent.mDividerControllerLocked.getWidthAdjustment(),
+ mDisplayContent.mDividerControllerLocked.getContentWidth(),
dockedOnTopOrLeft);
}
@@ -417,21 +405,25 @@
}
void getStackDockedModeBoundsLocked(Rect outBounds) {
- if (mStackId == DOCKED_STACK_ID
- || mStackId > LAST_STATIC_STACK_ID
- || mDisplayContent == null) {
+ if (!StackId.isResizeableByDockedStack(mStackId) || mDisplayContent == null) {
outBounds.set(mBounds);
return;
}
- final TaskStack dockedStack = mDisplayContent.getDockedStackLocked();
+ final TaskStack dockedStack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
if (dockedStack == null) {
// Not sure why you are calling this method when there is no docked stack...
throw new IllegalStateException(
"Calling getStackDockedModeBoundsLocked() when there is no docked stack.");
}
+ if (!dockedStack.isVisibleLocked()) {
+ // The docked stack is being dismissed, but we caught before it finished being
+ // dismissed. In that case we want to treat it as if it is not occupying any space and
+ // let others occupy the whole display.
+ mDisplayContent.getLogicalDisplayRect(mTmpRect);
+ return;
+ }
- @DockSide
final int dockedSide = dockedStack.getDockSide();
if (dockedSide == DOCKED_INVALID) {
// Not sure how you got here...Only thing we can do is return current bounds.
@@ -442,9 +434,10 @@
mDisplayContent.getLogicalDisplayRect(mTmpRect);
dockedStack.getRawBounds(mTmpRect2);
- final boolean dockedOnTopOrLeft = dockedSide == DOCKED_TOP || dockedSide == DOCKED_LEFT;
+ final boolean dockedOnTopOrLeft = dockedSide == DOCKED_TOP
+ || dockedSide == DOCKED_LEFT;
getStackDockedModeBounds(mTmpRect, outBounds, mStackId, mTmpRect2,
- mDisplayContent.mDividerControllerLocked.getWidthAdjustment(), dockedOnTopOrLeft);
+ mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft);
}
@@ -454,13 +447,13 @@
* @param outBounds Output bounds that should be used for the stack.
* @param stackId Id of stack we are calculating the bounds for.
* @param dockedBounds Bounds of the docked stack.
- * @param adjustment Additional adjustment to make to the output bounds close to the side of the
- * dock.
- * @param dockOntopOrLeft If the docked stack is on the top or left side of the screen.
+ * @param dockDividerWidth We need to know the width of the divider make to the output bounds
+ * close to the side of the dock.
+ * @param dockOnTopOrLeft If the docked stack is on the top or left side of the screen.
*/
private static void getStackDockedModeBounds(
- Rect displayRect, Rect outBounds, int stackId, Rect dockedBounds, int adjustment,
- boolean dockOntopOrLeft) {
+ Rect displayRect, Rect outBounds, int stackId, Rect dockedBounds, int dockDividerWidth,
+ boolean dockOnTopOrLeft) {
final boolean dockedStack = stackId == DOCKED_STACK_ID;
final boolean splitHorizontally = displayRect.width() > displayRect.height();
@@ -469,34 +462,34 @@
// The initial bounds of the docked stack when it is created half the screen space and
// its bounds can be adjusted after that. The bounds of all other stacks are adjusted
// to occupy whatever screen space the docked stack isn't occupying.
- if (dockOntopOrLeft) {
+ if (dockOnTopOrLeft) {
if (splitHorizontally) {
- outBounds.right = displayRect.centerX() - adjustment;
+ outBounds.right = displayRect.centerX() - dockDividerWidth / 2;
} else {
- outBounds.bottom = displayRect.centerY() - adjustment;
+ outBounds.bottom = displayRect.centerY() - dockDividerWidth / 2;
}
} else {
if (splitHorizontally) {
- outBounds.left = displayRect.centerX() + adjustment;
+ outBounds.left = displayRect.centerX() + dockDividerWidth / 2;
} else {
- outBounds.top = displayRect.centerY() + adjustment;
+ outBounds.top = displayRect.centerY() + dockDividerWidth / 2;
}
}
return;
}
// Other stacks occupy whatever space is left by the docked stack.
- if (!dockOntopOrLeft) {
+ if (!dockOnTopOrLeft) {
if (splitHorizontally) {
- outBounds.right = dockedBounds.left - adjustment;
+ outBounds.right = dockedBounds.left - dockDividerWidth;
} else {
- outBounds.bottom = dockedBounds.top - adjustment;
+ outBounds.bottom = dockedBounds.top - dockDividerWidth;
}
} else {
if (splitHorizontally) {
- outBounds.left = dockedBounds.right + adjustment;
+ outBounds.left = dockedBounds.right + dockDividerWidth;
} else {
- outBounds.top = dockedBounds.bottom + adjustment;
+ outBounds.top = dockedBounds.bottom + dockDividerWidth;
}
}
}
@@ -515,16 +508,14 @@
final boolean dockedOnTopOrLeft = WindowManagerService.sDockedStackCreateMode
== DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
getStackDockedModeBounds(bounds, bounds, FULLSCREEN_WORKSPACE_STACK_ID, dockedBounds,
- mDisplayContent.mDividerControllerLocked.getWidth(), dockedOnTopOrLeft);
+ mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft);
}
final int count = mService.mStackIdToStack.size();
for (int i = 0; i < count; i++) {
final TaskStack otherStack = mService.mStackIdToStack.valueAt(i);
final int otherStackId = otherStack.mStackId;
- if (otherStackId != DOCKED_STACK_ID
- && otherStackId >= FIRST_STATIC_STACK_ID
- && otherStackId <= LAST_STATIC_STACK_ID) {
+ if (StackId.isResizeableByDockedStack(otherStackId)) {
mService.mH.sendMessage(
mService.mH.obtainMessage(RESIZE_STACK, otherStackId,
1 /*allowResizeInDockedMode*/, bounds));
@@ -658,7 +649,6 @@
/**
* For docked workspace provides information which side of the screen was the dock anchored.
*/
- @DockSide
int getDockSide() {
if (mStackId != DOCKED_STACK_ID) {
return DOCKED_INVALID;
@@ -698,4 +688,26 @@
}
return false;
}
+
+ /**
+ * Returns true if this stack has a window that is fully visible, doesn't perform an entry
+ * animation and is just positioned where it's supposed to be.
+ */
+ boolean hasWindowWithFinalVisibility() {
+ for (int i = mTasks.size() - 1; i >= 0; i--) {
+ Task task = mTasks.get(i);
+ for (int j = task.mAppTokens.size() - 1; j >= 0; j--) {
+ final AppWindowToken token = task.mAppTokens.get(j);
+ if (token.mAppAnimator.animating || token.mWillReplaceWindow) {
+ continue;
+ }
+ WindowState win = token.findMainWindow();
+ if (win != null && !win.mWinAnimator.mEnterAnimationPending
+ && !win.mWinAnimator.mEnteringAnimation) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
}
diff --git a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
index ce1b785..1fe359e 100644
--- a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
+++ b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
@@ -16,13 +16,22 @@
package com.android.server.wm;
+import android.graphics.Rect;
import android.graphics.Region;
import android.view.DisplayInfo;
+import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.WindowManagerPolicy.PointerEventListener;
import com.android.server.wm.WindowManagerService.H;
+import static android.view.PointerIcon.STYLE_NOT_SPECIFIED;
+import static android.view.PointerIcon.STYLE_DEFAULT;
+import static android.view.PointerIcon.STYLE_HORIZONTAL_DOUBLE_ARROW;
+import static android.view.PointerIcon.STYLE_VERTICAL_DOUBLE_ARROW;
+import static android.view.PointerIcon.STYLE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW;
+import static android.view.PointerIcon.STYLE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW;
+
public class TaskTapPointerEventListener implements PointerEventListener {
private static final int TAP_TIMEOUT_MSEC = 300;
private static final float TAP_MOTION_SLOP_INCHES = 0.125f;
@@ -34,6 +43,8 @@
final private Region mTouchExcludeRegion = new Region();
private final WindowManagerService mService;
private final DisplayContent mDisplayContent;
+ private final Rect mTmpRect = new Rect();
+ private int mPointerIconShape = STYLE_NOT_SPECIFIED;
public TaskTapPointerEventListener(WindowManagerService service,
DisplayContent displayContent) {
@@ -76,6 +87,42 @@
break;
}
+ case MotionEvent.ACTION_HOVER_MOVE: {
+ final int x = (int) motionEvent.getX();
+ final int y = (int) motionEvent.getY();
+ final WindowState window = mDisplayContent.findWindowForControlPoint(x, y);
+ if (window == null) {
+ break;
+ }
+ window.getVisibleBounds(mTmpRect);
+ if (!mTmpRect.isEmpty() && !mTmpRect.contains(x, y)) {
+ int iconShape = STYLE_DEFAULT;
+ if (x < mTmpRect.left) {
+ iconShape =
+ (y < mTmpRect.top) ? STYLE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW :
+ (y > mTmpRect.bottom) ? STYLE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW :
+ STYLE_HORIZONTAL_DOUBLE_ARROW;
+ } else if (x > mTmpRect.right) {
+ iconShape =
+ (y < mTmpRect.top) ? STYLE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW :
+ (y > mTmpRect.bottom) ? STYLE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW :
+ STYLE_HORIZONTAL_DOUBLE_ARROW;
+ } else if (y < mTmpRect.top || y > mTmpRect.bottom) {
+ iconShape = STYLE_VERTICAL_DOUBLE_ARROW;
+ }
+ if (mPointerIconShape != iconShape) {
+ mPointerIconShape = iconShape;
+ motionEvent.getDevice().setPointerShape(iconShape);
+ }
+ } else {
+ mPointerIconShape = STYLE_NOT_SPECIFIED;
+ }
+ } break;
+
+ case MotionEvent.ACTION_HOVER_EXIT:
+ motionEvent.getDevice().setPointerShape(STYLE_DEFAULT);
+ break;
+
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP: {
int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 1a946b2..8b984f0 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -16,7 +16,7 @@
package com.android.server.wm;
-import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
@@ -149,11 +149,11 @@
}
void updateWallpaperVisibility() {
- final boolean visible = isWallpaperVisible(mWallpaperTarget);
final DisplayContent displayContent = mWallpaperTarget.getDisplayContent();
if (displayContent == null) {
return;
}
+ final boolean visible = isWallpaperVisible(mWallpaperTarget);
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
final int dw = displayInfo.logicalWidth;
final int dh = displayInfo.logicalHeight;
@@ -205,7 +205,7 @@
final WindowState wallpaper = token.windows.get(j);
final WindowStateAnimator winAnimator = wallpaper.mWinAnimator;
if (!winAnimator.mLastHidden || wasDeferred) {
- winAnimator.hide();
+ winAnimator.hide("hideWallpapers");
dispatchWallpaperVisibility(wallpaper, false);
final DisplayContent displayContent = wallpaper.getDisplayContent();
if (displayContent != null) {
@@ -419,7 +419,7 @@
winAnimator.computeShownFrameLocked();
// No need to lay out the windows - we can just set the wallpaper position
// directly.
- winAnimator.setWallpaperOffset(wallpaper.mShownFrame);
+ winAnimator.setWallpaperOffset(wallpaper.mShownPosition);
// We only want to be synchronous with one wallpaper.
sync = false;
}
@@ -470,13 +470,13 @@
}
private void findWallpaperTarget(WindowList windows, FindWallpaperTargetResult result) {
-
final WindowAnimator winAnimator = mService.mAnimator;
result.reset();
WindowState w = null;
int windowDetachedI = -1;
boolean resetTopWallpaper = false;
boolean inFreeformSpace = false;
+ boolean replacing = false;
for (int i = windows.size() - 1; i >= 0; i--) {
w = windows.get(i);
if ((w.mAttrs.type == TYPE_WALLPAPER)) {
@@ -504,12 +504,13 @@
inFreeformSpace = stack != null && stack.mStackId == FREEFORM_WORKSPACE_STACK_ID;
}
+ replacing = replacing || (w.mAppToken != null && w.mAppToken.mWillReplaceWindow);
+
// If the app is executing an animation because the keyguard is going away,
// keep the wallpaper during the animation so it doesn't flicker out.
final boolean hasWallpaper = (w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0
|| (w.mAppToken != null && w.mWinAnimator.mKeyguardGoingAwayAnimation);
- if (hasWallpaper && w.isOnScreen()
- && (mWallpaperTarget == w || w.isDrawFinishedLw())) {
+ if (hasWallpaper && w.isOnScreen() && (mWallpaperTarget == w || w.isDrawFinishedLw())) {
if (DEBUG_WALLPAPER) Slog.v(TAG, "Found wallpaper target: #" + i + "=" + w);
result.setWallpaperTarget(w, i);
if (w == mWallpaperTarget && w.mWinAnimator.isAnimating()) {
@@ -530,9 +531,12 @@
"Found animating detached wallpaper activity: #" + windowDetachedI + "=" + w);
result.setWallpaperTarget(w, windowDetachedI);
}
- if (result.wallpaperTarget == null && inFreeformSpace) {
+ if (result.wallpaperTarget == null
+ && (inFreeformSpace || (replacing && mWallpaperTarget != null))) {
// In freeform mode we set the wallpaper as its own target, so we don't need an
- // additional window to make it visible.
+ // additional window to make it visible. When we are replacing a window and there was
+ // wallpaper before replacement, we want to keep the window until the new windows fully
+ // appear and can determine the visibility, to avoid flickering.
result.setWallpaperTarget(result.topWallpaper, result.topWallpaperIndex);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 928a117..38bd71d 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -18,28 +18,35 @@
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
-
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+import static com.android.server.wm.WindowManagerService.DEBUG_ANIM;
+import static com.android.server.wm.WindowManagerService.DEBUG_FOCUS_LIGHT;
import static com.android.server.wm.WindowManagerService.DEBUG_KEYGUARD;
-import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION;
-import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE;
+import static com.android.server.wm.WindowManagerService.DEBUG_LAYOUT_REPEATS;
+import static com.android.server.wm.WindowManagerService.DEBUG_ORIENTATION;
+import static com.android.server.wm.WindowManagerService.DEBUG_VISIBILITY;
+import static com.android.server.wm.WindowManagerService.DEBUG_WALLPAPER;
+import static com.android.server.wm.WindowManagerService.DEBUG_WINDOW_TRACE;
+import static com.android.server.wm.WindowManagerService.SHOW_TRANSACTIONS;
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;
import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING;
+import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE;
import android.content.Context;
-import android.os.RemoteException;
+import android.os.Trace;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
+import android.view.Choreographer;
import android.view.Display;
import android.view.SurfaceControl;
import android.view.WindowManagerPolicy;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
-import android.view.Choreographer;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -100,6 +107,10 @@
static final int KEYGUARD_ANIMATING_OUT = 2;
int mForceHiding = KEYGUARD_NOT_SHOWN;
+ // When set to true the animator will go over all windows after an animation frame is posted and
+ // check if some got replaced and can be removed.
+ private boolean mRemoveReplacedWindows = false;
+
private String forceHidingToString() {
switch (mForceHiding) {
case KEYGUARD_NOT_SHOWN: return "KEYGUARD_NOT_SHOWN";
@@ -163,7 +174,7 @@
setAppLayoutChanges(appAnimator,
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER,
"appToken " + appAnimator.mAppToken + " done", displayId);
- if (WindowManagerService.DEBUG_ANIM) Slog.v(TAG,
+ if (DEBUG_ANIM) Slog.v(TAG,
"updateWindowsApps...: done animating " + appAnimator.mAppToken);
}
}
@@ -181,7 +192,7 @@
setAppLayoutChanges(appAnimator,
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER,
"exiting appToken " + appAnimator.mAppToken + " done", displayId);
- if (WindowManagerService.DEBUG_ANIM) Slog.v(TAG,
+ if (DEBUG_ANIM) Slog.v(TAG,
"updateWindowsApps...: done animating exiting "
+ appAnimator.mAppToken);
}
@@ -266,13 +277,14 @@
final int flags = win.mAttrs.flags;
boolean canBeForceHidden = mPolicy.canBeForceHidden(win, win.mAttrs);
boolean shouldBeForceHidden = shouldForceHide(win);
- if (winAnimator.mSurfaceControl != null) {
+ if (winAnimator.mSurfaceController != null
+ && winAnimator.mSurfaceController.hasSurface()) {
final boolean wasAnimating = winAnimator.mWasAnimating;
final boolean nowAnimating = winAnimator.stepAnimationLocked(mCurrentTime);
winAnimator.mWasAnimating = nowAnimating;
mAnimating |= nowAnimating;
- if (WindowManagerService.DEBUG_WALLPAPER) {
+ if (DEBUG_WALLPAPER) {
Slog.v(TAG, win + ": wasAnimating=" + wasAnimating +
", nowAnimating=" + nowAnimating);
}
@@ -282,7 +294,7 @@
mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;
setPendingLayoutChanges(Display.DEFAULT_DISPLAY,
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
- if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
+ if (DEBUG_LAYOUT_REPEATS) {
mWindowPlacerLocked.debugLayoutRepeats(
"updateWindowsAndWallpaperLocked 2",
getPendingLayoutChanges(Display.DEFAULT_DISPLAY));
@@ -291,13 +303,13 @@
if (mPolicy.isForceHiding(win.mAttrs)) {
if (!wasAnimating && nowAnimating) {
- if (DEBUG_KEYGUARD || WindowManagerService.DEBUG_ANIM ||
- WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
+ if (DEBUG_KEYGUARD || DEBUG_ANIM ||
+ DEBUG_VISIBILITY) Slog.v(TAG,
"Animation started that could impact force hide: " + win);
mBulkUpdateParams |= SET_FORCE_HIDING_CHANGED;
setPendingLayoutChanges(displayId,
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
- if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
+ if (DEBUG_LAYOUT_REPEATS) {
mWindowPlacerLocked.debugLayoutRepeats(
"updateWindowsAndWallpaperLocked 3",
getPendingLayoutChanges(displayId));
@@ -316,7 +328,7 @@
mForceHiding = win.isDrawnLw() ? KEYGUARD_SHOWN : KEYGUARD_NOT_SHOWN;
}
}
- if (DEBUG_KEYGUARD || WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
+ if (DEBUG_KEYGUARD || DEBUG_VISIBILITY) Slog.v(TAG,
"Force hide " + forceHidingToString()
+ " hasSurface=" + win.mHasSurface
+ " policyVis=" + win.mPolicyVisibility
@@ -331,7 +343,7 @@
// Was already hidden
continue;
}
- if (DEBUG_KEYGUARD || WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
+ if (DEBUG_KEYGUARD || DEBUG_VISIBILITY) Slog.v(TAG,
"Now policy hidden: " + win);
} else {
boolean applyExistingExitAnimation = mPostKeyguardExitAnimation != null
@@ -353,7 +365,7 @@
win.hideLw(false, false);
continue;
}
- if (DEBUG_KEYGUARD || WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
+ if (DEBUG_KEYGUARD || DEBUG_VISIBILITY) Slog.v(TAG,
"Now policy shown: " + win);
if ((mBulkUpdateParams & SET_FORCE_HIDING_CHANGED) != 0
&& win.mAttachedWindow == null) {
@@ -383,7 +395,7 @@
// We are showing on top of the current
// focus, so re-evaluate focus to make
// sure it is correct.
- if (WindowManagerService.DEBUG_FOCUS_LIGHT) Slog.v(TAG,
+ if (DEBUG_FOCUS_LIGHT) Slog.v(TAG,
"updateWindowsLocked: setting mFocusMayChange true");
mService.mFocusMayChange = true;
}
@@ -392,7 +404,7 @@
mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;
setPendingLayoutChanges(Display.DEFAULT_DISPLAY,
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
- if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
+ if (DEBUG_LAYOUT_REPEATS) {
mWindowPlacerLocked.debugLayoutRepeats(
"updateWindowsAndWallpaperLocked 4",
getPendingLayoutChanges(Display.DEFAULT_DISPLAY));
@@ -413,11 +425,11 @@
final AppWindowToken atoken = win.mAppToken;
if (winAnimator.mDrawState == WindowStateAnimator.READY_TO_SHOW) {
- if (atoken == null || atoken.allDrawn) {
+ if (atoken == null || atoken.allDrawn || atoken.mAnimatingWithSavedSurface) {
if (winAnimator.performShowLocked()) {
setPendingLayoutChanges(displayId,
WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM);
- if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
+ if (DEBUG_LAYOUT_REPEATS) {
mWindowPlacerLocked.debugLayoutRepeats(
"updateWindowsAndWallpaperLocked 5",
getPendingLayoutChanges(displayId));
@@ -511,7 +523,7 @@
for (int i = windows.size() - 1; i >= 0; i--) {
final WindowState win = windows.get(i);
WindowStateAnimator winAnimator = win.mWinAnimator;
- if (winAnimator.mSurfaceControl == null) {
+ if (winAnimator.mSurfaceController == null) {
continue;
}
@@ -559,7 +571,7 @@
} // end forall windows
if (mWindowDetachedWallpaper != detachedWallpaper) {
- if (WindowManagerService.DEBUG_WALLPAPER) Slog.v(TAG,
+ if (DEBUG_WALLPAPER) Slog.v(TAG,
"Detached wallpaper changed from " + mWindowDetachedWallpaper
+ " to " + detachedWallpaper);
mWindowDetachedWallpaper = detachedWallpaper;
@@ -589,7 +601,7 @@
if (appAnimator.freezingScreen) {
appAnimator.showAllWindowsLocked();
mService.unsetAppFreezingScreenLocked(wtoken, false, true);
- if (WindowManagerService.DEBUG_ORIENTATION) Slog.i(TAG,
+ if (DEBUG_ORIENTATION) Slog.i(TAG,
"Setting mOrientationChangeComplete=true because wtoken "
+ wtoken + " numInteresting=" + wtoken.numInterestingWindows
+ " numDrawn=" + wtoken.numDrawnWindows);
@@ -626,11 +638,11 @@
boolean wasAnimating = mAnimating;
mAnimating = false;
mAppWindowAnimating = false;
- if (WindowManagerService.DEBUG_WINDOW_TRACE) {
+ if (DEBUG_WINDOW_TRACE) {
Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
}
- if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(
+ if (SHOW_TRANSACTIONS) Slog.i(
TAG, ">>> OPEN TRANSACTION animateLocked");
SurfaceControl.openTransaction();
SurfaceControl.setAnimationTransaction();
@@ -705,7 +717,7 @@
Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
} finally {
SurfaceControl.closeTransaction();
- if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(
+ if (SHOW_TRANSACTIONS) Slog.i(
TAG, "<<< CLOSE TRANSACTION animateLocked");
}
@@ -731,20 +743,51 @@
mWindowPlacerLocked.requestTraversal();
}
+ if (mAnimating && !wasAnimating && Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
+ }
+
if (!mAnimating && wasAnimating) {
mWindowPlacerLocked.requestTraversal();
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
+ }
+ }
+
+ if (mRemoveReplacedWindows) {
+ removeReplacedWindowsLocked();
}
mService.destroyPreservedSurfaceLocked();
- if (WindowManagerService.DEBUG_WINDOW_TRACE) {
+ if (DEBUG_WINDOW_TRACE) {
Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating
- + " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
- + " mPendingLayoutChanges(DEFAULT_DISPLAY)="
- + Integer.toHexString(getPendingLayoutChanges(Display.DEFAULT_DISPLAY)));
+ + " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
+ + " mPendingLayoutChanges(DEFAULT_DISPLAY)="
+ + Integer.toHexString(getPendingLayoutChanges(Display.DEFAULT_DISPLAY)));
}
}
+ private void removeReplacedWindowsLocked() {
+ if (SHOW_TRANSACTIONS) Slog.i(
+ TAG, ">>> OPEN TRANSACTION removeReplacedWindows");
+ SurfaceControl.openTransaction();
+ try {
+ for (int i = mService.mDisplayContents.size() - 1; i >= 0; i--) {
+ DisplayContent display = mService.mDisplayContents.get(i);
+ final WindowList windows = mService.getWindowListLocked(display.getDisplayId());
+ for (int j = windows.size() - 1; j >= 0; j--) {
+ windows.get(j).maybeRemoveReplacedWindow();
+ }
+ }
+ } finally {
+ SurfaceControl.closeTransaction();
+ if (SHOW_TRANSACTIONS) Slog.i(
+ TAG, "<<< CLOSE TRANSACTION removeReplacedWindows");
+ }
+ mRemoveReplacedWindows = false;
+ }
+
private static String bulkUpdateParamsToString(int bulkUpdateParams) {
StringBuilder builder = new StringBuilder(128);
if ((bulkUpdateParams & WindowSurfacePlacer.SET_UPDATE_ROTATION) != 0) {
@@ -835,7 +878,7 @@
for (int i = windows.size() - 1; i >= 0; i--) {
if (displayId == windows.get(i).getDisplayId()) {
setPendingLayoutChanges(displayId, changes);
- if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
+ if (DEBUG_LAYOUT_REPEATS) {
mWindowPlacerLocked.debugLayoutRepeats(reason,
getPendingLayoutChanges(displayId));
}
@@ -866,6 +909,14 @@
return getDisplayContentsAnimatorLocked(displayId).mScreenRotationAnimation;
}
+ void requestRemovalOfReplacedWindows(WindowState win) {
+ final AppWindowToken token = win.mAppToken;
+ if (token != null && token.mWillReplaceWindow && token.mReplacingWindow == win) {
+ token.mHasReplacedWindow = true;
+ }
+ mRemoveReplacedWindows = true;
+ }
+
private class DisplayContentsAnimator {
ScreenRotationAnimation mScreenRotationAnimation = null;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index f571d9c..f17698c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -17,10 +17,12 @@
package com.android.server.wm;
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
-import static android.app.ActivityManager.DOCKED_STACK_ID;
-import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.StatusBarManager.DISABLE_MASK;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
+import static android.view.WindowManager.DOCKED_INVALID;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
@@ -31,8 +33,6 @@
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
@@ -46,6 +46,8 @@
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.view.WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY;
+import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.WindowState.BOUNDS_FOR_TOUCH;
@@ -55,7 +57,6 @@
import android.app.ActivityManagerNative;
import android.app.AppOpsManager;
import android.app.IActivityManager;
-import android.app.StatusBarManager;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -116,6 +117,7 @@
import android.view.DropPermissionHolder;
import android.view.Gravity;
import android.view.IApplicationToken;
+import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.IInputFilter;
import android.view.IOnKeyguardExitResult;
import android.view.IRotationWatcher;
@@ -223,6 +225,7 @@
static final boolean SHOW_SURFACE_ALLOC = false;
static final boolean SHOW_TRANSACTIONS = false;
static final boolean SHOW_LIGHT_TRANSACTIONS = false || SHOW_TRANSACTIONS;
+ static final boolean SHOW_VERBOSE_TRANSACTIONS = false && SHOW_TRANSACTIONS;
static final boolean HIDE_STACK_CRAWLS = true;
static final int LAYOUT_REPEAT_THRESHOLD = 4;
@@ -476,6 +479,8 @@
static int sDockedStackCreateMode = DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+ private final SparseIntArray mTmpTaskIds = new SparseIntArray();
+
int getDragLayerLocked() {
return mPolicy.windowTypeToLayerLw(LayoutParams.TYPE_DRAG) * TYPE_LAYER_MULTIPLIER
+ TYPE_LAYER_OFFSET;
@@ -698,7 +703,7 @@
if (DEBUG_DRAG) Slog.d(TAG, "Button no longer pressed; dropping at "
+ newX + "," + newY);
synchronized (mWindowMap) {
- endDrag = completeDrop(newX, newY);
+ endDrag = completeDropLw(newX, newY);
}
} else {
synchronized (mWindowMap) {
@@ -712,7 +717,7 @@
if (DEBUG_DRAG) Slog.d(TAG, "Got UP on move channel; dropping at "
+ newX + "," + newY);
synchronized (mWindowMap) {
- endDrag = completeDrop(newX, newY);
+ endDrag = completeDropLw(newX, newY);
}
} break;
@@ -742,11 +747,15 @@
}
}
- private boolean completeDrop(float x, float y) {
+ private boolean completeDropLw(float x, float y) {
WindowState dropTargetWin = mDragState.getDropTargetWinLw(x, y);
+ mDragState.mCurrentX = x;
+ mDragState.mCurrentY = y;
+
DropPermissionHolder dropPermissionHolder = null;
- if ((mDragState.mFlags & View.DRAG_FLAG_GLOBAL) != 0 &&
+ if (dropTargetWin != null &&
+ (mDragState.mFlags & View.DRAG_FLAG_GLOBAL) != 0 &&
(mDragState.mFlags & DRAG_FLAGS_URI_ACCESS) != 0) {
dropPermissionHolder = new DropPermissionHolder(
mDragState.mData,
@@ -901,7 +910,7 @@
PowerManager.PARTIAL_WAKE_LOCK, "SCREEN_FROZEN");
mScreenFrozenLock.setReferenceCounted(false);
- mAppTransition = new AppTransition(context, mH);
+ mAppTransition = new AppTransition(context, mH, mWindowMap, mWindowPlacerLocked);
mAppTransition.registerListenerLocked(mActivityManagerAppTransitionNotifier);
mActivityManager = ActivityManagerNative.getDefault();
@@ -1233,7 +1242,16 @@
final int myLayer = win.mBaseLayer;
int i;
for (i = windows.size() - 1; i >= 0; i--) {
- if (windows.get(i).mBaseLayer <= myLayer) {
+ final WindowState otherWin = windows.get(i);
+ if (otherWin.getBaseType() != TYPE_WALLPAPER && otherWin.mBaseLayer <= myLayer) {
+ // Wallpaper wanders through the window list, for example to position itself
+ // directly behind keyguard. Because of this it will break the ordering based on
+ // WindowState.mBaseLayer. There might windows with higher mBaseLayer behind it and
+ // we don't want the new window to appear above them. An example of this is adding
+ // of the docked stack divider. Consider a scenario with the following ordering (top
+ // to bottom): keyguard, wallpaper, assist preview, apps. We want the dock divider
+ // to land below the assist preview, so the dock divider must ignore the wallpaper,
+ // with which it shares the base layer.
break;
}
}
@@ -1327,8 +1345,14 @@
addAttachedWindowToListLocked(win, addToToken);
}
- if (win.mAppToken != null && addToToken) {
- win.mAppToken.allAppWindows.add(win);
+ final AppWindowToken appToken = win.mAppToken;
+ if (appToken != null) {
+ if (addToToken) {
+ appToken.allAppWindows.add(win);
+ }
+ if (appToken.mWillReplaceWindow) {
+ appToken.mReplacingWindow = win;
+ }
}
}
@@ -1344,7 +1368,7 @@
if (DEBUG_INPUT_METHOD) {
Slog.i(TAG, "isVisibleOrAdding " + w + ": " + w.isVisibleOrAdding());
if (!w.isVisibleOrAdding()) {
- Slog.i(TAG, " mSurface=" + w.mWinAnimator.mSurfaceControl
+ Slog.i(TAG, " mSurfaceController=" + w.mWinAnimator.mSurfaceController
+ " relayoutCalled=" + w.mRelayoutCalled
+ " viewVis=" + w.mViewVisibility
+ " policyVis=" + w.mPolicyVisibility
@@ -1510,7 +1534,7 @@
// TODO(multidisplay): IMEs are only supported on the default display.
getDefaultWindowListLocked().add(pos, win);
mWindowsChanged = true;
- moveInputMethodDialogsLocked(pos+1);
+ moveInputMethodDialogsLocked(pos + 1);
return;
}
win.mTargetAppToken = null;
@@ -1889,11 +1913,6 @@
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
- } else if (type == TYPE_DOCK_DIVIDER) {
- if (displayContent.mDividerControllerLocked.hasDivider()) {
- Slog.w(TAG, "Attempted to add docked stack divider twice. Aborting.");
- return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
- }
} else if (token.appWindowToken != null) {
Slog.w(TAG, "Non-null appWindowToken for system window of type=" + type);
// It is not valid to use an app token with other system types; we will
@@ -1918,6 +1937,11 @@
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
+ if (atoken != null && atoken.appDied) {
+ Slog.d(TAG, "App is now revived: " + atoken);
+ atoken.appDied = false;
+ }
+
mPolicy.adjustWindowParamsLw(win.mAttrs);
win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
@@ -1929,11 +1953,7 @@
final boolean openInputChannels = (outInputChannel != null
&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
if (openInputChannels) {
- String name = win.makeInputChannelName();
- InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
- win.setInputChannel(inputChannels[0]);
- inputChannels[1].transferTo(outInputChannel);
- mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
+ win.openInputChannel(outInputChannel);
}
// From now on, no exceptions or errors allowed!
@@ -1991,6 +2011,10 @@
}
}
+ if (type == TYPE_DOCK_DIVIDER) {
+ getDefaultDisplayContentLocked().getDockedDividerController().setWindow(win);
+ }
+
final WindowStateAnimator winAnimator = win.mWinAnimator;
winAnimator.mEnterAnimationPending = true;
winAnimator.mEnteringAnimation = true;
@@ -2053,7 +2077,7 @@
}
private void prepareWindowReplacementTransition(AppWindowToken atoken) {
- if (atoken == null || !atoken.mReplacingWindow || !atoken.mAnimateReplacingWindow) {
+ if (atoken == null || !atoken.mWillReplaceWindow || !atoken.mAnimateReplacingWindow) {
return;
}
atoken.allDrawn = false;
@@ -2069,7 +2093,9 @@
// happen, let's just simply add a window.
return;
}
- Rect frame = replacedWindow.mFrame;
+ // 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.
+ Rect frame = replacedWindow.mVisibleFrame;
// We treat this as if this activity was opening, so we can trigger the app transition
// animation and piggy-back on existing transition animation infrastructure.
mOpeningApps.add(atoken);
@@ -2144,7 +2170,7 @@
if (localLOGV || DEBUG_FOCUS || DEBUG_FOCUS_LIGHT && win==mCurrentFocus) Slog.v(
TAG, "Remove " + win + " client="
+ Integer.toHexString(System.identityHashCode(win.mClient.asBinder()))
- + ", surface=" + win.mWinAnimator.mSurfaceControl + " Callers="
+ + ", surfaceController=" + win.mWinAnimator.mSurfaceController + " Callers="
+ Debug.getCallers(4));
final long origId = Binder.clearCallingIdentity();
@@ -2152,11 +2178,13 @@
win.disposeInputChannel();
if (DEBUG_APP_TRANSITIONS) Slog.v(
- TAG, "Remove " + win + ": mSurface=" + win.mWinAnimator.mSurfaceControl
+ TAG, "Remove " + win + ": mSurfaceController=" + win.mWinAnimator.mSurfaceController
+ " mExiting=" + win.mExiting
+ " isAnimating=" + win.mWinAnimator.isAnimating()
+ " app-animation="
+ (win.mAppToken != null ? win.mAppToken.mAppAnimator.animation : null)
+ + " mWillReplaceWindow="
+ + (win.mAppToken != null ? win.mAppToken.mWillReplaceWindow : false)
+ " inPendingTransaction="
+ (win.mAppToken != null ? win.mAppToken.inPendingTransaction : false)
+ " mDisplayFrozen=" + mDisplayFrozen);
@@ -2168,20 +2196,38 @@
// animation wouldn't be seen.
if (win.mHasSurface && okToDisplay()) {
final AppWindowToken appToken = win.mAppToken;
- if (appToken != null && appToken.mReplacingWindow) {
- // This window is going to be replaced. We need to kepp it around until the new one
+ if (appToken != null && appToken.mWillReplaceWindow) {
+ // This window is going to be replaced. We need to keep it around until the new one
// gets added, then we will get rid of this one.
if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Preserving " + win + " until the new one is "
+ "added");
win.mExiting = true;
appToken.mReplacingRemoveRequested = true;
+ Binder.restoreCallingIdentity(origId);
return;
}
// If we are not currently running the exit animation, we
// need to see about starting one.
wasVisible = win.isWinVisibleLw();
- if (wasVisible) {
+ if (wasVisible && appToken != null && appToken.appDied) {
+ if (DEBUG_ADD_REMOVE) Slog.v(TAG,
+ "Not removing " + win + " because app died while it's visible");
+
+ win.mAppDied = true;
+ win.setDisplayLayoutNeeded();
+ mWindowPlacerLocked.performSurfacePlacement();
+
+ // Set up a replacement input channel since the app is now dead.
+ // We need to catch tapping on the dead window to restart the app.
+ win.openInputChannel(null);
+ mInputMonitor.updateInputWindowsLw(true /*force*/);
+
+ Binder.restoreCallingIdentity(origId);
+ return;
+ }
+
+ if (wasVisible) {
final int transit = (!startingWindow)
? WindowManagerPolicy.TRANSIT_EXIT
: WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
@@ -2196,7 +2242,8 @@
mAccessibilityController.onWindowTransitionLocked(win, transit);
}
}
- final boolean isAnimating = win.mWinAnimator.isAnimating();
+ final boolean isAnimating = win.mWinAnimator.isAnimating()
+ && !win.mWinAnimator.isDummyAnimation();
// The starting window is the last window in this app token and it isn't animating.
// Allow it to be removed now as there is no additional window or animation that will
// trigger its removal.
@@ -2232,10 +2279,6 @@
}
void removeWindowInnerLocked(WindowState win) {
- removeWindowInnerLocked(win, true);
- }
-
- private void removeWindowInnerLocked(WindowState win, boolean performLayout) {
if (win.mRemoved) {
// Nothing to do.
return;
@@ -2330,9 +2373,7 @@
if (!mWindowPlacerLocked.isInLayout()) {
assignLayersLocked(windows);
win.setDisplayLayoutNeeded();
- if (performLayout) {
- mWindowPlacerLocked.performSurfacePlacement();
- }
+ mWindowPlacerLocked.performSurfacePlacement();
if (win.mAppToken != null) {
win.mAppToken.updateReportedVisibilityLocked();
}
@@ -2384,6 +2425,9 @@
try {
synchronized (mWindowMap) {
WindowState w = windowForClientLocked(session, client, false);
+ if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
+ "transparentRegionHint=" + region, null);
+
if ((w != null) && w.mHasSurface) {
w.mWinAnimator.setTransparentRegionHintLocked(region);
}
@@ -2460,17 +2504,65 @@
}
}
+ void repositionChild(Session session, IWindow client,
+ int top, int left, int right, int bottom,
+ long deferTransactionUntilFrame, Rect outFrame) {
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "repositionChild");
+ long origId = Binder.clearCallingIdentity();
+
+ try {
+ synchronized(mWindowMap) {
+ WindowState win = windowForClientLocked(session, client, false);
+ if (win == null) {
+ return;
+ }
+ if (win.mAttachedWindow == null) {
+ throw new IllegalArgumentException(
+ "repositionChild called but window is not"
+ + "attached to a parent win=" + win);
+ }
+
+ win.mFrame.left = left;
+ win.mFrame.top = top;
+ win.mFrame.right = right;
+ win.mFrame.bottom = bottom;
+
+ win.mWinAnimator.computeShownFrameLocked();
+
+ if (SHOW_TRANSACTIONS) {
+ Slog.i(TAG, ">>> OPEN TRANSACTION repositionChild");
+ }
+
+ SurfaceControl.openTransaction();
+
+ if (deferTransactionUntilFrame > 0) {
+ win.mWinAnimator.mSurfaceController.deferTransactionUntil(
+ win.mAttachedWindow.mWinAnimator.mSurfaceController.getHandle(),
+ deferTransactionUntilFrame);
+ }
+ win.mWinAnimator.setSurfaceBoundariesLocked(false);
+
+ SurfaceControl.closeTransaction();
+ if (SHOW_TRANSACTIONS) {
+ Slog.i(TAG, "<<< CLOSE TRANSACTION repositionChild");
+ }
+
+ outFrame = win.mCompatFrame;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+ }
+ }
+
public int relayoutWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int requestedWidth,
int requestedHeight, int viewVisibility, int flags,
Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Configuration outConfig,
Surface outSurface) {
- boolean toBeDisplayed = false;
- boolean inTouchMode;
+ int result = 0;
boolean configChanged;
- boolean surfaceChanged = false;
- boolean dragResizing = false;
boolean hasStatusBarPermission =
mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR)
== PackageManager.PERMISSION_GRANTED;
@@ -2483,42 +2575,38 @@
return 0;
}
WindowStateAnimator winAnimator = win.mWinAnimator;
- if (viewVisibility != View.GONE && (win.mRequestedWidth != requestedWidth
- || win.mRequestedHeight != requestedHeight)) {
- win.mLayoutNeeded = true;
- win.mRequestedWidth = requestedWidth;
- win.mRequestedHeight = requestedHeight;
+ if (viewVisibility != View.GONE) {
+ win.setRequestedSize(requestedWidth, requestedHeight);
}
- if (attrs != null) {
- mPolicy.adjustWindowParamsLw(attrs);
- }
-
- // if they don't have the permission, mask out the status bar bits
- int systemUiVisibility = 0;
- if (attrs != null) {
- systemUiVisibility = (attrs.systemUiVisibility|attrs.subtreeSystemUiVisibility);
- if ((systemUiVisibility & StatusBarManager.DISABLE_MASK) != 0) {
- if (!hasStatusBarPermission) {
- systemUiVisibility &= ~StatusBarManager.DISABLE_MASK;
- }
- }
- }
-
- if (attrs != null && seq == win.mSeq) {
- win.mSystemUiVisibility = systemUiVisibility;
- }
-
- winAnimator.mSurfaceDestroyDeferred =
- (flags&WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY) != 0;
-
int attrChanges = 0;
int flagChanges = 0;
if (attrs != null) {
+ mPolicy.adjustWindowParamsLw(attrs);
+ // if they don't have the permission, mask out the status bar bits
+ if (seq == win.mSeq) {
+ int systemUiVisibility = attrs.systemUiVisibility
+ | attrs.subtreeSystemUiVisibility;
+ if ((systemUiVisibility & DISABLE_MASK) != 0) {
+ if (!hasStatusBarPermission) {
+ systemUiVisibility &= ~DISABLE_MASK;
+ }
+ }
+ win.mSystemUiVisibility = systemUiVisibility;
+ }
if (win.mAttrs.type != attrs.type) {
throw new IllegalArgumentException(
"Window type can not be changed after the window is added.");
}
+
+ // Odd choice but less odd than embedding in copyFrom()
+ if ((attrs.flags & WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY) != 0) {
+ attrs.x = win.mAttrs.x;
+ attrs.y = win.mAttrs.y;
+ attrs.width = win.mAttrs.width;
+ attrs.height = win.mAttrs.height;
+ }
+
flagChanges = win.mAttrs.flags ^= attrs.flags;
attrChanges = win.mAttrs.copyFrom(attrs);
if ((attrChanges & (WindowManager.LayoutParams.LAYOUT_CHANGED
@@ -2529,30 +2617,15 @@
if (DEBUG_LAYOUT) Slog.v(TAG, "Relayout " + win + ": viewVisibility=" + viewVisibility
+ " req=" + requestedWidth + "x" + requestedHeight + " " + win.mAttrs);
-
+ winAnimator.mSurfaceDestroyDeferred = (flags & RELAYOUT_DEFER_SURFACE_DESTROY) != 0;
win.mEnforceSizeCompat =
(win.mAttrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0;
-
if ((attrChanges & WindowManager.LayoutParams.ALPHA_CHANGED) != 0) {
winAnimator.mAlpha = attrs.alpha;
}
-
- final boolean scaledWindow =
- ((win.mAttrs.flags & WindowManager.LayoutParams.FLAG_SCALED) != 0);
-
- if (scaledWindow) {
- // requested{Width|Height} Surface's physical size
- // attrs.{width|height} Size on screen
- win.mHScale = (attrs.width != requestedWidth) ?
- (attrs.width / (float)requestedWidth) : 1.0f;
- win.mVScale = (attrs.height != requestedHeight) ?
- (attrs.height / (float)requestedHeight) : 1.0f;
- } else {
- win.mHScale = win.mVScale = 1;
- }
+ win.setWindowScale(requestedWidth, requestedHeight);
boolean imMayMove = (flagChanges & (FLAG_ALT_FOCUSABLE_IM | FLAG_NOT_FOCUSABLE)) != 0;
-
final boolean isDefaultDisplay = win.isDefaultDisplay();
boolean focusMayChange = isDefaultDisplay && (win.mViewVisibility != viewVisibility
|| ((flagChanges & FLAG_NOT_FOCUSABLE) != 0)
@@ -2561,8 +2634,8 @@
boolean wallpaperMayMove = win.mViewVisibility != viewVisibility
&& (win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0;
wallpaperMayMove |= (flagChanges & FLAG_SHOW_WALLPAPER) != 0;
- if ((flagChanges & FLAG_SECURE) != 0 && winAnimator.mSurfaceControl != null) {
- winAnimator.mSurfaceControl.setSecure(isSecureLocked(win));
+ if ((flagChanges & FLAG_SECURE) != 0 && winAnimator.mSurfaceController != null) {
+ winAnimator.mSurfaceController.setSecure(isSecureLocked(win));
}
win.mRelayoutCalled = true;
@@ -2576,73 +2649,10 @@
}
if (viewVisibility == View.VISIBLE &&
(win.mAppToken == null || !win.mAppToken.clientHidden)) {
- toBeDisplayed = !win.isVisibleLw();
- if (win.mExiting) {
- winAnimator.cancelExitAnimationForNextAnimationLocked();
- win.mExiting = false;
- }
- if (win.mDestroying) {
- win.mDestroying = false;
- mDestroySurface.remove(win);
- }
- if (oldVisibility == View.GONE) {
- winAnimator.mEnterAnimationPending = true;
- }
- winAnimator.mEnteringAnimation = true;
- if (toBeDisplayed) {
- if ((win.mAttrs.softInputMode & SOFT_INPUT_MASK_ADJUST)
- == SOFT_INPUT_ADJUST_RESIZE) {
- win.mLayoutNeeded = true;
- }
- if (win.isDrawnLw() && okToDisplay()) {
- winAnimator.applyEnterAnimationLocked();
- }
- if ((win.mAttrs.flags
- & WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON) != 0) {
- if (DEBUG_VISIBILITY) Slog.v(TAG,
- "Relayout window turning screen on: " + win);
- win.mTurnOnScreen = true;
- }
- if (win.isConfigChanged()) {
- if (DEBUG_CONFIGURATION) Slog.i(TAG, "Window " + win
- + " visible with new config: " + mCurConfiguration);
- outConfig.setTo(mCurConfiguration);
- }
- }
- if ((attrChanges&WindowManager.LayoutParams.FORMAT_CHANGED) != 0) {
- // If the format can be changed in place yaay!
- // If not, fall back to a surface re-build
- if (!winAnimator.tryChangeFormatInPlaceLocked()) {
- winAnimator.destroySurfaceLocked();
- toBeDisplayed = true;
- surfaceChanged = true;
- }
- }
-
- // 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()) {
- win.setDragResizing();
- if (win.mHasSurface) {
- winAnimator.preserveSurfaceLocked();
- toBeDisplayed = true;
- }
- }
- dragResizing = win.isDragResizing();
+ result = relayoutVisibleWindow(outConfig, result, win, winAnimator, attrChanges,
+ oldVisibility);
try {
- if (!win.mHasSurface) {
- surfaceChanged = true;
- }
- SurfaceControl surfaceControl = winAnimator.createSurfaceLocked();
- if (surfaceControl != null) {
- outSurface.copyFrom(surfaceControl);
- if (SHOW_TRANSACTIONS) Slog.i(TAG,
- " OUT SURFACE " + outSurface + ": copied");
- } else {
- // For some reason there isn't a surface. Clear the
- // caller's object so they see the same state.
- outSurface.release();
- }
+ result = createSurfaceControl(outSurface, result, win, winAnimator);
} catch (Exception e) {
mInputMonitor.updateInputWindowsLw(true /*force*/);
@@ -2652,68 +2662,36 @@
Binder.restoreCallingIdentity(origId);
return 0;
}
- if (toBeDisplayed) {
+ if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
focusMayChange = isDefaultDisplay;
}
- if (win.mAttrs.type == TYPE_INPUT_METHOD
- && mInputMethodWindow == null) {
+ if (win.mAttrs.type == TYPE_INPUT_METHOD && mInputMethodWindow == null) {
mInputMethodWindow = win;
imMayMove = true;
}
- if (win.mAttrs.type == TYPE_BASE_APPLICATION
- && win.mAppToken != null
- && win.mAppToken.startingWindow != null) {
- // Special handling of starting window over the base
- // window of the app: propagate lock screen flags to it,
- // to provide the correct semantics while starting.
- final int mask =
- WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
- | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
- | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
- WindowManager.LayoutParams sa = win.mAppToken.startingWindow.mAttrs;
- sa.flags = (sa.flags&~mask) | (win.mAttrs.flags&mask);
- }
+ win.adjustStartingWindowFlags();
} else {
winAnimator.mEnterAnimationPending = false;
winAnimator.mEnteringAnimation = false;
- if (winAnimator.mSurfaceControl != null) {
+ if (winAnimator.mSurfaceController != null &&
+ winAnimator.mSurfaceController.hasSurface()) {
if (DEBUG_VISIBILITY) Slog.i(TAG, "Relayout invis " + win
+ ": mExiting=" + win.mExiting);
+ // If we are using a saved surface to do enter animation, just let the
+ // animation run and don't destroy the surface. This could happen when
+ // the app sets visibility to invisible for the first time after resume,
+ // or when the user exits immediately after a resume. In both cases, we
+ // don't want to destroy the saved surface.
// If we are not currently running the exit animation, we
// need to see about starting one.
- if (!win.mExiting) {
- surfaceChanged = true;
- // Try starting an animation; if there isn't one, we
- // can destroy the surface right away.
- int transit = WindowManagerPolicy.TRANSIT_EXIT;
- if (win.mAttrs.type == TYPE_APPLICATION_STARTING) {
- transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
- }
- if (win.isWinVisibleLw() &&
- winAnimator.applyAnimationLocked(transit, false)) {
- focusMayChange = isDefaultDisplay;
- win.mExiting = true;
- } else if (win.mWinAnimator.isAnimating()) {
- // Currently in a hide animation... turn this into
- // an exit.
- win.mExiting = true;
- } else if (mWallpaperControllerLocked.isWallpaperTarget(win)) {
- // If the wallpaper is currently behind this
- // window, we need to change both of them inside
- // of a transaction to avoid artifacts.
- win.mExiting = true;
- win.mWinAnimator.mAnimating = true;
- } else {
- if (mInputMethodWindow == win) {
- mInputMethodWindow = null;
- }
- winAnimator.destroySurfaceLocked();
- }
- //TODO (multidisplay): Magnification is supported only for the default
- if (mAccessibilityController != null
- && win.getDisplayId() == Display.DEFAULT_DISPLAY) {
- mAccessibilityController.onWindowTransitionLocked(win, transit);
- }
+ final boolean notExitingOrAnimating =
+ !win.mExiting && !win.isAnimatingWithSavedSurface();
+ result |= notExitingOrAnimating
+ ? RELAYOUT_RES_SURFACE_CHANGED : 0;
+ if (notExitingOrAnimating) {
+ focusMayChange = tryStartingAnimation(win, winAnimator, isDefaultDisplay,
+ focusMayChange);
+
}
}
@@ -2732,6 +2710,7 @@
// updateFocusedWindowLocked() already assigned layers so we only need to
// reassign them at this point if the IM window state gets shuffled
+ boolean toBeDisplayed = (result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0;
if (imMayMove && (moveInputMethodWindowsIfNeededLocked(false) || toBeDisplayed)) {
// Little hack here -- we -should- be able to rely on the
// function to return true if the IME has moved and needs
@@ -2775,7 +2754,7 @@
if (localLOGV || DEBUG_FOCUS) Slog.v(
TAG, "Relayout of " + win + ": focusMayChange=" + focusMayChange);
- inTouchMode = mInTouchMode;
+ result |= mInTouchMode ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0;
mInputMonitor.updateInputWindowsLw(true /*force*/);
@@ -2787,13 +2766,108 @@
if (configChanged) {
sendNewConfiguration();
}
-
Binder.restoreCallingIdentity(origId);
+ return result;
+ }
- return (inTouchMode ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0)
- | (toBeDisplayed ? WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME : 0)
- | (surfaceChanged ? WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED : 0)
- | (dragResizing ? WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING : 0);
+ private boolean tryStartingAnimation(WindowState win, WindowStateAnimator winAnimator,
+ boolean isDefaultDisplay, boolean focusMayChange) {
+ // Try starting an animation; if there isn't one, we
+ // can destroy the surface right away.
+ int transit = WindowManagerPolicy.TRANSIT_EXIT;
+ if (win.mAttrs.type == TYPE_APPLICATION_STARTING) {
+ transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
+ }
+ if (win.isWinVisibleLw() && winAnimator.applyAnimationLocked(transit, false)) {
+ focusMayChange = isDefaultDisplay;
+ win.mExiting = true;
+ } else if (win.mWinAnimator.isAnimating()) {
+ // Currently in a hide animation... turn this into
+ // an exit.
+ win.mExiting = true;
+ } else if (mWallpaperControllerLocked.isWallpaperTarget(win)) {
+ // If the wallpaper is currently behind this
+ // window, we need to change both of them inside
+ // of a transaction to avoid artifacts.
+ win.mExiting = true;
+ win.mWinAnimator.mAnimating = true;
+ } else {
+ if (mInputMethodWindow == win) {
+ mInputMethodWindow = null;
+ }
+ if (!win.shouldSaveSurface()) {
+ winAnimator.destroySurfaceLocked();
+ }
+ }
+ //TODO (multidisplay): Magnification is supported only for the default
+ if (mAccessibilityController != null
+ && win.getDisplayId() == Display.DEFAULT_DISPLAY) {
+ mAccessibilityController.onWindowTransitionLocked(win, transit);
+ }
+ return focusMayChange;
+ }
+
+ private int createSurfaceControl(Surface outSurface, int result, WindowState win,
+ WindowStateAnimator winAnimator) {
+ if (!win.mHasSurface) {
+ result |= RELAYOUT_RES_SURFACE_CHANGED;
+ }
+ WindowSurfaceController surfaceController = winAnimator.createSurfaceLocked();
+ if (surfaceController != null) {
+ surfaceController.getSurface(outSurface);
+ if (SHOW_TRANSACTIONS) Slog.i(TAG, " OUT SURFACE " + outSurface + ": copied");
+ } else {
+ // For some reason there isn't a surface. Clear the
+ // caller's object so they see the same state.
+ outSurface.release();
+ }
+ return result;
+ }
+
+ private int relayoutVisibleWindow(Configuration outConfig, int result, WindowState win,
+ WindowStateAnimator winAnimator, int attrChanges, int oldVisibility) {
+ result |= !win.isVisibleLw() ? WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME : 0;
+ if (win.mExiting) {
+ winAnimator.cancelExitAnimationForNextAnimationLocked();
+ win.mExiting = false;
+ }
+ if (win.mDestroying) {
+ win.mDestroying = false;
+ mDestroySurface.remove(win);
+ }
+ if (oldVisibility == View.GONE) {
+ winAnimator.mEnterAnimationPending = true;
+ }
+ winAnimator.mEnteringAnimation = true;
+ if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
+ win.prepareWindowToDisplayDuringRelayout(outConfig);
+ }
+ if ((attrChanges& LayoutParams.FORMAT_CHANGED) != 0) {
+ // If the format can be changed in place yaay!
+ // If not, fall back to a surface re-build
+ if (!winAnimator.tryChangeFormatInPlaceLocked()) {
+ winAnimator.destroySurfaceLocked();
+ result |= RELAYOUT_RES_SURFACE_CHANGED
+ | WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME;
+ }
+ }
+
+ // 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()) {
+ win.setDragResizing();
+ if (win.mHasSurface) {
+ winAnimator.preserveSurfaceLocked();
+ result |= WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME;
+ }
+ }
+ result |= win.isDragResizing() ? WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING : 0;
+ if (win.isAnimatingWithSavedSurface()) {
+ // If we're animating with a saved surface now, request client to report draw.
+ // We still need to know when the real thing is drawn.
+ result |= WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME;
+ }
+ return result;
}
public void performDeferredDestroyWindow(Session session, IWindow client) {
@@ -2833,7 +2907,8 @@
try {
synchronized (mWindowMap) {
WindowState win = windowForClientLocked(session, client, false);
- if (DEBUG_ADD_REMOVE) Slog.d(TAG, "finishDrawingWindow: " + win);
+ if (DEBUG_ADD_REMOVE) Slog.d(TAG, "finishDrawingWindow: " + win + " mDrawState="
+ + (win != null ? win.mWinAnimator.drawStateToString() : "null"));
if (win != null && win.mWinAnimator.finishDrawingLocked()) {
if ((win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) {
getDefaultDisplayContentLocked().pendingLayoutChanges |=
@@ -2862,44 +2937,44 @@
"applyAnimation: atoken=" + atoken);
// Determine the visible rect to calculate the thumbnail clip
- WindowState win = atoken.findMainWindow();
- Rect containingFrame = new Rect(0, 0, width, height);
- Rect contentInsets = new Rect();
- Rect appFrame = new Rect(0, 0, width, height);
+ final WindowState win = atoken.findMainWindow();
+ final Rect frame = new Rect(0, 0, width, height);
+ final Rect insets = new Rect();
Rect surfaceInsets = null;
final boolean fullscreen = win != null && win.isFullscreen(width, height);
final boolean freeform = win != null && win.inFreeformWorkspace();
+ final boolean docked = win != null && win.inDockedWorkspace();
if (win != null) {
// Containing frame will usually cover the whole screen, including dialog windows.
// For freeform workspace windows it will not cover the whole screen and it also
// won't exactly match the final freeform window frame (e.g. when overlapping with
// the status bar). In that case we need to use the final frame.
if (freeform) {
- containingFrame.set(win.mFrame);
+ frame.set(win.mFrame);
} else {
- containingFrame.set(win.mContainingFrame);
+ frame.set(win.mContainingFrame);
}
surfaceInsets = win.getAttrs().surfaceInsets;
- if (fullscreen) {
+ if (fullscreen || docked) {
// For fullscreen windows use the window frames and insets to set the thumbnail
- // clip. For none-fullscreen windows we use the app display region so the clip
+ // clip. For non-fullscreen windows we use the app display region so the clip
// isn't affected by the window insets.
- contentInsets.set(win.mContentInsets);
- appFrame.set(win.mFrame);
+ insets.set(win.mContentInsets);
}
}
- final int containingWidth = containingFrame.width();
- final int containingHeight = containingFrame.height();
if (atoken.mLaunchTaskBehind) {
// Differentiate the two animations. This one which is briefly on the screen
// gets the !enter animation, and the other activity which remains on the
// screen gets the enter animation. Both appear in the mOpeningApps set.
enter = false;
}
- Animation a = mAppTransition.loadAnimation(lp, transit, enter, containingWidth,
- containingHeight, mCurConfiguration.orientation, containingFrame, contentInsets,
- surfaceInsets, appFrame, isVoiceInteraction, freeform, atoken.mTask.mTaskId);
+ if (DEBUG_APP_TRANSITIONS) Slog.d(TAG, "Loading animation for app transition."
+ + " transit=" + AppTransition.appTransitionToString(transit) + " enter=" + enter
+ + " frame=" + frame + " insets=" + insets + " surfaceInsets=" + surfaceInsets);
+ Animation a = mAppTransition.loadAnimation(lp, transit, enter,
+ mCurConfiguration.orientation, frame, insets, surfaceInsets, isVoiceInteraction,
+ freeform, atoken.mTask.mTaskId);
if (a != null) {
if (DEBUG_ANIM) {
RuntimeException e = null;
@@ -2909,6 +2984,8 @@
}
Slog.v(TAG, "Loaded animation " + a + " for " + atoken, e);
}
+ final int containingWidth = frame.width();
+ final int containingHeight = frame.height();
atoken.mAppAnimator.setAnimation(a, containingWidth, containingHeight,
mAppTransition.canSkipFirstFrame());
}
@@ -3260,10 +3337,8 @@
}
}
- final TaskStack dockedStack = mStackIdToStack.get(DOCKED_STACK_ID);
- final TaskStack freeformStack = mStackIdToStack.get(FREEFORM_WORKSPACE_STACK_ID);
- if ((dockedStack != null && dockedStack.isVisibleLocked())
- || (freeformStack != null && freeformStack.isVisibleLocked())) {
+ if (isStackVisibleLocked(DOCKED_STACK_ID)
+ || 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.
return SCREEN_ORIENTATION_UNSPECIFIED;
@@ -3456,13 +3531,6 @@
mLastFinishedFreezeSource = "new-config";
}
mWindowPlacerLocked.performSurfacePlacement();
- if (orientationChanged) {
- for (int i = mDisplayContents.size() - 1; i >= 0; i--) {
- DisplayContent content = mDisplayContents.valueAt(i);
- Message.obtain(mH, H.UPDATE_DOCKED_STACK_DIVIDER, H.DOCK_DIVIDER_FORCE_UPDATE,
- H.UNUSED, content).sendToTarget();
- }
- }
}
}
@@ -3609,16 +3677,36 @@
int startY, int targetWidth, int targetHeight, IRemoteCallback startedCallback,
boolean scaleUp) {
synchronized(mWindowMap) {
- mAppTransition.overridePendingAppTransitionAspectScaledThumb(srcThumb, startX,
- startY, targetWidth, targetHeight, startedCallback, scaleUp);
+ mAppTransition.overridePendingAppTransitionAspectScaledThumb(srcThumb, startX, startY,
+ targetWidth, targetHeight, startedCallback, scaleUp);
}
}
@Override
public void overridePendingAppTransitionMultiThumb(AppTransitionAnimationSpec[] specs,
- IRemoteCallback callback, boolean scaleUp) {
+ IRemoteCallback onAnimationStartedCallback, IRemoteCallback onAnimationFinishedCallback,
+ boolean scaleUp) {
synchronized (mWindowMap) {
- mAppTransition.overridePendingAppTransitionMultiThumb(specs, callback, scaleUp);
+ mAppTransition.overridePendingAppTransitionMultiThumb(specs, onAnimationStartedCallback,
+ onAnimationFinishedCallback, scaleUp);
+ if (!scaleUp) {
+ // This is used by freeform to recents windows transition. We need to synchronize
+ // the animation with the appearance of the content of recents, so we will make
+ // animation stay on the last frame a little longer.
+ mTmpTaskIds.clear();
+ for (int i = specs.length - 1; i >= 0; i--) {
+ mTmpTaskIds.put(specs[i].taskId, 0);
+ }
+ for (final WindowState win : mWindowMap.values()) {
+ final Task task = win.getTask();
+ if (task != null && mTmpTaskIds.get(task.mTaskId, -1) != -1) {
+ final AppWindowToken appToken = win.mAppToken;
+ if (appToken != null && appToken.mAppAnimator != null) {
+ appToken.mAppAnimator.startProlongAnimation();
+ }
+ }
+ }
+ }
}
}
@@ -3630,6 +3718,28 @@
}
@Override
+ public void overridePendingAppTransitionMultiThumbFuture(
+ IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback callback,
+ boolean scaleUp) {
+ synchronized(mWindowMap) {
+ mAppTransition.overridePendingAppTransitionMultiThumbFuture(specsFuture, callback,
+ scaleUp);
+ }
+ }
+
+ @Override
+ public void endProlongedAnimations() {
+ synchronized (mWindowMap) {
+ for (final WindowState win : mWindowMap.values()) {
+ final AppWindowToken appToken = win.mAppToken;
+ if (appToken != null && appToken.mAppAnimator != null) {
+ appToken.mAppAnimator.endProlongedAnimation();
+ }
+ }
+ }
+ }
+
+ @Override
public void executeAppTransition() {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"executeAppTransition()")) {
@@ -3907,7 +4017,7 @@
// transition animation
// * or this is an opening app and windows are being replaced.
if (wtoken.hidden == visible || (wtoken.hidden && wtoken.mIsExiting) ||
- (visible && wtoken.mReplacingWindow)) {
+ (visible && wtoken.mWillReplaceWindow)) {
boolean changed = false;
if (DEBUG_APP_TRANSITIONS) Slog.v(
TAG, "Changing app " + wtoken + " hidden=" + wtoken.hidden
@@ -4047,6 +4157,14 @@
wtoken.waitingToShow = false;
wtoken.hiddenRequested = !visible;
+ if (!visible && wtoken.appDied) {
+ // This app is dead while it was visible, we kept its dead window on screen.
+ // Now that the app is going invisible, we can remove it. It will be restarted
+ // if made visible again.
+ wtoken.appDied = false;
+ wtoken.removeAllWindows();
+ }
+
// If we are preparing an app transition, then delay changing
// the visibility of this token until we execute that transition.
if (okToDisplay() && mAppTransition.isTransitionSet()) {
@@ -4494,6 +4612,11 @@
}
}
+ boolean isStackVisibleLocked(int stackId) {
+ final TaskStack stack = mStackIdToStack.get(stackId);
+ return (stack != null && stack.isVisibleLocked());
+ }
+
public void setDockedStackCreateMode(int mode) {
synchronized (mWindowMap) {
sDockedStackCreateMode = mode;
@@ -4579,6 +4702,26 @@
}
}
+ @Override
+ public void cancelTaskWindowTransition(int taskId) {
+ synchronized (mWindowMap) {
+ Task task = mTaskIdToTask.get(taskId);
+ if (task != null) {
+ task.cancelTaskWindowTransition();
+ }
+ }
+ }
+
+ @Override
+ public void cancelTaskThumbnailTransition(int taskId) {
+ synchronized (mWindowMap) {
+ Task task = mTaskIdToTask.get(taskId);
+ if (task != null) {
+ task.cancelTaskThumbnailTransition();
+ }
+ }
+ }
+
public void addTask(int taskId, int stackId, boolean toTop) {
synchronized (mWindowMap) {
if (DEBUG_STACK) Slog.i(TAG, "addTask: adding taskId=" + taskId
@@ -4655,7 +4798,7 @@
throw new IllegalArgumentException("resizeStack: stackId " + stackId
+ " not found.");
}
- if (stack.setBounds(bounds, configs, taskBounds)) {
+ if (stack.setBounds(bounds, configs, taskBounds) && stack.isVisibleLocked()) {
stack.resizeWindows();
stack.getDisplayContent().layoutNeeded = true;
mWindowPlacerLocked.performSurfacePlacement();
@@ -4664,7 +4807,8 @@
}
}
- public void positionTaskInStack(int taskId, int stackId, int position) {
+ public void positionTaskInStack(int taskId, int stackId, int position, Rect bounds,
+ Configuration config) {
synchronized (mWindowMap) {
if (DEBUG_STACK) Slog.i(TAG, "positionTaskInStack: positioning taskId=" + taskId
+ " in stackId=" + stackId + " at " + position);
@@ -4680,7 +4824,7 @@
"positionTaskInStack: could not find stackId=" + stackId);
return;
}
- task.positionTaskInStack(stack, position);
+ task.positionTaskInStack(stack, position, bounds, config);
final DisplayContent displayContent = stack.getDisplayContent();
displayContent.layoutNeeded = true;
mWindowPlacerLocked.performSurfacePlacement();
@@ -4708,6 +4852,26 @@
}
}
+ /**
+ * Starts deferring layout passes. Useful when doing multiple changes but to optimize
+ * performance, only one layout pass should be done. This can be called multiple times, and
+ * layouting will be resumed once the last caller has called {@link #continueSurfaceLayout}
+ */
+ public void deferSurfaceLayout() {
+ synchronized (mWindowMap) {
+ mWindowPlacerLocked.deferLayout();
+ }
+ }
+
+ /**
+ * Resumes layout passes after deferring them. See {@link #deferSurfaceLayout()}
+ */
+ public void continueSurfaceLayout() {
+ synchronized (mWindowMap) {
+ mWindowPlacerLocked.continueLayout();
+ }
+ }
+
public void getTaskBounds(int taskId, Rect bounds) {
synchronized (mWindowMap) {
Task task = mTaskIdToTask.get(taskId);
@@ -5394,8 +5558,8 @@
if (visible) {
// TODO(multi-display): support multiple displays
if (mCircularDisplayMask == null) {
- int screenOffset = mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.circular_display_mask_offset);
+ int screenOffset = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_windowOutsetBottom);
int maskThickness = mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.circular_display_mask_thickness);
@@ -5477,7 +5641,7 @@
}
}
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
+ if (SHOW_VERBOSE_TRANSACTIONS) Slog.i(TAG,
">>> OPEN TRANSACTION showStrictModeViolation");
SurfaceControl.openTransaction();
try {
@@ -5489,7 +5653,7 @@
mStrictModeFlash.setVisibility(on);
} finally {
SurfaceControl.closeTransaction();
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
+ if (SHOW_VERBOSE_TRANSACTIONS) Slog.i(TAG,
"<<< CLOSE TRANSACTION showStrictModeViolation");
}
}
@@ -5669,11 +5833,12 @@
// Include this window.
final WindowStateAnimator winAnim = ws.mWinAnimator;
- if (maxLayer < winAnim.mSurfaceLayer) {
- maxLayer = winAnim.mSurfaceLayer;
+ int layer = winAnim.mSurfaceController.getLayer();
+ if (maxLayer < layer) {
+ maxLayer = layer;
}
- if (minLayer > winAnim.mSurfaceLayer) {
- minLayer = winAnim.mSurfaceLayer;
+ if (minLayer > layer) {
+ minLayer = layer;
}
// Don't include wallpaper in bounds calculation
@@ -5685,7 +5850,7 @@
int right = wf.right - cr.right;
int bottom = wf.bottom - cr.bottom;
frame.union(left, top, right, bottom);
- ws.getVisibleBounds(stackBounds, !BOUNDS_FOR_TOUCH);
+ ws.getVisibleBounds(stackBounds);
if (!frame.intersect(stackBounds)) {
// Set frame empty if there's no intersection.
frame.setEmpty();
@@ -5693,7 +5858,7 @@
}
if (ws.mAppToken != null && ws.mAppToken.token == appToken &&
- ws.isDisplayedLw() && winAnim.mSurfaceShown) {
+ ws.isDisplayedLw() && winAnim.getShown()) {
screenshotReady = true;
}
@@ -5783,7 +5948,7 @@
WindowState win = windows.get(i);
Slog.i(TAG, win + ": " + win.mLayer
+ " animLayer=" + win.mWinAnimator.mAnimLayer
- + " surfaceLayer=" + win.mWinAnimator.mSurfaceLayer);
+ + " surfaceLayer=" + win.mWinAnimator.mSurfaceController.getLayer());
}
}
@@ -5821,7 +5986,8 @@
if (allBlack) {
Slog.i(TAG, "Screenshot " + appWin + " was monochrome(" +
Integer.toHexString(firstColor) + ")! mSurfaceLayer=" +
- (appWin != null ? appWin.mWinAnimator.mSurfaceLayer : "null") +
+ (appWin != null ?
+ appWin.mWinAnimator.mSurfaceController.getLayer() : "null") +
" minLayer=" + minLayer + " maxLayer=" + maxLayer);
}
}
@@ -6068,6 +6234,10 @@
final WindowList windows = displayContent.getWindowList();
for (int i = windows.size() - 1; i >= 0; i--) {
WindowState w = windows.get(i);
+ // Discard surface after orientation change, these can't be reused.
+ if (w.mAppToken != null) {
+ w.mAppToken.destroySavedSurfaces();
+ }
if (w.mHasSurface) {
if (DEBUG_ORIENTATION) Slog.v(TAG, "Set mOrientationChanging of " + w);
w.mOrientationChanging = true;
@@ -6921,6 +7091,11 @@
Slog.w(TAG, "startPositioningLocked: Bad window " + win);
return false;
}
+ if (win.mInputChannel == null) {
+ Slog.wtf(TAG, "startPositioningLocked: " + win + " has no input channel, "
+ + " probably being removed");
+ return false;
+ }
final DisplayContent displayContent = win.getDisplayContent();
if (displayContent == null) {
@@ -6971,6 +7146,7 @@
}
final int callerPid = Binder.getCallingPid();
+ final int callerUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
IBinder token = null;
@@ -6995,6 +7171,8 @@
final IBinder winBinder = window.asBinder();
token = new Binder();
mDragState = new DragState(this, token, surface, flags, winBinder);
+ mDragState.mPid = callerPid;
+ mDragState.mUid = callerUid;
token = mDragState.mToken = new Binder();
// 5 second timeout for this window to actually begin the drag
@@ -7219,16 +7397,6 @@
public static final int RESIZE_TASK = 44;
/**
- * Used to indicate in the message that the dock divider needs to be updated only if it's
- * necessary.
- */
- static final int DOCK_DIVIDER_NO_FORCE_UPDATE = 0;
- /**
- * Used to indicate in the message that the dock divider should be force-removed before
- * updating, so new configuration can be applied.
- */
- static final int DOCK_DIVIDER_FORCE_UPDATE = 1;
- /**
* Used to denote that an integer field in a message will not be used.
*/
public static final int UNUSED = 0;
@@ -7771,12 +7939,10 @@
}
}
}
- break;
case UPDATE_DOCKED_STACK_DIVIDER: {
- DisplayContent content = (DisplayContent) msg.obj;
- final boolean forceUpdate = msg.arg1 == DOCK_DIVIDER_FORCE_UPDATE;
synchronized (mWindowMap) {
- content.mDividerControllerLocked.update(mCurConfiguration, forceUpdate);
+ getDefaultDisplayContentLocked().getDockedDividerController()
+ .reevaluateVisibility();
}
}
break;
@@ -8344,7 +8510,7 @@
final int numTokens = tokens.size();
for (int tokenNdx = 0; tokenNdx < numTokens; ++tokenNdx) {
final AppWindowToken wtoken = tokens.get(tokenNdx);
- if (wtoken.mIsExiting && !wtoken.mReplacingWindow) {
+ if (wtoken.mIsExiting && !wtoken.mWillReplaceWindow) {
continue;
}
i = reAddAppWindowsLocked(displayContent, i, wtoken);
@@ -8394,8 +8560,7 @@
final WindowStateAnimator winAnimator = w.mWinAnimator;
boolean layerChanged = false;
int oldLayer = w.mLayer;
- if (w.mBaseLayer == curBaseLayer || w.mIsImWindow
- || (i > 0 && w.mIsWallpaper)) {
+ if (w.mBaseLayer == curBaseLayer || w.mIsImWindow || (i > 0 && w.mIsWallpaper)) {
curLayer += WINDOW_LAYER_MULTIPLIER;
w.mLayer = curLayer;
} else {
@@ -8414,7 +8579,8 @@
} else if (wtoken != null) {
winAnimator.mAnimLayer =
w.mLayer + wtoken.mAppAnimator.animLayerAdjustment;
- if (wtoken.mReplacingWindow && wtoken.mAnimateReplacingWindow) {
+ if (wtoken.mWillReplaceWindow && wtoken.mAnimateReplacingWindow &&
+ wtoken.mReplacingWindow != w) {
// 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.
@@ -8436,7 +8602,7 @@
final DimLayer.DimLayerUser dimLayerUser = w.getDimLayerUser();
final DisplayContent displayContent = w.getDisplayContent();
if (layerChanged && dimLayerUser != null && displayContent != null &&
- displayContent.mDimBehindController.isDimming(dimLayerUser, winAnimator)) {
+ displayContent.mDimLayerController.isDimming(dimLayerUser, winAnimator)) {
// Force an animation pass just to update the mDimLayer layer.
scheduleAnimationLocked();
}
@@ -8555,6 +8721,13 @@
+ " dragResizingChanged=" + dragResizingChanged);
}
+ // If it's a dead window left on screen, and the configuration changed,
+ // there is nothing we can do about it. Remove the window now.
+ if (w.mAppToken != null && w.mAppDied) {
+ w.mAppToken.removeAllDeadWindows();
+ return;
+ }
+
w.mLastOverscanInsets.set(w.mOverscanInsets);
w.mLastContentInsets.set(w.mContentInsets);
w.mLastVisibleInsets.set(w.mVisibleInsets);
@@ -8567,9 +8740,11 @@
// we need to go through the process of getting informed by the
// application when it has finished drawing.
if (w.mOrientationChanging || dragResizingChanged) {
- if (DEBUG_SURFACE_TRACE || DEBUG_ANIM || DEBUG_ORIENTATION) Slog.v(TAG,
- "Orientation start waiting for draw mDrawState=DRAW_PENDING in "
- + w + ", surface " + winAnimator.mSurfaceControl);
+ if (DEBUG_SURFACE_TRACE || DEBUG_ANIM || DEBUG_ORIENTATION || DEBUG_RESIZE) {
+ Slog.v(TAG, "Orientation or resize start waiting for draw"
+ + ", mDrawState=DRAW_PENDING in " + w
+ + ", surfaceController " + winAnimator.mSurfaceController);
+ }
winAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING;
if (w.mAppToken != null) {
w.mAppToken.allDrawn = false;
@@ -8578,15 +8753,14 @@
}
if (!mResizingWindows.contains(w)) {
if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG,
- "Resizing window " + w + " to " + winAnimator.mSurfaceW
- + "x" + winAnimator.mSurfaceH);
+ "Resizing window " + w);
mResizingWindows.add(w);
}
} else if (w.mOrientationChanging) {
if (w.isDrawnLw()) {
if (DEBUG_ORIENTATION) Slog.v(TAG,
"Orientation not waiting for draw in "
- + w + ", surface " + winAnimator.mSurfaceControl);
+ + w + ", surfaceController " + winAnimator.mSurfaceController);
w.mOrientationChanging = false;
w.mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
- mDisplayFreezeTime);
@@ -8683,7 +8857,7 @@
boolean reclaimSomeSurfaceMemoryLocked(WindowStateAnimator winAnimator, String operation,
boolean secure) {
- final SurfaceControl surface = winAnimator.mSurfaceControl;
+ final WindowSurfaceController surfaceController = winAnimator.mSurfaceController;
boolean leakedSurface = false;
boolean killedApps = false;
@@ -8704,29 +8878,26 @@
for (int winNdx = 0; winNdx < numWindows; ++winNdx) {
final WindowState ws = windows.get(winNdx);
WindowStateAnimator wsa = ws.mWinAnimator;
- if (wsa.mSurfaceControl != null) {
+ if (wsa.mSurfaceController != null) {
if (!mSessions.contains(wsa.mSession)) {
Slog.w(TAG, "LEAKED SURFACE (session doesn't exist): "
- + ws + " surface=" + wsa.mSurfaceControl
+ + ws + " surface=" + wsa.mSurfaceController
+ " token=" + ws.mToken
+ " pid=" + ws.mSession.mPid
+ " uid=" + ws.mSession.mUid);
- if (SHOW_TRANSACTIONS) logSurface(ws, "LEAK DESTROY", null);
- wsa.mSurfaceControl.destroy();
- wsa.mSurfaceShown = false;
- wsa.mSurfaceControl = null;
- ws.mHasSurface = false;
+ wsa.destroySurface();
+ ws.setHasSurface(false);
mForceRemoves.add(ws);
leakedSurface = true;
} else if (ws.mAppToken != null && ws.mAppToken.clientHidden) {
Slog.w(TAG, "LEAKED SURFACE (app token hidden): "
- + ws + " surface=" + wsa.mSurfaceControl
- + " token=" + ws.mAppToken);
+ + ws + " surface=" + wsa.mSurfaceController
+ + " token=" + ws.mAppToken
+ + " saved=" + ws.mAppToken.mHasSavedSurface);
if (SHOW_TRANSACTIONS) logSurface(ws, "LEAK DESTROY", null);
- wsa.mSurfaceControl.destroy();
- wsa.mSurfaceShown = false;
- wsa.mSurfaceControl = null;
- ws.mHasSurface = false;
+ wsa.destroySurface();
+ ws.setHasSurface(false);
+ ws.mAppToken.mHasSavedSurface = false;
leakedSurface = true;
}
}
@@ -8745,7 +8916,7 @@
continue;
}
WindowStateAnimator wsa = ws.mWinAnimator;
- if (wsa.mSurfaceControl != null) {
+ if (wsa.mSurfaceController != null) {
pidCandidates.append(wsa.mSession.mPid, wsa.mSession.mPid);
}
}
@@ -8768,13 +8939,11 @@
// We managed to reclaim some memory, so get rid of the trouble
// surface and ask the app to request another one.
Slog.w(TAG, "Looks like we have reclaimed some memory, clearing surface for retry.");
- if (surface != null) {
+ if (surfaceController != null) {
if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) logSurface(winAnimator.mWin,
"RECOVER DESTROY", null);
- surface.destroy();
- winAnimator.mSurfaceShown = false;
- winAnimator.mSurfaceControl = null;
- winAnimator.mWin.mHasSurface = false;
+ surfaceController.destroyInTransaction();
+ winAnimator.mWin.setHasSurface(false);
scheduleRemoveStartingWindowLocked(winAnimator.mWin.mAppToken);
}
@@ -9250,11 +9419,11 @@
if (windowState == null) {
return false;
}
- SurfaceControl surfaceControl = windowState.mWinAnimator.mSurfaceControl;
- if (surfaceControl == null) {
+ WindowSurfaceController surfaceController = windowState.mWinAnimator.mSurfaceController;
+ if (surfaceController == null) {
return false;
}
- return surfaceControl.clearContentFrameStats();
+ return surfaceController.clearWindowContentFrameStats();
}
}
@@ -9269,15 +9438,15 @@
if (windowState == null) {
return null;
}
- SurfaceControl surfaceControl = windowState.mWinAnimator.mSurfaceControl;
- if (surfaceControl == null) {
+ WindowSurfaceController surfaceController = windowState.mWinAnimator.mSurfaceController;
+ if (surfaceController == null) {
return null;
}
if (mTempWindowRenderStats == null) {
mTempWindowRenderStats = new WindowContentFrameStats();
}
WindowContentFrameStats stats = mTempWindowRenderStats;
- if (!surfaceControl.getContentFrameStats(stats)) {
+ if (!surfaceController.getWindowContentFrameStats(stats)) {
return null;
}
return stats;
@@ -9564,7 +9733,7 @@
mDisplayContents.valueAt(displayNdx).getWindowList();
for (int winNdx = windowList.size() - 1; winNdx >= 0; --winNdx) {
final WindowState w = windowList.get(winNdx);
- if (w.mWinAnimator.mSurfaceShown
+ if (w.mWinAnimator.getShown()
&& (!appsOnly || (appsOnly && w.mAppToken != null))) {
windows.add(w);
}
@@ -9724,7 +9893,7 @@
return;
} else if ("surfaces".equals(cmd)) {
synchronized(mWindowMap) {
- WindowStateAnimator.SurfaceTrace.dumpAllSurfaces(pw, null);
+ WindowSurfaceController.SurfaceTrace.dumpAllSurfaces(pw, null);
}
return;
} else if ("displays".equals(cmd) || "d".equals(cmd)) {
@@ -9782,7 +9951,7 @@
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
- WindowStateAnimator.SurfaceTrace.dumpAllSurfaces(pw, dumpAll ?
+ WindowSurfaceController.SurfaceTrace.dumpAllSurfaces(pw, dumpAll ?
"-------------------------------------------------------------------------------"
: null);
pw.println();
@@ -9960,11 +10129,34 @@
Slog.w(TAG, "Attempted to set replacing window on non-existing app token " + token);
return;
}
- appWindowToken.mReplacingWindow = true;
+ if (DEBUG_ADD_REMOVE) Slog.d(TAG, "Marking app token " + appWindowToken
+ + " as replacing window.");
+ appWindowToken.mWillReplaceWindow = true;
+ appWindowToken.mHasReplacedWindow = false;
appWindowToken.mAnimateReplacingWindow = animate;
}
}
+ @Override
+ public int getDockedStackSide() {
+ synchronized (mWindowMap) {
+ TaskStack dockedStack = getDefaultDisplayContentLocked().getDockedStackLocked();
+ return dockedStack == null ? DOCKED_INVALID : dockedStack.getDockSide();
+ }
+ }
+
+ @Override
+ public void setDockedStackResizing(boolean resizing) {
+ synchronized (mWindowMap) {
+ getDefaultDisplayContentLocked().getDockedDividerController().setResizing(resizing);
+ requestTraversal();
+ }
+ }
+
+ boolean isDockedStackResizingLocked() {
+ return getDefaultDisplayContentLocked().getDockedDividerController().isResizing();
+ }
+
static int dipToPixel(int dip, DisplayMetrics displayMetrics) {
return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, displayMetrics);
}
@@ -10166,8 +10358,7 @@
@Override
public boolean isStackVisible(int stackId) {
synchronized (mWindowMap) {
- final TaskStack stack = mStackIdToStack.get(stackId);
- return (stack != null && stack.isVisibleLocked());
+ return WindowManagerService.this.isStackVisibleLocked(stackId);
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 383ad8c..93b40c0 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -18,18 +18,28 @@
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
+import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
+import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
+import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
+import static android.view.WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+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_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.WindowManagerService.DEBUG_ADD_REMOVE;
+import static com.android.server.wm.WindowManagerService.DEBUG_ANIM;
+import static com.android.server.wm.WindowManagerService.DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.WindowManagerService.DEBUG_CONFIGURATION;
-import static com.android.server.wm.WindowManagerService.DEBUG_DIM_LAYER;
+import static com.android.server.wm.WindowManagerService.DEBUG_FOCUS_LIGHT;
import static com.android.server.wm.WindowManagerService.DEBUG_LAYOUT;
import static com.android.server.wm.WindowManagerService.DEBUG_ORIENTATION;
import static com.android.server.wm.WindowManagerService.DEBUG_POWER;
@@ -38,6 +48,7 @@
import android.app.ActivityManager;
import android.app.AppOpsManager;
+import android.graphics.Point;
import android.os.PowerManager;
import android.os.RemoteCallbackList;
import android.os.SystemClock;
@@ -56,7 +67,6 @@
import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.graphics.Rect;
-import android.graphics.RectF;
import android.graphics.Region;
import android.os.IBinder;
import android.os.RemoteException;
@@ -67,6 +77,8 @@
import android.view.IApplicationToken;
import android.view.IWindow;
import android.view.InputChannel;
+import android.view.InputEvent;
+import android.view.InputEventReceiver;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
@@ -96,6 +108,9 @@
static final boolean BOUNDS_FOR_TOUCH = true;
+ static final int DRAG_RESIZE_MODE_FREEFORM = 0;
+ static final int DRAG_RESIZE_MODE_DOCKED_DIVIDER = 1;
+
final WindowManagerService mService;
final WindowManagerPolicy mPolicy;
final Context mContext;
@@ -133,6 +148,7 @@
boolean mAttachedHidden; // is our parent window hidden?
boolean mWallpaperVisible; // for wallpaper, what was last vis report?
boolean mDragResizing;
+ int mResizeMode;
RemoteCallbackList<IWindowFocusObserver> mFocusCallbacks;
@@ -159,11 +175,10 @@
private boolean mConfigHasChanged;
/**
- * Actual frame shown on-screen (may be modified by animation). These
- * are in the screen's coordinate space (WITH the compatibility scale
- * applied).
+ * Actual position of the surface shown on-screen (may be modified by animation). These are
+ * in the screen's coordinate space (WITH the compatibility scale applied).
*/
- final RectF mShownFrame = new RectF();
+ final Point mShownPosition = new Point();
/**
* Insets that determine the actually visible area. These are in the application's
@@ -339,6 +354,12 @@
boolean mRemoveOnExit;
/**
+ * Whether the app died while it was visible, if true we might need
+ * to continue to show it until it's restarted.
+ */
+ boolean mAppDied;
+
+ /**
* Set when the orientation is changing and this window has not yet
* been updated for the new orientation.
*/
@@ -361,6 +382,7 @@
// Input channel and input window handle used by the input dispatcher.
final InputWindowHandle mInputWindowHandle;
InputChannel mInputChannel;
+ InputChannel mClientChannel;
// Used to improve performance of toString()
String mStringNameCache;
@@ -448,7 +470,8 @@
+ WindowManagerService.TYPE_LAYER_OFFSET;
mSubLayer = mPolicy.subWindowTypeToLayerLw(a.type);
mAttachedWindow = attachedWindow;
- if (WindowManagerService.DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + this + " to " + mAttachedWindow);
+ if (WindowManagerService.DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + this + " to "
+ + mAttachedWindow);
final WindowList childWindows = mAttachedWindow.mChildWindows;
final int numChildWindows = childWindows.size();
@@ -554,7 +577,7 @@
@Override
public void computeFrameLw(Rect pf, Rect df, Rect of, Rect cf, Rect vf, Rect dcf, Rect sf,
Rect osf) {
- if (mAppToken != null && mAppToken.mReplacingWindow
+ if (mAppToken != null && mAppToken.mWillReplaceWindow
&& (mExiting || !mAppToken.mReplacingRemoveRequested)) {
// This window is being replaced and either already got information that it's being
// removed or we are still waiting for some information. Because of this we don't
@@ -594,7 +617,7 @@
final int ph = mContainingFrame.height();
int w,h;
- if ((mAttrs.flags & WindowManager.LayoutParams.FLAG_SCALED) != 0) {
+ if ((mAttrs.flags & FLAG_SCALED) != 0) {
if (mAttrs.width < 0) {
w = pw;
} else if (mEnforceSizeCompat) {
@@ -706,7 +729,7 @@
mVisibleFrame.set(mContentFrame);
mStableFrame.set(mContentFrame);
} else if (mAttrs.type == TYPE_DOCK_DIVIDER) {
- mDisplayContent.mDividerControllerLocked.positionDockedStackedDivider(mFrame);
+ mDisplayContent.getDockedDividerController().positionDockedStackedDivider(mFrame);
mContentFrame.set(mFrame);
} else {
mContentFrame.set(Math.max(mContentFrame.left, mFrame.left),
@@ -787,8 +810,8 @@
}
@Override
- public RectF getShownFrameLw() {
- return mShownFrame;
+ public Point getShownPositionLw() {
+ return mShownPosition;
}
@Override
@@ -939,17 +962,15 @@
/**
* Retrieves the visible bounds of the window.
* @param bounds The rect which gets the bounds.
- * @param forTouch Pass in BOUNDS_FOR_TOUCH to get touch related bounds, otherwise visible
- * bounds will be returned.
*/
- void getVisibleBounds(Rect bounds, boolean forTouch) {
+ void getVisibleBounds(Rect bounds) {
boolean intersectWithStackBounds = mAppToken != null && mAppToken.mCropWindowsToStack;
bounds.setEmpty();
mTmpRect.setEmpty();
if (intersectWithStackBounds) {
final TaskStack stack = getStack();
if (stack != null) {
- stack.getBounds(mTmpRect);
+ stack.getDimBounds(mTmpRect);
} else {
intersectWithStackBounds = false;
}
@@ -967,12 +988,6 @@
}
return;
}
- if (forTouch && inFreeformWorkspace()) {
- final DisplayMetrics displayMetrics = getDisplayContent().getDisplayMetrics();
- final int delta = WindowManagerService.dipToPixel(
- RESIZE_HANDLE_WIDTH_IN_DP, displayMetrics);
- bounds.inset(-delta, -delta);
- }
}
public long getInputDispatchingTimeoutNanos() {
@@ -1272,30 +1287,80 @@
mConfigHasChanged = false;
}
- void setInputChannel(InputChannel inputChannel) {
+ void setHasSurface(boolean hasSurface) {
+ mHasSurface = hasSurface;
+ }
+
+ private final class DeadWindowEventReceiver extends InputEventReceiver {
+ DeadWindowEventReceiver(InputChannel inputChannel) {
+ super(inputChannel, mService.mH.getLooper());
+ }
+ @Override
+ public void onInputEvent(InputEvent event) {
+ finishInputEvent(event, true);
+ }
+ }
+ /**
+ * Dummy event receiver for windows that died visible.
+ */
+ private DeadWindowEventReceiver mDeadWindowEventReceiver;
+
+ void openInputChannel(InputChannel outInputChannel) {
if (mInputChannel != null) {
throw new IllegalStateException("Window already has an input channel.");
}
-
- mInputChannel = inputChannel;
- mInputWindowHandle.inputChannel = inputChannel;
+ String name = makeInputChannelName();
+ InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
+ mInputChannel = inputChannels[0];
+ mClientChannel = inputChannels[1];
+ mInputWindowHandle.inputChannel = inputChannels[0];
+ if (outInputChannel != null) {
+ mClientChannel.transferTo(outInputChannel);
+ mClientChannel.dispose();
+ mClientChannel = null;
+ } else {
+ // If the window died visible, we setup a dummy input channel, so that taps
+ // can still detected by input monitor channel, and we can relaunch the app.
+ // Create dummy event receiver that simply reports all events as handled.
+ mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);
+ }
+ mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);
}
void disposeInputChannel() {
+ if (mDeadWindowEventReceiver != null) {
+ mDeadWindowEventReceiver.dispose();
+ mDeadWindowEventReceiver = null;
+ }
+
+ // unregister server channel first otherwise it complains about broken channel
if (mInputChannel != null) {
mService.mInputManager.unregisterInputChannel(mInputChannel);
-
mInputChannel.dispose();
mInputChannel = null;
}
-
+ if (mClientChannel != null) {
+ mClientChannel.dispose();
+ mClientChannel = null;
+ }
mInputWindowHandle.inputChannel = null;
}
- void handleFlagDimBehind() {
- if ((mAttrs.flags & FLAG_DIM_BEHIND) != 0 && mDisplayContent != null && !mExiting
- && isDisplayedLw()) {
- mDisplayContent.mDimBehindController.applyDimBehind(getDimLayerUser(), mWinAnimator);
+ void applyDimLayerIfNeeded() {
+ // When the app is terminated (eg. from Recents), the task might have already been
+ // removed with the window pending removal. Don't apply dim in such cases, as there
+ // will be no more updateDimLayer() calls, which leaves the dimlayer invalid.
+ final AppWindowToken token = mAppToken;
+ if (token != null && token.removed) {
+ return;
+ }
+
+ if (!mExiting && mAppDied) {
+ // If app died visible, apply a dim over the window to indicate that it's inactive
+ mDisplayContent.mDimLayerController.applyDimAbove(getDimLayerUser(), mWinAnimator);
+ } else if ((mAttrs.flags & FLAG_DIM_BEHIND) != 0
+ && mDisplayContent != null && !mExiting && isDisplayedLw()) {
+ mDisplayContent.mDimLayerController.applyDimBehind(getDimLayerUser(), mWinAnimator);
}
}
@@ -1309,12 +1374,16 @@
void maybeRemoveReplacedWindow() {
AppWindowToken token = mAppToken;
- if (token != null && token.mReplacingWindow) {
- token.mReplacingWindow = false;
+ if (token != null && token.mWillReplaceWindow && token.mReplacingWindow == this
+ && token.mHasReplacedWindow) {
+ if (DEBUG_ADD_REMOVE) Slog.d(TAG, "Removing replacing window: " + this);
+ token.mWillReplaceWindow = false;
token.mAnimateReplacingWindow = false;
token.mReplacingRemoveRequested = false;
+ token.mReplacingWindow = null;
+ token.mHasReplacedWindow = false;
for (int i = token.allAppWindows.size() - 1; i >= 0; i--) {
- WindowState win = token.allAppWindows.get(i);
+ final WindowState win = token.allAppWindows.get(i);
if (win.mExiting) {
mService.removeWindowInnerLocked(win);
}
@@ -1328,6 +1397,121 @@
}
}
+ boolean inDockedWorkspace() {
+ return mAppToken != null && mAppToken.mTask != null && mAppToken.mTask.inDockedWorkspace();
+ }
+
+ int getTouchableRegion(Region region, int flags, InputMonitor inputMonitor) {
+ final boolean modal = (flags & (WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)) == 0;
+ if (modal && mAppToken != null) {
+ // Limit the outer touch to the activity stack region.
+ flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+ // If this is a modal window we need to dismiss it if it's not full screen and the
+ // touch happens outside of the frame that displays the content. This means we
+ // need to intercept touches outside of that window. The dim layer user
+ // associated with the window (task or stack) will give us the good bounds, as
+ // they would be used to display the dim layer.
+ final DimLayer.DimLayerUser dimLayerUser = getDimLayerUser();
+ if (dimLayerUser != null) {
+ dimLayerUser.getDimBounds(mTmpRect);
+ } else {
+ getVisibleBounds(mTmpRect);
+ }
+ if (inFreeformWorkspace()) {
+ // For freeform windows we the touch region to include the whole surface for the
+ // shadows.
+ final DisplayMetrics displayMetrics = getDisplayContent().getDisplayMetrics();
+ final int delta = WindowManagerService.dipToPixel(
+ RESIZE_HANDLE_WIDTH_IN_DP, displayMetrics);
+ mTmpRect.inset(-delta, -delta);
+ }
+ region.set(mTmpRect);
+ } else {
+ // Not modal or full screen modal
+ getTouchableRegion(region);
+ }
+ return flags;
+ }
+
+ void checkPolicyVisibilityChange() {
+ if (mPolicyVisibility != mPolicyVisibilityAfterAnim) {
+ if (DEBUG_VISIBILITY) {
+ Slog.v(TAG, "Policy visibility changing after anim in " +
+ mWinAnimator + ": " + mPolicyVisibilityAfterAnim);
+ }
+ mPolicyVisibility = mPolicyVisibilityAfterAnim;
+ setDisplayLayoutNeeded();
+ if (!mPolicyVisibility) {
+ if (mService.mCurrentFocus == this) {
+ if (DEBUG_FOCUS_LIGHT) Slog.i(TAG,
+ "setAnimationLocked: setting mFocusMayChange true");
+ mService.mFocusMayChange = true;
+ }
+ // Window is no longer visible -- make sure if we were waiting
+ // for it to be displayed before enabling the display, that
+ // we allow the display to be enabled now.
+ mService.enableScreenIfNeededLocked();
+ }
+ }
+ }
+
+ void setRequestedSize(int requestedWidth, int requestedHeight) {
+ if ((mRequestedWidth != requestedWidth || mRequestedHeight != requestedHeight)) {
+ mLayoutNeeded = true;
+ mRequestedWidth = requestedWidth;
+ mRequestedHeight = requestedHeight;
+ }
+ }
+
+ void prepareWindowToDisplayDuringRelayout(Configuration outConfig) {
+ if ((mAttrs.softInputMode & SOFT_INPUT_MASK_ADJUST)
+ == SOFT_INPUT_ADJUST_RESIZE) {
+ mLayoutNeeded = true;
+ }
+ if (isDrawnLw() && mService.okToDisplay()) {
+ mWinAnimator.applyEnterAnimationLocked();
+ }
+ if ((mAttrs.flags & FLAG_TURN_SCREEN_ON) != 0) {
+ if (DEBUG_VISIBILITY) Slog.v(TAG, "Relayout window turning screen on: " + this);
+ mTurnOnScreen = true;
+ }
+ if (isConfigChanged()) {
+ if (DEBUG_CONFIGURATION) Slog.i(TAG, "Window " + this + " visible with new config: "
+ + mService.mCurConfiguration);
+ outConfig.setTo(mService.mCurConfiguration);
+ }
+ }
+
+ void adjustStartingWindowFlags() {
+ if (mAttrs.type == TYPE_BASE_APPLICATION && mAppToken != null
+ && mAppToken.startingWindow != null) {
+ // Special handling of starting window over the base
+ // window of the app: propagate lock screen flags to it,
+ // to provide the correct semantics while starting.
+ final int mask = FLAG_SHOW_WHEN_LOCKED | FLAG_DISMISS_KEYGUARD
+ | FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
+ WindowManager.LayoutParams sa = mAppToken.startingWindow.mAttrs;
+ sa.flags = (sa.flags & ~mask) | (mAttrs.flags & mask);
+ }
+ }
+
+ void setWindowScale(int requestedWidth, int requestedHeight) {
+ final boolean scaledWindow = (mAttrs.flags & FLAG_SCALED) != 0;
+
+ if (scaledWindow) {
+ // requested{Width|Height} Surface's physical size
+ // attrs.{width|height} Size on screen
+ // TODO: We don't check if attrs != null here. Is it implicitly checked?
+ mHScale = (mAttrs.width != requestedWidth) ?
+ (mAttrs.width / (float)requestedWidth) : 1.0f;
+ mVScale = (mAttrs.height != requestedHeight) ?
+ (mAttrs.height / (float)requestedHeight) : 1.0f;
+ } else {
+ mHScale = mVScale = 1;
+ }
+ }
+
private class DeathRecipient implements IBinder.DeathRecipient {
@Override
public void binderDied() {
@@ -1336,6 +1520,9 @@
WindowState win = mService.windowForClientLocked(mSession, mClient, false);
Slog.i(TAG, "WIN DEATH: " + win);
if (win != null) {
+ if (win.mAppToken != null && !win.mAppToken.clientHidden) {
+ win.mAppToken.appDied = true;
+ }
mService.removeWindowLocked(win);
} else if (mHasSurface) {
Slog.e(TAG, "!!! LEAK !!! Window removed but surface still valid.");
@@ -1438,7 +1625,7 @@
// we allow the display to be enabled now.
mService.enableScreenIfNeededLocked();
if (mService.mCurrentFocus == this) {
- if (WindowManagerService.DEBUG_FOCUS_LIGHT) Slog.i(TAG,
+ if (DEBUG_FOCUS_LIGHT) Slog.i(TAG,
"WindowState.hideLw: setting mFocusMayChange true");
mService.mFocusMayChange = true;
}
@@ -1499,6 +1686,28 @@
return mExiting || (mService.mClosingApps.contains(mAppToken));
}
+ boolean isAnimatingWithSavedSurface() {
+ return mAppToken != null && mAppToken.mAnimatingWithSavedSurface;
+ }
+
+ boolean shouldSaveSurface() {
+ if (ActivityManager.isLowRamDeviceStatic()) {
+ // Don't save surfaces on Svelte devices.
+ return false;
+ }
+
+ Task task = getTask();
+ if (task == null || task.inHomeStack()
+ || task.getTopAppWindowToken() != mAppToken) {
+ // Don't save surfaces for home stack apps. These usually resume and draw
+ // first frame very fast. Saving surfaces are mostly a waste of memory.
+ // Don't save if the window is not the topmost window.
+ return false;
+ }
+
+ return mAppToken.shouldSaveSurface();
+ }
+
@Override
public boolean isDefaultDisplay() {
final DisplayContent displayContent = getDisplayContent();
@@ -1513,7 +1722,7 @@
public boolean isDimming() {
final DimLayer.DimLayerUser dimLayerUser = getDimLayerUser();
return dimLayerUser != null && mDisplayContent != null &&
- mDisplayContent.mDimBehindController.isDimming(dimLayerUser, mWinAnimator);
+ mDisplayContent.mDimLayerController.isDimming(dimLayerUser, mWinAnimator);
}
public void setShowToOwnerOnlyLocked(boolean showToOwnerOnly) {
@@ -1611,13 +1820,13 @@
try {
if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG, "Reporting new frame to " + this
+ ": " + mCompatFrame);
- boolean configChanged = isConfigChanged();
+ final boolean configChanged = isConfigChanged();
final Task task = getTask();
final Configuration overrideConfig =
(task != null) ? task.mOverrideConfig : Configuration.EMPTY;
if ((DEBUG_RESIZE || DEBUG_ORIENTATION || DEBUG_CONFIGURATION) && configChanged) {
Slog.i(TAG, "Sending new config to window " + this + ": "
- + mWinAnimator.mSurfaceW + "x" + mWinAnimator.mSurfaceH + " / config="
+ + " / config="
+ mService.mCurConfiguration + " overrideConfig=" + overrideConfig);
}
setConfiguration(mService.mCurConfiguration, overrideConfig);
@@ -1639,15 +1848,15 @@
@Override
public void run() {
try {
- mClient.resized(frame, overscanInsets, contentInsets,
- visibleInsets, stableInsets, outsets, reportDraw, newConfig);
+ dispatchResized(frame, overscanInsets, contentInsets, visibleInsets,
+ stableInsets, outsets, reportDraw, newConfig);
} catch (RemoteException e) {
// Not a remote call, RemoteException won't be raised.
}
}
});
} else {
- mClient.resized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets,
+ dispatchResized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets,
outsets, reportDraw, newConfig);
}
@@ -1676,6 +1885,23 @@
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
+ private void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets,
+ Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
+ Configuration newConfig) throws RemoteException {
+ DisplayInfo displayInfo = getDisplayInfo();
+ mTmpRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
+ // When the task is docked, we send fullscreen sized backDropFrame as soon as resizing
+ // start even if we haven't received the relayout window, so that the client requests
+ // the relayout sooner. When dragging stops, backDropFrame needs to stay fullscreen
+ // until the window to small size, otherwise the multithread renderer will shift last
+ // one or more frame to wrong offset. So here we send fullscreen backdrop if either
+ // isDragResizing() or isDragResizeChanged() is true.
+ boolean resizing = isDragResizing() || isDragResizeChanged();
+ final Rect backDropFrame = (inFreeformWorkspace() || !resizing) ? frame : mTmpRect;
+ mClient.resized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets,
+ outsets, reportDraw, newConfig, backDropFrame);
+ }
+
public void registerFocusObserver(IWindowFocusObserver observer) {
synchronized(mService.mWindowMap) {
if (mFocusCallbacks == null) {
@@ -1708,6 +1934,10 @@
return mDragResizing != computeDragResizing();
}
+ int getResizeMode() {
+ return mResizeMode;
+ }
+
private boolean computeDragResizing() {
final Task task = getTask();
if (task == null) {
@@ -1722,6 +1952,9 @@
void setDragResizing() {
mDragResizing = computeDragResizing();
+ mResizeMode = mDragResizing && mDisplayContent.mDividerControllerLocked.isResizing()
+ ? DRAG_RESIZE_MODE_DOCKED_DIVIDER
+ : DRAG_RESIZE_MODE_FREEFORM;
}
boolean isDragResizing() {
@@ -1772,7 +2005,8 @@
pw.print(prefix); pw.print("mToken="); pw.println(mToken);
pw.print(prefix); pw.print("mRootToken="); pw.println(mRootToken);
if (mAppToken != null) {
- pw.print(prefix); pw.print("mAppToken="); pw.println(mAppToken);
+ pw.print(prefix); pw.print("mAppToken="); pw.print(mAppToken);
+ pw.print(" mAppDied=");pw.println(mAppDied);
}
if (mTargetAppToken != null) {
pw.print(prefix); pw.print("mTargetAppToken="); pw.println(mTargetAppToken);
@@ -1822,7 +2056,7 @@
}
}
pw.print(prefix); pw.print("mHasSurface="); pw.print(mHasSurface);
- pw.print(" mShownFrame="); mShownFrame.printShortString(pw);
+ pw.print(" mShownPosition="); mShownPosition.printShortString(pw);
pw.print(" isReadyForDisplay()="); pw.println(isReadyForDisplay());
if (dumpAll) {
pw.print(prefix); pw.print("mFrame="); mFrame.printShortString(pw);
@@ -1938,4 +2172,13 @@
}
return mStringNameCache;
}
+
+ void transformFromScreenToSurfaceSpace(Rect rect) {
+ if (mHScale >= 0) {
+ rect.right = rect.left + (int)((rect.right - rect.left) / mHScale);
+ }
+ if (mVScale >= 0) {
+ rect.bottom = rect.top + (int)((rect.bottom - rect.top) / mVScale);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 2733933..a3bb320 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -29,6 +29,7 @@
import static com.android.server.wm.WindowManagerService.SHOW_SURFACE_ALLOC;
import static com.android.server.wm.WindowManagerService.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
+import static com.android.server.wm.WindowState.*;
import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
import static com.android.server.wm.WindowSurfacePlacer.SET_TURN_ON_SCREEN;
@@ -38,7 +39,6 @@
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
-import android.graphics.RectF;
import android.graphics.Region;
import android.os.Debug;
import android.os.RemoteException;
@@ -47,6 +47,7 @@
import android.view.DisplayInfo;
import android.view.MagnificationSpec;
import android.view.Surface.OutOfResourcesException;
+import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.WindowManager;
@@ -95,14 +96,13 @@
long mAnimationStartTime;
long mLastAnimationTime;
- SurfaceControl mSurfaceControl;
- SurfaceControl mPendingDestroySurface;
-
/**
* Set when we have changed the size of the surface, to know that
* we must tell them application to resize (and thus redraw itself).
*/
boolean mSurfaceResized;
+ WindowSurfaceController mSurfaceController;
+ private WindowSurfaceController mPendingDestroySurface;
/**
* Set if the client has asked that the destroy of its surface be delayed
@@ -110,7 +110,7 @@
*/
boolean mSurfaceDestroyDeferred;
- boolean mDestroyPreservedSurfaceUponRedraw;
+ private boolean mDestroyPreservedSurfaceUponRedraw;
float mShownAlpha = 0;
float mAlpha = 0;
float mLastAlpha = 0;
@@ -137,13 +137,6 @@
boolean mHaveMatrix;
- // For debugging, this is the last information given to the surface flinger.
- boolean mSurfaceShown;
- float mSurfaceX, mSurfaceY;
- float mSurfaceW, mSurfaceH;
- int mSurfaceLayer;
- float mSurfaceAlpha;
-
// Set to true if, when the window gets displayed, it should perform
// an enter animation.
boolean mEnterAnimationPending;
@@ -173,11 +166,6 @@
/** Set when the window has been shown in the screen the first time. */
static final int HAS_DRAWN = 4;
- // Surface flinger doesn't support crop rectangles where width or height is non-positive.
- // However, we need to somehow handle the situation where the cropping would completely hide
- // the window. We achieve this by explicitly hiding the surface and not letting it be shown.
- private boolean mHiddenForCrop;
-
String drawStateToString() {
switch (mDrawState) {
case NO_SURFACE: return "NO_SURFACE";
@@ -283,9 +271,8 @@
}
mTransformation.clear();
final boolean more = mAnimation.getTransformation(currentTime, mTransformation);
- if (false && DEBUG_ANIM) Slog.v(
- TAG, "Stepped animation in " + this +
- ": more=" + more + ", xform=" + mTransformation);
+ if (false && DEBUG_ANIM) Slog.v(TAG, "Stepped animation in " + this + ": more=" + more
+ + ", xform=" + mTransformation);
return more;
}
@@ -391,31 +378,10 @@
} else if (mIsWallpaper) {
mAnimLayer += mWallpaperControllerLocked.getAnimLayerAdjustment();
}
- if (DEBUG_LAYERS) Slog.v(TAG, "Stepping win " + this
- + " anim layer: " + mAnimLayer);
+ if (DEBUG_LAYERS) Slog.v(TAG, "Stepping win " + this + " anim layer: " + mAnimLayer);
mHasTransformation = false;
mHasLocalTransformation = false;
- if (mWin.mPolicyVisibility != mWin.mPolicyVisibilityAfterAnim) {
- if (DEBUG_VISIBILITY) {
- Slog.v(TAG, "Policy visibility changing after anim in " + this + ": "
- + mWin.mPolicyVisibilityAfterAnim);
- }
- mWin.mPolicyVisibility = mWin.mPolicyVisibilityAfterAnim;
- if (displayContent != null) {
- displayContent.layoutNeeded = true;
- }
- if (!mWin.mPolicyVisibility) {
- if (mService.mCurrentFocus == mWin) {
- if (WindowManagerService.DEBUG_FOCUS_LIGHT) Slog.i(TAG,
- "setAnimationLocked: setting mFocusMayChange true");
- mService.mFocusMayChange = true;
- }
- // Window is no longer visible -- make sure if we were waiting
- // for it to be displayed before enabling the display, that
- // we allow the display to be enabled now.
- mService.enableScreenIfNeededLocked();
- }
- }
+ mWin.checkPolicyVisibilityChange();
mTransformation.clear();
if (mDrawState == HAS_DRAWN
&& mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING
@@ -460,11 +426,16 @@
mWin.mChildWindows.get(i).mWinAnimator.finishExit();
}
- if (mEnteringAnimation && mWin.mAppToken == null) {
- try {
- mEnteringAnimation = false;
- mWin.mClient.dispatchWindowShown();
- } catch (RemoteException e) {
+ if (mEnteringAnimation) {
+ mEnteringAnimation = false;
+ mService.requestTraversal();
+ // System windows don't have an activity and an app token as a result, but need a way
+ // to be informed about their entrance animation end.
+ if (mWin.mAppToken == null) {
+ try {
+ mWin.mClient.dispatchWindowShown();
+ } catch (RemoteException e) {
+ }
}
}
@@ -487,12 +458,10 @@
if (WindowManagerService.localLOGV) Slog.v(
TAG, "Exit animation finished in " + this
+ ": remove=" + mWin.mRemoveOnExit);
- if (mSurfaceControl != null) {
+ if (mSurfaceController != null && mSurfaceController.hasSurface()) {
mService.mDestroySurface.add(mWin);
mWin.mDestroying = true;
- if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(
- mWin, "HIDE (finishExit)", null);
- hide();
+ hide("finishExit");
}
mWin.mExiting = false;
if (mWin.mRemoveOnExit) {
@@ -502,19 +471,12 @@
mWallpaperControllerLocked.hideWallpapers(mWin);
}
- void hide() {
+ void hide(String reason) {
if (!mLastHidden) {
//dump();
mLastHidden = true;
- if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(mWin,
- "HIDE (performLayout)", null);
- if (mSurfaceControl != null) {
- mSurfaceShown = false;
- try {
- mSurfaceControl.hide();
- } catch (RuntimeException e) {
- Slog.w(TAG, "Exception hiding surface in " + mWin);
- }
+ if (mSurfaceController != null) {
+ mSurfaceController.hideInTransaction(reason);
}
}
}
@@ -526,16 +488,23 @@
Slog.v(TAG, "Finishing drawing window " + mWin + ": mDrawState="
+ drawStateToString());
}
+ if (mWin.mAppToken != null) {
+ // App has drawn something to its windows, we're no longer animating with
+ // the saved surfaces. If the user exits now, we only want to save again
+ // if allDrawn is true.
+ mWin.mAppToken.mAnimatingWithSavedSurface = false;
+ }
if (mDrawState == DRAW_PENDING) {
if (DEBUG_SURFACE_TRACE || DEBUG_ANIM || SHOW_TRANSACTIONS || DEBUG_ORIENTATION)
Slog.v(TAG, "finishDrawingLocked: mDrawState=COMMIT_DRAW_PENDING " + this + " in "
- + mSurfaceControl);
+ + mSurfaceController);
if (DEBUG_STARTING_WINDOW && startingWindow) {
Slog.v(TAG, "Draw state now committed in " + mWin);
}
mDrawState = COMMIT_DRAW_PENDING;
return true;
}
+
return false;
}
@@ -550,12 +519,13 @@
return false;
}
if (DEBUG_SURFACE_TRACE || DEBUG_ANIM) {
- Slog.i(TAG, "commitFinishDrawingLocked: mDrawState=READY_TO_SHOW " + mSurfaceControl);
+ Slog.i(TAG, "commitFinishDrawingLocked: mDrawState=READY_TO_SHOW " + mSurfaceController);
}
mDrawState = READY_TO_SHOW;
boolean result = false;
final AppWindowToken atoken = mWin.mAppToken;
- if (atoken == null || atoken.allDrawn || mWin.mAttrs.type == TYPE_APPLICATION_STARTING) {
+ if (atoken == null || atoken.allDrawn || atoken.mAnimatingWithSavedSurface ||
+ mWin.mAttrs.type == TYPE_APPLICATION_STARTING) {
result = performShowLocked();
}
if (mDestroyPreservedSurfaceUponRedraw && result) {
@@ -564,234 +534,13 @@
return result;
}
- static class SurfaceTrace extends SurfaceControl {
- private final static String SURFACE_TAG = "SurfaceTrace";
- private final static boolean logSurfaceTrace = DEBUG_SURFACE_TRACE;
- final static ArrayList<SurfaceTrace> sSurfaces = new ArrayList<SurfaceTrace>();
-
- private float mSurfaceTraceAlpha = 0;
- private int mLayer;
- private final PointF mPosition = new PointF();
- private final Point mSize = new Point();
- private final Rect mWindowCrop = new Rect();
- private boolean mShown = false;
- private int mLayerStack;
- private boolean mIsOpaque;
- private float mDsdx, mDtdx, mDsdy, mDtdy;
- private final String mName;
-
- public SurfaceTrace(SurfaceSession s,
- String name, int w, int h, int format, int flags)
- throws OutOfResourcesException {
- super(s, name, w, h, format, flags);
- mName = name != null ? name : "Not named";
- mSize.set(w, h);
- if (logSurfaceTrace) Slog.v(SURFACE_TAG, "ctor: " + this + ". Called by "
- + Debug.getCallers(3));
- synchronized (sSurfaces) {
- sSurfaces.add(0, this);
- }
- }
-
- @Override
- public void setAlpha(float alpha) {
- if (mSurfaceTraceAlpha != alpha) {
- if (logSurfaceTrace) Slog.v(SURFACE_TAG, "setAlpha(" + alpha + "): OLD:" + this +
- ". Called by " + Debug.getCallers(3));
- mSurfaceTraceAlpha = alpha;
- }
- super.setAlpha(alpha);
- }
-
- @Override
- public void setLayer(int zorder) {
- if (zorder != mLayer) {
- if (logSurfaceTrace) Slog.v(SURFACE_TAG, "setLayer(" + zorder + "): OLD:" + this
- + ". Called by " + Debug.getCallers(3));
- mLayer = zorder;
- }
- super.setLayer(zorder);
-
- synchronized (sSurfaces) {
- sSurfaces.remove(this);
- int i;
- for (i = sSurfaces.size() - 1; i >= 0; i--) {
- SurfaceTrace s = sSurfaces.get(i);
- if (s.mLayer < zorder) {
- break;
- }
- }
- sSurfaces.add(i + 1, this);
- }
- }
-
- @Override
- public void setPosition(float x, float y) {
- if (x != mPosition.x || y != mPosition.y) {
- if (logSurfaceTrace) Slog.v(SURFACE_TAG, "setPosition(" + x + "," + y + "): OLD:"
- + this + ". Called by " + Debug.getCallers(3));
- mPosition.set(x, y);
- }
- super.setPosition(x, y);
- }
-
- @Override
- public void setSize(int w, int h) {
- if (w != mSize.x || h != mSize.y) {
- if (logSurfaceTrace) Slog.v(SURFACE_TAG, "setSize(" + w + "," + h + "): OLD:"
- + this + ". Called by " + Debug.getCallers(3));
- mSize.set(w, h);
- }
- super.setSize(w, h);
- }
-
- @Override
- public void setWindowCrop(Rect crop) {
- if (crop != null) {
- if (!crop.equals(mWindowCrop)) {
- if (logSurfaceTrace) Slog.v(SURFACE_TAG, "setWindowCrop("
- + crop.toShortString() + "): OLD:" + this + ". Called by "
- + Debug.getCallers(3));
- mWindowCrop.set(crop);
- }
- }
- super.setWindowCrop(crop);
- }
-
- @Override
- public void setLayerStack(int layerStack) {
- if (layerStack != mLayerStack) {
- if (logSurfaceTrace) Slog.v(SURFACE_TAG, "setLayerStack(" + layerStack + "): OLD:"
- + this + ". Called by " + Debug.getCallers(3));
- mLayerStack = layerStack;
- }
- super.setLayerStack(layerStack);
- }
-
- @Override
- public void setOpaque(boolean isOpaque) {
- if (isOpaque != mIsOpaque) {
- if (logSurfaceTrace) Slog.v(SURFACE_TAG, "setOpaque(" + isOpaque + "): OLD:"
- + this + ". Called by " + Debug.getCallers(3));
- mIsOpaque = isOpaque;
- }
- super.setOpaque(isOpaque);
- }
-
- @Override
- public void setSecure(boolean isSecure) {
- super.setSecure(isSecure);
- }
-
- @Override
- public void setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
- if (dsdx != mDsdx || dtdx != mDtdx || dsdy != mDsdy || dtdy != mDtdy) {
- if (logSurfaceTrace) Slog.v(SURFACE_TAG, "setMatrix(" + dsdx + "," + dtdx + ","
- + dsdy + "," + dtdy + "): OLD:" + this + ". Called by "
- + Debug.getCallers(3));
- mDsdx = dsdx;
- mDtdx = dtdx;
- mDsdy = dsdy;
- mDtdy = dtdy;
- }
- super.setMatrix(dsdx, dtdx, dsdy, dtdy);
- }
-
- @Override
- public void hide() {
- if (mShown) {
- if (logSurfaceTrace) Slog.v(SURFACE_TAG, "hide: OLD:" + this + ". Called by "
- + Debug.getCallers(3));
- mShown = false;
- }
- super.hide();
- }
-
- @Override
- public void show() {
- if (!mShown) {
- if (logSurfaceTrace) Slog.v(SURFACE_TAG, "show: OLD:" + this + ". Called by "
- + Debug.getCallers(3));
- mShown = true;
- }
- super.show();
- }
-
- @Override
- public void destroy() {
- super.destroy();
- if (logSurfaceTrace) Slog.v(SURFACE_TAG, "destroy: " + this + ". Called by "
- + Debug.getCallers(3));
- synchronized (sSurfaces) {
- sSurfaces.remove(this);
- }
- }
-
- @Override
- public void release() {
- super.release();
- if (logSurfaceTrace) Slog.v(SURFACE_TAG, "release: " + this + ". Called by "
- + Debug.getCallers(3));
- synchronized (sSurfaces) {
- sSurfaces.remove(this);
- }
- }
-
- static void dumpAllSurfaces(PrintWriter pw, String header) {
- synchronized (sSurfaces) {
- final int N = sSurfaces.size();
- if (N <= 0) {
- return;
- }
- if (header != null) {
- pw.println(header);
- }
- pw.println("WINDOW MANAGER SURFACES (dumpsys window surfaces)");
- for (int i = 0; i < N; i++) {
- SurfaceTrace s = sSurfaces.get(i);
- pw.print(" Surface #"); pw.print(i); pw.print(": #");
- pw.print(Integer.toHexString(System.identityHashCode(s)));
- pw.print(" "); pw.println(s.mName);
- pw.print(" mLayerStack="); pw.print(s.mLayerStack);
- pw.print(" mLayer="); pw.println(s.mLayer);
- pw.print(" mShown="); pw.print(s.mShown); pw.print(" mAlpha=");
- pw.print(s.mSurfaceTraceAlpha); pw.print(" mIsOpaque=");
- pw.println(s.mIsOpaque);
- pw.print(" mPosition="); pw.print(s.mPosition.x); pw.print(",");
- pw.print(s.mPosition.y);
- 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(" 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(")");
- }
- }
- }
-
- @Override
- public String toString() {
- return "Surface " + Integer.toHexString(System.identityHashCode(this)) + " "
- + mName + " (" + mLayerStack + "): shown=" + mShown + " layer=" + mLayer
- + " alpha=" + mSurfaceTraceAlpha + " " + mPosition.x + "," + mPosition.y
- + " " + mSize.x + "x" + mSize.y
- + " crop=" + mWindowCrop.toShortString()
- + " opaque=" + mIsOpaque
- + " (" + mDsdx + "," + mDtdx + "," + mDsdy + "," + mDtdy + ")";
- }
- }
-
void preserveSurfaceLocked() {
if (mDestroyPreservedSurfaceUponRedraw) {
return;
}
- if (mSurfaceControl != null) {
- SurfaceControl.openTransaction();
- try {
- mSurfaceControl.setLayer(WINDOW_FREEZE_LAYER);
- } finally {
- SurfaceControl.closeTransaction();
- }
+ if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(mWin, "SET FREEZE LAYER", null);
+ if (mSurfaceController != null) {
+ mSurfaceController.setLayer(mAnimLayer + 1);
}
mDestroyPreservedSurfaceUponRedraw = true;
mSurfaceDestroyDeferred = true;
@@ -806,9 +555,9 @@
mDestroyPreservedSurfaceUponRedraw = false;
}
- SurfaceControl createSurfaceLocked() {
+ WindowSurfaceController createSurfaceLocked() {
final WindowState w = mWin;
- if (mSurfaceControl == null) {
+ if (mSurfaceController == null) {
if (DEBUG_ANIM || DEBUG_ORIENTATION) Slog.i(TAG,
"createSurface " + this + ": mDrawState=DRAW_PENDING");
mDrawState = DRAW_PENDING;
@@ -882,11 +631,6 @@
}
// We may abort, so initialize to defaults.
- mSurfaceShown = false;
- mSurfaceLayer = 0;
- mSurfaceAlpha = 0;
- mSurfaceX = 0;
- mSurfaceY = 0;
w.mLastSystemDecorRect.set(0, 0, 0, 0);
mHasClipRect = false;
mClipRect.set(0, 0, 0, 0);
@@ -894,8 +638,6 @@
// Set up surface control with initial size.
try {
- mSurfaceW = width;
- mSurfaceH = height;
final boolean isHwAccelerated = (attrs.flags & FLAG_HARDWARE_ACCELERATED) != 0;
final int format = isHwAccelerated ? PixelFormat.TRANSLUCENT : attrs.format;
@@ -912,24 +654,15 @@
flags |= SurfaceControl.OPAQUE;
}
- mSurfaceFormat = format;
- if (DEBUG_SURFACE_TRACE) {
- mSurfaceControl = new SurfaceTrace(
- mSession.mSurfaceSession,
- attrs.getTitle().toString(),
- width, height, format, flags);
- } else {
- mSurfaceControl = new SurfaceControl(
- mSession.mSurfaceSession,
+ mSurfaceController = new WindowSurfaceController(mSession.mSurfaceSession,
attrs.getTitle().toString(),
- width, height, format, flags);
- }
+ width, height, format, flags, this);
- w.mHasSurface = true;
+ w.setHasSurface(true);
if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
Slog.i(TAG, " CREATE SURFACE "
- + mSurfaceControl + " IN SESSION "
+ + mSurfaceController + " IN SESSION "
+ mSession.mSurfaceSession
+ ": pid=" + mSession.mPid + " format="
+ attrs.format + " flags=0x"
@@ -937,20 +670,20 @@
+ " / " + this);
}
} catch (OutOfResourcesException e) {
- w.mHasSurface = false;
+ w.setHasSurface(false);
Slog.w(TAG, "OutOfResourcesException creating surface");
mService.reclaimSomeSurfaceMemoryLocked(this, "create", true);
mDrawState = NO_SURFACE;
return null;
} catch (Exception e) {
- w.mHasSurface = false;
+ w.setHasSurface(false);
Slog.e(TAG, "Exception creating surface", e);
mDrawState = NO_SURFACE;
return null;
}
if (WindowManagerService.localLOGV) {
- Slog.v(TAG, "Got surface: " + mSurfaceControl
+ Slog.v(TAG, "Got surface: " + mSurfaceController
+ ", set left=" + w.mFrame.left + " top=" + w.mFrame.top
+ ", animLayer=" + mAnimLayer);
}
@@ -964,44 +697,29 @@
}
// Start a new transaction and apply position & offset.
- SurfaceControl.openTransaction();
- try {
- mSurfaceX = left;
- mSurfaceY = top;
+ final int layerStack = w.getDisplayContent().getDisplay().getLayerStack();
+ if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
+ "POS " + left + ", " + top, null);
+ mSurfaceController.setPositionAndLayer(left, top, layerStack, mAnimLayer);
+ mLastHidden = true;
- try {
- mSurfaceControl.setPosition(left, top);
- mSurfaceLayer = mAnimLayer;
- final DisplayContent displayContent = w.getDisplayContent();
- if (displayContent != null) {
- mSurfaceControl.setLayerStack(displayContent.getDisplay().getLayerStack());
- }
- mSurfaceControl.setLayer(mAnimLayer);
- mSurfaceControl.setAlpha(0);
- mSurfaceShown = false;
- } catch (RuntimeException e) {
- Slog.w(TAG, "Error creating surface in " + w, e);
- mService.reclaimSomeSurfaceMemoryLocked(this, "create-init", true);
- }
- mLastHidden = true;
- } finally {
- SurfaceControl.closeTransaction();
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
- "<<< CLOSE TRANSACTION createSurfaceLocked");
- }
if (WindowManagerService.localLOGV) Slog.v(
TAG, "Created surface " + this);
}
- return mSurfaceControl;
+ return mSurfaceController;
}
void destroySurfaceLocked() {
- if (mWin.mAppToken != null && mWin == mWin.mAppToken.startingWindow) {
- mWin.mAppToken.startingDisplayed = false;
+ final AppWindowToken wtoken = mWin.mAppToken;
+ if (wtoken != null) {
+ wtoken.mHasSavedSurface = false;
+ wtoken.mAnimatingWithSavedSurface = false;
+ if (mWin == wtoken.startingWindow) {
+ wtoken.startingDisplayed = false;
+ }
}
- if (mSurfaceControl != null) {
-
+ if (mSurfaceController != null) {
int i = mWin.mChildWindows.size();
while (i > 0) {
i--;
@@ -1017,10 +735,10 @@
e.fillInStackTrace();
}
Slog.w(TAG, "Window " + this + " destroying surface "
- + mSurfaceControl + ", session " + mSession, e);
+ + mSurfaceController + ", session " + mSession, e);
}
if (mSurfaceDestroyDeferred) {
- if (mSurfaceControl != null && mPendingDestroySurface != mSurfaceControl) {
+ if (mSurfaceController != null && mPendingDestroySurface != mSurfaceController) {
if (mPendingDestroySurface != null) {
if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
RuntimeException e = null;
@@ -1030,31 +748,33 @@
}
WindowManagerService.logSurface(mWin, "DESTROY PENDING", e);
}
- mPendingDestroySurface.destroy();
+ mPendingDestroySurface.destroyInTransaction();
}
- mPendingDestroySurface = mSurfaceControl;
+ mPendingDestroySurface = mSurfaceController;
}
} else {
- if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
- RuntimeException e = null;
- if (!WindowManagerService.HIDE_STACK_CRAWLS) {
- e = new RuntimeException();
- e.fillInStackTrace();
- }
- WindowManagerService.logSurface(mWin, "DESTROY", e);
- }
- mSurfaceControl.destroy();
+ WindowManagerService.logSurface(mWin, "DESTROY", null);
+ destroySurface();
}
- mWallpaperControllerLocked.hideWallpapers(mWin);
+ // Don't hide wallpaper if we're deferring the surface destroy
+ // because of a surface change.
+ if (!(mSurfaceDestroyDeferred && mDestroyPreservedSurfaceUponRedraw)) {
+ mWallpaperControllerLocked.hideWallpapers(mWin);
+ }
} catch (RuntimeException e) {
Slog.w(TAG, "Exception thrown when destroying Window " + this
- + " surface " + mSurfaceControl + " session " + mSession
+ + " surface " + mSurfaceController + " session " + mSession
+ ": " + e.toString());
}
- mSurfaceShown = false;
- mSurfaceControl = null;
- mWin.mHasSurface = false;
+ // Whether the surface was preserved (and copied to mPendingDestroySurface) or not, it
+ // needs to be cleared to match the WindowState.mHasSurface state. It is also necessary
+ // so it can be recreated successfully in mPendingDestroySurface case.
+ mWin.setHasSurface(false);
+ if (mSurfaceController != null) {
+ mSurfaceController.setShown(false);
+ }
+ mSurfaceController = null;
mDrawState = NO_SURFACE;
}
}
@@ -1070,8 +790,12 @@
}
WindowManagerService.logSurface(mWin, "DESTROY PENDING", e);
}
- mPendingDestroySurface.destroy();
- mWallpaperControllerLocked.hideWallpapers(mWin);
+ mPendingDestroySurface.destroyInTransaction();
+ // Don't hide wallpaper if we're destroying a deferred surface
+ // after a surface mode change.
+ if (!mDestroyPreservedSurfaceUponRedraw) {
+ mWallpaperControllerLocked.hideWallpapers(mWin);
+ }
}
} catch (RuntimeException e) {
Slog.w(TAG, "Exception thrown when destroying Window "
@@ -1190,9 +914,7 @@
mDtDy = tmpFloats[Matrix.MSCALE_Y];
float x = tmpFloats[Matrix.MTRANS_X];
float y = tmpFloats[Matrix.MTRANS_Y];
- int w = frame.width();
- int h = frame.height();
- mWin.mShownFrame.set(x, y, x+w, y+h);
+ mWin.mShownPosition.set((int) x, (int) y);
// Now set the alpha... but because our current hardware
// can't do alpha transformation on a non-opaque surface,
@@ -1215,14 +937,6 @@
mShownAlpha *= appTransformation.getAlpha();
if (appTransformation.hasClipRect()) {
mClipRect.set(appTransformation.getClipRect());
- if (mWin.mHScale > 0) {
- mClipRect.left /= mWin.mHScale;
- mClipRect.right /= mWin.mHScale;
- }
- if (mWin.mVScale > 0) {
- mClipRect.top /= mWin.mVScale;
- mClipRect.bottom /= mWin.mVScale;
- }
mHasClipRect = true;
}
}
@@ -1285,15 +999,13 @@
mDtDy = tmpFloats[Matrix.MSCALE_Y];
float x = tmpFloats[Matrix.MTRANS_X];
float y = tmpFloats[Matrix.MTRANS_Y];
- int w = frame.width();
- int h = frame.height();
- mWin.mShownFrame.set(x, y, x + w, y + h);
+ mWin.mShownPosition.set((int) x, (int) y);
mShownAlpha = mAlpha;
} else {
- mWin.mShownFrame.set(mWin.mFrame);
+ mWin.mShownPosition.set(mWin.mFrame.left, mWin.mFrame.top);
if (mWin.mXOffset != 0 || mWin.mYOffset != 0) {
- mWin.mShownFrame.offset(mWin.mXOffset, mWin.mYOffset);
+ mWin.mShownPosition.offset(mWin.mXOffset, mWin.mYOffset);
}
mShownAlpha = mAlpha;
mHaveMatrix = false;
@@ -1344,11 +1056,7 @@
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
// Need to recompute a new system decor rect each time.
- if ((w.mAttrs.flags & LayoutParams.FLAG_SCALED) != 0) {
- // Currently can't do this cropping for scaled windows. We'll
- // just keep the crop rect the same as the source surface.
- w.mSystemDecorRect.set(0, 0, w.mRequestedWidth, w.mRequestedHeight);
- } else if (!w.isDefaultDisplay()) {
+ if (!w.isDefaultDisplay()) {
// On a different display there is no system decor. Crop the window
// by the screen boundaries.
w.mSystemDecorRect.set(0, 0, w.mCompatFrame.width(), w.mCompatFrame.height());
@@ -1373,8 +1081,10 @@
}
final boolean fullscreen = w.isFullscreen(displayInfo.appWidth, displayInfo.appHeight);
+ final boolean isFreeformResizing =
+ w.isDragResizing() && w.getResizeMode() == DRAG_RESIZE_MODE_FREEFORM;
final Rect clipRect = mTmpClipRect;
- if (w.isDragResizing()) {
+ if (isFreeformResizing) {
// When we're doing a drag-resizing, the surface is set up to cover full screen.
// Set the clip rect to be the same size so that we don't get any scaling.
clipRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
@@ -1383,7 +1093,6 @@
// avoid premature clipping with the system decor rect.
clipRect.set((mHasClipRect && !fullscreen) ? mClipRect : w.mSystemDecorRect);
}
-
// Expand the clip rect for surface insets.
final WindowManager.LayoutParams attrs = w.mAttrs;
clipRect.left -= attrs.surfaceInsets.left;
@@ -1397,69 +1106,59 @@
// clip rect extends outside the system decor rect.
clipRect.intersect(mClipRect);
}
-
// The clip rect was generated assuming (0,0) as the window origin,
// so we need to translate to match the actual surface coordinates.
clipRect.offset(attrs.surfaceInsets.left, attrs.surfaceInsets.top);
-
// We don't want to clip to stack bounds windows that are currently doing entrance
- // animation. This is necessary for docking operation, otherwise the window will be
- // suddenly cut off.
- if (!mAnimator.mAnimating) {
- adjustCropToStackBounds(w, clipRect);
+ // animation for docked window, otherwise the animating window will be suddenly cut off.
+
+ if (!(mAnimator.mAnimating && w.inDockedWorkspace())) {
+ adjustCropToStackBounds(w, clipRect, isFreeformResizing);
}
+ w.transformFromScreenToSurfaceSpace(clipRect);
+
if (!clipRect.equals(mLastClipRect)) {
mLastClipRect.set(clipRect);
- try {
- if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
- "CROP " + clipRect.toShortString(), null);
- if (clipRect.width() > 0 && clipRect.height() > 0) {
- mSurfaceControl.setWindowCrop(clipRect);
- mHiddenForCrop = false;
- } else {
- hide();
- mHiddenForCrop = true;
- }
- } catch (RuntimeException e) {
- Slog.w(TAG, "Error setting crop surface of " + w
- + " crop=" + clipRect.toShortString(), e);
- if (!recoveringMemory) {
- mService.reclaimSomeSurfaceMemoryLocked(this, "crop", true);
- }
- }
+ mSurfaceController.setCropInTransaction(clipRect, recoveringMemory);
}
}
- private void adjustCropToStackBounds(WindowState w, Rect clipRect) {
+ private void adjustCropToStackBounds(WindowState w, Rect clipRect, boolean isFreeformResizing) {
final AppWindowToken appToken = w.mAppToken;
+ final Task task = w.getTask();
// We don't apply the the stack bounds to the window that is being replaced, 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.
- if (appToken != null && appToken.mCropWindowsToStack && !appToken.mReplacingWindow) {
- TaskStack stack = w.getTask().mStack;
- stack.getBounds(mTmpStackBounds);
- final int surfaceX = (int) mSurfaceX;
- final int surfaceY = (int) mSurfaceY;
+ if (task != null && appToken.mCropWindowsToStack && !appToken.mWillReplaceWindow) {
+ TaskStack stack = task.mStack;
+ stack.getDimBounds(mTmpStackBounds);
+ // When we resize we use the big surface approach, which means we can't trust the
+ // window frame bounds anymore. Instead, the window will be placed at 0, 0, but to avoid
+ // hardcoding it, we use surface coordinates.
+ final int frameX = isFreeformResizing ? (int) mSurfaceController.getX() :
+ w.mFrame.left + mWin.mXOffset - w.getAttrs().surfaceInsets.left;
+ final int frameY = isFreeformResizing ? (int) mSurfaceController.getY() :
+ w.mFrame.top + mWin.mYOffset - w.getAttrs().surfaceInsets.top;
// 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, surfaceX + clipRect.left) - surfaceX);
+ Math.max(mTmpStackBounds.left, frameX + clipRect.left) - frameX);
clipRect.top = Math.max(0,
- Math.max(mTmpStackBounds.top, surfaceY + clipRect.top) - surfaceY);
+ Math.max(mTmpStackBounds.top, frameY + clipRect.top) - frameY);
clipRect.right = Math.max(0,
- Math.min(mTmpStackBounds.right, surfaceX + clipRect.right) - surfaceX);
+ Math.min(mTmpStackBounds.right, frameX + clipRect.right) - frameX);
clipRect.bottom = Math.max(0,
- Math.min(mTmpStackBounds.bottom, surfaceY + clipRect.bottom) - surfaceY);
+ Math.min(mTmpStackBounds.bottom, frameY + clipRect.bottom) - frameY);
}
}
void setSurfaceBoundariesLocked(final boolean recoveringMemory) {
final WindowState w = mWin;
- float left = w.mShownFrame.left;
- float top = w.mShownFrame.top;
+ float left = w.mShownPosition.x;
+ float top = w.mShownPosition.y;
int width;
int height;
@@ -1473,9 +1172,13 @@
// so that we don't need to reallocate during the process. This also prevents
// buffer drops due to size mismatch.
final DisplayInfo displayInfo = w.getDisplayInfo();
- if (displayInfo != null && w.isDragResizing()) {
+
+ // In freeform resize mode, put surface at 0/0.
+ if (w.isDragResizing() && w.getResizeMode() == DRAG_RESIZE_MODE_FREEFORM) {
left = 0;
top = 0;
+ }
+ if (displayInfo != null && w.isDragResizing()) {
width = displayInfo.logicalWidth;
height = displayInfo.logicalHeight;
} else {
@@ -1511,50 +1214,16 @@
left -= scale * attrs.surfaceInsets.left;
top -= scale * attrs.surfaceInsets.top;
- final boolean surfaceMoved = mSurfaceX != left || mSurfaceY != top;
- if (surfaceMoved) {
- mSurfaceX = left;
- mSurfaceY = top;
+ mSurfaceController.setPositionInTransaction(left, top, recoveringMemory);
+ mSurfaceResized = mSurfaceController.setSizeInTransaction(width, height,
+ mDsDx * w.mHScale, mDtDx * w.mVScale,
+ mDsDy * w.mHScale, mDtDy * w.mVScale,
+ recoveringMemory);
- try {
- if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
- "POS " + left + ", " + top, null);
- mSurfaceControl.setPosition(left, top);
- } catch (RuntimeException e) {
- Slog.w(TAG, "Error positioning surface of " + w
- + " pos=(" + left + "," + top + ")", e);
- if (!recoveringMemory) {
- mService.reclaimSomeSurfaceMemoryLocked(this, "position", true);
- }
- }
- }
-
- final boolean surfaceResized = mSurfaceW != width || mSurfaceH != height;
- if (surfaceResized) {
- mSurfaceW = width;
- mSurfaceH = height;
- mSurfaceResized = true;
-
- try {
- if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
- "SIZE " + width + "x" + height, null);
- mSurfaceControl.setSize(width, height);
- mSurfaceControl.setMatrix(
- mDsDx * w.mHScale, mDtDx * w.mVScale,
- mDsDy * w.mHScale, mDtDy * w.mVScale);
- mAnimator.setPendingLayoutChanges(w.getDisplayId(),
- WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
- w.handleFlagDimBehind();
- } catch (RuntimeException e) {
- // If something goes wrong with the surface (such
- // as running out of memory), don't take down the
- // entire system.
- Slog.e(TAG, "Error resizing surface of " + w
- + " size=(" + width + "x" + height + ")", e);
- if (!recoveringMemory) {
- mService.reclaimSomeSurfaceMemoryLocked(this, "size", true);
- }
- }
+ if (mSurfaceResized) {
+ mAnimator.setPendingLayoutChanges(w.getDisplayId(),
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
+ w.applyDimLayerIfNeeded();
}
updateSurfaceWindowCrop(recoveringMemory);
@@ -1562,7 +1231,7 @@
void prepareSurfaceLocked(final boolean recoveringMemory) {
final WindowState w = mWin;
- if (mSurfaceControl == null) {
+ if (mSurfaceController == null || !mSurfaceController.hasSurface()) {
if (w.mOrientationChanging) {
if (DEBUG_ORIENTATION) {
Slog.v(TAG, "Orientation change skips hidden " + w);
@@ -1580,9 +1249,9 @@
if (mIsWallpaper && !mWin.mWallpaperVisible) {
// Wallpaper is no longer visible and there is no wp target => hide it.
- hide();
+ hide("prepareSurfaceLocked");
} else if (w.mAttachedHidden || !w.isOnScreen()) {
- hide();
+ hide("prepareSurfaceLocked");
mWallpaperControllerLocked.hideWallpapers(w);
// If we are waiting for this window to handle an
@@ -1615,51 +1284,38 @@
w.mLastHScale = w.mHScale;
w.mLastVScale = w.mVScale;
if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
- "control=" + mSurfaceControl +
+ "controller=" + mSurfaceController +
"alpha=" + mShownAlpha + " layer=" + mAnimLayer
+ " matrix=[" + mDsDx + "*" + w.mHScale
+ "," + mDtDx + "*" + w.mVScale
+ "][" + mDsDy + "*" + w.mHScale
+ "," + mDtDy + "*" + w.mVScale + "]", null);
- if (mSurfaceControl != null) {
- try {
- mSurfaceAlpha = mShownAlpha;
- mSurfaceControl.setAlpha(mShownAlpha);
- mSurfaceLayer = mAnimLayer;
- mSurfaceControl.setLayer(mAnimLayer);
- mSurfaceControl.setMatrix(
- mDsDx * w.mHScale, mDtDx * w.mVScale,
- mDsDy * w.mHScale, mDtDy * w.mVScale);
- if (mLastHidden && mDrawState == HAS_DRAWN && !mHiddenForCrop) {
- if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
- "SHOW (performLayout)", null);
- if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG, "Showing " + w
- + " during relayout");
- if (showSurfaceRobustlyLocked()) {
- mLastHidden = false;
- if (mIsWallpaper) {
- mWallpaperControllerLocked.dispatchWallpaperVisibility(w, true);
- }
- // This draw means the difference between unique content and mirroring.
- // Run another pass through performLayout to set mHasContent in the
- // LogicalDisplay.
- mAnimator.setPendingLayoutChanges(w.getDisplayId(),
- WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM);
- } else {
- w.mOrientationChanging = false;
- }
+ boolean prepared =
+ mSurfaceController.prepareToShowInTransaction(mShownAlpha, mAnimLayer,
+ mDsDx * w.mHScale, mDtDx * w.mVScale,
+ mDsDy * w.mHScale, mDtDy * w.mVScale,
+ recoveringMemory);
+
+ if (prepared && mLastHidden && mDrawState == HAS_DRAWN) {
+ if (showSurfaceRobustlyLocked()) {
+ mAnimator.requestRemovalOfReplacedWindows(w);
+ mLastHidden = false;
+ if (mIsWallpaper) {
+ mWallpaperControllerLocked.dispatchWallpaperVisibility(w, true);
}
- if (mSurfaceControl != null) {
- w.mToken.hasVisible = true;
- }
- } catch (RuntimeException e) {
- Slog.w(TAG, "Error updating surface in " + w, e);
- if (!recoveringMemory) {
- mService.reclaimSomeSurfaceMemoryLocked(this, "update", true);
- }
+ // This draw means the difference between unique content and mirroring.
+ // Run another pass through performLayout to set mHasContent in the
+ // LogicalDisplay.
+ mAnimator.setPendingLayoutChanges(w.getDisplayId(),
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM);
+ } else {
+ w.mOrientationChanging = false;
}
}
+ if (mSurfaceController != null && mSurfaceController.hasSurface()) {
+ w.mToken.hasVisible = true;
+ }
} else {
if (DEBUG_ANIM && isAnimating()) {
Slog.v(TAG, "prepareSurface: No changes in animation for " + this);
@@ -1684,52 +1340,31 @@
}
void setTransparentRegionHintLocked(final Region region) {
- if (mSurfaceControl == null) {
+ if (mSurfaceController == null) {
Slog.w(TAG, "setTransparentRegionHint: null mSurface after mHasSurface true");
return;
}
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setTransparentRegion");
- SurfaceControl.openTransaction();
+ mSurfaceController.setTransparentRegionHint(region);
+ }
+
+ void setWallpaperOffset(Point shownPosition) {
+ final LayoutParams attrs = mWin.getAttrs();
+ final int left = shownPosition.x - attrs.surfaceInsets.left;
+ final int top = shownPosition.y - attrs.surfaceInsets.top;
+
try {
- if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(mWin,
- "transparentRegionHint=" + region, null);
- mSurfaceControl.setTransparentRegionHint(region);
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setWallpaperOffset");
+ SurfaceControl.openTransaction();
+ mSurfaceController.setPositionInTransaction(mWin.mFrame.left + left,
+ mWin.mFrame.top + top, false);
+ updateSurfaceWindowCrop(false);
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Error positioning surface of " + mWin
+ + " pos=(" + left + "," + top + ")", e);
} finally {
SurfaceControl.closeTransaction();
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
- "<<< CLOSE TRANSACTION setTransparentRegion");
- }
- }
-
- void setWallpaperOffset(RectF shownFrame) {
- final LayoutParams attrs = mWin.getAttrs();
- final int left = ((int) shownFrame.left) - attrs.surfaceInsets.left;
- final int top = ((int) shownFrame.top) - attrs.surfaceInsets.top;
- if (mSurfaceX != left || mSurfaceY != top) {
- if (mAnimating) {
- // If this window (or its app token) is animating, then the position
- // of the surface will be re-computed on the next animation frame.
- // We can't poke it directly here because it depends on whatever
- // transformation is being applied by the animation.
- return;
- }
- mSurfaceX = left;
- mSurfaceY = top;
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setWallpaperOffset");
- SurfaceControl.openTransaction();
- try {
- if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(mWin,
- "POS " + left + ", " + top, null);
- mSurfaceControl.setPosition(mWin.mFrame.left + left, mWin.mFrame.top + top);
- updateSurfaceWindowCrop(false);
- } catch (RuntimeException e) {
- Slog.w(TAG, "Error positioning surface of " + mWin
- + " pos=(" + left + "," + top + ")", e);
- } finally {
- SurfaceControl.closeTransaction();
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
- "<<< CLOSE TRANSACTION setWallpaperOffset");
- }
+ "<<< CLOSE TRANSACTION setWallpaperOffset");
}
}
@@ -1741,7 +1376,7 @@
* @return True if format was succesfully changed, false otherwise
*/
boolean tryChangeFormatInPlaceLocked() {
- if (mSurfaceControl == null) {
+ if (mSurfaceController == null) {
return false;
}
final LayoutParams attrs = mWin.getAttrs();
@@ -1755,35 +1390,17 @@
}
void setOpaqueLocked(boolean isOpaque) {
- if (mSurfaceControl == null) {
+ if (mSurfaceController == null) {
return;
}
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setOpaqueLocked");
- SurfaceControl.openTransaction();
- try {
- if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(mWin, "isOpaque=" + isOpaque,
- null);
- mSurfaceControl.setOpaque(isOpaque);
- } finally {
- SurfaceControl.closeTransaction();
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION setOpaqueLocked");
- }
+ mSurfaceController.setOpaque(isOpaque);
}
void setSecureLocked(boolean isSecure) {
- if (mSurfaceControl == null) {
+ if (mSurfaceController == null) {
return;
}
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setSecureLocked");
- SurfaceControl.openTransaction();
- try {
- if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(mWin, "isSecure=" + isSecure,
- null);
- mSurfaceControl.setSecure(isSecure);
- } finally {
- SurfaceControl.closeTransaction();
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION setSecureLocked");
- }
+ mSurfaceController.setSecure(isSecure);
}
// This must be called while inside a transaction.
@@ -1811,8 +1428,6 @@
+ Debug.getCallers(3));
}
if (mDrawState == READY_TO_SHOW && mWin.isReadyForDisplayIgnoringKeyguard()) {
- if (SHOW_TRANSACTIONS || DEBUG_ORIENTATION)
- WindowManagerService.logSurface(mWin, "SHOW (performShowLocked)", null);
if (DEBUG_VISIBILITY || (DEBUG_STARTING_WINDOW &&
mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING)) {
Slog.v(TAG, "Showing " + this
@@ -1844,7 +1459,7 @@
WindowState c = mWin.mChildWindows.get(i);
if (c.mAttachedHidden) {
c.mAttachedHidden = false;
- if (c.mWinAnimator.mSurfaceControl != null) {
+ if (c.mWinAnimator.mSurfaceController != null) {
c.mWinAnimator.performShowLocked();
// It hadn't been shown, which means layout not
// performed on it, so now we want to make sure to
@@ -1859,10 +1474,12 @@
}
}
- if (mWin.mAttrs.type != TYPE_APPLICATION_STARTING
- && mWin.mAppToken != null) {
+ if (mWin.mAttrs.type != TYPE_APPLICATION_STARTING && mWin.mAppToken != null) {
mWin.mAppToken.firstWindowDrawn = true;
+ // We now have a good window to show, remove dead placeholders
+ mWin.mAppToken.removeAllDeadWindows();
+
if (mWin.mAppToken.startingData != null) {
if (WindowManagerService.DEBUG_STARTING_WINDOW ||
WindowManagerService.DEBUG_ANIM) Slog.v(TAG,
@@ -1878,12 +1495,8 @@
}
mWin.mAppToken.updateReportedVisibilityLocked();
}
-
- mWin.maybeRemoveReplacedWindow();
-
return true;
}
-
return false;
}
@@ -1896,22 +1509,16 @@
* @return Returns true if the surface was successfully shown.
*/
private boolean showSurfaceRobustlyLocked() {
- try {
- mSurfaceShown = true;
- mSurfaceControl.show();
- if (mWin.mTurnOnScreen) {
- if (DEBUG_VISIBILITY) Slog.v(TAG, "Show surface turning screen on: " + mWin);
- mWin.mTurnOnScreen = false;
- mAnimator.mBulkUpdateParams |= SET_TURN_ON_SCREEN;
- }
- return true;
- } catch (RuntimeException e) {
- Slog.w(TAG, "Failure showing surface " + mSurfaceControl + " in " + mWin, e);
+ boolean shown = mSurfaceController.showRobustlyInTransaction();
+ if (!shown)
+ return false;
+
+ if (mWin.mTurnOnScreen) {
+ if (DEBUG_VISIBILITY) Slog.v(TAG, "Show surface turning screen on: " + mWin);
+ mWin.mTurnOnScreen = false;
+ mAnimator.mBulkUpdateParams |= SET_TURN_ON_SCREEN;
}
-
- mService.reclaimSomeSurfaceMemoryLocked(this, "show", true);
-
- return false;
+ return true;
}
void applyEnterAnimationLocked() {
@@ -2045,21 +1652,14 @@
pw.print(" "); mTransformation.printShortString(pw);
pw.println();
}
- if (mSurfaceControl != null) {
- if (dumpAll) {
- pw.print(prefix); pw.print("mSurface="); pw.println(mSurfaceControl);
- pw.print(prefix); pw.print("mDrawState=");
- pw.print(drawStateToString());
- pw.print(" mLastHidden="); pw.println(mLastHidden);
- }
- pw.print(prefix); pw.print("Surface: shown="); pw.print(mSurfaceShown);
- pw.print(" layer="); pw.print(mSurfaceLayer);
- pw.print(" alpha="); pw.print(mSurfaceAlpha);
- pw.print(" rect=("); pw.print(mSurfaceX);
- pw.print(","); pw.print(mSurfaceY);
- pw.print(") "); pw.print(mSurfaceW);
- pw.print(" x "); pw.println(mSurfaceH);
+ if (mSurfaceController != null) {
+ mSurfaceController.dump(pw, prefix, dumpAll);
}
+ if (dumpAll) {
+ pw.print(prefix); pw.print("mDrawState="); pw.print(drawStateToString());
+ pw.print(prefix); pw.print(" mLastHidden="); pw.println(mLastHidden);
+ }
+
if (mPendingDestroySurface != null) {
pw.print(prefix); pw.print("mPendingDestroySurface=");
pw.println(mPendingDestroySurface);
@@ -2091,4 +1691,20 @@
sb.append('}');
return sb.toString();
}
+
+ void reclaimSomeSurfaceMemory(String operation, boolean secure) {
+ mService.reclaimSomeSurfaceMemoryLocked(this, operation, secure);
+ }
+
+ boolean getShown() {
+ if (mSurfaceController != null) {
+ return mSurfaceController.getShown();
+ }
+ return false;
+ }
+
+ void destroySurface() {
+ mSurfaceController.destroyInTransaction();
+ mSurfaceController = null;
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
new file mode 100644
index 0000000..f8b8d6c
--- /dev/null
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -0,0 +1,610 @@
+/*
+ * 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.wm;
+
+import static com.android.server.wm.WindowManagerService.SHOW_TRANSACTIONS;
+import static com.android.server.wm.WindowManagerService.SHOW_LIGHT_TRANSACTIONS;
+import static com.android.server.wm.WindowManagerService.SHOW_SURFACE_ALLOC;
+import static com.android.server.wm.WindowManagerService.DEBUG_SURFACE_TRACE;
+import static com.android.server.wm.WindowManagerService.HIDE_STACK_CRAWLS;
+import static com.android.server.wm.WindowManagerService.DEBUG_VISIBILITY;
+
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.os.IBinder;
+import android.os.Debug;
+import android.view.Surface;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+import android.view.WindowContentFrameStats;
+import android.view.Surface.OutOfResourcesException;
+
+import android.util.Slog;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+class WindowSurfaceController {
+ static final String TAG = "WindowSurfaceController";
+
+ final WindowStateAnimator mAnimator;
+
+ private SurfaceControl mSurfaceControl;
+
+ private boolean mSurfaceShown = false;
+ private float mSurfaceX = 0;
+ private float mSurfaceY = 0;
+ private float mSurfaceW = 0;
+ private float mSurfaceH = 0;
+
+ private float mSurfaceAlpha = 0;
+
+ private int mSurfaceLayer = 0;
+
+ // Surface flinger doesn't support crop rectangles where width or height is non-positive.
+ // However, we need to somehow handle the situation where the cropping would completely hide
+ // the window. We achieve this by explicitly hiding the surface and not letting it be shown.
+ private boolean mHiddenForCrop = false;
+
+ private final String title;
+
+ public WindowSurfaceController(SurfaceSession s,
+ String name, int w, int h, int format, int flags, WindowStateAnimator animator) {
+ mAnimator = animator;
+
+ mSurfaceW = w;
+ mSurfaceH = h;
+
+ title = name;
+
+ if (DEBUG_SURFACE_TRACE) {
+ mSurfaceControl = new SurfaceTrace(
+ s, name, w, h, format, flags);
+ } else {
+ mSurfaceControl = new SurfaceControl(
+ s, name, w, h, format, flags);
+ }
+ }
+
+
+ void logSurface(String msg, RuntimeException where) {
+ String str = " SURFACE " + msg + ": " + title;
+ if (where != null) {
+ Slog.i(TAG, str, where);
+ } else {
+ Slog.i(TAG, str);
+ }
+ }
+
+ void hideInTransaction(String reason) {
+ if (SHOW_TRANSACTIONS) logSurface("HIDE ( " + reason + " )", null);
+ if (mSurfaceControl != null) {
+ mSurfaceShown = false;
+ try {
+ mSurfaceControl.hide();
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Exception hiding surface in " + this);
+ }
+ }
+ }
+
+ void setPositionAndLayer(float left, float top, int layerStack, int layer) {
+ SurfaceControl.openTransaction();
+ try {
+ mSurfaceX = left;
+ mSurfaceY = top;
+
+ try {
+ mSurfaceControl.setPosition(left, top);
+ mSurfaceControl.setLayerStack(layerStack);
+
+ mSurfaceControl.setLayer(layer);
+ mSurfaceControl.setAlpha(0);
+ mSurfaceShown = false;
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Error creating surface in " + this, e);
+ mAnimator.reclaimSomeSurfaceMemory("create-init", true);
+ }
+ } finally {
+ SurfaceControl.closeTransaction();
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
+ "<<< CLOSE TRANSACTION setPositionAndLayer");
+ }
+ }
+
+ void destroyInTransaction() {
+ if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
+ RuntimeException e = null;
+ if (!HIDE_STACK_CRAWLS) {
+ e = new RuntimeException();
+ e.fillInStackTrace();
+ }
+ }
+ try {
+ mSurfaceControl.destroy();
+ mSurfaceShown = false;
+ mSurfaceControl = null;
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Error destroying surface in: " + this, e);
+ }
+ }
+
+ void setCropInTransaction(Rect clipRect, boolean recoveringMemory) {
+ if (SHOW_TRANSACTIONS) logSurface(
+ "CROP " + clipRect.toShortString(), null);
+ try {
+ if (clipRect.width() > 0 && clipRect.height() > 0) {
+ mSurfaceControl.setWindowCrop(clipRect);
+ mHiddenForCrop = false;
+ } else {
+ hideInTransaction("setCrop");
+ mHiddenForCrop = true;
+ }
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Error setting crop surface of " + this
+ + " crop=" + clipRect.toShortString(), e);
+ if (!recoveringMemory) {
+ mAnimator.reclaimSomeSurfaceMemory("crop", true);
+ }
+ }
+ }
+
+ void setLayer(int layer) {
+ if (mSurfaceControl != null) {
+ SurfaceControl.openTransaction();
+ try {
+ mSurfaceControl.setLayer(layer);
+ } finally {
+ SurfaceControl.closeTransaction();
+ }
+ }
+ }
+
+ void setPositionInTransaction(float left, float top, boolean recoveringMemory) {
+ final boolean surfaceMoved = mSurfaceX != left || mSurfaceY != top;
+ if (surfaceMoved) {
+ mSurfaceX = left;
+ mSurfaceY = top;
+
+ try {
+ mSurfaceControl.setPosition(left, top);
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Error positioning surface of " + this
+ + " pos=(" + left + "," + top + ")", e);
+ if (!recoveringMemory) {
+ mAnimator.reclaimSomeSurfaceMemory("position", true);
+ }
+ }
+ }
+ }
+
+ boolean setSizeInTransaction(int width, int height, float dsdx, float dtdx, float dsdy, float dtdy,
+ boolean recoveringMemory) {
+ final boolean surfaceResized = mSurfaceW != width || mSurfaceH != height;
+ if (surfaceResized) {
+ mSurfaceW = width;
+ mSurfaceH = height;
+
+ try {
+ if (SHOW_TRANSACTIONS) logSurface(
+ "SIZE " + width + "x" + height, null);
+ mSurfaceControl.setSize(width, height);
+ mSurfaceControl.setMatrix(
+ dsdx, dtdx, dsdy, dtdy);
+ } catch (RuntimeException e) {
+ // If something goes wrong with the surface (such
+ // as running out of memory), don't take down the
+ // entire system.
+ Slog.e(TAG, "Error resizing surface of " + title
+ + " size=(" + width + "x" + height + ")", e);
+ if (!recoveringMemory) {
+ mAnimator.reclaimSomeSurfaceMemory("size", true);
+ }
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ boolean prepareToShowInTransaction(float alpha, int layer, float dsdx, float dtdx, float dsdy,
+ float dtdy, boolean recoveringMemory) {
+ if (mSurfaceControl != null) {
+ try {
+ mSurfaceAlpha = alpha;
+ mSurfaceControl.setAlpha(alpha);
+ mSurfaceLayer = layer;
+ mSurfaceControl.setLayer(layer);
+ mSurfaceControl.setMatrix(
+ dsdx, dtdx, dsdy, dtdy);
+
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Error updating surface in " + title, e);
+ if (!recoveringMemory) {
+ mAnimator.reclaimSomeSurfaceMemory("update", true);
+ }
+ return false;
+ }
+ }
+ return true;
+ }
+
+ void setTransparentRegionHint(final Region region) {
+ if (mSurfaceControl == null) {
+ Slog.w(TAG, "setTransparentRegionHint: null mSurface after mHasSurface true");
+ return;
+ }
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setTransparentRegion");
+ SurfaceControl.openTransaction();
+ try {
+ mSurfaceControl.setTransparentRegionHint(region);
+ } finally {
+ SurfaceControl.closeTransaction();
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
+ "<<< CLOSE TRANSACTION setTransparentRegion");
+ }
+ }
+
+ void setOpaque(boolean isOpaque) {
+ if (SHOW_TRANSACTIONS) logSurface("isOpaque=" + isOpaque,
+ null);
+
+ if (mSurfaceControl == null) {
+ return;
+ }
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setOpaqueLocked");
+ SurfaceControl.openTransaction();
+ try {
+ mSurfaceControl.setOpaque(isOpaque);
+ } finally {
+ SurfaceControl.closeTransaction();
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION setOpaqueLocked");
+ }
+ }
+
+ void setSecure(boolean isSecure) {
+ if (SHOW_TRANSACTIONS) logSurface("isSecure=" + isSecure,
+ null);
+
+ if (mSurfaceControl == null) {
+ return;
+ }
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setSecureLocked");
+ SurfaceControl.openTransaction();
+ try {
+ mSurfaceControl.setSecure(isSecure);
+ } finally {
+ SurfaceControl.closeTransaction();
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION setSecureLocked");
+ }
+ }
+
+ boolean showRobustlyInTransaction() {
+ if (SHOW_TRANSACTIONS) logSurface(
+ "SHOW (performLayout)", null);
+ if (DEBUG_VISIBILITY) Slog.v(TAG, "Showing " + this
+ + " during relayout");
+
+ if (mHiddenForCrop) {
+ return false;
+ }
+
+ try {
+ mSurfaceShown = true;
+ mSurfaceControl.show();
+ return true;
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Failure showing surface " + mSurfaceControl + " in " + this, e);
+ }
+
+ mAnimator.reclaimSomeSurfaceMemory("show", true);
+
+ return false;
+ }
+
+ void deferTransactionUntil(IBinder handle, long frame) {
+ // TODO: Logging
+ mSurfaceControl.deferTransactionUntil(handle, frame);
+ }
+
+ boolean clearWindowContentFrameStats() {
+ if (mSurfaceControl == null) {
+ return false;
+ }
+ return mSurfaceControl.clearContentFrameStats();
+ }
+
+ boolean getWindowContentFrameStats(WindowContentFrameStats outStats) {
+ if (mSurfaceControl == null) {
+ return false;
+ }
+ return mSurfaceControl.getContentFrameStats(outStats);
+ }
+
+
+ boolean hasSurface() {
+ return mSurfaceControl != null;
+ }
+
+ IBinder getHandle() {
+ if (mSurfaceControl == null) {
+ return null;
+ }
+ return mSurfaceControl.getHandle();
+ }
+
+ void getSurface(Surface outSurface) {
+ outSurface.copyFrom(mSurfaceControl);
+ }
+
+ int getLayer() {
+ return mSurfaceLayer;
+ }
+
+ boolean getShown() {
+ return mSurfaceShown;
+ }
+
+ void setShown(boolean surfaceShown) {
+ mSurfaceShown = surfaceShown;
+ }
+
+ float getX() {
+ return mSurfaceX;
+ }
+
+ float getY() {
+ return mSurfaceY;
+ }
+
+ public void dump(PrintWriter pw, String prefix, boolean dumpAll) {
+ if (dumpAll) {
+ pw.print(prefix); pw.print("mSurface="); pw.println(mSurfaceControl);
+ }
+ pw.print(prefix); pw.print("Surface: shown="); pw.print(mSurfaceShown);
+ pw.print(" layer="); pw.print(mSurfaceLayer);
+ pw.print(" alpha="); pw.print(mSurfaceAlpha);
+ pw.print(" rect=("); pw.print(mSurfaceX);
+ pw.print(","); pw.print(mSurfaceY);
+ pw.print(") "); pw.print(mSurfaceW);
+ pw.print(" x "); pw.println(mSurfaceH);
+ }
+
+ @Override
+ public String toString() {
+ return mSurfaceControl.toString();
+ }
+
+ static class SurfaceTrace extends SurfaceControl {
+ private final static String SURFACE_TAG = "SurfaceTrace";
+ private final static boolean LOG_SURFACE_TRACE = DEBUG_SURFACE_TRACE;
+ final static ArrayList<SurfaceTrace> sSurfaces = new ArrayList<SurfaceTrace>();
+
+ private float mSurfaceTraceAlpha = 0;
+ private int mLayer;
+ private final PointF mPosition = new PointF();
+ private final Point mSize = new Point();
+ private final Rect mWindowCrop = new Rect();
+ private boolean mShown = false;
+ private int mLayerStack;
+ private boolean mIsOpaque;
+ private float mDsdx, mDtdx, mDsdy, mDtdy;
+ private final String mName;
+
+ public SurfaceTrace(SurfaceSession s,
+ String name, int w, int h, int format, int flags)
+ throws OutOfResourcesException {
+ super(s, name, w, h, format, flags);
+ mName = name != null ? name : "Not named";
+ mSize.set(w, h);
+ if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "ctor: " + this + ". Called by "
+ + Debug.getCallers(3));
+ synchronized (sSurfaces) {
+ sSurfaces.add(0, this);
+ }
+ }
+
+ @Override
+ public void setAlpha(float alpha) {
+ if (mSurfaceTraceAlpha != alpha) {
+ if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setAlpha(" + alpha + "): OLD:" + this +
+ ". Called by " + Debug.getCallers(3));
+ mSurfaceTraceAlpha = alpha;
+ }
+ super.setAlpha(alpha);
+ }
+
+ @Override
+ public void setLayer(int zorder) {
+ if (zorder != mLayer) {
+ if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setLayer(" + zorder + "): OLD:" + this
+ + ". Called by " + Debug.getCallers(3));
+ mLayer = zorder;
+ }
+ super.setLayer(zorder);
+
+ synchronized (sSurfaces) {
+ sSurfaces.remove(this);
+ int i;
+ for (i = sSurfaces.size() - 1; i >= 0; i--) {
+ SurfaceTrace s = sSurfaces.get(i);
+ if (s.mLayer < zorder) {
+ break;
+ }
+ }
+ sSurfaces.add(i + 1, this);
+ }
+ }
+
+ @Override
+ public void setPosition(float x, float y) {
+ if (x != mPosition.x || y != mPosition.y) {
+ if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setPosition(" + x + "," + y + "): OLD:"
+ + this + ". Called by " + Debug.getCallers(3));
+ mPosition.set(x, y);
+ }
+ super.setPosition(x, y);
+ }
+
+ @Override
+ public void setSize(int w, int h) {
+ if (w != mSize.x || h != mSize.y) {
+ if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setSize(" + w + "," + h + "): OLD:"
+ + this + ". Called by " + Debug.getCallers(3));
+ mSize.set(w, h);
+ }
+ super.setSize(w, h);
+ }
+
+ @Override
+ public void setWindowCrop(Rect crop) {
+ if (crop != null) {
+ if (!crop.equals(mWindowCrop)) {
+ if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setWindowCrop("
+ + crop.toShortString() + "): OLD:" + this + ". Called by "
+ + Debug.getCallers(3));
+ mWindowCrop.set(crop);
+ }
+ }
+ super.setWindowCrop(crop);
+ }
+
+ @Override
+ public void setLayerStack(int layerStack) {
+ if (layerStack != mLayerStack) {
+ if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setLayerStack(" + layerStack + "): OLD:"
+ + this + ". Called by " + Debug.getCallers(3));
+ mLayerStack = layerStack;
+ }
+ super.setLayerStack(layerStack);
+ }
+
+ @Override
+ public void setOpaque(boolean isOpaque) {
+ if (isOpaque != mIsOpaque) {
+ if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setOpaque(" + isOpaque + "): OLD:"
+ + this + ". Called by " + Debug.getCallers(3));
+ mIsOpaque = isOpaque;
+ }
+ super.setOpaque(isOpaque);
+ }
+
+ @Override
+ public void setSecure(boolean isSecure) {
+ super.setSecure(isSecure);
+ }
+
+ @Override
+ public void setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
+ if (dsdx != mDsdx || dtdx != mDtdx || dsdy != mDsdy || dtdy != mDtdy) {
+ if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setMatrix(" + dsdx + "," + dtdx + ","
+ + dsdy + "," + dtdy + "): OLD:" + this + ". Called by "
+ + Debug.getCallers(3));
+ mDsdx = dsdx;
+ mDtdx = dtdx;
+ mDsdy = dsdy;
+ mDtdy = dtdy;
+ }
+ super.setMatrix(dsdx, dtdx, dsdy, dtdy);
+ }
+
+ @Override
+ public void hide() {
+ if (mShown) {
+ if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "hide: OLD:" + this + ". Called by "
+ + Debug.getCallers(3));
+ mShown = false;
+ }
+ super.hide();
+ }
+
+ @Override
+ public void show() {
+ if (!mShown) {
+ if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "show: OLD:" + this + ". Called by "
+ + Debug.getCallers(3));
+ mShown = true;
+ }
+ super.show();
+ }
+
+ @Override
+ public void destroy() {
+ super.destroy();
+ if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "destroy: " + this + ". Called by "
+ + Debug.getCallers(3));
+ synchronized (sSurfaces) {
+ sSurfaces.remove(this);
+ }
+ }
+
+ @Override
+ public void release() {
+ super.release();
+ if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "release: " + this + ". Called by "
+ + Debug.getCallers(3));
+ synchronized (sSurfaces) {
+ sSurfaces.remove(this);
+ }
+ }
+
+ static void dumpAllSurfaces(PrintWriter pw, String header) {
+ synchronized (sSurfaces) {
+ final int N = sSurfaces.size();
+ if (N <= 0) {
+ return;
+ }
+ if (header != null) {
+ pw.println(header);
+ }
+ pw.println("WINDOW MANAGER SURFACES (dumpsys window surfaces)");
+ for (int i = 0; i < N; i++) {
+ SurfaceTrace s = sSurfaces.get(i);
+ pw.print(" Surface #"); pw.print(i); pw.print(": #");
+ pw.print(Integer.toHexString(System.identityHashCode(s)));
+ pw.print(" "); pw.println(s.mName);
+ pw.print(" mLayerStack="); pw.print(s.mLayerStack);
+ pw.print(" mLayer="); pw.println(s.mLayer);
+ pw.print(" mShown="); pw.print(s.mShown); pw.print(" mAlpha=");
+ pw.print(s.mSurfaceTraceAlpha); pw.print(" mIsOpaque=");
+ pw.println(s.mIsOpaque);
+ pw.print(" mPosition="); pw.print(s.mPosition.x); pw.print(",");
+ pw.print(s.mPosition.y);
+ 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(" 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(")");
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Surface " + Integer.toHexString(System.identityHashCode(this)) + " "
+ + mName + " (" + mLayerStack + "): shown=" + mShown + " layer=" + mLayer
+ + " alpha=" + mSurfaceTraceAlpha + " " + mPosition.x + "," + mPosition.y
+ + " " + mSize.x + "x" + mSize.y
+ + " crop=" + mWindowCrop.toShortString()
+ + " opaque=" + mIsOpaque
+ + " (" + mDsdx + "," + mDtdx + "," + mDsdy + "," + mDtdy + ")";
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 112646a..3b57634 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -17,6 +17,7 @@
import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.WindowManagerService.DEBUG;
import static com.android.server.wm.WindowManagerService.DEBUG_ADD_REMOVE;
+import static com.android.server.wm.WindowManagerService.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerService.DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.WindowManagerService.DEBUG_LAYOUT;
import static com.android.server.wm.WindowManagerService.DEBUG_LAYOUT_REPEATS;
@@ -111,13 +112,34 @@
private int mPreferredModeId = 0;
private boolean mTraversalScheduled;
+ private int mDeferDepth = 0;
public WindowSurfacePlacer(WindowManagerService service) {
mService = service;
mWallpaperControllerLocked = mService.mWallpaperControllerLocked;
}
+ /**
+ * See {@link WindowManagerService#deferSurfaceLayout()}
+ */
+ void deferLayout() {
+ mDeferDepth++;
+ }
+
+ /**
+ * See {@link WindowManagerService#continueSurfaceLayout()}
+ */
+ void continueLayout() {
+ mDeferDepth--;
+ if (mDeferDepth <= 0) {
+ performSurfacePlacement();
+ }
+ }
+
final void performSurfacePlacement() {
+ if (mDeferDepth > 0) {
+ return;
+ }
int loopCount = 6;
do {
mTraversalScheduled = false;
@@ -133,8 +155,7 @@
if (DEBUG) {
throw new RuntimeException("Recursive call!");
}
- Slog.w(TAG,
- "performLayoutAndPlaceSurfacesLocked called while in layout. Callers="
+ Slog.w(TAG, "performLayoutAndPlaceSurfacesLocked called while in layout. Callers="
+ Debug.getCallers(3));
return;
}
@@ -340,6 +361,10 @@
// Don't remove this window until rotation has completed.
continue;
}
+ // Discard the saved surface if window size is changed, it can't be reused.
+ if (win.mAppToken != null) {
+ win.mAppToken.destroySavedSurfaces();
+ }
win.reportResized();
mService.mResizingWindows.remove(i);
}
@@ -371,7 +396,9 @@
if (mWallpaperControllerLocked.isWallpaperTarget(win)) {
wallpaperDestroyed = true;
}
- win.mWinAnimator.destroySurfaceLocked();
+ if (!win.shouldSaveSurface()) {
+ win.mWinAnimator.destroySurfaceLocked();
+ }
} while (i > 0);
mService.mDestroySurface.clear();
}
@@ -642,7 +669,7 @@
handleNotObscuredLocked(w, innerDw, innerDh);
}
- w.handleFlagDimBehind();
+ w.applyDimLayerIfNeeded();
if (isDefaultDisplay && obscuredChanged
&& mWallpaperControllerLocked.isWallpaperTarget(w) && w.isVisibleLw()) {
@@ -717,7 +744,8 @@
/*
* Updates the shown frame before we set up the surface. This is needed because
* the resizing could change the top-left position (in addition to size) of the
- * window. setSurfaceBoundariesLocked uses mShownFrame to position the surface.
+ * window. setSurfaceBoundariesLocked uses mShownPosition to position the
+ * surface.
*/
winAnimator.computeShownFrameLocked();
winAnimator.setSurfaceBoundariesLocked(recoveringMemory);
@@ -744,7 +772,7 @@
+ ", isAnimating=" + winAnimator.isAnimating());
if (!w.isDrawnLw()) {
Slog.v(TAG, "Not displayed: s="
- + winAnimator.mSurfaceControl
+ + winAnimator.mSurfaceController
+ " pv=" + w.mPolicyVisibility
+ " mDrawState=" + winAnimator.drawStateToString()
+ " ah=" + w.mAttachedHidden
@@ -752,7 +780,7 @@
+ " a=" + winAnimator.mAnimating);
}
}
- if (w != atoken.startingWindow) {
+ if (w != atoken.startingWindow && !w.mAppDied) {
if (!atoken.mAppAnimator.freezingScreen || !w.mAppFreezing) {
atoken.numInterestingWindows++;
if (w.isDrawnLw()) {
@@ -904,6 +932,13 @@
win.prelayout();
mService.mPolicy.layoutWindowLw(win, null);
win.mLayoutSeq = seq;
+
+ // Window frames may have changed. Update dim layer with the new bounds.
+ final Task task = win.getTask();
+ if (task != null) {
+ displayContent.mDimLayerController.updateDimLayer(task);
+ }
+
if (DEBUG_LAYOUT) Slog.v(TAG,
" LAYOUT: mFrame="
+ win.mFrame + " mContainingFrame="
@@ -958,15 +993,14 @@
}
}
- // Window frames may have changed. Tell the input dispatcher about it.
+ // Window frames may have changed. Tell the input dispatcher about it.
mService.mInputMonitor.setUpdateInputWindowsNeededLw();
if (updateInputWindows) {
mService.mInputMonitor.updateInputWindowsLw(false /*force*/);
}
mService.mPolicy.finishLayoutLw();
- mService.mH.obtainMessage(UPDATE_DOCKED_STACK_DIVIDER,
- DOCK_DIVIDER_NO_FORCE_UPDATE, UNUSED, displayContent).sendToTarget();
+ mService.mH.sendEmptyMessage(UPDATE_DOCKED_STACK_DIVIDER);
}
/**
@@ -1146,14 +1180,30 @@
for (int j = 0; j < windowsCount; j++) {
appAnimator.mAllAppWinAnimators.add(wtoken.allAppWindows.get(j).mWinAnimator);
}
- mService.mAnimator.mAnimating |= appAnimator.showAllWindowsLocked();
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
+ ">>> OPEN TRANSACTION handleAppTransitionReadyLocked()");
+ SurfaceControl.openTransaction();
+ try {
+ mService.mAnimator.mAnimating |= appAnimator.showAllWindowsLocked();
+ } finally {
+ SurfaceControl.closeTransaction();
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
+ "<<< CLOSE TRANSACTION handleAppTransitionReadyLocked()");
+ }
mService.mAnimator.mAppWindowAnimating |= appAnimator.isAnimating();
int topOpeningLayer = 0;
if (animLp != null) {
int layer = -1;
for (int j = 0; j < wtoken.windows.size(); j++) {
- WindowState win = wtoken.windows.get(j);
+ final WindowState win = wtoken.windows.get(j);
+ // Clearing the mExiting flag before entering animation. It will be set to true
+ // if app window is removed, or window relayout to invisible. We don't want to
+ // clear it out for windows that get replaced, because the animation depends on
+ // the flag to remove the replaced window.
+ if (win.mAppToken == null || !win.mAppToken.mWillReplaceWindow) {
+ win.mExiting = false;
+ }
if (win.mWinAnimator.mAnimLayer > layer) {
layer = win.mWinAnimator.mAnimLayer;
}
@@ -1164,6 +1214,8 @@
}
}
createThumbnailAppAnimator(transit, wtoken, topOpeningLayer, topClosingLayer);
+
+ wtoken.restoreSavedSurfaces();
}
AppWindowAnimator openingAppAnimator = (topOpeningApp == null) ? null :
@@ -1191,9 +1243,7 @@
true /*updateInputWindows*/);
mService.mFocusMayChange = false;
mService.notifyActivityDrawnForKeyguard();
- return FINISH_LAYOUT_REDO_LAYOUT
- | FINISH_LAYOUT_REDO_CONFIG;
-
+ return FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG;
}
private boolean transitionGoodToGo(int appsCount) {
@@ -1209,11 +1259,21 @@
+ wtoken.allDrawn + " startingDisplayed="
+ wtoken.startingDisplayed + " startingMoved="
+ wtoken.startingMoved);
+
+ if (wtoken.mHasSavedSurface || wtoken.mAnimatingWithSavedSurface) {
+ continue;
+ }
if (!wtoken.allDrawn && !wtoken.startingDisplayed && !wtoken.startingMoved) {
return false;
}
}
+ // We also need to wait for the specs to be fetched, if needed.
+ if (mService.mAppTransition.isFetchingAppTransitionsSpecs()) {
+ if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "isFetchingAppTransitionSpecs=true");
+ return false;
+ }
+
// If the wallpaper is visible, we need to check it's ready too.
return !mWallpaperControllerLocked.isWallpaperVisible() ||
mWallpaperControllerLocked.wallpaperTransitionReady();
@@ -1421,6 +1481,7 @@
final int taskId = appToken.mTask.mTaskId;
Bitmap thumbnailHeader = mService.mAppTransition.getAppTransitionThumbnailHeader(taskId);
if (thumbnailHeader == null || thumbnailHeader.getConfig() == Bitmap.Config.ALPHA_8) {
+ if (DEBUG_APP_TRANSITIONS) Slog.d(TAG, "No thumbnail header bitmap for: " + taskId);
return;
}
// This thumbnail animation is very special, we need to have
diff --git a/services/core/jni/com_android_server_UsbMidiDevice.cpp b/services/core/jni/com_android_server_UsbMidiDevice.cpp
index 06b9bc3..e12a016 100644
--- a/services/core/jni/com_android_server_UsbMidiDevice.cpp
+++ b/services/core/jni/com_android_server_UsbMidiDevice.cpp
@@ -43,12 +43,26 @@
jint card, jint device)
{
char path[100];
+ int fd;
+ const int kMaxRetries = 10;
+ const int kSleepMicroseconds = 2000;
snprintf(path, sizeof(path), "/dev/snd/controlC%d", card);
- int fd = open(path, O_RDWR);
- if (fd < 0) {
- ALOGE("could not open %s", path);
- return 0;
+ // This control device may not have been created yet. So we should
+ // try to open it several times to prevent intermittent failure
+ // from a race condition.
+ int retryCounter = 0;
+ while ((fd = open(path, O_RDWR)) < 0) {
+ if (++retryCounter > kMaxRetries) {
+ ALOGE("timed out after %d tries, could not open %s", retryCounter, path);
+ return 0;
+ } else {
+ ALOGW("attempt #%d, could not open %s", retryCounter, path);
+ // Increase the sleep interval each time.
+ // 10 retries will total 2 * sum(1..10) = 110 milliseconds.
+ // Typically the device should be ready in 5-10 milliseconds.
+ usleep(kSleepMicroseconds * retryCounter);
+ }
}
struct snd_rawmidi_info info;
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 1d4f047..be190cb 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -199,6 +199,7 @@
void setShowTouches(bool enabled);
void setInteractive(bool interactive);
void reloadCalibration();
+ void setPointerIconShape(int32_t iconId);
/* --- InputReaderPolicyInterface implementation --- */
@@ -237,6 +238,8 @@
/* --- PointerControllerPolicyInterface implementation --- */
virtual void loadPointerResources(PointerResources* outResources);
+ virtual void loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources);
+ virtual int32_t getDefaultPointerIconId();
private:
sp<InputManager> mInputManager;
@@ -779,6 +782,15 @@
InputReaderConfiguration::CHANGE_TOUCH_AFFINE_TRANSFORMATION);
}
+void NativeInputManager::setPointerIconShape(int32_t iconId) {
+ AutoMutex _l(mLock);
+ sp<PointerController> controller = mLocked.pointerController.promote();
+ if (controller != NULL) {
+ // Use 0 (the default icon) for ARROW.
+ controller->updatePointerShape(iconId);
+ }
+}
+
TouchAffineTransformation NativeInputManager::getTouchAffineTransformation(
JNIEnv *env, jfloatArray matrixArr) {
ScopedFloatArrayRO matrix(env, matrixArr);
@@ -1029,6 +1041,19 @@
&outResources->spotAnchor);
}
+void NativeInputManager::loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources) {
+ JNIEnv* env = jniEnv();
+
+ for (int iconId = POINTER_ICON_STYLE_CONTEXT_MENU; iconId <= POINTER_ICON_STYLE_GRABBING;
+ ++iconId) {
+ loadSystemIconAsSprite(env, mContextObj, iconId, &((*outResources)[iconId]));
+ }
+ loadSystemIconAsSprite(env, mContextObj, POINTER_ICON_STYLE_NULL, &((*outResources)[POINTER_ICON_STYLE_NULL]));
+}
+
+int32_t NativeInputManager::getDefaultPointerIconId() {
+ return POINTER_ICON_STYLE_ARROW;
+}
// ----------------------------------------------------------------------------
@@ -1367,6 +1392,11 @@
im->getInputManager()->getDispatcher()->monitor();
}
+static void nativeSetPointerIconShape(JNIEnv* /* env */, jclass /* clazz */, jlong ptr, jint iconId) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+ im->setPointerIconShape(iconId);
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gInputManagerMethods[] = {
@@ -1425,6 +1455,8 @@
(void*) nativeDump },
{ "nativeMonitor", "(J)V",
(void*) nativeMonitor },
+ { "nativeSetPointerIconShape", "(JI)V",
+ (void*) nativeSetPointerIconShape },
};
#define FIND_CLASS(var, className) \
diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp
index 89b2a47..01acdef 100644
--- a/services/core/jni/com_android_server_tv_TvInputHal.cpp
+++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp
@@ -414,12 +414,9 @@
return NO_ERROR;
}
if (Surface::isValid(connection.mSurface)) {
- connection.mSurface.clear();
- }
- if (connection.mSurface != NULL) {
connection.mSurface->setSidebandStream(NULL);
- connection.mSurface.clear();
}
+ connection.mSurface.clear();
if (connection.mThread != NULL) {
connection.mThread->shutdown();
connection.mThread.clear();
@@ -616,6 +613,9 @@
return BAD_VALUE;
}
sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
+ if (!Surface::isValid(surface)) {
+ return BAD_VALUE;
+ }
return tvInputHal->addOrUpdateStream(deviceId, streamId, surface);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 0860f02..1e32f60 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -16,6 +16,8 @@
package com.android.server.devicepolicy;
+import com.google.android.collect.Sets;
+
import static android.Manifest.permission.MANAGE_CA_CERTIFICATES;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
@@ -88,6 +90,7 @@
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.UserManagerInternal;
import android.os.storage.StorageManager;
import android.provider.ContactsContract.QuickContact;
import android.provider.ContactsInternal;
@@ -183,8 +186,6 @@
private static final int MONITORING_CERT_NOTIFICATION_ID = R.string.ssl_ca_cert_warning;
private static final int PROFILE_WIPED_NOTIFICATION_ID = 1001;
- private static final boolean DBG = false;
-
private static final String ATTR_PERMISSION_PROVIDER = "permission-provider";
private static final String ATTR_SETUP_COMPLETE = "setup-complete";
private static final String ATTR_PERMISSION_POLICY = "permission-policy";
@@ -211,8 +212,6 @@
DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_CONFIG_CELL_BROADCASTS);
DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA);
- DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_UNMUTE_MICROPHONE);
- DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_ADJUST_VOLUME);
DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_SMS);
DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_FUN);
DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_SAFE_BOOT);
@@ -274,6 +273,7 @@
final Injector mInjector;
final IPackageManager mIPackageManager;
final UserManager mUserManager;
+ final UserManagerInternal mUserManagerInternal;
final LocalService mLocalService;
@@ -357,8 +357,10 @@
getSendingUserId());
if (Intent.ACTION_BOOT_COMPLETED.equals(action)
|| ACTION_EXPIRED_PASSWORD_NOTIFICATION.equals(action)) {
- if (DBG) Slog.v(LOG_TAG, "Sending password expiration notifications for action "
- + action + " for user " + userHandle);
+ if (VERBOSE_LOG) {
+ Slog.v(LOG_TAG, "Sending password expiration notifications for action "
+ + action + " for user " + userHandle);
+ }
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -1012,9 +1014,10 @@
}
}
+ // DO NOT call it while taking the "this" lock, which could cause a dead lock.
private void handlePackagesChanged(String packageName, int userHandle) {
boolean removed = false;
- if (DBG) Slog.d(LOG_TAG, "Handling package changes for user " + userHandle);
+ if (VERBOSE_LOG) Slog.d(LOG_TAG, "Handling package changes for user " + userHandle);
DevicePolicyData policy = getUserData(userHandle);
synchronized (this) {
for (int i = policy.mAdminList.size() - 1; i >= 0; i--) {
@@ -1038,7 +1041,6 @@
}
if (removed) {
validatePasswordOwnerLocked(policy);
- syncDeviceCapabilitiesLocked(policy);
saveSettingsLocked(policy.mUserHandle);
}
@@ -1057,6 +1059,13 @@
}
}
}
+ if (removed) {
+ synchronized (mUserManagerInternal.getUserRestrictionsLock()) {
+ synchronized (DevicePolicyManagerService.this) {
+ mUserManagerInternal.updateEffectiveUserRestrictionsLR(userHandle);
+ }
+ }
+ }
}
/**
@@ -1079,6 +1088,10 @@
return UserManager.get(mContext);
}
+ UserManagerInternal getUserManagerInternal() {
+ return LocalServices.getService(UserManagerInternal.class);
+ }
+
NotificationManager getNotificationManager() {
return mContext.getSystemService(NotificationManager.class);
}
@@ -1205,6 +1218,10 @@
Settings.Secure.putInt(mContext.getContentResolver(), name, value);
}
+ int settingsGlobalGetInt(String name, int def) {
+ return Settings.Global.getInt(mContext.getContentResolver(), name, def);
+ }
+
void settingsGlobalPutInt(String name, int value) {
Settings.Global.putInt(mContext.getContentResolver(), name, value);
}
@@ -1233,6 +1250,7 @@
mOwners = Preconditions.checkNotNull(injector.newOwners());
mUserManager = Preconditions.checkNotNull(injector.getUserManager());
+ mUserManagerInternal = Preconditions.checkNotNull(injector.getUserManagerInternal());
mIPackageManager = Preconditions.checkNotNull(injector.getIPackageManager());
mLocalService = new LocalService();
@@ -1327,10 +1345,13 @@
synchronized (this) {
mOwners.load();
findOwnerComponentIfNecessaryLocked();
+ migrateUserRestrictionsIfNecessaryLocked();
// TODO PO may not have a class name either due to b/17652534. Address that too.
updateDeviceOwnerLocked();
+
+ // TODO Notify UM to update restrictions (?)
}
}
@@ -1350,14 +1371,113 @@
if (doComponent == null) {
Slog.e(LOG_TAG, "Device-owner isn't registered as device-admin");
} else {
- mOwners.setDeviceOwner(
+ mOwners.setDeviceOwnerWithRestrictionsMigrated(
doComponent,
mOwners.getDeviceOwnerName(),
- mOwners.getDeviceOwnerUserId());
+ mOwners.getDeviceOwnerUserId(),
+ !mOwners.getDeviceOwnerUserRestrictionsNeedsMigration());
mOwners.writeDeviceOwner();
+ if (VERBOSE_LOG) {
+ Log.v(LOG_TAG, "Device owner component filled in");
+ }
}
}
+ /**
+ * We didn't use to persist user restrictions for each owners but only persisted in user
+ * manager.
+ */
+ private void migrateUserRestrictionsIfNecessaryLocked() {
+ boolean migrated = false;
+ // Migrate for the DO. Basically all restrictions should be considered to be set by DO,
+ // except for the "system controlled" ones.
+ if (mOwners.getDeviceOwnerUserRestrictionsNeedsMigration()) {
+ if (VERBOSE_LOG) {
+ Log.v(LOG_TAG, "Migrating DO user restrictions");
+ }
+ migrated = true;
+
+ // Migrate user 0 restrictions to DO, except for "system" restrictions.
+ final ActiveAdmin deviceOwnerAdmin = getDeviceOwnerAdminLocked();
+
+ migrateUserRestrictionsForUser(UserHandle.SYSTEM, deviceOwnerAdmin,
+ /* exceptionList =*/ UserRestrictionsUtils.SYSTEM_CONTROLLED_USER_RESTRICTIONS);
+
+ mOwners.setDeviceOwnerUserRestrictionsMigrated();
+ }
+
+ // Migrate for POs. We have a few more exceptions.
+ final Set<String> normalExceptionList = Sets.newArraySet(
+ UserManager.DISALLOW_OUTGOING_CALLS,
+ UserManager.DISALLOW_SMS);
+ normalExceptionList.addAll(UserRestrictionsUtils.SYSTEM_CONTROLLED_USER_RESTRICTIONS);
+
+ final Set<String> managedExceptionList = new ArraySet<>(normalExceptionList.size() + 1);
+ managedExceptionList.addAll(normalExceptionList);
+ managedExceptionList.add(UserManager.DISALLOW_WALLPAPER);
+
+ for (UserInfo ui : mUserManager.getUsers()) {
+ final int userId = ui.id;
+ if (mOwners.getProfileOwnerUserRestrictionsNeedsMigration(userId)) {
+ if (userId != UserHandle.USER_SYSTEM) {
+ if (VERBOSE_LOG) {
+ Log.v(LOG_TAG, "Migrating PO user restrictions for user " + userId);
+ }
+ migrated = true;
+
+ final ActiveAdmin profileOwnerAdmin = getProfileOwnerAdminLocked(userId);
+
+ final Set<String> exceptionList =
+ ui.isManagedProfile() ? managedExceptionList : normalExceptionList;
+
+ migrateUserRestrictionsForUser(ui.getUserHandle(), profileOwnerAdmin,
+ exceptionList);
+ }
+
+ mOwners.setProfileOwnerUserRestrictionsMigrated(userId);
+ }
+ }
+ if (VERBOSE_LOG && migrated) {
+ Log.v(LOG_TAG, "User restrictions migrated.");
+ }
+ }
+
+ private void migrateUserRestrictionsForUser(UserHandle user, ActiveAdmin admin,
+ Set<String> exceptionList) {
+ final Bundle origRestrictions = mUserManagerInternal.getBaseUserRestrictions(
+ user.getIdentifier());
+
+ final Bundle newSystemRestrictions = new Bundle();
+ final Bundle newOwnerRestrictions = new Bundle();
+
+ for (String key : origRestrictions.keySet()) {
+ if (!origRestrictions.getBoolean(key)) {
+ continue;
+ }
+ if (exceptionList.contains(key)) {
+ newSystemRestrictions.putBoolean(key, true);
+ } else {
+ newOwnerRestrictions.putBoolean(key, true);
+ }
+ }
+
+ if (VERBOSE_LOG) {
+ Log.v(LOG_TAG, "origRestrictions=" + origRestrictions);
+ Log.v(LOG_TAG, "newSystemRestrictions=" + newSystemRestrictions);
+ Log.v(LOG_TAG, "newOwnerRestrictions=" + newOwnerRestrictions);
+ }
+ mUserManagerInternal.setBaseUserRestrictionsByDpmsForMigration(user.getIdentifier(),
+ newSystemRestrictions);
+
+ if (admin != null) {
+ admin.ensureUserRestrictions().clear();
+ admin.ensureUserRestrictions().putAll(newOwnerRestrictions);
+ } else {
+ Slog.w(LOG_TAG, "ActiveAdmin for DO/PO not found. user=" + user.getIdentifier());
+ }
+ saveSettingsLocked(user.getIdentifier());
+ }
+
private ComponentName findAdminComponentWithPackageLocked(String packageName, int userId) {
final DevicePolicyData policy = getUserData(userId);
final int n = policy.mAdminList.size();
@@ -1373,7 +1493,7 @@
nFound++;
}
}
- if (nFound > 0) {
+ if (nFound > 1) {
Slog.w(LOG_TAG, "Multiple DA found; assume the first one is DO.");
}
return found;
@@ -1571,7 +1691,7 @@
}
}
- void removeActiveAdminLocked(final ComponentName adminReceiver, int userHandle) {
+ void removeActiveAdminLocked(final ComponentName adminReceiver, final int userHandle) {
final ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
if (admin != null) {
synchronized (this) {
@@ -1590,7 +1710,6 @@
policy.mAdminList.remove(admin);
policy.mAdminMap.remove(adminReceiver);
validatePasswordOwnerLocked(policy);
- syncDeviceCapabilitiesLocked(policy);
if (doProxyCleanup) {
resetGlobalProxyLocked(getUserData(userHandle));
}
@@ -1598,6 +1717,12 @@
updateMaximumTimeToLockLocked(policy);
policy.mRemovingAdmins.remove(adminReceiver);
}
+ synchronized (mUserManagerInternal.getUserRestrictionsLock()) {
+ synchronized (DevicePolicyManagerService.this) {
+ mUserManagerInternal.updateEffectiveUserRestrictionsLR(
+ userHandle);
+ }
+ }
}
});
}
@@ -1636,6 +1761,9 @@
? mInjector.getDevicePolicyFilePathForSystemUser() + DEVICE_POLICIES_XML
: new File(mInjector.environmentGetUserSystemDirectory(userHandle),
DEVICE_POLICIES_XML).getAbsolutePath();
+ if (VERBOSE_LOG) {
+ Log.v(LOG_TAG, "Opening " + base);
+ }
return new JournaledFile(new File(base), new File(base + ".tmp"));
}
@@ -1808,7 +1936,8 @@
try {
DeviceAdminInfo dai = findAdmin(
ComponentName.unflattenFromString(name), userHandle);
- if (DBG && (UserHandle.getUserId(dai.getActivityInfo().applicationInfo.uid)
+ if (VERBOSE_LOG
+ && (UserHandle.getUserId(dai.getActivityInfo().applicationInfo.uid)
!= userHandle)) {
Slog.w(LOG_TAG, "findAdmin returned an incorrect uid "
+ dai.getActivityInfo().applicationInfo.uid + " for user "
@@ -1907,7 +2036,6 @@
}
validatePasswordOwnerLocked(policy);
- syncDeviceCapabilitiesLocked(policy);
updateMaximumTimeToLockLocked(policy);
updateLockTaskPackagesLocked(policy.mLockTaskPackages, userHandle);
if (policy.mStatusBarDisabled) {
@@ -1974,29 +2102,6 @@
}
}
- /**
- * Pushes down policy information to the system for any policies related to general device
- * capabilities that need to be enforced by lower level services (e.g. Camera services).
- */
- void syncDeviceCapabilitiesLocked(DevicePolicyData policy) {
- // Ensure the status of the camera is synced down to the system. Interested native services
- // should monitor this value and act accordingly.
- String cameraPropertyForUser = SYSTEM_PROP_DISABLE_CAMERA_PREFIX + policy.mUserHandle;
- boolean systemState = mInjector.systemPropertiesGetBoolean(cameraPropertyForUser, false);
- boolean cameraDisabled = getCameraDisabled(null, policy.mUserHandle);
- if (cameraDisabled != systemState) {
- long token = mInjector.binderClearCallingIdentity();
- try {
- String value = cameraDisabled ? "1" : "0";
- if (DBG) Slog.v(LOG_TAG, "Change in camera state ["
- + cameraPropertyForUser + "] = " + value);
- mInjector.systemPropertiesSet(cameraPropertyForUser, value);
- } finally {
- mInjector.binderRestoreCallingIdentity(token);
- }
- }
- }
-
@VisibleForTesting
void systemReady(int phase) {
if (!mHasFeature) {
@@ -3494,7 +3599,7 @@
final UserHandle caller = mInjector.binderGetCallingUserHandle();
// If there is a profile owner, redirect to that; otherwise query the device owner.
ComponentName aliasChooser = getProfileOwner(caller.getIdentifier());
- if (aliasChooser == null && caller.isOwner()) {
+ if (aliasChooser == null && caller.isSystem()) {
ActiveAdmin deviceOwnerAdmin = getDeviceOwnerAdminLocked();
if (deviceOwnerAdmin != null) {
aliasChooser = deviceOwnerAdmin.info.getComponent();
@@ -3700,7 +3805,10 @@
return;
}
enforceCrossUserPermission(userHandle);
- enforceNotManagedProfile(userHandle, "set the active password");
+ // Managed Profile password can only be changed when per user encryption is present.
+ if (!mContext.getSystemService(StorageManager.class).isPerUserEncryptionEnabled()) {
+ enforceNotManagedProfile(userHandle, "set the active password");
+ }
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BIND_DEVICE_ADMIN, null);
@@ -4209,13 +4317,6 @@
}
/**
- * The system property used to share the state of the camera. The native camera service
- * is expected to read this property and act accordingly. The userId should be appended
- * to this key.
- */
- public static final String SYSTEM_PROP_DISABLE_CAMERA_PREFIX = "sys.secpolicy.camera.off_";
-
- /**
* Disables all device cameras according to the specified admin.
*/
@Override
@@ -4232,7 +4333,16 @@
ap.disableCamera = disabled;
saveSettingsLocked(userHandle);
}
- syncDeviceCapabilitiesLocked(getUserData(userHandle));
+ }
+ // Tell the user manager that the restrictions have changed.
+ synchronized (mUserManagerInternal.getUserRestrictionsLock()) {
+ synchronized (this) {
+ if (isDeviceOwner(who)) {
+ mUserManagerInternal.updateEffectiveUserRestrictionsForAllUsersLR();
+ } else {
+ mUserManagerInternal.updateEffectiveUserRestrictionsLR(userHandle);
+ }
+ }
}
}
@@ -4250,7 +4360,13 @@
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
return (admin != null) ? admin.disableCamera : false;
}
+ // First, see if DO has set it. If so, it's device-wide.
+ final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
+ if (deviceOwner != null && deviceOwner.disableCamera) {
+ return true;
+ }
+ // Then check each device admin on the user.
DevicePolicyData policy = getUserData(userHandle);
// Determine whether or not the device camera is disabled for any active admins.
final int N = policy.mAdminList.size();
@@ -4284,7 +4400,6 @@
ap.disabledKeyguardFeatures = which;
saveSettingsLocked(userHandle);
}
- syncDeviceCapabilitiesLocked(getUserData(userHandle));
}
}
@@ -4510,8 +4625,10 @@
}
UserHandle callingUser = mInjector.binderGetCallingUserHandle();
// Check if this is the profile owner who is calling
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ final ActiveAdmin admin =
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
synchronized (this) {
+ admin.userRestrictions = null;
clearUserPoliciesLocked(callingUser);
final int userId = callingUser.getIdentifier();
mOwners.removeProfileOwner(userId);
@@ -4530,38 +4647,18 @@
final long ident = mInjector.binderClearCallingIdentity();
try {
- clearUserRestrictions(userHandle);
mIPackageManager.updatePermissionFlagsForAllApps(
PackageManager.FLAG_PERMISSION_POLICY_FIXED,
0 /* flagValues */, userHandle.getIdentifier());
+ synchronized (mUserManagerInternal.getUserRestrictionsLock()) {
+ mUserManagerInternal.updateEffectiveUserRestrictionsLR(userHandle.getIdentifier());
+ }
} catch (RemoteException re) {
} finally {
mInjector.binderRestoreCallingIdentity(ident);
}
}
-
- private void clearUserRestrictions(UserHandle userHandle) {
- Bundle userRestrictions = mUserManager.getUserRestrictions();
- mUserManager.setUserRestrictions(new Bundle(), userHandle);
- if (userRestrictions.getBoolean(UserManager.DISALLOW_ADJUST_VOLUME)) {
- try {
- mInjector.getIAudioService().setMasterMute(true, 0, mContext.getPackageName(),
- userHandle.getIdentifier());
- } catch (RemoteException e) {
- // Not much we can do here.
- }
- }
- if (userRestrictions.getBoolean(UserManager.DISALLOW_UNMUTE_MICROPHONE)) {
- try {
- mInjector.getIAudioService().setMicrophoneMute(true, mContext.getPackageName(),
- userHandle.getIdentifier());
- } catch (RemoteException e) {
- // Not much we can do here.
- }
- }
- }
-
@Override
public boolean hasUserSetupCompleted() {
return hasUserSetupCompleted(UserHandle.getCallingUserId());
@@ -4703,10 +4800,14 @@
if (info.isGuest()) {
throw new IllegalStateException("Cannot set a profile owner on a guest");
}
- if (getProfileOwner(userHandle) != null) {
+ if (mOwners.hasProfileOwner(userHandle)) {
throw new IllegalStateException("Trying to set the profile owner, but profile owner "
+ "is already set.");
}
+ if (mOwners.hasDeviceOwner() && mOwners.getDeviceOwnerUserId() == userHandle) {
+ throw new IllegalStateException("Trying to set the profile owner, but the user "
+ + "already has a device owner.");
+ }
int callingUid = mInjector.binderGetCallingUid();
if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
if (hasUserSetupCompleted(userHandle) &&
@@ -4736,6 +4837,10 @@
throw new IllegalStateException("Trying to set the device owner, but device owner "
+ "is already set.");
}
+ if (mOwners.hasProfileOwner(userId)) {
+ throw new IllegalStateException("Trying to set the device owner, but the user already "
+ + "has a profile owner.");
+ }
if (!mUserManager.isUserRunning(new UserHandle(userId))) {
throw new IllegalStateException("User not running: " + userId);
}
@@ -4933,7 +5038,6 @@
DeviceAdminInfo.USES_POLICY_DISABLE_KEYGUARD_FEATURES);
ap.trustAgentInfos.put(agent.flattenToString(), new TrustAgentInfo(args));
saveSettingsLocked(userHandle);
- syncDeviceCapabilitiesLocked(getUserData(userHandle));
}
}
@@ -5499,96 +5603,58 @@
}
}
+ // DO NOT call it while taking the "this" lock, which could cause a dead lock.
@Override
- public void setUserRestriction(ComponentName who, String key, boolean enabled) {
+ public void setUserRestriction(ComponentName who, String key, boolean enabledFromThisOwner) {
Preconditions.checkNotNull(who, "ComponentName is null");
final int userHandle = mInjector.userHandleGetCallingUserId();
- final UserHandle user = new UserHandle(userHandle);
+ synchronized (mUserManagerInternal.getUserRestrictionsLock()) {
+ synchronized (this) {
+ ActiveAdmin activeAdmin =
+ getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ final boolean isDeviceOwner = isDeviceOwner(who);
+ if (!isDeviceOwner && userHandle != UserHandle.USER_SYSTEM
+ && DEVICE_OWNER_USER_RESTRICTIONS.contains(key)) {
+ throw new SecurityException(
+ "Profile owners cannot set user restriction " + key);
+ }
+ if (IMMUTABLE_USER_RESTRICTIONS.contains(key)) {
+ throw new SecurityException("User restriction " + key + " cannot be changed");
+ }
+
+ final long id = mInjector.binderClearCallingIdentity();
+ try {
+ // Save the restriction to ActiveAdmin.
+ // TODO When DO sets a restriction, it'll always be treated as device-wide.
+ // If there'll be a policy that can be set by both, we'll need scoping support,
+ // and need to have another Bundle in DO active admin to hold restrictions as
+ // PO.
+ activeAdmin.ensureUserRestrictions().putBoolean(key, enabledFromThisOwner);
+ saveSettingsLocked(userHandle);
+
+ // Tell UserManager the new value.
+ if (isDeviceOwner) {
+ mUserManagerInternal.updateEffectiveUserRestrictionsForAllUsersLR();
+ } else {
+ mUserManagerInternal.updateEffectiveUserRestrictionsLR(userHandle);
+ }
+ } finally {
+ mInjector.binderRestoreCallingIdentity(id);
+ }
+
+ sendChangedNotification(userHandle);
+ }
+ }
+ }
+
+ @Override
+ public Bundle getUserRestrictions(ComponentName who) {
+ Preconditions.checkNotNull(who, "ComponentName is null");
synchronized (this) {
- ActiveAdmin activeAdmin =
+ final ActiveAdmin activeAdmin =
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- boolean isDeviceOwner = isDeviceOwner(who);
- if (!isDeviceOwner && userHandle != UserHandle.USER_SYSTEM
- && DEVICE_OWNER_USER_RESTRICTIONS.contains(key)) {
- throw new SecurityException("Profile owners cannot set user restriction " + key);
- }
- if (IMMUTABLE_USER_RESTRICTIONS.contains(key)) {
- throw new SecurityException("User restriction " + key + " cannot be changed");
- }
- boolean alreadyRestricted = mUserManager.hasUserRestriction(key, user);
-
- long id = mInjector.binderClearCallingIdentity();
- try {
- if (enabled && !alreadyRestricted) {
- if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)) {
- mInjector.getIAudioService()
- .setMicrophoneMute(true, mContext.getPackageName(), userHandle);
- } else if (UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
- mInjector.getIAudioService()
- .setMasterMute(true, 0, mContext.getPackageName(), userHandle);
- } else if (UserManager.DISALLOW_CONFIG_WIFI.equals(key)) {
- mInjector.settingsSecurePutIntForUser(
- Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 0,
- userHandle);
- } else if (UserManager.DISALLOW_SHARE_LOCATION.equals(key)) {
- mInjector.settingsSecurePutIntForUser(
- Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF,
- userHandle);
- mInjector.settingsSecurePutStringForUser(
- Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "",
- userHandle);
- } else if (UserManager.DISALLOW_DEBUGGING_FEATURES.equals(key)) {
- // Only disable adb if changing for system user, since it is global
- // TODO: should this be admin user?
- if (userHandle == UserHandle.USER_SYSTEM) {
- mInjector.settingsGlobalPutStringForUser(
- Settings.Global.ADB_ENABLED, "0", userHandle);
- }
- } else if (UserManager.ENSURE_VERIFY_APPS.equals(key)) {
- mInjector.settingsGlobalPutStringForUser(
- Settings.Global.PACKAGE_VERIFIER_ENABLE, "1",
- userHandle);
- mInjector.settingsGlobalPutStringForUser(
- Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, "1",
- userHandle);
- } else if (UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES.equals(key)) {
- mInjector.settingsSecurePutIntForUser(
- Settings.Secure.INSTALL_NON_MARKET_APPS, 0,
- userHandle);
- }
- }
- mUserManager.setUserRestriction(key, enabled, user);
- activeAdmin.ensureUserRestrictions().putBoolean(key, enabled);
- saveSettingsLocked(userHandle);
-
- if (enabled != alreadyRestricted) {
- if (UserManager.DISALLOW_SHARE_LOCATION.equals(key)) {
- // Send out notifications however as some clients may want to reread the
- // value which actually changed due to a restriction having been applied.
- final String property = Settings.Secure.SYS_PROP_SETTING_VERSION;
- long version = mInjector.systemPropertiesGetLong(property, 0) + 1;
- mInjector.systemPropertiesSet(property, Long.toString(version));
-
- final String name = Settings.Secure.LOCATION_PROVIDERS_ALLOWED;
- Uri url = Uri.withAppendedPath(Settings.Secure.CONTENT_URI, name);
- mContext.getContentResolver().notifyChange(url, null, true, userHandle);
- }
- }
- if (!enabled && alreadyRestricted) {
- if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)) {
- mInjector.getIAudioService()
- .setMicrophoneMute(false, mContext.getPackageName(), userHandle);
- } else if (UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
- mInjector.getIAudioService()
- .setMasterMute(false, 0, mContext.getPackageName(), userHandle);
- }
- }
- } catch (RemoteException re) {
- Slog.e(LOG_TAG, "Failed to talk to AudioService.", re);
- } finally {
- mInjector.binderRestoreCallingIdentity(id);
- }
- sendChangedNotification(userHandle);
+ return activeAdmin.userRestrictions;
}
}
@@ -5647,7 +5713,7 @@
long id = mInjector.binderClearCallingIdentity();
try {
- if (DBG) {
+ if (VERBOSE_LOG) {
Slog.v(LOG_TAG, "installing " + packageName + " for "
+ userId);
}
@@ -5702,7 +5768,9 @@
0, // no flags
primaryUser.id);
- if (DBG) Slog.d(LOG_TAG, "Enabling system activities: " + activitiesToEnable);
+ if (VERBOSE_LOG) {
+ Slog.d(LOG_TAG, "Enabling system activities: " + activitiesToEnable);
+ }
int numberOfAppsInstalled = 0;
if (activitiesToEnable != null) {
for (ResolveInfo info : activitiesToEnable) {
@@ -6272,7 +6340,8 @@
}
}
- private final class LocalService extends DevicePolicyManagerInternal {
+ @VisibleForTesting
+ final class LocalService extends DevicePolicyManagerInternal {
private List<OnCrossProfileWidgetProvidersChangeListener> mWidgetProviderListeners;
@Override
@@ -6319,6 +6388,37 @@
}
}
+ @Override
+ public Bundle getComposedUserRestrictions(int userId, Bundle inBundle) {
+ synchronized (DevicePolicyManagerService.this) {
+ final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
+ final ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userId);
+
+ final Bundle deviceOwnerRestrictions =
+ deviceOwner == null ? null : deviceOwner.userRestrictions;
+ final Bundle profileOwnerRestrictions =
+ profileOwner == null ? null : profileOwner.userRestrictions;
+ final boolean cameraDisabled = getCameraDisabled(null, userId);
+
+ if (deviceOwnerRestrictions == null && profileOwnerRestrictions == null
+ && !cameraDisabled) {
+ // No restrictions to merge.
+ return inBundle;
+ }
+
+ final Bundle composed = new Bundle(inBundle);
+ UserRestrictionsUtils.merge(composed, deviceOwnerRestrictions);
+ UserRestrictionsUtils.merge(composed, profileOwnerRestrictions);
+
+ // Also merge in the camera restriction.
+ if (cameraDisabled) {
+ composed.putBoolean(UserManager.DISALLOW_CAMERA, true);
+ }
+
+ return composed;
+ }
+ }
+
private void notifyCrossProfileProvidersChanged(int userId, List<String> packages) {
final List<OnCrossProfileWidgetProvidersChangeListener> listeners;
synchronized (DevicePolicyManagerService.this) {
@@ -6540,4 +6640,48 @@
throw new RuntimeException("Package manager has died", re);
}
}
+
+ @Override
+ public boolean isProvisioningAllowed(String action) {
+ if (mOwners.hasDeviceOwner()) {
+ return false;
+ }
+ final int callingUserId = mInjector.userHandleGetCallingUserId();
+ if (DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE.equals(action)) {
+ try {
+ if (!mIPackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS)) {
+ return false;
+ }
+ } catch (RemoteException e) {
+ return false;
+ }
+ final long ident = mInjector.binderClearCallingIdentity();
+ try {
+ if (!mUserManager.canAddMoreManagedProfiles(callingUserId, true)) {
+ return false;
+ }
+ } finally {
+ mInjector.binderRestoreCallingIdentity(ident);
+ }
+ return true;
+ } else if (DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE.equals(action)) {
+ if (getProfileOwner(callingUserId) != null) {
+ return false;
+ }
+ if (mInjector.settingsGlobalGetInt(Settings.Global.DEVICE_PROVISIONED, 0) != 0) {
+ return false;
+ }
+ if (callingUserId != UserHandle.USER_SYSTEM) {
+ // Device owner provisioning can only be initiated from system user.
+ return false;
+ }
+ return true;
+ } else if (DevicePolicyManager.ACTION_PROVISION_MANAGED_USER.equals(action)) {
+ if (hasUserSetupCompleted(callingUserId)) {
+ return false;
+ }
+ return true;
+ }
+ throw new IllegalArgumentException("Unknown provisioning action " + action);
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 799267d..ded4422 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -30,7 +30,6 @@
import android.util.Xml;
import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.util.Preconditions;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -75,6 +74,7 @@
private static final String ATTR_PACKAGE = "package";
private static final String ATTR_COMPONENT_NAME = "component";
private static final String ATTR_USERID = "userId";
+ private static final String ATTR_USER_RESTRICTIONS_MIGRATED = "userRestrictionsMigrated";
private static final String TAG_SYSTEM_UPDATE_POLICY = "system-update-policy";
@@ -121,17 +121,20 @@
if (!legacy.delete()) {
Slog.e(TAG, "Failed to remove the legacy setting file");
}
- return;
- }
+ } else {
+ // No legacy file, read from the new format files.
+ new DeviceOwnerReadWriter().readFromFileLocked();
- // No legacy file, read from the new format files.
- new DeviceOwnerReadWriter().readFromFileLocked();
-
- final List<UserInfo> users = mUserManager.getUsers();
- for (UserInfo ui : users) {
- new ProfileOwnerReadWriter(ui.id).readFromFileLocked();
+ final List<UserInfo> users = mUserManager.getUsers();
+ for (UserInfo ui : users) {
+ new ProfileOwnerReadWriter(ui.id).readFromFileLocked();
+ }
}
}
+ if (hasDeviceOwner() && hasProfileOwner(getDeviceOwnerUserId())) {
+ Slog.w(TAG, String.format("User %d has both DO and PO, which is not supported",
+ getDeviceOwnerUserId()));
+ }
}
String getDeviceOwnerPackageName() {
@@ -155,7 +158,16 @@
Slog.e(TAG, "Invalid user id for device owner user: " + userId);
return;
}
- mDeviceOwner = new OwnerInfo(ownerName, admin);
+ // For a newly set DO, there's no need for migration.
+ setDeviceOwnerWithRestrictionsMigrated(admin, ownerName, userId,
+ /* userRestrictionsMigrated =*/ true);
+ }
+
+ // Note this should be only called during migration. Normally when DO is set,
+ // userRestrictionsMigrated should always be true.
+ void setDeviceOwnerWithRestrictionsMigrated(ComponentName admin, String ownerName, int userId,
+ boolean userRestrictionsMigrated) {
+ mDeviceOwner = new OwnerInfo(ownerName, admin, userRestrictionsMigrated);
mDeviceOwnerUserId = userId;
}
@@ -165,7 +177,9 @@
}
void setProfileOwner(ComponentName admin, String ownerName, int userId) {
- mProfileOwners.put(userId, new OwnerInfo(ownerName, admin));
+ // For a newly set PO, there's no need for migration.
+ mProfileOwners.put(userId, new OwnerInfo(ownerName, admin,
+ /* userRestrictionsMigrated =*/ true));
}
void removeProfileOwner(int userId) {
@@ -207,6 +221,42 @@
return mDeviceOwner != null;
}
+ boolean hasProfileOwner(int userId) {
+ return getProfileOwnerComponent(userId) != null;
+ }
+
+ /**
+ * @return true if user restrictions need to be migrated for DO.
+ */
+ boolean getDeviceOwnerUserRestrictionsNeedsMigration() {
+ return mDeviceOwner != null && !mDeviceOwner.userRestrictionsMigrated;
+ }
+
+ /**
+ * @return true if user restrictions need to be migrated for PO.
+ */
+ boolean getProfileOwnerUserRestrictionsNeedsMigration(int userId) {
+ OwnerInfo profileOwner = mProfileOwners.get(userId);
+ return profileOwner != null && !profileOwner.userRestrictionsMigrated;
+ }
+
+ /** Sets the user restrictions migrated flag, and also writes to the file. */
+ void setDeviceOwnerUserRestrictionsMigrated() {
+ if (mDeviceOwner != null) {
+ mDeviceOwner.userRestrictionsMigrated = true;
+ }
+ writeDeviceOwner();
+ }
+
+ /** Sets the user restrictions migrated flag, and also writes to the file. */
+ void setProfileOwnerUserRestrictionsMigrated(int userId) {
+ OwnerInfo profileOwner = mProfileOwners.get(userId);
+ if (profileOwner != null) {
+ profileOwner.userRestrictionsMigrated = true;
+ }
+ writeProfileOwner(userId);
+ }
+
private boolean readLegacyOwnerFile(File file) {
if (!file.exists()) {
// Already migrated or the device has no owners.
@@ -226,7 +276,8 @@
if (tag.equals(TAG_DEVICE_OWNER)) {
String name = parser.getAttributeValue(null, ATTR_NAME);
String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
- mDeviceOwner = new OwnerInfo(name, packageName);
+ mDeviceOwner = new OwnerInfo(name, packageName,
+ /* userRestrictionsMigrated =*/ false);
mDeviceOwnerUserId = UserHandle.USER_SYSTEM;
} else if (tag.equals(TAG_DEVICE_INITIALIZER)) {
// Deprecated tag
@@ -241,7 +292,8 @@
ComponentName admin = ComponentName.unflattenFromString(
profileOwnerComponentStr);
if (admin != null) {
- profileOwnerInfo = new OwnerInfo(profileOwnerName, admin);
+ profileOwnerInfo = new OwnerInfo(profileOwnerName, admin,
+ /* userRestrictionsMigrated =*/ false);
} else {
// This shouldn't happen but switch from package name -> component name
// might have written bad device owner files. b/17652534
@@ -250,7 +302,8 @@
}
}
if (profileOwnerInfo == null) {
- profileOwnerInfo = new OwnerInfo(profileOwnerName, profileOwnerPackageName);
+ profileOwnerInfo = new OwnerInfo(profileOwnerName, profileOwnerPackageName,
+ /* userRestrictionsMigrated =*/ false);
}
mProfileOwners.put(userId, profileOwnerInfo);
} else if (TAG_SYSTEM_UPDATE_POLICY.equals(tag)) {
@@ -503,21 +556,24 @@
}
}
- private static class OwnerInfo {
+ static class OwnerInfo {
public final String name;
public final String packageName;
public final ComponentName admin;
+ public boolean userRestrictionsMigrated;
- public OwnerInfo(String name, String packageName) {
+ public OwnerInfo(String name, String packageName, boolean userRestrictionsMigrated) {
this.name = name;
this.packageName = packageName;
this.admin = new ComponentName(packageName, "");
+ this.userRestrictionsMigrated = userRestrictionsMigrated;
}
- public OwnerInfo(String name, ComponentName admin) {
+ public OwnerInfo(String name, ComponentName admin, boolean userRestrictionsMigrated) {
this.name = name;
this.admin = admin;
this.packageName = admin.getPackageName();
+ this.userRestrictionsMigrated = userRestrictionsMigrated;
}
public void writeToXml(XmlSerializer out, String tag) throws IOException {
@@ -529,6 +585,8 @@
if (admin != null) {
out.attribute(null, ATTR_COMPONENT_NAME, admin.flattenToString());
}
+ out.attribute(null, ATTR_USER_RESTRICTIONS_MIGRATED,
+ String.valueOf(userRestrictionsMigrated));
out.endTag(null, tag);
}
@@ -537,12 +595,16 @@
final String name = parser.getAttributeValue(null, ATTR_NAME);
final String componentName =
parser.getAttributeValue(null, ATTR_COMPONENT_NAME);
+ final String userRestrictionsMigratedStr =
+ parser.getAttributeValue(null, ATTR_USER_RESTRICTIONS_MIGRATED);
+ final boolean userRestrictionsMigrated =
+ ("true".equals(userRestrictionsMigratedStr));
// Has component name? If so, return [name, component]
if (componentName != null) {
final ComponentName admin = ComponentName.unflattenFromString(componentName);
if (admin != null) {
- return new OwnerInfo(name, admin);
+ return new OwnerInfo(name, admin, userRestrictionsMigrated);
} else {
// This shouldn't happen but switch from package name -> component name
// might have written bad device owner files. b/17652534
@@ -552,7 +614,7 @@
}
// Else, build with [name, package]
- return new OwnerInfo(name, packageName);
+ return new OwnerInfo(name, packageName, userRestrictionsMigrated);
}
public void dump(String prefix, PrintWriter pw) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 1ec1a46..2f33d7c 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -407,8 +407,6 @@
mSystemServiceManager.startService(UsageStatsService.class);
mActivityManagerService.setUsageStatsManager(
LocalServices.getService(UsageStatsManagerInternal.class));
- // Update after UsageStatsService is available, needed before performBootDexOpt.
- mPackageManagerService.getUsageStatsIfNoPackageUsageInfo();
// Tracks whether the updatable WebView is in a ready state and watches for update installs.
mSystemServiceManager.startService(WebViewUpdateService.class);
@@ -438,7 +436,6 @@
InputManagerService inputManager = null;
TelephonyRegistry telephonyRegistry = null;
ConsumerIrService consumerIr = null;
- AudioService audioService = null;
MmsServiceBroker mmsService = null;
EntropyMixer entropyMixer = null;
@@ -613,11 +610,11 @@
// as appropriate.
mSystemServiceManager.startService(UiModeManagerService.class);
- Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "PerformBootDexOpt");
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "PerformFstrimIfNeeded");
try {
- mPackageManagerService.performBootDexOpt();
+ mPackageManagerService.performFstrimIfNeeded();
} catch (Throwable e) {
- reportWtf("performing boot dexopt", e);
+ reportWtf("performing fstrim", e);
}
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
@@ -857,12 +854,7 @@
}
traceBeginAndSlog("StartAudioService");
- try {
- audioService = new AudioService(context);
- ServiceManager.addService(Context.AUDIO_SERVICE, audioService);
- } catch (Throwable e) {
- reportWtf("starting Audio Service", e);
- }
+ mSystemServiceManager.startService(AudioService.Lifecycle.class);
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
if (!disableNonCoreServices) {
@@ -1163,7 +1155,6 @@
final InputManagerService inputManagerF = inputManager;
final TelephonyRegistry telephonyRegistryF = telephonyRegistry;
final MediaRouterService mediaRouterF = mediaRouter;
- final AudioService audioServiceF = audioService;
final MmsServiceBroker mmsServiceF = mmsService;
// We now tell the activity manager it is okay to run third party
@@ -1234,13 +1225,7 @@
reportWtf("making Connectivity Service ready", e);
}
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
- Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "MakeAudioServiceReady");
- try {
- if (audioServiceF != null) audioServiceF.systemReady();
- } catch (Throwable e) {
- reportWtf("Notifying AudioService running", e);
- }
- Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+
Watchdog.getInstance().start();
// It is now okay to let the various system services start their
@@ -1332,4 +1317,4 @@
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, name);
Slog.i(TAG, name);
}
-}
\ No newline at end of file
+}
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index 28cb114..c9efc69 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -417,9 +417,10 @@
encap, mTransactionId, getSecs(), clientAddress,
DO_UNICAST, mHwAddr, requestedAddress,
serverAddress, REQUESTED_PARAMS, null);
+ String serverStr = (serverAddress != null) ? serverAddress.getHostAddress() : null;
String description = "DHCPREQUEST ciaddr=" + clientAddress.getHostAddress() +
" request=" + requestedAddress.getHostAddress() +
- " to=" + serverAddress.getHostAddress();
+ " serverid=" + serverStr;
return transmitPacket(packet, description, to);
}
@@ -822,7 +823,8 @@
public void enter() {
super.enter();
if (!setIpAddress(mDhcpLease.ipAddress) ||
- !connectUdpSock((mDhcpLease.serverAddress))) {
+ (mDhcpLease.serverAddress != null &&
+ !connectUdpSock((mDhcpLease.serverAddress)))) {
notifyFailure();
// There's likely no point in going into DhcpInitState here, we'll probably just
// repeat the transaction, get the same IP address as before, and fail.
@@ -878,11 +880,15 @@
}
protected boolean sendPacket() {
+ // Not specifying a SERVER_IDENTIFIER option is a violation of RFC 2131, but...
+ // http://b/25343517 . Try to make things work anyway by using broadcast renews.
+ Inet4Address to = (mDhcpLease.serverAddress != null) ?
+ mDhcpLease.serverAddress : INADDR_BROADCAST;
return sendRequestPacket(
(Inet4Address) mDhcpLease.ipAddress.getAddress(), // ciaddr
INADDR_ANY, // DHCP_REQUESTED_IP
- INADDR_ANY, // DHCP_SERVER_IDENTIFIER
- (Inet4Address) mDhcpLease.serverAddress); // packet destination address
+ null, // DHCP_SERVER_IDENTIFIER
+ to); // packet destination address
}
protected void receivePacket(DhcpPacket packet) {
diff --git a/services/net/java/android/net/ip/IpReachabilityMonitor.java b/services/net/java/android/net/ip/IpReachabilityMonitor.java
index 53f55cd..88155f7 100644
--- a/services/net/java/android/net/ip/IpReachabilityMonitor.java
+++ b/services/net/java/android/net/ip/IpReachabilityMonitor.java
@@ -403,7 +403,6 @@
// TODO: simply the number of objects by making this extend Thread.
private final class NetlinkSocketObserver implements Runnable {
- private static final String TAG = "NetlinkSocketObserver";
private NetlinkSocket mSocket;
@Override
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index 34347cf..92bd81f 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -19,14 +19,10 @@
import android.Manifest;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
@@ -51,7 +47,6 @@
import android.text.TextUtils;
import android.util.SparseArray;
-import com.android.internal.R;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
import com.android.server.SystemService;
@@ -527,6 +522,7 @@
while (iterator.hasNext()) {
ComponentName componentName = iterator.next();
if (packageName.equals(componentName.getPackageName())) {
+ userState.removeApprovedPrintService(componentName);
iterator.remove();
servicesRemoved = true;
}
@@ -587,15 +583,29 @@
return;
}
- final int installedServiceCount = installedServices.size();
- for (int i = 0; i < installedServiceCount; i++) {
- ServiceInfo serviceInfo = installedServices.get(i).serviceInfo;
- ComponentName component = new ComponentName(serviceInfo.packageName,
- serviceInfo.name);
- String label = serviceInfo.loadLabel(mContext.getPackageManager())
- .toString();
- showEnableInstalledPrintServiceNotification(component, label,
- getChangingUserId());
+ // Enable all added services by default
+ synchronized (mLock) {
+ UserState userState = getOrCreateUserStateLocked(getChangingUserId());
+
+ Set<ComponentName> enabledServices = userState.getEnabledServices();
+ boolean servicesAdded = false;
+
+ final int installedServiceCount = installedServices.size();
+ for (int i = 0; i < installedServiceCount; i++) {
+ ServiceInfo serviceInfo = installedServices.get(i).serviceInfo;
+ ComponentName component = new ComponentName(serviceInfo.packageName,
+ serviceInfo.name);
+
+ enabledServices.add(component);
+ servicesAdded = true;
+ }
+
+ if (servicesAdded) {
+ persistComponentNamesToSettingLocked(
+ Settings.Secure.ENABLED_PRINT_SERVICES, enabledServices,
+ getChangingUserId());
+ userState.updateIfNeededLocked();
+ }
}
}
@@ -733,43 +743,5 @@
Binder.restoreCallingIdentity(identity);
}
}
-
- private void showEnableInstalledPrintServiceNotification(ComponentName component,
- String label, int userId) {
- UserHandle userHandle = new UserHandle(userId);
-
- Intent intent = new Intent(Settings.ACTION_PRINT_SETTINGS);
- intent.putExtra(EXTRA_PRINT_SERVICE_COMPONENT_NAME, component.flattenToString());
-
- PendingIntent pendingIntent = PendingIntent.getActivityAsUser(mContext, 0, intent,
- PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_CANCEL_CURRENT, null,
- userHandle);
-
- Context builderContext = mContext;
- try {
- builderContext = mContext.createPackageContextAsUser(mContext.getPackageName(), 0,
- userHandle);
- } catch (NameNotFoundException e) {
- // Ignore can't find the package the system is running as.
- }
- Notification.Builder builder = new Notification.Builder(builderContext)
- .setSmallIcon(R.drawable.ic_print)
- .setContentTitle(mContext.getString(R.string.print_service_installed_title,
- label))
- .setContentText(mContext.getString(R.string.print_service_installed_message))
- .setContentIntent(pendingIntent)
- .setWhen(System.currentTimeMillis())
- .setAutoCancel(true)
- .setShowWhen(true)
- .setColor(mContext.getColor(
- com.android.internal.R.color.system_notification_accent_color));
-
- NotificationManager notificationManager = (NotificationManager) mContext
- .getSystemService(Context.NOTIFICATION_SERVICE);
-
- String notificationTag = getClass().getName() + ":" + component.flattenToString();
- notificationManager.notifyAsUser(notificationTag, 0, builder.build(),
- userHandle);
- }
}
}
diff --git a/services/print/java/com/android/server/print/RemotePrintSpooler.java b/services/print/java/com/android/server/print/RemotePrintSpooler.java
index 85c876a..e5370f4 100644
--- a/services/print/java/com/android/server/print/RemotePrintSpooler.java
+++ b/services/print/java/com/android/server/print/RemotePrintSpooler.java
@@ -279,6 +279,35 @@
}
}
+ /**
+ * Connect to the print spooler service and remove an approved print service.
+ *
+ * @param serviceToRemove The {@link ComponentName} of the service to be removed.
+ */
+ public final void removeApprovedPrintService(ComponentName serviceToRemove) {
+ throwIfCalledOnMainThread();
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ mCanUnbind = false;
+ }
+ try {
+ getRemoteInstanceLazy().removeApprovedPrintService(serviceToRemove);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error removing approved print service.", re);
+ } catch (TimeoutException te) {
+ Slog.e(LOG_TAG, "Error removing approved print service.", te);
+ } finally {
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier()
+ + "] removing approved print service()");
+ }
+ synchronized (mLock) {
+ mCanUnbind = true;
+ mLock.notifyAll();
+ }
+ }
+ }
+
public final void removeObsoletePrintJobs() {
throwIfCalledOnMainThread();
synchronized (mLock) {
diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java
index ae19dac..f37bb9eb 100644
--- a/services/print/java/com/android/server/print/UserState.java
+++ b/services/print/java/com/android/server/print/UserState.java
@@ -297,6 +297,15 @@
}
}
+ /**
+ * Remove an approved print service.
+ *
+ * @param serviceToRemove The {@link ComponentName} of the service to be removed.
+ */
+ public void removeApprovedPrintService(ComponentName serviceToRemove) {
+ mSpooler.removeApprovedPrintService(serviceToRemove);
+ }
+
public void restartPrintJob(PrintJobId printJobId, int appId) {
PrintJobInfo printJobInfo = getPrintJobInfo(printJobId, appId);
if (printJobInfo == null || printJobInfo.getState() != PrintJobInfo.STATE_FAILED) {
diff --git a/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest/legacy_device_owner.xml b/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest/legacy_device_owner.xml
new file mode 100644
index 0000000..9564969
--- /dev/null
+++ b/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest/legacy_device_owner.xml
@@ -0,0 +1,4 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+<device-owner package="com.android.frameworks.servicestests" />
+<profile-owner package="com.android.frameworks.servicestests" name="0" userId="10" component="com.android.frameworks.servicestests/com.android.server.devicepolicy.DummyDeviceAdmins$Admin2" />
+<profile-owner package="com.android.frameworks.servicestests" name="0" userId="11" component="com.android.frameworks.servicestests/com.android.server.devicepolicy.DummyDeviceAdmins$Admin3" />
diff --git a/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest/legacy_device_policies.xml b/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest/legacy_device_policies.xml
new file mode 100644
index 0000000..48cb814
--- /dev/null
+++ b/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest/legacy_device_policies.xml
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+<policies setup-complete="true">
+ <admin name="com.google.android.gms/com.google.android.gms.mdm.receivers.MdmDeviceAdminReceiver">
+ </admin>
+ <admin name="com.android.frameworks.servicestests/com.android.server.devicepolicy.DummyDeviceAdmins$Admin1">
+ </admin>
+</policies>
diff --git a/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest/legacy_device_policies_10.xml b/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest/legacy_device_policies_10.xml
new file mode 100644
index 0000000..6b53840
--- /dev/null
+++ b/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest/legacy_device_policies_10.xml
@@ -0,0 +1,5 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+<policies setup-complete="true">
+ <admin name="com.android.frameworks.servicestests/com.android.server.devicepolicy.DummyDeviceAdmins$Admin2">
+ </admin>
+</policies>
diff --git a/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest/legacy_device_policies_11.xml b/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest/legacy_device_policies_11.xml
new file mode 100644
index 0000000..2bcc5d4
--- /dev/null
+++ b/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest/legacy_device_policies_11.xml
@@ -0,0 +1,5 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+<policies setup-complete="true">
+ <admin name="com.android.frameworks.servicestests/com.android.server.devicepolicy.DummyDeviceAdmins$Admin3">
+ </admin>
+</policies>
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index 90b4f43..c12f978 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -876,11 +876,12 @@
}
private void expectSystemReady() throws Exception {
- mAlarmManager.remove(isA(PendingIntent.class));
+ mAlarmManager.remove(isA(PendingIntent.class), null);
expectLastCall().anyTimes();
- mAlarmManager.set(eq(AlarmManager.ELAPSED_REALTIME), anyLong(), anyLong(), anyLong(),
- anyInt(), isA(PendingIntent.class), isA(WorkSource.class),
+ mAlarmManager.set(getContext().getPackageName(),
+ eq(AlarmManager.ELAPSED_REALTIME), anyLong(), anyLong(), anyLong(),
+ anyInt(), isA(PendingIntent.class), null, null, isA(WorkSource.class),
isA(AlarmManager.AlarmClockInfo.class));
expectLastCall().atLeastOnce();
diff --git a/services/tests/servicestests/src/com/android/server/content/SyncManagerTest.java b/services/tests/servicestests/src/com/android/server/content/SyncManagerTest.java
new file mode 100644
index 0000000..be6861c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/content/SyncManagerTest.java
@@ -0,0 +1,64 @@
+package com.android.server.content;
+
+import android.os.Bundle;
+
+import junit.framework.TestCase;
+
+public class SyncManagerTest extends TestCase {
+
+ final String KEY_1 = "key_1";
+ final String KEY_2 = "key_2";
+
+ public void testSyncExtrasEquals_WithNull() throws Exception {
+ Bundle b1 = new Bundle();
+ Bundle b2 = new Bundle();
+
+ b1.putString(KEY_1, null);
+ b2.putString(KEY_1, null);
+
+ assertTrue("Null extra not properly compared between bundles.",
+ SyncManager.syncExtrasEquals(b1, b2, false /* don't care about system extras */));
+ }
+
+ public void testSyncExtrasEqualsBigger_WithNull() throws Exception {
+ Bundle b1 = new Bundle();
+ Bundle b2 = new Bundle();
+
+ b1.putString(KEY_1, null);
+ b2.putString(KEY_1, null);
+
+ b1.putString(KEY_2, "bla");
+ b2.putString(KEY_2, "bla");
+
+ assertTrue("Extras not properly compared between bundles.",
+ SyncManager.syncExtrasEquals(b1, b2, false /* don't care about system extras */));
+ }
+
+ public void testSyncExtrasEqualsFails_differentValues() throws Exception {
+ Bundle b1 = new Bundle();
+ Bundle b2 = new Bundle();
+
+ b1.putString(KEY_1, null);
+ b2.putString(KEY_1, null);
+
+ b1.putString(KEY_2, "bla");
+ b2.putString(KEY_2, "ble"); // different key
+
+ assertFalse("Extras considered equal when they are different.",
+ SyncManager.syncExtrasEquals(b1, b2, false /* don't care about system extras */));
+ }
+
+ public void testSyncExtrasEqualsFails_differentNulls() throws Exception {
+ Bundle b1 = new Bundle();
+ Bundle b2 = new Bundle();
+
+ b1.putString(KEY_1, null);
+ b2.putString(KEY_1, "bla"); // different key
+
+ b1.putString(KEY_2, "ble");
+ b2.putString(KEY_2, "ble");
+
+ assertFalse("Extras considered equal when they are different.",
+ SyncManager.syncExtrasEquals(b1, b2, false /* don't care about system extras */));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
new file mode 100644
index 0000000..dfa9f8f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
@@ -0,0 +1,190 @@
+/*
+ * 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.devicepolicy;
+
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+import com.android.server.devicepolicy.DevicePolicyManagerServiceTestable.OwnersTestable;
+
+import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManagerInternal;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Pair;
+
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.when;
+
+public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase {
+ private DpmMockContext mContext;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mContext = getContext();
+
+ when(mContext.packageManager.hasSystemFeature(eq(PackageManager.FEATURE_DEVICE_ADMIN)))
+ .thenReturn(true);
+ }
+
+ public void testMigration() throws Exception {
+ final File user10dir = mMockContext.addUser(10, 0);
+ final File user11dir = mMockContext.addUser(11, UserInfo.FLAG_MANAGED_PROFILE);
+ final File user12dir = mMockContext.addUser(12, 0);
+
+ setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
+ setUpPackageManagerForAdmin(admin2, UserHandle.getUid(10, 123));
+ setUpPackageManagerForAdmin(admin3, UserHandle.getUid(11, 456));
+
+ // Create the legacy owners & policies file.
+ DpmTestUtils.writeToFile(
+ (new File(mContext.dataDir, OwnersTestable.LEGACY_FILE)).getAbsoluteFile(),
+ DpmTestUtils.readAsset(mRealTestContext,
+ "DevicePolicyManagerServiceMigrationTest/legacy_device_owner.xml"));
+
+ DpmTestUtils.writeToFile(
+ (new File(mContext.systemUserDataDir, "device_policies.xml")).getAbsoluteFile(),
+ DpmTestUtils.readAsset(mRealTestContext,
+ "DevicePolicyManagerServiceMigrationTest/legacy_device_policies.xml"));
+
+ DpmTestUtils.writeToFile(
+ (new File(user10dir, "device_policies.xml")).getAbsoluteFile(),
+ DpmTestUtils.readAsset(mRealTestContext,
+ "DevicePolicyManagerServiceMigrationTest/legacy_device_policies_10.xml"));
+ DpmTestUtils.writeToFile(
+ (new File(user11dir, "device_policies.xml")).getAbsoluteFile(),
+ DpmTestUtils.readAsset(mRealTestContext,
+ "DevicePolicyManagerServiceMigrationTest/legacy_device_policies_11.xml"));
+
+ // Set up UserManager
+ when(mMockContext.userManagerInternal.getBaseUserRestrictions(
+ eq(UserHandle.USER_SYSTEM))).thenReturn(DpmTestUtils.newRestrictions(
+ UserManager.DISALLOW_ADD_USER,
+ UserManager.DISALLOW_RECORD_AUDIO));
+
+ when(mMockContext.userManagerInternal.getBaseUserRestrictions(
+ eq(10))).thenReturn(DpmTestUtils.newRestrictions(
+ UserManager.DISALLOW_REMOVE_USER,
+ UserManager.DISALLOW_SMS,
+ UserManager.DISALLOW_OUTGOING_CALLS,
+ UserManager.DISALLOW_WALLPAPER,
+ UserManager.DISALLOW_RECORD_AUDIO));
+
+ when(mMockContext.userManagerInternal.getBaseUserRestrictions(
+ eq(11))).thenReturn(DpmTestUtils.newRestrictions(
+ UserManager.DISALLOW_REMOVE_USER,
+ UserManager.DISALLOW_SMS,
+ UserManager.DISALLOW_OUTGOING_CALLS,
+ UserManager.DISALLOW_WALLPAPER,
+ UserManager.DISALLOW_RECORD_AUDIO));
+
+ final Map<Integer, Bundle> newBaseRestrictions = new HashMap<>();
+
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Integer userId = (Integer) invocation.getArguments()[0];
+ Bundle bundle = (Bundle) invocation.getArguments()[1];
+
+ newBaseRestrictions.put(userId, bundle);
+
+ return null;
+ }
+ }).when(mContext.userManagerInternal).setBaseUserRestrictionsByDpmsForMigration(
+ anyInt(), any(Bundle.class));
+
+ // Initialize DPM/DPMS and let it migrate the persisted information.
+ // (Need clearCallingIdentity() to pass permission checks.)
+
+ final DevicePolicyManagerServiceTestable dpms;
+
+ final long ident = mContext.binder.clearCallingIdentity();
+ try {
+ LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+
+ dpms = new DevicePolicyManagerServiceTestable(mContext, dataDir);
+
+ dpms.systemReady(SystemService.PHASE_LOCK_SETTINGS_READY);
+ dpms.systemReady(SystemService.PHASE_BOOT_COMPLETED);
+ } finally {
+ mContext.binder.restoreCallingIdentity(ident);
+ }
+
+ // Now all information should be migrated.
+ assertFalse(dpms.mOwners.getDeviceOwnerUserRestrictionsNeedsMigration());
+ assertFalse(dpms.mOwners.getProfileOwnerUserRestrictionsNeedsMigration(10));
+ assertFalse(dpms.mOwners.getProfileOwnerUserRestrictionsNeedsMigration(11));
+ assertFalse(dpms.mOwners.getProfileOwnerUserRestrictionsNeedsMigration(12));
+
+ // Check the new base restrictions.
+ DpmTestUtils.assertRestrictions(
+ DpmTestUtils.newRestrictions(
+ UserManager.DISALLOW_RECORD_AUDIO
+ ),
+ newBaseRestrictions.get(UserHandle.USER_SYSTEM));
+
+ DpmTestUtils.assertRestrictions(
+ DpmTestUtils.newRestrictions(
+ UserManager.DISALLOW_SMS,
+ UserManager.DISALLOW_OUTGOING_CALLS,
+ UserManager.DISALLOW_RECORD_AUDIO
+ ),
+ newBaseRestrictions.get(10));
+
+ DpmTestUtils.assertRestrictions(
+ DpmTestUtils.newRestrictions(
+ UserManager.DISALLOW_SMS,
+ UserManager.DISALLOW_OUTGOING_CALLS,
+ UserManager.DISALLOW_WALLPAPER,
+ UserManager.DISALLOW_RECORD_AUDIO
+ ),
+ newBaseRestrictions.get(11));
+
+ // Check the new owner restrictions.
+ DpmTestUtils.assertRestrictions(
+ DpmTestUtils.newRestrictions(
+ UserManager.DISALLOW_ADD_USER
+ ),
+ dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions());
+
+ DpmTestUtils.assertRestrictions(
+ DpmTestUtils.newRestrictions(
+ UserManager.DISALLOW_REMOVE_USER,
+ UserManager.DISALLOW_WALLPAPER
+ ),
+ dpms.getProfileOwnerAdminLocked(10).ensureUserRestrictions());
+
+ DpmTestUtils.assertRestrictions(
+ DpmTestUtils.newRestrictions(
+ UserManager.DISALLOW_REMOVE_USER
+ ),
+ dpms.getProfileOwnerAdminLocked(11).ensureUserRestrictions());
+ }
+}
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 b109e7b..2c01b8a 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -27,6 +27,7 @@
import android.os.PowerManagerInternal;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.UserManagerInternal;
import android.view.IWindowManager;
import java.io.File;
@@ -107,6 +108,11 @@
}
@Override
+ UserManagerInternal getUserManagerInternal() {
+ return context.userManagerInternal;
+ }
+
+ @Override
PowerManagerInternal getPowerManagerInternal() {
return context.powerManagerInternal;
}
@@ -153,7 +159,7 @@
@Override
String getDevicePolicyFilePathForSystemUser() {
- return context.systemUserDataDir.getAbsolutePath();
+ return context.systemUserDataDir.getAbsolutePath() + "/";
}
@Override
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 d6a60c7..36980e3 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -27,7 +27,6 @@
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
@@ -70,9 +69,6 @@
private DpmMockContext mContext;
public DevicePolicyManager dpm;
public DevicePolicyManagerServiceTestable dpms;
- public ComponentName admin1;
- public ComponentName admin2;
- public ComponentName admin3;
@Override
protected void setUp() throws Exception {
@@ -85,10 +81,6 @@
initializeDpms();
- admin1 = new ComponentName(mRealTestContext, DummyDeviceAdmins.Admin1.class);
- admin2 = new ComponentName(mRealTestContext, DummyDeviceAdmins.Admin2.class);
- admin3 = new ComponentName(mRealTestContext, DummyDeviceAdmins.Admin3.class);
-
setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_UID);
setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_UID);
setUpPackageManagerForAdmin(admin3, DpmMockContext.CALLER_UID);
@@ -113,67 +105,6 @@
}
}
- private void setUpPackageManagerForAdmin(ComponentName admin, int packageUid) throws Exception {
- setUpPackageManagerForAdmin(admin, packageUid,
- PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED);
- }
-
- private void setUpPackageManagerForAdmin(ComponentName admin, int packageUid,
- int enabledSetting) throws Exception {
-
- // Set up queryBroadcastReceivers().
-
- final Intent resolveIntent = new Intent();
- resolveIntent.setComponent(admin);
- final List<ResolveInfo> realResolveInfo =
- mRealTestContext.getPackageManager().queryBroadcastReceivers(
- resolveIntent,
- PackageManager.GET_META_DATA);
- assertNotNull(realResolveInfo);
- assertEquals(1, realResolveInfo.size());
-
- // We need to change AI, so set a clone.
- realResolveInfo.set(0, DpmTestUtils.cloneParcelable(realResolveInfo.get(0)));
-
- // We need to rewrite the UID in the activity info.
- realResolveInfo.get(0).activityInfo.applicationInfo.uid = packageUid;
-
- doReturn(realResolveInfo).when(mContext.packageManager).queryBroadcastReceivers(
- MockUtils.checkIntentComponent(admin),
- eq(PackageManager.GET_META_DATA
- | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS),
- eq(UserHandle.getUserId(packageUid)));
-
- // Set up getApplicationInfo().
-
- final ApplicationInfo ai = DpmTestUtils.cloneParcelable(
- mRealTestContext.getPackageManager().getApplicationInfo(
- admin1.getPackageName(),
- PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS));
-
- ai.enabledSetting = enabledSetting;
- ai.uid = packageUid;
-
- doReturn(ai).when(mContext.ipackageManager).getApplicationInfo(
- eq(admin1.getPackageName()),
- eq(PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS),
- eq(UserHandle.getUserId(packageUid)));
-
- // Set up getPackageInfo().
-
- final PackageInfo pi = DpmTestUtils.cloneParcelable(
- mRealTestContext.getPackageManager().getPackageInfo(
- admin1.getPackageName(), 0));
- assertTrue(pi.applicationInfo.flags != 0);
-
- pi.applicationInfo.uid = packageUid;
-
- doReturn(pi).when(mContext.ipackageManager).getPackageInfo(
- eq(admin1.getPackageName()),
- eq(0),
- eq(UserHandle.getUserId(packageUid)));
- }
-
private void setUpUserManager() {
// Emulate UserManager.set/getApplicationRestriction().
final Map<Pair<String, UserHandle>, Bundle> appRestrictions = new HashMap<>();
@@ -220,7 +151,7 @@
assertTrue(dpm.setProfileOwner(admin, "owner-name", DpmMockContext.CALLER_USER_HANDLE));
// Check
- assertEquals(admin1, dpm.getProfileOwnerAsUser(DpmMockContext.CALLER_USER_HANDLE));
+ assertEquals(admin, dpm.getProfileOwnerAsUser(DpmMockContext.CALLER_USER_HANDLE));
}
public void testHasNoFeature() throws Exception {
@@ -559,6 +490,17 @@
assertEquals(admin1.getPackageName(), dpm.getDeviceOwner());
+ // 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"));
+ }
+
// TODO Test getDeviceOwnerName() too. To do so, we need to change
// DPMS.getApplicationLabel() because Context.createPackageContextAsUser() is not mockable.
}
@@ -578,6 +520,8 @@
dpm.setDeviceOwner(new ComponentName("a.b.c", ".def"));
fail("Didn't throw IllegalArgumentException");
} catch (IllegalArgumentException expected) {
+ assertTrue("Message was: " + expected.getMessage(),
+ expected.getMessage().contains("Invalid component"));
}
}
@@ -666,6 +610,17 @@
public void testSetProfileOwner() throws Exception {
setAsProfileOwner(admin1);
+
+ // Try setting DO on the same user, which should fail.
+ setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_UID);
+ dpm.setActiveAdmin(admin2, /* refreshing= */ true, DpmMockContext.CALLER_USER_HANDLE);
+ try {
+ dpm.setDeviceOwner(admin2, "owner-name", DpmMockContext.CALLER_USER_HANDLE);
+ fail("IllegalStateException not thrown");
+ } catch (IllegalStateException expected) {
+ assertTrue("Message was: " + expected.getMessage(),
+ expected.getMessage().contains("already has a profile owner"));
+ }
}
public void testSetProfileOwner_failures() throws Exception {
@@ -743,6 +698,8 @@
assertEquals("", dpms.getDeviceOwner().getClassName());
// Then create a new DPMS to have it load the settings from files.
+ when(mContext.userManager.getUserRestrictions(any(UserHandle.class)))
+ .thenReturn(new Bundle());
initializeDpms();
// Now the DO component name is a full name.
@@ -802,32 +759,50 @@
assertTrue(dpm.setDeviceOwner(admin1, "owner-name",
UserHandle.USER_SYSTEM));
- assertFalse(dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
- .getBoolean(UserManager.DISALLOW_SMS));
- assertFalse(dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
- .getBoolean(UserManager.DISALLOW_OUTGOING_CALLS));
+ DpmTestUtils.assertRestrictions(
+ DpmTestUtils.newRestrictions(),
+ dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
+ );
+ DpmTestUtils.assertRestrictions(
+ DpmTestUtils.newRestrictions(),
+ dpm.getUserRestrictions(admin1)
+ );
dpm.addUserRestriction(admin1, UserManager.DISALLOW_SMS);
dpm.addUserRestriction(admin1, UserManager.DISALLOW_OUTGOING_CALLS);
- assertTrue(dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
- .getBoolean(UserManager.DISALLOW_SMS));
- assertTrue(dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
- .getBoolean(UserManager.DISALLOW_OUTGOING_CALLS));
+ DpmTestUtils.assertRestrictions(
+ DpmTestUtils.newRestrictions(
+ UserManager.DISALLOW_SMS, UserManager.DISALLOW_OUTGOING_CALLS),
+ dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
+ );
+ DpmTestUtils.assertRestrictions(
+ DpmTestUtils.newRestrictions(
+ UserManager.DISALLOW_SMS, UserManager.DISALLOW_OUTGOING_CALLS),
+ dpm.getUserRestrictions(admin1)
+ );
dpm.clearUserRestriction(admin1, UserManager.DISALLOW_SMS);
- assertFalse(dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
- .getBoolean(UserManager.DISALLOW_SMS));
- assertTrue(dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
- .getBoolean(UserManager.DISALLOW_OUTGOING_CALLS));
+ DpmTestUtils.assertRestrictions(
+ DpmTestUtils.newRestrictions(UserManager.DISALLOW_OUTGOING_CALLS),
+ dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
+ );
+ DpmTestUtils.assertRestrictions(
+ DpmTestUtils.newRestrictions(UserManager.DISALLOW_OUTGOING_CALLS),
+ dpm.getUserRestrictions(admin1)
+ );
dpm.clearUserRestriction(admin1, UserManager.DISALLOW_OUTGOING_CALLS);
- assertFalse(dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
- .getBoolean(UserManager.DISALLOW_SMS));
- assertFalse(dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
- .getBoolean(UserManager.DISALLOW_OUTGOING_CALLS));
+ DpmTestUtils.assertRestrictions(
+ DpmTestUtils.newRestrictions(),
+ dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
+ );
+ DpmTestUtils.assertRestrictions(
+ DpmTestUtils.newRestrictions(),
+ dpm.getUserRestrictions(admin1)
+ );
// TODO Check inner calls.
// TODO Make sure restrictions are written to the file.
@@ -836,42 +811,123 @@
public void testSetUserRestriction_asPo() {
setAsProfileOwner(admin1);
- assertFalse(dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
- .ensureUserRestrictions()
- .getBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES));
- assertFalse(dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
- .ensureUserRestrictions()
- .getBoolean(UserManager.DISALLOW_OUTGOING_CALLS));
+ DpmTestUtils.assertRestrictions(
+ DpmTestUtils.newRestrictions(),
+ dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
+ .ensureUserRestrictions()
+ );
dpm.addUserRestriction(admin1, UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
dpm.addUserRestriction(admin1, UserManager.DISALLOW_OUTGOING_CALLS);
- assertTrue(dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
- .ensureUserRestrictions()
- .getBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES));
- assertTrue(dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
- .ensureUserRestrictions()
- .getBoolean(UserManager.DISALLOW_OUTGOING_CALLS));
+ DpmTestUtils.assertRestrictions(
+ DpmTestUtils.newRestrictions(
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
+ UserManager.DISALLOW_OUTGOING_CALLS
+ ),
+ dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
+ .ensureUserRestrictions()
+ );
+ DpmTestUtils.assertRestrictions(
+ DpmTestUtils.newRestrictions(
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
+ UserManager.DISALLOW_OUTGOING_CALLS
+ ),
+ dpm.getUserRestrictions(admin1)
+ );
dpm.clearUserRestriction(admin1, UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
- assertFalse(dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
- .ensureUserRestrictions()
- .getBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES));
- assertTrue(dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
- .ensureUserRestrictions()
- .getBoolean(UserManager.DISALLOW_OUTGOING_CALLS));
+
+ DpmTestUtils.assertRestrictions(
+ DpmTestUtils.newRestrictions(
+ UserManager.DISALLOW_OUTGOING_CALLS
+ ),
+ dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
+ .ensureUserRestrictions()
+ );
+ DpmTestUtils.assertRestrictions(
+ DpmTestUtils.newRestrictions(
+ UserManager.DISALLOW_OUTGOING_CALLS
+ ),
+ dpm.getUserRestrictions(admin1)
+ );
dpm.clearUserRestriction(admin1, UserManager.DISALLOW_OUTGOING_CALLS);
- assertFalse(dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
- .ensureUserRestrictions()
- .getBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES));
- assertFalse(dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
- .ensureUserRestrictions()
- .getBoolean(UserManager.DISALLOW_OUTGOING_CALLS));
+ DpmTestUtils.assertRestrictions(
+ DpmTestUtils.newRestrictions(),
+ dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
+ .ensureUserRestrictions()
+ );
+ DpmTestUtils.assertRestrictions(
+ DpmTestUtils.newRestrictions(),
+ dpm.getUserRestrictions(admin1)
+ );
// TODO Check inner calls.
// TODO Make sure restrictions are written to the file.
}
+
+ public void testGetComposedUserRestrictions_noDoNoPo() throws Exception {
+ final Bundle in = DpmTestUtils.newRestrictions(UserManager.DISALLOW_OUTGOING_CALLS);
+
+ Bundle actual = dpms.mLocalService.getComposedUserRestrictions(
+ UserHandle.USER_SYSTEM, in);
+ assertTrue(in == actual);
+
+ actual = dpms.mLocalService.getComposedUserRestrictions(
+ DpmMockContext.CALLER_USER_HANDLE, in);
+ assertTrue(in == actual);
+ }
+
+ public void testGetComposedUserRestrictions() throws Exception {
+ mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+ mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+ mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
+
+ // First, set DO.
+
+ // Call from a process on the system user.
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+
+ // Make sure admin1 is installed on system user.
+ setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
+
+ // Call.
+ dpm.setActiveAdmin(admin1, /* replace =*/ false, UserHandle.USER_SYSTEM);
+ assertTrue(dpm.setDeviceOwner(admin1, "owner-name",
+ UserHandle.USER_SYSTEM));
+
+ dpm.addUserRestriction(admin1, "rest1");
+ dpm.addUserRestriction(admin1, "rest2");
+
+ // Set PO on CALLER_USER_HANDLE.
+ mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+
+ setAsProfileOwner(admin2);
+
+ dpm.addUserRestriction(admin2, "restA");
+ dpm.addUserRestriction(admin2, "restB");
+
+ final Bundle in = DpmTestUtils.newRestrictions("abc");
+
+ Bundle actual = dpms.mLocalService.getComposedUserRestrictions(
+ UserHandle.USER_SYSTEM, in);
+ DpmTestUtils.assertRestrictions(
+ DpmTestUtils.newRestrictions("abc", "rest1", "rest2"),
+ actual);
+
+ actual = dpms.mLocalService.getComposedUserRestrictions(
+ DpmMockContext.CALLER_USER_HANDLE, in);
+ DpmTestUtils.assertRestrictions(
+ DpmTestUtils.newRestrictions("abc", "rest1", "rest2", "restA", "restB"),
+ actual);
+
+ actual = dpms.mLocalService.getComposedUserRestrictions(
+ DpmMockContext.CALLER_USER_HANDLE + 1, in);
+ DpmTestUtils.assertRestrictions(
+ DpmTestUtils.newRestrictions("abc", "rest1", "rest2"),
+ actual);
+ }
}
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 d1b4835..cc337b0 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -36,6 +36,7 @@
import android.os.PowerManagerInternal;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.UserManagerInternal;
import android.test.mock.MockContentResolver;
import android.test.mock.MockContext;
import android.view.IWindowManager;
@@ -203,6 +204,7 @@
public final EnvironmentForMock environment;
public final SystemPropertiesForMock systemProperties;
public final UserManager userManager;
+ public final UserManagerInternal userManagerInternal;
public final UserManagerForMock userManagerForMock;
public final PowerManagerForMock powerManager;
public final PowerManagerInternal powerManagerInternal;
@@ -233,6 +235,7 @@
environment = mock(EnvironmentForMock.class);
systemProperties= mock(SystemPropertiesForMock.class);
userManager = mock(UserManager.class);
+ userManagerInternal = mock(UserManagerInternal.class);
userManagerForMock = mock(UserManagerForMock.class);
powerManager = mock(PowerManagerForMock.class);
powerManagerInternal = mock(PowerManagerInternal.class);
@@ -257,6 +260,9 @@
// System user is always running.
setUserRunning(UserHandle.USER_SYSTEM, true);
+
+ // This method must return an object.
+ when(userManagerInternal.getUserRestrictionsLock()).thenReturn(new Object());
}
public File addUser(int userId, int flags) {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
index 63bf125..e11f3fb 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
@@ -16,10 +16,21 @@
package com.android.server.devicepolicy;
+import android.content.ComponentName;
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.os.UserHandle;
import android.test.AndroidTestCase;
import java.io.File;
+import java.util.List;
+
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
public abstract class DpmTestBase extends AndroidTestCase {
public static final String TAG = "DpmTest";
@@ -29,6 +40,10 @@
public File dataDir;
+ public ComponentName admin1;
+ public ComponentName admin2;
+ public ComponentName admin3;
+
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -37,10 +52,77 @@
mMockContext = new DpmMockContext(
mRealTestContext, new File(mRealTestContext.getCacheDir(), "test-data"));
+
+ admin1 = new ComponentName(mRealTestContext, DummyDeviceAdmins.Admin1.class);
+ admin2 = new ComponentName(mRealTestContext, DummyDeviceAdmins.Admin2.class);
+ admin3 = new ComponentName(mRealTestContext, DummyDeviceAdmins.Admin3.class);
}
@Override
public DpmMockContext getContext() {
return mMockContext;
}
+
+
+ protected void setUpPackageManagerForAdmin(ComponentName admin, int packageUid)
+ throws Exception {
+ setUpPackageManagerForAdmin(admin, packageUid,
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED);
+ }
+
+ protected void setUpPackageManagerForAdmin(ComponentName admin, int packageUid,
+ int enabledSetting) throws Exception {
+
+ // Set up queryBroadcastReceivers().
+
+ final Intent resolveIntent = new Intent();
+ resolveIntent.setComponent(admin);
+ final List<ResolveInfo> realResolveInfo =
+ mRealTestContext.getPackageManager().queryBroadcastReceivers(
+ resolveIntent,
+ PackageManager.GET_META_DATA);
+ assertNotNull(realResolveInfo);
+ assertEquals(1, realResolveInfo.size());
+
+ // We need to change AI, so set a clone.
+ realResolveInfo.set(0, DpmTestUtils.cloneParcelable(realResolveInfo.get(0)));
+
+ // We need to rewrite the UID in the activity info.
+ realResolveInfo.get(0).activityInfo.applicationInfo.uid = packageUid;
+
+ doReturn(realResolveInfo).when(mMockContext.packageManager).queryBroadcastReceivers(
+ MockUtils.checkIntentComponent(admin),
+ eq(PackageManager.GET_META_DATA
+ | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS),
+ eq(UserHandle.getUserId(packageUid)));
+
+ // Set up getApplicationInfo().
+
+ final ApplicationInfo ai = DpmTestUtils.cloneParcelable(
+ mRealTestContext.getPackageManager().getApplicationInfo(
+ admin.getPackageName(),
+ PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS));
+
+ ai.enabledSetting = enabledSetting;
+ ai.uid = packageUid;
+
+ doReturn(ai).when(mMockContext.ipackageManager).getApplicationInfo(
+ eq(admin.getPackageName()),
+ eq(PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS),
+ eq(UserHandle.getUserId(packageUid)));
+
+ // Set up getPackageInfo().
+
+ final PackageInfo pi = DpmTestUtils.cloneParcelable(
+ mRealTestContext.getPackageManager().getPackageInfo(
+ admin.getPackageName(), 0));
+ assertTrue(pi.applicationInfo.flags != 0);
+
+ pi.applicationInfo.uid = packageUid;
+
+ doReturn(pi).when(mMockContext.ipackageManager).getPackageInfo(
+ eq(admin.getPackageName()),
+ eq(0),
+ eq(UserHandle.getUserId(packageUid)));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestUtils.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestUtils.java
index 7506273..cceb2d2 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestUtils.java
@@ -16,21 +16,35 @@
package com.android.server.devicepolicy;
+import com.google.android.collect.Lists;
+import com.google.android.collect.Sets;
+
+import android.content.Context;
+import android.os.Bundle;
import android.os.FileUtils;
import android.os.Parcel;
import android.os.Parcelable;
+import android.test.AndroidTestCase;
import android.util.Log;
import android.util.Printer;
import org.junit.Assert;
+import java.io.BufferedReader;
import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
+import java.util.Objects;
+import java.util.Set;
-public class DpmTestUtils {
- private DpmTestUtils() {
- }
+import junit.framework.AssertionFailedError;
+public class DpmTestUtils extends AndroidTestCase {
public static void clearDir(File dir) {
if (dir.exists()) {
Assert.assertTrue("failed to delete dir", FileUtils.deleteContents(dir));
@@ -43,6 +57,44 @@
return list == null ? 0 : list.size();
}
+ public static Bundle newRestrictions(String... restrictions) {
+ final Bundle ret = new Bundle();
+ for (String restriction : restrictions) {
+ ret.putBoolean(restriction, true);
+ }
+ return ret;
+ }
+
+ public static void assertRestrictions(Bundle expected, Bundle actual) {
+ final ArrayList<String> elist;
+ if (expected == null) {
+ elist = null;
+ } else {
+ elist = Lists.newArrayList();
+ for (String key : expected.keySet()) {
+ if (expected.getBoolean(key)) {
+ elist.add(key);
+ }
+ }
+ Collections.sort(elist);
+ }
+
+ final ArrayList<String> alist;
+ if (actual == null) {
+ alist = null;
+ } else {
+ alist = Lists.newArrayList();
+ for (String key : actual.keySet()) {
+ if (actual.getBoolean(key)) {
+ alist.add(key);
+ }
+ }
+ Collections.sort(alist);
+ }
+
+ assertEquals(elist, alist);
+ }
+
public static <T extends Parcelable> T cloneParcelable(T source) {
Parcel p = Parcel.obtain();
p.writeParcelable(source, 0);
@@ -58,4 +110,57 @@
Log.i(DpmTestBase.TAG, x);
}
};
+
+ public static String readAsset(Context context, String assetPath) throws IOException {
+ final StringBuilder sb = new StringBuilder();
+ try (BufferedReader br = new BufferedReader(
+ new InputStreamReader(
+ context.getResources().getAssets().open(assetPath)))) {
+ String line;
+ while ((line = br.readLine()) != null) {
+ sb.append(line);
+ sb.append(System.lineSeparator());
+ }
+ }
+ return sb.toString();
+ }
+
+ public static void writeToFile(File path, String content)
+ throws IOException {
+ path.getParentFile().mkdirs();
+
+ try (FileWriter writer = new FileWriter(path)) {
+ Log.i(DpmTestBase.TAG, "Writing to " + path);
+ Log.i(DpmTestBase.TAG, content);
+ writer.write(content);
+ }
+ }
+
+ private static boolean checkAssertRestrictions(Bundle a, Bundle b) {
+ try {
+ assertRestrictions(a, b);
+ return true;
+ } catch (AssertionFailedError e) {
+ return false;
+ }
+ }
+
+ public void testAssertRestrictions() {
+ final Bundle a = newRestrictions();
+ final Bundle b = newRestrictions("a");
+ final Bundle c = newRestrictions("a");
+ final Bundle d = newRestrictions("b", "c");
+ final Bundle e = newRestrictions("b", "c");
+
+ assertTrue(checkAssertRestrictions(null, null));
+ assertFalse(checkAssertRestrictions(null, a));
+ assertFalse(checkAssertRestrictions(a, null));
+ assertTrue(checkAssertRestrictions(a, a));
+
+ assertFalse(checkAssertRestrictions(a, b));
+ assertTrue(checkAssertRestrictions(b, c));
+
+ assertFalse(checkAssertRestrictions(c, d));
+ assertTrue(checkAssertRestrictions(d, e));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
index 79845d2..4e11762 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
@@ -47,31 +47,6 @@
(mmma frameworks/base/services/tests/servicestests/ for non-ninja build)
*/
public class OwnersTest extends DpmTestBase {
- private String readAsset(String assetPath) throws IOException {
- final StringBuilder sb = new StringBuilder();
- try (BufferedReader br = new BufferedReader(
- new InputStreamReader(
- mRealTestContext.getResources().getAssets().open(assetPath)))) {
- String line;
- while ((line = br.readLine()) != null) {
- sb.append(line);
- sb.append(System.lineSeparator());
- }
- }
- return sb.toString();
- }
-
- private void createLegacyFile(File path, String content)
- throws IOException {
- path.getParentFile().mkdirs();
-
- try (FileWriter writer = new FileWriter(path)) {
- Log.i(TAG, "Writing to " + path);
- Log.i(TAG, content);
- writer.write(content);
- }
- }
-
public void testUpgrade01() throws Exception {
getContext().addUsers(10, 11, 20, 21);
@@ -79,8 +54,8 @@
{
final OwnersTestable owners = new OwnersTestable(getContext());
- createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
- readAsset("OwnersTest/test01/input.xml"));
+ DpmTestUtils.writeToFile(owners.getLegacyConfigFileWithTestOverride(),
+ DpmTestUtils.readAsset(mRealTestContext, "OwnersTest/test01/input.xml"));
owners.load();
@@ -99,6 +74,12 @@
assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
assertNull(owners.getSystemUpdatePolicy());
assertEquals(0, owners.getProfileOwnerKeys().size());
+
+ assertFalse(owners.getDeviceOwnerUserRestrictionsNeedsMigration());
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(10));
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(11));
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(20));
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(21));
}
// Then re-read and check.
@@ -110,6 +91,12 @@
assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
assertNull(owners.getSystemUpdatePolicy());
assertEquals(0, owners.getProfileOwnerKeys().size());
+
+ assertFalse(owners.getDeviceOwnerUserRestrictionsNeedsMigration());
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(10));
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(11));
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(20));
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(21));
}
}
@@ -120,8 +107,8 @@
{
final OwnersTestable owners = new OwnersTestable(getContext());
- createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
- readAsset("OwnersTest/test02/input.xml"));
+ DpmTestUtils.writeToFile(owners.getLegacyConfigFileWithTestOverride(),
+ DpmTestUtils.readAsset(mRealTestContext, "OwnersTest/test02/input.xml"));
owners.load();
@@ -142,6 +129,12 @@
assertNull(owners.getSystemUpdatePolicy());
assertEquals(0, owners.getProfileOwnerKeys().size());
+
+ assertTrue(owners.getDeviceOwnerUserRestrictionsNeedsMigration());
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(10));
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(11));
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(20));
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(21));
}
// Then re-read and check.
@@ -156,6 +149,12 @@
assertNull(owners.getSystemUpdatePolicy());
assertEquals(0, owners.getProfileOwnerKeys().size());
+
+ assertTrue(owners.getDeviceOwnerUserRestrictionsNeedsMigration());
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(10));
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(11));
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(20));
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(21));
}
}
@@ -166,8 +165,8 @@
{
final OwnersTestable owners = new OwnersTestable(getContext());
- createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
- readAsset("OwnersTest/test03/input.xml"));
+ DpmTestUtils.writeToFile(owners.getLegacyConfigFileWithTestOverride(),
+ DpmTestUtils.readAsset(mRealTestContext, "OwnersTest/test03/input.xml"));
owners.load();
@@ -196,6 +195,12 @@
owners.getProfileOwnerComponent(11));
assertEquals("1", owners.getProfileOwnerName(11));
assertEquals("com.google.android.testdpc1", owners.getProfileOwnerPackage(11));
+
+ assertFalse(owners.getDeviceOwnerUserRestrictionsNeedsMigration());
+ assertTrue(owners.getProfileOwnerUserRestrictionsNeedsMigration(10));
+ assertTrue(owners.getProfileOwnerUserRestrictionsNeedsMigration(11));
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(20));
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(21));
}
// Then re-read and check.
@@ -218,9 +223,19 @@
owners.getProfileOwnerComponent(11));
assertEquals("1", owners.getProfileOwnerName(11));
assertEquals("com.google.android.testdpc1", owners.getProfileOwnerPackage(11));
+
+ assertFalse(owners.getDeviceOwnerUserRestrictionsNeedsMigration());
+ assertTrue(owners.getProfileOwnerUserRestrictionsNeedsMigration(10));
+ assertTrue(owners.getProfileOwnerUserRestrictionsNeedsMigration(11));
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(20));
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(21));
}
}
+ /**
+ * Note this also tests {@link Owners#setDeviceOwnerUserRestrictionsMigrated()}
+ * and {@link Owners#setProfileOwnerUserRestrictionsMigrated(int)}.
+ */
public void testUpgrade04() throws Exception {
getContext().addUsers(10, 11, 20, 21);
@@ -228,8 +243,8 @@
{
final OwnersTestable owners = new OwnersTestable(getContext());
- createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
- readAsset("OwnersTest/test04/input.xml"));
+ DpmTestUtils.writeToFile(owners.getLegacyConfigFileWithTestOverride(),
+ DpmTestUtils.readAsset(mRealTestContext, "OwnersTest/test04/input.xml"));
owners.load();
@@ -262,6 +277,12 @@
owners.getProfileOwnerComponent(11));
assertEquals("1", owners.getProfileOwnerName(11));
assertEquals("com.google.android.testdpc1", owners.getProfileOwnerPackage(11));
+
+ assertTrue(owners.getDeviceOwnerUserRestrictionsNeedsMigration());
+ assertTrue(owners.getProfileOwnerUserRestrictionsNeedsMigration(10));
+ assertTrue(owners.getProfileOwnerUserRestrictionsNeedsMigration(11));
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(20));
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(21));
}
// Then re-read and check.
@@ -288,6 +309,40 @@
owners.getProfileOwnerComponent(11));
assertEquals("1", owners.getProfileOwnerName(11));
assertEquals("com.google.android.testdpc1", owners.getProfileOwnerPackage(11));
+
+ assertTrue(owners.getDeviceOwnerUserRestrictionsNeedsMigration());
+ assertTrue(owners.getProfileOwnerUserRestrictionsNeedsMigration(10));
+ assertTrue(owners.getProfileOwnerUserRestrictionsNeedsMigration(11));
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(20));
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(21));
+
+ owners.setDeviceOwnerUserRestrictionsMigrated();
+ }
+
+ {
+ final OwnersTestable owners = new OwnersTestable(getContext());
+ owners.load();
+
+ assertFalse(owners.getDeviceOwnerUserRestrictionsNeedsMigration());
+ assertTrue(owners.getProfileOwnerUserRestrictionsNeedsMigration(10));
+ assertTrue(owners.getProfileOwnerUserRestrictionsNeedsMigration(11));
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(20));
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(21));
+
+ owners.setProfileOwnerUserRestrictionsMigrated(11);
+ }
+
+ {
+ final OwnersTestable owners = new OwnersTestable(getContext());
+ owners.load();
+
+ assertFalse(owners.getDeviceOwnerUserRestrictionsNeedsMigration());
+ assertTrue(owners.getProfileOwnerUserRestrictionsNeedsMigration(10));
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(11));
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(20));
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(21));
+
+ owners.setProfileOwnerUserRestrictionsMigrated(11);
}
}
@@ -298,8 +353,8 @@
{
final OwnersTestable owners = new OwnersTestable(getContext());
- createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
- readAsset("OwnersTest/test05/input.xml"));
+ DpmTestUtils.writeToFile(owners.getLegacyConfigFileWithTestOverride(),
+ DpmTestUtils.readAsset(mRealTestContext, "OwnersTest/test05/input.xml"));
owners.load();
@@ -319,6 +374,12 @@
assertNull(owners.getSystemUpdatePolicy());
assertEquals(0, owners.getProfileOwnerKeys().size());
+
+ assertFalse(owners.getDeviceOwnerUserRestrictionsNeedsMigration());
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(10));
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(11));
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(20));
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(21));
}
// Then re-read and check.
@@ -332,6 +393,12 @@
assertNull(owners.getSystemUpdatePolicy());
assertEquals(0, owners.getProfileOwnerKeys().size());
+
+ assertFalse(owners.getDeviceOwnerUserRestrictionsNeedsMigration());
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(10));
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(11));
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(20));
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(21));
}
}
@@ -342,8 +409,8 @@
{
final OwnersTestable owners = new OwnersTestable(getContext());
- createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
- readAsset("OwnersTest/test06/input.xml"));
+ DpmTestUtils.writeToFile(owners.getLegacyConfigFileWithTestOverride(),
+ DpmTestUtils.readAsset(mRealTestContext, "OwnersTest/test06/input.xml"));
owners.load();
@@ -362,6 +429,12 @@
assertNotNull(owners.getSystemUpdatePolicy());
assertEquals(5, owners.getSystemUpdatePolicy().getPolicyType());
+
+ assertFalse(owners.getDeviceOwnerUserRestrictionsNeedsMigration());
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(10));
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(11));
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(20));
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(21));
}
// Then re-read and check.
@@ -375,6 +448,12 @@
assertNotNull(owners.getSystemUpdatePolicy());
assertEquals(5, owners.getSystemUpdatePolicy().getPolicyType());
+
+ assertFalse(owners.getDeviceOwnerUserRestrictionsNeedsMigration());
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(10));
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(11));
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(20));
+ assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(21));
}
}
@@ -384,8 +463,8 @@
final OwnersTestable owners = new OwnersTestable(getContext());
// First, migrate to create new-style config files.
- createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
- readAsset("OwnersTest/test04/input.xml"));
+ DpmTestUtils.writeToFile(owners.getLegacyConfigFileWithTestOverride(),
+ DpmTestUtils.readAsset(mRealTestContext, "OwnersTest/test04/input.xml"));
owners.load();
diff --git a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
index bd64392..0b73beb 100644
--- a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
@@ -6,6 +6,7 @@
import android.app.job.JobInfo;
import android.app.job.JobInfo.Builder;
import android.os.PersistableBundle;
+import android.os.SystemClock;
import android.test.AndroidTestCase;
import android.test.RenamingDelegatingContext;
import android.util.Log;
@@ -102,6 +103,14 @@
Iterator<JobStatus> it = jobStatusSet.iterator();
JobStatus loaded1 = it.next();
JobStatus loaded2 = it.next();
+
+ // Reverse them so we know which comparison to make.
+ if (loaded1.getJobId() != 8) {
+ JobStatus tmp = loaded1;
+ loaded1 = loaded2;
+ loaded2 = tmp;
+ }
+
assertTasksEqual(task1, loaded1.getJob());
assertTasksEqual(task2, loaded2.getJob());
assertTrue("JobStore#contains invalid.", mTaskStoreUnderTest.containsJob(taskStatus1));
@@ -143,6 +152,36 @@
assertTasksEqual(task, loaded.getJob());
}
+ public void testMassivePeriodClampedOnRead() throws Exception {
+ final long TEN_SECONDS = 10000L;
+ JobInfo.Builder b = new Builder(8, mComponent)
+ .setPeriodic(TEN_SECONDS)
+ .setPersisted(true);
+ final long invalidLateRuntimeElapsedMillis =
+ SystemClock.elapsedRealtime() + (TEN_SECONDS * 2) + 5000; // >2P from now.
+ final long invalidEarlyRuntimeElapsedMillis =
+ invalidLateRuntimeElapsedMillis - TEN_SECONDS; // Early is (late - period).
+ final JobStatus js = new JobStatus(b.build(), SOME_UID,
+ invalidEarlyRuntimeElapsedMillis, invalidLateRuntimeElapsedMillis);
+
+ mTaskStoreUnderTest.add(js);
+ Thread.sleep(IO_WAIT);
+
+ final ArraySet<JobStatus> jobStatusSet = new ArraySet<JobStatus>();
+ mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet);
+ assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size());
+ JobStatus loaded = jobStatusSet.iterator().next();
+
+ // Assert early runtime was clamped to be under now + period. We can do <= here b/c we'll
+ // call SystemClock.elapsedRealtime after doing the disk i/o.
+ final long newNowElapsed = SystemClock.elapsedRealtime();
+ assertTrue("Early runtime wasn't correctly clamped.",
+ loaded.getEarliestRunTime() <= newNowElapsed + TEN_SECONDS);
+ // Assert late runtime was clamped to be now + period*2.
+ assertTrue("Early runtime wasn't correctly clamped.",
+ loaded.getEarliestRunTime() <= newNowElapsed + TEN_SECONDS*2);
+ }
+
/**
* Helper function to throw an error if the provided task and TaskStatus objects are not equal.
*/
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
index eb7eb15..9e70138 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -46,11 +46,11 @@
public void testWriteReadApplicationRestrictions() throws IOException {
AtomicFile atomicFile = new AtomicFile(restrictionsFile);
Bundle bundle = createBundle();
- UserManagerService.writeApplicationRestrictionsLocked(bundle, atomicFile);
+ UserManagerService.writeApplicationRestrictionsLP(bundle, atomicFile);
assertTrue(atomicFile.getBaseFile().exists());
String s = FileUtils.readTextFile(restrictionsFile, 10000, "");
System.out.println("restrictionsFile: " + s);
- bundle = UserManagerService.readApplicationRestrictionsLocked(atomicFile);
+ bundle = UserManagerService.readApplicationRestrictionsLP(atomicFile);
System.out.println("readApplicationRestrictionsLocked bundle: " + bundle);
assertBundle(bundle);
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 86f5ed7..66c7dbb 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -211,11 +211,14 @@
public void testRestrictions() {
UserInfo testUser = createUser("User 1", 0);
- Bundle restrictions = new Bundle();
- restrictions.putBoolean(UserManager.DISALLOW_INSTALL_APPS, true);
- restrictions.putBoolean(UserManager.DISALLOW_CONFIG_WIFI, false);
- mUserManager.setUserRestrictions(restrictions, new UserHandle(testUser.id));
+
+ mUserManager.setUserRestriction(
+ UserManager.DISALLOW_INSTALL_APPS, true, new UserHandle(testUser.id));
+ mUserManager.setUserRestriction(
+ UserManager.DISALLOW_CONFIG_WIFI, false, new UserHandle(testUser.id));
+
Bundle stored = mUserManager.getUserRestrictions(new UserHandle(testUser.id));
+ // Note this will fail if DO already sets those restrictions.
assertEquals(stored.getBoolean(UserManager.DISALLOW_CONFIG_WIFI), false);
assertEquals(stored.getBoolean(UserManager.DISALLOW_UNINSTALL_APPS), false);
assertEquals(stored.getBoolean(UserManager.DISALLOW_INSTALL_APPS), true);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 13e600e..2b0919b 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -215,11 +215,11 @@
mPowerManager = getContext().getSystemService(PowerManager.class);
mScreenOnSystemTimeSnapshot = System.currentTimeMillis();
- synchronized (this) {
+ synchronized (mLock) {
mScreenOnTime = readScreenOnTimeLocked();
}
- mDisplayManager.registerDisplayListener(mDisplayListener, null);
- synchronized (this) {
+ mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
+ synchronized (mLock) {
updateDisplayLocked();
}
} else if (phase == PHASE_BOOT_COMPLETED) {
@@ -385,9 +385,11 @@
timeNow);
final int packageCount = packages.size();
for (int p = 0; p < packageCount; p++) {
- final String packageName = packages.get(p).packageName;
- final boolean isIdle = isAppIdleFiltered(packageName, userId, service, timeNow,
- screenOnTime);
+ final PackageInfo pi = packages.get(p);
+ final String packageName = pi.packageName;
+ final boolean isIdle = isAppIdleFiltered(packageName,
+ UserHandle.getAppId(pi.applicationInfo.uid),
+ userId, service, timeNow, screenOnTime);
mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS,
userId, isIdle ? 1 : 0, packageName));
mAppIdleHistory.addEntry(packageName, userId, isIdle, timeNow);
@@ -769,10 +771,17 @@
if (mAppIdleParoled) {
return false;
}
- return isAppIdleFiltered(packageName, userId, timeNow);
+ try {
+ ApplicationInfo ai = getContext().getPackageManager().getApplicationInfo(packageName,
+ PackageManager.GET_UNINSTALLED_PACKAGES
+ | PackageManager.GET_DISABLED_COMPONENTS);
+ return isAppIdleFiltered(packageName, ai.uid, userId, timeNow);
+ } catch (PackageManager.NameNotFoundException e) {
+ }
+ return false;
}
- boolean isAppIdleFiltered(String packageName, int userId, long timeNow) {
+ boolean isAppIdleFiltered(String packageName, int uidForAppId, int userId, long timeNow) {
final UserUsageStatsService userService;
final long screenOnTime;
synchronized (mLock) {
@@ -782,7 +791,8 @@
userService = getUserDataAndInitializeIfNeededLocked(userId, timeNow);
screenOnTime = getScreenOnTimeLocked(timeNow);
}
- return isAppIdleFiltered(packageName, userId, userService, timeNow, screenOnTime);
+ return isAppIdleFiltered(packageName, UserHandle.getAppId(uidForAppId), userId,
+ userService, timeNow, screenOnTime);
}
/**
@@ -791,14 +801,22 @@
* This happens if the device is plugged in or temporarily allowed to make exceptions.
* Called by interface impls.
*/
- private boolean isAppIdleFiltered(String packageName, int userId,
+ private boolean isAppIdleFiltered(String packageName, int appId, int userId,
UserUsageStatsService userService, long timeNow, long screenOnTime) {
if (packageName == null) return false;
// If not enabled at all, of course nobody is ever idle.
if (!mAppIdleEnabled) {
return false;
}
- if (packageName.equals("android")) return false;
+ if (appId < Process.FIRST_APPLICATION_UID) {
+ // System uids never go idle.
+ return false;
+ }
+ if (packageName.equals("android")) {
+ // Nor does the framework (which should be redundant with the above, but for MR1 we will
+ // 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
@@ -865,8 +883,8 @@
ApplicationInfo ai = apps.get(i);
// Check whether this app is idle.
- boolean idle = isAppIdleFiltered(ai.packageName, userId, userService, timeNow,
- screenOnTime);
+ boolean idle = isAppIdleFiltered(ai.packageName, UserHandle.getAppId(ai.uid),
+ userId, userService, timeNow, screenOnTime);
int index = uidStates.indexOfKey(ai.uid);
if (index < 0) {
@@ -1352,8 +1370,8 @@
}
@Override
- public boolean isAppIdle(String packageName, int userId) {
- return UsageStatsService.this.isAppIdleFiltered(packageName, userId, -1);
+ public boolean isAppIdle(String packageName, int uidForAppId, int userId) {
+ return UsageStatsService.this.isAppIdleFiltered(packageName, uidForAppId, userId, -1);
}
@Override
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index 31763e7..129e537 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -212,6 +212,10 @@
}
private AlsaDevice waitForAlsaDevice(int card, int device, int type) {
+ if (DEBUG) {
+ Slog.e(TAG, "waitForAlsaDevice(c:" + card + " d:" + device + ")");
+ }
+
AlsaDevice testDevice = new AlsaDevice(type, card, device);
// This value was empirically determined.
@@ -292,7 +296,8 @@
*/
/* package */ UsbAudioDevice selectAudioCard(int card) {
if (DEBUG) {
- Slog.d(TAG, "selectAudioCard() card:" + card);
+ Slog.d(TAG, "selectAudioCard() card:" + card
+ + " isCardUsb(): " + mCardsParser.isCardUsb(card));
}
if (!mCardsParser.isCardUsb(card)) {
// Don't. AudioPolicyManager has logic for falling back to internal devices.
@@ -304,6 +309,10 @@
boolean hasPlayback = mDevicesParser.hasPlaybackDevices(card);
boolean hasCapture = mDevicesParser.hasCaptureDevices(card);
+ if (DEBUG) {
+ Slog.d(TAG, "usb: hasPlayback:" + hasPlayback + " hasCapture:" + hasCapture);
+ }
+
int deviceClass =
(mCardsParser.isCardUsb(card)
? UsbAudioDevice.kAudioDeviceClass_External
@@ -320,10 +329,6 @@
return null;
}
- if (DEBUG) {
- Slog.d(TAG, "usb: hasPlayback:" + hasPlayback + " hasCapture:" + hasCapture);
- }
-
UsbAudioDevice audioDevice =
new UsbAudioDevice(card, device, hasPlayback, hasCapture, deviceClass);
AlsaCardsParser.AlsaCardRecord cardRecord = mCardsParser.getCardRecordFor(card);
@@ -339,14 +344,13 @@
if (DEBUG) {
Slog.d(TAG, "UsbAudioManager.selectDefaultDevice()");
}
- mCardsParser.scan();
return selectAudioCard(mCardsParser.getDefaultCard());
}
/* package */ void usbDeviceAdded(UsbDevice usbDevice) {
if (DEBUG) {
Slog.d(TAG, "deviceAdded(): " + usbDevice.getManufacturerName() +
- "nm:" + usbDevice.getProductName());
+ " nm:" + usbDevice.getProductName());
}
// Is there an audio interface in there?
@@ -361,27 +365,22 @@
isAudioDevice = true;
}
}
+
+ if (DEBUG) {
+ Slog.d(TAG, " isAudioDevice: " + isAudioDevice);
+ }
if (!isAudioDevice) {
return;
}
- ArrayList<AlsaCardsParser.AlsaCardRecord> prevScanRecs = mCardsParser.getScanRecords();
- mCardsParser.scan();
-
- int addedCard = -1;
- ArrayList<AlsaCardsParser.AlsaCardRecord>
- newScanRecs = mCardsParser.getNewCardRecords(prevScanRecs);
- if (newScanRecs.size() > 0) {
- // This is where we select the just connected device
- // NOTE - to switch to prefering the first-connected device, just always
- // take the else clause below.
- addedCard = newScanRecs.get(0).mCardNum;
- } else {
- addedCard = mCardsParser.getDefaultUsbCard();
- }
+ int addedCard = mCardsParser.getDefaultUsbCard();
// If the default isn't a USB device, let the existing "select internal mechanism"
// handle the selection.
+ if (DEBUG) {
+ Slog.d(TAG, " mCardsParser.isCardUsb(" + addedCard + ") = "
+ + mCardsParser.isCardUsb(addedCard));
+ }
if (mCardsParser.isCardUsb(addedCard)) {
UsbAudioDevice audioDevice = selectAudioCard(addedCard);
if (audioDevice != null) {
@@ -429,6 +428,10 @@
}
}
}
+
+ if (DEBUG) {
+ Slog.d(TAG, "deviceAdded() - done");
+ }
}
/* package */ void usbDeviceRemoved(UsbDevice usbDevice) {
@@ -439,7 +442,7 @@
UsbAudioDevice audioDevice = mAudioDevices.remove(usbDevice);
if (audioDevice != null) {
- if (audioDevice.mHasPlayback || audioDevice.mHasPlayback) {
+ if (audioDevice.mHasPlayback || audioDevice.mHasCapture) {
notifyDeviceState(audioDevice, false);
// if there any external devices left, select one of them
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 3160e39..e0f95cf 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -756,6 +756,8 @@
if (mUsbDataUnlocked && active && mCurrentUser != UserHandle.USER_NULL) {
Slog.v(TAG, "Current user switched to " + mCurrentUser
+ "; resetting USB host stack for MTP or PTP");
+ // avoid leaking sensitive data from previous user
+ mUsbDataUnlocked = false;
setEnabledFunctions(mCurrentFunctions, true);
}
mCurrentUser = msg.arg1;
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java
index 3ca0c84..31d859f 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java
@@ -87,6 +87,7 @@
// This is an indirect indication of the microphone being open in some other application.
private boolean mServiceDisabled = false;
private boolean mStarted = false;
+ private boolean mRecognitionAborted = false;
private PowerSaveModeListener mPowerSaveModeListener;
SoundTriggerHelper(Context context) {
@@ -386,8 +387,9 @@
private void onRecognitionAbortLocked() {
Slog.w(TAG, "Recognition aborted");
- // No-op
- // This is handled via service state changes instead.
+ // If abort has been called, the hardware has already stopped recognition, so we shouldn't
+ // call it again when we process the state change.
+ mRecognitionAborted = true;
}
private void onRecognitionFailureLocked() {
@@ -490,8 +492,13 @@
}
return status;
} else {
- // Stop recognition.
- int status = mModule.stopRecognition(mCurrentSoundModelHandle);
+ // Stop recognition (only if we haven't been aborted).
+ int status = STATUS_OK;
+ if (!mRecognitionAborted) {
+ status = mModule.stopRecognition(mCurrentSoundModelHandle);
+ } else {
+ mRecognitionAborted = false;
+ }
if (status != SoundTrigger.STATUS_OK) {
Slog.w(TAG, "stopRecognition call failed with " + status);
if (notify) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index a96c164..f396c2d 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -291,21 +291,26 @@
String curService = Settings.Secure.getStringForUser(
mResolver, Settings.Secure.VOICE_INTERACTION_SERVICE, mCurUser);
ComponentName serviceComponent = null;
+ ServiceInfo serviceInfo = null;
if (curService != null && !curService.isEmpty()) {
try {
serviceComponent = ComponentName.unflattenFromString(curService);
- } catch (RuntimeException e) {
+ serviceInfo = AppGlobals.getPackageManager()
+ .getServiceInfo(serviceComponent, 0, mCurUser);
+ } catch (RuntimeException | RemoteException e) {
Slog.wtf(TAG, "Bad voice interaction service name " + curService, e);
serviceComponent = null;
+ serviceInfo = null;
}
}
+
if (force || mImpl == null || mImpl.mUser != mCurUser
|| !mImpl.mComponent.equals(serviceComponent)) {
mSoundTriggerHelper.stopAllRecognitions();
if (mImpl != null) {
mImpl.shutdownLocked();
}
- if (serviceComponent != null) {
+ if (serviceComponent != null && serviceInfo != null) {
mImpl = new VoiceInteractionManagerServiceImpl(mContext,
UiThread.getHandler(), this, mCurUser, serviceComponent);
mImpl.startLocked();
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index 5c64168..5ecd2b5 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -52,6 +52,32 @@
public final class PhoneAccount implements Parcelable {
/**
+ * {@link PhoneAccount} extras key (see {@link PhoneAccount#getExtras()}) which determines the
+ * maximum permitted length of a call subject specified via the
+ * {@link TelecomManager#EXTRA_CALL_SUBJECT} extra on an
+ * {@link android.content.Intent#ACTION_CALL} intent. Ultimately a {@link ConnectionService} is
+ * responsible for enforcing the maximum call subject length when sending the message, however
+ * this extra is provided so that the user interface can proactively limit the length of the
+ * call subject as the user types it.
+ */
+ public static final String EXTRA_CALL_SUBJECT_MAX_LENGTH =
+ "android.telecom.extra.CALL_SUBJECT_MAX_LENGTH";
+
+ /**
+ * {@link PhoneAccount} extras key (see {@link PhoneAccount#getExtras()}) which determines the
+ * character encoding to be used when determining the length of messages.
+ * The user interface can use this when determining the number of characters the user may type
+ * in a call subject. If empty-string, the call subject message size limit will be enforced on
+ * a 1:1 basis. That is, each character will count towards the messages size limit as a single
+ * character. If a character encoding is specified, the message size limit will be based on the
+ * number of bytes in the message per the specified encoding. See
+ * {@link #EXTRA_CALL_SUBJECT_MAX_LENGTH} for more information on the call subject maximum
+ * length.
+ */
+ public static final String EXTRA_CALL_SUBJECT_CHARACTER_ENCODING =
+ "android.telecom.extra.CALL_SUBJECT_CHARACTER_ENCODING";
+
+ /**
* Flag indicating that this {@code PhoneAccount} can act as a connection manager for
* other connections. The {@link ConnectionService} associated with this {@code PhoneAccount}
* will be allowed to manage phone calls including using its own proprietary phone-call
@@ -122,6 +148,14 @@
public static final int CAPABILITY_CALL_SUBJECT = 0x40;
/**
+ * Flag indicating that this {@code PhoneAccount} should only be used for emergency calls.
+ * <p>
+ * See {@link #getCapabilities}
+ * @hide
+ */
+ public static final int CAPABILITY_EMERGENCY_CALLS_ONLY = 0x80;
+
+ /**
* URI scheme for telephone number URIs.
*/
public static final String SCHEME_TEL = "tel";
@@ -205,6 +239,7 @@
mSupportedUriSchemes.addAll(phoneAccount.getSupportedUriSchemes());
mIcon = phoneAccount.getIcon();
mIsEnabled = phoneAccount.isEnabled();
+ mExtras = phoneAccount.getExtras();
}
/**
@@ -584,9 +619,8 @@
out.writeInt(1);
mIcon.writeToParcel(out, flags);
}
-
- out.writeBundle(mExtras);
out.writeByte((byte) (mIsEnabled ? 1 : 0));
+ out.writeBundle(mExtras);
}
public static final Creator<PhoneAccount> CREATOR
@@ -628,8 +662,8 @@
} else {
mIcon = null;
}
- mExtras = in.readBundle();
mIsEnabled = in.readByte() == 1;
+ mExtras = in.readBundle();
}
@Override
@@ -645,7 +679,7 @@
sb.append(scheme)
.append(" ");
}
- sb.append(" Extras : ");
+ sb.append(" Extras: ");
sb.append(mExtras);
sb.append("]");
return sb.toString();
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index ba91254..8ebef32 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -191,7 +191,7 @@
public static final String
KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool";
- /**
+ /**
* Override the platform's notion of a network operator being considered roaming.
* Value is string array of MCCMNCs to be considered roaming for 3GPP RATs.
*/
@@ -268,6 +268,13 @@
= "carrier_allow_turnoff_ims_bool";
/**
+ * Flag specifying whether Generic Bootstrapping Architecture capable SIM is required for IMS.
+ * @hide
+ */
+ public static final String KEY_CARRIER_IMS_GBA_REQUIRED_BOOL
+ = "carrier_ims_gba_required_bool";
+
+ /**
* Flag specifying whether IMS instant lettering is available for the carrier. {@code True} if
* instant lettering is available for the carrier, {@code false} otherwise.
* @hide
@@ -468,6 +475,12 @@
*/
public static final String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool";
+ /**
+ * Allow user to add APNs
+ * @hide
+ */
+ public static final String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool";
+
// These variables are used by the MMS service and exposed through another API, {@link
// SmsManager}. The variable names and string values are copied from there.
public static final String KEY_MMS_ALIAS_ENABLED_BOOL = "aliasEnabled";
@@ -529,6 +542,7 @@
sDefaults.putBoolean(KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL, true);
sDefaults.putBoolean(KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL, true);
+ sDefaults.putBoolean(KEY_CARRIER_IMS_GBA_REQUIRED_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL, true);
sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_INVALID_CHARS_STRING, "");
@@ -567,6 +581,7 @@
sDefaults.putString(KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING, "");
sDefaults.putString(KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING, "");
sDefaults.putBoolean(KEY_CSP_ENABLED_BOOL, false);
+ sDefaults.putBoolean(KEY_ALLOW_ADDING_APNS_BOOL, true);
sDefaults.putBoolean(KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL, false);
sDefaults.putStringArray(KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY, null);
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index f535e5d..fced667 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -911,7 +911,7 @@
else if (tdScdmaDbm >= -49) level = SIGNAL_STRENGTH_GREAT;
else if (tdScdmaDbm >= -73) level = SIGNAL_STRENGTH_GOOD;
else if (tdScdmaDbm >= -97) level = SIGNAL_STRENGTH_MODERATE;
- else if (tdScdmaDbm >= -120) level = SIGNAL_STRENGTH_POOR;
+ else if (tdScdmaDbm >= -110) level = SIGNAL_STRENGTH_POOR;
else level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
if (DBG) log("getTdScdmaLevel = " + level);
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 37ffa06..d1d6e0d 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -332,9 +332,25 @@
return 0;
}
+ /**
+ * @hide
+ */
+ public static String givePrintableIccid(String iccId) {
+ String iccIdToPrint = null;
+ if (iccId != null) {
+ if (iccId.length() > 9) {
+ iccIdToPrint = iccId.substring(0, 9) + "XXXXXXXXXXX";
+ } else {
+ iccIdToPrint = iccId;
+ }
+ }
+ return iccIdToPrint;
+ }
+
@Override
public String toString() {
- return "{id=" + mId + ", iccId=" + mIccId + " simSlotIndex=" + mSimSlotIndex
+ String iccIdToPrint = givePrintableIccid(mIccId);
+ return "{id=" + mId + ", iccId=" + iccIdToPrint + " simSlotIndex=" + mSimSlotIndex
+ " displayName=" + mDisplayName + " carrierName=" + mCarrierName
+ " nameSource=" + mNameSource + " iconTint=" + mIconTint
+ " dataRoaming=" + mDataRoaming + " iconBitmap=" + mIconBitmap + " mcc " + mMcc
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index d22727d..3b281e2 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2874,7 +2874,7 @@
/**
* Returns all observed cell information from all radios on the
* device including the primary and neighboring cells. This does
- * not cause or change the rate of PhoneStateListner#onCellInfoChanged.
+ * not cause or change the rate of PhoneStateListener#onCellInfoChanged.
*<p>
* The list can include one or more of {@link android.telephony.CellInfoGsm CellInfoGsm},
* {@link android.telephony.CellInfoCdma CellInfoCdma},
@@ -2888,6 +2888,9 @@
* devices this may return null in which case getCellLocation should
* be called.
*<p>
+ * This API will return valid data for registered cells on devices with
+ * {@link android.content.pm.PackageManager#FEATURE_TELEPHONY}
+ *<p>
* @return List of CellInfo or null if info unavailable.
*
* <p>Requires Permission: {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}
@@ -4845,4 +4848,21 @@
}
return null;
}
-}
+
+ /**
+ * Returns the service state information on specified subscription. Callers require
+ * either READ_PRIVILEGED_PHONE_STATE or READ_PHONE_STATE to retrieve the information.
+ * @hide
+ */
+ public ServiceState getServiceStateForSubscriber(int subId) {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.getServiceStateForSubscriber(subId, getOpPackageName());
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#getServiceStateForSubscriber", e);
+ }
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/telephony/java/com/android/ims/ImsCallProfile.java b/telephony/java/com/android/ims/ImsCallProfile.java
index f263b4d..5f84e0c 100644
--- a/telephony/java/com/android/ims/ImsCallProfile.java
+++ b/telephony/java/com/android/ims/ImsCallProfile.java
@@ -178,6 +178,7 @@
* Codec: Codec info.
* DisplayText: Display text for the call.
* AdditionalCallInfo: Additional call info.
+ * CallRadioTech: The radio tech on which the call is placed.
*/
public static final String EXTRA_OI = "oi";
public static final String EXTRA_CNA = "cna";
@@ -188,6 +189,21 @@
public static final String EXTRA_DISPLAY_TEXT = "DisplayText";
public static final String EXTRA_ADDITIONAL_CALL_INFO = "AdditionalCallInfo";
+ /**
+ * Extra key which the RIL can use to indicate the radio technology used for a call.
+ * Valid values are:
+ * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE},
+ * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_IWLAN}, and the other defined
+ * {@code RIL_RADIO_TECHNOLOGY_*} constants.
+ * Note: Despite the fact the {@link android.telephony.ServiceState} values are integer
+ * constants, the values passed for the {@link #EXTRA_CALL_RAT_TYPE} should be strings (e.g.
+ * "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.
+ */
+ public static final String EXTRA_CALL_RAT_TYPE = "CallRadioTech";
+
public int mServiceType;
public int mCallType;
public int mRestrictCause = CALL_RESTRICT_CAUSE_NONE;
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index dcece26..8172e94 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -21,9 +21,10 @@
import android.telecom.PhoneAccount;
import android.telephony.CellInfo;
import android.telephony.IccOpenLogicalChannelResponse;
+import android.telephony.ModemActivityInfo;
import android.telephony.NeighboringCellInfo;
import android.telephony.RadioAccessFamily;
-import android.telephony.ModemActivityInfo;
+import android.telephony.ServiceState;
import com.android.internal.telephony.CellNetworkScanResult;
import com.android.internal.telephony.OperatorInfo;
import java.util.List;
@@ -1005,4 +1006,12 @@
* Return the modem activity info.
*/
ModemActivityInfo getModemActivityInfo();
+
+ /**
+ * Get the service state on specified subscription
+ * @param subId Subscription id
+ * @param callingPackage The package making the call
+ * @return Service state on specified subscription.
+ */
+ ServiceState getServiceStateForSubscriber(int subId, String callingPackage);
}
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index f563839..d2e4de3 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -144,28 +144,6 @@
= "android.intent.action.ANY_DATA_STATE";
/**
- * Broadcast Action: Occurs when a data connection connects to a provisioning apn
- * and is broadcast by the low level data connection code.
- * The intent will have the following extra values:</p>
- * <dl>
- * <dt>apn</dt><dd>A string that is the APN associated with this connection.</dd>
- * <dt>apnType</dt><dd>A string array of APN types associated with this connection.
- * The APN type {@code *} is a special type that means this APN services all types.</dd>
- * <dt>linkProperties</dt><dd>{@code LinkProperties} for this APN.</dd>
- * <dt>linkCapabilities</dt><dd>The {@code LinkCapabilities} for this APN.</dd>
- * <dt>iface</dt><dd>A string that is the name of the interface.</dd>
- * </dl>
- *
- * <p class="note">
- * Requires the READ_PHONE_STATE permission.
- *
- * <p class="note">This is a protected intent that can only be sent
- * by the system.
- */
- public static final String ACTION_DATA_CONNECTION_CONNECTED_TO_PROVISIONING_APN
- = "android.intent.action.DATA_CONNECTION_CONNECTED_TO_PROVISIONING_APN";
-
- /**
* Broadcast Action: An attempt to establish a data connection has failed.
* The intent will have the following extra values:</p>
* <dl>
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index e09d124..4821678 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -177,6 +177,18 @@
}
@Override
+ public File getDeviceEncryptedFilesDir() {
+ throw new UnsupportedOperationException();
+ }
+
+ /** {@hide} */
+ @SystemApi
+ @Override
+ public File getCredentialEncryptedFilesDir() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public File getNoBackupFilesDir() {
throw new UnsupportedOperationException();
}
diff --git a/tests/NetworkSecurityConfigTest/Android.mk b/tests/NetworkSecurityConfigTest/Android.mk
new file mode 100644
index 0000000..a63162d
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/Android.mk
@@ -0,0 +1,15 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# We only want this apk build for tests.
+LOCAL_MODULE_TAGS := tests
+LOCAL_CERTIFICATE := platform
+
+LOCAL_JAVA_LIBRARIES := android.test.runner bouncycastle conscrypt
+
+# Include all test java files.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := NetworkSecurityConfigTests
+
+include $(BUILD_PACKAGE)
diff --git a/tests/NetworkSecurityConfigTest/AndroidManifest.xml b/tests/NetworkSecurityConfigTest/AndroidManifest.xml
new file mode 100644
index 0000000..4c1fbd3
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.security.net.config"
+ android:sharedUserId="android.uid.system">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="android.security.net.config"
+ android:label="ANSC Tests">
+ </instrumentation>
+</manifest>
diff --git a/tests/NetworkSecurityConfigTest/res/raw/ca_certs_der.der b/tests/NetworkSecurityConfigTest/res/raw/ca_certs_der.der
new file mode 100644
index 0000000..235bd47
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/raw/ca_certs_der.der
Binary files differ
diff --git a/tests/NetworkSecurityConfigTest/res/raw/ca_certs_pem.pem b/tests/NetworkSecurityConfigTest/res/raw/ca_certs_pem.pem
new file mode 100644
index 0000000..413e3c0
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/raw/ca_certs_pem.pem
@@ -0,0 +1,35 @@
+-----BEGIN CERTIFICATE-----
+MIIDfTCCAuagAwIBAgIDErvmMA0GCSqGSIb3DQEBBQUAME4xCzAJBgNVBAYTAlVT
+MRAwDgYDVQQKEwdFcXVpZmF4MS0wKwYDVQQLEyRFcXVpZmF4IFNlY3VyZSBDZXJ0
+aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDIwNTIxMDQwMDAwWhcNMTgwODIxMDQwMDAw
+WjBCMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UE
+AxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9m
+OSm9BXiLnTjoBbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIu
+T8rxh0PBFpVXLVDviS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6c
+JmTM386DGXHKTubU1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmR
+Cw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5asz
+PeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQABo4HwMIHtMB8GA1UdIwQYMBaAFEjm
+aPkr0rKV10fYIyAQTzOYkJ/UMB0GA1UdDgQWBBTAephojYn7qwVkDBF9qn1luMrM
+TjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjA6BgNVHR8EMzAxMC+g
+LaArhilodHRwOi8vY3JsLmdlb3RydXN0LmNvbS9jcmxzL3NlY3VyZWNhLmNybDBO
+BgNVHSAERzBFMEMGBFUdIAAwOzA5BggrBgEFBQcCARYtaHR0cHM6Ly93d3cuZ2Vv
+dHJ1c3QuY29tL3Jlc291cmNlcy9yZXBvc2l0b3J5MA0GCSqGSIb3DQEBBQUAA4GB
+AHbhEm5OSxYShjAGsoEIz/AIx8dxfmbuwu3UOx//8PDITtZDOLC5MH0Y0FWDomrL
+NhGc6Ehmo21/uBPUR/6LWlxz/K7ZGzIZOKuXNBSqltLroxwUCEm2u+WR74M26x1W
+b8ravHNjkOR/ez4iyz0H7V84dJzjA1BOoa+Y7mHyhD8S
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICPDCCAaUCEDyRMcsf9tAbDpq40ES/Er4wDQYJKoZIhvcNAQEFBQAwXzELMAkG
+A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
+cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2
+MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV
+BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt
+YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN
+ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE
+BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is
+I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G
+CSqGSIb3DQEBBQUAA4GBABByUqkFFBkyCEHwxWsKzH4PIRnN5GfcX6kb5sroc50i
+2JhucwNhkcV8sEVAbkSdjbCxlnRhLQ2pRdKkkirWmnWXbj9T/UWZYB2oK0z5XqcJ
+2HUw19JlYD1n1khVdWk/kfVIC0dpImmClr7JyDiGSnoscxlIaU5rfGW/D/xwzoiQ
+-----END CERTIFICATE-----
diff --git a/tests/NetworkSecurityConfigTest/res/raw/test_debug_ca.pem b/tests/NetworkSecurityConfigTest/res/raw/test_debug_ca.pem
new file mode 100644
index 0000000..81648d9
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/raw/test_debug_ca.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDITCCAgmgAwIBAgIJAP/YiWztz/J7MA0GCSqGSIb3DQEBCwUAMCcxFjAUBgNV
+BAMMDVRlc3QgZGVidWcgQ0ExDTALBgNVBAoMBEFPU1AwHhcNMTUxMTA5MjEyNjQ2
+WhcNMTgwODI5MjEyNjQ2WjAnMRYwFAYDVQQDDA1UZXN0IGRlYnVnIENBMQ0wCwYD
+VQQKDARBT1NQMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuPFmkOJj
+ehjfvdDr2qTcBWNqNATrW1SuM88Vj00ubUFQ4tZElozj8YnQOw1FeC79c1k88b8R
+6jcqYYp/mw2JYoD6yWcFPHo5BplIpk0EhIUARH/aeoclHvsUN2GGDyTO0vf0CfJn
+9Wp6lSLjyq7V/6tYdk+0cL632t56MHp8TCO+AaveYP1T8JZqx0/50xNcsK7lIqNa
+ctWyRGFxR4ifdVsgkw9WhAB/Ow2uOwN9uLGqzsCd+yXW2weX52EIivoTGZfJo+U8
+Fi0ygnCHBv2jsJA7yWLhHmZ4ijsVtfutIKmN0w+DHkl6S25girXhy0zJp/1QvHGm
+jaF60V1gw471jQIDAQABo1AwTjAdBgNVHQ4EFgQUoq66jncy83L5eeyW1g78s/uq
+iyQwHwYDVR0jBBgwFoAUoq66jncy83L5eeyW1g78s/uqiyQwDAYDVR0TBAUwAwEB
+/zANBgkqhkiG9w0BAQsFAAOCAQEAohytuH4CdX0gO8EGVDRVurRH7LO69lwd/6Iw
+hJ1lIK/mzj5RM2itVGTkintyHCLu5giVkHn4FHg4X9qzZaTPOcXv9ntQNS2nacZe
+bY8nfhsAhstJT4nIOWHE3FrZkMDOK6nZHIzfscX3V/VVq5MeA+WzXwmKp6MBNr+E
+oUegXCGjd26Bl6SFz3rD7Qh+dzSTtyf/ECzXaMjpZu3k6fb4EgRz6vdBCHKKtpv6
+Mxcr0nLwdI6LnAGXvJLV4sj+l6Ngg00EeyorG8ATgtmsUrXXOR1e+yDCQv6fjQfs
+CWYztECAUE9hfCXJwb0TBrq9YeJAvcO7iE6S0Pq+X3xNtetE1A==
+-----END CERTIFICATE-----
diff --git a/tests/NetworkSecurityConfigTest/res/xml/attributes.xml b/tests/NetworkSecurityConfigTest/res/xml/attributes.xml
new file mode 100644
index 0000000..eff13c8
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/attributes.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+ <base-config cleartextTrafficPermitted="false" hstsEnforced="true">
+ </base-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/bad_config0.xml b/tests/NetworkSecurityConfigTest/res/xml/bad_config0.xml
new file mode 100644
index 0000000..6af855d
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/bad_config0.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+ <domain-config>
+ <domain>android.com</domain>
+ <pin-set>
+ <!-- Bad pin digest -->
+ <pin digest="I am probably not an algorithm">1HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
+ </pin-set>
+ </domain-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/bad_config1.xml b/tests/NetworkSecurityConfigTest/res/xml/bad_config1.xml
new file mode 100644
index 0000000..d683b74
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/bad_config1.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+ <domain-config>
+ <domain>android.com</domain>
+ <pin-set>
+ <!-- Unknown pin digest -->
+ <pin>1HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
+ </pin-set>
+ </domain-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/bad_config2.xml b/tests/NetworkSecurityConfigTest/res/xml/bad_config2.xml
new file mode 100644
index 0000000..6f3f8b4
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/bad_config2.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+ <domain-config>
+ <domain>android.com</domain>
+ <pin-set>
+ <!-- empty digest -->
+ <pin digest="SHA-256"></pin>
+ </pin-set>
+ </domain-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/bad_config3.xml b/tests/NetworkSecurityConfigTest/res/xml/bad_config3.xml
new file mode 100644
index 0000000..fb2126c
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/bad_config3.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+ <domain-config>
+ <domain>android.com</domain>
+ </domain-config>
+ <domain-config>
+ <!-- Same domain name used in two configs -->
+ <domain>android.com</domain>
+ </domain-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/bad_config4.xml b/tests/NetworkSecurityConfigTest/res/xml/bad_config4.xml
new file mode 100644
index 0000000..95972ce
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/bad_config4.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+ <base-config>
+ <!-- domains are not allowed in base-config -->
+ <domain>android.com</domain>
+ </base-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/bad_config5.xml b/tests/NetworkSecurityConfigTest/res/xml/bad_config5.xml
new file mode 100644
index 0000000..8b6b721
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/bad_config5.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+ <base-config>
+ <!-- pins are not allowed in base-config -->
+ <pin-set>
+ </pin-set>
+ </base-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/bad_pin.xml b/tests/NetworkSecurityConfigTest/res/xml/bad_pin.xml
new file mode 100644
index 0000000..62a7b88
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/bad_pin.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+ <domain-config>
+ <domain>android.com</domain>
+ <pin-set>
+ <pin digest="SHA-256">1HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
+ </pin-set>
+ </domain-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/debug_basic.xml b/tests/NetworkSecurityConfigTest/res/xml/debug_basic.xml
new file mode 100644
index 0000000..8da9317
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/debug_basic.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+ <base-config>
+ <trust-anchors>
+ </trust-anchors>
+ </base-config>
+ <debug-overrides>
+ <trust-anchors>
+ <certificates src="system" />
+ </trust-anchors>
+ </debug-overrides>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/debug_domain.xml b/tests/NetworkSecurityConfigTest/res/xml/debug_domain.xml
new file mode 100644
index 0000000..24eed7a
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/debug_domain.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+ <domain-config>
+ <domain>android.com</domain>
+ <trust-anchors>
+ <certificates src="@raw/ca_certs_pem" />
+ </trust-anchors>
+ </domain-config>
+ <debug-overrides>
+ <trust-anchors>
+ <certificates src="@raw/test_debug_ca" />
+ </trust-anchors>
+ </debug-overrides>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/debug_inherit.xml b/tests/NetworkSecurityConfigTest/res/xml/debug_inherit.xml
new file mode 100644
index 0000000..ce0cbc8
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/debug_inherit.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+ <debug-overrides>
+ <trust-anchors>
+ <certificates src="@raw/test_debug_ca" />
+ </trust-anchors>
+ </debug-overrides>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/domain1.xml b/tests/NetworkSecurityConfigTest/res/xml/domain1.xml
new file mode 100644
index 0000000..6d8565c
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/domain1.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+ <base-config>
+ <trust-anchors>
+ </trust-anchors>
+ </base-config>
+ <domain-config>
+ <domain>android.com</domain>
+ <trust-anchors>
+ <certificates src="system" />
+ <certificates src="user" />
+ </trust-anchors>
+ </domain-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/empty_config.xml b/tests/NetworkSecurityConfigTest/res/xml/empty_config.xml
new file mode 100644
index 0000000..1bd94b6
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/empty_config.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/empty_trust.xml b/tests/NetworkSecurityConfigTest/res/xml/empty_trust.xml
new file mode 100644
index 0000000..8093b9d
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/empty_trust.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+ <base-config>
+ <trust-anchors>
+ </trust-anchors>
+ </base-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/expired_pin.xml b/tests/NetworkSecurityConfigTest/res/xml/expired_pin.xml
new file mode 100644
index 0000000..f9f8465
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/expired_pin.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+ <domain-config>
+ <domain>android.com</domain>
+ <!-- Invalid pin that has expired -->
+ <pin-set expiration="2015-01-01">
+ <pin digest="SHA-256">aaaaaaaaaaa2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
+ </pin-set>
+ </domain-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/multiple_configs.xml b/tests/NetworkSecurityConfigTest/res/xml/multiple_configs.xml
new file mode 100644
index 0000000..df08467
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/multiple_configs.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+ <base-config>
+ <trust-anchors>
+ </trust-anchors>
+ </base-config>
+ <domain-config>
+ <domain>android.com</domain>
+ <trust-anchors>
+ <certificates src="system" />
+ <certificates src="user" />
+ </trust-anchors>
+ </domain-config>
+ <domain-config>
+ <domain>google.com</domain>
+ <trust-anchors>
+ <certificates src="system" />
+ <certificates src="user" />
+ </trust-anchors>
+ </domain-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/multiple_domains.xml b/tests/NetworkSecurityConfigTest/res/xml/multiple_domains.xml
new file mode 100644
index 0000000..9743c5f
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/multiple_domains.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+ <base-config>
+ <trust-anchors>
+ </trust-anchors>
+ </base-config>
+ <domain-config>
+ <domain>android.com</domain>
+ <domain>google.com</domain>
+ <trust-anchors>
+ <certificates src="system" />
+ <certificates src="user" />
+ </trust-anchors>
+ </domain-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/nested_domains.xml b/tests/NetworkSecurityConfigTest/res/xml/nested_domains.xml
new file mode 100644
index 0000000..d45fd77
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/nested_domains.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+ <domain-config>
+ <domain includeSubdomains="true">android.com</domain>
+ <trust-anchors>
+ <certificates src="system" />
+ </trust-anchors>
+ <!-- nested config that adds pins -->
+ <domain-config>
+ <domain>developer.android.com</domain>
+ <pin-set>
+ <pin digest="SHA-256">7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
+ </pin-set>
+ </domain-config>
+ </domain-config>
+ <base-config cleartextTrafficPermitted="false">
+ </base-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/nested_domains_override.xml b/tests/NetworkSecurityConfigTest/res/xml/nested_domains_override.xml
new file mode 100644
index 0000000..84e06e3
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/nested_domains_override.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+ <base-config cleartextTrafficPermitted="false">
+ </base-config>
+ <!-- Nested config that overrides parent -->
+ <domain-config cleartextTrafficPermitted="true">
+ <domain includeSubdomains="true">android.com</domain>
+ <domain-config cleartextTrafficPermitted="false">
+ <domain>developer.android.com</domain>
+ </domain-config>
+ </domain-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/override_pins.xml b/tests/NetworkSecurityConfigTest/res/xml/override_pins.xml
new file mode 100644
index 0000000..785714a
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/override_pins.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+ <domain-config>
+ <domain>android.com</domain>
+ <pin-set>
+ <pin digest="SHA-256">aaaaaaaaIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
+ </pin-set>
+ <trust-anchors>
+ <certificates src="system" overridePins="true" />
+ </trust-anchors>
+ </domain-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/pins1.xml b/tests/NetworkSecurityConfigTest/res/xml/pins1.xml
new file mode 100644
index 0000000..1773d280
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/pins1.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+ <domain-config>
+ <domain>android.com</domain>
+ <pin-set>
+ <pin digest="SHA-256">7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
+ </pin-set>
+ </domain-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/resource_anchors_der.xml b/tests/NetworkSecurityConfigTest/res/xml/resource_anchors_der.xml
new file mode 100644
index 0000000..dfd6fd9
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/resource_anchors_der.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+ <base-config>
+ <trust-anchors>
+ </trust-anchors>
+ </base-config>
+ <domain-config>
+ <domain>android.com</domain>
+ <trust-anchors>
+ <certificates src="@raw/ca_certs_der" />
+ </trust-anchors>
+ </domain-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/resource_anchors_pem.xml b/tests/NetworkSecurityConfigTest/res/xml/resource_anchors_pem.xml
new file mode 100644
index 0000000..894f29b
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/resource_anchors_pem.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+ <base-config>
+ <trust-anchors>
+ </trust-anchors>
+ </base-config>
+ <domain-config>
+ <domain>android.com</domain>
+ <trust-anchors>
+ <certificates src="@raw/ca_certs_pem" />
+ </trust-anchors>
+ </domain-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/subdomains.xml b/tests/NetworkSecurityConfigTest/res/xml/subdomains.xml
new file mode 100644
index 0000000..482b26c
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/subdomains.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+ <base-config>
+ <trust-anchors>
+ </trust-anchors>
+ </base-config>
+ <domain-config>
+ <domain includeSubdomains="true">android.com</domain>
+ <trust-anchors>
+ <certificates src="system" />
+ <certificates src="user" />
+ </trust-anchors>
+ </domain-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java
new file mode 100644
index 0000000..9f48d56
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java
@@ -0,0 +1,211 @@
+/*
+ * 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 android.security.net.config;
+
+import android.app.Activity;
+import android.test.ActivityUnitTestCase;
+import android.util.ArraySet;
+import android.util.Pair;
+import java.io.IOException;
+import java.net.Socket;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.TrustManager;
+
+public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> {
+
+ public NetworkSecurityConfigTests() {
+ super(Activity.class);
+ }
+
+ // SHA-256 of the G2 intermediate CA for android.com (as of 10/2015).
+ private static final byte[] G2_SPKI_SHA256
+ = hexToBytes("ec722969cb64200ab6638f68ac538e40abab5b19a6485661042a1061c4612776");
+
+ private static byte[] hexToBytes(String s) {
+ int len = s.length();
+ byte[] data = new byte[len / 2];
+ for (int i = 0; i < len; i += 2) {
+ data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(
+ s.charAt(i + 1), 16));
+ }
+ return data;
+ }
+
+
+
+ /**
+ * Return a NetworkSecurityConfig that has an empty TrustAnchor set. This should always cause a
+ * SSLHandshakeException when used for a connection.
+ */
+ private NetworkSecurityConfig getEmptyConfig() {
+ return new NetworkSecurityConfig.Builder().build();
+ }
+
+ private NetworkSecurityConfig getSystemStoreConfig() {
+ return new NetworkSecurityConfig.Builder()
+ .addCertificatesEntryRef(
+ new CertificatesEntryRef(SystemCertificateSource.getInstance(), false))
+ .build();
+ }
+
+ public void testEmptyConfig() throws Exception {
+ ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
+ = new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
+ ConfigSource testSource =
+ new TestConfigSource(domainMap, getEmptyConfig());
+ SSLContext context = TestUtils.getSSLContext(testSource);
+ TestUtils.assertConnectionFails(context, "android.com", 443);
+ }
+
+ public void testEmptyPerNetworkSecurityConfig() throws Exception {
+ ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
+ = new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
+ domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
+ new Domain("android.com", true), getEmptyConfig()));
+ NetworkSecurityConfig defaultConfig = getSystemStoreConfig();
+ SSLContext context = TestUtils.getSSLContext(new TestConfigSource(domainMap, defaultConfig));
+ TestUtils.assertConnectionFails(context, "android.com", 443);
+ TestUtils.assertConnectionSucceeds(context, "google.com", 443);
+ }
+
+ public void testBadPin() throws Exception {
+ ArraySet<Pin> pins = new ArraySet<Pin>();
+ pins.add(new Pin("SHA-256", new byte[0]));
+ NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder()
+ .setPinSet(new PinSet(pins, Long.MAX_VALUE))
+ .addCertificatesEntryRef(
+ new CertificatesEntryRef(SystemCertificateSource.getInstance(), false))
+ .build();
+ ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
+ = new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
+ domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
+ new Domain("android.com", true), domain));
+ SSLContext context
+ = TestUtils.getSSLContext(new TestConfigSource(domainMap, getSystemStoreConfig()));
+ TestUtils.assertConnectionFails(context, "android.com", 443);
+ TestUtils.assertConnectionSucceeds(context, "google.com", 443);
+ }
+
+ public void testGoodPin() throws Exception {
+ ArraySet<Pin> pins = new ArraySet<Pin>();
+ pins.add(new Pin("SHA-256", G2_SPKI_SHA256));
+ NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder()
+ .setPinSet(new PinSet(pins, Long.MAX_VALUE))
+ .addCertificatesEntryRef(
+ new CertificatesEntryRef(SystemCertificateSource.getInstance(), false))
+ .build();
+ ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
+ = new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
+ domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
+ new Domain("android.com", true), domain));
+ SSLContext context
+ = TestUtils.getSSLContext(new TestConfigSource(domainMap, getEmptyConfig()));
+ TestUtils.assertConnectionSucceeds(context, "android.com", 443);
+ TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
+ }
+
+ public void testOverridePins() throws Exception {
+ // Use a bad pin + granting the system CA store the ability to override pins.
+ ArraySet<Pin> pins = new ArraySet<Pin>();
+ pins.add(new Pin("SHA-256", new byte[0]));
+ NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder()
+ .setPinSet(new PinSet(pins, Long.MAX_VALUE))
+ .addCertificatesEntryRef(
+ new CertificatesEntryRef(SystemCertificateSource.getInstance(), true))
+ .build();
+ ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
+ = new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
+ domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
+ new Domain("android.com", true), domain));
+ SSLContext context
+ = TestUtils.getSSLContext(new TestConfigSource(domainMap, getEmptyConfig()));
+ TestUtils.assertConnectionSucceeds(context, "android.com", 443);
+ }
+
+ public void testMostSpecificNetworkSecurityConfig() throws Exception {
+ ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
+ = new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
+ domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
+ new Domain("android.com", true), getEmptyConfig()));
+ domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
+ new Domain("developer.android.com", false), getSystemStoreConfig()));
+ SSLContext context
+ = TestUtils.getSSLContext(new TestConfigSource(domainMap, getEmptyConfig()));
+ TestUtils.assertConnectionFails(context, "android.com", 443);
+ TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
+ }
+
+ public void testSubdomainIncluded() throws Exception {
+ // First try connecting to a subdomain of a domain entry that includes subdomains.
+ ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
+ = new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
+ domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
+ new Domain("android.com", true), getSystemStoreConfig()));
+ SSLContext context
+ = TestUtils.getSSLContext(new TestConfigSource(domainMap, getEmptyConfig()));
+ TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
+ // Now try without including subdomains.
+ domainMap = new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
+ domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
+ new Domain("android.com", false), getSystemStoreConfig()));
+ context = TestUtils.getSSLContext(new TestConfigSource(domainMap, getEmptyConfig()));
+ TestUtils.assertConnectionFails(context, "developer.android.com", 443);
+ }
+
+ public void testConfigBuilderUsesParents() throws Exception {
+ // Check that a builder with a parent uses the parent's values when non is set.
+ NetworkSecurityConfig config = new NetworkSecurityConfig.Builder()
+ .setParent(NetworkSecurityConfig.getDefaultBuilder())
+ .build();
+ assert(!config.getTrustAnchors().isEmpty());
+ }
+
+ public void testConfigBuilderParentLoop() throws Exception {
+ NetworkSecurityConfig.Builder config1 = new NetworkSecurityConfig.Builder();
+ NetworkSecurityConfig.Builder config2 = new NetworkSecurityConfig.Builder();
+ config1.setParent(config2);
+ try {
+ config2.setParent(config1);
+ fail("Loop in NetworkSecurityConfig parents");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ public void testWithUrlConnection() throws Exception {
+ ArraySet<Pin> pins = new ArraySet<Pin>();
+ pins.add(new Pin("SHA-256", G2_SPKI_SHA256));
+ NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder()
+ .setPinSet(new PinSet(pins, Long.MAX_VALUE))
+ .addCertificatesEntryRef(
+ new CertificatesEntryRef(SystemCertificateSource.getInstance(), false))
+ .build();
+ ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
+ = new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
+ domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
+ new Domain("android.com", true), domain));
+ SSLContext context
+ = TestUtils.getSSLContext(new TestConfigSource(domainMap, getEmptyConfig()));
+ TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
+ TestUtils.assertUrlConnectionSucceeds(context, "developer.android.com", 443);
+ TestUtils.assertUrlConnectionFails(context, "google.com", 443);
+ }
+}
diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java
new file mode 100644
index 0000000..92eadc0
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java
@@ -0,0 +1,33 @@
+/*
+ * 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 android.security.net.config;
+
+import java.util.Set;
+import java.security.cert.X509Certificate;
+
+/** @hide */
+public class TestCertificateSource implements CertificateSource {
+
+ private final Set<X509Certificate> mCertificates;
+ public TestCertificateSource(Set<X509Certificate> certificates) {
+ mCertificates = certificates;
+ }
+
+ public Set<X509Certificate> getCertificates() {
+ return mCertificates;
+ }
+}
diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestConfigSource.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestConfigSource.java
new file mode 100644
index 0000000..609f481
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestConfigSource.java
@@ -0,0 +1,39 @@
+/*
+ * 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 android.security.net.config;
+
+import android.util.Pair;
+import java.util.Set;
+
+/** @hide */
+public class TestConfigSource implements ConfigSource {
+ private final Set<Pair<Domain, NetworkSecurityConfig>> mConfigs;
+ private final NetworkSecurityConfig mDefaultConfig;
+ public TestConfigSource(Set<Pair<Domain, NetworkSecurityConfig>> configs,
+ NetworkSecurityConfig defaultConfig) {
+ mConfigs = configs;
+ mDefaultConfig = defaultConfig;
+ }
+
+ public Set<Pair<Domain, NetworkSecurityConfig>> getPerDomainConfigs() {
+ return mConfigs;
+ }
+
+ public NetworkSecurityConfig getDefaultConfig() {
+ return mDefaultConfig;
+ }
+}
diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestUtils.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestUtils.java
new file mode 100644
index 0000000..f7590fd
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestUtils.java
@@ -0,0 +1,80 @@
+/*
+ * 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 android.security.net.config;
+
+import java.net.Socket;
+import java.net.URL;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+
+import junit.framework.Assert;
+
+public final class TestUtils extends Assert {
+
+ private TestUtils() {
+ }
+
+ public static void assertConnectionFails(SSLContext context, String host, int port)
+ throws Exception {
+ try {
+ Socket s = context.getSocketFactory().createSocket(host, port);
+ s.getInputStream();
+ fail("Expected connection to " + host + ":" + port + " to fail.");
+ } catch (SSLHandshakeException expected) {
+ }
+ }
+
+ public static void assertConnectionSucceeds(SSLContext context, String host, int port)
+ throws Exception {
+ Socket s = context.getSocketFactory().createSocket(host, port);
+ s.getInputStream();
+ }
+
+ public static void assertUrlConnectionFails(SSLContext context, String host, int port)
+ throws Exception {
+ URL url = new URL("https://" + host + ":" + port);
+ HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
+ connection.setSSLSocketFactory(context.getSocketFactory());
+ try {
+ connection.getInputStream();
+ fail("Connection to " + host + ":" + port + " expected to fail");
+ } catch (SSLHandshakeException expected) {
+ // ignored.
+ }
+ }
+
+ public static void assertUrlConnectionSucceeds(SSLContext context, String host, int port)
+ throws Exception {
+ URL url = new URL("https://" + host + ":" + port);
+ HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
+ connection.setSSLSocketFactory(context.getSocketFactory());
+ connection.getInputStream();
+ }
+
+ public static SSLContext getSSLContext(ConfigSource source) throws Exception {
+ ApplicationConfig config = new ApplicationConfig(source);
+ TrustManagerFactory tmf =
+ TrustManagerFactory.getInstance("PKIX", new NetworkSecurityConfigProvider());
+ tmf.init(new RootTrustManagerFactorySpi.ApplicationConfigParameters(config));
+ SSLContext context = SSLContext.getInstance("TLS");
+ context.init(null, tmf.getTrustManagers(), null);
+ return context;
+ }
+}
diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java
new file mode 100644
index 0000000..c6f3680
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java
@@ -0,0 +1,405 @@
+/*
+ * 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 android.security.net.config;
+
+import android.content.Context;
+import android.test.AndroidTestCase;
+import android.test.MoreAsserts;
+import android.util.ArraySet;
+import android.util.Pair;
+import java.io.IOException;
+import java.net.Socket;
+import java.net.URL;
+import java.security.KeyStore;
+import java.security.Provider;
+import java.security.Security;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Set;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+
+public class XmlConfigTests extends AndroidTestCase {
+
+ private final static String DEBUG_CA_SUBJ = "O=AOSP, CN=Test debug CA";
+
+ public void testEmptyConfigFile() throws Exception {
+ XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.empty_config);
+ ApplicationConfig appConfig = new ApplicationConfig(source);
+ assertFalse(appConfig.hasPerDomainConfigs());
+ NetworkSecurityConfig config = appConfig.getConfigForHostname("");
+ assertNotNull(config);
+ // Check defaults.
+ assertTrue(config.isCleartextTrafficPermitted());
+ assertFalse(config.isHstsEnforced());
+ assertFalse(config.getTrustAnchors().isEmpty());
+ PinSet pinSet = config.getPins();
+ assertTrue(pinSet.pins.isEmpty());
+ // Try some connections.
+ SSLContext context = TestUtils.getSSLContext(source);
+ TestUtils.assertConnectionSucceeds(context, "android.com", 443);
+ TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
+ TestUtils.assertUrlConnectionSucceeds(context, "google.com", 443);
+ }
+
+ public void testEmptyAnchors() throws Exception {
+ XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.empty_trust);
+ ApplicationConfig appConfig = new ApplicationConfig(source);
+ assertFalse(appConfig.hasPerDomainConfigs());
+ NetworkSecurityConfig config = appConfig.getConfigForHostname("");
+ assertNotNull(config);
+ // Check defaults.
+ assertTrue(config.isCleartextTrafficPermitted());
+ assertFalse(config.isHstsEnforced());
+ assertTrue(config.getTrustAnchors().isEmpty());
+ PinSet pinSet = config.getPins();
+ assertTrue(pinSet.pins.isEmpty());
+ SSLContext context = TestUtils.getSSLContext(source);
+ TestUtils.assertConnectionFails(context, "android.com", 443);
+ TestUtils.assertConnectionFails(context, "developer.android.com", 443);
+ TestUtils.assertUrlConnectionFails(context, "google.com", 443);
+ }
+
+ public void testBasicDomainConfig() throws Exception {
+ XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.domain1);
+ ApplicationConfig appConfig = new ApplicationConfig(source);
+ assertTrue(appConfig.hasPerDomainConfigs());
+ NetworkSecurityConfig config = appConfig.getConfigForHostname("");
+ assertNotNull(config);
+ // Check defaults.
+ assertTrue(config.isCleartextTrafficPermitted());
+ assertFalse(config.isHstsEnforced());
+ assertTrue(config.getTrustAnchors().isEmpty());
+ PinSet pinSet = config.getPins();
+ assertTrue(pinSet.pins.isEmpty());
+ // Check android.com.
+ config = appConfig.getConfigForHostname("android.com");
+ assertTrue(config.isCleartextTrafficPermitted());
+ assertFalse(config.isHstsEnforced());
+ assertFalse(config.getTrustAnchors().isEmpty());
+ pinSet = config.getPins();
+ assertTrue(pinSet.pins.isEmpty());
+ // Try connections.
+ SSLContext context = TestUtils.getSSLContext(source);
+ TestUtils.assertConnectionSucceeds(context, "android.com", 443);
+ TestUtils.assertConnectionFails(context, "developer.android.com", 443);
+ TestUtils.assertUrlConnectionFails(context, "google.com", 443);
+ TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
+ }
+
+ public void testBasicPinning() throws Exception {
+ XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.pins1);
+ ApplicationConfig appConfig = new ApplicationConfig(source);
+ assertTrue(appConfig.hasPerDomainConfigs());
+ // Check android.com.
+ NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
+ PinSet pinSet = config.getPins();
+ assertFalse(pinSet.pins.isEmpty());
+ // Try connections.
+ SSLContext context = TestUtils.getSSLContext(source);
+ TestUtils.assertConnectionSucceeds(context, "android.com", 443);
+ TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
+ TestUtils.assertConnectionSucceeds(context, "google.com", 443);
+ }
+
+ public void testExpiredPin() throws Exception {
+ XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.expired_pin);
+ ApplicationConfig appConfig = new ApplicationConfig(source);
+ assertTrue(appConfig.hasPerDomainConfigs());
+ // Check android.com.
+ NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
+ PinSet pinSet = config.getPins();
+ assertFalse(pinSet.pins.isEmpty());
+ // Try connections.
+ SSLContext context = TestUtils.getSSLContext(source);
+ TestUtils.assertConnectionSucceeds(context, "android.com", 443);
+ TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
+ }
+
+ public void testOverridesPins() throws Exception {
+ XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.override_pins);
+ ApplicationConfig appConfig = new ApplicationConfig(source);
+ assertTrue(appConfig.hasPerDomainConfigs());
+ // Check android.com.
+ NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
+ PinSet pinSet = config.getPins();
+ assertFalse(pinSet.pins.isEmpty());
+ // Try connections.
+ SSLContext context = TestUtils.getSSLContext(source);
+ TestUtils.assertConnectionSucceeds(context, "android.com", 443);
+ TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
+ }
+
+ public void testBadPin() throws Exception {
+ XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.bad_pin);
+ ApplicationConfig appConfig = new ApplicationConfig(source);
+ assertTrue(appConfig.hasPerDomainConfigs());
+ // Check android.com.
+ NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
+ PinSet pinSet = config.getPins();
+ assertFalse(pinSet.pins.isEmpty());
+ // Try connections.
+ SSLContext context = TestUtils.getSSLContext(source);
+ TestUtils.assertConnectionFails(context, "android.com", 443);
+ TestUtils.assertUrlConnectionFails(context, "android.com", 443);
+ TestUtils.assertConnectionSucceeds(context, "google.com", 443);
+ }
+
+ public void testMultipleDomains() throws Exception {
+ XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.multiple_domains);
+ ApplicationConfig appConfig = new ApplicationConfig(source);
+ assertTrue(appConfig.hasPerDomainConfigs());
+ NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
+ assertTrue(config.isCleartextTrafficPermitted());
+ assertFalse(config.isHstsEnforced());
+ assertFalse(config.getTrustAnchors().isEmpty());
+ PinSet pinSet = config.getPins();
+ assertTrue(pinSet.pins.isEmpty());
+ // Both android.com and google.com should use the same config
+ NetworkSecurityConfig other = appConfig.getConfigForHostname("google.com");
+ assertEquals(config, other);
+ // Try connections.
+ SSLContext context = TestUtils.getSSLContext(source);
+ TestUtils.assertConnectionSucceeds(context, "android.com", 443);
+ TestUtils.assertConnectionSucceeds(context, "google.com", 443);
+ TestUtils.assertConnectionFails(context, "developer.android.com", 443);
+ TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
+ }
+
+ public void testMultipleDomainConfigs() throws Exception {
+ XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.multiple_configs);
+ ApplicationConfig appConfig = new ApplicationConfig(source);
+ assertTrue(appConfig.hasPerDomainConfigs());
+ // Should be two different config objects
+ NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
+ NetworkSecurityConfig other = appConfig.getConfigForHostname("google.com");
+ MoreAsserts.assertNotEqual(config, other);
+ // Try connections.
+ SSLContext context = TestUtils.getSSLContext(source);
+ TestUtils.assertConnectionSucceeds(context, "android.com", 443);
+ TestUtils.assertConnectionSucceeds(context, "google.com", 443);
+ TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
+ }
+
+ public void testIncludeSubdomains() throws Exception {
+ XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.subdomains);
+ ApplicationConfig appConfig = new ApplicationConfig(source);
+ assertTrue(appConfig.hasPerDomainConfigs());
+ // Try connections.
+ SSLContext context = TestUtils.getSSLContext(source);
+ TestUtils.assertConnectionSucceeds(context, "android.com", 443);
+ TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
+ TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
+ TestUtils.assertUrlConnectionSucceeds(context, "developer.android.com", 443);
+ TestUtils.assertConnectionFails(context, "google.com", 443);
+ }
+
+ public void testAttributes() throws Exception {
+ XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.attributes);
+ ApplicationConfig appConfig = new ApplicationConfig(source);
+ assertFalse(appConfig.hasPerDomainConfigs());
+ NetworkSecurityConfig config = appConfig.getConfigForHostname("");
+ assertTrue(config.isHstsEnforced());
+ assertFalse(config.isCleartextTrafficPermitted());
+ }
+
+ public void testResourcePemCertificateSource() throws Exception {
+ XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.resource_anchors_pem);
+ ApplicationConfig appConfig = new ApplicationConfig(source);
+ // Check android.com.
+ NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
+ assertTrue(config.isCleartextTrafficPermitted());
+ assertFalse(config.isHstsEnforced());
+ assertEquals(2, config.getTrustAnchors().size());
+ // Try connections.
+ SSLContext context = TestUtils.getSSLContext(source);
+ TestUtils.assertConnectionSucceeds(context, "android.com", 443);
+ TestUtils.assertConnectionFails(context, "developer.android.com", 443);
+ TestUtils.assertUrlConnectionFails(context, "google.com", 443);
+ TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
+ }
+
+ public void testResourceDerCertificateSource() throws Exception {
+ XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.resource_anchors_der);
+ ApplicationConfig appConfig = new ApplicationConfig(source);
+ // Check android.com.
+ NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
+ assertTrue(config.isCleartextTrafficPermitted());
+ assertFalse(config.isHstsEnforced());
+ assertEquals(2, config.getTrustAnchors().size());
+ // Try connections.
+ SSLContext context = TestUtils.getSSLContext(source);
+ TestUtils.assertConnectionSucceeds(context, "android.com", 443);
+ TestUtils.assertConnectionFails(context, "developer.android.com", 443);
+ TestUtils.assertUrlConnectionFails(context, "google.com", 443);
+ TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
+ }
+
+ public void testNestedDomainConfigs() throws Exception {
+ XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.nested_domains);
+ ApplicationConfig appConfig = new ApplicationConfig(source);
+ assertTrue(appConfig.hasPerDomainConfigs());
+ NetworkSecurityConfig parent = appConfig.getConfigForHostname("android.com");
+ NetworkSecurityConfig child = appConfig.getConfigForHostname("developer.android.com");
+ MoreAsserts.assertNotEqual(parent, child);
+ MoreAsserts.assertEmpty(parent.getPins().pins);
+ MoreAsserts.assertNotEmpty(child.getPins().pins);
+ // Check that the child inherited the cleartext value and anchors.
+ assertFalse(child.isCleartextTrafficPermitted());
+ MoreAsserts.assertNotEmpty(child.getTrustAnchors());
+ // Test connections.
+ SSLContext context = TestUtils.getSSLContext(source);
+ TestUtils.assertConnectionSucceeds(context, "android.com", 443);
+ TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
+ }
+
+ public void testNestedDomainConfigsOverride() throws Exception {
+ XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.nested_domains_override);
+ ApplicationConfig appConfig = new ApplicationConfig(source);
+ assertTrue(appConfig.hasPerDomainConfigs());
+ NetworkSecurityConfig parent = appConfig.getConfigForHostname("android.com");
+ NetworkSecurityConfig child = appConfig.getConfigForHostname("developer.android.com");
+ MoreAsserts.assertNotEqual(parent, child);
+ assertTrue(parent.isCleartextTrafficPermitted());
+ assertFalse(child.isCleartextTrafficPermitted());
+ }
+
+ public void testDebugOverridesDisabled() throws Exception {
+ XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.debug_basic, false);
+ ApplicationConfig appConfig = new ApplicationConfig(source);
+ NetworkSecurityConfig config = appConfig.getConfigForHostname("");
+ Set<TrustAnchor> anchors = config.getTrustAnchors();
+ MoreAsserts.assertEmpty(anchors);
+ SSLContext context = TestUtils.getSSLContext(source);
+ TestUtils.assertConnectionFails(context, "android.com", 443);
+ TestUtils.assertConnectionFails(context, "developer.android.com", 443);
+ }
+
+ public void testBasicDebugOverrides() throws Exception {
+ XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.debug_basic, true);
+ ApplicationConfig appConfig = new ApplicationConfig(source);
+ NetworkSecurityConfig config = appConfig.getConfigForHostname("");
+ Set<TrustAnchor> anchors = config.getTrustAnchors();
+ MoreAsserts.assertNotEmpty(anchors);
+ for (TrustAnchor anchor : anchors) {
+ assertTrue(anchor.overridesPins);
+ }
+ SSLContext context = TestUtils.getSSLContext(source);
+ TestUtils.assertConnectionSucceeds(context, "android.com", 443);
+ TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
+ }
+
+ public void testDebugOverridesWithDomain() throws Exception {
+ XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.debug_domain, true);
+ ApplicationConfig appConfig = new ApplicationConfig(source);
+ NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
+ Set<TrustAnchor> anchors = config.getTrustAnchors();
+ boolean foundDebugCA = false;
+ for (TrustAnchor anchor : anchors) {
+ if (anchor.certificate.getSubjectDN().toString().equals(DEBUG_CA_SUBJ)) {
+ foundDebugCA = true;
+ assertTrue(anchor.overridesPins);
+ }
+ }
+ assertTrue(foundDebugCA);
+ SSLContext context = TestUtils.getSSLContext(source);
+ TestUtils.assertConnectionSucceeds(context, "android.com", 443);
+ TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
+ }
+
+ public void testDebugInherit() throws Exception {
+ XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.debug_domain, true);
+ ApplicationConfig appConfig = new ApplicationConfig(source);
+ NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
+ Set<TrustAnchor> anchors = config.getTrustAnchors();
+ boolean foundDebugCA = false;
+ for (TrustAnchor anchor : anchors) {
+ if (anchor.certificate.getSubjectDN().toString().equals(DEBUG_CA_SUBJ)) {
+ foundDebugCA = true;
+ assertTrue(anchor.overridesPins);
+ }
+ }
+ assertTrue(foundDebugCA);
+ assertTrue(anchors.size() > 1);
+ SSLContext context = TestUtils.getSSLContext(source);
+ TestUtils.assertConnectionSucceeds(context, "android.com", 443);
+ TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
+ }
+
+ private void testBadConfig(int configId) throws Exception {
+ try {
+ XmlConfigSource source = new XmlConfigSource(getContext(), configId);
+ ApplicationConfig appConfig = new ApplicationConfig(source);
+ appConfig.getConfigForHostname("android.com");
+ fail("Bad config " + getContext().getResources().getResourceName(configId)
+ + " did not fail to parse");
+ } catch (RuntimeException e) {
+ MoreAsserts.assertAssignableFrom(XmlConfigSource.ParserException.class,
+ e.getCause());
+ }
+ }
+
+ public void testBadConfig0() throws Exception {
+ testBadConfig(R.xml.bad_config0);
+ }
+
+ public void testBadConfig1() throws Exception {
+ testBadConfig(R.xml.bad_config1);
+ }
+
+ public void testBadConfig2() throws Exception {
+ testBadConfig(R.xml.bad_config2);
+ }
+
+ public void testBadConfig3() throws Exception {
+ testBadConfig(R.xml.bad_config3);
+ }
+
+ public void testBadConfig4() throws Exception {
+ testBadConfig(R.xml.bad_config4);
+ }
+
+ public void testBadConfig5() throws Exception {
+ testBadConfig(R.xml.bad_config4);
+ }
+
+ public void testTrustManagerKeystore() throws Exception {
+ XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.bad_pin, true);
+ ApplicationConfig appConfig = new ApplicationConfig(source);
+ Provider provider = new NetworkSecurityConfigProvider();
+ TrustManagerFactory tmf =
+ TrustManagerFactory.getInstance("PKIX", provider);
+ KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
+ keystore.load(null);
+ int i = 0;
+ for (X509Certificate cert : SystemCertificateSource.getInstance().getCertificates()) {
+ keystore.setEntry(String.valueOf(i),
+ new KeyStore.TrustedCertificateEntry(cert),
+ null);
+ i++;
+ }
+ tmf.init(keystore);
+ TrustManager[] tms = tmf.getTrustManagers();
+ SSLContext context = SSLContext.getInstance("TLS");
+ context.init(null, tms, null);
+ TestUtils.assertConnectionSucceeds(context, "android.com" , 443);
+ }
+}
diff --git a/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionMeasuringActivity.java b/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionMeasuringActivity.java
index b4e0c70..4771b6c 100644
--- a/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionMeasuringActivity.java
+++ b/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionMeasuringActivity.java
@@ -24,6 +24,7 @@
import android.app.ActivityManager;
import android.app.ActivityManager.MemoryInfo;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.Rect;
@@ -95,11 +96,9 @@
private boolean mResumed;
// Drop one frame per half second.
- // TODO(khmel)
- // Add a feature flag and set the target FPS dependent on the target system as e.g.:
- // 59FPS for MULTI_WINDOW and 54 otherwise (to satisfy the default lax Android requirements).
private double mRefreshRate;
private double mTargetFPS;
+ private boolean mAndromeda;
private int mWidth;
private int mHeight;
@@ -182,6 +181,10 @@
return score;
}
+ public boolean isAndromeda() {
+ return mAndromeda;
+ }
+
@Override
public void onClick(View view) {
if (view == mMeasureCompositionButton) {
@@ -247,6 +250,9 @@
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ // Detect Andromeda devices by having free-form window management feature.
+ mAndromeda = getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT);
detectRefreshRate();
// To layouts in parent. First contains list of Surfaces and second
@@ -513,7 +519,8 @@
}
MemoryInfo memInfo = getMemoryInfo();
- String info = "Available " +
+ String platformName = mAndromeda ? "Andromeda" : "Android";
+ String info = platformName + ": available " +
getReadableMemory(memInfo.availMem) + " from " +
getReadableMemory(memInfo.totalMem) + ".\nVisible " +
visibleCnt + " from " + mViews.size() + " " +
diff --git a/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java b/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java
index 3f04888..388f91a 100644
--- a/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java
+++ b/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java
@@ -17,6 +17,7 @@
import android.app.Activity;
import android.graphics.PixelFormat;
+import android.os.Build;
import android.os.Bundle;
import android.surfacecomposition.SurfaceCompositionMeasuringActivity.AllocationScore;
import android.surfacecomposition.SurfaceCompositionMeasuringActivity.CompositorScore;
@@ -44,11 +45,16 @@
PixelFormat.OPAQUE,
};
- // Based on Nexus 9 performance which is usually < 9.0.
- private final static double[] MIN_ACCEPTED_COMPOSITION_SCORE = new double[] {
+ // Nexus 9 performance is around 8.8. We distinguish results for Andromeda and
+ // Android devices. Andromeda devices require higher performance score.
+ private final static double[] MIN_ACCEPTED_COMPOSITION_SCORE_ANDROMDEDA = new double[] {
8.0,
8.0,
};
+ private final static double[] MIN_ACCEPTED_COMPOSITION_SCORE_ANDROID = new double[] {
+ 4.0,
+ 4.0,
+ };
// Based on Nexus 6 performance which is usually < 28.0.
private final static double[] MIN_ACCEPTED_ALLOCATION_SCORE = new double[] {
@@ -66,6 +72,8 @@
@SmallTest
public void testSurfaceCompositionPerformance() {
Bundle status = new Bundle();
+ double[] minScores = getActivity().isAndromeda() ?
+ MIN_ACCEPTED_COMPOSITION_SCORE_ANDROMDEDA : MIN_ACCEPTED_COMPOSITION_SCORE_ANDROID;
for (int i = 0; i < TEST_PIXEL_FORMATS.length; ++i) {
int pixelFormat = TEST_PIXEL_FORMATS[i];
String formatName = SurfaceCompositionMeasuringActivity.getPixelFormatInfo(pixelFormat);
@@ -73,8 +81,8 @@
Log.i(TAG, "testSurfaceCompositionPerformance(" + formatName + ") = " + score);
assertTrue("Device does not support surface(" + formatName + ") composition " +
"performance score. " + score.mSurfaces + " < " +
- MIN_ACCEPTED_COMPOSITION_SCORE[i] + ".",
- score.mSurfaces >= MIN_ACCEPTED_COMPOSITION_SCORE[i]);
+ minScores[i] + ". Build: " + Build.FINGERPRINT + ".",
+ score.mSurfaces >= minScores[i]);
// Send status only for TRANSLUCENT format.
if (pixelFormat == PixelFormat.TRANSLUCENT) {
status.putDouble(KEY_SURFACE_COMPOSITION_PERFORMANCE, score.mSurfaces);
@@ -96,7 +104,8 @@
Log.i(TAG, "testSurfaceAllocationPerformance(" + formatName + ") = " + score);
assertTrue("Device does not support surface(" + formatName + ") allocation " +
"performance score. " + score.mMedian + " < " +
- MIN_ACCEPTED_ALLOCATION_SCORE[i] + ".",
+ MIN_ACCEPTED_ALLOCATION_SCORE[i] + ". Build: " +
+ Build.FINGERPRINT + ".",
score.mMedian >= MIN_ACCEPTED_ALLOCATION_SCORE[i]);
// Send status only for TRANSLUCENT format.
if (pixelFormat == PixelFormat.TRANSLUCENT) {
diff --git a/tools/aapt/ZipFile.cpp b/tools/aapt/ZipFile.cpp
index 36f4e73..2840826 100644
--- a/tools/aapt/ZipFile.cpp
+++ b/tools/aapt/ZipFile.cpp
@@ -364,7 +364,7 @@
long lfhPosn, startPosn, endPosn, uncompressedLen;
FILE* inputFp = NULL;
unsigned long crc;
- time_t modWhen;
+ time_t modWhen = 0;
if (mReadOnly)
return INVALID_OPERATION;
@@ -497,7 +497,6 @@
*/
pEntry->setDataInfo(uncompressedLen, endPosn - startPosn, crc,
compressionMethod);
- modWhen = getModTime(inputFp ? fileno(inputFp) : fileno(mZipFp));
pEntry->setModWhen(modWhen);
pEntry->setLFHOffset(lfhPosn);
mEOCD.mNumEntries++;
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index e5c42d5..ec29c38 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -25,62 +25,77 @@
main := Main.cpp
sources := \
- BigBuffer.cpp \
- BinaryResourceParser.cpp \
- BindingXmlPullParser.cpp \
+ compile/IdAssigner.cpp \
+ compile/Png.cpp \
+ compile/XmlIdCollector.cpp \
+ flatten/Archive.cpp \
+ flatten/TableFlattener.cpp \
+ flatten/XmlFlattener.cpp \
+ link/AutoVersioner.cpp \
+ link/ManifestFixer.cpp \
+ link/PrivateAttributeMover.cpp \
+ link/ReferenceLinker.cpp \
+ link/TableMerger.cpp \
+ link/XmlReferenceLinker.cpp \
+ process/SymbolTable.cpp \
+ unflatten/BinaryResourceParser.cpp \
+ unflatten/ResChunkPullParser.cpp \
+ util/BigBuffer.cpp \
+ util/Files.cpp \
+ util/Util.cpp \
ConfigDescription.cpp \
Debug.cpp \
- Files.cpp \
- Flag.cpp \
- JavaClassGenerator.cpp \
- Linker.cpp \
+ Flags.cpp \
+ java/AnnotationProcessor.cpp \
+ java/JavaClassGenerator.cpp \
+ java/ManifestClassGenerator.cpp \
+ java/ProguardRules.cpp \
Locale.cpp \
- Logger.cpp \
- ManifestMerger.cpp \
- ManifestParser.cpp \
- ManifestValidator.cpp \
- Png.cpp \
- ProguardRules.cpp \
- ResChunkPullParser.cpp \
Resource.cpp \
ResourceParser.cpp \
ResourceTable.cpp \
- ResourceTableResolver.cpp \
+ ResourceUtils.cpp \
ResourceValues.cpp \
SdkConstants.cpp \
StringPool.cpp \
- TableFlattener.cpp \
- Util.cpp \
- ScopedXmlPullParser.cpp \
- SourceXmlPullParser.cpp \
- XliffXmlPullParser.cpp \
XmlDom.cpp \
- XmlFlattener.cpp \
- ZipEntry.cpp \
- ZipFile.cpp
+ XmlPullParser.cpp
testSources := \
- BigBuffer_test.cpp \
- BindingXmlPullParser_test.cpp \
- Compat_test.cpp \
+ compile/IdAssigner_test.cpp \
+ compile/XmlIdCollector_test.cpp \
+ flatten/FileExportWriter_test.cpp \
+ flatten/TableFlattener_test.cpp \
+ flatten/XmlFlattener_test.cpp \
+ link/AutoVersioner_test.cpp \
+ link/ManifestFixer_test.cpp \
+ link/PrivateAttributeMover_test.cpp \
+ link/ReferenceLinker_test.cpp \
+ link/TableMerger_test.cpp \
+ link/XmlReferenceLinker_test.cpp \
+ process/SymbolTable_test.cpp \
+ unflatten/FileExportHeaderReader_test.cpp \
+ util/BigBuffer_test.cpp \
+ util/Maybe_test.cpp \
+ util/StringPiece_test.cpp \
+ util/Util_test.cpp \
ConfigDescription_test.cpp \
- JavaClassGenerator_test.cpp \
- Linker_test.cpp \
+ java/AnnotationProcessor_test.cpp \
+ java/JavaClassGenerator_test.cpp \
+ java/ManifestClassGenerator_test.cpp \
Locale_test.cpp \
- ManifestMerger_test.cpp \
- ManifestParser_test.cpp \
- Maybe_test.cpp \
- NameMangler_test.cpp \
- ResourceParser_test.cpp \
Resource_test.cpp \
+ ResourceParser_test.cpp \
ResourceTable_test.cpp \
- ScopedXmlPullParser_test.cpp \
- StringPiece_test.cpp \
+ ResourceUtils_test.cpp \
StringPool_test.cpp \
- Util_test.cpp \
- XliffXmlPullParser_test.cpp \
+ ValueVisitor_test.cpp \
XmlDom_test.cpp \
- XmlFlattener_test.cpp
+ XmlPullParser_test.cpp
+
+toolSources := \
+ compile/Compile.cpp \
+ link/Link.cpp
hostLdLibs :=
@@ -101,7 +116,7 @@
endif
cFlags := -Wall -Werror -Wno-unused-parameter -UNDEBUG
-cppFlags := -std=c++11 -Wno-missing-field-initializers -Wno-unused-private-field
+cppFlags := -std=c++11 -Wno-missing-field-initializers -fno-exceptions -fno-rtti
# ==========================================================
# Build the host static library: libaapt2
@@ -139,7 +154,7 @@
include $(CLEAR_VARS)
LOCAL_MODULE := aapt2
-LOCAL_SRC_FILES := $(main)
+LOCAL_SRC_FILES := $(main) $(toolSources)
LOCAL_STATIC_LIBRARIES += libaapt2 $(hostStaticLibs)
LOCAL_LDLIBS += $(hostLdLibs)
diff --git a/tools/aapt2/BigBuffer.cpp b/tools/aapt2/BigBuffer.cpp
deleted file mode 100644
index 8f57172..0000000
--- a/tools/aapt2/BigBuffer.cpp
+++ /dev/null
@@ -1,52 +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.
- */
-
-#include "BigBuffer.h"
-
-#include <algorithm>
-#include <memory>
-#include <vector>
-
-namespace aapt {
-
-void* BigBuffer::nextBlockImpl(size_t size) {
- if (!mBlocks.empty()) {
- Block& block = mBlocks.back();
- if (block.mBlockSize - block.size >= size) {
- void* outBuffer = block.buffer.get() + block.size;
- block.size += size;
- mSize += size;
- return outBuffer;
- }
- }
-
- const size_t actualSize = std::max(mBlockSize, size);
-
- Block block = {};
-
- // Zero-allocate the block's buffer.
- block.buffer = std::unique_ptr<uint8_t[]>(new uint8_t[actualSize]());
- assert(block.buffer);
-
- block.size = size;
- block.mBlockSize = actualSize;
-
- mBlocks.push_back(std::move(block));
- mSize += size;
- return mBlocks.back().buffer.get();
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/BigBuffer.h b/tools/aapt2/BigBuffer.h
deleted file mode 100644
index 8b6569c..0000000
--- a/tools/aapt2/BigBuffer.h
+++ /dev/null
@@ -1,159 +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_BIG_BUFFER_H
-#define AAPT_BIG_BUFFER_H
-
-#include <cassert>
-#include <cstring>
-#include <memory>
-#include <vector>
-
-namespace aapt {
-
-/**
- * Inspired by protobuf's ZeroCopyOutputStream, offers blocks of memory
- * in which to write without knowing the full size of the entire payload.
- * This is essentially a list of memory blocks. As one fills up, another
- * block is allocated and appended to the end of the list.
- */
-class BigBuffer {
-public:
- /**
- * A contiguous block of allocated memory.
- */
- struct Block {
- /**
- * Pointer to the memory.
- */
- std::unique_ptr<uint8_t[]> buffer;
-
- /**
- * Size of memory that is currently occupied. The actual
- * allocation may be larger.
- */
- size_t size;
-
- private:
- friend class BigBuffer;
-
- /**
- * The size of the memory block allocation.
- */
- size_t mBlockSize;
- };
-
- typedef std::vector<Block>::const_iterator const_iterator;
-
- /**
- * Create a BigBuffer with block allocation sizes
- * of blockSize.
- */
- BigBuffer(size_t blockSize);
-
- BigBuffer(const BigBuffer&) = delete; // No copying.
-
- BigBuffer(BigBuffer&& rhs);
-
- /**
- * Number of occupied bytes in all the allocated blocks.
- */
- size_t size() const;
-
- /**
- * Returns a pointer to an array of T, where T is
- * a POD type. The elements are zero-initialized.
- */
- template <typename T>
- T* nextBlock(size_t count = 1);
-
- /**
- * Moves the specified BigBuffer into this one. When this method
- * returns, buffer is empty.
- */
- void appendBuffer(BigBuffer&& buffer);
-
- /**
- * Pads the block with 'bytes' bytes of zero values.
- */
- void pad(size_t bytes);
-
- /**
- * Pads the block so that it aligns on a 4 byte boundary.
- */
- void align4();
-
- const_iterator begin() const;
- const_iterator end() const;
-
-private:
- /**
- * Returns a pointer to a buffer of the requested size.
- * The buffer is zero-initialized.
- */
- void* nextBlockImpl(size_t size);
-
- size_t mBlockSize;
- size_t mSize;
- std::vector<Block> mBlocks;
-};
-
-inline BigBuffer::BigBuffer(size_t blockSize) : mBlockSize(blockSize), mSize(0) {
-}
-
-inline BigBuffer::BigBuffer(BigBuffer&& rhs) :
- mBlockSize(rhs.mBlockSize), mSize(rhs.mSize), mBlocks(std::move(rhs.mBlocks)) {
-}
-
-inline size_t BigBuffer::size() const {
- return mSize;
-}
-
-template <typename T>
-inline T* BigBuffer::nextBlock(size_t count) {
- assert(count != 0);
- return reinterpret_cast<T*>(nextBlockImpl(sizeof(T) * count));
-}
-
-inline void BigBuffer::appendBuffer(BigBuffer&& buffer) {
- std::move(buffer.mBlocks.begin(), buffer.mBlocks.end(), std::back_inserter(mBlocks));
- mSize += buffer.mSize;
- buffer.mBlocks.clear();
- buffer.mSize = 0;
-}
-
-inline void BigBuffer::pad(size_t bytes) {
- nextBlock<char>(bytes);
-}
-
-inline void BigBuffer::align4() {
- const size_t unaligned = mSize % 4;
- if (unaligned != 0) {
- pad(4 - unaligned);
- }
-}
-
-inline BigBuffer::const_iterator BigBuffer::begin() const {
- return mBlocks.begin();
-}
-
-inline BigBuffer::const_iterator BigBuffer::end() const {
- return mBlocks.end();
-}
-
-} // namespace aapt
-
-#endif // AAPT_BIG_BUFFER_H
diff --git a/tools/aapt2/BigBuffer_test.cpp b/tools/aapt2/BigBuffer_test.cpp
deleted file mode 100644
index 01ee8d7..0000000
--- a/tools/aapt2/BigBuffer_test.cpp
+++ /dev/null
@@ -1,98 +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.
- */
-
-#include "BigBuffer.h"
-
-#include <gtest/gtest.h>
-
-namespace aapt {
-
-TEST(BigBufferTest, AllocateSingleBlock) {
- BigBuffer buffer(4);
-
- EXPECT_NE(nullptr, buffer.nextBlock<char>(2));
- EXPECT_EQ(2u, buffer.size());
-}
-
-TEST(BigBufferTest, ReturnSameBlockIfNextAllocationFits) {
- BigBuffer buffer(16);
-
- char* b1 = buffer.nextBlock<char>(8);
- EXPECT_NE(nullptr, b1);
-
- char* b2 = buffer.nextBlock<char>(4);
- EXPECT_NE(nullptr, b2);
-
- EXPECT_EQ(b1 + 8, b2);
-}
-
-TEST(BigBufferTest, AllocateExactSizeBlockIfLargerThanBlockSize) {
- BigBuffer buffer(16);
-
- EXPECT_NE(nullptr, buffer.nextBlock<char>(32));
- EXPECT_EQ(32u, buffer.size());
-}
-
-TEST(BigBufferTest, AppendAndMoveBlock) {
- BigBuffer buffer(16);
-
- uint32_t* b1 = buffer.nextBlock<uint32_t>();
- ASSERT_NE(nullptr, b1);
- *b1 = 33;
-
- {
- BigBuffer buffer2(16);
- b1 = buffer2.nextBlock<uint32_t>();
- ASSERT_NE(nullptr, b1);
- *b1 = 44;
-
- buffer.appendBuffer(std::move(buffer2));
- EXPECT_EQ(0u, buffer2.size());
- EXPECT_EQ(buffer2.begin(), buffer2.end());
- }
-
- EXPECT_EQ(2 * sizeof(uint32_t), buffer.size());
-
- auto b = buffer.begin();
- ASSERT_NE(b, buffer.end());
- ASSERT_EQ(sizeof(uint32_t), b->size);
- ASSERT_EQ(33u, *reinterpret_cast<uint32_t*>(b->buffer.get()));
- ++b;
-
- ASSERT_NE(b, buffer.end());
- ASSERT_EQ(sizeof(uint32_t), b->size);
- ASSERT_EQ(44u, *reinterpret_cast<uint32_t*>(b->buffer.get()));
- ++b;
-
- ASSERT_EQ(b, buffer.end());
-}
-
-TEST(BigBufferTest, PadAndAlignProperly) {
- BigBuffer buffer(16);
-
- ASSERT_NE(buffer.nextBlock<char>(2), nullptr);
- ASSERT_EQ(2u, buffer.size());
- buffer.pad(2);
- ASSERT_EQ(4u, buffer.size());
- buffer.align4();
- ASSERT_EQ(4u, buffer.size());
- buffer.pad(2);
- ASSERT_EQ(6u, buffer.size());
- buffer.align4();
- ASSERT_EQ(8u, buffer.size());
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/BinaryResourceParser.cpp b/tools/aapt2/BinaryResourceParser.cpp
deleted file mode 100644
index 4f1947a..0000000
--- a/tools/aapt2/BinaryResourceParser.cpp
+++ /dev/null
@@ -1,897 +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.
- */
-
-#include "BinaryResourceParser.h"
-#include "Logger.h"
-#include "ResChunkPullParser.h"
-#include "Resolver.h"
-#include "ResourceParser.h"
-#include "ResourceTable.h"
-#include "ResourceTypeExtensions.h"
-#include "ResourceValues.h"
-#include "Source.h"
-#include "Util.h"
-
-#include <androidfw/ResourceTypes.h>
-#include <androidfw/TypeWrappers.h>
-#include <map>
-#include <string>
-
-namespace aapt {
-
-using namespace android;
-
-/*
- * Visitor that converts a reference's resource ID to a resource name,
- * given a mapping from resource ID to resource name.
- */
-struct ReferenceIdToNameVisitor : ValueVisitor {
- ReferenceIdToNameVisitor(const std::shared_ptr<IResolver>& resolver,
- std::map<ResourceId, ResourceName>* cache) :
- mResolver(resolver), mCache(cache) {
- }
-
- void visit(Reference& reference, ValueVisitorArgs&) override {
- idToName(reference);
- }
-
- void visit(Attribute& attr, ValueVisitorArgs&) override {
- for (auto& entry : attr.symbols) {
- idToName(entry.symbol);
- }
- }
-
- void visit(Style& style, ValueVisitorArgs&) override {
- if (style.parent.id.isValid()) {
- idToName(style.parent);
- }
-
- for (auto& entry : style.entries) {
- idToName(entry.key);
- entry.value->accept(*this, {});
- }
- }
-
- void visit(Styleable& styleable, ValueVisitorArgs&) override {
- for (auto& attr : styleable.entries) {
- idToName(attr);
- }
- }
-
- void visit(Array& array, ValueVisitorArgs&) override {
- for (auto& item : array.items) {
- item->accept(*this, {});
- }
- }
-
- void visit(Plural& plural, ValueVisitorArgs&) override {
- for (auto& item : plural.values) {
- if (item) {
- item->accept(*this, {});
- }
- }
- }
-
-private:
- void idToName(Reference& reference) {
- if (!reference.id.isValid()) {
- return;
- }
-
- auto cacheIter = mCache->find(reference.id);
- if (cacheIter != mCache->end()) {
- reference.name = cacheIter->second;
- reference.id = 0;
- } else {
- Maybe<ResourceName> result = mResolver->findName(reference.id);
- if (result) {
- reference.name = result.value();
-
- // Add to cache.
- mCache->insert({reference.id, reference.name});
-
- reference.id = 0;
- }
- }
- }
-
- std::shared_ptr<IResolver> mResolver;
- std::map<ResourceId, ResourceName>* mCache;
-};
-
-
-BinaryResourceParser::BinaryResourceParser(const std::shared_ptr<ResourceTable>& table,
- const std::shared_ptr<IResolver>& resolver,
- const Source& source,
- const std::u16string& defaultPackage,
- const void* data,
- size_t len) :
- mTable(table), mResolver(resolver), mSource(source), mDefaultPackage(defaultPackage),
- mData(data), mDataLen(len) {
-}
-
-bool BinaryResourceParser::parse() {
- ResChunkPullParser parser(mData, mDataLen);
-
- bool error = false;
- while(ResChunkPullParser::isGoodEvent(parser.next())) {
- if (parser.getChunk()->type != android::RES_TABLE_TYPE) {
- Logger::warn(mSource)
- << "unknown chunk of type '"
- << parser.getChunk()->type
- << "'."
- << std::endl;
- continue;
- }
-
- error |= !parseTable(parser.getChunk());
- }
-
- if (parser.getEvent() == ResChunkPullParser::Event::BadDocument) {
- Logger::error(mSource)
- << "bad document: "
- << parser.getLastError()
- << "."
- << std::endl;
- return false;
- }
- return !error;
-}
-
-bool BinaryResourceParser::getSymbol(const void* data, ResourceNameRef* outSymbol) {
- if (!mSymbolEntries || mSymbolEntryCount == 0) {
- return false;
- }
-
- if (reinterpret_cast<uintptr_t>(data) < reinterpret_cast<uintptr_t>(mData)) {
- return false;
- }
-
- // We only support 32 bit offsets right now.
- const uintptr_t offset = reinterpret_cast<uintptr_t>(data) -
- reinterpret_cast<uintptr_t>(mData);
- if (offset > std::numeric_limits<uint32_t>::max()) {
- return false;
- }
-
- for (size_t i = 0; i < mSymbolEntryCount; i++) {
- if (mSymbolEntries[i].offset == offset) {
- // This offset is a symbol!
- const StringPiece16 str = util::getString(mSymbolPool,
- mSymbolEntries[i].stringIndex);
- StringPiece16 typeStr;
- ResourceParser::extractResourceName(str, &outSymbol->package, &typeStr,
- &outSymbol->entry);
- const ResourceType* type = parseResourceType(typeStr);
- if (!type) {
- return false;
- }
- if (outSymbol->package.empty()) {
- outSymbol->package = mTable->getPackage();
- }
- outSymbol->type = *type;
-
- // Since we scan the symbol table in order, we can start looking for the
- // next symbol from this point.
- mSymbolEntryCount -= i + 1;
- mSymbolEntries += i + 1;
- return true;
- }
- }
- return false;
-}
-
-bool BinaryResourceParser::parseSymbolTable(const ResChunk_header* chunk) {
- const SymbolTable_header* symbolTableHeader = convertTo<SymbolTable_header>(chunk);
- if (!symbolTableHeader) {
- Logger::error(mSource)
- << "could not parse chunk as SymbolTable_header."
- << std::endl;
- return false;
- }
-
- const size_t entrySizeBytes = symbolTableHeader->count * sizeof(SymbolTable_entry);
- if (entrySizeBytes > getChunkDataLen(symbolTableHeader->header)) {
- Logger::error(mSource)
- << "entries extend beyond chunk."
- << std::endl;
- return false;
- }
-
- mSymbolEntries = reinterpret_cast<const SymbolTable_entry*>(
- getChunkData(symbolTableHeader->header));
- mSymbolEntryCount = symbolTableHeader->count;
-
- ResChunkPullParser parser(getChunkData(symbolTableHeader->header) + entrySizeBytes,
- getChunkDataLen(symbolTableHeader->header) - entrySizeBytes);
- if (!ResChunkPullParser::isGoodEvent(parser.next())) {
- Logger::error(mSource)
- << "failed to parse chunk: "
- << parser.getLastError()
- << "."
- << std::endl;
- return false;
- }
-
- if (parser.getChunk()->type != android::RES_STRING_POOL_TYPE) {
- Logger::error(mSource)
- << "expected Symbol string pool."
- << std::endl;
- return false;
- }
-
- if (mSymbolPool.setTo(parser.getChunk(), parser.getChunk()->size) != NO_ERROR) {
- Logger::error(mSource)
- << "failed to parse symbol string pool with code: "
- << mSymbolPool.getError()
- << "."
- << std::endl;
- return false;
- }
- return true;
-}
-
-bool BinaryResourceParser::parseTable(const ResChunk_header* chunk) {
- const ResTable_header* tableHeader = convertTo<ResTable_header>(chunk);
- if (!tableHeader) {
- Logger::error(mSource)
- << "could not parse chunk as ResTable_header."
- << std::endl;
- return false;
- }
-
- ResChunkPullParser parser(getChunkData(tableHeader->header),
- getChunkDataLen(tableHeader->header));
- while (ResChunkPullParser::isGoodEvent(parser.next())) {
- switch (parser.getChunk()->type) {
- case android::RES_STRING_POOL_TYPE:
- if (mValuePool.getError() == NO_INIT) {
- if (mValuePool.setTo(parser.getChunk(), parser.getChunk()->size) !=
- NO_ERROR) {
- Logger::error(mSource)
- << "failed to parse value string pool with code: "
- << mValuePool.getError()
- << "."
- << std::endl;
- return false;
- }
-
- // Reserve some space for the strings we are going to add.
- mTable->getValueStringPool().hintWillAdd(
- mValuePool.size(), mValuePool.styleCount());
- } else {
- Logger::warn(mSource)
- << "unexpected string pool."
- << std::endl;
- }
- break;
-
- case RES_TABLE_SYMBOL_TABLE_TYPE:
- if (!parseSymbolTable(parser.getChunk())) {
- return false;
- }
- break;
-
- case RES_TABLE_SOURCE_POOL_TYPE: {
- if (mSourcePool.setTo(getChunkData(*parser.getChunk()),
- getChunkDataLen(*parser.getChunk())) != NO_ERROR) {
- Logger::error(mSource)
- << "failed to parse source pool with code: "
- << mSourcePool.getError()
- << "."
- << std::endl;
- return false;
- }
- break;
- }
-
- case android::RES_TABLE_PACKAGE_TYPE:
- if (!parsePackage(parser.getChunk())) {
- return false;
- }
- break;
-
- default:
- Logger::warn(mSource)
- << "unexpected chunk of type "
- << parser.getChunk()->type
- << "."
- << std::endl;
- break;
- }
- }
-
- if (parser.getEvent() == ResChunkPullParser::Event::BadDocument) {
- Logger::error(mSource)
- << "bad resource table: " << parser.getLastError()
- << "."
- << std::endl;
- return false;
- }
- return true;
-}
-
-bool BinaryResourceParser::parsePackage(const ResChunk_header* chunk) {
- if (mValuePool.getError() != NO_ERROR) {
- Logger::error(mSource)
- << "no value string pool for ResTable."
- << std::endl;
- return false;
- }
-
- const ResTable_package* packageHeader = convertTo<ResTable_package>(chunk);
- if (!packageHeader) {
- Logger::error(mSource)
- << "could not parse chunk as ResTable_header."
- << std::endl;
- return false;
- }
-
- if (mTable->getPackageId() == ResourceTable::kUnsetPackageId) {
- // This is the first time the table has it's package ID set.
- mTable->setPackageId(packageHeader->id);
- } else if (mTable->getPackageId() != packageHeader->id) {
- Logger::error(mSource)
- << "ResTable_package has package ID "
- << std::hex << packageHeader->id << std::dec
- << " but ResourceTable has package ID "
- << std::hex << mTable->getPackageId() << std::dec
- << std::endl;
- return false;
- }
-
- size_t len = strnlen16(reinterpret_cast<const char16_t*>(packageHeader->name),
- sizeof(packageHeader->name) / sizeof(packageHeader->name[0]));
- if (mTable->getPackage().empty() && len == 0) {
- mTable->setPackage(mDefaultPackage);
- } else if (len > 0) {
- StringPiece16 thisPackage(reinterpret_cast<const char16_t*>(packageHeader->name), len);
- if (mTable->getPackage().empty()) {
- mTable->setPackage(thisPackage);
- } else if (thisPackage != mTable->getPackage()) {
- Logger::error(mSource)
- << "incompatible packages: "
- << mTable->getPackage()
- << " vs. "
- << thisPackage
- << std::endl;
- return false;
- }
- }
-
- ResChunkPullParser parser(getChunkData(packageHeader->header),
- getChunkDataLen(packageHeader->header));
- while (ResChunkPullParser::isGoodEvent(parser.next())) {
- switch (parser.getChunk()->type) {
- case android::RES_STRING_POOL_TYPE:
- if (mTypePool.getError() == NO_INIT) {
- if (mTypePool.setTo(parser.getChunk(), parser.getChunk()->size) !=
- NO_ERROR) {
- Logger::error(mSource)
- << "failed to parse type string pool with code "
- << mTypePool.getError()
- << "."
- << std::endl;
- return false;
- }
- } else if (mKeyPool.getError() == NO_INIT) {
- if (mKeyPool.setTo(parser.getChunk(), parser.getChunk()->size) !=
- NO_ERROR) {
- Logger::error(mSource)
- << "failed to parse key string pool with code "
- << mKeyPool.getError()
- << "."
- << std::endl;
- return false;
- }
- } else {
- Logger::warn(mSource)
- << "unexpected string pool."
- << std::endl;
- }
- break;
-
- case android::RES_TABLE_TYPE_SPEC_TYPE:
- if (!parseTypeSpec(parser.getChunk())) {
- return false;
- }
- break;
-
- case android::RES_TABLE_TYPE_TYPE:
- if (!parseType(parser.getChunk())) {
- return false;
- }
- break;
-
- case RES_TABLE_PUBLIC_TYPE:
- if (!parsePublic(parser.getChunk())) {
- return false;
- }
- break;
-
- default:
- Logger::warn(mSource)
- << "unexpected chunk of type "
- << parser.getChunk()->type
- << "."
- << std::endl;
- break;
- }
- }
-
- if (parser.getEvent() == ResChunkPullParser::Event::BadDocument) {
- Logger::error(mSource)
- << "bad package: "
- << parser.getLastError()
- << "."
- << std::endl;
- return false;
- }
-
- // Now go through the table and change resource ID references to
- // symbolic references.
-
- ReferenceIdToNameVisitor visitor(mResolver, &mIdIndex);
- for (auto& type : *mTable) {
- for (auto& entry : type->entries) {
- for (auto& configValue : entry->values) {
- configValue.value->accept(visitor, {});
- }
- }
- }
- return true;
-}
-
-bool BinaryResourceParser::parsePublic(const ResChunk_header* chunk) {
- const Public_header* header = convertTo<Public_header>(chunk);
-
- if (header->typeId == 0) {
- Logger::error(mSource)
- << "invalid type ID " << header->typeId << std::endl;
- return false;
- }
-
- const ResourceType* parsedType = parseResourceType(util::getString(mTypePool,
- header->typeId - 1));
- if (!parsedType) {
- Logger::error(mSource)
- << "invalid type " << util::getString(mTypePool, header->typeId - 1) << std::endl;
- return false;
- }
-
- const uintptr_t chunkEnd = reinterpret_cast<uintptr_t>(chunk) + chunk->size;
- const Public_entry* entry = reinterpret_cast<const Public_entry*>(
- getChunkData(header->header));
- for (uint32_t i = 0; i < header->count; i++) {
- if (reinterpret_cast<uintptr_t>(entry) + sizeof(*entry) > chunkEnd) {
- Logger::error(mSource)
- << "Public_entry extends beyond chunk."
- << std::endl;
- return false;
- }
-
- const ResourceId resId = { mTable->getPackageId(), header->typeId, entry->entryId };
- const ResourceName name = {
- mTable->getPackage(),
- *parsedType,
- util::getString(mKeyPool, entry->key.index).toString() };
-
- SourceLine source;
- if (mSourcePool.getError() == NO_ERROR) {
- source.path = util::utf16ToUtf8(util::getString(mSourcePool, entry->source.index));
- source.line = entry->sourceLine;
- }
-
- if (!mTable->markPublicAllowMangled(name, resId, source)) {
- return false;
- }
-
- // Add this resource name->id mapping to the index so
- // that we can resolve all ID references to name references.
- auto cacheIter = mIdIndex.find(resId);
- if (cacheIter == mIdIndex.end()) {
- mIdIndex.insert({ resId, name });
- }
-
- entry++;
- }
- return true;
-}
-
-bool BinaryResourceParser::parseTypeSpec(const ResChunk_header* chunk) {
- if (mTypePool.getError() != NO_ERROR) {
- Logger::error(mSource)
- << "no type string pool available for ResTable_typeSpec."
- << std::endl;
- return false;
- }
-
- const ResTable_typeSpec* typeSpec = convertTo<ResTable_typeSpec>(chunk);
- if (!typeSpec) {
- Logger::error(mSource)
- << "could not parse chunk as ResTable_typeSpec."
- << std::endl;
- return false;
- }
-
- if (typeSpec->id == 0) {
- Logger::error(mSource)
- << "ResTable_typeSpec has invalid id: "
- << typeSpec->id
- << "."
- << std::endl;
- return false;
- }
- return true;
-}
-
-bool BinaryResourceParser::parseType(const ResChunk_header* chunk) {
- if (mTypePool.getError() != NO_ERROR) {
- Logger::error(mSource)
- << "no type string pool available for ResTable_typeSpec."
- << std::endl;
- return false;
- }
-
- if (mKeyPool.getError() != NO_ERROR) {
- Logger::error(mSource)
- << "no key string pool available for ResTable_type."
- << std::endl;
- return false;
- }
-
- const ResTable_type* type = convertTo<ResTable_type>(chunk);
- if (!type) {
- Logger::error(mSource)
- << "could not parse chunk as ResTable_type."
- << std::endl;
- return false;
- }
-
- if (type->id == 0) {
- Logger::error(mSource)
- << "ResTable_type has invalid id: "
- << type->id
- << "."
- << std::endl;
- return false;
- }
-
- const ConfigDescription config(type->config);
- const StringPiece16 typeName = util::getString(mTypePool, type->id - 1);
-
- const ResourceType* parsedType = parseResourceType(typeName);
- if (!parsedType) {
- Logger::error(mSource)
- << "invalid type name '"
- << typeName
- << "' for type with ID "
- << uint32_t(type->id)
- << "." << std::endl;
- return false;
- }
-
- android::TypeVariant tv(type);
- for (auto it = tv.beginEntries(); it != tv.endEntries(); ++it) {
- if (!*it) {
- continue;
- }
-
- const ResTable_entry* entry = *it;
- const ResourceName name = {
- mTable->getPackage(),
- *parsedType,
- util::getString(mKeyPool, entry->key.index).toString()
- };
-
- const ResourceId resId = { mTable->getPackageId(), type->id, it.index() };
-
- std::unique_ptr<Value> resourceValue;
- const ResTable_entry_source* sourceBlock = nullptr;
- if (entry->flags & ResTable_entry::FLAG_COMPLEX) {
- const ResTable_map_entry* mapEntry = static_cast<const ResTable_map_entry*>(entry);
- if (mapEntry->size - sizeof(*mapEntry) == sizeof(*sourceBlock)) {
- const uint8_t* data = reinterpret_cast<const uint8_t*>(mapEntry);
- data += mapEntry->size - sizeof(*sourceBlock);
- sourceBlock = reinterpret_cast<const ResTable_entry_source*>(data);
- }
-
- // TODO(adamlesinski): Check that the entry count is valid.
- resourceValue = parseMapEntry(name, config, mapEntry);
- } else {
- if (entry->size - sizeof(*entry) == sizeof(*sourceBlock)) {
- const uint8_t* data = reinterpret_cast<const uint8_t*>(entry);
- data += entry->size - sizeof(*sourceBlock);
- sourceBlock = reinterpret_cast<const ResTable_entry_source*>(data);
- }
-
- const Res_value* value = reinterpret_cast<const Res_value*>(
- reinterpret_cast<const uint8_t*>(entry) + entry->size);
- resourceValue = parseValue(name, config, value, entry->flags);
- }
-
- if (!resourceValue) {
- // TODO(adamlesinski): For now this is ok, but it really shouldn't be.
- continue;
- }
-
- SourceLine source = mSource.line(0);
- if (sourceBlock) {
- size_t len;
- const char* str = mSourcePool.string8At(sourceBlock->pathIndex, &len);
- if (str) {
- source.path.assign(str, len);
- }
- source.line = sourceBlock->line;
- }
-
- if (!mTable->addResourceAllowMangled(name, config, source, std::move(resourceValue))) {
- return false;
- }
-
- if ((entry->flags & ResTable_entry::FLAG_PUBLIC) != 0) {
- if (!mTable->markPublicAllowMangled(name, resId, mSource.line(0))) {
- return false;
- }
- }
-
- // Add this resource name->id mapping to the index so
- // that we can resolve all ID references to name references.
- auto cacheIter = mIdIndex.find(resId);
- if (cacheIter == mIdIndex.end()) {
- mIdIndex.insert({ resId, name });
- }
- }
- return true;
-}
-
-std::unique_ptr<Item> BinaryResourceParser::parseValue(const ResourceNameRef& name,
- const ConfigDescription& config,
- const Res_value* value,
- uint16_t flags) {
- if (name.type == ResourceType::kId) {
- return util::make_unique<Id>();
- }
-
- if (value->dataType == Res_value::TYPE_STRING) {
- StringPiece16 str = util::getString(mValuePool, value->data);
-
- const ResStringPool_span* spans = mValuePool.styleAt(value->data);
- if (spans != nullptr) {
- StyleString styleStr = { str.toString() };
- while (spans->name.index != ResStringPool_span::END) {
- styleStr.spans.push_back(Span{
- util::getString(mValuePool, spans->name.index).toString(),
- spans->firstChar,
- spans->lastChar
- });
- spans++;
- }
- return util::make_unique<StyledString>(
- mTable->getValueStringPool().makeRef(
- styleStr, StringPool::Context{1, config}));
- } else {
- if (name.type != ResourceType::kString &&
- util::stringStartsWith<char16_t>(str, u"res/")) {
- // This must be a FileReference.
- return util::make_unique<FileReference>(mTable->getValueStringPool().makeRef(
- str, StringPool::Context{ 0, config }));
- }
-
- // There are no styles associated with this string, so treat it as
- // a simple string.
- return util::make_unique<String>(
- mTable->getValueStringPool().makeRef(
- str, StringPool::Context{1, config}));
- }
- }
-
- if (value->dataType == Res_value::TYPE_REFERENCE ||
- value->dataType == Res_value::TYPE_ATTRIBUTE) {
- const Reference::Type type = (value->dataType == Res_value::TYPE_REFERENCE) ?
- Reference::Type::kResource : Reference::Type::kAttribute;
-
- if (value->data != 0) {
- // This is a normal reference.
- return util::make_unique<Reference>(value->data, type);
- }
-
- // This reference has an invalid ID. Check if it is an unresolved symbol.
- ResourceNameRef symbol;
- if (getSymbol(&value->data, &symbol)) {
- return util::make_unique<Reference>(symbol, type);
- }
-
- // This is not an unresolved symbol, so it must be the magic @null reference.
- Res_value nullType = {};
- nullType.dataType = Res_value::TYPE_REFERENCE;
- return util::make_unique<BinaryPrimitive>(nullType);
- }
-
- if (value->dataType == ExtendedTypes::TYPE_RAW_STRING) {
- return util::make_unique<RawString>(
- mTable->getValueStringPool().makeRef(util::getString(mValuePool, value->data),
- StringPool::Context{ 1, config }));
- }
-
- // Treat this as a raw binary primitive.
- return util::make_unique<BinaryPrimitive>(*value);
-}
-
-std::unique_ptr<Value> BinaryResourceParser::parseMapEntry(const ResourceNameRef& name,
- const ConfigDescription& config,
- const ResTable_map_entry* map) {
- switch (name.type) {
- case ResourceType::kStyle:
- return parseStyle(name, config, map);
- case ResourceType::kAttr:
- return parseAttr(name, config, map);
- case ResourceType::kArray:
- return parseArray(name, config, map);
- case ResourceType::kStyleable:
- return parseStyleable(name, config, map);
- case ResourceType::kPlurals:
- return parsePlural(name, config, map);
- default:
- break;
- }
- return {};
-}
-
-std::unique_ptr<Style> BinaryResourceParser::parseStyle(const ResourceNameRef& name,
- const ConfigDescription& config,
- const ResTable_map_entry* map) {
- std::unique_ptr<Style> style = util::make_unique<Style>();
- if (map->parent.ident == 0) {
- // The parent is either not set or it is an unresolved symbol.
- // Check to see if it is a symbol.
- ResourceNameRef symbol;
- if (getSymbol(&map->parent.ident, &symbol)) {
- style->parent.name = symbol.toResourceName();
- }
- } else {
- // The parent is a regular reference to a resource.
- style->parent.id = map->parent.ident;
- }
-
- for (const ResTable_map& mapEntry : map) {
- style->entries.emplace_back();
- Style::Entry& styleEntry = style->entries.back();
-
- if (mapEntry.name.ident == 0) {
- // The map entry's key (attribute) is not set. This must be
- // a symbol reference, so resolve it.
- ResourceNameRef symbol;
- bool result = getSymbol(&mapEntry.name.ident, &symbol);
- assert(result);
- styleEntry.key.name = symbol.toResourceName();
- } else {
- // The map entry's key (attribute) is a regular reference.
- styleEntry.key.id = mapEntry.name.ident;
- }
-
- // Parse the attribute's value.
- styleEntry.value = parseValue(name, config, &mapEntry.value, 0);
- assert(styleEntry.value);
- }
- return style;
-}
-
-std::unique_ptr<Attribute> BinaryResourceParser::parseAttr(const ResourceNameRef& name,
- const ConfigDescription& config,
- const ResTable_map_entry* map) {
- const bool isWeak = (map->flags & ResTable_entry::FLAG_WEAK) != 0;
- std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(isWeak);
-
- // First we must discover what type of attribute this is. Find the type mask.
- auto typeMaskIter = std::find_if(begin(map), end(map), [](const ResTable_map& entry) -> bool {
- return entry.name.ident == ResTable_map::ATTR_TYPE;
- });
-
- if (typeMaskIter != end(map)) {
- attr->typeMask = typeMaskIter->value.data;
- }
-
- if (attr->typeMask & (ResTable_map::TYPE_ENUM | ResTable_map::TYPE_FLAGS)) {
- for (const ResTable_map& mapEntry : map) {
- if (Res_INTERNALID(mapEntry.name.ident)) {
- continue;
- }
-
- Attribute::Symbol symbol;
- symbol.value = mapEntry.value.data;
- if (mapEntry.name.ident == 0) {
- // The map entry's key (id) is not set. This must be
- // a symbol reference, so resolve it.
- ResourceNameRef symbolName;
- bool result = getSymbol(&mapEntry.name.ident, &symbolName);
- assert(result);
- symbol.symbol.name = symbolName.toResourceName();
- } else {
- // The map entry's key (id) is a regular reference.
- symbol.symbol.id = mapEntry.name.ident;
- }
-
- attr->symbols.push_back(std::move(symbol));
- }
- }
-
- // TODO(adamlesinski): Find min, max, i80n, etc attributes.
- return attr;
-}
-
-std::unique_ptr<Array> BinaryResourceParser::parseArray(const ResourceNameRef& name,
- const ConfigDescription& config,
- const ResTable_map_entry* map) {
- std::unique_ptr<Array> array = util::make_unique<Array>();
- for (const ResTable_map& mapEntry : map) {
- array->items.push_back(parseValue(name, config, &mapEntry.value, 0));
- }
- return array;
-}
-
-std::unique_ptr<Styleable> BinaryResourceParser::parseStyleable(const ResourceNameRef& name,
- const ConfigDescription& config,
- const ResTable_map_entry* map) {
- std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
- for (const ResTable_map& mapEntry : map) {
- if (mapEntry.name.ident == 0) {
- // The map entry's key (attribute) is not set. This must be
- // a symbol reference, so resolve it.
- ResourceNameRef symbol;
- bool result = getSymbol(&mapEntry.name.ident, &symbol);
- assert(result);
- styleable->entries.emplace_back(symbol);
- } else {
- // The map entry's key (attribute) is a regular reference.
- styleable->entries.emplace_back(mapEntry.name.ident);
- }
- }
- return styleable;
-}
-
-std::unique_ptr<Plural> BinaryResourceParser::parsePlural(const ResourceNameRef& name,
- const ConfigDescription& config,
- const ResTable_map_entry* map) {
- std::unique_ptr<Plural> plural = util::make_unique<Plural>();
- for (const ResTable_map& mapEntry : map) {
- std::unique_ptr<Item> item = parseValue(name, config, &mapEntry.value, 0);
-
- switch (mapEntry.name.ident) {
- case android::ResTable_map::ATTR_ZERO:
- plural->values[Plural::Zero] = std::move(item);
- break;
- case android::ResTable_map::ATTR_ONE:
- plural->values[Plural::One] = std::move(item);
- break;
- case android::ResTable_map::ATTR_TWO:
- plural->values[Plural::Two] = std::move(item);
- break;
- case android::ResTable_map::ATTR_FEW:
- plural->values[Plural::Few] = std::move(item);
- break;
- case android::ResTable_map::ATTR_MANY:
- plural->values[Plural::Many] = std::move(item);
- break;
- case android::ResTable_map::ATTR_OTHER:
- plural->values[Plural::Other] = std::move(item);
- break;
- }
- }
- return plural;
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/BinaryResourceParser.h b/tools/aapt2/BinaryResourceParser.h
deleted file mode 100644
index 3aab301..0000000
--- a/tools/aapt2/BinaryResourceParser.h
+++ /dev/null
@@ -1,160 +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_BINARY_RESOURCE_PARSER_H
-#define AAPT_BINARY_RESOURCE_PARSER_H
-
-#include "Resolver.h"
-#include "ResourceTable.h"
-#include "ResourceValues.h"
-#include "Source.h"
-
-#include <androidfw/ResourceTypes.h>
-#include <string>
-
-namespace aapt {
-
-struct SymbolTable_entry;
-
-/*
- * Parses a binary resource table (resources.arsc) and adds the entries
- * to a ResourceTable. This is different than the libandroidfw ResTable
- * in that it scans the table from top to bottom and doesn't require
- * support for random access. It is also able to parse non-runtime
- * chunks and types.
- */
-class BinaryResourceParser {
-public:
- /*
- * Creates a parser, which will read `len` bytes from `data`, and
- * add any resources parsed to `table`. `source` is for logging purposes.
- */
- BinaryResourceParser(const std::shared_ptr<ResourceTable>& table,
- const std::shared_ptr<IResolver>& resolver,
- const Source& source,
- const std::u16string& defaultPackage,
- const void* data, size_t len);
-
- BinaryResourceParser(const BinaryResourceParser&) = delete; // No copy.
-
- /*
- * Parses the binary resource table and returns true if successful.
- */
- bool parse();
-
-private:
- // Helper method to retrieve the symbol name for a given table offset specified
- // as a pointer.
- bool getSymbol(const void* data, ResourceNameRef* outSymbol);
-
- bool parseTable(const android::ResChunk_header* chunk);
- bool parseSymbolTable(const android::ResChunk_header* chunk);
-
- // Looks up the resource ID in the reference and converts it to a name if available.
- bool idToName(Reference* reference);
-
- bool parsePackage(const android::ResChunk_header* chunk);
- bool parsePublic(const android::ResChunk_header* chunk);
- bool parseTypeSpec(const android::ResChunk_header* chunk);
- bool parseType(const android::ResChunk_header* chunk);
-
- std::unique_ptr<Item> parseValue(const ResourceNameRef& name,
- const ConfigDescription& config, const android::Res_value* value, uint16_t flags);
-
- std::unique_ptr<Value> parseMapEntry(const ResourceNameRef& name,
- const ConfigDescription& config, const android::ResTable_map_entry* map);
-
- std::unique_ptr<Style> parseStyle(const ResourceNameRef& name,
- const ConfigDescription& config, const android::ResTable_map_entry* map);
-
- std::unique_ptr<Attribute> parseAttr(const ResourceNameRef& name,
- const ConfigDescription& config, const android::ResTable_map_entry* map);
-
- std::unique_ptr<Array> parseArray(const ResourceNameRef& name,
- const ConfigDescription& config, const android::ResTable_map_entry* map);
-
- std::unique_ptr<Plural> parsePlural(const ResourceNameRef& name,
- const ConfigDescription& config, const android::ResTable_map_entry* map);
-
- std::unique_ptr<Styleable> parseStyleable(const ResourceNameRef& name,
- const ConfigDescription& config, const android::ResTable_map_entry* map);
-
- std::shared_ptr<ResourceTable> mTable;
-
- std::shared_ptr<IResolver> mResolver;
-
- const Source mSource;
-
- // The package name of the resource table.
- std::u16string mDefaultPackage;
-
- const void* mData;
- const size_t mDataLen;
-
- // The array of symbol entries. Each element points to an offset
- // in the table and an index into the symbol table string pool.
- const SymbolTable_entry* mSymbolEntries = nullptr;
-
- // Number of symbol entries.
- size_t mSymbolEntryCount = 0;
-
- // The symbol table string pool. Holds the names of symbols
- // referenced in this table but not defined nor resolved to an
- // ID.
- android::ResStringPool mSymbolPool;
-
- // The source string pool. Resource entries may have an extra
- // field that points into this string pool, which denotes where
- // the resource was parsed from originally.
- android::ResStringPool mSourcePool;
-
- // The standard value string pool for resource values.
- android::ResStringPool mValuePool;
-
- // The string pool that holds the names of the types defined
- // in this table.
- android::ResStringPool mTypePool;
-
- // The string pool that holds the names of the entries defined
- // in this table.
- android::ResStringPool mKeyPool;
-
- // A mapping of resource ID to resource name. When we finish parsing
- // we use this to convert all resource IDs to symbolic references.
- std::map<ResourceId, ResourceName> mIdIndex;
-};
-
-} // namespace aapt
-
-namespace android {
-
-/**
- * Iterator functionality for ResTable_map_entry.
- */
-
-inline const ResTable_map* begin(const ResTable_map_entry* map) {
- return reinterpret_cast<const ResTable_map*>(
- reinterpret_cast<const uint8_t*>(map) + map->size);
-}
-
-inline const ResTable_map* end(const ResTable_map_entry* map) {
- return reinterpret_cast<const ResTable_map*>(
- reinterpret_cast<const uint8_t*>(map) + map->size) + map->count;
-}
-
-} // namespace android
-
-#endif // AAPT_BINARY_RESOURCE_PARSER_H
diff --git a/tools/aapt2/BindingXmlPullParser.cpp b/tools/aapt2/BindingXmlPullParser.cpp
deleted file mode 100644
index 4b7a656..0000000
--- a/tools/aapt2/BindingXmlPullParser.cpp
+++ /dev/null
@@ -1,268 +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.
- */
-
-#include "BindingXmlPullParser.h"
-#include "Util.h"
-
-#include <iostream>
-#include <sstream>
-#include <string>
-#include <vector>
-
-namespace aapt {
-
-constexpr const char16_t* kBindingNamespaceUri = u"http://schemas.android.com/apk/binding";
-constexpr const char16_t* kAndroidNamespaceUri = u"http://schemas.android.com/apk/res/android";
-constexpr const char16_t* kVariableTagName = u"variable";
-constexpr const char* kBindingTagPrefix = "android:binding_";
-
-BindingXmlPullParser::BindingXmlPullParser(const std::shared_ptr<XmlPullParser>& parser) :
- mParser(parser), mOverride(false), mNextTagId(0) {
-}
-
-bool BindingXmlPullParser::readVariableDeclaration() {
- VarDecl var;
-
- const auto endAttrIter = mParser->endAttributes();
- for (auto attrIter = mParser->beginAttributes(); attrIter != endAttrIter; ++attrIter) {
- if (!attrIter->namespaceUri.empty()) {
- continue;
- }
-
- if (attrIter->name == u"name") {
- var.name = util::utf16ToUtf8(attrIter->value);
- } else if (attrIter->name == u"type") {
- var.type = util::utf16ToUtf8(attrIter->value);
- }
- }
-
- XmlPullParser::skipCurrentElement(mParser.get());
-
- if (var.name.empty()) {
- mLastError = "variable declaration missing name";
- return false;
- }
-
- if (var.type.empty()) {
- mLastError = "variable declaration missing type";
- return false;
- }
-
- mVarDecls.push_back(std::move(var));
- return true;
-}
-
-bool BindingXmlPullParser::readExpressions() {
- mOverride = true;
- std::vector<XmlPullParser::Attribute> expressions;
- std::string idValue;
-
- const auto endAttrIter = mParser->endAttributes();
- for (auto attr = mParser->beginAttributes(); attr != endAttrIter; ++attr) {
- if (attr->namespaceUri == kAndroidNamespaceUri && attr->name == u"id") {
- idValue = util::utf16ToUtf8(attr->value);
- } else {
- StringPiece16 value = util::trimWhitespace(attr->value);
- if (util::stringStartsWith<char16_t>(value, u"@{") &&
- util::stringEndsWith<char16_t>(value, u"}")) {
- // This is attribute's value is an expression of the form
- // @{expression}. We need to capture the expression inside.
- expressions.push_back(XmlPullParser::Attribute{
- attr->namespaceUri,
- attr->name,
- value.substr(2, value.size() - 3).toString()
- });
- } else {
- // This is a normal attribute, use as is.
- mAttributes.emplace_back(*attr);
- }
- }
- }
-
- // Check if we have any expressions.
- if (!expressions.empty()) {
- // We have expressions, so let's assign the target a tag number
- // and add it to our targets list.
- int32_t targetId = mNextTagId++;
- mTargets.push_back(Target{
- util::utf16ToUtf8(mParser->getElementName()),
- idValue,
- targetId,
- std::move(expressions)
- });
-
- std::stringstream numGen;
- numGen << kBindingTagPrefix << targetId;
- mAttributes.push_back(XmlPullParser::Attribute{
- std::u16string(kAndroidNamespaceUri),
- std::u16string(u"tag"),
- util::utf8ToUtf16(numGen.str())
- });
- }
- return true;
-}
-
-XmlPullParser::Event BindingXmlPullParser::next() {
- // Clear old state in preparation for the next event.
- mOverride = false;
- mAttributes.clear();
-
- while (true) {
- Event event = mParser->next();
- if (event == Event::kStartElement) {
- if (mParser->getElementNamespace().empty() &&
- mParser->getElementName() == kVariableTagName) {
- // This is a variable tag. Record data from it, and
- // then discard the entire element.
- if (!readVariableDeclaration()) {
- // mLastError is set, so getEvent will return kBadDocument.
- return getEvent();
- }
- continue;
- } else {
- // Check for expressions of the form @{} in attribute text.
- const auto endAttrIter = mParser->endAttributes();
- for (auto attr = mParser->beginAttributes(); attr != endAttrIter; ++attr) {
- StringPiece16 value = util::trimWhitespace(attr->value);
- if (util::stringStartsWith<char16_t>(value, u"@{") &&
- util::stringEndsWith<char16_t>(value, u"}")) {
- if (!readExpressions()) {
- return getEvent();
- }
- break;
- }
- }
- }
- } else if (event == Event::kStartNamespace || event == Event::kEndNamespace) {
- if (mParser->getNamespaceUri() == kBindingNamespaceUri) {
- // Skip binding namespace tags.
- continue;
- }
- }
- return event;
- }
- return Event::kBadDocument;
-}
-
-bool BindingXmlPullParser::writeToFile(std::ostream& out) const {
- out << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
- out << "<Layout directory=\"\" layout=\"\" layoutId=\"\">\n";
-
- // Write the variables.
- out << " <Variables>\n";
- for (const VarDecl& v : mVarDecls) {
- out << " <entries name=\"" << v.name << "\" type=\"" << v.type << "\"/>\n";
- }
- out << " </Variables>\n";
-
- // Write the imports.
-
- std::stringstream tagGen;
-
- // Write the targets.
- out << " <Targets>\n";
- for (const Target& t : mTargets) {
- tagGen.str({});
- tagGen << kBindingTagPrefix << t.tagId;
- out << " <Target boundClass=\"" << t.className << "\" id=\"" << t.id
- << "\" tag=\"" << tagGen.str() << "\">\n";
- out << " <Expressions>\n";
- for (const XmlPullParser::Attribute& a : t.expressions) {
- out << " <Expression attribute=\"" << a.namespaceUri << ":" << a.name
- << "\" text=\"" << a.value << "\"/>\n";
- }
- out << " </Expressions>\n";
- out << " </Target>\n";
- }
- out << " </Targets>\n";
-
- out << "</Layout>\n";
- return bool(out);
-}
-
-XmlPullParser::const_iterator BindingXmlPullParser::beginAttributes() const {
- if (mOverride) {
- return mAttributes.begin();
- }
- return mParser->beginAttributes();
-}
-
-XmlPullParser::const_iterator BindingXmlPullParser::endAttributes() const {
- if (mOverride) {
- return mAttributes.end();
- }
- return mParser->endAttributes();
-}
-
-size_t BindingXmlPullParser::getAttributeCount() const {
- if (mOverride) {
- return mAttributes.size();
- }
- return mParser->getAttributeCount();
-}
-
-XmlPullParser::Event BindingXmlPullParser::getEvent() const {
- if (!mLastError.empty()) {
- return Event::kBadDocument;
- }
- return mParser->getEvent();
-}
-
-const std::string& BindingXmlPullParser::getLastError() const {
- if (!mLastError.empty()) {
- return mLastError;
- }
- return mParser->getLastError();
-}
-
-const std::u16string& BindingXmlPullParser::getComment() const {
- return mParser->getComment();
-}
-
-size_t BindingXmlPullParser::getLineNumber() const {
- return mParser->getLineNumber();
-}
-
-size_t BindingXmlPullParser::getDepth() const {
- return mParser->getDepth();
-}
-
-const std::u16string& BindingXmlPullParser::getText() const {
- return mParser->getText();
-}
-
-const std::u16string& BindingXmlPullParser::getNamespacePrefix() const {
- return mParser->getNamespacePrefix();
-}
-
-const std::u16string& BindingXmlPullParser::getNamespaceUri() const {
- return mParser->getNamespaceUri();
-}
-
-bool BindingXmlPullParser::applyPackageAlias(std::u16string* package,
- const std::u16string& defaultPackage) const {
- return mParser->applyPackageAlias(package, defaultPackage);
-}
-
-const std::u16string& BindingXmlPullParser::getElementNamespace() const {
- return mParser->getElementNamespace();
-}
-
-const std::u16string& BindingXmlPullParser::getElementName() const {
- return mParser->getElementName();
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/BindingXmlPullParser.h b/tools/aapt2/BindingXmlPullParser.h
deleted file mode 100644
index cfb16ef..0000000
--- a/tools/aapt2/BindingXmlPullParser.h
+++ /dev/null
@@ -1,90 +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_BINDING_XML_PULL_PARSER_H
-#define AAPT_BINDING_XML_PULL_PARSER_H
-
-#include "XmlPullParser.h"
-
-#include <iostream>
-#include <memory>
-#include <string>
-
-namespace aapt {
-
-class BindingXmlPullParser : public XmlPullParser {
-public:
- BindingXmlPullParser(const std::shared_ptr<XmlPullParser>& parser);
- BindingXmlPullParser(const BindingXmlPullParser& rhs) = delete;
-
- Event getEvent() const override;
- const std::string& getLastError() const override;
- Event next() override;
-
- const std::u16string& getComment() const override;
- size_t getLineNumber() const override;
- size_t getDepth() const override;
-
- const std::u16string& getText() const override;
-
- const std::u16string& getNamespacePrefix() const override;
- const std::u16string& getNamespaceUri() const override;
- bool applyPackageAlias(std::u16string* package, const std::u16string& defaultPackage)
- const override;
-
- const std::u16string& getElementNamespace() const override;
- const std::u16string& getElementName() const override;
-
- const_iterator beginAttributes() const override;
- const_iterator endAttributes() const override;
- size_t getAttributeCount() const override;
-
- bool writeToFile(std::ostream& out) const;
-
-private:
- struct VarDecl {
- std::string name;
- std::string type;
- };
-
- struct Import {
- std::string name;
- std::string type;
- };
-
- struct Target {
- std::string className;
- std::string id;
- int32_t tagId;
-
- std::vector<XmlPullParser::Attribute> expressions;
- };
-
- bool readVariableDeclaration();
- bool readExpressions();
-
- std::shared_ptr<XmlPullParser> mParser;
- std::string mLastError;
- bool mOverride;
- std::vector<XmlPullParser::Attribute> mAttributes;
- std::vector<VarDecl> mVarDecls;
- std::vector<Target> mTargets;
- int32_t mNextTagId;
-};
-
-} // namespace aapt
-
-#endif // AAPT_BINDING_XML_PULL_PARSER_H
diff --git a/tools/aapt2/BindingXmlPullParser_test.cpp b/tools/aapt2/BindingXmlPullParser_test.cpp
deleted file mode 100644
index 28edcb6..0000000
--- a/tools/aapt2/BindingXmlPullParser_test.cpp
+++ /dev/null
@@ -1,110 +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.
- */
-
-#include "SourceXmlPullParser.h"
-#include "BindingXmlPullParser.h"
-
-#include <gtest/gtest.h>
-#include <sstream>
-#include <string>
-
-namespace aapt {
-
-constexpr const char16_t* kAndroidNamespaceUri = u"http://schemas.android.com/apk/res/android";
-
-TEST(BindingXmlPullParserTest, SubstituteBindingExpressionsWithTag) {
- std::stringstream input;
- input << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
- << "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
- << " xmlns:bind=\"http://schemas.android.com/apk/binding\"\n"
- << " android:id=\"@+id/content\">\n"
- << " <variable name=\"user\" type=\"com.android.test.User\"/>\n"
- << " <TextView android:text=\"@{user.name}\" android:layout_width=\"wrap_content\"\n"
- << " android:layout_height=\"wrap_content\"/>\n"
- << "</LinearLayout>\n";
- std::shared_ptr<XmlPullParser> sourceParser = std::make_shared<SourceXmlPullParser>(input);
- BindingXmlPullParser parser(sourceParser);
-
- ASSERT_EQ(XmlPullParser::Event::kStartNamespace, parser.next());
- EXPECT_EQ(std::u16string(u"http://schemas.android.com/apk/res/android"),
- parser.getNamespaceUri());
-
- ASSERT_EQ(XmlPullParser::Event::kStartElement, parser.next());
- EXPECT_EQ(std::u16string(u"LinearLayout"), parser.getElementName());
-
- while (parser.next() == XmlPullParser::Event::kText) {}
-
- ASSERT_EQ(XmlPullParser::Event::kStartElement, parser.getEvent());
- EXPECT_EQ(std::u16string(u"TextView"), parser.getElementName());
-
- ASSERT_EQ(3u, parser.getAttributeCount());
- const auto endAttr = parser.endAttributes();
- EXPECT_NE(endAttr, parser.findAttribute(kAndroidNamespaceUri, u"layout_width"));
- EXPECT_NE(endAttr, parser.findAttribute(kAndroidNamespaceUri, u"layout_height"));
- EXPECT_NE(endAttr, parser.findAttribute(kAndroidNamespaceUri, u"tag"));
-
- while (parser.next() == XmlPullParser::Event::kText) {}
-
- ASSERT_EQ(XmlPullParser::Event::kEndElement, parser.getEvent());
-
- while (parser.next() == XmlPullParser::Event::kText) {}
-
- ASSERT_EQ(XmlPullParser::Event::kEndElement, parser.getEvent());
- ASSERT_EQ(XmlPullParser::Event::kEndNamespace, parser.next());
-}
-
-TEST(BindingXmlPullParserTest, GenerateVariableDeclarations) {
- std::stringstream input;
- input << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
- << "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
- << " xmlns:bind=\"http://schemas.android.com/apk/binding\"\n"
- << " android:id=\"@+id/content\">\n"
- << " <variable name=\"user\" type=\"com.android.test.User\"/>\n"
- << "</LinearLayout>\n";
- std::shared_ptr<XmlPullParser> sourceParser = std::make_shared<SourceXmlPullParser>(input);
- BindingXmlPullParser parser(sourceParser);
-
- while (XmlPullParser::isGoodEvent(parser.next())) {
- ASSERT_NE(XmlPullParser::Event::kBadDocument, parser.getEvent());
- }
-
- std::stringstream output;
- ASSERT_TRUE(parser.writeToFile(output));
-
- std::string result = output.str();
- EXPECT_NE(std::string::npos,
- result.find("<entries name=\"user\" type=\"com.android.test.User\"/>"));
-}
-
-TEST(BindingXmlPullParserTest, FailOnMissingNameOrTypeInVariableDeclaration) {
- std::stringstream input;
- input << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
- << "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
- << " xmlns:bind=\"http://schemas.android.com/apk/binding\"\n"
- << " android:id=\"@+id/content\">\n"
- << " <variable name=\"user\"/>\n"
- << "</LinearLayout>\n";
- std::shared_ptr<XmlPullParser> sourceParser = std::make_shared<SourceXmlPullParser>(input);
- BindingXmlPullParser parser(sourceParser);
-
- while (XmlPullParser::isGoodEvent(parser.next())) {}
-
- EXPECT_EQ(XmlPullParser::Event::kBadDocument, parser.getEvent());
- EXPECT_FALSE(parser.getLastError().empty());
-}
-
-
-} // namespace aapt
diff --git a/tools/aapt2/Compat_test.cpp b/tools/aapt2/Compat_test.cpp
deleted file mode 100644
index 96aee44..0000000
--- a/tools/aapt2/Compat_test.cpp
+++ /dev/null
@@ -1,33 +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.
- */
-
-#include <gtest/gtest.h>
-
-namespace aapt {
-
-TEST(CompatTest, VersionAttributesInStyle) {
-}
-
-TEST(CompatTest, VersionAttributesInXML) {
-}
-
-TEST(CompatTest, DoNotOverrideExistingVersionedFiles) {
-}
-
-TEST(CompatTest, VersionAttributesInStyleWithCorrectPrecedence) {
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/ConfigDescription.cpp b/tools/aapt2/ConfigDescription.cpp
index 6ddf94a..64353de 100644
--- a/tools/aapt2/ConfigDescription.cpp
+++ b/tools/aapt2/ConfigDescription.cpp
@@ -17,8 +17,8 @@
#include "ConfigDescription.h"
#include "Locale.h"
#include "SdkConstants.h"
-#include "StringPiece.h"
-#include "Util.h"
+#include "util/StringPiece.h"
+#include "util/Util.h"
#include <androidfw/ResourceTypes.h>
#include <string>
@@ -164,6 +164,26 @@
return false;
}
+static bool parseScreenRound(const char* name, ResTable_config* out) {
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->screenLayout2 =
+ (out->screenLayout2&~ResTable_config::MASK_SCREENROUND)
+ | ResTable_config::SCREENROUND_ANY;
+ return true;
+ } else if (strcmp(name, "round") == 0) {
+ if (out) out->screenLayout2 =
+ (out->screenLayout2&~ResTable_config::MASK_SCREENROUND)
+ | ResTable_config::SCREENROUND_YES;
+ return true;
+ } else if (strcmp(name, "notround") == 0) {
+ if (out) out->screenLayout2 =
+ (out->screenLayout2&~ResTable_config::MASK_SCREENROUND)
+ | ResTable_config::SCREENROUND_NO;
+ return true;
+ }
+ return false;
+}
+
static bool parseOrientation(const char* name, ResTable_config* out) {
if (strcmp(name, kWildcardName) == 0) {
if (out) out->orientation = out->ORIENTATION_ANY;
@@ -635,6 +655,13 @@
}
}
+ if (parseScreenRound(partIter->c_str(), &config)) {
+ ++partIter;
+ if (partIter == partsEnd) {
+ goto success;
+ }
+ }
+
if (parseOrientation(partIter->c_str(), &config)) {
++partIter;
if (partIter == partsEnd) {
@@ -725,7 +752,9 @@
void ConfigDescription::applyVersionForCompatibility(ConfigDescription* config) {
uint16_t minSdk = 0;
- if (config->density == ResTable_config::DENSITY_ANY) {
+ if (config->screenLayout2 & ResTable_config::MASK_SCREENROUND) {
+ minSdk = SDK_MARSHMALLOW;
+ } else if (config->density == ResTable_config::DENSITY_ANY) {
minSdk = SDK_LOLLIPOP;
} else if (config->smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
|| config->screenWidthDp != ResTable_config::SCREENWIDTH_ANY
diff --git a/tools/aapt2/ConfigDescription.h b/tools/aapt2/ConfigDescription.h
index 67b4b75..4af089d 100644
--- a/tools/aapt2/ConfigDescription.h
+++ b/tools/aapt2/ConfigDescription.h
@@ -17,7 +17,7 @@
#ifndef AAPT_CONFIG_DESCRIPTION_H
#define AAPT_CONFIG_DESCRIPTION_H
-#include "StringPiece.h"
+#include "util/StringPiece.h"
#include <androidfw/ResourceTypes.h>
#include <ostream>
diff --git a/tools/aapt2/ConfigDescription_test.cpp b/tools/aapt2/ConfigDescription_test.cpp
index c57e351..e68d6be 100644
--- a/tools/aapt2/ConfigDescription_test.cpp
+++ b/tools/aapt2/ConfigDescription_test.cpp
@@ -15,7 +15,9 @@
*/
#include "ConfigDescription.h"
-#include "StringPiece.h"
+#include "SdkConstants.h"
+
+#include "util/StringPiece.h"
#include <gtest/gtest.h>
#include <string>
@@ -79,4 +81,19 @@
EXPECT_EQ(android::ResTable_config::UI_MODE_TYPE_CAR, config.uiMode);
}
+TEST(ConfigDescriptionTest, TestParsingRoundQualifier) {
+ ConfigDescription config;
+ EXPECT_TRUE(TestParse("round", &config));
+ EXPECT_EQ(android::ResTable_config::SCREENROUND_YES,
+ config.screenLayout2 & android::ResTable_config::MASK_SCREENROUND);
+ EXPECT_EQ(SDK_MARSHMALLOW, config.sdkVersion);
+ EXPECT_EQ(std::string("round-v23"), config.toString().string());
+
+ EXPECT_TRUE(TestParse("notround", &config));
+ EXPECT_EQ(android::ResTable_config::SCREENROUND_NO,
+ config.screenLayout2 & android::ResTable_config::MASK_SCREENROUND);
+ EXPECT_EQ(SDK_MARSHMALLOW, config.sdkVersion);
+ EXPECT_EQ(std::string("notround-v23"), config.toString().string());
+}
+
} // namespace aapt
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index cf222c6..d864f66 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -17,7 +17,8 @@
#include "Debug.h"
#include "ResourceTable.h"
#include "ResourceValues.h"
-#include "Util.h"
+#include "util/Util.h"
+#include "ValueVisitor.h"
#include <algorithm>
#include <iostream>
@@ -29,102 +30,120 @@
namespace aapt {
-struct PrintVisitor : ConstValueVisitor {
- void visit(const Attribute& attr, ValueVisitorArgs&) override {
+struct PrintVisitor : public ValueVisitor {
+ using ValueVisitor::visit;
+
+ void visit(Attribute* attr) override {
std::cout << "(attr) type=";
- attr.printMask(std::cout);
+ attr->printMask(&std::cout);
static constexpr uint32_t kMask = android::ResTable_map::TYPE_ENUM |
android::ResTable_map::TYPE_FLAGS;
- if (attr.typeMask & kMask) {
- for (const auto& symbol : attr.symbols) {
- std::cout << "\n "
- << symbol.symbol.name.entry << " (" << symbol.symbol.id << ") = "
- << symbol.value;
+ if (attr->typeMask & kMask) {
+ for (const auto& symbol : attr->symbols) {
+ std::cout << "\n " << symbol.symbol.name.value().entry;
+ if (symbol.symbol.id) {
+ std::cout << " (" << symbol.symbol.id.value() << ")";
+ }
+ std::cout << " = " << symbol.value;
}
}
}
- void visit(const Style& style, ValueVisitorArgs&) override {
+ void visit(Style* style) override {
std::cout << "(style)";
- if (style.parent.name.isValid() || style.parent.id.isValid()) {
+ if (style->parent) {
std::cout << " parent=";
- if (style.parent.name.isValid()) {
- std::cout << style.parent.name << " ";
+ if (style->parent.value().name) {
+ std::cout << style->parent.value().name.value() << " ";
}
- if (style.parent.id.isValid()) {
- std::cout << style.parent.id;
+ if (style->parent.value().id) {
+ std::cout << style->parent.value().id.value();
}
}
- for (const auto& entry : style.entries) {
+ for (const auto& entry : style->entries) {
std::cout << "\n ";
- if (entry.key.name.isValid()) {
- std::cout << entry.key.name.package << ":" << entry.key.name.entry;
+ if (entry.key.name) {
+ std::cout << entry.key.name.value().package << ":" << entry.key.name.value().entry;
}
- if (entry.key.id.isValid()) {
- std::cout << "(" << entry.key.id << ")";
+ if (entry.key.id) {
+ std::cout << "(" << entry.key.id.value() << ")";
}
std::cout << "=" << *entry.value;
}
}
- void visit(const Array& array, ValueVisitorArgs&) override {
- array.print(std::cout);
+ void visit(Array* array) override {
+ array->print(&std::cout);
}
- void visit(const Plural& plural, ValueVisitorArgs&) override {
- plural.print(std::cout);
+ void visit(Plural* plural) override {
+ plural->print(&std::cout);
}
- void visit(const Styleable& styleable, ValueVisitorArgs&) override {
- styleable.print(std::cout);
+ void visit(Styleable* styleable) override {
+ styleable->print(&std::cout);
}
- void visitItem(const Item& item, ValueVisitorArgs& args) override {
- item.print(std::cout);
+ void visitItem(Item* item) override {
+ item->print(&std::cout);
}
};
-void Debug::printTable(const std::shared_ptr<ResourceTable>& table) {
- std::cout << "Package name=" << table->getPackage();
- if (table->getPackageId() != ResourceTable::kUnsetPackageId) {
- std::cout << " id=" << std::hex << table->getPackageId() << std::dec;
- }
- std::cout << std::endl;
-
- for (const auto& type : *table) {
- std::cout << " type " << type->type;
- if (type->typeId != ResourceTableType::kUnsetTypeId) {
- std::cout << " id=" << std::hex << type->typeId << std::dec;
+void Debug::printTable(ResourceTable* table) {
+ for (auto& package : table->packages) {
+ std::cout << "Package name=" << package->name;
+ if (package->id) {
+ std::cout << " id=" << std::hex << (int) package->id.value() << std::dec;
}
- std::cout << " entryCount=" << type->entries.size() << std::endl;
+ std::cout << std::endl;
- std::vector<const ResourceEntry*> sortedEntries;
- for (const auto& entry : type->entries) {
- auto iter = std::lower_bound(sortedEntries.begin(), sortedEntries.end(), entry.get(),
- [](const ResourceEntry* a, const ResourceEntry* b) -> bool {
- return a->entryId < b->entryId;
- });
- sortedEntries.insert(iter, entry.get());
- }
-
- for (const ResourceEntry* entry : sortedEntries) {
- ResourceId id = { table->getPackageId(), type->typeId, entry->entryId };
- ResourceName name = { table->getPackage(), type->type, entry->name };
- std::cout << " spec resource " << id << " " << name;
- if (entry->publicStatus.isPublic) {
- std::cout << " PUBLIC";
+ for (const auto& type : package->types) {
+ std::cout << " type " << type->type;
+ if (type->id) {
+ std::cout << " id=" << std::hex << (int) type->id.value() << std::dec;
}
- std::cout << std::endl;
+ std::cout << " entryCount=" << type->entries.size() << std::endl;
- PrintVisitor visitor;
- for (const auto& value : entry->values) {
- std::cout << " (" << value.config << ") ";
- value.value->accept(visitor, {});
+ std::vector<const ResourceEntry*> sortedEntries;
+ for (const auto& entry : type->entries) {
+ auto iter = std::lower_bound(sortedEntries.begin(), sortedEntries.end(), entry.get(),
+ [](const ResourceEntry* a, const ResourceEntry* b) -> bool {
+ if (a->id && b->id) {
+ return a->id.value() < b->id.value();
+ } else if (a->id) {
+ return true;
+ } else {
+ return false;
+ }
+ });
+ sortedEntries.insert(iter, entry.get());
+ }
+
+ for (const ResourceEntry* entry : sortedEntries) {
+ ResourceId id(package->id ? package->id.value() : uint8_t(0),
+ type->id ? type->id.value() : uint8_t(0),
+ entry->id ? entry->id.value() : uint16_t(0));
+ ResourceName name(package->name, type->type, entry->name);
+
+ std::cout << " spec resource " << id << " " << name;
+ switch (entry->symbolStatus.state) {
+ case SymbolState::kPublic: std::cout << " PUBLIC"; break;
+ case SymbolState::kPrivate: std::cout << " _PRIVATE_"; break;
+ default: break;
+ }
+
std::cout << std::endl;
+
+ PrintVisitor visitor;
+ for (const auto& value : entry->values) {
+ std::cout << " (" << value.config << ") ";
+ value.value->accept(&visitor);
+ std::cout << std::endl;
+ }
}
}
}
@@ -136,8 +155,7 @@
return std::distance(names.begin(), iter);
}
-void Debug::printStyleGraph(const std::shared_ptr<ResourceTable>& table,
- const ResourceName& targetStyle) {
+void Debug::printStyleGraph(ResourceTable* table, const ResourceName& targetStyle) {
std::map<ResourceName, std::set<ResourceName>> graph;
std::queue<ResourceName> stylesToVisit;
@@ -150,17 +168,16 @@
continue;
}
- const ResourceTableType* type;
- const ResourceEntry* entry;
- std::tie(type, entry) = table->findResource(styleName);
- if (entry) {
+ Maybe<ResourceTable::SearchResult> result = table->findResource(styleName);
+ if (result) {
+ ResourceEntry* entry = result.value().entry;
for (const auto& value : entry->values) {
- visitFunc<Style>(*value.value, [&](const Style& style) {
- if (style.parent.name.isValid()) {
- parents.insert(style.parent.name);
- stylesToVisit.push(style.parent.name);
+ if (Style* style = valueCast<Style>(value.value.get())) {
+ if (style->parent && style->parent.value().name) {
+ parents.insert(style->parent.value().name.value());
+ stylesToVisit.push(style->parent.value().name.value());
}
- });
+ }
}
}
}
diff --git a/tools/aapt2/Debug.h b/tools/aapt2/Debug.h
index cdb3dcb..5b0d7d6 100644
--- a/tools/aapt2/Debug.h
+++ b/tools/aapt2/Debug.h
@@ -20,13 +20,11 @@
#include "Resource.h"
#include "ResourceTable.h"
-#include <memory>
-
namespace aapt {
struct Debug {
- static void printTable(const std::shared_ptr<ResourceTable>& table);
- static void printStyleGraph(const std::shared_ptr<ResourceTable>& table,
+ static void printTable(ResourceTable* table);
+ static void printStyleGraph(ResourceTable* table,
const ResourceName& targetStyle);
};
diff --git a/tools/aapt2/Diagnostics.h b/tools/aapt2/Diagnostics.h
new file mode 100644
index 0000000..7ea26b3
--- /dev/null
+++ b/tools/aapt2/Diagnostics.h
@@ -0,0 +1,97 @@
+/*
+ * 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_DIAGNOSTICS_H
+#define AAPT_DIAGNOSTICS_H
+
+#include "Source.h"
+
+#include "util/StringPiece.h"
+#include "util/Util.h"
+
+#include <iostream>
+#include <sstream>
+#include <string>
+
+namespace aapt {
+
+struct DiagMessageActual {
+ Source source;
+ std::string message;
+};
+
+struct DiagMessage {
+private:
+ Source mSource;
+ std::stringstream mMessage;
+
+public:
+ DiagMessage() = default;
+
+ DiagMessage(const StringPiece& src) : mSource(src) {
+ }
+
+ DiagMessage(const Source& src) : mSource(src) {
+ }
+
+ template <typename T> DiagMessage& operator<<(const T& value) {
+ mMessage << value;
+ return *this;
+ }
+
+ DiagMessageActual build() const {
+ return DiagMessageActual{ mSource, mMessage.str() };
+ }
+};
+
+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;
+};
+
+struct StdErrDiagnostics : public IDiagnostics {
+ 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;
+ }
+
+ void error(const DiagMessage& msg) override {
+ if (mNumErrors < 20) {
+ emit(msg, "error: ");
+ }
+ mNumErrors++;
+ }
+
+ void warn(const DiagMessage& msg) override {
+ emit(msg, "warn: ");
+ }
+
+ void note(const DiagMessage& msg) override {
+ emit(msg, "note: ");
+ }
+};
+
+} // namespace aapt
+
+#endif /* AAPT_DIAGNOSTICS_H */
diff --git a/tools/aapt2/Files.cpp b/tools/aapt2/Files.cpp
deleted file mode 100644
index b24ff6b..0000000
--- a/tools/aapt2/Files.cpp
+++ /dev/null
@@ -1,188 +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.
- */
-
-#include "Files.h"
-#include "Util.h"
-
-#include <cerrno>
-#include <dirent.h>
-#include <string>
-#include <sys/stat.h>
-
-#ifdef _WIN32
-// Windows includes.
-#include <direct.h>
-#endif
-
-namespace aapt {
-
-FileType getFileType(const StringPiece& path) {
- struct stat sb;
- if (stat(path.data(), &sb) < 0) {
- if (errno == ENOENT || errno == ENOTDIR) {
- return FileType::kNonexistant;
- }
- return FileType::kUnknown;
- }
-
- if (S_ISREG(sb.st_mode)) {
- return FileType::kRegular;
- } else if (S_ISDIR(sb.st_mode)) {
- return FileType::kDirectory;
- } else if (S_ISCHR(sb.st_mode)) {
- return FileType::kCharDev;
- } else if (S_ISBLK(sb.st_mode)) {
- return FileType::kBlockDev;
- } else if (S_ISFIFO(sb.st_mode)) {
- return FileType::kFifo;
-#if defined(S_ISLNK)
- } else if (S_ISLNK(sb.st_mode)) {
- return FileType::kSymlink;
-#endif
-#if defined(S_ISSOCK)
- } else if (S_ISSOCK(sb.st_mode)) {
- return FileType::kSocket;
-#endif
- } else {
- return FileType::kUnknown;
- }
-}
-
-std::vector<std::string> listFiles(const StringPiece& root) {
- DIR* dir = opendir(root.data());
- if (dir == nullptr) {
- Logger::error(Source{ root.toString() })
- << "unable to open file: "
- << strerror(errno)
- << "."
- << std::endl;
- return {};
- }
-
- std::vector<std::string> files;
- dirent* entry;
- while ((entry = readdir(dir))) {
- files.emplace_back(entry->d_name);
- }
-
- closedir(dir);
- return files;
-}
-
-inline static int mkdirImpl(const StringPiece& path) {
-#ifdef _WIN32
- return _mkdir(path.toString().c_str());
-#else
- return mkdir(path.toString().c_str(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP);
-#endif
-}
-
-bool mkdirs(const StringPiece& path) {
- const char* start = path.begin();
- const char* end = path.end();
- for (const char* current = start; current != end; ++current) {
- if (*current == sDirSep) {
- StringPiece parentPath(start, current - start);
- int result = mkdirImpl(parentPath);
- if (result < 0 && errno != EEXIST) {
- return false;
- }
- }
- }
- return mkdirImpl(path) == 0 || errno == EEXIST;
-}
-
-std::string getStem(const StringPiece& path) {
- const char* start = path.begin();
- const char* end = path.end();
- for (const char* current = end - 1; current != start - 1; --current) {
- if (*current == sDirSep) {
- return std::string(start, current - start);
- }
- }
- return {};
-}
-
-bool FileFilter::setPattern(const StringPiece& pattern) {
- mPatternTokens = util::splitAndLowercase(pattern, ':');
- return true;
-}
-
-bool FileFilter::operator()(const std::string& filename, FileType type) const {
- if (filename == "." || filename == "..") {
- return false;
- }
-
- const char kDir[] = "dir";
- const char kFile[] = "file";
- const size_t filenameLen = filename.length();
- bool chatty = true;
- for (const std::string& token : mPatternTokens) {
- const char* tokenStr = token.c_str();
- if (*tokenStr == '!') {
- chatty = false;
- tokenStr++;
- }
-
- if (strncasecmp(tokenStr, kDir, sizeof(kDir)) == 0) {
- if (type != FileType::kDirectory) {
- continue;
- }
- tokenStr += sizeof(kDir);
- }
-
- if (strncasecmp(tokenStr, kFile, sizeof(kFile)) == 0) {
- if (type != FileType::kRegular) {
- continue;
- }
- tokenStr += sizeof(kFile);
- }
-
- bool ignore = false;
- size_t n = strlen(tokenStr);
- if (*tokenStr == '*') {
- // Math suffix.
- tokenStr++;
- n--;
- if (n <= filenameLen) {
- ignore = strncasecmp(tokenStr, filename.c_str() + filenameLen - n, n) == 0;
- }
- } else if (n > 1 && tokenStr[n - 1] == '*') {
- // Match prefix.
- ignore = strncasecmp(tokenStr, filename.c_str(), n - 1) == 0;
- } else {
- ignore = strcasecmp(tokenStr, filename.c_str()) == 0;
- }
-
- if (ignore) {
- if (chatty) {
- Logger::warn()
- << "skipping " <<
- (type == FileType::kDirectory ? "dir '" : "file '")
- << filename
- << "' due to ignore pattern '"
- << token
- << "'."
- << std::endl;
- }
- return false;
- }
- }
- return true;
-}
-
-
-} // namespace aapt
diff --git a/tools/aapt2/Files.h b/tools/aapt2/Files.h
deleted file mode 100644
index 844fd2b..0000000
--- a/tools/aapt2/Files.h
+++ /dev/null
@@ -1,128 +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_FILES_H
-#define AAPT_FILES_H
-
-#include "Logger.h"
-#include "Source.h"
-#include "StringPiece.h"
-
-#include <cassert>
-#include <string>
-#include <vector>
-
-namespace aapt {
-
-#ifdef _WIN32
-constexpr const char sDirSep = '\\';
-#else
-constexpr const char sDirSep = '/';
-#endif
-
-enum class FileType {
- kUnknown = 0,
- kNonexistant,
- kRegular,
- kDirectory,
- kCharDev,
- kBlockDev,
- kFifo,
- kSymlink,
- kSocket,
-};
-
-FileType getFileType(const StringPiece& path);
-
-/*
- * Lists files under the directory `root`. Files are listed
- * with just their leaf (filename) names.
- */
-std::vector<std::string> listFiles(const StringPiece& root);
-
-/*
- * Appends a path to `base`, separated by the directory separator.
- */
-void appendPath(std::string* base, const StringPiece& part);
-
-/*
- * Appends a series of paths to `base`, separated by the
- * system directory separator.
- */
-template <typename... Ts >
-void appendPath(std::string* base, const StringPiece& part, const Ts&... parts);
-
-/*
- * Makes all the directories in `path`. The last element in the path
- * is interpreted as a directory.
- */
-bool mkdirs(const StringPiece& path);
-
-/**
- * Returns all but the last part of the path.
- */
-std::string getStem(const StringPiece& path);
-
-/*
- * Filter that determines which resource files/directories are
- * processed by AAPT. Takes a pattern string supplied by the user.
- * Pattern format is specified in the
- * FileFilter::setPattern(const std::string&) method.
- */
-class FileFilter {
-public:
- /*
- * Patterns syntax:
- * - Delimiter is :
- * - Entry can start with the flag ! to avoid printing a warning
- * about the file being ignored.
- * - Entry can have the flag "<dir>" to match only directories
- * or <file> to match only files. Default is to match both.
- * - Entry can be a simplified glob "<prefix>*" or "*<suffix>"
- * where prefix/suffix must have at least 1 character (so that
- * we don't match a '*' catch-all pattern.)
- * - The special filenames "." and ".." are always ignored.
- * - Otherwise the full string is matched.
- * - match is not case-sensitive.
- */
- bool setPattern(const StringPiece& pattern);
-
- /**
- * Applies the filter, returning true for pass, false for fail.
- */
- bool operator()(const std::string& filename, FileType type) const;
-
-private:
- std::vector<std::string> mPatternTokens;
-};
-
-inline void appendPath(std::string* base, const StringPiece& part) {
- assert(base);
- *base += sDirSep;
- base->append(part.data(), part.size());
-}
-
-template <typename... Ts >
-void appendPath(std::string* base, const StringPiece& part, const Ts&... parts) {
- assert(base);
- *base += sDirSep;
- base->append(part.data(), part.size());
- appendPath(base, parts...);
-}
-
-} // namespace aapt
-
-#endif // AAPT_FILES_H
diff --git a/tools/aapt2/Flag.cpp b/tools/aapt2/Flag.cpp
deleted file mode 100644
index 76985da..0000000
--- a/tools/aapt2/Flag.cpp
+++ /dev/null
@@ -1,132 +0,0 @@
-#include "Flag.h"
-#include "StringPiece.h"
-
-#include <functional>
-#include <iomanip>
-#include <iostream>
-#include <string>
-#include <vector>
-
-namespace aapt {
-namespace flag {
-
-struct Flag {
- std::string name;
- std::string description;
- std::function<bool(const StringPiece&, std::string*)> action;
- bool required;
- bool* flagResult;
- bool flagValueWhenSet;
- bool parsed;
-};
-
-static std::vector<Flag> sFlags;
-static std::vector<std::string> sArgs;
-
-static std::function<bool(const StringPiece&, std::string*)> wrap(
- const std::function<void(const StringPiece&)>& action) {
- return [action](const StringPiece& arg, std::string*) -> bool {
- action(arg);
- return true;
- };
-}
-
-void optionalFlag(const StringPiece& name, const StringPiece& description,
- std::function<void(const StringPiece&)> action) {
- sFlags.push_back(Flag{
- name.toString(), description.toString(), wrap(action),
- false, nullptr, false, false });
-}
-
-void requiredFlag(const StringPiece& name, const StringPiece& description,
- std::function<void(const StringPiece&)> action) {
- sFlags.push_back(Flag{ name.toString(), description.toString(), wrap(action),
- true, nullptr, false, false });
-}
-
-void requiredFlag(const StringPiece& name, const StringPiece& description,
- std::function<bool(const StringPiece&, std::string*)> action) {
- sFlags.push_back(Flag{ name.toString(), description.toString(), action,
- true, nullptr, false, false });
-}
-
-void optionalSwitch(const StringPiece& name, const StringPiece& description, bool resultWhenSet,
- bool* result) {
- sFlags.push_back(Flag{
- name.toString(), description.toString(), {},
- false, result, resultWhenSet, false });
-}
-
-void usageAndDie(const StringPiece& command) {
- std::cerr << command << " [options]";
- for (const Flag& flag : sFlags) {
- if (flag.required) {
- std::cerr << " " << flag.name << " arg";
- }
- }
- std::cerr << " files..." << std::endl << std::endl << "Options:" << std::endl;
-
- for (const Flag& flag : sFlags) {
- std::string command = flag.name;
- if (!flag.flagResult) {
- command += " arg ";
- }
- std::cerr << " " << std::setw(30) << std::left << command
- << flag.description << std::endl;
- }
- exit(1);
-}
-
-void parse(int argc, char** argv, const StringPiece& command) {
- std::string errorStr;
- for (int i = 0; i < argc; i++) {
- const StringPiece arg(argv[i]);
- if (*arg.data() != '-') {
- sArgs.push_back(arg.toString());
- continue;
- }
-
- bool match = false;
- for (Flag& flag : sFlags) {
- if (arg == flag.name) {
- match = true;
- flag.parsed = true;
- if (flag.flagResult) {
- *flag.flagResult = flag.flagValueWhenSet;
- } else {
- i++;
- if (i >= argc) {
- std::cerr << flag.name << " missing argument." << std::endl
- << std::endl;
- usageAndDie(command);
- }
-
- if (!flag.action(argv[i], &errorStr)) {
- std::cerr << errorStr << "." << std::endl << std::endl;
- usageAndDie(command);
- }
- }
- break;
- }
- }
-
- if (!match) {
- std::cerr << "unknown option '" << arg << "'." << std::endl << std::endl;
- usageAndDie(command);
- }
- }
-
- for (const Flag& flag : sFlags) {
- if (flag.required && !flag.parsed) {
- std::cerr << "missing required flag " << flag.name << std::endl << std::endl;
- usageAndDie(command);
- }
- }
-}
-
-const std::vector<std::string>& getArgs() {
- return sArgs;
-}
-
-} // namespace flag
-} // namespace aapt
diff --git a/tools/aapt2/Flag.h b/tools/aapt2/Flag.h
deleted file mode 100644
index e863742..0000000
--- a/tools/aapt2/Flag.h
+++ /dev/null
@@ -1,34 +0,0 @@
-#ifndef AAPT_FLAG_H
-#define AAPT_FLAG_H
-
-#include "StringPiece.h"
-
-#include <functional>
-#include <string>
-#include <vector>
-
-namespace aapt {
-namespace flag {
-
-void requiredFlag(const StringPiece& name, const StringPiece& description,
- std::function<void(const StringPiece&)> action);
-
-void requiredFlag(const StringPiece& name, const StringPiece& description,
- std::function<bool(const StringPiece&, std::string*)> action);
-
-void optionalFlag(const StringPiece& name, const StringPiece& description,
- std::function<void(const StringPiece&)> action);
-
-void optionalSwitch(const StringPiece& name, const StringPiece& description, bool resultWhenSet,
- bool* result);
-
-void usageAndDie(const StringPiece& command);
-
-void parse(int argc, char** argv, const StringPiece& command);
-
-const std::vector<std::string>& getArgs();
-
-} // namespace flag
-} // namespace aapt
-
-#endif // AAPT_FLAG_H
diff --git a/tools/aapt2/Flags.cpp b/tools/aapt2/Flags.cpp
new file mode 100644
index 0000000..9435396
--- /dev/null
+++ b/tools/aapt2/Flags.cpp
@@ -0,0 +1,166 @@
+/*
+ * 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.
+ */
+
+#include "Flags.h"
+#include "util/StringPiece.h"
+#include "util/Util.h"
+
+#include <iomanip>
+#include <iostream>
+#include <string>
+#include <vector>
+
+namespace aapt {
+
+Flags& Flags::requiredFlag(const StringPiece& name, const StringPiece& description,
+ std::string* value) {
+ auto func = [value](const StringPiece& arg) -> bool {
+ *value = arg.toString();
+ return true;
+ };
+
+ mFlags.push_back(Flag{ name.toString(), description.toString(), func, true, 1, false});
+ return *this;
+}
+
+Flags& Flags::requiredFlagList(const StringPiece& name, const StringPiece& description,
+ std::vector<std::string>* value) {
+ auto func = [value](const StringPiece& arg) -> bool {
+ value->push_back(arg.toString());
+ return true;
+ };
+
+ mFlags.push_back(Flag{ name.toString(), description.toString(), func, true, 1, false });
+ return *this;
+}
+
+Flags& Flags::optionalFlag(const StringPiece& name, const StringPiece& description,
+ Maybe<std::string>* value) {
+ auto func = [value](const StringPiece& arg) -> bool {
+ *value = arg.toString();
+ return true;
+ };
+
+ mFlags.push_back(Flag{ name.toString(), description.toString(), func, false, 1, false });
+ return *this;
+}
+
+Flags& Flags::optionalFlagList(const StringPiece& name, const StringPiece& description,
+ std::vector<std::string>* value) {
+ auto func = [value](const StringPiece& arg) -> bool {
+ value->push_back(arg.toString());
+ return true;
+ };
+
+ mFlags.push_back(Flag{ name.toString(), description.toString(), func, false, 1, false });
+ return *this;
+}
+
+Flags& Flags::optionalSwitch(const StringPiece& name, const StringPiece& description,
+ bool* value) {
+ auto func = [value](const StringPiece& arg) -> bool {
+ *value = true;
+ return true;
+ };
+
+ mFlags.push_back(Flag{ name.toString(), description.toString(), func, false, 0, false });
+ return *this;
+}
+
+void Flags::usage(const StringPiece& command, std::ostream* out) {
+ *out << command << " [options]";
+ for (const Flag& flag : mFlags) {
+ if (flag.required) {
+ *out << " " << flag.name << " arg";
+ }
+ }
+
+ *out << " files...\n\nOptions:\n";
+
+ for (const Flag& flag : mFlags) {
+ std::string argLine = flag.name;
+ if (flag.numArgs > 0) {
+ argLine += " arg";
+ }
+
+ // Split the description by newlines and write out the argument (which is empty after
+ // the first line) followed by the description line. This will make sure that multiline
+ // descriptions are still right justified and aligned.
+ for (StringPiece line : util::tokenize<char>(flag.description, '\n')) {
+ *out << " " << std::setw(30) << std::left << argLine << line << "\n";
+ argLine = " ";
+ }
+ }
+ *out << " " << std::setw(30) << std::left << "-h" << "Displays this help menu\n";
+ out->flush();
+}
+
+bool Flags::parse(const StringPiece& command, const std::vector<StringPiece>& args,
+ std::ostream* outError) {
+ for (size_t i = 0; i < args.size(); i++) {
+ StringPiece arg = args[i];
+ if (*(arg.data()) != '-') {
+ mArgs.push_back(arg.toString());
+ continue;
+ }
+
+ if (arg == "-h" || arg == "--help") {
+ usage(command, outError);
+ return false;
+ }
+
+ bool match = false;
+ for (Flag& flag : mFlags) {
+ if (arg == flag.name) {
+ if (flag.numArgs > 0) {
+ i++;
+ if (i >= args.size()) {
+ *outError << flag.name << " missing argument.\n\n";
+ usage(command, outError);
+ return false;
+ }
+ flag.action(args[i]);
+ } else {
+ flag.action({});
+ }
+ flag.parsed = true;
+ match = true;
+ break;
+ }
+ }
+
+ if (!match) {
+ *outError << "unknown option '" << arg << "'.\n\n";
+ usage(command, outError);
+ return false;
+ }
+ }
+
+ for (const Flag& flag : mFlags) {
+ if (flag.required && !flag.parsed) {
+ *outError << "missing required flag " << flag.name << "\n\n";
+ usage(command, outError);
+ return false;
+ }
+ }
+ return true;
+}
+
+const std::vector<std::string>& Flags::getArgs() {
+ return mArgs;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/Flags.h b/tools/aapt2/Flags.h
new file mode 100644
index 0000000..ce7a485
--- /dev/null
+++ b/tools/aapt2/Flags.h
@@ -0,0 +1,67 @@
+/*
+ * 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_FLAGS_H
+#define AAPT_FLAGS_H
+
+#include "util/Maybe.h"
+#include "util/StringPiece.h"
+
+#include <functional>
+#include <ostream>
+#include <string>
+#include <vector>
+
+namespace aapt {
+
+class Flags {
+public:
+ Flags& requiredFlag(const StringPiece& name, const StringPiece& description,
+ std::string* value);
+ Flags& requiredFlagList(const StringPiece& name, const StringPiece& description,
+ std::vector<std::string>* value);
+ Flags& optionalFlag(const StringPiece& name, const StringPiece& description,
+ Maybe<std::string>* value);
+ Flags& optionalFlagList(const StringPiece& name, const StringPiece& description,
+ std::vector<std::string>* value);
+ Flags& optionalSwitch(const StringPiece& name, const StringPiece& description,
+ bool* value);
+
+ void usage(const StringPiece& command, std::ostream* out);
+
+ bool parse(const StringPiece& command, const std::vector<StringPiece>& args,
+ std::ostream* outError);
+
+ const std::vector<std::string>& getArgs();
+
+private:
+ struct Flag {
+ std::string name;
+ std::string description;
+ std::function<bool(const StringPiece& value)> action;
+ bool required;
+ size_t numArgs;
+
+ bool parsed;
+ };
+
+ std::vector<Flag> mFlags;
+ std::vector<std::string> mArgs;
+};
+
+} // namespace aapt
+
+#endif // AAPT_FLAGS_H
diff --git a/tools/aapt2/JavaClassGenerator.cpp b/tools/aapt2/JavaClassGenerator.cpp
deleted file mode 100644
index e2ffe79..0000000
--- a/tools/aapt2/JavaClassGenerator.cpp
+++ /dev/null
@@ -1,208 +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.
- */
-
-#include "JavaClassGenerator.h"
-#include "NameMangler.h"
-#include "Resource.h"
-#include "ResourceTable.h"
-#include "ResourceValues.h"
-#include "StringPiece.h"
-
-#include <algorithm>
-#include <ostream>
-#include <set>
-#include <sstream>
-#include <tuple>
-
-namespace aapt {
-
-// The number of attributes to emit per line in a Styleable array.
-constexpr size_t kAttribsPerLine = 4;
-
-JavaClassGenerator::JavaClassGenerator(const std::shared_ptr<const ResourceTable>& table,
- Options options) :
- mTable(table), mOptions(options) {
-}
-
-static void generateHeader(std::ostream& out, const StringPiece16& package) {
- 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";
- out << "package " << package << ";"
- << std::endl
- << std::endl;
-}
-
-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",
- u"default", u"do", u"double", u"else", u"enum", u"extends",
- u"final", u"finally", u"float", u"for", u"goto", u"if",
- u"implements", u"import", u"instanceof", u"int", u"interface",
- u"long", u"native", u"new", u"package", u"private", u"protected",
- u"public", u"return", u"short", u"static", u"strictfp", u"super",
- u"switch", u"synchronized", u"this", u"throw", u"throws",
- u"transient", u"try", u"void", u"volatile", u"while", u"true",
- u"false", u"null"
-};
-
-static bool isValidSymbol(const StringPiece16& symbol) {
- return sJavaIdentifiers.find(symbol) == sJavaIdentifiers.end();
-}
-
-/*
- * Java symbols can not contain . or -, but those are valid in a resource name.
- * Replace those with '_'.
- */
-static std::u16string transform(const StringPiece16& symbol) {
- std::u16string output = symbol.toString();
- for (char16_t& c : output) {
- if (c == u'.' || c == u'-') {
- c = u'_';
- }
- }
- return output;
-}
-
-struct GenArgs : ValueVisitorArgs {
- GenArgs(std::ostream* o, const std::u16string* p, std::u16string* e) :
- out(o), package(p), entryName(e) {
- }
-
- std::ostream* out;
- const std::u16string* package;
- std::u16string* entryName;
-};
-
-void JavaClassGenerator::visit(const Styleable& styleable, ValueVisitorArgs& a) {
- const StringPiece finalModifier = mOptions.useFinal ? " final" : "";
- std::ostream* out = static_cast<GenArgs&>(a).out;
- const std::u16string* package = static_cast<GenArgs&>(a).package;
- std::u16string* entryName = static_cast<GenArgs&>(a).entryName;
-
- // This must be sorted by resource ID.
- std::vector<std::pair<ResourceId, ResourceNameRef>> sortedAttributes;
- sortedAttributes.reserve(styleable.entries.size());
- for (const auto& attr : styleable.entries) {
- // If we are not encoding final attributes, the styleable entry may have no ID
- // if we are building a static library.
- assert((!mOptions.useFinal || attr.id.isValid()) && "no ID set for Styleable entry");
- assert(attr.name.isValid() && "no name set for Styleable entry");
- sortedAttributes.emplace_back(attr.id, attr.name);
- }
- std::sort(sortedAttributes.begin(), sortedAttributes.end());
-
- // First we emit the array containing the IDs of each attribute.
- *out << " "
- << "public static final int[] " << transform(*entryName) << " = {";
-
- const size_t attrCount = sortedAttributes.size();
- for (size_t i = 0; i < attrCount; i++) {
- if (i % kAttribsPerLine == 0) {
- *out << std::endl << " ";
- }
-
- *out << sortedAttributes[i].first;
- if (i != attrCount - 1) {
- *out << ", ";
- }
- }
- *out << std::endl << " };" << std::endl;
-
- // Now we emit the indices into the array.
- for (size_t i = 0; i < attrCount; i++) {
- *out << " "
- << "public static" << finalModifier
- << " int " << transform(*entryName);
-
- // We may reference IDs from other packages, so prefix the entry name with
- // the package.
- const ResourceNameRef& itemName = sortedAttributes[i].second;
- if (itemName.package != *package) {
- *out << "_" << transform(itemName.package);
- }
- *out << "_" << transform(itemName.entry) << " = " << i << ";" << std::endl;
- }
-}
-
-bool JavaClassGenerator::generateType(const std::u16string& package, size_t packageId,
- const ResourceTableType& type, std::ostream& out) {
- const StringPiece finalModifier = mOptions.useFinal ? " final" : "";
-
- std::u16string unmangledPackage;
- std::u16string unmangledName;
- for (const auto& entry : type.entries) {
- ResourceId id = { packageId, type.typeId, entry->entryId };
- assert(id.isValid());
-
- unmangledName = entry->name;
- if (NameMangler::unmangle(&unmangledName, &unmangledPackage)) {
- // The entry name was mangled, and we successfully unmangled it.
- // Check that we want to emit this symbol.
- if (package != unmangledPackage) {
- // Skip the entry if it doesn't belong to the package we're writing.
- continue;
- }
- } else {
- if (package != mTable->getPackage()) {
- // We are processing a mangled package name,
- // but this is a non-mangled resource.
- continue;
- }
- }
-
- if (!isValidSymbol(unmangledName)) {
- ResourceNameRef resourceName = { package, type.type, unmangledName };
- std::stringstream err;
- err << "invalid symbol name '" << resourceName << "'";
- mError = err.str();
- return false;
- }
-
- if (type.type == ResourceType::kStyleable) {
- assert(!entry->values.empty());
- entry->values.front().value->accept(*this, GenArgs{ &out, &package, &unmangledName });
- } else {
- out << " " << "public static" << finalModifier
- << " int " << transform(unmangledName) << " = " << id << ";" << std::endl;
- }
- }
- return true;
-}
-
-bool JavaClassGenerator::generate(const std::u16string& package, std::ostream& out) {
- const size_t packageId = mTable->getPackageId();
-
- generateHeader(out, package);
-
- out << "public final class R {" << std::endl;
-
- for (const auto& type : *mTable) {
- out << " public static final class " << type->type << " {" << std::endl;
- if (!generateType(package, packageId, *type, out)) {
- return false;
- }
- out << " }" << std::endl;
- }
-
- out << "}" << std::endl;
- return true;
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/JavaClassGenerator.h b/tools/aapt2/JavaClassGenerator.h
deleted file mode 100644
index f8b9ee3..0000000
--- a/tools/aapt2/JavaClassGenerator.h
+++ /dev/null
@@ -1,77 +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_CLASS_GENERATOR_H
-#define AAPT_JAVA_CLASS_GENERATOR_H
-
-#include "ResourceTable.h"
-#include "ResourceValues.h"
-
-#include <ostream>
-#include <string>
-
-namespace aapt {
-
-/*
- * Generates the R.java file for a resource table.
- */
-class JavaClassGenerator : ConstValueVisitor {
-public:
- /*
- * A set of options for this JavaClassGenerator.
- */
- struct Options {
- /*
- * Specifies whether to use the 'final' modifier
- * on resource entries. Default is true.
- */
- bool useFinal = true;
- };
-
- JavaClassGenerator(const std::shared_ptr<const ResourceTable>& table, Options options);
-
- /*
- * Writes the R.java file to `out`. Only symbols belonging to `package` are written.
- * All symbols technically belong to a single package, but linked libraries will
- * have their names mangled, denoting that they came from a different package.
- * We need to generate these symbols in a separate file.
- * Returns true on success.
- */
- bool generate(const std::u16string& package, std::ostream& out);
-
- /*
- * ConstValueVisitor implementation.
- */
- void visit(const Styleable& styleable, ValueVisitorArgs& args);
-
- const std::string& getError() const;
-
-private:
- bool generateType(const std::u16string& package, size_t packageId,
- const ResourceTableType& type, std::ostream& out);
-
- std::shared_ptr<const ResourceTable> mTable;
- Options mOptions;
- std::string mError;
-};
-
-inline const std::string& JavaClassGenerator::getError() const {
- return mError;
-}
-
-} // namespace aapt
-
-#endif // AAPT_JAVA_CLASS_GENERATOR_H
diff --git a/tools/aapt2/JavaClassGenerator_test.cpp b/tools/aapt2/JavaClassGenerator_test.cpp
deleted file mode 100644
index b385ff4..0000000
--- a/tools/aapt2/JavaClassGenerator_test.cpp
+++ /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.
- */
-
-#include "JavaClassGenerator.h"
-#include "Linker.h"
-#include "MockResolver.h"
-#include "ResourceTable.h"
-#include "ResourceTableResolver.h"
-#include "ResourceValues.h"
-#include "Util.h"
-
-#include <gtest/gtest.h>
-#include <sstream>
-#include <string>
-
-namespace aapt {
-
-struct JavaClassGeneratorTest : public ::testing::Test {
- virtual void SetUp() override {
- mTable = std::make_shared<ResourceTable>();
- mTable->setPackage(u"android");
- mTable->setPackageId(0x01);
- }
-
- bool addResource(const ResourceNameRef& name, ResourceId id) {
- return mTable->addResource(name, id, {}, SourceLine{ "test.xml", 21 },
- util::make_unique<Id>());
- }
-
- std::shared_ptr<ResourceTable> mTable;
-};
-
-TEST_F(JavaClassGeneratorTest, FailWhenEntryIsJavaKeyword) {
- ASSERT_TRUE(addResource(ResourceName{ {}, ResourceType::kId, u"class" },
- ResourceId{ 0x01, 0x02, 0x0000 }));
-
- JavaClassGenerator generator(mTable, {});
-
- std::stringstream out;
- EXPECT_FALSE(generator.generate(mTable->getPackage(), out));
-}
-
-TEST_F(JavaClassGeneratorTest, TransformInvalidJavaIdentifierCharacter) {
- ASSERT_TRUE(addResource(ResourceName{ {}, ResourceType::kId, u"hey-man" },
- ResourceId{ 0x01, 0x02, 0x0000 }));
-
- ASSERT_TRUE(addResource(ResourceName{ {}, ResourceType::kAttr, u"cool.attr" },
- ResourceId{ 0x01, 0x01, 0x0000 }));
-
- std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
- Reference ref(ResourceName{ u"android", ResourceType::kAttr, u"cool.attr"});
- ref.id = ResourceId{ 0x01, 0x01, 0x0000 };
- styleable->entries.emplace_back(ref);
-
- ASSERT_TRUE(mTable->addResource(ResourceName{ {}, ResourceType::kStyleable, u"hey.dude" },
- ResourceId{ 0x01, 0x03, 0x0000 }, {},
- SourceLine{ "test.xml", 21 }, std::move(styleable)));
-
- JavaClassGenerator generator(mTable, {});
-
- std::stringstream out;
- EXPECT_TRUE(generator.generate(mTable->getPackage(), out));
- std::string output = out.str();
-
- EXPECT_NE(std::string::npos,
- output.find("public static final int hey_man = 0x01020000;"));
-
- EXPECT_NE(std::string::npos,
- output.find("public static final int[] hey_dude = {"));
-
- EXPECT_NE(std::string::npos,
- output.find("public static final int hey_dude_cool_attr = 0;"));
-}
-
-
-TEST_F(JavaClassGeneratorTest, EmitPackageMangledSymbols) {
- ASSERT_TRUE(addResource(ResourceName{ {}, ResourceType::kId, u"foo" },
- ResourceId{ 0x01, 0x02, 0x0000 }));
- ResourceTable table;
- table.setPackage(u"com.lib");
- ASSERT_TRUE(table.addResource(ResourceName{ {}, ResourceType::kId, u"test" }, {},
- SourceLine{ "lib.xml", 33 }, util::make_unique<Id>()));
- ASSERT_TRUE(mTable->merge(std::move(table)));
-
- Linker linker(mTable,
- std::make_shared<MockResolver>(mTable, std::map<ResourceName, ResourceId>()),
- {});
- ASSERT_TRUE(linker.linkAndValidate());
-
- JavaClassGenerator generator(mTable, {});
-
- std::stringstream out;
- EXPECT_TRUE(generator.generate(mTable->getPackage(), out));
- std::string output = out.str();
- EXPECT_NE(std::string::npos, output.find("int foo ="));
- EXPECT_EQ(std::string::npos, output.find("int test ="));
-
- out.str("");
- EXPECT_TRUE(generator.generate(u"com.lib", out));
- output = out.str();
- EXPECT_NE(std::string::npos, output.find("int test ="));
- EXPECT_EQ(std::string::npos, output.find("int foo ="));
-}
-
-TEST_F(JavaClassGeneratorTest, EmitOtherPackagesAttributesInStyleable) {
- std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
- styleable->entries.emplace_back(ResourceNameRef{ mTable->getPackage(),
- ResourceType::kAttr,
- u"bar" });
- styleable->entries.emplace_back(ResourceNameRef{ u"com.lib", ResourceType::kAttr, u"bar" });
- ASSERT_TRUE(mTable->addResource(ResourceName{ {}, ResourceType::kStyleable, u"Foo" }, {}, {},
- std::move(styleable)));
-
- std::shared_ptr<IResolver> resolver = std::make_shared<MockResolver>(mTable,
- std::map<ResourceName, ResourceId>({
- { ResourceName{ u"android", ResourceType::kAttr, u"bar" },
- ResourceId{ 0x01, 0x01, 0x0000 } },
- { ResourceName{ u"com.lib", ResourceType::kAttr, u"bar" },
- ResourceId{ 0x02, 0x01, 0x0000 } }}));
-
- Linker linker(mTable, resolver, {});
- ASSERT_TRUE(linker.linkAndValidate());
-
- JavaClassGenerator generator(mTable, {});
-
- std::stringstream out;
- EXPECT_TRUE(generator.generate(mTable->getPackage(), out));
- std::string output = out.str();
- EXPECT_NE(std::string::npos, output.find("int Foo_bar ="));
- EXPECT_NE(std::string::npos, output.find("int Foo_com_lib_bar ="));
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/Linker.cpp b/tools/aapt2/Linker.cpp
deleted file mode 100644
index c37cc93..0000000
--- a/tools/aapt2/Linker.cpp
+++ /dev/null
@@ -1,290 +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.
- */
-
-#include "Linker.h"
-#include "Logger.h"
-#include "NameMangler.h"
-#include "Resolver.h"
-#include "ResourceParser.h"
-#include "ResourceTable.h"
-#include "ResourceValues.h"
-#include "StringPiece.h"
-#include "Util.h"
-
-#include <androidfw/AssetManager.h>
-#include <array>
-#include <bitset>
-#include <iostream>
-#include <map>
-#include <ostream>
-#include <set>
-#include <sstream>
-#include <tuple>
-#include <vector>
-
-namespace aapt {
-
-Linker::Args::Args(const ResourceNameRef& r, const SourceLine& s) : referrer(r), source(s) {
-}
-
-Linker::Linker(const std::shared_ptr<ResourceTable>& table,
- const std::shared_ptr<IResolver>& resolver, const Options& options) :
- mResolver(resolver), mTable(table), mOptions(options), mError(false) {
-}
-
-bool Linker::linkAndValidate() {
- std::bitset<256> usedTypeIds;
- std::array<std::set<uint16_t>, 256> usedIds;
- usedTypeIds.set(0);
-
- // Collect which resource IDs are already taken.
- for (auto& type : *mTable) {
- if (type->typeId != ResourceTableType::kUnsetTypeId) {
- // The ID for this type has already been set. We
- // mark this ID as taken so we don't re-assign it
- // later.
- usedTypeIds.set(type->typeId);
- }
-
- for (auto& entry : type->entries) {
- if (type->typeId != ResourceTableType::kUnsetTypeId &&
- entry->entryId != ResourceEntry::kUnsetEntryId) {
- // The ID for this entry has already been set. We
- // mark this ID as taken so we don't re-assign it
- // later.
- usedIds[type->typeId].insert(entry->entryId);
- }
- }
- }
-
- // Assign resource IDs that are available.
- size_t nextTypeIndex = 0;
- for (auto& type : *mTable) {
- if (type->typeId == ResourceTableType::kUnsetTypeId) {
- while (nextTypeIndex < usedTypeIds.size() && usedTypeIds[nextTypeIndex]) {
- nextTypeIndex++;
- }
- type->typeId = nextTypeIndex++;
- }
-
- const auto endEntryIter = std::end(usedIds[type->typeId]);
- auto nextEntryIter = std::begin(usedIds[type->typeId]);
- size_t nextIndex = 0;
- for (auto& entry : type->entries) {
- if (entry->entryId == ResourceTableType::kUnsetTypeId) {
- while (nextEntryIter != endEntryIter &&
- nextIndex == *nextEntryIter) {
- nextIndex++;
- ++nextEntryIter;
- }
- entry->entryId = nextIndex++;
- }
- }
- }
-
- // Now do reference linking.
- for (auto& type : *mTable) {
- for (auto& entry : type->entries) {
- if (entry->publicStatus.isPublic && entry->values.empty()) {
- // A public resource has no values. It will not be encoded
- // properly without a symbol table. This is a unresolved symbol.
- addUnresolvedSymbol(ResourceNameRef{
- mTable->getPackage(), type->type, entry->name },
- entry->publicStatus.source);
- continue;
- }
-
- for (auto& valueConfig : entry->values) {
- // Dispatch to the right method of this linker
- // based on the value's type.
- valueConfig.value->accept(*this, Args{
- ResourceNameRef{ mTable->getPackage(), type->type, entry->name },
- valueConfig.source
- });
- }
- }
- }
- return !mError;
-}
-
-const Linker::ResourceNameToSourceMap& Linker::getUnresolvedReferences() const {
- return mUnresolvedSymbols;
-}
-
-void Linker::doResolveReference(Reference& reference, const SourceLine& source) {
- Maybe<ResourceId> result = mResolver->findId(reference.name);
- if (!result) {
- addUnresolvedSymbol(reference.name, source);
- return;
- }
- assert(result.value().isValid());
-
- if (mOptions.linkResourceIds) {
- reference.id = result.value();
- } else {
- reference.id = 0;
- }
-}
-
-const Attribute* Linker::doResolveAttribute(Reference& attribute, const SourceLine& source) {
- Maybe<IResolver::Entry> result = mResolver->findAttribute(attribute.name);
- if (!result || !result.value().attr) {
- addUnresolvedSymbol(attribute.name, source);
- return nullptr;
- }
-
- const IResolver::Entry& entry = result.value();
- assert(entry.id.isValid());
-
- if (mOptions.linkResourceIds) {
- attribute.id = entry.id;
- } else {
- attribute.id = 0;
- }
- return entry.attr;
-}
-
-void Linker::visit(Reference& reference, ValueVisitorArgs& a) {
- Args& args = static_cast<Args&>(a);
-
- if (reference.name.entry.empty()) {
- // We can't have a completely bad reference.
- if (!reference.id.isValid()) {
- Logger::error() << "srsly? " << args.referrer << std::endl;
- assert(reference.id.isValid());
- }
-
- // This reference has no name but has an ID.
- // It is a really bad error to have no name and have the same
- // package ID.
- assert(reference.id.packageId() != mTable->getPackageId());
-
- // The reference goes outside this package, let it stay as a
- // resource ID because it will not change.
- return;
- }
-
- doResolveReference(reference, args.source);
-
- // TODO(adamlesinski): Verify the referencedType is another reference
- // or a compatible primitive.
-}
-
-void Linker::processAttributeValue(const ResourceNameRef& name, const SourceLine& source,
- const Attribute& attr, std::unique_ptr<Item>& value) {
- std::unique_ptr<Item> convertedValue;
- visitFunc<RawString>(*value, [&](RawString& str) {
- // This is a raw string, so check if it can be converted to anything.
- // We can NOT swap value with the converted value in here, since
- // we called through the original value.
-
- auto onCreateReference = [&](const ResourceName& name) {
- // We should never get here. All references would have been
- // parsed in the parser phase.
- assert(false);
- };
-
- convertedValue = ResourceParser::parseItemForAttribute(*str.value, attr,
- onCreateReference);
- if (!convertedValue && attr.typeMask & android::ResTable_map::TYPE_STRING) {
- // Last effort is to parse as a string.
- util::StringBuilder builder;
- builder.append(*str.value);
- if (builder) {
- convertedValue = util::make_unique<String>(
- mTable->getValueStringPool().makeRef(builder.str()));
- }
- }
- });
-
- if (convertedValue) {
- value = std::move(convertedValue);
- }
-
- // Process this new or old value (it can be a reference!).
- value->accept(*this, Args{ name, source });
-
- // Flatten the value to see what resource type it is.
- android::Res_value resValue;
- value->flatten(resValue);
-
- // Always allow references.
- const uint32_t typeMask = attr.typeMask | android::ResTable_map::TYPE_REFERENCE;
- if (!(typeMask & ResourceParser::androidTypeToAttributeTypeMask(resValue.dataType))) {
- Logger::error(source)
- << *value
- << " is not compatible with attribute "
- << attr
- << "."
- << std::endl;
- mError = true;
- }
-}
-
-void Linker::visit(Style& style, ValueVisitorArgs& a) {
- Args& args = static_cast<Args&>(a);
-
- if (style.parent.name.isValid() || style.parent.id.isValid()) {
- visit(style.parent, a);
- }
-
- for (Style::Entry& styleEntry : style.entries) {
- const Attribute* attr = doResolveAttribute(styleEntry.key, args.source);
- if (attr) {
- processAttributeValue(args.referrer, args.source, *attr, styleEntry.value);
- }
- }
-}
-
-void Linker::visit(Attribute& attr, ValueVisitorArgs& a) {
- static constexpr uint32_t kMask = android::ResTable_map::TYPE_ENUM |
- android::ResTable_map::TYPE_FLAGS;
- if (attr.typeMask & kMask) {
- for (auto& symbol : attr.symbols) {
- visit(symbol.symbol, a);
- }
- }
-}
-
-void Linker::visit(Styleable& styleable, ValueVisitorArgs& a) {
- for (auto& attrRef : styleable.entries) {
- visit(attrRef, a);
- }
-}
-
-void Linker::visit(Array& array, ValueVisitorArgs& a) {
- Args& args = static_cast<Args&>(a);
-
- for (auto& item : array.items) {
- item->accept(*this, Args{ args.referrer, args.source });
- }
-}
-
-void Linker::visit(Plural& plural, ValueVisitorArgs& a) {
- Args& args = static_cast<Args&>(a);
-
- for (auto& item : plural.values) {
- if (item) {
- item->accept(*this, Args{ args.referrer, args.source });
- }
- }
-}
-
-void Linker::addUnresolvedSymbol(const ResourceNameRef& name, const SourceLine& source) {
- mUnresolvedSymbols[name.toResourceName()].push_back(source);
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/Linker.h b/tools/aapt2/Linker.h
deleted file mode 100644
index 6f03515..0000000
--- a/tools/aapt2/Linker.h
+++ /dev/null
@@ -1,124 +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_LINKER_H
-#define AAPT_LINKER_H
-
-#include "Resolver.h"
-#include "ResourceTable.h"
-#include "ResourceValues.h"
-#include "Source.h"
-#include "StringPiece.h"
-
-#include <androidfw/AssetManager.h>
-#include <map>
-#include <memory>
-#include <ostream>
-#include <set>
-#include <vector>
-
-namespace aapt {
-
-/**
- * The Linker has two jobs. It follows resource references
- * and verifies that their targert exists and that their
- * types are compatible. The Linker will also assign resource
- * IDs and fill in all the dependent references with the newly
- * assigned resource IDs.
- *
- * To do this, the Linker builds a graph of references. This
- * can be useful to do other analysis, like building a
- * dependency graph of source files. The hope is to be able to
- * add functionality that operates on the graph without
- * overcomplicating the Linker.
- *
- * TODO(adamlesinski): Build the graph first then run the separate
- * steps over the graph.
- */
-class Linker : ValueVisitor {
-public:
- struct Options {
- /**
- * Assign resource Ids to references when linking.
- * When building a static library, set this to false.
- */
- bool linkResourceIds = true;
- };
-
- /**
- * Create a Linker for the given resource table with the sources available in
- * IResolver. IResolver should contain the ResourceTable as a source too.
- */
- Linker(const std::shared_ptr<ResourceTable>& table,
- const std::shared_ptr<IResolver>& resolver, const Options& options);
-
- Linker(const Linker&) = delete;
-
- virtual ~Linker() = default;
-
- /**
- * Entry point to the linker. Assigns resource IDs, follows references,
- * and validates types. Returns true if all references to defined values
- * are type-compatible. Missing resource references are recorded but do
- * not cause this method to fail.
- */
- bool linkAndValidate();
-
- /**
- * Returns any references to resources that were not defined in any of the
- * sources.
- */
- using ResourceNameToSourceMap = std::map<ResourceName, std::vector<SourceLine>>;
- const ResourceNameToSourceMap& getUnresolvedReferences() const;
-
-protected:
- virtual void doResolveReference(Reference& reference, const SourceLine& source);
- virtual const Attribute* doResolveAttribute(Reference& attribute, const SourceLine& source);
-
- std::shared_ptr<IResolver> mResolver;
-
-private:
- struct Args : public ValueVisitorArgs {
- Args(const ResourceNameRef& r, const SourceLine& s);
-
- const ResourceNameRef& referrer;
- const SourceLine& source;
- };
-
- //
- // Overrides of ValueVisitor
- //
- void visit(Reference& reference, ValueVisitorArgs& args) override;
- void visit(Attribute& attribute, ValueVisitorArgs& args) override;
- void visit(Styleable& styleable, ValueVisitorArgs& args) override;
- void visit(Style& style, ValueVisitorArgs& args) override;
- void visit(Array& array, ValueVisitorArgs& args) override;
- void visit(Plural& plural, ValueVisitorArgs& args) override;
-
- void processAttributeValue(const ResourceNameRef& name, const SourceLine& source,
- const Attribute& attr, std::unique_ptr<Item>& value);
-
- void addUnresolvedSymbol(const ResourceNameRef& name, const SourceLine& source);
-
- std::shared_ptr<ResourceTable> mTable;
- std::map<ResourceName, std::vector<SourceLine>> mUnresolvedSymbols;
- Options mOptions;
- bool mError;
-};
-
-} // namespace aapt
-
-#endif // AAPT_LINKER_H
diff --git a/tools/aapt2/Linker_test.cpp b/tools/aapt2/Linker_test.cpp
deleted file mode 100644
index d897f98..0000000
--- a/tools/aapt2/Linker_test.cpp
+++ /dev/null
@@ -1,153 +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.
- */
-
-#include "Linker.h"
-#include "ResourceTable.h"
-#include "ResourceTableResolver.h"
-#include "ResourceValues.h"
-#include "Util.h"
-
-#include <androidfw/AssetManager.h>
-#include <gtest/gtest.h>
-#include <string>
-
-namespace aapt {
-
-struct LinkerTest : public ::testing::Test {
- virtual void SetUp() override {
- mTable = std::make_shared<ResourceTable>();
- mTable->setPackage(u"android");
- mTable->setPackageId(0x01);
- mLinker = std::make_shared<Linker>(mTable, std::make_shared<ResourceTableResolver>(
- mTable, std::vector<std::shared_ptr<const android::AssetManager>>()),
- Linker::Options{});
-
- // Create a few attributes for use in the tests.
-
- addResource(ResourceName{ {}, ResourceType::kAttr, u"integer" },
- util::make_unique<Attribute>(false, android::ResTable_map::TYPE_INTEGER));
-
- addResource(ResourceName{ {}, ResourceType::kAttr, u"string" },
- util::make_unique<Attribute>(false, android::ResTable_map::TYPE_STRING));
-
- addResource(ResourceName{ {}, ResourceType::kId, u"apple" }, util::make_unique<Id>());
-
- addResource(ResourceName{ {}, ResourceType::kId, u"banana" }, util::make_unique<Id>());
-
- std::unique_ptr<Attribute> flagAttr = util::make_unique<Attribute>(
- false, android::ResTable_map::TYPE_FLAGS);
- flagAttr->symbols.push_back(Attribute::Symbol{
- ResourceNameRef{ u"android", ResourceType::kId, u"apple" }, 1 });
- flagAttr->symbols.push_back(Attribute::Symbol{
- ResourceNameRef{ u"android", ResourceType::kId, u"banana" }, 2 });
- addResource(ResourceName{ {}, ResourceType::kAttr, u"flags" }, std::move(flagAttr));
- }
-
- /*
- * Convenience method for adding resources with the default configuration and some
- * bogus source line.
- */
- bool addResource(const ResourceNameRef& name, std::unique_ptr<Value> value) {
- return mTable->addResource(name, {}, SourceLine{ "test.xml", 21 }, std::move(value));
- }
-
- std::shared_ptr<ResourceTable> mTable;
- std::shared_ptr<Linker> mLinker;
-};
-
-TEST_F(LinkerTest, DoNotInterpretEscapedStringAsReference) {
- ASSERT_TRUE(addResource(ResourceName{ u"android", ResourceType::kString, u"foo" },
- util::make_unique<String>(mTable->getValueStringPool().makeRef(u"?123"))));
-
- ASSERT_TRUE(mLinker->linkAndValidate());
- EXPECT_TRUE(mLinker->getUnresolvedReferences().empty());
-}
-
-TEST_F(LinkerTest, EscapeAndConvertRawString) {
- std::unique_ptr<Style> style = util::make_unique<Style>();
- style->entries.push_back(Style::Entry{
- ResourceNameRef{ u"android", ResourceType::kAttr, u"integer" },
- util::make_unique<RawString>(mTable->getValueStringPool().makeRef(u" 123"))
- });
- const Style* result = style.get();
- ASSERT_TRUE(addResource(ResourceName{ u"android", ResourceType::kStyle, u"foo" },
- std::move(style)));
-
- ASSERT_TRUE(mLinker->linkAndValidate());
- EXPECT_TRUE(mLinker->getUnresolvedReferences().empty());
-
- EXPECT_NE(nullptr, dynamic_cast<BinaryPrimitive*>(result->entries.front().value.get()));
-}
-
-TEST_F(LinkerTest, FailToConvertRawString) {
- std::unique_ptr<Style> style = util::make_unique<Style>();
- style->entries.push_back(Style::Entry{
- ResourceNameRef{ u"android", ResourceType::kAttr, u"integer" },
- util::make_unique<RawString>(mTable->getValueStringPool().makeRef(u"yo what is up?"))
- });
- ASSERT_TRUE(addResource(ResourceName{ u"android", ResourceType::kStyle, u"foo" },
- std::move(style)));
-
- ASSERT_FALSE(mLinker->linkAndValidate());
-}
-
-TEST_F(LinkerTest, ConvertRawStringToString) {
- std::unique_ptr<Style> style = util::make_unique<Style>();
- style->entries.push_back(Style::Entry{
- ResourceNameRef{ u"android", ResourceType::kAttr, u"string" },
- util::make_unique<RawString>(
- mTable->getValueStringPool().makeRef(u" \"this is \\u00fa\"."))
- });
- const Style* result = style.get();
- ASSERT_TRUE(addResource(ResourceName{ u"android", ResourceType::kStyle, u"foo" },
- std::move(style)));
-
- ASSERT_TRUE(mLinker->linkAndValidate());
- EXPECT_TRUE(mLinker->getUnresolvedReferences().empty());
-
- const String* str = dynamic_cast<const String*>(result->entries.front().value.get());
- ASSERT_NE(nullptr, str);
- EXPECT_EQ(*str->value, u"this is \u00fa.");
-}
-
-TEST_F(LinkerTest, ConvertRawStringToFlags) {
- std::unique_ptr<Style> style = util::make_unique<Style>();
- style->entries.push_back(Style::Entry{
- ResourceNameRef{ u"android", ResourceType::kAttr, u"flags" },
- util::make_unique<RawString>(mTable->getValueStringPool().makeRef(u"banana | apple"))
- });
- const Style* result = style.get();
- ASSERT_TRUE(addResource(ResourceName{ u"android", ResourceType::kStyle, u"foo" },
- std::move(style)));
-
- ASSERT_TRUE(mLinker->linkAndValidate());
- EXPECT_TRUE(mLinker->getUnresolvedReferences().empty());
-
- const BinaryPrimitive* bin = dynamic_cast<const BinaryPrimitive*>(
- result->entries.front().value.get());
- ASSERT_NE(nullptr, bin);
- EXPECT_EQ(bin->value.data, 1u | 2u);
-}
-
-TEST_F(LinkerTest, AllowReferenceWithOnlyResourceIdPointingToDifferentPackage) {
- ASSERT_TRUE(addResource(ResourceName{ u"android", ResourceType::kInteger, u"foo" },
- util::make_unique<Reference>(ResourceId{ 0x02, 0x01, 0x01 })));
-
- ASSERT_TRUE(mLinker->linkAndValidate());
- EXPECT_TRUE(mLinker->getUnresolvedReferences().empty());
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/Locale.cpp b/tools/aapt2/Locale.cpp
index eed0ea7..20a2d0c 100644
--- a/tools/aapt2/Locale.cpp
+++ b/tools/aapt2/Locale.cpp
@@ -15,7 +15,7 @@
*/
#include "Locale.h"
-#include "Util.h"
+#include "util/Util.h"
#include <algorithm>
#include <ctype.h>
diff --git a/tools/aapt2/Locale_test.cpp b/tools/aapt2/Locale_test.cpp
index 4e154d6..758e1e3 100644
--- a/tools/aapt2/Locale_test.cpp
+++ b/tools/aapt2/Locale_test.cpp
@@ -15,7 +15,7 @@
*/
#include "Locale.h"
-#include "Util.h"
+#include "util/Util.h"
#include <gtest/gtest.h>
#include <string>
diff --git a/tools/aapt2/Logger.cpp b/tools/aapt2/Logger.cpp
deleted file mode 100644
index 3847185..0000000
--- a/tools/aapt2/Logger.cpp
+++ /dev/null
@@ -1,97 +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.
- */
-#include "Logger.h"
-#include "Source.h"
-
-#include <memory>
-#include <iostream>
-
-namespace aapt {
-
-Log::Log(std::ostream& _out, std::ostream& _err) : out(_out), err(_err) {
-}
-
-std::shared_ptr<Log> Logger::sLog(std::make_shared<Log>(std::cerr, std::cerr));
-
-void Logger::setLog(const std::shared_ptr<Log>& log) {
- sLog = log;
-}
-
-std::ostream& Logger::error() {
- return sLog->err << "error: ";
-}
-
-std::ostream& Logger::error(const Source& source) {
- return sLog->err << source << ": error: ";
-}
-
-std::ostream& Logger::error(const SourceLine& source) {
- return sLog->err << source << ": error: ";
-}
-
-std::ostream& Logger::warn() {
- return sLog->err << "warning: ";
-}
-
-std::ostream& Logger::warn(const Source& source) {
- return sLog->err << source << ": warning: ";
-}
-
-std::ostream& Logger::warn(const SourceLine& source) {
- return sLog->err << source << ": warning: ";
-}
-
-std::ostream& Logger::note() {
- return sLog->out << "note: ";
-}
-
-std::ostream& Logger::note(const Source& source) {
- return sLog->err << source << ": note: ";
-}
-
-std::ostream& Logger::note(const SourceLine& source) {
- return sLog->err << source << ": note: ";
-}
-
-SourceLogger::SourceLogger(const Source& source)
-: mSource(source) {
-}
-
-std::ostream& SourceLogger::error() {
- return Logger::error(mSource);
-}
-
-std::ostream& SourceLogger::error(size_t line) {
- return Logger::error(SourceLine{ mSource.path, line });
-}
-
-std::ostream& SourceLogger::warn() {
- return Logger::warn(mSource);
-}
-
-std::ostream& SourceLogger::warn(size_t line) {
- return Logger::warn(SourceLine{ mSource.path, line });
-}
-
-std::ostream& SourceLogger::note() {
- return Logger::note(mSource);
-}
-
-std::ostream& SourceLogger::note(size_t line) {
- return Logger::note(SourceLine{ mSource.path, line });
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/Logger.h b/tools/aapt2/Logger.h
deleted file mode 100644
index eed58b8..0000000
--- a/tools/aapt2/Logger.h
+++ /dev/null
@@ -1,76 +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_LOGGER_H
-#define AAPT_LOGGER_H
-
-#include "Source.h"
-#include "StringPiece.h"
-
-#include <memory>
-#include <ostream>
-#include <string>
-
-namespace aapt {
-
-struct Log {
- Log(std::ostream& out, std::ostream& err);
- Log(const Log& rhs) = delete;
-
- std::ostream& out;
- std::ostream& err;
-};
-
-class Logger {
-public:
- static void setLog(const std::shared_ptr<Log>& log);
-
- static std::ostream& error();
- static std::ostream& error(const Source& source);
- static std::ostream& error(const SourceLine& sourceLine);
-
- static std::ostream& warn();
- static std::ostream& warn(const Source& source);
- static std::ostream& warn(const SourceLine& sourceLine);
-
- static std::ostream& note();
- static std::ostream& note(const Source& source);
- static std::ostream& note(const SourceLine& sourceLine);
-
-private:
- static std::shared_ptr<Log> sLog;
-};
-
-class SourceLogger {
-public:
- SourceLogger(const Source& source);
-
- std::ostream& error();
- std::ostream& error(size_t line);
-
- std::ostream& warn();
- std::ostream& warn(size_t line);
-
- std::ostream& note();
- std::ostream& note(size_t line);
-
-private:
- Source mSource;
-};
-
-} // namespace aapt
-
-#endif // AAPT_LOGGER_H
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp
index 54a7329..248e7ad 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -14,1262 +14,39 @@
* limitations under the License.
*/
-#include "AppInfo.h"
-#include "BigBuffer.h"
-#include "BinaryResourceParser.h"
-#include "BindingXmlPullParser.h"
-#include "Debug.h"
-#include "Files.h"
-#include "Flag.h"
-#include "JavaClassGenerator.h"
-#include "Linker.h"
-#include "ManifestMerger.h"
-#include "ManifestParser.h"
-#include "ManifestValidator.h"
-#include "NameMangler.h"
-#include "Png.h"
-#include "ProguardRules.h"
-#include "ResourceParser.h"
-#include "ResourceTable.h"
-#include "ResourceTableResolver.h"
-#include "ResourceValues.h"
-#include "SdkConstants.h"
-#include "SourceXmlPullParser.h"
-#include "StringPiece.h"
-#include "TableFlattener.h"
-#include "Util.h"
-#include "XmlFlattener.h"
-#include "ZipFile.h"
+#include "util/StringPiece.h"
-#include <algorithm>
-#include <androidfw/AssetManager.h>
-#include <cstdlib>
-#include <dirent.h>
-#include <errno.h>
-#include <fstream>
#include <iostream>
-#include <sstream>
-#include <sys/stat.h>
-#include <unordered_set>
-#include <utils/Errors.h>
+#include <vector>
-constexpr const char* kAaptVersionStr = "2.0-alpha";
+namespace aapt {
-using namespace aapt;
+extern int compile(const std::vector<StringPiece>& args);
+extern int link(const std::vector<StringPiece>& args);
-/**
- * Used with smart pointers to free malloc'ed memory.
- */
-struct DeleteMalloc {
- void operator()(void* ptr) {
- free(ptr);
- }
-};
-
-struct StaticLibraryData {
- Source source;
- std::unique_ptr<ZipFile> apk;
-};
-
-/**
- * Collect files from 'root', filtering out any files that do not
- * match the FileFilter 'filter'.
- */
-bool walkTree(const Source& root, const FileFilter& filter,
- std::vector<Source>* outEntries) {
- bool error = false;
-
- for (const std::string& dirName : listFiles(root.path)) {
- std::string dir = root.path;
- appendPath(&dir, dirName);
-
- FileType ft = getFileType(dir);
- if (!filter(dirName, ft)) {
- continue;
- }
-
- if (ft != FileType::kDirectory) {
- continue;
- }
-
- for (const std::string& fileName : listFiles(dir)) {
- std::string file(dir);
- appendPath(&file, fileName);
-
- FileType ft = getFileType(file);
- if (!filter(fileName, ft)) {
- continue;
- }
-
- if (ft != FileType::kRegular) {
- Logger::error(Source{ file }) << "not a regular file." << std::endl;
- error = true;
- continue;
- }
- outEntries->push_back(Source{ file });
- }
- }
- return !error;
-}
-
-void versionStylesForCompat(const std::shared_ptr<ResourceTable>& table) {
- for (auto& type : *table) {
- if (type->type != ResourceType::kStyle) {
- continue;
- }
-
- for (auto& entry : type->entries) {
- // Add the versioned styles we want to create
- // here. They are added to the table after
- // iterating over the original set of styles.
- //
- // A stack is used since auto-generated styles
- // from later versions should override
- // auto-generated styles from earlier versions.
- // Iterating over the styles is done in order,
- // so we will always visit sdkVersions from smallest
- // to largest.
- std::stack<ResourceConfigValue> addStack;
-
- for (ResourceConfigValue& configValue : entry->values) {
- visitFunc<Style>(*configValue.value, [&](Style& style) {
- // Collect which entries we've stripped and the smallest
- // SDK level which was stripped.
- size_t minSdkStripped = std::numeric_limits<size_t>::max();
- std::vector<Style::Entry> stripped;
-
- // Iterate over the style's entries and erase/record the
- // attributes whose SDK level exceeds the config's sdkVersion.
- auto iter = style.entries.begin();
- while (iter != style.entries.end()) {
- if (iter->key.name.package == u"android") {
- size_t sdkLevel = findAttributeSdkLevel(iter->key.name);
- if (sdkLevel > 1 && sdkLevel > configValue.config.sdkVersion) {
- // Record that we are about to strip this.
- stripped.emplace_back(std::move(*iter));
- minSdkStripped = std::min(minSdkStripped, sdkLevel);
-
- // Erase this from this style.
- iter = style.entries.erase(iter);
- continue;
- }
- }
- ++iter;
- }
-
- if (!stripped.empty()) {
- // We have stripped attributes, so let's create a new style to hold them.
- ConfigDescription versionConfig(configValue.config);
- versionConfig.sdkVersion = minSdkStripped;
-
- ResourceConfigValue value = {
- versionConfig,
- configValue.source,
- {},
-
- // Create a copy of the original style.
- std::unique_ptr<Value>(configValue.value->clone(
- &table->getValueStringPool()))
- };
-
- Style& newStyle = static_cast<Style&>(*value.value);
-
- // Move the recorded stripped attributes into this new style.
- std::move(stripped.begin(), stripped.end(),
- std::back_inserter(newStyle.entries));
-
- // We will add this style to the table later. If we do it now, we will
- // mess up iteration.
- addStack.push(std::move(value));
- }
- });
- }
-
- auto comparator =
- [](const ResourceConfigValue& lhs, const ConfigDescription& rhs) -> bool {
- return lhs.config < rhs;
- };
-
- while (!addStack.empty()) {
- ResourceConfigValue& value = addStack.top();
- auto iter = std::lower_bound(entry->values.begin(), entry->values.end(),
- value.config, comparator);
- if (iter == entry->values.end() || iter->config != value.config) {
- entry->values.insert(iter, std::move(value));
- }
- addStack.pop();
- }
- }
- }
-}
-
-struct CompileItem {
- ResourceName name;
- ConfigDescription config;
- Source source;
- std::string extension;
-};
-
-struct LinkItem {
- ResourceName name;
- ConfigDescription config;
- Source source;
- std::string originalPath;
- ZipFile* apk;
- std::u16string originalPackage;
-};
-
-template <typename TChar>
-static BasicStringPiece<TChar> getExtension(const BasicStringPiece<TChar>& str) {
- auto iter = std::find(str.begin(), str.end(), static_cast<TChar>('.'));
- if (iter == str.end()) {
- return BasicStringPiece<TChar>();
- }
- size_t offset = (iter - str.begin()) + 1;
- return str.substr(offset, str.size() - offset);
-}
-
-std::string buildFileReference(const ResourceNameRef& name, const ConfigDescription& config,
- const StringPiece& extension) {
- std::stringstream path;
- path << "res/" << name.type;
- if (config != ConfigDescription{}) {
- path << "-" << config;
- }
- path << "/" << util::utf16ToUtf8(name.entry);
- if (!extension.empty()) {
- path << "." << extension;
- }
- return path.str();
-}
-
-std::string buildFileReference(const CompileItem& item) {
- return buildFileReference(item.name, item.config, item.extension);
-}
-
-std::string buildFileReference(const LinkItem& item) {
- return buildFileReference(item.name, item.config, getExtension<char>(item.originalPath));
-}
-
-template <typename T>
-bool addFileReference(const std::shared_ptr<ResourceTable>& table, const T& item) {
- StringPool& pool = table->getValueStringPool();
- StringPool::Ref ref = pool.makeRef(util::utf8ToUtf16(buildFileReference(item)),
- StringPool::Context{ 0, item.config });
- return table->addResource(item.name, item.config, item.source.line(0),
- util::make_unique<FileReference>(ref));
-}
-
-struct AaptOptions {
- enum class Phase {
- Link,
- Compile,
- Dump,
- DumpStyleGraph,
- };
-
- enum class PackageType {
- StandardApp,
- StaticLibrary,
- };
-
- // The phase to process.
- Phase phase;
-
- // The type of package to produce.
- PackageType packageType = PackageType::StandardApp;
-
- // Details about the app.
- AppInfo appInfo;
-
- // The location of the manifest file.
- Source manifest;
-
- // The APK files to link.
- std::vector<Source> input;
-
- // The libraries these files may reference.
- std::vector<Source> libraries;
-
- // Output path. This can be a directory or file
- // depending on the phase.
- Source output;
-
- // Directory in which to write binding xml files.
- Source bindingOutput;
-
- // Directory to in which to generate R.java.
- Maybe<Source> generateJavaClass;
-
- // File in which to produce proguard rules.
- Maybe<Source> generateProguardRules;
-
- // Whether to output verbose details about
- // compilation.
- bool verbose = false;
-
- // Whether or not to auto-version styles or layouts
- // referencing attributes defined in a newer SDK
- // level than the style or layout is defined for.
- bool versionStylesAndLayouts = true;
-
- // The target style that will have it's style hierarchy dumped
- // when the phase is DumpStyleGraph.
- ResourceName dumpStyleTarget;
-};
-
-struct IdCollector : public xml::Visitor {
- IdCollector(const Source& source, const std::shared_ptr<ResourceTable>& table) :
- mSource(source), mTable(table) {
- }
-
- virtual void visit(xml::Text* node) override {}
-
- virtual void visit(xml::Namespace* node) override {
- for (const auto& child : node->children) {
- child->accept(this);
- }
- }
-
- virtual void visit(xml::Element* node) override {
- for (const xml::Attribute& attr : node->attributes) {
- bool create = false;
- bool priv = false;
- ResourceNameRef nameRef;
- if (ResourceParser::tryParseReference(attr.value, &nameRef, &create, &priv)) {
- if (create) {
- mTable->addResource(nameRef, {}, mSource.line(node->lineNumber),
- util::make_unique<Id>());
- }
- }
- }
-
- for (const auto& child : node->children) {
- child->accept(this);
- }
- }
-
-private:
- Source mSource;
- std::shared_ptr<ResourceTable> mTable;
-};
-
-bool compileXml(const AaptOptions& options, const std::shared_ptr<ResourceTable>& table,
- const CompileItem& item, ZipFile* outApk) {
- std::ifstream in(item.source.path, std::ifstream::binary);
- if (!in) {
- Logger::error(item.source) << strerror(errno) << std::endl;
- return false;
- }
-
- SourceLogger logger(item.source);
- std::unique_ptr<xml::Node> root = xml::inflate(&in, &logger);
- if (!root) {
- return false;
- }
-
- // Collect any resource ID's declared here.
- IdCollector idCollector(item.source, table);
- root->accept(&idCollector);
-
- BigBuffer outBuffer(1024);
- if (!xml::flatten(root.get(), options.appInfo.package, &outBuffer)) {
- logger.error() << "failed to encode XML." << std::endl;
- return false;
- }
-
- // Write the resulting compiled XML file to the output APK.
- if (outApk->add(outBuffer, buildFileReference(item).data(), ZipEntry::kCompressStored,
- nullptr) != android::NO_ERROR) {
- Logger::error(options.output) << "failed to write compiled '" << item.source
- << "' to apk." << std::endl;
- return false;
- }
- return true;
-}
-
-/**
- * Determines if a layout should be auto generated based on SDK level. We do not
- * generate a layout if there is already a layout defined whose SDK version is greater than
- * the one we want to generate.
- */
-bool shouldGenerateVersionedResource(const std::shared_ptr<const ResourceTable>& table,
- const ResourceName& name, const ConfigDescription& config,
- int sdkVersionToGenerate) {
- assert(sdkVersionToGenerate > config.sdkVersion);
- const ResourceTableType* type;
- const ResourceEntry* entry;
- std::tie(type, entry) = table->findResource(name);
- assert(type && entry);
-
- auto iter = std::lower_bound(entry->values.begin(), entry->values.end(), config,
- [](const ResourceConfigValue& lhs, const ConfigDescription& config) -> bool {
- return lhs.config < config;
- });
-
- assert(iter != entry->values.end());
- ++iter;
-
- if (iter == entry->values.end()) {
- return true;
- }
-
- ConfigDescription newConfig = config;
- newConfig.sdkVersion = sdkVersionToGenerate;
- return newConfig < iter->config;
-}
-
-bool linkXml(const AaptOptions& options, const std::shared_ptr<ResourceTable>& table,
- const std::shared_ptr<IResolver>& resolver, const LinkItem& item,
- const void* data, size_t dataLen, ZipFile* outApk, std::queue<LinkItem>* outQueue,
- proguard::KeepSet* keepSet) {
- SourceLogger logger(item.source);
- std::unique_ptr<xml::Node> root = xml::inflate(data, dataLen, &logger);
- if (!root) {
- return false;
- }
-
- xml::FlattenOptions xmlOptions;
- if (options.packageType == AaptOptions::PackageType::StaticLibrary) {
- xmlOptions.keepRawValues = true;
- }
-
- if (options.versionStylesAndLayouts) {
- // We strip attributes that do not belong in this version of the resource.
- // Non-version qualified resources have an implicit version 1 requirement.
- xmlOptions.maxSdkAttribute = item.config.sdkVersion ? item.config.sdkVersion : 1;
- }
-
- if (options.generateProguardRules) {
- proguard::collectProguardRules(item.name.type, item.source, root.get(), keepSet);
- }
-
- BigBuffer outBuffer(1024);
- Maybe<size_t> minStrippedSdk = xml::flattenAndLink(item.source, root.get(),
- item.originalPackage, resolver,
- xmlOptions, &outBuffer);
- if (!minStrippedSdk) {
- logger.error() << "failed to encode XML." << std::endl;
- return false;
- }
-
- if (minStrippedSdk.value() > 0) {
- // Something was stripped, so let's generate a new file
- // with the version of the smallest SDK version stripped.
- // We can only generate a versioned layout if there doesn't exist a layout
- // with sdk version greater than the current one but less than the one we
- // want to generate.
- if (shouldGenerateVersionedResource(table, item.name, item.config,
- minStrippedSdk.value())) {
- LinkItem newWork = item;
- newWork.config.sdkVersion = minStrippedSdk.value();
- outQueue->push(newWork);
-
- if (!addFileReference(table, newWork)) {
- Logger::error(options.output) << "failed to add auto-versioned resource '"
- << newWork.name << "'." << std::endl;
- return false;
- }
- }
- }
-
- if (outApk->add(outBuffer, buildFileReference(item).data(), ZipEntry::kCompressDeflated,
- nullptr) != android::NO_ERROR) {
- Logger::error(options.output) << "failed to write linked file '"
- << buildFileReference(item) << "' to apk." << std::endl;
- return false;
- }
- return true;
-}
-
-bool compilePng(const AaptOptions& options, const CompileItem& item, ZipFile* outApk) {
- std::ifstream in(item.source.path, std::ifstream::binary);
- if (!in) {
- Logger::error(item.source) << strerror(errno) << std::endl;
- return false;
- }
-
- BigBuffer outBuffer(4096);
- std::string err;
- Png png;
- if (!png.process(item.source, in, &outBuffer, {}, &err)) {
- Logger::error(item.source) << err << std::endl;
- return false;
- }
-
- if (outApk->add(outBuffer, buildFileReference(item).data(), ZipEntry::kCompressStored,
- nullptr) != android::NO_ERROR) {
- Logger::error(options.output) << "failed to write compiled '" << item.source
- << "' to apk." << std::endl;
- return false;
- }
- return true;
-}
-
-bool copyFile(const AaptOptions& options, const CompileItem& item, ZipFile* outApk) {
- if (outApk->add(item.source.path.data(), buildFileReference(item).data(),
- ZipEntry::kCompressStored, nullptr) != android::NO_ERROR) {
- Logger::error(options.output) << "failed to copy file '" << item.source << "' to apk."
- << std::endl;
- return false;
- }
- return true;
-}
-
-bool compileManifest(const AaptOptions& options, const std::shared_ptr<IResolver>& resolver,
- const std::map<std::shared_ptr<ResourceTable>, StaticLibraryData>& libApks,
- const android::ResTable& table, ZipFile* outApk, proguard::KeepSet* keepSet) {
- if (options.verbose) {
- Logger::note(options.manifest) << "compiling AndroidManifest.xml." << std::endl;
- }
-
- std::ifstream in(options.manifest.path, std::ifstream::binary);
- if (!in) {
- Logger::error(options.manifest) << strerror(errno) << std::endl;
- return false;
- }
-
- SourceLogger logger(options.manifest);
- std::unique_ptr<xml::Node> root = xml::inflate(&in, &logger);
- if (!root) {
- return false;
- }
-
- ManifestMerger merger({});
- if (!merger.setAppManifest(options.manifest, options.appInfo.package, std::move(root))) {
- return false;
- }
-
- for (const auto& entry : libApks) {
- ZipFile* libApk = entry.second.apk.get();
- const std::u16string& libPackage = entry.first->getPackage();
- const Source& libSource = entry.second.source;
-
- ZipEntry* zipEntry = libApk->getEntryByName("AndroidManifest.xml");
- if (!zipEntry) {
- continue;
- }
-
- std::unique_ptr<void, DeleteMalloc> uncompressedData = std::unique_ptr<void, DeleteMalloc>(
- libApk->uncompress(zipEntry));
- assert(uncompressedData);
-
- SourceLogger logger(libSource);
- std::unique_ptr<xml::Node> libRoot = xml::inflate(uncompressedData.get(),
- zipEntry->getUncompressedLen(), &logger);
- if (!libRoot) {
- return false;
- }
-
- if (!merger.mergeLibraryManifest(libSource, libPackage, std::move(libRoot))) {
- return false;
- }
- }
-
- if (options.generateProguardRules) {
- proguard::collectProguardRulesForManifest(options.manifest, merger.getMergedXml(),
- keepSet);
- }
-
- BigBuffer outBuffer(1024);
- if (!xml::flattenAndLink(options.manifest, merger.getMergedXml(), options.appInfo.package,
- resolver, {}, &outBuffer)) {
- return false;
- }
-
- std::unique_ptr<uint8_t[]> data = util::copy(outBuffer);
-
- android::ResXMLTree tree;
- if (tree.setTo(data.get(), outBuffer.size(), false) != android::NO_ERROR) {
- return false;
- }
-
- ManifestValidator validator(table);
- if (!validator.validate(options.manifest, &tree)) {
- return false;
- }
-
- if (outApk->add(data.get(), outBuffer.size(), "AndroidManifest.xml",
- ZipEntry::kCompressStored, nullptr) != android::NO_ERROR) {
- Logger::error(options.output) << "failed to write 'AndroidManifest.xml' to apk."
- << std::endl;
- return false;
- }
- return true;
-}
-
-static bool compileValues(const std::shared_ptr<ResourceTable>& table, const Source& source,
- const ConfigDescription& config) {
- std::ifstream in(source.path, std::ifstream::binary);
- if (!in) {
- Logger::error(source) << strerror(errno) << std::endl;
- return false;
- }
-
- std::shared_ptr<XmlPullParser> xmlParser = std::make_shared<SourceXmlPullParser>(in);
- ResourceParser parser(table, source, config, xmlParser);
- return parser.parse();
-}
-
-struct ResourcePathData {
- std::u16string resourceDir;
- std::u16string name;
- std::string extension;
- ConfigDescription config;
-};
-
-/**
- * Resource file paths are expected to look like:
- * [--/res/]type[-config]/name
- */
-static Maybe<ResourcePathData> extractResourcePathData(const Source& source) {
- // TODO(adamlesinski): Use Windows path separator on windows.
- std::vector<std::string> parts = util::splitAndLowercase(source.path, '/');
- if (parts.size() < 2) {
- Logger::error(source) << "bad resource path." << std::endl;
- return {};
- }
-
- std::string& dir = parts[parts.size() - 2];
- StringPiece dirStr = dir;
-
- ConfigDescription config;
- size_t dashPos = dir.find('-');
- if (dashPos != std::string::npos) {
- StringPiece configStr = dirStr.substr(dashPos + 1, dir.size() - (dashPos + 1));
- if (!ConfigDescription::parse(configStr, &config)) {
- Logger::error(source)
- << "invalid configuration '"
- << configStr
- << "'."
- << std::endl;
- return {};
- }
- dirStr = dirStr.substr(0, dashPos);
- }
-
- std::string& filename = parts[parts.size() - 1];
- StringPiece name = filename;
- StringPiece extension;
- size_t dotPos = filename.find('.');
- if (dotPos != std::string::npos) {
- extension = name.substr(dotPos + 1, filename.size() - (dotPos + 1));
- name = name.substr(0, dotPos);
- }
-
- return ResourcePathData{
- util::utf8ToUtf16(dirStr),
- util::utf8ToUtf16(name),
- extension.toString(),
- config
- };
-}
-
-bool writeResourceTable(const AaptOptions& options, const std::shared_ptr<ResourceTable>& table,
- const TableFlattener::Options& flattenerOptions, ZipFile* outApk) {
- if (table->begin() != table->end()) {
- BigBuffer buffer(1024);
- TableFlattener flattener(flattenerOptions);
- if (!flattener.flatten(&buffer, *table)) {
- Logger::error() << "failed to flatten resource table." << std::endl;
- return false;
- }
-
- if (options.verbose) {
- Logger::note() << "Final resource table size=" << util::formatSize(buffer.size())
- << std::endl;
- }
-
- if (outApk->add(buffer, "resources.arsc", ZipEntry::kCompressStored, nullptr) !=
- android::NO_ERROR) {
- Logger::note(options.output) << "failed to store resource table." << std::endl;
- return false;
- }
- }
- return true;
-}
-
-/**
- * For each FileReference in the table, adds a LinkItem to the link queue for processing.
- */
-static void addApkFilesToLinkQueue(const std::u16string& package, const Source& source,
- const std::shared_ptr<ResourceTable>& table,
- const std::unique_ptr<ZipFile>& apk,
- std::queue<LinkItem>* outLinkQueue) {
- bool mangle = package != table->getPackage();
- for (auto& type : *table) {
- for (auto& entry : type->entries) {
- ResourceName name = { package, type->type, entry->name };
- if (mangle) {
- NameMangler::mangle(table->getPackage(), &name.entry);
- }
-
- for (auto& value : entry->values) {
- visitFunc<FileReference>(*value.value, [&](FileReference& ref) {
- std::string pathUtf8 = util::utf16ToUtf8(*ref.path);
- Source newSource = source;
- newSource.path += "/";
- newSource.path += pathUtf8;
- outLinkQueue->push(LinkItem{
- name, value.config, newSource, pathUtf8, apk.get(),
- table->getPackage() });
- // Now rewrite the file path.
- if (mangle) {
- ref.path = table->getValueStringPool().makeRef(util::utf8ToUtf16(
- buildFileReference(name, value.config,
- getExtension<char>(pathUtf8))));
- }
- });
- }
- }
- }
-}
-
-static constexpr int kOpenFlags = ZipFile::kOpenCreate | ZipFile::kOpenTruncate |
- ZipFile::kOpenReadWrite;
-
-bool link(const AaptOptions& options, const std::shared_ptr<ResourceTable>& outTable,
- const std::shared_ptr<IResolver>& resolver) {
- std::map<std::shared_ptr<ResourceTable>, StaticLibraryData> apkFiles;
- std::unordered_set<std::u16string> linkedPackages;
-
- // Populate the linkedPackages with our own.
- linkedPackages.insert(options.appInfo.package);
-
- // Load all APK files.
- for (const Source& source : options.input) {
- std::unique_ptr<ZipFile> zipFile = util::make_unique<ZipFile>();
- if (zipFile->open(source.path.data(), ZipFile::kOpenReadOnly) != android::NO_ERROR) {
- Logger::error(source) << "failed to open: " << strerror(errno) << std::endl;
- return false;
- }
-
- std::shared_ptr<ResourceTable> table = std::make_shared<ResourceTable>();
-
- ZipEntry* entry = zipFile->getEntryByName("resources.arsc");
- if (!entry) {
- Logger::error(source) << "missing 'resources.arsc'." << std::endl;
- return false;
- }
-
- std::unique_ptr<void, DeleteMalloc> uncompressedData = std::unique_ptr<void, DeleteMalloc>(
- zipFile->uncompress(entry));
- assert(uncompressedData);
-
- BinaryResourceParser parser(table, resolver, source, options.appInfo.package,
- uncompressedData.get(), entry->getUncompressedLen());
- if (!parser.parse()) {
- return false;
- }
-
- // Keep track of where this table came from.
- apkFiles[table] = StaticLibraryData{ source, std::move(zipFile) };
-
- // Add the package to the set of linked packages.
- linkedPackages.insert(table->getPackage());
- }
-
- std::queue<LinkItem> linkQueue;
- for (auto& p : apkFiles) {
- const std::shared_ptr<ResourceTable>& inTable = p.first;
-
- // Collect all FileReferences and add them to the queue for processing.
- addApkFilesToLinkQueue(options.appInfo.package, p.second.source, inTable, p.second.apk,
- &linkQueue);
-
- // Merge the tables.
- if (!outTable->merge(std::move(*inTable))) {
- return false;
- }
- }
-
- // Version all styles referencing attributes outside of their specified SDK version.
- if (options.versionStylesAndLayouts) {
- versionStylesForCompat(outTable);
- }
-
- {
- // Now that everything is merged, let's link it.
- Linker::Options linkerOptions;
- if (options.packageType == AaptOptions::PackageType::StaticLibrary) {
- linkerOptions.linkResourceIds = false;
- }
- Linker linker(outTable, resolver, linkerOptions);
- if (!linker.linkAndValidate()) {
- return false;
- }
-
- // Verify that all symbols exist.
- const auto& unresolvedRefs = linker.getUnresolvedReferences();
- if (!unresolvedRefs.empty()) {
- for (const auto& entry : unresolvedRefs) {
- for (const auto& source : entry.second) {
- Logger::error(source) << "unresolved symbol '" << entry.first << "'."
- << std::endl;
- }
- }
- return false;
- }
- }
-
- // Open the output APK file for writing.
- ZipFile outApk;
- if (outApk.open(options.output.path.data(), kOpenFlags) != android::NO_ERROR) {
- Logger::error(options.output) << "failed to open: " << strerror(errno) << std::endl;
- return false;
- }
-
- proguard::KeepSet keepSet;
-
- android::ResTable binTable;
- if (!compileManifest(options, resolver, apkFiles, binTable, &outApk, &keepSet)) {
- return false;
- }
-
- for (; !linkQueue.empty(); linkQueue.pop()) {
- const LinkItem& item = linkQueue.front();
-
- assert(!item.originalPackage.empty());
- ZipEntry* entry = item.apk->getEntryByName(item.originalPath.data());
- if (!entry) {
- Logger::error(item.source) << "failed to find '" << item.originalPath << "'."
- << std::endl;
- return false;
- }
-
- if (util::stringEndsWith<char>(item.originalPath, ".xml")) {
- void* uncompressedData = item.apk->uncompress(entry);
- assert(uncompressedData);
-
- if (!linkXml(options, outTable, resolver, item, uncompressedData,
- entry->getUncompressedLen(), &outApk, &linkQueue, &keepSet)) {
- Logger::error(options.output) << "failed to link '" << item.originalPath << "'."
- << std::endl;
- return false;
- }
- } else {
- if (outApk.add(item.apk, entry, buildFileReference(item).data(), 0, nullptr) !=
- android::NO_ERROR) {
- Logger::error(options.output) << "failed to copy '" << item.originalPath << "'."
- << std::endl;
- return false;
- }
- }
- }
-
- // Generate the Java class file.
- if (options.generateJavaClass) {
- JavaClassGenerator::Options javaOptions;
- if (options.packageType == AaptOptions::PackageType::StaticLibrary) {
- javaOptions.useFinal = false;
- }
- JavaClassGenerator generator(outTable, javaOptions);
-
- for (const std::u16string& package : linkedPackages) {
- Source outPath = options.generateJavaClass.value();
-
- // Build the output directory from the package name.
- // Eg. com.android.app -> com/android/app
- const std::string packageUtf8 = util::utf16ToUtf8(package);
- for (StringPiece part : util::tokenize<char>(packageUtf8, '.')) {
- appendPath(&outPath.path, part);
- }
-
- if (!mkdirs(outPath.path)) {
- Logger::error(outPath) << strerror(errno) << std::endl;
- return false;
- }
-
- appendPath(&outPath.path, "R.java");
-
- if (options.verbose) {
- Logger::note(outPath) << "writing Java symbols." << std::endl;
- }
-
- std::ofstream fout(outPath.path);
- if (!fout) {
- Logger::error(outPath) << strerror(errno) << std::endl;
- return false;
- }
-
- if (!generator.generate(package, fout)) {
- Logger::error(outPath) << generator.getError() << "." << std::endl;
- return false;
- }
- }
- }
-
- // Generate the Proguard rules file.
- if (options.generateProguardRules) {
- const Source& outPath = options.generateProguardRules.value();
-
- if (options.verbose) {
- Logger::note(outPath) << "writing proguard rules." << std::endl;
- }
-
- std::ofstream fout(outPath.path);
- if (!fout) {
- Logger::error(outPath) << strerror(errno) << std::endl;
- return false;
- }
-
- if (!proguard::writeKeepSet(&fout, keepSet)) {
- Logger::error(outPath) << "failed to write proguard rules." << std::endl;
- return false;
- }
- }
-
- outTable->getValueStringPool().prune();
- outTable->getValueStringPool().sort(
- [](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
- if (a.context.priority < b.context.priority) {
- return true;
- }
-
- if (a.context.priority > b.context.priority) {
- return false;
- }
- return a.value < b.value;
- });
-
-
- // Flatten the resource table.
- TableFlattener::Options flattenerOptions;
- if (options.packageType != AaptOptions::PackageType::StaticLibrary) {
- flattenerOptions.useExtendedChunks = false;
- }
-
- if (!writeResourceTable(options, outTable, flattenerOptions, &outApk)) {
- return false;
- }
-
- outApk.flush();
- return true;
-}
-
-bool compile(const AaptOptions& options, const std::shared_ptr<ResourceTable>& table,
- const std::shared_ptr<IResolver>& resolver) {
- std::queue<CompileItem> compileQueue;
- bool error = false;
-
- // Compile all the resource files passed in on the command line.
- for (const Source& source : options.input) {
- // Need to parse the resource type/config/filename.
- Maybe<ResourcePathData> maybePathData = extractResourcePathData(source);
- if (!maybePathData) {
- return false;
- }
-
- const ResourcePathData& pathData = maybePathData.value();
- if (pathData.resourceDir == u"values") {
- // The file is in the values directory, which means its contents will
- // go into the resource table.
- if (options.verbose) {
- Logger::note(source) << "compiling values." << std::endl;
- }
-
- error |= !compileValues(table, source, pathData.config);
- } else {
- // The file is in a directory like 'layout' or 'drawable'. Find out
- // the type.
- const ResourceType* type = parseResourceType(pathData.resourceDir);
- if (!type) {
- Logger::error(source) << "invalid resource type '" << pathData.resourceDir << "'."
- << std::endl;
- return false;
- }
-
- compileQueue.push(CompileItem{
- ResourceName{ table->getPackage(), *type, pathData.name },
- pathData.config,
- source,
- pathData.extension
- });
- }
- }
-
- if (error) {
- return false;
- }
- // Open the output APK file for writing.
- ZipFile outApk;
- if (outApk.open(options.output.path.data(), kOpenFlags) != android::NO_ERROR) {
- Logger::error(options.output) << "failed to open: " << strerror(errno) << std::endl;
- return false;
- }
-
- // Compile each file.
- for (; !compileQueue.empty(); compileQueue.pop()) {
- const CompileItem& item = compileQueue.front();
-
- // Add the file name to the resource table.
- error |= !addFileReference(table, item);
-
- if (item.extension == "xml") {
- error |= !compileXml(options, table, item, &outApk);
- } else if (item.extension == "png" || item.extension == "9.png") {
- error |= !compilePng(options, item, &outApk);
- } else {
- error |= !copyFile(options, item, &outApk);
- }
- }
-
- if (error) {
- return false;
- }
-
- // Link and assign resource IDs.
- Linker linker(table, resolver, {});
- if (!linker.linkAndValidate()) {
- return false;
- }
-
- // Flatten the resource table.
- if (!writeResourceTable(options, table, {}, &outApk)) {
- return false;
- }
-
- outApk.flush();
- return true;
-}
-
-bool loadAppInfo(const Source& source, AppInfo* outInfo) {
- std::ifstream ifs(source.path, std::ifstream::in | std::ifstream::binary);
- if (!ifs) {
- Logger::error(source) << strerror(errno) << std::endl;
- return false;
- }
-
- ManifestParser parser;
- std::shared_ptr<XmlPullParser> pullParser = std::make_shared<SourceXmlPullParser>(ifs);
- return parser.parse(source, pullParser, outInfo);
-}
-
-static void printCommandsAndDie() {
- std::cerr << "The following commands are supported:" << std::endl << std::endl;
- std::cerr << "compile compiles a subset of resources" << std::endl;
- std::cerr << "link links together compiled resources and libraries" << std::endl;
- std::cerr << "dump dumps resource contents to to standard out" << std::endl;
- std::cerr << std::endl;
- std::cerr << "run aapt2 with one of the commands and the -h flag for extra details."
- << std::endl;
- exit(1);
-}
-
-static AaptOptions prepareArgs(int argc, char** argv) {
- if (argc < 2) {
- std::cerr << "no command specified." << std::endl << std::endl;
- printCommandsAndDie();
- }
-
- const StringPiece command(argv[1]);
- argc -= 2;
- argv += 2;
-
- AaptOptions options;
-
- if (command == "--version" || command == "version") {
- std::cout << kAaptVersionStr << std::endl;
- exit(0);
- } else if (command == "link") {
- options.phase = AaptOptions::Phase::Link;
- } else if (command == "compile") {
- options.phase = AaptOptions::Phase::Compile;
- } else if (command == "dump") {
- options.phase = AaptOptions::Phase::Dump;
- } else if (command == "dump-style-graph") {
- options.phase = AaptOptions::Phase::DumpStyleGraph;
- } else {
- std::cerr << "invalid command '" << command << "'." << std::endl << std::endl;
- printCommandsAndDie();
- }
-
- bool isStaticLib = false;
- if (options.phase == AaptOptions::Phase::Link) {
- flag::requiredFlag("--manifest", "AndroidManifest.xml of your app",
- [&options](const StringPiece& arg) {
- options.manifest = Source{ arg.toString() };
- });
-
- flag::optionalFlag("-I", "add an Android APK to link against",
- [&options](const StringPiece& arg) {
- options.libraries.push_back(Source{ arg.toString() });
- });
-
- flag::optionalFlag("--java", "directory in which to generate R.java",
- [&options](const StringPiece& arg) {
- options.generateJavaClass = Source{ arg.toString() };
- });
-
- flag::optionalFlag("--proguard", "file in which to output proguard rules",
- [&options](const StringPiece& arg) {
- options.generateProguardRules = Source{ arg.toString() };
- });
-
- flag::optionalSwitch("--static-lib", "generate a static Android library", true,
- &isStaticLib);
-
- flag::optionalFlag("--binding", "Output directory for binding XML files",
- [&options](const StringPiece& arg) {
- options.bindingOutput = Source{ arg.toString() };
- });
- flag::optionalSwitch("--no-version", "Disables automatic style and layout versioning",
- false, &options.versionStylesAndLayouts);
- }
-
- if (options.phase == AaptOptions::Phase::Compile ||
- options.phase == AaptOptions::Phase::Link) {
- // Common flags for all steps.
- flag::requiredFlag("-o", "Output path", [&options](const StringPiece& arg) {
- options.output = Source{ arg.toString() };
- });
- }
-
- if (options.phase == AaptOptions::Phase::DumpStyleGraph) {
- flag::requiredFlag("--style", "Name of the style to dump",
- [&options](const StringPiece& arg, std::string* outError) -> bool {
- Reference styleReference;
- if (!ResourceParser::parseStyleParentReference(util::utf8ToUtf16(arg),
- &styleReference, outError)) {
- return false;
- }
- options.dumpStyleTarget = styleReference.name;
- return true;
- });
- }
-
- bool help = false;
- flag::optionalSwitch("-v", "enables verbose logging", true, &options.verbose);
- flag::optionalSwitch("-h", "displays this help menu", true, &help);
-
- // Build the command string for output (eg. "aapt2 compile").
- std::string fullCommand = "aapt2";
- fullCommand += " ";
- fullCommand += command.toString();
-
- // Actually read the command line flags.
- flag::parse(argc, argv, fullCommand);
-
- if (help) {
- flag::usageAndDie(fullCommand);
- }
-
- if (isStaticLib) {
- options.packageType = AaptOptions::PackageType::StaticLibrary;
- }
-
- // Copy all the remaining arguments.
- for (const std::string& arg : flag::getArgs()) {
- options.input.push_back(Source{ arg });
- }
- return options;
-}
-
-static bool doDump(const AaptOptions& options) {
- for (const Source& source : options.input) {
- std::unique_ptr<ZipFile> zipFile = util::make_unique<ZipFile>();
- if (zipFile->open(source.path.data(), ZipFile::kOpenReadOnly) != android::NO_ERROR) {
- Logger::error(source) << "failed to open: " << strerror(errno) << std::endl;
- return false;
- }
-
- std::shared_ptr<ResourceTable> table = std::make_shared<ResourceTable>();
- std::shared_ptr<ResourceTableResolver> resolver =
- std::make_shared<ResourceTableResolver>(
- table, std::vector<std::shared_ptr<const android::AssetManager>>());
-
- ZipEntry* entry = zipFile->getEntryByName("resources.arsc");
- if (!entry) {
- Logger::error(source) << "missing 'resources.arsc'." << std::endl;
- return false;
- }
-
- std::unique_ptr<void, DeleteMalloc> uncompressedData = std::unique_ptr<void, DeleteMalloc>(
- zipFile->uncompress(entry));
- assert(uncompressedData);
-
- BinaryResourceParser parser(table, resolver, source, {}, uncompressedData.get(),
- entry->getUncompressedLen());
- if (!parser.parse()) {
- return false;
- }
-
- if (options.phase == AaptOptions::Phase::Dump) {
- Debug::printTable(table);
- } else if (options.phase == AaptOptions::Phase::DumpStyleGraph) {
- Debug::printStyleGraph(table, options.dumpStyleTarget);
- }
- }
- return true;
-}
+} // namespace aapt
int main(int argc, char** argv) {
- Logger::setLog(std::make_shared<Log>(std::cerr, std::cerr));
- AaptOptions options = prepareArgs(argc, argv);
+ if (argc >= 2) {
+ argv += 1;
+ argc -= 1;
- if (options.phase == AaptOptions::Phase::Dump ||
- options.phase == AaptOptions::Phase::DumpStyleGraph) {
- if (!doDump(options)) {
- return 1;
- }
- return 0;
- }
-
- // If we specified a manifest, go ahead and load the package name from the manifest.
- if (!options.manifest.path.empty()) {
- if (!loadAppInfo(options.manifest, &options.appInfo)) {
- return false;
+ std::vector<aapt::StringPiece> args;
+ for (int i = 1; i < argc; i++) {
+ args.push_back(argv[i]);
}
- if (options.appInfo.package.empty()) {
- Logger::error() << "no package name specified." << std::endl;
- return false;
+ aapt::StringPiece command(argv[0]);
+ if (command == "compile" || command == "c") {
+ return aapt::compile(args);
+ } else if (command == "link" || command == "l") {
+ return aapt::link(args);
}
- }
-
- // Every phase needs a resource table.
- std::shared_ptr<ResourceTable> table = std::make_shared<ResourceTable>();
-
- // The package name is empty when in the compile phase.
- table->setPackage(options.appInfo.package);
- if (options.appInfo.package == u"android") {
- table->setPackageId(0x01);
+ std::cerr << "unknown command '" << command << "'\n";
} else {
- table->setPackageId(0x7f);
+ std::cerr << "no command specified\n";
}
- // Load the included libraries.
- std::vector<std::shared_ptr<const android::AssetManager>> sources;
- for (const Source& source : options.libraries) {
- std::shared_ptr<android::AssetManager> assetManager =
- std::make_shared<android::AssetManager>();
- int32_t cookie;
- if (!assetManager->addAssetPath(android::String8(source.path.data()), &cookie)) {
- Logger::error(source) << "failed to load library." << std::endl;
- return false;
- }
-
- if (cookie == 0) {
- Logger::error(source) << "failed to load library." << std::endl;
- return false;
- }
- sources.push_back(assetManager);
- }
-
- // Make the resolver that will cache IDs for us.
- std::shared_ptr<ResourceTableResolver> resolver = std::make_shared<ResourceTableResolver>(
- table, sources);
-
- if (options.phase == AaptOptions::Phase::Compile) {
- if (!compile(options, table, resolver)) {
- Logger::error() << "aapt exiting with failures." << std::endl;
- return 1;
- }
- } else if (options.phase == AaptOptions::Phase::Link) {
- if (!link(options, table, resolver)) {
- Logger::error() << "aapt exiting with failures." << std::endl;
- return 1;
- }
- }
- return 0;
+ std::cerr << "\nusage: aapt2 [compile|link] ..." << std::endl;
+ return 1;
}
diff --git a/tools/aapt2/ManifestMerger.cpp b/tools/aapt2/ManifestMerger.cpp
deleted file mode 100644
index 71d3424..0000000
--- a/tools/aapt2/ManifestMerger.cpp
+++ /dev/null
@@ -1,376 +0,0 @@
-#include "ManifestMerger.h"
-#include "Maybe.h"
-#include "ResourceParser.h"
-#include "Source.h"
-#include "Util.h"
-#include "XmlPullParser.h"
-
-#include <iostream>
-#include <memory>
-#include <set>
-#include <string>
-
-namespace aapt {
-
-constexpr const char16_t* kSchemaAndroid = u"http://schemas.android.com/apk/res/android";
-
-static xml::Element* findManifest(xml::Node* root) {
- if (!root) {
- return nullptr;
- }
-
- while (root->type == xml::NodeType::kNamespace) {
- if (root->children.empty()) {
- break;
- }
- root = root->children[0].get();
- }
-
- if (root && root->type == xml::NodeType::kElement) {
- xml::Element* el = static_cast<xml::Element*>(root);
- if (el->namespaceUri.empty() && el->name == u"manifest") {
- return el;
- }
- }
- return nullptr;
-}
-
-static xml::Element* findChildWithSameName(xml::Element* parent, xml::Element* src) {
- xml::Attribute* attrKey = src->findAttribute(kSchemaAndroid, u"name");
- if (!attrKey) {
- return nullptr;
- }
- return parent->findChildWithAttribute(src->namespaceUri, src->name, attrKey);
-}
-
-static bool attrLess(const xml::Attribute& lhs, const xml::Attribute& rhs) {
- return std::tie(lhs.namespaceUri, lhs.name, lhs.value)
- < std::tie(rhs.namespaceUri, rhs.name, rhs.value);
-}
-
-static int compare(xml::Element* lhs, xml::Element* rhs) {
- int diff = lhs->attributes.size() - rhs->attributes.size();
- if (diff != 0) {
- return diff;
- }
-
- std::set<xml::Attribute, decltype(&attrLess)> lhsAttrs(&attrLess);
- lhsAttrs.insert(lhs->attributes.begin(), lhs->attributes.end());
- for (auto& attr : rhs->attributes) {
- if (lhsAttrs.erase(attr) == 0) {
- // The rhs attribute is not in the left.
- return -1;
- }
- }
-
- if (!lhsAttrs.empty()) {
- // The lhs has attributes not in the rhs.
- return 1;
- }
- return 0;
-}
-
-ManifestMerger::ManifestMerger(const Options& options) :
- mOptions(options), mAppLogger({}), mLogger({}) {
-}
-
-bool ManifestMerger::setAppManifest(const Source& source, const std::u16string& package,
- std::unique_ptr<xml::Node> root) {
-
- mAppLogger = SourceLogger{ source };
- mRoot = std::move(root);
- return true;
-}
-
-bool ManifestMerger::checkEqual(xml::Element* elA, xml::Element* elB) {
- if (compare(elA, elB) != 0) {
- mLogger.error(elB->lineNumber)
- << "library tag '" << elB->name << "' conflicts with app tag."
- << std::endl;
- mAppLogger.note(elA->lineNumber)
- << "app tag '" << elA->name << "' defined here."
- << std::endl;
- return false;
- }
-
- std::vector<xml::Element*> childrenA = elA->getChildElements();
- std::vector<xml::Element*> childrenB = elB->getChildElements();
-
- if (childrenA.size() != childrenB.size()) {
- mLogger.error(elB->lineNumber)
- << "library tag '" << elB->name << "' children conflict with app tag."
- << std::endl;
- mAppLogger.note(elA->lineNumber)
- << "app tag '" << elA->name << "' defined here."
- << std::endl;
- return false;
- }
-
- auto cmp = [](xml::Element* lhs, xml::Element* rhs) -> bool {
- return compare(lhs, rhs) < 0;
- };
-
- std::sort(childrenA.begin(), childrenA.end(), cmp);
- std::sort(childrenB.begin(), childrenB.end(), cmp);
-
- for (size_t i = 0; i < childrenA.size(); i++) {
- if (!checkEqual(childrenA[i], childrenB[i])) {
- return false;
- }
- }
- return true;
-}
-
-bool ManifestMerger::mergeNewOrEqual(xml::Element* parentA, xml::Element* elA, xml::Element* elB) {
- if (!elA) {
- parentA->addChild(elB->clone());
- return true;
- }
- return checkEqual(elA, elB);
-}
-
-bool ManifestMerger::mergePreferRequired(xml::Element* parentA, xml::Element* elA,
- xml::Element* elB) {
- if (!elA) {
- parentA->addChild(elB->clone());
- return true;
- }
-
- xml::Attribute* reqA = elA->findAttribute(kSchemaAndroid, u"required");
- xml::Attribute* reqB = elB->findAttribute(kSchemaAndroid, u"required");
- bool requiredA = !reqA || (reqA->value != u"false" && reqA->value != u"FALSE");
- bool requiredB = !reqB || (reqB->value != u"false" && reqB->value != u"FALSE");
- if (!requiredA && requiredB) {
- if (reqA) {
- *reqA = xml::Attribute{ kSchemaAndroid, u"required", u"true" };
- } else {
- elA->attributes.push_back(xml::Attribute{ kSchemaAndroid, u"required", u"true" });
- }
- }
- return true;
-}
-
-static int findIntegerValue(xml::Attribute* attr, int defaultValue) {
- if (attr) {
- std::unique_ptr<BinaryPrimitive> integer = ResourceParser::tryParseInt(attr->value);
- if (integer) {
- return integer->value.data;
- }
- }
- return defaultValue;
-}
-
-bool ManifestMerger::mergeUsesSdk(xml::Element* elA, xml::Element* elB) {
- bool error = false;
- xml::Attribute* minAttrA = nullptr;
- xml::Attribute* minAttrB = nullptr;
- if (elA) {
- minAttrA = elA->findAttribute(kSchemaAndroid, u"minSdkVersion");
- }
-
- if (elB) {
- minAttrB = elB->findAttribute(kSchemaAndroid, u"minSdkVersion");
- }
-
- int minSdkA = findIntegerValue(minAttrA, 1);
- int minSdkB = findIntegerValue(minAttrB, 1);
-
- if (minSdkA < minSdkB) {
- std::ostream* out;
- if (minAttrA) {
- out = &(mAppLogger.error(elA->lineNumber) << "app declares ");
- } else if (elA) {
- out = &(mAppLogger.error(elA->lineNumber) << "app has implied ");
- } else {
- out = &(mAppLogger.error() << "app has implied ");
- }
-
- *out << "minSdkVersion=" << minSdkA << " but library expects a higher SDK version."
- << std::endl;
-
- // elB is valid because minSdkB wouldn't be greater than minSdkA if it wasn't.
- mLogger.note(elB->lineNumber)
- << "library declares minSdkVersion=" << minSdkB << "."
- << std::endl;
- error = true;
- }
-
- xml::Attribute* targetAttrA = nullptr;
- xml::Attribute* targetAttrB = nullptr;
-
- if (elA) {
- targetAttrA = elA->findAttribute(kSchemaAndroid, u"targetSdkVersion");
- }
-
- if (elB) {
- targetAttrB = elB->findAttribute(kSchemaAndroid, u"targetSdkVersion");
- }
-
- int targetSdkA = findIntegerValue(targetAttrA, minSdkA);
- int targetSdkB = findIntegerValue(targetAttrB, minSdkB);
-
- if (targetSdkA < targetSdkB) {
- std::ostream* out;
- if (targetAttrA) {
- out = &(mAppLogger.warn(elA->lineNumber) << "app declares ");
- } else if (elA) {
- out = &(mAppLogger.warn(elA->lineNumber) << "app has implied ");
- } else {
- out = &(mAppLogger.warn() << "app has implied ");
- }
-
- *out << "targetSdkVerion=" << targetSdkA << " but library expects target SDK "
- << targetSdkB << "." << std::endl;
-
- mLogger.note(elB->lineNumber)
- << "library declares targetSdkVersion=" << targetSdkB << "."
- << std::endl;
- error = true;
- }
- return !error;
-}
-
-bool ManifestMerger::mergeApplication(xml::Element* applicationA, xml::Element* applicationB) {
- if (!applicationA || !applicationB) {
- return true;
- }
-
- bool error = false;
-
- // First make sure that the names are identical.
- xml::Attribute* nameA = applicationA->findAttribute(kSchemaAndroid, u"name");
- xml::Attribute* nameB = applicationB->findAttribute(kSchemaAndroid, u"name");
- if (nameB) {
- if (!nameA) {
- applicationA->attributes.push_back(*nameB);
- } else if (nameA->value != nameB->value) {
- mLogger.error(applicationB->lineNumber)
- << "conflicting application name '"
- << nameB->value
- << "'." << std::endl;
- mAppLogger.note(applicationA->lineNumber)
- << "application defines application name '"
- << nameA->value
- << "'." << std::endl;
- error = true;
- }
- }
-
- // Now we descend into the activity/receiver/service/provider tags
- for (xml::Element* elB : applicationB->getChildElements()) {
- if (!elB->namespaceUri.empty()) {
- continue;
- }
-
- if (elB->name == u"activity" || elB->name == u"activity-alias"
- || elB->name == u"service" || elB->name == u"receiver"
- || elB->name == u"provider" || elB->name == u"meta-data") {
- xml::Element* elA = findChildWithSameName(applicationA, elB);
- error |= !mergeNewOrEqual(applicationA, elA, elB);
- } else if (elB->name == u"uses-library") {
- xml::Element* elA = findChildWithSameName(applicationA, elB);
- error |= !mergePreferRequired(applicationA, elA, elB);
- }
- }
- return !error;
-}
-
-bool ManifestMerger::mergeLibraryManifest(const Source& source, const std::u16string& package,
- std::unique_ptr<xml::Node> libRoot) {
- mLogger = SourceLogger{ source };
- xml::Element* manifestA = findManifest(mRoot.get());
- xml::Element* manifestB = findManifest(libRoot.get());
- if (!manifestA) {
- mAppLogger.error() << "missing manifest tag." << std::endl;
- return false;
- }
-
- if (!manifestB) {
- mLogger.error() << "library missing manifest tag." << std::endl;
- return false;
- }
-
- bool error = false;
-
- // Do <application> first.
- xml::Element* applicationA = manifestA->findChild({}, u"application");
- xml::Element* applicationB = manifestB->findChild({}, u"application");
- error |= !mergeApplication(applicationA, applicationB);
-
- // Do <uses-sdk> next.
- xml::Element* usesSdkA = manifestA->findChild({}, u"uses-sdk");
- xml::Element* usesSdkB = manifestB->findChild({}, u"uses-sdk");
- error |= !mergeUsesSdk(usesSdkA, usesSdkB);
-
- for (xml::Element* elB : manifestB->getChildElements()) {
- if (!elB->namespaceUri.empty()) {
- continue;
- }
-
- if (elB->name == u"uses-permission" || elB->name == u"permission"
- || elB->name == u"permission-group" || elB->name == u"permission-tree") {
- xml::Element* elA = findChildWithSameName(manifestA, elB);
- error |= !mergeNewOrEqual(manifestA, elA, elB);
- } else if (elB->name == u"uses-feature") {
- xml::Element* elA = findChildWithSameName(manifestA, elB);
- error |= !mergePreferRequired(manifestA, elA, elB);
- } else if (elB->name == u"uses-configuration" || elB->name == u"supports-screen"
- || elB->name == u"compatible-screens" || elB->name == u"supports-gl-texture") {
- xml::Element* elA = findChildWithSameName(manifestA, elB);
- error |= !checkEqual(elA, elB);
- }
- }
- return !error;
-}
-
-static void printMerged(xml::Node* node, int depth) {
- std::string indent;
- for (int i = 0; i < depth; i++) {
- indent += " ";
- }
-
- switch (node->type) {
- case xml::NodeType::kNamespace:
- std::cerr << indent << "N: "
- << "xmlns:" << static_cast<xml::Namespace*>(node)->namespacePrefix
- << "=\"" << static_cast<xml::Namespace*>(node)->namespaceUri
- << "\"\n";
- break;
-
- case xml::NodeType::kElement:
- std::cerr << indent << "E: "
- << static_cast<xml::Element*>(node)->namespaceUri
- << ":" << static_cast<xml::Element*>(node)->name
- << "\n";
- for (const auto& attr : static_cast<xml::Element*>(node)->attributes) {
- std::cerr << indent << " A: "
- << attr.namespaceUri
- << ":" << attr.name
- << "=\"" << attr.value << "\"\n";
- }
- break;
-
- case xml::NodeType::kText:
- std::cerr << indent << "T: \"" << static_cast<xml::Text*>(node)->text << "\"\n";
- break;
- }
-
- for (auto& child : node->children) {
- printMerged(child.get(), depth + 1);
- }
-}
-
-xml::Node* ManifestMerger::getMergedXml() {
- return mRoot.get();
-}
-
-bool ManifestMerger::printMerged() {
- if (!mRoot) {
- return false;
- }
-
- ::aapt::printMerged(mRoot.get(), 0);
- return true;
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/ManifestMerger.h b/tools/aapt2/ManifestMerger.h
deleted file mode 100644
index c6219db..0000000
--- a/tools/aapt2/ManifestMerger.h
+++ /dev/null
@@ -1,45 +0,0 @@
-#ifndef AAPT_MANIFEST_MERGER_H
-#define AAPT_MANIFEST_MERGER_H
-
-#include "Logger.h"
-#include "Source.h"
-#include "XmlDom.h"
-
-#include <memory>
-#include <string>
-
-namespace aapt {
-
-class ManifestMerger {
-public:
- struct Options {
- };
-
- ManifestMerger(const Options& options);
-
- bool setAppManifest(const Source& source, const std::u16string& package,
- std::unique_ptr<xml::Node> root);
-
- bool mergeLibraryManifest(const Source& source, const std::u16string& package,
- std::unique_ptr<xml::Node> libRoot);
-
- xml::Node* getMergedXml();
-
- bool printMerged();
-
-private:
- bool mergeNewOrEqual(xml::Element* parentA, xml::Element* elA, xml::Element* elB);
- bool mergePreferRequired(xml::Element* parentA, xml::Element* elA, xml::Element* elB);
- bool checkEqual(xml::Element* elA, xml::Element* elB);
- bool mergeApplication(xml::Element* applicationA, xml::Element* applicationB);
- bool mergeUsesSdk(xml::Element* elA, xml::Element* elB);
-
- Options mOptions;
- std::unique_ptr<xml::Node> mRoot;
- SourceLogger mAppLogger;
- SourceLogger mLogger;
-};
-
-} // namespace aapt
-
-#endif // AAPT_MANIFEST_MERGER_H
diff --git a/tools/aapt2/ManifestMerger_test.cpp b/tools/aapt2/ManifestMerger_test.cpp
deleted file mode 100644
index 6838253..0000000
--- a/tools/aapt2/ManifestMerger_test.cpp
+++ /dev/null
@@ -1,121 +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.
- */
-
-#include "ManifestMerger.h"
-#include "SourceXmlPullParser.h"
-
-#include <gtest/gtest.h>
-#include <sstream>
-#include <string>
-
-namespace aapt {
-
-constexpr const char* kAppManifest = R"EOF(<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android">
- <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="21" />
- <uses-permission android:name="android.permission.INTERNET"/>
- <uses-feature android:name="android.hardware.GPS" android:required="false" />
- <application android:name="com.android.library.Application">
- <activity android:name="com.android.example.MainActivity">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </activity>
- <service android:name="com.android.library.Service">
- <intent-filter>
- <action android:name="com.android.library.intent.action.SYNC" />
- </intent-filter>
- </service>
- </application>
-</manifest>
-)EOF";
-
-constexpr const char* kLibManifest = R"EOF(<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android">
- <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="21" />
- <uses-permission android:name="android.permission.INTERNET" />
- <uses-feature android:name="android.hardware.GPS" />
- <uses-permission android:name="android.permission.GPS" />
- <application android:name="com.android.library.Application">
- <service android:name="com.android.library.Service">
- <intent-filter>
- <action android:name="com.android.library.intent.action.SYNC" />
- </intent-filter>
- </service>
- <provider android:name="com.android.library.DocumentProvider"
- android:authorities="com.android.library.documents"
- android:grantUriPermission="true"
- android:exported="true"
- android:permission="android.permission.MANAGE_DOCUMENTS"
- android:enabled="@bool/atLeastKitKat">
- <intent-filter>
- <action android:name="android.content.action.DOCUMENTS_PROVIDER" />
- </intent-filter>
- </provider>
- </application>
-</manifest>
-)EOF";
-
-constexpr const char* kBadLibManifest = R"EOF(<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android">
- <uses-sdk android:minSdkVersion="17" android:targetSdkVersion="22" />
- <uses-permission android:name="android.permission.INTERNET" />
- <uses-feature android:name="android.hardware.GPS" />
- <uses-permission android:name="android.permission.GPS" />
- <application android:name="com.android.library.Application2">
- <service android:name="com.android.library.Service">
- <intent-filter>
- <action android:name="com.android.library.intent.action.SYNC_ACTION" />
- </intent-filter>
- </service>
- </application>
-</manifest>
-)EOF";
-
-TEST(ManifestMergerTest, MergeManifestsSuccess) {
- std::stringstream inA(kAppManifest);
- std::stringstream inB(kLibManifest);
-
- const Source sourceA = { "AndroidManifest.xml" };
- const Source sourceB = { "lib.apk/AndroidManifest.xml" };
- SourceLogger loggerA(sourceA);
- SourceLogger loggerB(sourceB);
-
- ManifestMerger merger({});
- EXPECT_TRUE(merger.setAppManifest(sourceA, u"com.android.example",
- xml::inflate(&inA, &loggerA)));
- EXPECT_TRUE(merger.mergeLibraryManifest(sourceB, u"com.android.library",
- xml::inflate(&inB, &loggerB)));
-}
-
-TEST(ManifestMergerTest, MergeManifestFail) {
- std::stringstream inA(kAppManifest);
- std::stringstream inB(kBadLibManifest);
-
- const Source sourceA = { "AndroidManifest.xml" };
- const Source sourceB = { "lib.apk/AndroidManifest.xml" };
- SourceLogger loggerA(sourceA);
- SourceLogger loggerB(sourceB);
-
- ManifestMerger merger({});
- EXPECT_TRUE(merger.setAppManifest(sourceA, u"com.android.example",
- xml::inflate(&inA, &loggerA)));
- EXPECT_FALSE(merger.mergeLibraryManifest(sourceB, u"com.android.library",
- xml::inflate(&inB, &loggerB)));
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/ManifestParser.cpp b/tools/aapt2/ManifestParser.cpp
deleted file mode 100644
index b8f0a43..0000000
--- a/tools/aapt2/ManifestParser.cpp
+++ /dev/null
@@ -1,84 +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.
- */
-
-#include "AppInfo.h"
-#include "Logger.h"
-#include "ManifestParser.h"
-#include "Source.h"
-#include "XmlPullParser.h"
-
-#include <string>
-
-namespace aapt {
-
-bool ManifestParser::parse(const Source& source, std::shared_ptr<XmlPullParser> parser,
- AppInfo* outInfo) {
- SourceLogger logger = { source };
-
- int depth = 0;
- while (XmlPullParser::isGoodEvent(parser->next())) {
- XmlPullParser::Event event = parser->getEvent();
- if (event == XmlPullParser::Event::kEndElement) {
- depth--;
- continue;
- } else if (event != XmlPullParser::Event::kStartElement) {
- continue;
- }
-
- depth++;
-
- const std::u16string& element = parser->getElementName();
- if (depth == 1) {
- if (element == u"manifest") {
- if (!parseManifest(logger, parser, outInfo)) {
- return false;
- }
- } else {
- logger.error()
- << "unexpected top-level element '"
- << element
- << "'."
- << std::endl;
- return false;
- }
- } else {
- XmlPullParser::skipCurrentElement(parser.get());
- }
- }
-
- if (parser->getEvent() == XmlPullParser::Event::kBadDocument) {
- logger.error(parser->getLineNumber())
- << "failed to parse manifest: "
- << parser->getLastError()
- << "."
- << std::endl;
- return false;
- }
- return true;
-}
-
-bool ManifestParser::parseManifest(SourceLogger& logger, std::shared_ptr<XmlPullParser> parser,
- AppInfo* outInfo) {
- auto attrIter = parser->findAttribute(u"", u"package");
- if (attrIter == parser->endAttributes() || attrIter->value.empty()) {
- logger.error() << "no 'package' attribute found for element <manifest>." << std::endl;
- return false;
- }
- outInfo->package = attrIter->value;
- return true;
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/ManifestParser.h b/tools/aapt2/ManifestParser.h
deleted file mode 100644
index f2e43d4..0000000
--- a/tools/aapt2/ManifestParser.h
+++ /dev/null
@@ -1,45 +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_MANIFEST_PARSER_H
-#define AAPT_MANIFEST_PARSER_H
-
-#include "AppInfo.h"
-#include "Logger.h"
-#include "Source.h"
-#include "XmlPullParser.h"
-
-namespace aapt {
-
-/*
- * Parses an AndroidManifest.xml file and fills in an AppInfo structure with
- * app data.
- */
-class ManifestParser {
-public:
- ManifestParser() = default;
- ManifestParser(const ManifestParser&) = delete;
-
- bool parse(const Source& source, std::shared_ptr<XmlPullParser> parser, AppInfo* outInfo);
-
-private:
- bool parseManifest(SourceLogger& logger, std::shared_ptr<XmlPullParser> parser,
- AppInfo* outInfo);
-};
-
-} // namespace aapt
-
-#endif // AAPT_MANIFEST_PARSER_H
diff --git a/tools/aapt2/ManifestParser_test.cpp b/tools/aapt2/ManifestParser_test.cpp
deleted file mode 100644
index be3a6fb..0000000
--- a/tools/aapt2/ManifestParser_test.cpp
+++ /dev/null
@@ -1,42 +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.
- */
-
-#include "AppInfo.h"
-#include "ManifestParser.h"
-#include "SourceXmlPullParser.h"
-
-#include <gtest/gtest.h>
-#include <sstream>
-#include <string>
-
-namespace aapt {
-
-TEST(ManifestParserTest, FindPackage) {
- std::stringstream input;
- input << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
- "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
- "package=\"android\">\n"
- "</manifest>\n";
-
- ManifestParser parser;
- AppInfo info;
- std::shared_ptr<XmlPullParser> xmlParser = std::make_shared<SourceXmlPullParser>(input);
- ASSERT_TRUE(parser.parse(Source{ "AndroidManifest.xml" }, xmlParser, &info));
-
- EXPECT_EQ(std::u16string(u"android"), info.package);
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/ManifestValidator.cpp b/tools/aapt2/ManifestValidator.cpp
deleted file mode 100644
index 123b9fa..0000000
--- a/tools/aapt2/ManifestValidator.cpp
+++ /dev/null
@@ -1,217 +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.
- */
-
-#include "Logger.h"
-#include "ManifestValidator.h"
-#include "Maybe.h"
-#include "Source.h"
-#include "Util.h"
-
-#include <androidfw/ResourceTypes.h>
-
-namespace aapt {
-
-ManifestValidator::ManifestValidator(const android::ResTable& table)
-: mTable(table) {
-}
-
-bool ManifestValidator::validate(const Source& source, android::ResXMLParser* parser) {
- SourceLogger logger(source);
-
- android::ResXMLParser::event_code_t code;
- while ((code = parser->next()) != android::ResXMLParser::END_DOCUMENT &&
- code != android::ResXMLParser::BAD_DOCUMENT) {
- if (code != android::ResXMLParser::START_TAG) {
- continue;
- }
-
- size_t len = 0;
- const StringPiece16 namespaceUri(parser->getElementNamespace(&len), len);
- if (!namespaceUri.empty()) {
- continue;
- }
-
- const StringPiece16 name(parser->getElementName(&len), len);
- if (name.empty()) {
- logger.error(parser->getLineNumber())
- << "failed to get the element name."
- << std::endl;
- return false;
- }
-
- if (name == u"manifest") {
- if (!validateManifest(source, parser)) {
- return false;
- }
- }
- }
- return true;
-}
-
-Maybe<StringPiece16> ManifestValidator::getAttributeValue(android::ResXMLParser* parser,
- size_t idx) {
- android::Res_value value;
- if (parser->getAttributeValue(idx, &value) < 0) {
- return StringPiece16();
- }
-
- const android::ResStringPool* pool = &parser->getStrings();
- if (value.dataType == android::Res_value::TYPE_REFERENCE) {
- ssize_t strIdx = mTable.resolveReference(&value, 0x10000000u);
- if (strIdx < 0) {
- return {};
- }
- pool = mTable.getTableStringBlock(strIdx);
- }
-
- if (value.dataType != android::Res_value::TYPE_STRING || !pool) {
- return {};
- }
- return util::getString(*pool, value.data);
-}
-
-Maybe<StringPiece16> ManifestValidator::getAttributeInlineValue(android::ResXMLParser* parser,
- size_t idx) {
- android::Res_value value;
- if (parser->getAttributeValue(idx, &value) < 0) {
- return StringPiece16();
- }
-
- if (value.dataType != android::Res_value::TYPE_STRING) {
- return {};
- }
- return util::getString(parser->getStrings(), value.data);
-}
-
-bool ManifestValidator::validateInlineAttribute(android::ResXMLParser* parser, size_t idx,
- SourceLogger& logger,
- const StringPiece16& charSet) {
- size_t len = 0;
- StringPiece16 element(parser->getElementName(&len), len);
- StringPiece16 attributeName(parser->getAttributeName(idx, &len), len);
- Maybe<StringPiece16> result = getAttributeInlineValue(parser, idx);
- if (!result) {
- logger.error(parser->getLineNumber())
- << "<"
- << element
- << "> must have a '"
- << attributeName
- << "' attribute with a string literal value."
- << std::endl;
- return false;
- }
- return validateAttributeImpl(element, attributeName, result.value(), charSet,
- parser->getLineNumber(), logger);
-}
-
-bool ManifestValidator::validateAttribute(android::ResXMLParser* parser, size_t idx,
- SourceLogger& logger, const StringPiece16& charSet) {
- size_t len = 0;
- StringPiece16 element(parser->getElementName(&len), len);
- StringPiece16 attributeName(parser->getAttributeName(idx, &len), len);
- Maybe<StringPiece16> result = getAttributeValue(parser, idx);
- if (!result) {
- logger.error(parser->getLineNumber())
- << "<"
- << element
- << "> must have a '"
- << attributeName
- << "' attribute that points to a string."
- << std::endl;
- return false;
- }
- return validateAttributeImpl(element, attributeName, result.value(), charSet,
- parser->getLineNumber(), logger);
-}
-
-bool ManifestValidator::validateAttributeImpl(const StringPiece16& element,
- const StringPiece16& attributeName,
- const StringPiece16& attributeValue,
- const StringPiece16& charSet, size_t lineNumber,
- SourceLogger& logger) {
- StringPiece16::const_iterator badIter =
- util::findNonAlphaNumericAndNotInSet(attributeValue, charSet);
- if (badIter != attributeValue.end()) {
- logger.error(lineNumber)
- << "tag <"
- << element
- << "> attribute '"
- << attributeName
- << "' has invalid character '"
- << StringPiece16(badIter, 1)
- << "'."
- << std::endl;
- return false;
- }
-
- if (!attributeValue.empty()) {
- StringPiece16 trimmed = util::trimWhitespace(attributeValue);
- if (attributeValue.begin() != trimmed.begin()) {
- logger.error(lineNumber)
- << "tag <"
- << element
- << "> attribute '"
- << attributeName
- << "' can not start with whitespace."
- << std::endl;
- return false;
- }
-
- if (attributeValue.end() != trimmed.end()) {
- logger.error(lineNumber)
- << "tag <"
- << element
- << "> attribute '"
- << attributeName
- << "' can not end with whitespace."
- << std::endl;
- return false;
- }
- }
- return true;
-}
-
-constexpr const char16_t* kPackageIdentSet = u"._";
-
-bool ManifestValidator::validateManifest(const Source& source, android::ResXMLParser* parser) {
- bool error = false;
- SourceLogger logger(source);
-
- const StringPiece16 kAndroid = u"android";
- const StringPiece16 kPackage = u"package";
- const StringPiece16 kSharedUserId = u"sharedUserId";
-
- ssize_t idx;
-
- idx = parser->indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size());
- if (idx < 0) {
- logger.error(parser->getLineNumber())
- << "missing package attribute."
- << std::endl;
- error = true;
- } else {
- error |= !validateInlineAttribute(parser, idx, logger, kPackageIdentSet);
- }
-
- idx = parser->indexOfAttribute(kAndroid.data(), kAndroid.size(),
- kSharedUserId.data(), kSharedUserId.size());
- if (idx >= 0) {
- error |= !validateInlineAttribute(parser, idx, logger, kPackageIdentSet);
- }
- return !error;
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/ManifestValidator.h b/tools/aapt2/ManifestValidator.h
deleted file mode 100644
index 3188784..0000000
--- a/tools/aapt2/ManifestValidator.h
+++ /dev/null
@@ -1,55 +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_MANIFEST_VALIDATOR_H
-#define AAPT_MANIFEST_VALIDATOR_H
-
-#include "Logger.h"
-#include "Maybe.h"
-#include "Source.h"
-#include "StringPiece.h"
-
-#include <androidfw/ResourceTypes.h>
-
-namespace aapt {
-
-class ManifestValidator {
-public:
- ManifestValidator(const android::ResTable& table);
- ManifestValidator(const ManifestValidator&) = delete;
-
- bool validate(const Source& source, android::ResXMLParser* parser);
-
-private:
- bool validateManifest(const Source& source, android::ResXMLParser* parser);
-
- Maybe<StringPiece16> getAttributeInlineValue(android::ResXMLParser* parser, size_t idx);
- Maybe<StringPiece16> getAttributeValue(android::ResXMLParser* parser, size_t idx);
-
- bool validateInlineAttribute(android::ResXMLParser* parser, size_t idx,
- SourceLogger& logger, const StringPiece16& charSet);
- bool validateAttribute(android::ResXMLParser* parser, size_t idx, SourceLogger& logger,
- const StringPiece16& charSet);
- bool validateAttributeImpl(const StringPiece16& element, const StringPiece16& attributeName,
- const StringPiece16& attributeValue, const StringPiece16& charSet,
- size_t lineNumber, SourceLogger& logger);
-
- const android::ResTable& mTable;
-};
-
-} // namespace aapt
-
-#endif // AAPT_MANIFEST_VALIDATOR_H
diff --git a/tools/aapt2/Maybe.h b/tools/aapt2/Maybe.h
deleted file mode 100644
index ff6625f..0000000
--- a/tools/aapt2/Maybe.h
+++ /dev/null
@@ -1,280 +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_MAYBE_H
-#define AAPT_MAYBE_H
-
-#include <cassert>
-#include <type_traits>
-#include <utility>
-
-namespace aapt {
-
-/**
- * Either holds a valid value of type T, or holds Nothing.
- * The value is stored inline in this structure, so no
- * heap memory is used when creating a Maybe<T> object.
- */
-template <typename T>
-class Maybe {
-public:
- /**
- * Construct Nothing.
- */
- Maybe();
-
- ~Maybe();
-
- Maybe(const Maybe& rhs);
-
- template <typename U>
- Maybe(const Maybe<U>& rhs);
-
- Maybe(Maybe&& rhs);
-
- template <typename U>
- Maybe(Maybe<U>&& rhs);
-
- Maybe& operator=(const Maybe& rhs);
-
- template <typename U>
- Maybe& operator=(const Maybe<U>& rhs);
-
- Maybe& operator=(Maybe&& rhs);
-
- template <typename U>
- Maybe& operator=(Maybe<U>&& rhs);
-
- /**
- * Construct a Maybe holding a value.
- */
- Maybe(const T& value);
-
- /**
- * Construct a Maybe holding a value.
- */
- Maybe(T&& value);
-
- /**
- * True if this holds a value, false if
- * it holds Nothing.
- */
- operator bool() const;
-
- /**
- * Gets the value if one exists, or else
- * panics.
- */
- T& value();
-
- /**
- * Gets the value if one exists, or else
- * panics.
- */
- const T& value() const;
-
-private:
- template <typename U>
- friend class Maybe;
-
- template <typename U>
- Maybe& copy(const Maybe<U>& rhs);
-
- template <typename U>
- Maybe& move(Maybe<U>&& rhs);
-
- void destroy();
-
- bool mNothing;
-
- typename std::aligned_storage<sizeof(T), alignof(T)>::type mStorage;
-};
-
-template <typename T>
-Maybe<T>::Maybe()
-: mNothing(true) {
-}
-
-template <typename T>
-Maybe<T>::~Maybe() {
- if (!mNothing) {
- destroy();
- }
-}
-
-template <typename T>
-Maybe<T>::Maybe(const Maybe& rhs)
-: mNothing(rhs.mNothing) {
- if (!rhs.mNothing) {
- new (&mStorage) T(reinterpret_cast<const T&>(rhs.mStorage));
- }
-}
-
-template <typename T>
-template <typename U>
-Maybe<T>::Maybe(const Maybe<U>& rhs)
-: mNothing(rhs.mNothing) {
- if (!rhs.mNothing) {
- new (&mStorage) T(reinterpret_cast<const U&>(rhs.mStorage));
- }
-}
-
-template <typename T>
-Maybe<T>::Maybe(Maybe&& rhs)
-: mNothing(rhs.mNothing) {
- if (!rhs.mNothing) {
- rhs.mNothing = true;
-
- // Move the value from rhs.
- new (&mStorage) T(std::move(reinterpret_cast<T&>(rhs.mStorage)));
- rhs.destroy();
- }
-}
-
-template <typename T>
-template <typename U>
-Maybe<T>::Maybe(Maybe<U>&& rhs)
-: mNothing(rhs.mNothing) {
- if (!rhs.mNothing) {
- rhs.mNothing = true;
-
- // Move the value from rhs.
- new (&mStorage) T(std::move(reinterpret_cast<U&>(rhs.mStorage)));
- rhs.destroy();
- }
-}
-
-template <typename T>
-inline Maybe<T>& Maybe<T>::operator=(const Maybe& rhs) {
- // Delegate to the actual assignment.
- return copy(rhs);
-}
-
-template <typename T>
-template <typename U>
-inline Maybe<T>& Maybe<T>::operator=(const Maybe<U>& rhs) {
- return copy(rhs);
-}
-
-template <typename T>
-template <typename U>
-Maybe<T>& Maybe<T>::copy(const Maybe<U>& rhs) {
- if (mNothing && rhs.mNothing) {
- // Both are nothing, nothing to do.
- return *this;
- } else if (!mNothing && !rhs.mNothing) {
- // We both are something, so assign rhs to us.
- reinterpret_cast<T&>(mStorage) = reinterpret_cast<const U&>(rhs.mStorage);
- } else if (mNothing) {
- // We are nothing but rhs is something.
- mNothing = rhs.mNothing;
-
- // Copy the value from rhs.
- new (&mStorage) T(reinterpret_cast<const U&>(rhs.mStorage));
- } else {
- // We are something but rhs is nothing, so destroy our value.
- mNothing = rhs.mNothing;
- destroy();
- }
- return *this;
-}
-
-template <typename T>
-inline Maybe<T>& Maybe<T>::operator=(Maybe&& rhs) {
- // Delegate to the actual assignment.
- return move(std::forward<Maybe<T>>(rhs));
-}
-
-template <typename T>
-template <typename U>
-inline Maybe<T>& Maybe<T>::operator=(Maybe<U>&& rhs) {
- return move(std::forward<Maybe<U>>(rhs));
-}
-
-template <typename T>
-template <typename U>
-Maybe<T>& Maybe<T>::move(Maybe<U>&& rhs) {
- if (mNothing && rhs.mNothing) {
- // Both are nothing, nothing to do.
- return *this;
- } else if (!mNothing && !rhs.mNothing) {
- // We both are something, so move assign rhs to us.
- rhs.mNothing = true;
- reinterpret_cast<T&>(mStorage) = std::move(reinterpret_cast<U&>(rhs.mStorage));
- rhs.destroy();
- } else if (mNothing) {
- // We are nothing but rhs is something.
- mNothing = false;
- rhs.mNothing = true;
-
- // Move the value from rhs.
- new (&mStorage) T(std::move(reinterpret_cast<U&>(rhs.mStorage)));
- rhs.destroy();
- } else {
- // We are something but rhs is nothing, so destroy our value.
- mNothing = true;
- destroy();
- }
- return *this;
-}
-
-template <typename T>
-Maybe<T>::Maybe(const T& value)
-: mNothing(false) {
- new (&mStorage) T(value);
-}
-
-template <typename T>
-Maybe<T>::Maybe(T&& value)
-: mNothing(false) {
- new (&mStorage) T(std::forward<T>(value));
-}
-
-template <typename T>
-Maybe<T>::operator bool() const {
- return !mNothing;
-}
-
-template <typename T>
-T& Maybe<T>::value() {
- assert(!mNothing && "Maybe<T>::value() called on Nothing");
- return reinterpret_cast<T&>(mStorage);
-}
-
-template <typename T>
-const T& Maybe<T>::value() const {
- assert(!mNothing && "Maybe<T>::value() called on Nothing");
- return reinterpret_cast<const T&>(mStorage);
-}
-
-template <typename T>
-void Maybe<T>::destroy() {
- reinterpret_cast<T&>(mStorage).~T();
-}
-
-template <typename T>
-inline Maybe<typename std::remove_reference<T>::type> make_value(T&& value) {
- return Maybe<typename std::remove_reference<T>::type>(std::forward<T>(value));
-}
-
-template <typename T>
-inline Maybe<T> make_nothing() {
- return Maybe<T>();
-}
-
-} // namespace aapt
-
-#endif // AAPT_MAYBE_H
diff --git a/tools/aapt2/Maybe_test.cpp b/tools/aapt2/Maybe_test.cpp
deleted file mode 100644
index 71bbb94..0000000
--- a/tools/aapt2/Maybe_test.cpp
+++ /dev/null
@@ -1,121 +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.
- */
-
-#include <gtest/gtest.h>
-#include <string>
-
-#include "Maybe.h"
-
-namespace aapt {
-
-struct Dummy {
- Dummy() {
- data = new int;
- *data = 1;
- std::cerr << "Construct Dummy{0x" << (void *) this
- << "} with data=0x" << (void*) data
- << std::endl;
- }
-
- Dummy(const Dummy& rhs) {
- data = nullptr;
- if (rhs.data) {
- data = new int;
- *data = *rhs.data;
- }
- std::cerr << "CopyConstruct Dummy{0x" << (void *) this
- << "} from Dummy{0x" << (const void*) &rhs
- << "}" << std::endl;
- }
-
- Dummy(Dummy&& rhs) {
- data = rhs.data;
- rhs.data = nullptr;
- std::cerr << "MoveConstruct Dummy{0x" << (void *) this
- << "} from Dummy{0x" << (const void*) &rhs
- << "}" << std::endl;
- }
-
- Dummy& operator=(const Dummy& rhs) {
- delete data;
- data = nullptr;
-
- if (rhs.data) {
- data = new int;
- *data = *rhs.data;
- }
- std::cerr << "CopyAssign Dummy{0x" << (void *) this
- << "} from Dummy{0x" << (const void*) &rhs
- << "}" << std::endl;
- return *this;
- }
-
- Dummy& operator=(Dummy&& rhs) {
- delete data;
- data = rhs.data;
- rhs.data = nullptr;
- std::cerr << "MoveAssign Dummy{0x" << (void *) this
- << "} from Dummy{0x" << (const void*) &rhs
- << "}" << std::endl;
- return *this;
- }
-
- ~Dummy() {
- std::cerr << "Destruct Dummy{0x" << (void *) this
- << "} with data=0x" << (void*) data
- << std::endl;
- delete data;
- }
-
- int* data;
-};
-
-TEST(MaybeTest, MakeNothing) {
- Maybe<int> val = make_nothing<int>();
- EXPECT_FALSE(val);
-
- Maybe<std::string> val2 = make_nothing<std::string>();
- EXPECT_FALSE(val2);
-
- val2 = make_nothing<std::string>();
- EXPECT_FALSE(val2);
-}
-
-TEST(MaybeTest, MakeSomething) {
- Maybe<int> val = make_value(23);
- ASSERT_TRUE(val);
- EXPECT_EQ(23, val.value());
-
- Maybe<std::string> val2 = make_value(std::string("hey"));
- ASSERT_TRUE(val2);
- EXPECT_EQ(std::string("hey"), val2.value());
-}
-
-TEST(MaybeTest, Lifecycle) {
- Maybe<Dummy> val = make_nothing<Dummy>();
-
- Maybe<Dummy> val2 = make_value(Dummy());
-}
-
-TEST(MaybeTest, MoveAssign) {
- Maybe<Dummy> val;
- {
- Maybe<Dummy> val2 = Dummy();
- val = std::move(val2);
- }
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/MockResolver.h b/tools/aapt2/MockResolver.h
deleted file mode 100644
index 0c9b954..0000000
--- a/tools/aapt2/MockResolver.h
+++ /dev/null
@@ -1,93 +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_MOCK_RESOLVER_H
-#define AAPT_MOCK_RESOLVER_H
-
-#include "Maybe.h"
-#include "Resolver.h"
-#include "Resource.h"
-#include "ResourceTable.h"
-#include "ResourceTableResolver.h"
-#include "ResourceValues.h"
-#include "StringPiece.h"
-
-#include <map>
-#include <string>
-
-namespace aapt {
-
-struct MockResolver : public IResolver {
- MockResolver(const std::shared_ptr<ResourceTable>& table,
- const std::map<ResourceName, ResourceId>& items) :
- mResolver(std::make_shared<ResourceTableResolver>(
- table, std::vector<std::shared_ptr<const android::AssetManager>>())),
- mAttr(false, android::ResTable_map::TYPE_ANY), mItems(items) {
- }
-
- virtual Maybe<ResourceId> findId(const ResourceName& name) override {
- Maybe<ResourceId> result = mResolver->findId(name);
- if (result) {
- return result;
- }
-
- const auto iter = mItems.find(name);
- if (iter != mItems.end()) {
- return iter->second;
- }
- return {};
- }
-
- virtual Maybe<Entry> findAttribute(const ResourceName& name) override {
- Maybe<Entry> tableResult = mResolver->findAttribute(name);
- if (tableResult) {
- return tableResult;
- }
-
- Maybe<ResourceId> result = findId(name);
- if (result) {
- if (name.type == ResourceType::kAttr) {
- return Entry{ result.value(), &mAttr };
- } else {
- return Entry{ result.value() };
- }
- }
- return {};
- }
-
- virtual Maybe<ResourceName> findName(ResourceId resId) override {
- Maybe<ResourceName> result = mResolver->findName(resId);
- if (result) {
- return result;
- }
-
- for (auto& p : mItems) {
- if (p.second == resId) {
- return p.first;
- }
- }
- return {};
- }
-
-private:
- std::shared_ptr<ResourceTableResolver> mResolver;
- Attribute mAttr;
- std::map<ResourceName, ResourceId> mItems;
-};
-
-} // namespace aapt
-
-#endif // AAPT_MOCK_RESOLVER_H
diff --git a/tools/aapt2/NameMangler.h b/tools/aapt2/NameMangler.h
index 1e15e20..6d752bb 100644
--- a/tools/aapt2/NameMangler.h
+++ b/tools/aapt2/NameMangler.h
@@ -17,19 +17,63 @@
#ifndef AAPT_NAME_MANGLER_H
#define AAPT_NAME_MANGLER_H
+#include "Resource.h"
+
+#include "util/Maybe.h"
+
+#include <set>
#include <string>
namespace aapt {
-struct NameMangler {
+struct NameManglerPolicy {
/**
- * Mangles the name in `outName` with the `package` and stores the mangled
- * result in `outName`. The mangled name should contain symbols that are
- * illegal to define in XML, so that there will never be name mangling
- * collisions.
+ * Represents the package we are trying to build. References pointing
+ * to this package are not mangled, and mangled references inherit this package name.
*/
- static void mangle(const std::u16string& package, std::u16string* outName) {
- *outName = package + u"$" + *outName;
+ std::u16string targetPackageName;
+
+ /**
+ * We must know which references to mangle, and which to keep (android vs. com.android.support).
+ */
+ std::set<std::u16string> packagesToMangle;
+};
+
+class NameMangler {
+private:
+ NameManglerPolicy mPolicy;
+
+public:
+ NameMangler(NameManglerPolicy policy) : mPolicy(policy) {
+ }
+
+ Maybe<ResourceName> mangleName(const ResourceName& name) {
+ if (mPolicy.targetPackageName == name.package ||
+ mPolicy.packagesToMangle.count(name.package) == 0) {
+ return {};
+ }
+
+ return ResourceName{
+ mPolicy.targetPackageName,
+ name.type,
+ mangleEntry(name.package, name.entry)
+ };
+ }
+
+ bool shouldMangle(const std::u16string& package) {
+ if (package.empty() || mPolicy.targetPackageName == package) {
+ return false;
+ }
+ return mPolicy.packagesToMangle.count(package) != 0;
+ }
+
+ /**
+ * Returns a mangled name that is a combination of `name` and `package`.
+ * The mangled name should contain symbols that are illegal to define in XML,
+ * so that there will never be name mangling collisions.
+ */
+ static std::u16string mangleEntry(const std::u16string& package, const std::u16string& name) {
+ return package + u"$" + name;
}
/**
diff --git a/tools/aapt2/Png.cpp b/tools/aapt2/Png.cpp
deleted file mode 100644
index 4e9b68e..0000000
--- a/tools/aapt2/Png.cpp
+++ /dev/null
@@ -1,1280 +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.
- */
-
-#include "BigBuffer.h"
-#include "Logger.h"
-#include "Png.h"
-#include "Source.h"
-#include "Util.h"
-
-#include <androidfw/ResourceTypes.h>
-#include <iostream>
-#include <png.h>
-#include <sstream>
-#include <string>
-#include <vector>
-#include <zlib.h>
-
-namespace aapt {
-
-constexpr bool kDebug = false;
-constexpr size_t kPngSignatureSize = 8u;
-
-struct PngInfo {
- ~PngInfo() {
- for (png_bytep row : rows) {
- if (row != nullptr) {
- delete[] row;
- }
- }
-
- delete[] xDivs;
- delete[] yDivs;
- }
-
- void* serialize9Patch() {
- void* serialized = android::Res_png_9patch::serialize(info9Patch, xDivs, yDivs,
- colors.data());
- reinterpret_cast<android::Res_png_9patch*>(serialized)->deviceToFile();
- return serialized;
- }
-
- uint32_t width = 0;
- uint32_t height = 0;
- std::vector<png_bytep> rows;
-
- bool is9Patch = false;
- android::Res_png_9patch info9Patch;
- int32_t* xDivs = nullptr;
- int32_t* yDivs = nullptr;
- std::vector<uint32_t> colors;
-
- // Layout padding.
- bool haveLayoutBounds = false;
- int32_t layoutBoundsLeft;
- int32_t layoutBoundsTop;
- int32_t layoutBoundsRight;
- int32_t layoutBoundsBottom;
-
- // Round rect outline description.
- int32_t outlineInsetsLeft;
- int32_t outlineInsetsTop;
- int32_t outlineInsetsRight;
- int32_t outlineInsetsBottom;
- float outlineRadius;
- uint8_t outlineAlpha;
-};
-
-static void readDataFromStream(png_structp readPtr, png_bytep data, png_size_t length) {
- std::istream* input = reinterpret_cast<std::istream*>(png_get_io_ptr(readPtr));
- if (!input->read(reinterpret_cast<char*>(data), length)) {
- png_error(readPtr, strerror(errno));
- }
-}
-
-static void writeDataToStream(png_structp writePtr, png_bytep data, png_size_t length) {
- BigBuffer* outBuffer = reinterpret_cast<BigBuffer*>(png_get_io_ptr(writePtr));
- png_bytep buf = outBuffer->nextBlock<png_byte>(length);
- memcpy(buf, data, length);
-}
-
-static void flushDataToStream(png_structp /*writePtr*/) {
-}
-
-static void logWarning(png_structp readPtr, png_const_charp warningMessage) {
- SourceLogger* logger = reinterpret_cast<SourceLogger*>(png_get_error_ptr(readPtr));
- logger->warn() << warningMessage << "." << std::endl;
-}
-
-
-static bool readPng(png_structp readPtr, png_infop infoPtr, PngInfo* outInfo,
- std::string* outError) {
- if (setjmp(png_jmpbuf(readPtr))) {
- *outError = "failed reading png";
- return false;
- }
-
- png_set_sig_bytes(readPtr, kPngSignatureSize);
- png_read_info(readPtr, infoPtr);
-
- int colorType, bitDepth, interlaceType, compressionType;
- png_get_IHDR(readPtr, infoPtr, &outInfo->width, &outInfo->height, &bitDepth, &colorType,
- &interlaceType, &compressionType, nullptr);
-
- if (colorType == PNG_COLOR_TYPE_PALETTE) {
- png_set_palette_to_rgb(readPtr);
- }
-
- if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) {
- png_set_expand_gray_1_2_4_to_8(readPtr);
- }
-
- if (png_get_valid(readPtr, infoPtr, PNG_INFO_tRNS)) {
- png_set_tRNS_to_alpha(readPtr);
- }
-
- if (bitDepth == 16) {
- png_set_strip_16(readPtr);
- }
-
- if (!(colorType & PNG_COLOR_MASK_ALPHA)) {
- png_set_add_alpha(readPtr, 0xFF, PNG_FILLER_AFTER);
- }
-
- if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
- png_set_gray_to_rgb(readPtr);
- }
-
- png_set_interlace_handling(readPtr);
- png_read_update_info(readPtr, infoPtr);
-
- const uint32_t rowBytes = png_get_rowbytes(readPtr, infoPtr);
- outInfo->rows.resize(outInfo->height);
- for (size_t i = 0; i < outInfo->height; i++) {
- outInfo->rows[i] = new png_byte[rowBytes];
- }
-
- png_read_image(readPtr, outInfo->rows.data());
- png_read_end(readPtr, infoPtr);
- return true;
-}
-
-static void checkNinePatchSerialization(android::Res_png_9patch* inPatch, void* data) {
- size_t patchSize = inPatch->serializedSize();
- void* newData = malloc(patchSize);
- memcpy(newData, data, patchSize);
- android::Res_png_9patch* outPatch = inPatch->deserialize(newData);
- outPatch->fileToDevice();
- // deserialization is done in place, so outPatch == newData
- assert(outPatch == newData);
- assert(outPatch->numXDivs == inPatch->numXDivs);
- assert(outPatch->numYDivs == inPatch->numYDivs);
- assert(outPatch->paddingLeft == inPatch->paddingLeft);
- assert(outPatch->paddingRight == inPatch->paddingRight);
- assert(outPatch->paddingTop == inPatch->paddingTop);
- assert(outPatch->paddingBottom == inPatch->paddingBottom);
-/* for (int i = 0; i < outPatch->numXDivs; i++) {
- assert(outPatch->getXDivs()[i] == inPatch->getXDivs()[i]);
- }
- for (int i = 0; i < outPatch->numYDivs; i++) {
- assert(outPatch->getYDivs()[i] == inPatch->getYDivs()[i]);
- }
- for (int i = 0; i < outPatch->numColors; i++) {
- assert(outPatch->getColors()[i] == inPatch->getColors()[i]);
- }*/
- free(newData);
-}
-
-/*static void dump_image(int w, int h, const png_byte* const* rows, int color_type) {
- int i, j, rr, gg, bb, aa;
-
- int bpp;
- if (color_type == PNG_COLOR_TYPE_PALETTE || color_type == PNG_COLOR_TYPE_GRAY) {
- bpp = 1;
- } else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
- bpp = 2;
- } else if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
- // We use a padding byte even when there is no alpha
- bpp = 4;
- } else {
- printf("Unknown color type %d.\n", color_type);
- }
-
- for (j = 0; j < h; j++) {
- const png_byte* row = rows[j];
- for (i = 0; i < w; i++) {
- rr = row[0];
- gg = row[1];
- bb = row[2];
- aa = row[3];
- row += bpp;
-
- if (i == 0) {
- printf("Row %d:", j);
- }
- switch (bpp) {
- case 1:
- printf(" (%d)", rr);
- break;
- case 2:
- printf(" (%d %d", rr, gg);
- break;
- case 3:
- printf(" (%d %d %d)", rr, gg, bb);
- break;
- case 4:
- printf(" (%d %d %d %d)", rr, gg, bb, aa);
- break;
- }
- if (i == (w - 1)) {
- printf("\n");
- }
- }
- }
-}*/
-
-#define MAX(a,b) ((a)>(b)?(a):(b))
-#define ABS(a) ((a)<0?-(a):(a))
-
-static void analyze_image(SourceLogger* logger, const PngInfo& imageInfo, int grayscaleTolerance,
- png_colorp rgbPalette, png_bytep alphaPalette,
- int *paletteEntries, bool *hasTransparency, int *colorType,
- png_bytepp outRows) {
- int w = imageInfo.width;
- int h = imageInfo.height;
- int i, j, rr, gg, bb, aa, idx;
- uint32_t colors[256], col;
- int num_colors = 0;
- int maxGrayDeviation = 0;
-
- bool isOpaque = true;
- bool isPalette = true;
- bool isGrayscale = true;
-
- // Scan the entire image and determine if:
- // 1. Every pixel has R == G == B (grayscale)
- // 2. Every pixel has A == 255 (opaque)
- // 3. There are no more than 256 distinct RGBA colors
-
- if (kDebug) {
- printf("Initial image data:\n");
- //dump_image(w, h, imageInfo.rows.data(), PNG_COLOR_TYPE_RGB_ALPHA);
- }
-
- for (j = 0; j < h; j++) {
- const png_byte* row = imageInfo.rows[j];
- png_bytep out = outRows[j];
- for (i = 0; i < w; i++) {
- rr = *row++;
- gg = *row++;
- bb = *row++;
- aa = *row++;
-
- int odev = maxGrayDeviation;
- maxGrayDeviation = MAX(ABS(rr - gg), maxGrayDeviation);
- maxGrayDeviation = MAX(ABS(gg - bb), maxGrayDeviation);
- maxGrayDeviation = MAX(ABS(bb - rr), maxGrayDeviation);
- if (maxGrayDeviation > odev) {
- if (kDebug) {
- printf("New max dev. = %d at pixel (%d, %d) = (%d %d %d %d)\n",
- maxGrayDeviation, i, j, rr, gg, bb, aa);
- }
- }
-
- // Check if image is really grayscale
- if (isGrayscale) {
- if (rr != gg || rr != bb) {
- if (kDebug) {
- printf("Found a non-gray pixel at %d, %d = (%d %d %d %d)\n",
- i, j, rr, gg, bb, aa);
- }
- isGrayscale = false;
- }
- }
-
- // Check if image is really opaque
- if (isOpaque) {
- if (aa != 0xff) {
- if (kDebug) {
- printf("Found a non-opaque pixel at %d, %d = (%d %d %d %d)\n",
- i, j, rr, gg, bb, aa);
- }
- isOpaque = false;
- }
- }
-
- // Check if image is really <= 256 colors
- if (isPalette) {
- col = (uint32_t) ((rr << 24) | (gg << 16) | (bb << 8) | aa);
- bool match = false;
- for (idx = 0; idx < num_colors; idx++) {
- if (colors[idx] == col) {
- match = true;
- break;
- }
- }
-
- // Write the palette index for the pixel to outRows optimistically
- // We might overwrite it later if we decide to encode as gray or
- // gray + alpha
- *out++ = idx;
- if (!match) {
- if (num_colors == 256) {
- if (kDebug) {
- printf("Found 257th color at %d, %d\n", i, j);
- }
- isPalette = false;
- } else {
- colors[num_colors++] = col;
- }
- }
- }
- }
- }
-
- *paletteEntries = 0;
- *hasTransparency = !isOpaque;
- int bpp = isOpaque ? 3 : 4;
- int paletteSize = w * h + bpp * num_colors;
-
- if (kDebug) {
- printf("isGrayscale = %s\n", isGrayscale ? "true" : "false");
- printf("isOpaque = %s\n", isOpaque ? "true" : "false");
- printf("isPalette = %s\n", isPalette ? "true" : "false");
- printf("Size w/ palette = %d, gray+alpha = %d, rgb(a) = %d\n",
- paletteSize, 2 * w * h, bpp * w * h);
- printf("Max gray deviation = %d, tolerance = %d\n", maxGrayDeviation, grayscaleTolerance);
- }
-
- // Choose the best color type for the image.
- // 1. Opaque gray - use COLOR_TYPE_GRAY at 1 byte/pixel
- // 2. Gray + alpha - use COLOR_TYPE_PALETTE if the number of distinct combinations
- // is sufficiently small, otherwise use COLOR_TYPE_GRAY_ALPHA
- // 3. RGB(A) - use COLOR_TYPE_PALETTE if the number of distinct colors is sufficiently
- // small, otherwise use COLOR_TYPE_RGB{_ALPHA}
- if (isGrayscale) {
- if (isOpaque) {
- *colorType = PNG_COLOR_TYPE_GRAY; // 1 byte/pixel
- } else {
- // Use a simple heuristic to determine whether using a palette will
- // save space versus using gray + alpha for each pixel.
- // This doesn't take into account chunk overhead, filtering, LZ
- // compression, etc.
- if (isPalette && (paletteSize < 2 * w * h)) {
- *colorType = PNG_COLOR_TYPE_PALETTE; // 1 byte/pixel + 4 bytes/color
- } else {
- *colorType = PNG_COLOR_TYPE_GRAY_ALPHA; // 2 bytes per pixel
- }
- }
- } else if (isPalette && (paletteSize < bpp * w * h)) {
- *colorType = PNG_COLOR_TYPE_PALETTE;
- } else {
- if (maxGrayDeviation <= grayscaleTolerance) {
- logger->note() << "forcing image to gray (max deviation = " << maxGrayDeviation
- << ")."
- << std::endl;
- *colorType = isOpaque ? PNG_COLOR_TYPE_GRAY : PNG_COLOR_TYPE_GRAY_ALPHA;
- } else {
- *colorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
- }
- }
-
- // Perform postprocessing of the image or palette data based on the final
- // color type chosen
-
- if (*colorType == PNG_COLOR_TYPE_PALETTE) {
- // Create separate RGB and Alpha palettes and set the number of colors
- *paletteEntries = num_colors;
-
- // Create the RGB and alpha palettes
- for (int idx = 0; idx < num_colors; idx++) {
- col = colors[idx];
- rgbPalette[idx].red = (png_byte) ((col >> 24) & 0xff);
- rgbPalette[idx].green = (png_byte) ((col >> 16) & 0xff);
- rgbPalette[idx].blue = (png_byte) ((col >> 8) & 0xff);
- alphaPalette[idx] = (png_byte) (col & 0xff);
- }
- } else if (*colorType == PNG_COLOR_TYPE_GRAY || *colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
- // If the image is gray or gray + alpha, compact the pixels into outRows
- for (j = 0; j < h; j++) {
- const png_byte* row = imageInfo.rows[j];
- png_bytep out = outRows[j];
- for (i = 0; i < w; i++) {
- rr = *row++;
- gg = *row++;
- bb = *row++;
- aa = *row++;
-
- if (isGrayscale) {
- *out++ = rr;
- } else {
- *out++ = (png_byte) (rr * 0.2126f + gg * 0.7152f + bb * 0.0722f);
- }
- if (!isOpaque) {
- *out++ = aa;
- }
- }
- }
- }
-}
-
-static bool writePng(png_structp writePtr, png_infop infoPtr, PngInfo* info,
- int grayScaleTolerance, SourceLogger* logger, std::string* outError) {
- if (setjmp(png_jmpbuf(writePtr))) {
- *outError = "failed to write png";
- return false;
- }
-
- uint32_t width, height;
- int colorType, bitDepth, interlaceType, compressionType;
-
- png_unknown_chunk unknowns[3];
- unknowns[0].data = nullptr;
- unknowns[1].data = nullptr;
- unknowns[2].data = nullptr;
-
- png_bytepp outRows = (png_bytepp) malloc((int) info->height * sizeof(png_bytep));
- if (outRows == (png_bytepp) 0) {
- printf("Can't allocate output buffer!\n");
- exit(1);
- }
- for (uint32_t i = 0; i < info->height; i++) {
- outRows[i] = (png_bytep) malloc(2 * (int) info->width);
- if (outRows[i] == (png_bytep) 0) {
- printf("Can't allocate output buffer!\n");
- exit(1);
- }
- }
-
- png_set_compression_level(writePtr, Z_BEST_COMPRESSION);
-
- if (kDebug) {
- logger->note() << "writing image: w = " << info->width
- << ", h = " << info->height
- << std::endl;
- }
-
- png_color rgbPalette[256];
- png_byte alphaPalette[256];
- bool hasTransparency;
- int paletteEntries;
-
- analyze_image(logger, *info, grayScaleTolerance, rgbPalette, alphaPalette,
- &paletteEntries, &hasTransparency, &colorType, outRows);
-
- // If the image is a 9-patch, we need to preserve it as a ARGB file to make
- // sure the pixels will not be pre-dithered/clamped until we decide they are
- if (info->is9Patch && (colorType == PNG_COLOR_TYPE_RGB ||
- colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_PALETTE)) {
- colorType = PNG_COLOR_TYPE_RGB_ALPHA;
- }
-
- if (kDebug) {
- switch (colorType) {
- case PNG_COLOR_TYPE_PALETTE:
- logger->note() << "has " << paletteEntries
- << " colors" << (hasTransparency ? " (with alpha)" : "")
- << ", using PNG_COLOR_TYPE_PALLETTE."
- << std::endl;
- break;
- case PNG_COLOR_TYPE_GRAY:
- logger->note() << "is opaque gray, using PNG_COLOR_TYPE_GRAY." << std::endl;
- break;
- case PNG_COLOR_TYPE_GRAY_ALPHA:
- logger->note() << "is gray + alpha, using PNG_COLOR_TYPE_GRAY_ALPHA." << std::endl;
- break;
- case PNG_COLOR_TYPE_RGB:
- logger->note() << "is opaque RGB, using PNG_COLOR_TYPE_RGB." << std::endl;
- break;
- case PNG_COLOR_TYPE_RGB_ALPHA:
- logger->note() << "is RGB + alpha, using PNG_COLOR_TYPE_RGB_ALPHA." << std::endl;
- break;
- }
- }
-
- png_set_IHDR(writePtr, infoPtr, info->width, info->height, 8, colorType,
- PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
-
- if (colorType == PNG_COLOR_TYPE_PALETTE) {
- png_set_PLTE(writePtr, infoPtr, rgbPalette, paletteEntries);
- if (hasTransparency) {
- png_set_tRNS(writePtr, infoPtr, alphaPalette, paletteEntries, (png_color_16p) 0);
- }
- png_set_filter(writePtr, 0, PNG_NO_FILTERS);
- } else {
- png_set_filter(writePtr, 0, PNG_ALL_FILTERS);
- }
-
- if (info->is9Patch) {
- int chunkCount = 2 + (info->haveLayoutBounds ? 1 : 0);
- int pIndex = info->haveLayoutBounds ? 2 : 1;
- int bIndex = 1;
- int oIndex = 0;
-
- // Chunks ordered thusly because older platforms depend on the base 9 patch data being last
- png_bytep chunkNames = info->haveLayoutBounds
- ? (png_bytep)"npOl\0npLb\0npTc\0"
- : (png_bytep)"npOl\0npTc";
-
- // base 9 patch data
- if (kDebug) {
- logger->note() << "adding 9-patch info..." << std::endl;
- }
- strcpy((char*)unknowns[pIndex].name, "npTc");
- unknowns[pIndex].data = (png_byte*) info->serialize9Patch();
- unknowns[pIndex].size = info->info9Patch.serializedSize();
- // TODO: remove the check below when everything works
- checkNinePatchSerialization(&info->info9Patch, unknowns[pIndex].data);
-
- // automatically generated 9 patch outline data
- int chunkSize = sizeof(png_uint_32) * 6;
- strcpy((char*)unknowns[oIndex].name, "npOl");
- unknowns[oIndex].data = (png_byte*) calloc(chunkSize, 1);
- png_byte outputData[chunkSize];
- memcpy(&outputData, &info->outlineInsetsLeft, 4 * sizeof(png_uint_32));
- ((float*) outputData)[4] = info->outlineRadius;
- ((png_uint_32*) outputData)[5] = info->outlineAlpha;
- memcpy(unknowns[oIndex].data, &outputData, chunkSize);
- unknowns[oIndex].size = chunkSize;
-
- // optional optical inset / layout bounds data
- if (info->haveLayoutBounds) {
- int chunkSize = sizeof(png_uint_32) * 4;
- strcpy((char*)unknowns[bIndex].name, "npLb");
- unknowns[bIndex].data = (png_byte*) calloc(chunkSize, 1);
- memcpy(unknowns[bIndex].data, &info->layoutBoundsLeft, chunkSize);
- unknowns[bIndex].size = chunkSize;
- }
-
- for (int i = 0; i < chunkCount; i++) {
- unknowns[i].location = PNG_HAVE_PLTE;
- }
- png_set_keep_unknown_chunks(writePtr, PNG_HANDLE_CHUNK_ALWAYS,
- chunkNames, chunkCount);
- png_set_unknown_chunks(writePtr, infoPtr, unknowns, chunkCount);
-
-#if PNG_LIBPNG_VER < 10600
- // Deal with unknown chunk location bug in 1.5.x and earlier.
- png_set_unknown_chunk_location(writePtr, infoPtr, 0, PNG_HAVE_PLTE);
- if (info->haveLayoutBounds) {
- png_set_unknown_chunk_location(writePtr, infoPtr, 1, PNG_HAVE_PLTE);
- }
-#endif
- }
-
- png_write_info(writePtr, infoPtr);
-
- png_bytepp rows;
- if (colorType == PNG_COLOR_TYPE_RGB || colorType == PNG_COLOR_TYPE_RGB_ALPHA) {
- if (colorType == PNG_COLOR_TYPE_RGB) {
- png_set_filler(writePtr, 0, PNG_FILLER_AFTER);
- }
- rows = info->rows.data();
- } else {
- rows = outRows;
- }
- png_write_image(writePtr, rows);
-
- if (kDebug) {
- printf("Final image data:\n");
- //dump_image(info->width, info->height, rows, colorType);
- }
-
- png_write_end(writePtr, infoPtr);
-
- for (uint32_t i = 0; i < info->height; i++) {
- free(outRows[i]);
- }
- free(outRows);
- free(unknowns[0].data);
- free(unknowns[1].data);
- free(unknowns[2].data);
-
- png_get_IHDR(writePtr, infoPtr, &width, &height, &bitDepth, &colorType, &interlaceType,
- &compressionType, nullptr);
-
- if (kDebug) {
- logger->note() << "image written: w = " << width << ", h = " << height
- << ", d = " << bitDepth << ", colors = " << colorType
- << ", inter = " << interlaceType << ", comp = " << compressionType
- << std::endl;
- }
- return true;
-}
-
-constexpr uint32_t kColorWhite = 0xffffffffu;
-constexpr uint32_t kColorTick = 0xff000000u;
-constexpr uint32_t kColorLayoutBoundsTick = 0xff0000ffu;
-
-enum class TickType {
- kNone,
- kTick,
- kLayoutBounds,
- kBoth
-};
-
-static TickType tickType(png_bytep p, bool transparent, const char** outError) {
- png_uint_32 color = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
-
- if (transparent) {
- if (p[3] == 0) {
- return TickType::kNone;
- }
- if (color == kColorLayoutBoundsTick) {
- return TickType::kLayoutBounds;
- }
- if (color == kColorTick) {
- return TickType::kTick;
- }
-
- // Error cases
- if (p[3] != 0xff) {
- *outError = "Frame pixels must be either solid or transparent "
- "(not intermediate alphas)";
- return TickType::kNone;
- }
-
- if (p[0] != 0 || p[1] != 0 || p[2] != 0) {
- *outError = "Ticks in transparent frame must be black or red";
- }
- return TickType::kTick;
- }
-
- if (p[3] != 0xFF) {
- *outError = "White frame must be a solid color (no alpha)";
- }
- if (color == kColorWhite) {
- return TickType::kNone;
- }
- if (color == kColorTick) {
- return TickType::kTick;
- }
- if (color == kColorLayoutBoundsTick) {
- return TickType::kLayoutBounds;
- }
-
- if (p[0] != 0 || p[1] != 0 || p[2] != 0) {
- *outError = "Ticks in white frame must be black or red";
- return TickType::kNone;
- }
- return TickType::kTick;
-}
-
-enum class TickState {
- kStart,
- kInside1,
- kOutside1
-};
-
-static bool getHorizontalTicks(png_bytep row, int width, bool transparent, bool required,
- int32_t* outLeft, int32_t* outRight, const char** outError,
- uint8_t* outDivs, bool multipleAllowed) {
- *outLeft = *outRight = -1;
- TickState state = TickState::kStart;
- bool found = false;
-
- for (int i = 1; i < width - 1; i++) {
- if (tickType(row+i*4, transparent, outError) == TickType::kTick) {
- if (state == TickState::kStart ||
- (state == TickState::kOutside1 && multipleAllowed)) {
- *outLeft = i-1;
- *outRight = width-2;
- found = true;
- if (outDivs != NULL) {
- *outDivs += 2;
- }
- state = TickState::kInside1;
- } else if (state == TickState::kOutside1) {
- *outError = "Can't have more than one marked region along edge";
- *outLeft = i;
- return false;
- }
- } else if (!*outError) {
- if (state == TickState::kInside1) {
- // We're done with this div. Move on to the next.
- *outRight = i-1;
- outRight += 2;
- outLeft += 2;
- state = TickState::kOutside1;
- }
- } else {
- *outLeft = i;
- return false;
- }
- }
-
- if (required && !found) {
- *outError = "No marked region found along edge";
- *outLeft = -1;
- return false;
- }
- return true;
-}
-
-static bool getVerticalTicks(png_bytepp rows, int offset, int height, bool transparent,
- bool required, int32_t* outTop, int32_t* outBottom,
- const char** outError, uint8_t* outDivs, bool multipleAllowed) {
- *outTop = *outBottom = -1;
- TickState state = TickState::kStart;
- bool found = false;
-
- for (int i = 1; i < height - 1; i++) {
- if (tickType(rows[i]+offset, transparent, outError) == TickType::kTick) {
- if (state == TickState::kStart ||
- (state == TickState::kOutside1 && multipleAllowed)) {
- *outTop = i-1;
- *outBottom = height-2;
- found = true;
- if (outDivs != NULL) {
- *outDivs += 2;
- }
- state = TickState::kInside1;
- } else if (state == TickState::kOutside1) {
- *outError = "Can't have more than one marked region along edge";
- *outTop = i;
- return false;
- }
- } else if (!*outError) {
- if (state == TickState::kInside1) {
- // We're done with this div. Move on to the next.
- *outBottom = i-1;
- outTop += 2;
- outBottom += 2;
- state = TickState::kOutside1;
- }
- } else {
- *outTop = i;
- return false;
- }
- }
-
- if (required && !found) {
- *outError = "No marked region found along edge";
- *outTop = -1;
- return false;
- }
- return true;
-}
-
-static bool getHorizontalLayoutBoundsTicks(png_bytep row, int width, bool transparent,
- bool /* required */, int32_t* outLeft,
- int32_t* outRight, const char** outError) {
- *outLeft = *outRight = 0;
-
- // Look for left tick
- if (tickType(row + 4, transparent, outError) == TickType::kLayoutBounds) {
- // Starting with a layout padding tick
- int i = 1;
- while (i < width - 1) {
- (*outLeft)++;
- i++;
- if (tickType(row + i * 4, transparent, outError) != TickType::kLayoutBounds) {
- break;
- }
- }
- }
-
- // Look for right tick
- if (tickType(row + (width - 2) * 4, transparent, outError) == TickType::kLayoutBounds) {
- // Ending with a layout padding tick
- int i = width - 2;
- while (i > 1) {
- (*outRight)++;
- i--;
- if (tickType(row+i*4, transparent, outError) != TickType::kLayoutBounds) {
- break;
- }
- }
- }
- return true;
-}
-
-static bool getVerticalLayoutBoundsTicks(png_bytepp rows, int offset, int height, bool transparent,
- bool /* required */, int32_t* outTop, int32_t* outBottom,
- const char** outError) {
- *outTop = *outBottom = 0;
-
- // Look for top tick
- if (tickType(rows[1] + offset, transparent, outError) == TickType::kLayoutBounds) {
- // Starting with a layout padding tick
- int i = 1;
- while (i < height - 1) {
- (*outTop)++;
- i++;
- if (tickType(rows[i] + offset, transparent, outError) != TickType::kLayoutBounds) {
- break;
- }
- }
- }
-
- // Look for bottom tick
- if (tickType(rows[height - 2] + offset, transparent, outError) == TickType::kLayoutBounds) {
- // Ending with a layout padding tick
- int i = height - 2;
- while (i > 1) {
- (*outBottom)++;
- i--;
- if (tickType(rows[i] + offset, transparent, outError) != TickType::kLayoutBounds) {
- break;
- }
- }
- }
- return true;
-}
-
-static void findMaxOpacity(png_bytepp rows, int startX, int startY, int endX, int endY,
- int dX, int dY, int* outInset) {
- uint8_t maxOpacity = 0;
- int inset = 0;
- *outInset = 0;
- for (int x = startX, y = startY; x != endX && y != endY; x += dX, y += dY, inset++) {
- png_byte* color = rows[y] + x * 4;
- uint8_t opacity = color[3];
- if (opacity > maxOpacity) {
- maxOpacity = opacity;
- *outInset = inset;
- }
- if (opacity == 0xff) return;
- }
-}
-
-static uint8_t maxAlphaOverRow(png_bytep row, int startX, int endX) {
- uint8_t maxAlpha = 0;
- for (int x = startX; x < endX; x++) {
- uint8_t alpha = (row + x * 4)[3];
- if (alpha > maxAlpha) maxAlpha = alpha;
- }
- return maxAlpha;
-}
-
-static uint8_t maxAlphaOverCol(png_bytepp rows, int offsetX, int startY, int endY) {
- uint8_t maxAlpha = 0;
- for (int y = startY; y < endY; y++) {
- uint8_t alpha = (rows[y] + offsetX * 4)[3];
- if (alpha > maxAlpha) maxAlpha = alpha;
- }
- return maxAlpha;
-}
-
-static void getOutline(PngInfo* image) {
- int midX = image->width / 2;
- int midY = image->height / 2;
- int endX = image->width - 2;
- int endY = image->height - 2;
-
- // find left and right extent of nine patch content on center row
- if (image->width > 4) {
- findMaxOpacity(image->rows.data(), 1, midY, midX, -1, 1, 0, &image->outlineInsetsLeft);
- findMaxOpacity(image->rows.data(), endX, midY, midX, -1, -1, 0,
- &image->outlineInsetsRight);
- } else {
- image->outlineInsetsLeft = 0;
- image->outlineInsetsRight = 0;
- }
-
- // find top and bottom extent of nine patch content on center column
- if (image->height > 4) {
- findMaxOpacity(image->rows.data(), midX, 1, -1, midY, 0, 1, &image->outlineInsetsTop);
- findMaxOpacity(image->rows.data(), midX, endY, -1, midY, 0, -1,
- &image->outlineInsetsBottom);
- } else {
- image->outlineInsetsTop = 0;
- image->outlineInsetsBottom = 0;
- }
-
- int innerStartX = 1 + image->outlineInsetsLeft;
- int innerStartY = 1 + image->outlineInsetsTop;
- int innerEndX = endX - image->outlineInsetsRight;
- int innerEndY = endY - image->outlineInsetsBottom;
- int innerMidX = (innerEndX + innerStartX) / 2;
- int innerMidY = (innerEndY + innerStartY) / 2;
-
- // assuming the image is a round rect, compute the radius by marching
- // diagonally from the top left corner towards the center
- image->outlineAlpha = std::max(
- maxAlphaOverRow(image->rows[innerMidY], innerStartX, innerEndX),
- maxAlphaOverCol(image->rows.data(), innerMidX, innerStartY, innerStartY));
-
- int diagonalInset = 0;
- findMaxOpacity(image->rows.data(), innerStartX, innerStartY, innerMidX, innerMidY, 1, 1,
- &diagonalInset);
-
- /* Determine source radius based upon inset:
- * sqrt(r^2 + r^2) = sqrt(i^2 + i^2) + r
- * sqrt(2) * r = sqrt(2) * i + r
- * (sqrt(2) - 1) * r = sqrt(2) * i
- * r = sqrt(2) / (sqrt(2) - 1) * i
- */
- image->outlineRadius = 3.4142f * diagonalInset;
-
- if (kDebug) {
- printf("outline insets %d %d %d %d, rad %f, alpha %x\n",
- image->outlineInsetsLeft,
- image->outlineInsetsTop,
- image->outlineInsetsRight,
- image->outlineInsetsBottom,
- image->outlineRadius,
- image->outlineAlpha);
- }
-}
-
-static uint32_t getColor(png_bytepp rows, int left, int top, int right, int bottom) {
- png_bytep color = rows[top] + left*4;
-
- if (left > right || top > bottom) {
- return android::Res_png_9patch::TRANSPARENT_COLOR;
- }
-
- while (top <= bottom) {
- for (int i = left; i <= right; i++) {
- png_bytep p = rows[top]+i*4;
- if (color[3] == 0) {
- if (p[3] != 0) {
- return android::Res_png_9patch::NO_COLOR;
- }
- } else if (p[0] != color[0] || p[1] != color[1] ||
- p[2] != color[2] || p[3] != color[3]) {
- return android::Res_png_9patch::NO_COLOR;
- }
- }
- top++;
- }
-
- if (color[3] == 0) {
- return android::Res_png_9patch::TRANSPARENT_COLOR;
- }
- return (color[3]<<24) | (color[0]<<16) | (color[1]<<8) | color[2];
-}
-
-static bool do9Patch(PngInfo* image, std::string* outError) {
- image->is9Patch = true;
-
- int W = image->width;
- int H = image->height;
- int i, j;
-
- const int maxSizeXDivs = W * sizeof(int32_t);
- const int maxSizeYDivs = H * sizeof(int32_t);
- int32_t* xDivs = image->xDivs = new int32_t[W];
- int32_t* yDivs = image->yDivs = new int32_t[H];
- uint8_t numXDivs = 0;
- uint8_t numYDivs = 0;
-
- int8_t numColors;
- int numRows;
- int numCols;
- int top;
- int left;
- int right;
- int bottom;
- memset(xDivs, -1, maxSizeXDivs);
- memset(yDivs, -1, maxSizeYDivs);
- image->info9Patch.paddingLeft = image->info9Patch.paddingRight = -1;
- image->info9Patch.paddingTop = image->info9Patch.paddingBottom = -1;
- image->layoutBoundsLeft = image->layoutBoundsRight = 0;
- image->layoutBoundsTop = image->layoutBoundsBottom = 0;
-
- png_bytep p = image->rows[0];
- bool transparent = p[3] == 0;
- bool hasColor = false;
-
- const char* errorMsg = nullptr;
- int errorPixel = -1;
- const char* errorEdge = nullptr;
-
- int colorIndex = 0;
- std::vector<png_bytep> newRows;
-
- // Validate size...
- if (W < 3 || H < 3) {
- errorMsg = "Image must be at least 3x3 (1x1 without frame) pixels";
- goto getout;
- }
-
- // Validate frame...
- if (!transparent &&
- (p[0] != 0xFF || p[1] != 0xFF || p[2] != 0xFF || p[3] != 0xFF)) {
- errorMsg = "Must have one-pixel frame that is either transparent or white";
- goto getout;
- }
-
- // Find left and right of sizing areas...
- if (!getHorizontalTicks(p, W, transparent, true, &xDivs[0], &xDivs[1], &errorMsg, &numXDivs,
- true)) {
- errorPixel = xDivs[0];
- errorEdge = "top";
- goto getout;
- }
-
- // Find top and bottom of sizing areas...
- if (!getVerticalTicks(image->rows.data(), 0, H, transparent, true, &yDivs[0], &yDivs[1],
- &errorMsg, &numYDivs, true)) {
- errorPixel = yDivs[0];
- errorEdge = "left";
- goto getout;
- }
-
- // Copy patch size data into image...
- image->info9Patch.numXDivs = numXDivs;
- image->info9Patch.numYDivs = numYDivs;
-
- // Find left and right of padding area...
- if (!getHorizontalTicks(image->rows[H-1], W, transparent, false,
- &image->info9Patch.paddingLeft, &image->info9Patch.paddingRight,
- &errorMsg, nullptr, false)) {
- errorPixel = image->info9Patch.paddingLeft;
- errorEdge = "bottom";
- goto getout;
- }
-
- // Find top and bottom of padding area...
- if (!getVerticalTicks(image->rows.data(), (W-1)*4, H, transparent, false,
- &image->info9Patch.paddingTop, &image->info9Patch.paddingBottom,
- &errorMsg, nullptr, false)) {
- errorPixel = image->info9Patch.paddingTop;
- errorEdge = "right";
- goto getout;
- }
-
- // Find left and right of layout padding...
- getHorizontalLayoutBoundsTicks(image->rows[H-1], W, transparent, false,
- &image->layoutBoundsLeft, &image->layoutBoundsRight, &errorMsg);
-
- getVerticalLayoutBoundsTicks(image->rows.data(), (W-1)*4, H, transparent, false,
- &image->layoutBoundsTop, &image->layoutBoundsBottom, &errorMsg);
-
- image->haveLayoutBounds = image->layoutBoundsLeft != 0
- || image->layoutBoundsRight != 0
- || image->layoutBoundsTop != 0
- || image->layoutBoundsBottom != 0;
-
- if (image->haveLayoutBounds) {
- if (kDebug) {
- printf("layoutBounds=%d %d %d %d\n", image->layoutBoundsLeft, image->layoutBoundsTop,
- image->layoutBoundsRight, image->layoutBoundsBottom);
- }
- }
-
- // use opacity of pixels to estimate the round rect outline
- getOutline(image);
-
- // If padding is not yet specified, take values from size.
- if (image->info9Patch.paddingLeft < 0) {
- image->info9Patch.paddingLeft = xDivs[0];
- image->info9Patch.paddingRight = W - 2 - xDivs[1];
- } else {
- // Adjust value to be correct!
- image->info9Patch.paddingRight = W - 2 - image->info9Patch.paddingRight;
- }
- if (image->info9Patch.paddingTop < 0) {
- image->info9Patch.paddingTop = yDivs[0];
- image->info9Patch.paddingBottom = H - 2 - yDivs[1];
- } else {
- // Adjust value to be correct!
- image->info9Patch.paddingBottom = H - 2 - image->info9Patch.paddingBottom;
- }
-
-/* if (kDebug) {
- printf("Size ticks for %s: x0=%d, x1=%d, y0=%d, y1=%d\n", imageName,
- xDivs[0], xDivs[1],
- yDivs[0], yDivs[1]);
- printf("padding ticks for %s: l=%d, r=%d, t=%d, b=%d\n", imageName,
- image->info9Patch.paddingLeft, image->info9Patch.paddingRight,
- image->info9Patch.paddingTop, image->info9Patch.paddingBottom);
- }*/
-
- // Remove frame from image.
- newRows.resize(H - 2);
- for (i = 0; i < H - 2; i++) {
- newRows[i] = image->rows[i + 1];
- memmove(newRows[i], newRows[i] + 4, (W - 2) * 4);
- }
- image->rows.swap(newRows);
-
- image->width -= 2;
- W = image->width;
- image->height -= 2;
- H = image->height;
-
- // Figure out the number of rows and columns in the N-patch
- numCols = numXDivs + 1;
- if (xDivs[0] == 0) { // Column 1 is strechable
- numCols--;
- }
- if (xDivs[numXDivs - 1] == W) {
- numCols--;
- }
- numRows = numYDivs + 1;
- if (yDivs[0] == 0) { // Row 1 is strechable
- numRows--;
- }
- if (yDivs[numYDivs - 1] == H) {
- numRows--;
- }
-
- // Make sure the amount of rows and columns will fit in the number of
- // colors we can use in the 9-patch format.
- if (numRows * numCols > 0x7F) {
- errorMsg = "Too many rows and columns in 9-patch perimeter";
- goto getout;
- }
-
- numColors = numRows * numCols;
- image->info9Patch.numColors = numColors;
- image->colors.resize(numColors);
-
- // Fill in color information for each patch.
-
- uint32_t c;
- top = 0;
-
- // The first row always starts with the top being at y=0 and the bottom
- // being either yDivs[1] (if yDivs[0]=0) of yDivs[0]. In the former case
- // the first row is stretchable along the Y axis, otherwise it is fixed.
- // The last row always ends with the bottom being bitmap.height and the top
- // being either yDivs[numYDivs-2] (if yDivs[numYDivs-1]=bitmap.height) or
- // yDivs[numYDivs-1]. In the former case the last row is stretchable along
- // the Y axis, otherwise it is fixed.
- //
- // The first and last columns are similarly treated with respect to the X
- // axis.
- //
- // The above is to help explain some of the special casing that goes on the
- // code below.
-
- // The initial yDiv and whether the first row is considered stretchable or
- // not depends on whether yDiv[0] was zero or not.
- for (j = (yDivs[0] == 0 ? 1 : 0); j <= numYDivs && top < H; j++) {
- if (j == numYDivs) {
- bottom = H;
- } else {
- bottom = yDivs[j];
- }
- left = 0;
- // The initial xDiv and whether the first column is considered
- // stretchable or not depends on whether xDiv[0] was zero or not.
- for (i = xDivs[0] == 0 ? 1 : 0; i <= numXDivs && left < W; i++) {
- if (i == numXDivs) {
- right = W;
- } else {
- right = xDivs[i];
- }
- c = getColor(image->rows.data(), left, top, right - 1, bottom - 1);
- image->colors[colorIndex++] = c;
- if (kDebug) {
- if (c != android::Res_png_9patch::NO_COLOR) {
- hasColor = true;
- }
- }
- left = right;
- }
- top = bottom;
- }
-
- assert(colorIndex == numColors);
-
- if (kDebug && hasColor) {
- for (i = 0; i < numColors; i++) {
- if (i == 0) printf("Colors:\n");
- printf(" #%08x", image->colors[i]);
- if (i == numColors - 1) printf("\n");
- }
- }
-getout:
- if (errorMsg) {
- std::stringstream err;
- err << "9-patch malformed: " << errorMsg;
- if (!errorEdge) {
- err << "." << std::endl;
- if (errorPixel >= 0) {
- err << "Found at pixel #" << errorPixel << " along " << errorEdge << " edge";
- } else {
- err << "Found along " << errorEdge << " edge";
- }
- }
- *outError = err.str();
- return false;
- }
- return true;
-}
-
-
-bool Png::process(const Source& source, std::istream& input, BigBuffer* outBuffer,
- const Options& options, std::string* outError) {
- png_byte signature[kPngSignatureSize];
-
- // Read the PNG signature first.
- if (!input.read(reinterpret_cast<char*>(signature), kPngSignatureSize)) {
- *outError = strerror(errno);
- return false;
- }
-
- // If the PNG signature doesn't match, bail early.
- if (png_sig_cmp(signature, 0, kPngSignatureSize) != 0) {
- *outError = "not a valid png file";
- return false;
- }
-
- SourceLogger logger(source);
- bool result = false;
- png_structp readPtr = nullptr;
- png_infop infoPtr = nullptr;
- png_structp writePtr = nullptr;
- png_infop writeInfoPtr = nullptr;
- PngInfo pngInfo = {};
-
- readPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, nullptr, nullptr);
- if (!readPtr) {
- *outError = "failed to allocate read ptr";
- goto bail;
- }
-
- infoPtr = png_create_info_struct(readPtr);
- if (!infoPtr) {
- *outError = "failed to allocate info ptr";
- goto bail;
- }
-
- png_set_error_fn(readPtr, reinterpret_cast<png_voidp>(&logger), nullptr, logWarning);
-
- // Set the read function to read from std::istream.
- png_set_read_fn(readPtr, (png_voidp)&input, readDataFromStream);
-
- if (!readPng(readPtr, infoPtr, &pngInfo, outError)) {
- goto bail;
- }
-
- if (util::stringEndsWith<char>(source.path, ".9.png")) {
- if (!do9Patch(&pngInfo, outError)) {
- goto bail;
- }
- }
-
- writePtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, nullptr, nullptr);
- if (!writePtr) {
- *outError = "failed to allocate write ptr";
- goto bail;
- }
-
- writeInfoPtr = png_create_info_struct(writePtr);
- if (!writeInfoPtr) {
- *outError = "failed to allocate write info ptr";
- goto bail;
- }
-
- png_set_error_fn(writePtr, nullptr, nullptr, logWarning);
-
- // Set the write function to write to std::ostream.
- png_set_write_fn(writePtr, (png_voidp)outBuffer, writeDataToStream, flushDataToStream);
-
- if (!writePng(writePtr, writeInfoPtr, &pngInfo, options.grayScaleTolerance, &logger,
- outError)) {
- goto bail;
- }
-
- result = true;
-bail:
- if (readPtr) {
- png_destroy_read_struct(&readPtr, &infoPtr, nullptr);
- }
-
- if (writePtr) {
- png_destroy_write_struct(&writePtr, &writeInfoPtr);
- }
- return result;
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/Png.h b/tools/aapt2/Png.h
deleted file mode 100644
index 4577ab8..0000000
--- a/tools/aapt2/Png.h
+++ /dev/null
@@ -1,39 +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_PNG_H
-#define AAPT_PNG_H
-
-#include "BigBuffer.h"
-#include "Source.h"
-
-#include <iostream>
-#include <string>
-
-namespace aapt {
-
-struct Png {
- struct Options {
- int grayScaleTolerance = 0;
- };
-
- bool process(const Source& source, std::istream& input, BigBuffer* outBuffer,
- const Options& options, std::string* outError);
-};
-
-} // namespace aapt
-
-#endif // AAPT_PNG_H
diff --git a/tools/aapt2/ProguardRules.cpp b/tools/aapt2/ProguardRules.cpp
deleted file mode 100644
index e89fb7c..0000000
--- a/tools/aapt2/ProguardRules.cpp
+++ /dev/null
@@ -1,240 +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.
- */
-
-#include "ProguardRules.h"
-#include "Util.h"
-#include "XmlDom.h"
-
-#include <memory>
-#include <string>
-
-namespace aapt {
-namespace proguard {
-
-constexpr const char16_t* kSchemaAndroid = u"http://schemas.android.com/apk/res/android";
-
-class BaseVisitor : public xml::Visitor {
-public:
- BaseVisitor(const Source& source, KeepSet* keepSet) : mSource(source), mKeepSet(keepSet) {
- }
-
- virtual void visit(xml::Text*) override {};
-
- virtual void visit(xml::Namespace* node) override {
- for (const auto& child : node->children) {
- child->accept(this);
- }
- }
-
- virtual void visit(xml::Element* node) override {
- if (!node->namespaceUri.empty()) {
- Maybe<std::u16string> maybePackage = util::extractPackageFromNamespace(
- node->namespaceUri);
- if (maybePackage) {
- // This is a custom view, let's figure out the class name from this.
- std::u16string package = maybePackage.value() + u"." + node->name;
- if (util::isJavaClassName(package)) {
- addClass(node->lineNumber, package);
- }
- }
- } else if (util::isJavaClassName(node->name)) {
- addClass(node->lineNumber, node->name);
- }
-
- for (const auto& child: node->children) {
- child->accept(this);
- }
- }
-
-protected:
- void addClass(size_t lineNumber, const std::u16string& className) {
- mKeepSet->addClass(mSource.line(lineNumber), className);
- }
-
- void addMethod(size_t lineNumber, const std::u16string& methodName) {
- mKeepSet->addMethod(mSource.line(lineNumber), methodName);
- }
-
-private:
- Source mSource;
- KeepSet* mKeepSet;
-};
-
-struct LayoutVisitor : public BaseVisitor {
- LayoutVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
- }
-
- virtual void visit(xml::Element* node) override {
- bool checkClass = false;
- bool checkName = false;
- if (node->namespaceUri.empty()) {
- checkClass = node->name == u"view" || node->name == u"fragment";
- } else if (node->namespaceUri == kSchemaAndroid) {
- checkName = node->name == u"fragment";
- }
-
- for (const auto& attr : node->attributes) {
- if (checkClass && attr.namespaceUri.empty() && attr.name == u"class" &&
- util::isJavaClassName(attr.value)) {
- addClass(node->lineNumber, attr.value);
- } else if (checkName && attr.namespaceUri == kSchemaAndroid && attr.name == u"name" &&
- util::isJavaClassName(attr.value)) {
- addClass(node->lineNumber, attr.value);
- } else if (attr.namespaceUri == kSchemaAndroid && attr.name == u"onClick") {
- addMethod(node->lineNumber, attr.value);
- }
- }
-
- BaseVisitor::visit(node);
- }
-};
-
-struct XmlResourceVisitor : public BaseVisitor {
- XmlResourceVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
- }
-
- virtual void visit(xml::Element* node) override {
- bool checkFragment = false;
- if (node->namespaceUri.empty()) {
- checkFragment = node->name == u"PreferenceScreen" || node->name == u"header";
- }
-
- if (checkFragment) {
- xml::Attribute* attr = node->findAttribute(kSchemaAndroid, u"fragment");
- if (attr && util::isJavaClassName(attr->value)) {
- addClass(node->lineNumber, attr->value);
- }
- }
-
- BaseVisitor::visit(node);
- }
-};
-
-struct TransitionVisitor : public BaseVisitor {
- TransitionVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
- }
-
- virtual void visit(xml::Element* node) override {
- bool checkClass = node->namespaceUri.empty() &&
- (node->name == u"transition" || node->name == u"pathMotion");
- if (checkClass) {
- xml::Attribute* attr = node->findAttribute({}, u"class");
- if (attr && util::isJavaClassName(attr->value)) {
- addClass(node->lineNumber, attr->value);
- }
- }
-
- BaseVisitor::visit(node);
- }
-};
-
-struct ManifestVisitor : public BaseVisitor {
- ManifestVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
- }
-
- virtual void visit(xml::Element* node) override {
- if (node->namespaceUri.empty()) {
- bool getName = false;
- if (node->name == u"manifest") {
- xml::Attribute* attr = node->findAttribute({}, u"package");
- if (attr) {
- mPackage = attr->value;
- }
- } else if (node->name == u"application") {
- getName = true;
- xml::Attribute* attr = node->findAttribute(kSchemaAndroid, u"backupAgent");
- if (attr) {
- Maybe<std::u16string> result = util::getFullyQualifiedClassName(mPackage,
- attr->value);
- if (result) {
- addClass(node->lineNumber, result.value());
- }
- }
- } else if (node->name == u"activity" || node->name == u"service" ||
- node->name == u"receiver" || node->name == u"provider" ||
- node->name == u"instrumentation") {
- getName = true;
- }
-
- if (getName) {
- xml::Attribute* attr = node->findAttribute(kSchemaAndroid, u"name");
- if (attr) {
- Maybe<std::u16string> result = util::getFullyQualifiedClassName(mPackage,
- attr->value);
- if (result) {
- addClass(node->lineNumber, result.value());
- }
- }
- }
- }
- BaseVisitor::visit(node);
- }
-
- std::u16string mPackage;
-};
-
-bool collectProguardRulesForManifest(const Source& source, xml::Node* node, KeepSet* keepSet) {
- ManifestVisitor visitor(source, keepSet);
- node->accept(&visitor);
- return true;
-}
-
-bool collectProguardRules(ResourceType type, const Source& source, xml::Node* node,
- KeepSet* keepSet) {
- switch (type) {
- case ResourceType::kLayout: {
- LayoutVisitor visitor(source, keepSet);
- node->accept(&visitor);
- break;
- }
-
- case ResourceType::kXml: {
- XmlResourceVisitor visitor(source, keepSet);
- node->accept(&visitor);
- break;
- }
-
- case ResourceType::kTransition: {
- TransitionVisitor visitor(source, keepSet);
- node->accept(&visitor);
- break;
- }
-
- default:
- break;
- }
- return true;
-}
-
-bool writeKeepSet(std::ostream* out, const KeepSet& keepSet) {
- for (const auto& entry : keepSet.mKeepSet) {
- for (const SourceLine& source : entry.second) {
- *out << "// Referenced at " << source << "\n";
- }
- *out << "-keep class " << entry.first << " { <init>(...); }\n" << std::endl;
- }
-
- for (const auto& entry : keepSet.mKeepMethodSet) {
- for (const SourceLine& source : entry.second) {
- *out << "// Referenced at " << source << "\n";
- }
- *out << "-keepclassmembers class * { *** " << entry.first << "(...); }\n" << std::endl;
- }
- return true;
-}
-
-} // namespace proguard
-} // namespace aapt
diff --git a/tools/aapt2/ProguardRules.h b/tools/aapt2/ProguardRules.h
deleted file mode 100644
index bbb3e64..0000000
--- a/tools/aapt2/ProguardRules.h
+++ /dev/null
@@ -1,58 +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_PROGUARD_RULES_H
-#define AAPT_PROGUARD_RULES_H
-
-#include "Resource.h"
-#include "Source.h"
-#include "XmlDom.h"
-
-#include <map>
-#include <ostream>
-#include <set>
-#include <string>
-
-namespace aapt {
-namespace proguard {
-
-class KeepSet {
-public:
- inline void addClass(const SourceLine& source, const std::u16string& className) {
- mKeepSet[className].insert(source);
- }
-
- inline void addMethod(const SourceLine& source, const std::u16string& methodName) {
- mKeepMethodSet[methodName].insert(source);
- }
-
-private:
- friend bool writeKeepSet(std::ostream* out, const KeepSet& keepSet);
-
- std::map<std::u16string, std::set<SourceLine>> mKeepSet;
- std::map<std::u16string, std::set<SourceLine>> mKeepMethodSet;
-};
-
-bool collectProguardRulesForManifest(const Source& source, xml::Node* node, KeepSet* keepSet);
-bool collectProguardRules(ResourceType type, const Source& source, xml::Node* node,
- KeepSet* keepSet);
-
-bool writeKeepSet(std::ostream* out, const KeepSet& keepSet);
-
-} // namespace proguard
-} // namespace aapt
-
-#endif // AAPT_PROGUARD_RULES_H
diff --git a/tools/aapt2/ResChunkPullParser.cpp b/tools/aapt2/ResChunkPullParser.cpp
deleted file mode 100644
index 78ea60e..0000000
--- a/tools/aapt2/ResChunkPullParser.cpp
+++ /dev/null
@@ -1,68 +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.
- */
-
-#include "ResChunkPullParser.h"
-
-#include <androidfw/ResourceTypes.h>
-#include <cstddef>
-
-namespace aapt {
-
-using android::ResChunk_header;
-
-ResChunkPullParser::Event ResChunkPullParser::next() {
- if (!isGoodEvent(mEvent)) {
- return mEvent;
- }
-
- if (mEvent == Event::StartDocument) {
- mCurrentChunk = mData;
- } else {
- mCurrentChunk = reinterpret_cast<const ResChunk_header*>(
- reinterpret_cast<const char*>(mCurrentChunk) + mCurrentChunk->size);
- }
-
- const std::ptrdiff_t diff = reinterpret_cast<const char*>(mCurrentChunk)
- - reinterpret_cast<const char*>(mData);
- assert(diff >= 0 && "diff is negative");
- const size_t offset = static_cast<const size_t>(diff);
-
- if (offset == mLen) {
- mCurrentChunk = nullptr;
- return (mEvent = Event::EndDocument);
- } else if (offset + sizeof(ResChunk_header) > mLen) {
- mLastError = "chunk is past the end of the document";
- mCurrentChunk = nullptr;
- return (mEvent = Event::BadDocument);
- }
-
- if (mCurrentChunk->headerSize < sizeof(ResChunk_header)) {
- mLastError = "chunk has too small header";
- mCurrentChunk = nullptr;
- return (mEvent = Event::BadDocument);
- } else if (mCurrentChunk->size < mCurrentChunk->headerSize) {
- mLastError = "chunk's total size is smaller than header";
- mCurrentChunk = nullptr;
- return (mEvent = Event::BadDocument);
- } else if (offset + mCurrentChunk->size > mLen) {
- mLastError = "chunk's data extends past the end of the document";
- mCurrentChunk = nullptr;
- return (mEvent = Event::BadDocument);
- }
- return (mEvent = Event::Chunk);
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/ResChunkPullParser.h b/tools/aapt2/ResChunkPullParser.h
deleted file mode 100644
index 1426ed2..0000000
--- a/tools/aapt2/ResChunkPullParser.h
+++ /dev/null
@@ -1,122 +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_RES_CHUNK_PULL_PARSER_H
-#define AAPT_RES_CHUNK_PULL_PARSER_H
-
-#include <androidfw/ResourceTypes.h>
-#include <string>
-
-namespace aapt {
-
-/**
- * A pull parser, modeled after XmlPullParser, that reads
- * android::ResChunk_header structs from a block of data.
- *
- * An android::ResChunk_header specifies a type, headerSize,
- * and size. The pull parser will verify that the chunk's size
- * doesn't extend beyond the available data, and will iterate
- * over each chunk in the given block of data.
- *
- * Processing nested chunks is done by creating a new ResChunkPullParser
- * pointing to the data portion of a chunk.
- */
-class ResChunkPullParser {
-public:
- enum class Event {
- StartDocument,
- EndDocument,
- BadDocument,
-
- Chunk,
- };
-
- /**
- * Returns false if the event is EndDocument or BadDocument.
- */
- static bool isGoodEvent(Event event);
-
- /**
- * Create a ResChunkPullParser to read android::ResChunk_headers
- * from the memory pointed to by data, of len bytes.
- */
- ResChunkPullParser(const void* data, size_t len);
-
- ResChunkPullParser(const ResChunkPullParser&) = delete;
-
- Event getEvent() const;
- const std::string& getLastError() const;
- const android::ResChunk_header* getChunk() const;
-
- /**
- * Move to the next android::ResChunk_header.
- */
- Event next();
-
-private:
- Event mEvent;
- const android::ResChunk_header* mData;
- size_t mLen;
- const android::ResChunk_header* mCurrentChunk;
- std::string mLastError;
-};
-
-template <typename T>
-inline static const T* convertTo(const android::ResChunk_header* chunk) {
- if (chunk->headerSize < sizeof(T)) {
- return nullptr;
- }
- return reinterpret_cast<const T*>(chunk);
-}
-
-inline static const uint8_t* getChunkData(const android::ResChunk_header& chunk) {
- return reinterpret_cast<const uint8_t*>(&chunk) + chunk.headerSize;
-}
-
-inline static size_t getChunkDataLen(const android::ResChunk_header& chunk) {
- return chunk.size - chunk.headerSize;
-}
-
-//
-// Implementation
-//
-
-inline bool ResChunkPullParser::isGoodEvent(ResChunkPullParser::Event event) {
- return event != Event::EndDocument && event != Event::BadDocument;
-}
-
-inline ResChunkPullParser::ResChunkPullParser(const void* data, size_t len) :
- mEvent(Event::StartDocument),
- mData(reinterpret_cast<const android::ResChunk_header*>(data)),
- mLen(len),
- mCurrentChunk(nullptr) {
-}
-
-inline ResChunkPullParser::Event ResChunkPullParser::getEvent() const {
- return mEvent;
-}
-
-inline const std::string& ResChunkPullParser::getLastError() const {
- return mLastError;
-}
-
-inline const android::ResChunk_header* ResChunkPullParser::getChunk() const {
- return mCurrentChunk;
-}
-
-} // namespace aapt
-
-#endif // AAPT_RES_CHUNK_PULL_PARSER_H
diff --git a/tools/aapt2/Resolver.h b/tools/aapt2/Resolver.h
deleted file mode 100644
index cb9318e..0000000
--- a/tools/aapt2/Resolver.h
+++ /dev/null
@@ -1,75 +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_RESOLVER_H
-#define AAPT_RESOLVER_H
-
-#include "Maybe.h"
-#include "Resource.h"
-#include "ResourceValues.h"
-
-#include <androidfw/ResourceTypes.h>
-
-namespace aapt {
-
-/**
- * Resolves symbolic references (package:type/entry) into resource IDs/objects.
- */
-class IResolver {
-public:
- virtual ~IResolver() {};
-
- /**
- * Holds the result of a resource name lookup.
- */
- struct Entry {
- /**
- * The ID of the resource. ResourceId::isValid() may
- * return false if the resource has not been assigned
- * an ID.
- */
- ResourceId id;
-
- /**
- * If the resource is an attribute, this will point
- * to a valid Attribute object, or else it will be
- * nullptr.
- */
- const Attribute* attr;
- };
-
- /**
- * Returns a ResourceID if the name is found. The ResourceID
- * may not be valid if the resource was not assigned an ID.
- */
- virtual Maybe<ResourceId> findId(const ResourceName& name) = 0;
-
- /**
- * Returns an Entry if the name is found. Entry::attr
- * may be nullptr if the resource is not an attribute.
- */
- virtual Maybe<Entry> findAttribute(const ResourceName& name) = 0;
-
- /**
- * Find a resource by ID. Resolvers may contain resources without
- * resource IDs assigned to them.
- */
- virtual Maybe<ResourceName> findName(ResourceId resId) = 0;
-};
-
-} // namespace aapt
-
-#endif // AAPT_RESOLVER_H
diff --git a/tools/aapt2/Resource.cpp b/tools/aapt2/Resource.cpp
index 287d8de..34dc1d5 100644
--- a/tools/aapt2/Resource.cpp
+++ b/tools/aapt2/Resource.cpp
@@ -15,7 +15,7 @@
*/
#include "Resource.h"
-#include "StringPiece.h"
+#include "util/StringPiece.h"
#include <map>
#include <string>
@@ -28,7 +28,7 @@
case ResourceType::kAnimator: return u"animator";
case ResourceType::kArray: return u"array";
case ResourceType::kAttr: return u"attr";
- case ResourceType::kAttrPrivate: return u"attr";
+ case ResourceType::kAttrPrivate: return u"^attr-private";
case ResourceType::kBool: return u"bool";
case ResourceType::kColor: return u"color";
case ResourceType::kDimen: return u"dimen";
@@ -36,7 +36,6 @@
case ResourceType::kFraction: return u"fraction";
case ResourceType::kId: return u"id";
case ResourceType::kInteger: return u"integer";
- case ResourceType::kIntegerArray: return u"integer-array";
case ResourceType::kInterpolator: return u"interpolator";
case ResourceType::kLayout: return u"layout";
case ResourceType::kMenu: return u"menu";
@@ -65,7 +64,6 @@
{ u"fraction", ResourceType::kFraction },
{ u"id", ResourceType::kId },
{ u"integer", ResourceType::kInteger },
- { u"integer-array", ResourceType::kIntegerArray },
{ u"interpolator", ResourceType::kInterpolator },
{ u"layout", ResourceType::kLayout },
{ u"menu", ResourceType::kMenu },
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index fa9ac07..a7afbb5 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -17,12 +17,16 @@
#ifndef AAPT_RESOURCE_H
#define AAPT_RESOURCE_H
-#include "StringPiece.h"
+#include "ConfigDescription.h"
+#include "Source.h"
+
+#include "util/StringPiece.h"
#include <iomanip>
#include <limits>
#include <string>
#include <tuple>
+#include <vector>
namespace aapt {
@@ -43,7 +47,6 @@
kFraction,
kId,
kInteger,
- kIntegerArray,
kInterpolator,
kLayout,
kMenu,
@@ -74,10 +77,14 @@
ResourceType type;
std::u16string entry;
+ ResourceName() = default;
+ ResourceName(const StringPiece16& p, ResourceType t, const StringPiece16& e);
+
bool isValid() const;
bool operator<(const ResourceName& rhs) const;
bool operator==(const ResourceName& rhs) const;
bool operator!=(const ResourceName& rhs) const;
+ std::u16string toString() const;
};
/**
@@ -125,7 +132,7 @@
ResourceId();
ResourceId(const ResourceId& rhs);
ResourceId(uint32_t resId);
- ResourceId(size_t p, size_t t, size_t e);
+ ResourceId(uint8_t p, uint8_t t, uint16_t e);
bool isValid() const;
uint8_t packageId() const;
@@ -135,6 +142,29 @@
bool operator==(const ResourceId& rhs) const;
};
+struct SourcedResourceName {
+ ResourceName name;
+ size_t line;
+
+ inline bool operator==(const SourcedResourceName& rhs) const {
+ return name == rhs.name && line == rhs.line;
+ }
+};
+
+struct ResourceFile {
+ // Name
+ ResourceName name;
+
+ // Configuration
+ ConfigDescription config;
+
+ // Source
+ Source source;
+
+ // Exported symbols
+ std::vector<SourcedResourceName> exportedSymbols;
+};
+
//
// ResourceId implementation.
//
@@ -148,17 +178,7 @@
inline ResourceId::ResourceId(uint32_t resId) : id(resId) {
}
-inline ResourceId::ResourceId(size_t p, size_t t, size_t e) : id(0) {
- if (p > std::numeric_limits<uint8_t>::max() ||
- t > std::numeric_limits<uint8_t>::max() ||
- e > std::numeric_limits<uint16_t>::max()) {
- // This will leave the ResourceId in an invalid state.
- return;
- }
-
- id = (static_cast<uint8_t>(p) << 24) |
- (static_cast<uint8_t>(t) << 16) |
- static_cast<uint16_t>(e);
+inline ResourceId::ResourceId(uint8_t p, uint8_t t, uint16_t e) : id((p << 24) | (t << 16) | e) {
}
inline bool ResourceId::isValid() const {
@@ -208,6 +228,10 @@
// ResourceName implementation.
//
+inline ResourceName::ResourceName(const StringPiece16& p, ResourceType t, const StringPiece16& e) :
+ package(p.toString()), type(t), entry(e.toString()) {
+}
+
inline bool ResourceName::isValid() const {
return !package.empty() && !entry.empty();
}
@@ -217,6 +241,10 @@
< std::tie(rhs.package, rhs.type, rhs.entry);
}
+inline bool operator<(const ResourceName& lhs, const ResourceNameRef& b) {
+ return ResourceNameRef(lhs) < b;
+}
+
inline bool ResourceName::operator==(const ResourceName& rhs) const {
return std::tie(package, type, entry)
== std::tie(rhs.package, rhs.type, rhs.entry);
@@ -227,6 +255,18 @@
!= std::tie(rhs.package, rhs.type, rhs.entry);
}
+inline bool operator!=(const ResourceName& lhs, const ResourceNameRef& rhs) {
+ return ResourceNameRef(lhs) != rhs;
+}
+
+inline std::u16string ResourceName::toString() const {
+ std::u16string result;
+ if (!package.empty()) {
+ result = package + u":";
+ }
+ return result + aapt::toString(type).toString() + u"/" + entry;
+}
+
inline ::std::ostream& operator<<(::std::ostream& out, const ResourceName& name) {
if (!name.package.empty()) {
out << name.package << ":";
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 13f916b..f2a1878 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -14,473 +14,52 @@
* limitations under the License.
*/
-#include "Logger.h"
#include "ResourceParser.h"
+#include "ResourceTable.h"
+#include "ResourceUtils.h"
#include "ResourceValues.h"
-#include "ScopedXmlPullParser.h"
-#include "SourceXmlPullParser.h"
-#include "Util.h"
-#include "XliffXmlPullParser.h"
+#include "ValueVisitor.h"
+#include "XmlPullParser.h"
+
+#include "util/Util.h"
#include <sstream>
namespace aapt {
-void ResourceParser::extractResourceName(const StringPiece16& str, StringPiece16* outPackage,
- StringPiece16* outType, StringPiece16* outEntry) {
- const char16_t* start = str.data();
- const char16_t* end = start + str.size();
- const char16_t* current = start;
- while (current != end) {
- if (outType->size() == 0 && *current == u'/') {
- outType->assign(start, current - start);
- start = current + 1;
- } else if (outPackage->size() == 0 && *current == u':') {
- outPackage->assign(start, current - start);
- start = current + 1;
- }
- current++;
- }
- outEntry->assign(start, end - start);
-}
+constexpr const char16_t* sXliffNamespaceUri = u"urn:oasis:names:tc:xliff:document:1.2";
-bool ResourceParser::tryParseReference(const StringPiece16& str, ResourceNameRef* outRef,
- bool* outCreate, bool* outPrivate) {
- StringPiece16 trimmedStr(util::trimWhitespace(str));
- if (trimmedStr.empty()) {
- return false;
- }
-
- if (trimmedStr.data()[0] == u'@') {
- size_t offset = 1;
- *outCreate = false;
- if (trimmedStr.data()[1] == u'+') {
- *outCreate = true;
- offset += 1;
- } else if (trimmedStr.data()[1] == u'*') {
- *outPrivate = true;
- offset += 1;
- }
- StringPiece16 package;
- StringPiece16 type;
- StringPiece16 entry;
- extractResourceName(trimmedStr.substr(offset, trimmedStr.size() - offset),
- &package, &type, &entry);
-
- const ResourceType* parsedType = parseResourceType(type);
- if (!parsedType) {
- return false;
- }
-
- if (*outCreate && *parsedType != ResourceType::kId) {
- return false;
- }
-
- outRef->package = package;
- outRef->type = *parsedType;
- outRef->entry = entry;
- return true;
- }
- return false;
-}
-
-bool ResourceParser::tryParseAttributeReference(const StringPiece16& str,
- ResourceNameRef* outRef) {
- StringPiece16 trimmedStr(util::trimWhitespace(str));
- if (trimmedStr.empty()) {
- return false;
- }
-
- if (*trimmedStr.data() == u'?') {
- StringPiece16 package;
- StringPiece16 type;
- StringPiece16 entry;
- extractResourceName(trimmedStr.substr(1, trimmedStr.size() - 1), &package, &type, &entry);
-
- if (!type.empty() && type != u"attr") {
- return false;
- }
-
- outRef->package = package;
- outRef->type = ResourceType::kAttr;
- outRef->entry = entry;
- return true;
- }
- return false;
-}
-
-/*
- * Style parent's are a bit different. We accept the following formats:
- *
- * @[package:]style/<entry>
- * ?[package:]style/<entry>
- * <package>:[style/]<entry>
- * [package:style/]<entry>
- */
-bool ResourceParser::parseStyleParentReference(const StringPiece16& str, Reference* outReference,
- std::string* outError) {
- if (str.empty()) {
- return true;
- }
-
- StringPiece16 name = str;
-
- bool hasLeadingIdentifiers = false;
- bool privateRef = false;
-
- // Skip over these identifiers. A style's parent is a normal reference.
- if (name.data()[0] == u'@' || name.data()[0] == u'?') {
- hasLeadingIdentifiers = true;
- name = name.substr(1, name.size() - 1);
- if (name.data()[0] == u'*') {
- privateRef = true;
- name = name.substr(1, name.size() - 1);
- }
- }
-
- ResourceNameRef ref;
- ref.type = ResourceType::kStyle;
-
- StringPiece16 typeStr;
- extractResourceName(name, &ref.package, &typeStr, &ref.entry);
- if (!typeStr.empty()) {
- // If we have a type, make sure it is a Style.
- const ResourceType* parsedType = parseResourceType(typeStr);
- if (!parsedType || *parsedType != ResourceType::kStyle) {
- std::stringstream err;
- err << "invalid resource type '" << typeStr << "' for parent of style";
- *outError = err.str();
- return false;
- }
- } else {
- // No type was defined, this should not have a leading identifier.
- if (hasLeadingIdentifiers) {
- std::stringstream err;
- err << "invalid parent reference '" << str << "'";
- *outError = err.str();
- return false;
- }
- }
-
- if (!hasLeadingIdentifiers && ref.package.empty() && !typeStr.empty()) {
- std::stringstream err;
- err << "invalid parent reference '" << str << "'";
- *outError = err.str();
- return false;
- }
-
- outReference->name = ref.toResourceName();
- outReference->privateReference = privateRef;
- return true;
-}
-
-std::unique_ptr<Reference> ResourceParser::tryParseReference(const StringPiece16& str,
- bool* outCreate) {
- ResourceNameRef ref;
- bool privateRef = false;
- if (tryParseReference(str, &ref, outCreate, &privateRef)) {
- std::unique_ptr<Reference> value = util::make_unique<Reference>(ref);
- value->privateReference = privateRef;
- return value;
- }
-
- if (tryParseAttributeReference(str, &ref)) {
- *outCreate = false;
- return util::make_unique<Reference>(ref, Reference::Type::kAttribute);
+static Maybe<StringPiece16> findAttribute(XmlPullParser* parser, const StringPiece16& name) {
+ auto iter = parser->findAttribute(u"", name);
+ if (iter != parser->endAttributes()) {
+ return StringPiece16(util::trimWhitespace(iter->value));
}
return {};
}
-std::unique_ptr<BinaryPrimitive> ResourceParser::tryParseNullOrEmpty(const StringPiece16& str) {
- StringPiece16 trimmedStr(util::trimWhitespace(str));
- android::Res_value value = {};
- if (trimmedStr == u"@null") {
- // TYPE_NULL with data set to 0 is interpreted by the runtime as an error.
- // Instead we set the data type to TYPE_REFERENCE with a value of 0.
- value.dataType = android::Res_value::TYPE_REFERENCE;
- } else if (trimmedStr == u"@empty") {
- // TYPE_NULL with value of DATA_NULL_EMPTY is handled fine by the runtime.
- value.dataType = android::Res_value::TYPE_NULL;
- value.data = android::Res_value::DATA_NULL_EMPTY;
- } else {
- return {};
- }
- return util::make_unique<BinaryPrimitive>(value);
-}
-
-std::unique_ptr<BinaryPrimitive> ResourceParser::tryParseEnumSymbol(const Attribute& enumAttr,
- const StringPiece16& str) {
- StringPiece16 trimmedStr(util::trimWhitespace(str));
- for (const auto& entry : enumAttr.symbols) {
- // Enum symbols are stored as @package:id/symbol resources,
- // so we need to match against the 'entry' part of the identifier.
- const ResourceName& enumSymbolResourceName = entry.symbol.name;
- if (trimmedStr == enumSymbolResourceName.entry) {
- android::Res_value value = {};
- value.dataType = android::Res_value::TYPE_INT_DEC;
- value.data = entry.value;
- return util::make_unique<BinaryPrimitive>(value);
- }
- }
- return {};
-}
-
-std::unique_ptr<BinaryPrimitive> ResourceParser::tryParseFlagSymbol(const Attribute& flagAttr,
- const StringPiece16& str) {
- android::Res_value flags = {};
- flags.dataType = android::Res_value::TYPE_INT_DEC;
-
- for (StringPiece16 part : util::tokenize(str, u'|')) {
- StringPiece16 trimmedPart = util::trimWhitespace(part);
-
- bool flagSet = false;
- for (const auto& entry : flagAttr.symbols) {
- // Flag symbols are stored as @package:id/symbol resources,
- // so we need to match against the 'entry' part of the identifier.
- const ResourceName& flagSymbolResourceName = entry.symbol.name;
- if (trimmedPart == flagSymbolResourceName.entry) {
- flags.data |= entry.value;
- flagSet = true;
- break;
- }
- }
-
- if (!flagSet) {
- return {};
- }
- }
- return util::make_unique<BinaryPrimitive>(flags);
-}
-
-static uint32_t parseHex(char16_t c, bool* outError) {
- if (c >= u'0' && c <= u'9') {
- return c - u'0';
- } else if (c >= u'a' && c <= u'f') {
- return c - u'a' + 0xa;
- } else if (c >= u'A' && c <= u'F') {
- return c - u'A' + 0xa;
- } else {
- *outError = true;
- return 0xffffffffu;
- }
-}
-
-std::unique_ptr<BinaryPrimitive> ResourceParser::tryParseColor(const StringPiece16& str) {
- StringPiece16 colorStr(util::trimWhitespace(str));
- const char16_t* start = colorStr.data();
- const size_t len = colorStr.size();
- if (len == 0 || start[0] != u'#') {
- return {};
- }
-
- android::Res_value value = {};
- bool error = false;
- if (len == 4) {
- value.dataType = android::Res_value::TYPE_INT_COLOR_RGB4;
- value.data = 0xff000000u;
- value.data |= parseHex(start[1], &error) << 20;
- value.data |= parseHex(start[1], &error) << 16;
- value.data |= parseHex(start[2], &error) << 12;
- value.data |= parseHex(start[2], &error) << 8;
- value.data |= parseHex(start[3], &error) << 4;
- value.data |= parseHex(start[3], &error);
- } else if (len == 5) {
- value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB4;
- value.data |= parseHex(start[1], &error) << 28;
- value.data |= parseHex(start[1], &error) << 24;
- value.data |= parseHex(start[2], &error) << 20;
- value.data |= parseHex(start[2], &error) << 16;
- value.data |= parseHex(start[3], &error) << 12;
- value.data |= parseHex(start[3], &error) << 8;
- value.data |= parseHex(start[4], &error) << 4;
- value.data |= parseHex(start[4], &error);
- } else if (len == 7) {
- value.dataType = android::Res_value::TYPE_INT_COLOR_RGB8;
- value.data = 0xff000000u;
- value.data |= parseHex(start[1], &error) << 20;
- value.data |= parseHex(start[2], &error) << 16;
- value.data |= parseHex(start[3], &error) << 12;
- value.data |= parseHex(start[4], &error) << 8;
- value.data |= parseHex(start[5], &error) << 4;
- value.data |= parseHex(start[6], &error);
- } else if (len == 9) {
- value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB8;
- value.data |= parseHex(start[1], &error) << 28;
- value.data |= parseHex(start[2], &error) << 24;
- value.data |= parseHex(start[3], &error) << 20;
- value.data |= parseHex(start[4], &error) << 16;
- value.data |= parseHex(start[5], &error) << 12;
- value.data |= parseHex(start[6], &error) << 8;
- value.data |= parseHex(start[7], &error) << 4;
- value.data |= parseHex(start[8], &error);
- } else {
- return {};
- }
- return error ? std::unique_ptr<BinaryPrimitive>() : util::make_unique<BinaryPrimitive>(value);
-}
-
-std::unique_ptr<BinaryPrimitive> ResourceParser::tryParseBool(const StringPiece16& str) {
- StringPiece16 trimmedStr(util::trimWhitespace(str));
- uint32_t data = 0;
- if (trimmedStr == u"true" || trimmedStr == u"TRUE") {
- data = 0xffffffffu;
- } else if (trimmedStr != u"false" && trimmedStr != u"FALSE") {
- return {};
- }
- android::Res_value value = {};
- value.dataType = android::Res_value::TYPE_INT_BOOLEAN;
- value.data = data;
- return util::make_unique<BinaryPrimitive>(value);
-}
-
-std::unique_ptr<BinaryPrimitive> ResourceParser::tryParseInt(const StringPiece16& str) {
- android::Res_value value;
- if (!android::ResTable::stringToInt(str.data(), str.size(), &value)) {
- return {};
- }
- return util::make_unique<BinaryPrimitive>(value);
-}
-
-std::unique_ptr<BinaryPrimitive> ResourceParser::tryParseFloat(const StringPiece16& str) {
- android::Res_value value;
- if (!android::ResTable::stringToFloat(str.data(), str.size(), &value)) {
- return {};
- }
- return util::make_unique<BinaryPrimitive>(value);
-}
-
-uint32_t ResourceParser::androidTypeToAttributeTypeMask(uint16_t type) {
- switch (type) {
- case android::Res_value::TYPE_NULL:
- case android::Res_value::TYPE_REFERENCE:
- case android::Res_value::TYPE_ATTRIBUTE:
- case android::Res_value::TYPE_DYNAMIC_REFERENCE:
- return android::ResTable_map::TYPE_REFERENCE;
-
- case android::Res_value::TYPE_STRING:
- return android::ResTable_map::TYPE_STRING;
-
- case android::Res_value::TYPE_FLOAT:
- return android::ResTable_map::TYPE_FLOAT;
-
- case android::Res_value::TYPE_DIMENSION:
- return android::ResTable_map::TYPE_DIMENSION;
-
- case android::Res_value::TYPE_FRACTION:
- return android::ResTable_map::TYPE_FRACTION;
-
- case android::Res_value::TYPE_INT_DEC:
- case android::Res_value::TYPE_INT_HEX:
- return android::ResTable_map::TYPE_INTEGER |
- android::ResTable_map::TYPE_ENUM |
- android::ResTable_map::TYPE_FLAGS;
-
- case android::Res_value::TYPE_INT_BOOLEAN:
- return android::ResTable_map::TYPE_BOOLEAN;
-
- case android::Res_value::TYPE_INT_COLOR_ARGB8:
- case android::Res_value::TYPE_INT_COLOR_RGB8:
- case android::Res_value::TYPE_INT_COLOR_ARGB4:
- case android::Res_value::TYPE_INT_COLOR_RGB4:
- return android::ResTable_map::TYPE_COLOR;
-
- default:
- return 0;
- };
-}
-
-std::unique_ptr<Item> ResourceParser::parseItemForAttribute(
- const StringPiece16& value, uint32_t typeMask,
- std::function<void(const ResourceName&)> onCreateReference) {
- std::unique_ptr<BinaryPrimitive> nullOrEmpty = tryParseNullOrEmpty(value);
- if (nullOrEmpty) {
- return std::move(nullOrEmpty);
- }
-
- bool create = false;
- std::unique_ptr<Reference> reference = tryParseReference(value, &create);
- if (reference) {
- if (create && onCreateReference) {
- onCreateReference(reference->name);
- }
- return std::move(reference);
- }
-
- if (typeMask & android::ResTable_map::TYPE_COLOR) {
- // Try parsing this as a color.
- std::unique_ptr<BinaryPrimitive> color = tryParseColor(value);
- if (color) {
- return std::move(color);
- }
- }
-
- if (typeMask & android::ResTable_map::TYPE_BOOLEAN) {
- // Try parsing this as a boolean.
- std::unique_ptr<BinaryPrimitive> boolean = tryParseBool(value);
- if (boolean) {
- return std::move(boolean);
- }
- }
-
- if (typeMask & android::ResTable_map::TYPE_INTEGER) {
- // Try parsing this as an integer.
- std::unique_ptr<BinaryPrimitive> integer = tryParseInt(value);
- if (integer) {
- return std::move(integer);
- }
- }
-
- const uint32_t floatMask = android::ResTable_map::TYPE_FLOAT |
- android::ResTable_map::TYPE_DIMENSION |
- android::ResTable_map::TYPE_FRACTION;
- if (typeMask & floatMask) {
- // Try parsing this as a float.
- std::unique_ptr<BinaryPrimitive> floatingPoint = tryParseFloat(value);
- if (floatingPoint) {
- if (typeMask & androidTypeToAttributeTypeMask(floatingPoint->value.dataType)) {
- return std::move(floatingPoint);
- }
+static Maybe<StringPiece16> findNonEmptyAttribute(XmlPullParser* parser,
+ const StringPiece16& name) {
+ auto iter = parser->findAttribute(u"", name);
+ if (iter != parser->endAttributes()) {
+ StringPiece16 trimmed = util::trimWhitespace(iter->value);
+ if (!trimmed.empty()) {
+ return trimmed;
}
}
return {};
}
/**
- * We successively try to parse the string as a resource type that the Attribute
- * allows.
+ * Returns true if the element is <skip> or <eat-comment> and can be safely ignored.
*/
-std::unique_ptr<Item> ResourceParser::parseItemForAttribute(
- const StringPiece16& str, const Attribute& attr,
- std::function<void(const ResourceName&)> onCreateReference) {
- const uint32_t typeMask = attr.typeMask;
- std::unique_ptr<Item> value = parseItemForAttribute(str, typeMask, onCreateReference);
- if (value) {
- return value;
- }
-
- if (typeMask & android::ResTable_map::TYPE_ENUM) {
- // Try parsing this as an enum.
- std::unique_ptr<BinaryPrimitive> enumValue = tryParseEnumSymbol(attr, str);
- if (enumValue) {
- return std::move(enumValue);
- }
- }
-
- if (typeMask & android::ResTable_map::TYPE_FLAGS) {
- // Try parsing this as a flag.
- std::unique_ptr<BinaryPrimitive> flagValue = tryParseFlagSymbol(attr, str);
- if (flagValue) {
- return std::move(flagValue);
- }
- }
- return {};
+static bool shouldIgnoreElement(const StringPiece16& ns, const StringPiece16& name) {
+ return ns.empty() && (name == u"skip" || name == u"eat-comment");
}
-ResourceParser::ResourceParser(const std::shared_ptr<ResourceTable>& table, const Source& source,
+ResourceParser::ResourceParser(IDiagnostics* diag, ResourceTable* table, const Source& source,
const ConfigDescription& config,
- const std::shared_ptr<XmlPullParser>& parser) :
- mTable(table), mSource(source), mConfig(config), mLogger(source),
- mParser(std::make_shared<XliffXmlPullParser>(parser)) {
+ const ResourceParserOptions& options) :
+ mDiag(diag), mTable(table), mSource(source), mConfig(config), mOptions(options) {
}
/**
@@ -490,6 +69,7 @@
StyleString* outStyleString) {
std::vector<Span> spanStack;
+ bool error = false;
outRawString->clear();
outStyleString->spans.clear();
util::StringBuilder builder;
@@ -497,6 +77,11 @@
while (XmlPullParser::isGoodEvent(parser->next())) {
const XmlPullParser::Event event = parser->getEvent();
if (event == XmlPullParser::Event::kEndElement) {
+ if (!parser->getElementNamespace().empty()) {
+ // We already warned and skipped the start element, so just skip here too
+ continue;
+ }
+
depth--;
if (depth == 0) {
break;
@@ -507,20 +92,20 @@
spanStack.pop_back();
} else if (event == XmlPullParser::Event::kText) {
- // TODO(adamlesinski): Verify format strings.
outRawString->append(parser->getText());
builder.append(parser->getText());
} else if (event == XmlPullParser::Event::kStartElement) {
- if (parser->getElementNamespace().size() > 0) {
- mLogger.warn(parser->getLineNumber())
- << "skipping element '"
- << parser->getElementName()
- << "' with unknown namespace '"
- << parser->getElementNamespace()
- << "'."
- << std::endl;
- XmlPullParser::skipCurrentElement(parser);
+ if (!parser->getElementNamespace().empty()) {
+ if (parser->getElementNamespace() != sXliffNamespaceUri) {
+ // Only warn if this isn't an xliff namespace.
+ mDiag->warn(DiagMessage(mSource.withLine(parser->getLineNumber()))
+ << "skipping element '"
+ << parser->getElementName()
+ << "' with unknown namespace '"
+ << parser->getElementNamespace()
+ << "'");
+ }
continue;
}
depth++;
@@ -536,65 +121,121 @@
}
if (builder.str().size() > std::numeric_limits<uint32_t>::max()) {
- mLogger.error(parser->getLineNumber())
- << "style string '"
- << builder.str()
- << "' is too long."
- << std::endl;
- return false;
+ mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
+ << "style string '" << builder.str() << "' is too long");
+ error = true;
+ } else {
+ spanStack.push_back(Span{ spanName, static_cast<uint32_t>(builder.str().size()) });
}
- spanStack.push_back(Span{ spanName, static_cast<uint32_t>(builder.str().size()) });
} else if (event == XmlPullParser::Event::kComment) {
// Skip
} else {
- mLogger.warn(parser->getLineNumber())
- << "unknown event "
- << event
- << "."
- << std::endl;
+ assert(false);
}
}
assert(spanStack.empty() && "spans haven't been fully processed");
outStyleString->str = builder.str();
- return true;
+ return !error;
}
-bool ResourceParser::parse() {
- while (XmlPullParser::isGoodEvent(mParser->next())) {
- if (mParser->getEvent() != XmlPullParser::Event::kStartElement) {
+bool ResourceParser::parse(XmlPullParser* parser) {
+ bool error = false;
+ const size_t depth = parser->getDepth();
+ while (XmlPullParser::nextChildNode(parser, depth)) {
+ if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
+ // Skip comments and text.
continue;
}
- ScopedXmlPullParser parser(mParser.get());
- if (!parser.getElementNamespace().empty() ||
- parser.getElementName() != u"resources") {
- mLogger.error(parser.getLineNumber())
- << "root element must be <resources> in the global namespace."
- << std::endl;
+ if (!parser->getElementNamespace().empty() || parser->getElementName() != u"resources") {
+ mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
+ << "root element must be <resources>");
return false;
}
- if (!parseResources(&parser)) {
- return false;
- }
- }
+ error |= !parseResources(parser);
+ break;
+ };
- if (mParser->getEvent() == XmlPullParser::Event::kBadDocument) {
- mLogger.error(mParser->getLineNumber())
- << mParser->getLastError()
- << std::endl;
+ if (parser->getEvent() == XmlPullParser::Event::kBadDocument) {
+ mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
+ << "xml parser error: " << parser->getLastError());
return false;
}
- return true;
+ return !error;
+}
+
+static bool shouldStripResource(XmlPullParser* parser, const Maybe<std::u16string> productToMatch) {
+ assert(parser->getEvent() == XmlPullParser::Event::kStartElement);
+
+ if (Maybe<StringPiece16> maybeProduct = findNonEmptyAttribute(parser, u"product")) {
+ if (!productToMatch) {
+ if (maybeProduct.value() != u"default" && maybeProduct.value() != u"phone") {
+ // We didn't specify a product and this is not a default product, so skip.
+ return true;
+ }
+ } else {
+ if (productToMatch && maybeProduct.value() != productToMatch.value()) {
+ // We specified a product, but they don't match.
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+/**
+ * A parsed resource ready to be added to the ResourceTable.
+ */
+struct ParsedResource {
+ ResourceName name;
+ Source source;
+ ResourceId id;
+ SymbolState symbolState = SymbolState::kUndefined;
+ std::u16string comment;
+ std::unique_ptr<Value> value;
+ std::list<ParsedResource> childResources;
+};
+
+// Recursively adds resources to the ResourceTable.
+static bool addResourcesToTable(ResourceTable* table, const ConfigDescription& config,
+ IDiagnostics* diag, ParsedResource* res) {
+ if (res->symbolState != SymbolState::kUndefined) {
+ Symbol symbol;
+ symbol.state = res->symbolState;
+ symbol.source = res->source;
+ symbol.comment = res->comment;
+ if (!table->setSymbolState(res->name, res->id, symbol, diag)) {
+ return false;
+ }
+ }
+
+ if (res->value) {
+ // Attach the comment, source and config to the value.
+ res->value->setComment(std::move(res->comment));
+ res->value->setSource(std::move(res->source));
+
+ if (!table->addResource(res->name, res->id, config, std::move(res->value), diag)) {
+ return false;
+ }
+ }
+
+ bool error = false;
+ for (ParsedResource& child : res->childResources) {
+ error |= !addResourcesToTable(table, config, diag, &child);
+ }
+ return !error;
}
bool ResourceParser::parseResources(XmlPullParser* parser) {
- bool success = true;
+ std::set<ResourceName> strippedResources;
+ bool error = false;
std::u16string comment;
- while (XmlPullParser::isGoodEvent(parser->next())) {
+ const size_t depth = parser->getDepth();
+ while (XmlPullParser::nextChildNode(parser, depth)) {
const XmlPullParser::Event event = parser->getEvent();
if (event == XmlPullParser::Event::kComment) {
comment = parser->getComment();
@@ -603,134 +244,153 @@
if (event == XmlPullParser::Event::kText) {
if (!util::trimWhitespace(parser->getText()).empty()) {
- comment = u"";
+ mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
+ << "plain text not allowed here");
+ error = true;
}
continue;
}
- if (event != XmlPullParser::Event::kStartElement) {
- continue;
- }
+ assert(event == XmlPullParser::Event::kStartElement);
- ScopedXmlPullParser childParser(parser);
-
- if (!childParser.getElementNamespace().empty()) {
+ if (!parser->getElementNamespace().empty()) {
// Skip unknown namespace.
continue;
}
- StringPiece16 name = childParser.getElementName();
- if (name == u"skip" || name == u"eat-comment") {
+ std::u16string elementName = parser->getElementName();
+ if (elementName == u"skip" || elementName == u"eat-comment") {
+ comment = u"";
continue;
}
- if (name == u"private-symbols") {
- // Handle differently.
- mLogger.note(childParser.getLineNumber())
- << "got a <private-symbols> tag."
- << std::endl;
- continue;
- }
-
- const auto endAttrIter = childParser.endAttributes();
- auto attrIter = childParser.findAttribute(u"", u"name");
- if (attrIter == endAttrIter || attrIter->value.empty()) {
- mLogger.error(childParser.getLineNumber())
- << "<" << name << "> tag must have a 'name' attribute."
- << std::endl;
- success = false;
- continue;
- }
-
- // Copy because our iterator will go out of scope when
- // we parse more XML.
- std::u16string attributeName = attrIter->value;
-
- if (name == u"item") {
+ if (elementName == u"item") {
// Items simply have their type encoded in the type attribute.
- auto typeIter = childParser.findAttribute(u"", u"type");
- if (typeIter == endAttrIter || typeIter->value.empty()) {
- mLogger.error(childParser.getLineNumber())
- << "<item> must have a 'type' attribute."
- << std::endl;
- success = false;
+ if (Maybe<StringPiece16> maybeType = findNonEmptyAttribute(parser, u"type")) {
+ elementName = maybeType.value().toString();
+ } else {
+ mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
+ << "<item> must have a 'type' attribute");
+ error = true;
continue;
}
- name = typeIter->value;
}
- if (name == u"id") {
- success &= mTable->addResource(ResourceNameRef{ {}, ResourceType::kId, attributeName },
- {}, mSource.line(childParser.getLineNumber()),
- util::make_unique<Id>());
- } else if (name == u"string") {
- success &= parseString(&childParser,
- ResourceNameRef{ {}, ResourceType::kString, attributeName });
- } else if (name == u"color") {
- success &= parseColor(&childParser,
- ResourceNameRef{ {}, ResourceType::kColor, attributeName });
- } else if (name == u"drawable") {
- success &= parseColor(&childParser,
- ResourceNameRef{ {}, ResourceType::kDrawable, attributeName });
- } else if (name == u"bool") {
- success &= parsePrimitive(&childParser,
- ResourceNameRef{ {}, ResourceType::kBool, attributeName });
- } else if (name == u"integer") {
- success &= parsePrimitive(
- &childParser,
- ResourceNameRef{ {}, ResourceType::kInteger, attributeName });
- } else if (name == u"dimen") {
- success &= parsePrimitive(&childParser,
- ResourceNameRef{ {}, ResourceType::kDimen, attributeName });
- } else if (name == u"fraction") {
-// success &= parsePrimitive(
-// &childParser,
-// ResourceNameRef{ {}, ResourceType::kFraction, attributeName });
- } else if (name == u"style") {
- success &= parseStyle(&childParser,
- ResourceNameRef{ {}, ResourceType::kStyle, attributeName });
- } else if (name == u"plurals") {
- success &= parsePlural(&childParser,
- ResourceNameRef{ {}, ResourceType::kPlurals, attributeName });
- } else if (name == u"array") {
- success &= parseArray(&childParser,
- ResourceNameRef{ {}, ResourceType::kArray, attributeName },
- android::ResTable_map::TYPE_ANY);
- } else if (name == u"string-array") {
- success &= parseArray(&childParser,
- ResourceNameRef{ {}, ResourceType::kArray, attributeName },
- android::ResTable_map::TYPE_STRING);
- } else if (name == u"integer-array") {
- success &= parseArray(&childParser,
- ResourceNameRef{ {}, ResourceType::kArray, attributeName },
- android::ResTable_map::TYPE_INTEGER);
- } else if (name == u"public") {
- success &= parsePublic(&childParser, attributeName);
- } else if (name == u"declare-styleable") {
- success &= parseDeclareStyleable(
- &childParser,
- ResourceNameRef{ {}, ResourceType::kStyleable, attributeName });
- } else if (name == u"attr") {
- success &= parseAttr(&childParser,
- ResourceNameRef{ {}, ResourceType::kAttr, attributeName });
- } else if (name == u"bag") {
- } else if (name == u"public-padding") {
- } else if (name == u"java-symbol") {
- } else if (name == u"add-resource") {
- }
+ ParsedResource parsedResource;
+ parsedResource.source = mSource.withLine(parser->getLineNumber());
+ parsedResource.comment = std::move(comment);
+
+ if (Maybe<StringPiece16> maybeName = findNonEmptyAttribute(parser, u"name")) {
+ parsedResource.name.entry = maybeName.value().toString();
+
+ } else if (elementName != u"public-group") {
+ mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
+ << "<" << elementName << "> tag must have a 'name' attribute");
+ error = true;
+ continue;
+ }
+
+ // Check if we should skip this product.
+ const bool stripResource = shouldStripResource(parser, mOptions.product);
+
+ bool result = true;
+ if (elementName == u"id") {
+ parsedResource.name.type = ResourceType::kId;
+ parsedResource.value = util::make_unique<Id>();
+ } else if (elementName == u"string") {
+ parsedResource.name.type = ResourceType::kString;
+ result = parseString(parser, &parsedResource);
+ } else if (elementName == u"color") {
+ parsedResource.name.type = ResourceType::kColor;
+ result = parseColor(parser, &parsedResource);
+ } else if (elementName == u"drawable") {
+ parsedResource.name.type = ResourceType::kDrawable;
+ result = parseColor(parser, &parsedResource);
+ } else if (elementName == u"bool") {
+ parsedResource.name.type = ResourceType::kBool;
+ result = parsePrimitive(parser, &parsedResource);
+ } else if (elementName == u"integer") {
+ parsedResource.name.type = ResourceType::kInteger;
+ result = parsePrimitive(parser, &parsedResource);
+ } else if (elementName == u"dimen") {
+ parsedResource.name.type = ResourceType::kDimen;
+ result = parsePrimitive(parser, &parsedResource);
+ } else if (elementName == u"fraction") {
+ parsedResource.name.type = ResourceType::kFraction;
+ result = parsePrimitive(parser, &parsedResource);
+ } else if (elementName == u"style") {
+ parsedResource.name.type = ResourceType::kStyle;
+ result = parseStyle(parser, &parsedResource);
+ } else if (elementName == u"plurals") {
+ parsedResource.name.type = ResourceType::kPlurals;
+ result = parsePlural(parser, &parsedResource);
+ } else if (elementName == u"array") {
+ parsedResource.name.type = ResourceType::kArray;
+ result = parseArray(parser, &parsedResource, android::ResTable_map::TYPE_ANY);
+ } else if (elementName == u"string-array") {
+ parsedResource.name.type = ResourceType::kArray;
+ result = parseArray(parser, &parsedResource, android::ResTable_map::TYPE_STRING);
+ } else if (elementName == u"integer-array") {
+ parsedResource.name.type = ResourceType::kArray;
+ result = parseArray(parser, &parsedResource, android::ResTable_map::TYPE_INTEGER);
+ } else if (elementName == u"declare-styleable") {
+ parsedResource.name.type = ResourceType::kStyleable;
+ result = parseDeclareStyleable(parser, &parsedResource);
+ } else if (elementName == u"attr") {
+ parsedResource.name.type = ResourceType::kAttr;
+ result = parseAttr(parser, &parsedResource);
+ } else if (elementName == u"public") {
+ result = parsePublic(parser, &parsedResource);
+ } else if (elementName == u"java-symbol" || elementName == u"symbol") {
+ result = parseSymbol(parser, &parsedResource);
+ } else if (elementName == u"public-group") {
+ result = parsePublicGroup(parser, &parsedResource);
+ } else {
+ // Try parsing the elementName (or type) as a resource. These shall only be
+ // resources like 'layout' or 'xml' and they can only be references.
+ if (const ResourceType* type = parseResourceType(elementName)) {
+ parsedResource.name.type = *type;
+ parsedResource.value = parseXml(parser, android::ResTable_map::TYPE_REFERENCE,
+ false);
+ if (!parsedResource.value) {
+ mDiag->error(DiagMessage(parsedResource.source) << "invalid value for type '"
+ << *type << "'. Expected a reference");
+ result = false;
+ }
+ } else {
+ mDiag->warn(DiagMessage(mSource.withLine(parser->getLineNumber()))
+ << "unknown resource type '" << elementName << "'");
+ }
+ }
+
+ if (result) {
+ // We successfully parsed the resource.
+
+ if (stripResource) {
+ // Record that we stripped out this resource name.
+ // We will check that at least one variant of this resource was included.
+ strippedResources.insert(parsedResource.name);
+ } else {
+ error |= !addResourcesToTable(mTable, mConfig, mDiag, &parsedResource);
+ }
+ } else {
+ error = true;
+ }
}
- if (parser->getEvent() == XmlPullParser::Event::kBadDocument) {
- mLogger.error(parser->getLineNumber())
- << parser->getLastError()
- << std::endl;
- return false;
+ // Check that we included at least one variant of each stripped resource.
+ for (const ResourceName& strippedResource : strippedResources) {
+ if (!mTable->findResource(strippedResource)) {
+ // Failed to find the resource.
+ mDiag->error(DiagMessage(mSource) << "resource '" << strippedResource << "' "
+ "was filtered out but no product variant remains");
+ error = true;
+ }
}
- return success;
+
+ return !error;
}
-
-
enum {
kAllowRawString = true,
kNoRawString = false
@@ -743,8 +403,8 @@
* an Item. If allowRawValue is false, nullptr is returned in this
* case.
*/
-std::unique_ptr<Item> ResourceParser::parseXml(XmlPullParser* parser, uint32_t typeMask,
- bool allowRawValue) {
+std::unique_ptr<Item> ResourceParser::parseXml(XmlPullParser* parser, const uint32_t typeMask,
+ const bool allowRawValue) {
const size_t beginXmlLine = parser->getLineNumber();
std::u16string rawValue;
@@ -753,34 +413,30 @@
return {};
}
- StringPool& pool = mTable->getValueStringPool();
-
if (!styleString.spans.empty()) {
// This can only be a StyledString.
return util::make_unique<StyledString>(
- pool.makeRef(styleString, StringPool::Context{ 1, mConfig }));
+ mTable->stringPool.makeRef(styleString, StringPool::Context{ 1, mConfig }));
}
auto onCreateReference = [&](const ResourceName& name) {
// name.package can be empty here, as it will assume the package name of the table.
- mTable->addResource(name, {}, mSource.line(beginXmlLine), util::make_unique<Id>());
+ std::unique_ptr<Id> id = util::make_unique<Id>();
+ id->setSource(mSource.withLine(beginXmlLine));
+ mTable->addResource(name, {}, std::move(id), mDiag);
};
// Process the raw value.
- std::unique_ptr<Item> processedItem = parseItemForAttribute(rawValue, typeMask,
- onCreateReference);
+ std::unique_ptr<Item> processedItem = ResourceUtils::parseItemForAttribute(rawValue, typeMask,
+ onCreateReference);
if (processedItem) {
// Fix up the reference.
- visitFunc<Reference>(*processedItem, [&](Reference& ref) {
- if (!ref.name.package.empty()) {
- // The package name was set, so lookup its alias.
- parser->applyPackageAlias(&ref.name.package, mTable->getPackage());
- } else {
- // The package name was left empty, so it assumes the default package
- // without alias lookup.
- ref.name.package = mTable->getPackage();
+ if (Reference* ref = valueCast<Reference>(processedItem.get())) {
+ if (Maybe<ResourceName> transformedName =
+ parser->transformPackage(ref->name.value(), u"")) {
+ ref->name = std::move(transformedName);
}
- });
+ }
return processedItem;
}
@@ -788,142 +444,248 @@
if (typeMask & android::ResTable_map::TYPE_STRING) {
// Use the trimmed, escaped string.
return util::make_unique<String>(
- pool.makeRef(styleString.str, StringPool::Context{ 1, mConfig }));
+ mTable->stringPool.makeRef(styleString.str, StringPool::Context{ 1, mConfig }));
}
- // We can't parse this so return a RawString if we are allowed.
if (allowRawValue) {
+ // We can't parse this so return a RawString if we are allowed.
return util::make_unique<RawString>(
- pool.makeRef(rawValue, StringPool::Context{ 1, mConfig }));
+ mTable->stringPool.makeRef(rawValue, StringPool::Context{ 1, mConfig }));
}
+
return {};
}
-bool ResourceParser::parseString(XmlPullParser* parser, const ResourceNameRef& resourceName) {
- const SourceLine source = mSource.line(parser->getLineNumber());
+bool ResourceParser::parseString(XmlPullParser* parser, ParsedResource* outResource) {
+ const Source source = mSource.withLine(parser->getLineNumber());
- // Mark the string as untranslateable if needed.
- const auto endAttrIter = parser->endAttributes();
- auto attrIter = parser->findAttribute(u"", u"untranslateable");
- // bool untranslateable = attrIter != endAttrIter;
- // TODO(adamlesinski): Do something with this (mark the string).
-
- // Deal with the product.
- attrIter = parser->findAttribute(u"", u"product");
- if (attrIter != endAttrIter) {
- if (attrIter->value != u"default" && attrIter->value != u"phone") {
- // TODO(adamlesinski): Match products.
- return true;
- }
- }
-
- std::unique_ptr<Item> processedItem = parseXml(parser, android::ResTable_map::TYPE_STRING,
- kNoRawString);
- if (!processedItem) {
- mLogger.error(source.line)
- << "not a valid string."
- << std::endl;
- return false;
- }
-
- return mTable->addResource(resourceName, mConfig, source, std::move(processedItem));
-}
-
-bool ResourceParser::parseColor(XmlPullParser* parser, const ResourceNameRef& resourceName) {
- const SourceLine source = mSource.line(parser->getLineNumber());
-
- std::unique_ptr<Item> item = parseXml(parser, android::ResTable_map::TYPE_COLOR, kNoRawString);
- if (!item) {
- mLogger.error(source.line) << "invalid color." << std::endl;
- return false;
- }
- return mTable->addResource(resourceName, mConfig, source, std::move(item));
-}
-
-bool ResourceParser::parsePrimitive(XmlPullParser* parser, const ResourceNameRef& resourceName) {
- const SourceLine source = mSource.line(parser->getLineNumber());
-
- uint32_t typeMask = 0;
- switch (resourceName.type) {
- case ResourceType::kInteger:
- typeMask |= android::ResTable_map::TYPE_INTEGER;
- break;
-
- case ResourceType::kDimen:
- typeMask |= android::ResTable_map::TYPE_DIMENSION
- | android::ResTable_map::TYPE_FLOAT
- | android::ResTable_map::TYPE_FRACTION;
- break;
-
- case ResourceType::kBool:
- typeMask |= android::ResTable_map::TYPE_BOOLEAN;
- break;
-
- default:
- assert(false);
- break;
- }
-
- std::unique_ptr<Item> item = parseXml(parser, typeMask, kNoRawString);
- if (!item) {
- mLogger.error(source.line)
- << "invalid "
- << resourceName.type
- << "."
- << std::endl;
- return false;
- }
-
- return mTable->addResource(resourceName, mConfig, source, std::move(item));
-}
-
-bool ResourceParser::parsePublic(XmlPullParser* parser, const StringPiece16& name) {
- const SourceLine source = mSource.line(parser->getLineNumber());
-
- const auto endAttrIter = parser->endAttributes();
- const auto typeAttrIter = parser->findAttribute(u"", u"type");
- if (typeAttrIter == endAttrIter || typeAttrIter->value.empty()) {
- mLogger.error(source.line)
- << "<public> must have a 'type' attribute."
- << std::endl;
- return false;
- }
-
- const ResourceType* parsedType = parseResourceType(typeAttrIter->value);
- if (!parsedType) {
- mLogger.error(source.line)
- << "invalid resource type '"
- << typeAttrIter->value
- << "' in <public>."
- << std::endl;
- return false;
- }
-
- ResourceNameRef resourceName { {}, *parsedType, name };
- ResourceId resourceId;
-
- const auto idAttrIter = parser->findAttribute(u"", u"id");
- if (idAttrIter != endAttrIter && !idAttrIter->value.empty()) {
- android::Res_value val;
- bool result = android::ResTable::stringToInt(idAttrIter->value.data(),
- idAttrIter->value.size(), &val);
- resourceId.id = val.data;
- if (!result || !resourceId.isValid()) {
- mLogger.error(source.line)
- << "invalid resource ID '"
- << idAttrIter->value
- << "' in <public>."
- << std::endl;
+ bool formatted = true;
+ if (Maybe<StringPiece16> formattedAttr = findAttribute(parser, u"formatted")) {
+ if (!ResourceUtils::tryParseBool(formattedAttr.value(), &formatted)) {
+ mDiag->error(DiagMessage(source) << "invalid value for 'formatted'. Must be a boolean");
return false;
}
}
- if (*parsedType == ResourceType::kId) {
- // An ID marked as public is also the definition of an ID.
- mTable->addResource(resourceName, {}, source, util::make_unique<Id>());
+ bool translateable = mOptions.translatable;
+ if (Maybe<StringPiece16> translateableAttr = findAttribute(parser, u"translatable")) {
+ if (!ResourceUtils::tryParseBool(translateableAttr.value(), &translateable)) {
+ mDiag->error(DiagMessage(source)
+ << "invalid value for 'translatable'. Must be a boolean");
+ return false;
+ }
}
- return mTable->markPublic(resourceName, resourceId, source);
+ outResource->value = parseXml(parser, android::ResTable_map::TYPE_STRING, kNoRawString);
+ if (!outResource->value) {
+ mDiag->error(DiagMessage(source) << "not a valid string");
+ return false;
+ }
+
+ if (formatted && translateable) {
+ if (String* stringValue = valueCast<String>(outResource->value.get())) {
+ if (!util::verifyJavaStringFormat(*stringValue->value)) {
+ mDiag->error(DiagMessage(source)
+ << "multiple substitutions specified in non-positional format; "
+ "did you mean to add the formatted=\"false\" attribute?");
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool ResourceParser::parseColor(XmlPullParser* parser, ParsedResource* outResource) {
+ const Source source = mSource.withLine(parser->getLineNumber());
+
+ outResource->value = parseXml(parser, android::ResTable_map::TYPE_COLOR, kNoRawString);
+ if (!outResource->value) {
+ mDiag->error(DiagMessage(source) << "invalid color");
+ return false;
+ }
+ return true;
+}
+
+bool ResourceParser::parsePrimitive(XmlPullParser* parser, ParsedResource* outResource) {
+ const Source source = mSource.withLine(parser->getLineNumber());
+
+ uint32_t typeMask = 0;
+ switch (outResource->name.type) {
+ case ResourceType::kInteger:
+ typeMask |= android::ResTable_map::TYPE_INTEGER;
+ break;
+
+ case ResourceType::kFraction:
+ // fallthrough
+ case ResourceType::kDimen:
+ typeMask |= android::ResTable_map::TYPE_DIMENSION
+ | android::ResTable_map::TYPE_FLOAT
+ | android::ResTable_map::TYPE_FRACTION;
+ break;
+
+ case ResourceType::kBool:
+ typeMask |= android::ResTable_map::TYPE_BOOLEAN;
+ break;
+
+ default:
+ assert(false);
+ break;
+ }
+
+ outResource->value = parseXml(parser, typeMask, kNoRawString);
+ if (!outResource->value) {
+ mDiag->error(DiagMessage(source) << "invalid " << outResource->name.type);
+ return false;
+ }
+ return true;
+}
+
+bool ResourceParser::parsePublic(XmlPullParser* parser, ParsedResource* outResource) {
+ const Source source = mSource.withLine(parser->getLineNumber());
+
+ Maybe<StringPiece16> maybeType = findNonEmptyAttribute(parser, u"type");
+ if (!maybeType) {
+ mDiag->error(DiagMessage(source) << "<public> must have a 'type' attribute");
+ return false;
+ }
+
+ const ResourceType* parsedType = parseResourceType(maybeType.value());
+ if (!parsedType) {
+ mDiag->error(DiagMessage(source) << "invalid resource type '" << maybeType.value()
+ << "' in <public>");
+ return false;
+ }
+
+ outResource->name.type = *parsedType;
+
+ if (Maybe<StringPiece16> maybeId = findNonEmptyAttribute(parser, u"id")) {
+ android::Res_value val;
+ bool result = android::ResTable::stringToInt(maybeId.value().data(),
+ maybeId.value().size(), &val);
+ ResourceId resourceId(val.data);
+ if (!result || !resourceId.isValid()) {
+ mDiag->error(DiagMessage(source) << "invalid resource ID '" << maybeId.value()
+ << "' in <public>");
+ return false;
+ }
+ outResource->id = resourceId;
+ }
+
+ if (*parsedType == ResourceType::kId) {
+ // An ID marked as public is also the definition of an ID.
+ outResource->value = util::make_unique<Id>();
+ }
+
+ outResource->symbolState = SymbolState::kPublic;
+ return true;
+}
+
+bool ResourceParser::parsePublicGroup(XmlPullParser* parser, ParsedResource* outResource) {
+ const Source source = mSource.withLine(parser->getLineNumber());
+
+ Maybe<StringPiece16> maybeType = findNonEmptyAttribute(parser, u"type");
+ if (!maybeType) {
+ mDiag->error(DiagMessage(source) << "<public-group> must have a 'type' attribute");
+ return false;
+ }
+
+ const ResourceType* parsedType = parseResourceType(maybeType.value());
+ if (!parsedType) {
+ mDiag->error(DiagMessage(source) << "invalid resource type '" << maybeType.value()
+ << "' in <public-group>");
+ return false;
+ }
+
+ Maybe<StringPiece16> maybeId = findNonEmptyAttribute(parser, u"first-id");
+ if (!maybeId) {
+ mDiag->error(DiagMessage(source) << "<public-group> must have a 'first-id' attribute");
+ return false;
+ }
+
+ android::Res_value val;
+ bool result = android::ResTable::stringToInt(maybeId.value().data(),
+ maybeId.value().size(), &val);
+ ResourceId nextId(val.data);
+ if (!result || !nextId.isValid()) {
+ mDiag->error(DiagMessage(source) << "invalid resource ID '" << maybeId.value()
+ << "' in <public-group>");
+ return false;
+ }
+
+ std::u16string comment;
+ bool error = false;
+ const size_t depth = parser->getDepth();
+ while (XmlPullParser::nextChildNode(parser, depth)) {
+ if (parser->getEvent() == XmlPullParser::Event::kComment) {
+ comment = util::trimWhitespace(parser->getComment()).toString();
+ continue;
+ } else if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
+ // Skip text.
+ continue;
+ }
+
+ const Source itemSource = mSource.withLine(parser->getLineNumber());
+ const std::u16string& elementNamespace = parser->getElementNamespace();
+ const std::u16string& elementName = parser->getElementName();
+ if (elementNamespace.empty() && elementName == u"public") {
+ Maybe<StringPiece16> maybeName = findNonEmptyAttribute(parser, u"name");
+ if (!maybeName) {
+ mDiag->error(DiagMessage(itemSource) << "<public> must have a 'name' attribute");
+ error = true;
+ continue;
+ }
+
+ if (findNonEmptyAttribute(parser, u"id")) {
+ mDiag->error(DiagMessage(itemSource) << "'id' is ignored within <public-group>");
+ error = true;
+ continue;
+ }
+
+ if (findNonEmptyAttribute(parser, u"type")) {
+ mDiag->error(DiagMessage(itemSource) << "'type' is ignored within <public-group>");
+ error = true;
+ continue;
+ }
+
+ ParsedResource childResource;
+ childResource.name.type = *parsedType;
+ childResource.name.entry = maybeName.value().toString();
+ childResource.id = nextId;
+ childResource.comment = std::move(comment);
+ childResource.source = itemSource;
+ childResource.symbolState = SymbolState::kPublic;
+ outResource->childResources.push_back(std::move(childResource));
+
+ nextId.id += 1;
+
+ } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
+ mDiag->error(DiagMessage(itemSource) << ":" << elementName << ">");
+ error = true;
+ }
+ }
+ return !error;
+}
+
+bool ResourceParser::parseSymbol(XmlPullParser* parser, ParsedResource* outResource) {
+ const Source source = mSource.withLine(parser->getLineNumber());
+
+ Maybe<StringPiece16> maybeType = findNonEmptyAttribute(parser, u"type");
+ if (!maybeType) {
+ mDiag->error(DiagMessage(source) << "<" << parser->getElementName() << "> must have a "
+ "'type' attribute");
+ return false;
+ }
+
+ const ResourceType* parsedType = parseResourceType(maybeType.value());
+ if (!parsedType) {
+ mDiag->error(DiagMessage(source) << "invalid resource type '" << maybeType.value()
+ << "' in <" << parser->getElementName() << ">");
+ return false;
+ }
+
+ outResource->name.type = *parsedType;
+ outResource->symbolState = SymbolState::kPrivate;
+ return true;
}
static uint32_t parseFormatType(const StringPiece16& piece) {
@@ -953,150 +715,137 @@
return mask;
}
-bool ResourceParser::parseAttr(XmlPullParser* parser, const ResourceNameRef& resourceName) {
- const SourceLine source = mSource.line(parser->getLineNumber());
- ResourceName actualName = resourceName.toResourceName();
- std::unique_ptr<Attribute> attr = parseAttrImpl(parser, &actualName, false);
- if (!attr) {
- return false;
- }
- return mTable->addResource(actualName, mConfig, source, std::move(attr));
+
+
+bool ResourceParser::parseAttr(XmlPullParser* parser, ParsedResource* outResource) {
+ outResource->source = mSource.withLine(parser->getLineNumber());
+ return parseAttrImpl(parser, outResource, false);
}
-std::unique_ptr<Attribute> ResourceParser::parseAttrImpl(XmlPullParser* parser,
- ResourceName* resourceName,
- bool weak) {
+bool ResourceParser::parseAttrImpl(XmlPullParser* parser, ParsedResource* outResource, bool weak) {
uint32_t typeMask = 0;
- const auto endAttrIter = parser->endAttributes();
- const auto formatAttrIter = parser->findAttribute(u"", u"format");
- if (formatAttrIter != endAttrIter) {
- typeMask = parseFormatAttribute(formatAttrIter->value);
+ Maybe<StringPiece16> maybeFormat = findAttribute(parser, u"format");
+ if (maybeFormat) {
+ typeMask = parseFormatAttribute(maybeFormat.value());
if (typeMask == 0) {
- mLogger.error(parser->getLineNumber())
- << "invalid attribute format '"
- << formatAttrIter->value
- << "'."
- << std::endl;
- return {};
+ mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
+ << "invalid attribute format '" << maybeFormat.value() << "'");
+ return false;
}
}
// If this is a declaration, the package name may be in the name. Separate these out.
// Eg. <attr name="android:text" />
// No format attribute is allowed.
- if (weak && formatAttrIter == endAttrIter) {
+ if (weak && !maybeFormat) {
StringPiece16 package, type, name;
- extractResourceName(resourceName->entry, &package, &type, &name);
+ ResourceUtils::extractResourceName(outResource->name.entry, &package, &type, &name);
if (type.empty() && !package.empty()) {
- resourceName->package = package.toString();
- resourceName->entry = name.toString();
+ outResource->name.package = package.toString();
+ outResource->name.entry = name.toString();
}
}
std::vector<Attribute::Symbol> items;
+ std::u16string comment;
bool error = false;
- while (XmlPullParser::isGoodEvent(parser->next())) {
- if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
+ const size_t depth = parser->getDepth();
+ while (XmlPullParser::nextChildNode(parser, depth)) {
+ if (parser->getEvent() == XmlPullParser::Event::kComment) {
+ comment = util::trimWhitespace(parser->getComment()).toString();
+ continue;
+ } else if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
+ // Skip text.
continue;
}
- ScopedXmlPullParser childParser(parser);
+ const Source itemSource = mSource.withLine(parser->getLineNumber());
+ const std::u16string& elementNamespace = parser->getElementNamespace();
+ const std::u16string& elementName = parser->getElementName();
+ if (elementNamespace.empty() && (elementName == u"flag" || elementName == u"enum")) {
+ if (elementName == u"enum") {
+ if (typeMask & android::ResTable_map::TYPE_FLAGS) {
+ mDiag->error(DiagMessage(itemSource)
+ << "can not define an <enum>; already defined a <flag>");
+ error = true;
+ continue;
+ }
+ typeMask |= android::ResTable_map::TYPE_ENUM;
- const std::u16string& name = childParser.getElementName();
- if (!childParser.getElementNamespace().empty()
- || (name != u"flag" && name != u"enum")) {
- mLogger.error(childParser.getLineNumber())
- << "unexpected tag <"
- << name
- << "> in <attr>."
- << std::endl;
- error = true;
- continue;
- }
-
- if (name == u"enum") {
- if (typeMask & android::ResTable_map::TYPE_FLAGS) {
- mLogger.error(childParser.getLineNumber())
- << "can not define an <enum>; already defined a <flag>."
- << std::endl;
- error = true;
- continue;
+ } else if (elementName == u"flag") {
+ if (typeMask & android::ResTable_map::TYPE_ENUM) {
+ mDiag->error(DiagMessage(itemSource)
+ << "can not define a <flag>; already defined an <enum>");
+ error = true;
+ continue;
+ }
+ typeMask |= android::ResTable_map::TYPE_FLAGS;
}
- typeMask |= android::ResTable_map::TYPE_ENUM;
- } else if (name == u"flag") {
- if (typeMask & android::ResTable_map::TYPE_ENUM) {
- mLogger.error(childParser.getLineNumber())
- << "can not define a <flag>; already defined an <enum>."
- << std::endl;
- error = true;
- continue;
- }
- typeMask |= android::ResTable_map::TYPE_FLAGS;
- }
- Attribute::Symbol item;
- if (parseEnumOrFlagItem(&childParser, name, &item)) {
- if (!mTable->addResource(item.symbol.name, mConfig,
- mSource.line(childParser.getLineNumber()),
- util::make_unique<Id>())) {
- error = true;
+ if (Maybe<Attribute::Symbol> s = parseEnumOrFlagItem(parser, elementName)) {
+ ParsedResource childResource;
+ childResource.name = s.value().symbol.name.value();
+ childResource.source = itemSource;
+ childResource.value = util::make_unique<Id>();
+ outResource->childResources.push_back(std::move(childResource));
+
+ s.value().symbol.setComment(std::move(comment));
+ s.value().symbol.setSource(itemSource);
+ items.push_back(std::move(s.value()));
} else {
- items.push_back(std::move(item));
+ error = true;
}
- } else {
+ } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
+ mDiag->error(DiagMessage(itemSource) << ":" << elementName << ">");
error = true;
}
+
+ comment = {};
}
if (error) {
- return {};
+ return false;
}
std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(weak);
attr->symbols.swap(items);
attr->typeMask = typeMask ? typeMask : uint32_t(android::ResTable_map::TYPE_ANY);
- return attr;
-}
-
-bool ResourceParser::parseEnumOrFlagItem(XmlPullParser* parser, const StringPiece16& tag,
- Attribute::Symbol* outSymbol) {
- const auto attrIterEnd = parser->endAttributes();
- const auto nameAttrIter = parser->findAttribute(u"", u"name");
- if (nameAttrIter == attrIterEnd || nameAttrIter->value.empty()) {
- mLogger.error(parser->getLineNumber())
- << "no attribute 'name' found for tag <" << tag << ">."
- << std::endl;
- return false;
- }
-
- const auto valueAttrIter = parser->findAttribute(u"", u"value");
- if (valueAttrIter == attrIterEnd || valueAttrIter->value.empty()) {
- mLogger.error(parser->getLineNumber())
- << "no attribute 'value' found for tag <" << tag << ">."
- << std::endl;
- return false;
- }
-
- android::Res_value val;
- if (!android::ResTable::stringToInt(valueAttrIter->value.data(),
- valueAttrIter->value.size(), &val)) {
- mLogger.error(parser->getLineNumber())
- << "invalid value '"
- << valueAttrIter->value
- << "' for <" << tag << ">; must be an integer."
- << std::endl;
- return false;
- }
-
- outSymbol->symbol.name = ResourceName {
- mTable->getPackage(), ResourceType::kId, nameAttrIter->value };
- outSymbol->value = val.data;
+ outResource->value = std::move(attr);
return true;
}
-static bool parseXmlAttributeName(StringPiece16 str, ResourceName* outName) {
+Maybe<Attribute::Symbol> ResourceParser::parseEnumOrFlagItem(XmlPullParser* parser,
+ const StringPiece16& tag) {
+ const Source source = mSource.withLine(parser->getLineNumber());
+
+ Maybe<StringPiece16> maybeName = findNonEmptyAttribute(parser, u"name");
+ if (!maybeName) {
+ mDiag->error(DiagMessage(source) << "no attribute 'name' found for tag <" << tag << ">");
+ return {};
+ }
+
+ Maybe<StringPiece16> maybeValue = findNonEmptyAttribute(parser, u"value");
+ if (!maybeValue) {
+ mDiag->error(DiagMessage(source) << "no attribute 'value' found for tag <" << tag << ">");
+ return {};
+ }
+
+ android::Res_value val;
+ if (!android::ResTable::stringToInt(maybeValue.value().data(),
+ maybeValue.value().size(), &val)) {
+ mDiag->error(DiagMessage(source) << "invalid value '" << maybeValue.value()
+ << "' for <" << tag << ">; must be an integer");
+ return {};
+ }
+
+ return Attribute::Symbol{
+ Reference(ResourceName({}, ResourceType::kId, maybeName.value().toString())),
+ val.data };
+}
+
+static Maybe<ResourceName> parseXmlAttributeName(StringPiece16 str) {
str = util::trimWhitespace(str);
const char16_t* const start = str.data();
const char16_t* const end = start + str.size();
@@ -1113,289 +862,280 @@
p++;
}
- outName->package = package.toString();
- outName->type = ResourceType::kAttr;
- if (name.size() == 0) {
- outName->entry = str.toString();
- } else {
- outName->entry = name.toString();
- }
- return true;
+ return ResourceName(package.toString(), ResourceType::kAttr,
+ name.empty() ? str.toString() : name.toString());
}
-bool ResourceParser::parseUntypedItem(XmlPullParser* parser, Style& style) {
- const auto endAttrIter = parser->endAttributes();
- const auto nameAttrIter = parser->findAttribute(u"", u"name");
- if (nameAttrIter == endAttrIter || nameAttrIter->value.empty()) {
- mLogger.error(parser->getLineNumber())
- << "<item> must have a 'name' attribute."
- << std::endl;
+bool ResourceParser::parseStyleItem(XmlPullParser* parser, Style* style) {
+ const Source source = mSource.withLine(parser->getLineNumber());
+
+ Maybe<StringPiece16> maybeName = findNonEmptyAttribute(parser, u"name");
+ if (!maybeName) {
+ mDiag->error(DiagMessage(source) << "<item> must have a 'name' attribute");
return false;
}
- ResourceName key;
- if (!parseXmlAttributeName(nameAttrIter->value, &key)) {
- mLogger.error(parser->getLineNumber())
- << "invalid attribute name '"
- << nameAttrIter->value
- << "'."
- << std::endl;
+ Maybe<ResourceName> maybeKey = parseXmlAttributeName(maybeName.value());
+ if (!maybeKey) {
+ mDiag->error(DiagMessage(source) << "invalid attribute name '" << maybeName.value() << "'");
return false;
}
- if (!key.package.empty()) {
- // We have a package name set, so lookup its alias.
- parser->applyPackageAlias(&key.package, mTable->getPackage());
- } else {
- // The package name was omitted, so use the default package name with
- // no alias lookup.
- key.package = mTable->getPackage();
+ if (Maybe<ResourceName> transformedName = parser->transformPackage(maybeKey.value(), u"")) {
+ maybeKey = std::move(transformedName);
}
std::unique_ptr<Item> value = parseXml(parser, 0, kAllowRawString);
if (!value) {
+ mDiag->error(DiagMessage(source) << "could not parse style item");
return false;
}
- style.entries.push_back(Style::Entry{ Reference(key), std::move(value) });
+ style->entries.push_back(Style::Entry{ Reference(maybeKey.value()), std::move(value) });
return true;
}
-bool ResourceParser::parseStyle(XmlPullParser* parser, const ResourceNameRef& resourceName) {
- const SourceLine source = mSource.line(parser->getLineNumber());
+bool ResourceParser::parseStyle(XmlPullParser* parser, ParsedResource* outResource) {
+ const Source source = mSource.withLine(parser->getLineNumber());
std::unique_ptr<Style> style = util::make_unique<Style>();
- const auto endAttrIter = parser->endAttributes();
- const auto parentAttrIter = parser->findAttribute(u"", u"parent");
- if (parentAttrIter != endAttrIter) {
- std::string errStr;
- if (!parseStyleParentReference(parentAttrIter->value, &style->parent, &errStr)) {
- mLogger.error(source.line) << errStr << "." << std::endl;
- return false;
+ Maybe<StringPiece16> maybeParent = findAttribute(parser, u"parent");
+ if (maybeParent) {
+ // If the parent is empty, we don't have a parent, but we also don't infer either.
+ if (!maybeParent.value().empty()) {
+ std::string errStr;
+ style->parent = ResourceUtils::parseStyleParentReference(maybeParent.value(), &errStr);
+ if (!style->parent) {
+ mDiag->error(DiagMessage(source) << errStr);
+ return false;
+ }
+
+ if (Maybe<ResourceName> transformedName =
+ parser->transformPackage(style->parent.value().name.value(), u"")) {
+ style->parent.value().name = std::move(transformedName);
+ }
}
- if (!style->parent.name.package.empty()) {
- // Try to interpret the package name as an alias. These take precedence.
- parser->applyPackageAlias(&style->parent.name.package, mTable->getPackage());
- } else {
- // If no package is specified, this can not be an alias and is the local package.
- style->parent.name.package = mTable->getPackage();
- }
} else {
// No parent was specified, so try inferring it from the style name.
- std::u16string styleName = resourceName.entry.toString();
+ std::u16string styleName = outResource->name.entry;
size_t pos = styleName.find_last_of(u'.');
if (pos != std::string::npos) {
style->parentInferred = true;
- style->parent.name.package = mTable->getPackage();
- style->parent.name.type = ResourceType::kStyle;
- style->parent.name.entry = styleName.substr(0, pos);
+ style->parent = Reference(
+ ResourceName({}, ResourceType::kStyle, styleName.substr(0, pos)));
}
}
- bool success = true;
- while (XmlPullParser::isGoodEvent(parser->next())) {
- if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
- continue;
- }
-
- ScopedXmlPullParser childParser(parser);
- const std::u16string& name = childParser.getElementName();
- if (name == u"item") {
- success &= parseUntypedItem(&childParser, *style);
- } else {
- mLogger.error(childParser.getLineNumber())
- << "unexpected tag <"
- << name
- << "> in <style> resource."
- << std::endl;
- success = false;
- }
- }
-
- if (!success) {
- return false;
- }
-
- return mTable->addResource(resourceName, mConfig, source, std::move(style));
-}
-
-bool ResourceParser::parseArray(XmlPullParser* parser, const ResourceNameRef& resourceName,
- uint32_t typeMask) {
- const SourceLine source = mSource.line(parser->getLineNumber());
- std::unique_ptr<Array> array = util::make_unique<Array>();
-
bool error = false;
- while (XmlPullParser::isGoodEvent(parser->next())) {
+ const size_t depth = parser->getDepth();
+ while (XmlPullParser::nextChildNode(parser, depth)) {
if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
+ // Skip text and comments.
continue;
}
- ScopedXmlPullParser childParser(parser);
+ const std::u16string& elementNamespace = parser->getElementNamespace();
+ const std::u16string& elementName = parser->getElementName();
+ if (elementNamespace == u"" && elementName == u"item") {
+ error |= !parseStyleItem(parser, style.get());
- if (childParser.getElementName() != u"item") {
- mLogger.error(childParser.getLineNumber())
- << "unexpected tag <"
- << childParser.getElementName()
- << "> in <array> resource."
- << std::endl;
+ } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
+ mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
+ << ":" << elementName << ">");
error = true;
- continue;
}
-
- std::unique_ptr<Item> item = parseXml(&childParser, typeMask, kNoRawString);
- if (!item) {
- error = true;
- continue;
- }
- array->items.emplace_back(std::move(item));
}
if (error) {
return false;
}
- return mTable->addResource(resourceName, mConfig, source, std::move(array));
+ outResource->value = std::move(style);
+ return true;
}
-bool ResourceParser::parsePlural(XmlPullParser* parser, const ResourceNameRef& resourceName) {
- const SourceLine source = mSource.line(parser->getLineNumber());
+bool ResourceParser::parseArray(XmlPullParser* parser, ParsedResource* outResource,
+ uint32_t typeMask) {
+ const Source source = mSource.withLine(parser->getLineNumber());
+ std::unique_ptr<Array> array = util::make_unique<Array>();
+
+ bool error = false;
+ const size_t depth = parser->getDepth();
+ while (XmlPullParser::nextChildNode(parser, depth)) {
+ if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
+ // Skip text and comments.
+ continue;
+ }
+
+ const Source itemSource = mSource.withLine(parser->getLineNumber());
+ const std::u16string& elementNamespace = parser->getElementNamespace();
+ const std::u16string& elementName = parser->getElementName();
+ if (elementNamespace.empty() && elementName == u"item") {
+ std::unique_ptr<Item> item = parseXml(parser, typeMask, kNoRawString);
+ if (!item) {
+ mDiag->error(DiagMessage(itemSource) << "could not parse array item");
+ error = true;
+ continue;
+ }
+ item->setSource(itemSource);
+ array->items.emplace_back(std::move(item));
+
+ } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
+ mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
+ << "unknown tag <" << elementNamespace << ":" << elementName << ">");
+ error = true;
+ }
+ }
+
+ if (error) {
+ return false;
+ }
+
+ outResource->value = std::move(array);
+ return true;
+}
+
+bool ResourceParser::parsePlural(XmlPullParser* parser, ParsedResource* outResource) {
+ const Source source = mSource.withLine(parser->getLineNumber());
std::unique_ptr<Plural> plural = util::make_unique<Plural>();
- bool success = true;
- while (XmlPullParser::isGoodEvent(parser->next())) {
+ bool error = false;
+ const size_t depth = parser->getDepth();
+ while (XmlPullParser::nextChildNode(parser, depth)) {
if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
+ // Skip text and comments.
continue;
}
- ScopedXmlPullParser childParser(parser);
+ const Source itemSource = mSource.withLine(parser->getLineNumber());
+ const std::u16string& elementNamespace = parser->getElementNamespace();
+ const std::u16string& elementName = parser->getElementName();
+ if (elementNamespace.empty() && elementName == u"item") {
+ const auto endAttrIter = parser->endAttributes();
+ auto attrIter = parser->findAttribute(u"", u"quantity");
+ if (attrIter == endAttrIter || attrIter->value.empty()) {
+ mDiag->error(DiagMessage(itemSource) << "<item> in <plurals> requires attribute "
+ << "'quantity'");
+ error = true;
+ continue;
+ }
- if (!childParser.getElementNamespace().empty() ||
- childParser.getElementName() != u"item") {
- success = false;
- continue;
- }
+ StringPiece16 trimmedQuantity = util::trimWhitespace(attrIter->value);
+ size_t index = 0;
+ if (trimmedQuantity == u"zero") {
+ index = Plural::Zero;
+ } else if (trimmedQuantity == u"one") {
+ index = Plural::One;
+ } else if (trimmedQuantity == u"two") {
+ index = Plural::Two;
+ } else if (trimmedQuantity == u"few") {
+ index = Plural::Few;
+ } else if (trimmedQuantity == u"many") {
+ index = Plural::Many;
+ } else if (trimmedQuantity == u"other") {
+ index = Plural::Other;
+ } else {
+ mDiag->error(DiagMessage(itemSource)
+ << "<item> in <plural> has invalid value '" << trimmedQuantity
+ << "' for attribute 'quantity'");
+ error = true;
+ continue;
+ }
- const auto endAttrIter = childParser.endAttributes();
- auto attrIter = childParser.findAttribute(u"", u"quantity");
- if (attrIter == endAttrIter || attrIter->value.empty()) {
- mLogger.error(childParser.getLineNumber())
- << "<item> in <plurals> requires attribute 'quantity'."
- << std::endl;
- success = false;
- continue;
- }
+ if (plural->values[index]) {
+ mDiag->error(DiagMessage(itemSource)
+ << "duplicate quantity '" << trimmedQuantity << "'");
+ error = true;
+ continue;
+ }
- StringPiece16 trimmedQuantity = util::trimWhitespace(attrIter->value);
- size_t index = 0;
- if (trimmedQuantity == u"zero") {
- index = Plural::Zero;
- } else if (trimmedQuantity == u"one") {
- index = Plural::One;
- } else if (trimmedQuantity == u"two") {
- index = Plural::Two;
- } else if (trimmedQuantity == u"few") {
- index = Plural::Few;
- } else if (trimmedQuantity == u"many") {
- index = Plural::Many;
- } else if (trimmedQuantity == u"other") {
- index = Plural::Other;
- } else {
- mLogger.error(childParser.getLineNumber())
- << "<item> in <plural> has invalid value '"
- << trimmedQuantity
- << "' for attribute 'quantity'."
- << std::endl;
- success = false;
- continue;
- }
+ if (!(plural->values[index] = parseXml(parser, android::ResTable_map::TYPE_STRING,
+ kNoRawString))) {
+ error = true;
+ }
+ plural->values[index]->setSource(itemSource);
- if (plural->values[index]) {
- mLogger.error(childParser.getLineNumber())
- << "duplicate quantity '"
- << trimmedQuantity
- << "'."
- << std::endl;
- success = false;
- continue;
- }
-
- if (!(plural->values[index] = parseXml(&childParser, android::ResTable_map::TYPE_STRING,
- kNoRawString))) {
- success = false;
+ } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
+ mDiag->error(DiagMessage(itemSource) << "unknown tag <" << elementNamespace << ":"
+ << elementName << ">");
+ error = true;
}
}
- if (!success) {
+ if (error) {
return false;
}
- return mTable->addResource(resourceName, mConfig, source, std::move(plural));
+ outResource->value = std::move(plural);
+ return true;
}
-bool ResourceParser::parseDeclareStyleable(XmlPullParser* parser,
- const ResourceNameRef& resourceName) {
- const SourceLine source = mSource.line(parser->getLineNumber());
+bool ResourceParser::parseDeclareStyleable(XmlPullParser* parser, ParsedResource* outResource) {
+ const Source source = mSource.withLine(parser->getLineNumber());
std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
- bool success = true;
- while (XmlPullParser::isGoodEvent(parser->next())) {
- if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
+ // Declare-styleable is kPrivate by default, because it technically only exists in R.java.
+ outResource->symbolState = SymbolState::kPublic;
+
+ std::u16string comment;
+ bool error = false;
+ const size_t depth = parser->getDepth();
+ while (XmlPullParser::nextChildNode(parser, depth)) {
+ if (parser->getEvent() == XmlPullParser::Event::kComment) {
+ comment = util::trimWhitespace(parser->getComment()).toString();
+ continue;
+ } else if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
+ // Ignore text.
continue;
}
- ScopedXmlPullParser childParser(parser);
-
- const std::u16string& elementName = childParser.getElementName();
- if (elementName == u"attr") {
- const auto endAttrIter = childParser.endAttributes();
- auto attrIter = childParser.findAttribute(u"", u"name");
+ const Source itemSource = mSource.withLine(parser->getLineNumber());
+ const std::u16string& elementNamespace = parser->getElementNamespace();
+ const std::u16string& elementName = parser->getElementName();
+ if (elementNamespace.empty() && elementName == u"attr") {
+ const auto endAttrIter = parser->endAttributes();
+ auto attrIter = parser->findAttribute(u"", u"name");
if (attrIter == endAttrIter || attrIter->value.empty()) {
- mLogger.error(childParser.getLineNumber())
- << "<attr> tag must have a 'name' attribute."
- << std::endl;
- success = false;
+ mDiag->error(DiagMessage(itemSource) << "<attr> tag must have a 'name' attribute");
+ error = true;
continue;
}
- // Copy because our iterator will be invalidated.
- ResourceName attrResourceName = {
- mTable->getPackage(),
- ResourceType::kAttr,
- attrIter->value
- };
+ // Create the ParsedResource that will add the attribute to the table.
+ ParsedResource childResource;
+ childResource.name = ResourceName({}, ResourceType::kAttr, attrIter->value);
+ childResource.source = itemSource;
+ childResource.comment = std::move(comment);
- std::unique_ptr<Attribute> attr = parseAttrImpl(&childParser, &attrResourceName, true);
- if (!attr) {
- success = false;
+ if (!parseAttrImpl(parser, &childResource, true)) {
+ error = true;
continue;
}
- styleable->entries.emplace_back(attrResourceName);
+ // Create the reference to this attribute.
+ Reference childRef(childResource.name);
+ childRef.setComment(childResource.comment);
+ childRef.setSource(itemSource);
+ styleable->entries.push_back(std::move(childRef));
- // The package may have been corrected to another package. If that is so,
- // we don't add the declaration.
- if (attrResourceName.package == mTable->getPackage()) {
- success &= mTable->addResource(attrResourceName, mConfig,
- mSource.line(childParser.getLineNumber()),
- std::move(attr));
- }
+ outResource->childResources.push_back(std::move(childResource));
- } else if (elementName != u"eat-comment" && elementName != u"skip") {
- mLogger.error(childParser.getLineNumber())
- << "<"
- << elementName
- << "> is not allowed inside <declare-styleable>."
- << std::endl;
- success = false;
+ } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
+ mDiag->error(DiagMessage(itemSource) << "unknown tag <" << elementNamespace << ":"
+ << elementName << ">");
+ error = true;
}
+
+ comment = {};
}
- if (!success) {
+ if (error) {
return false;
}
- return mTable->addResource(resourceName, mConfig, source, std::move(styleable));
+ outResource->value = std::move(styleable);
+ return true;
}
} // namespace aapt
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index 7618999..18101ee 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -18,135 +18,46 @@
#define AAPT_RESOURCE_PARSER_H
#include "ConfigDescription.h"
-#include "Logger.h"
+#include "Diagnostics.h"
#include "ResourceTable.h"
#include "ResourceValues.h"
-#include "StringPiece.h"
#include "StringPool.h"
#include "XmlPullParser.h"
-#include <istream>
+#include "util/Maybe.h"
+#include "util/StringPiece.h"
+
#include <memory>
namespace aapt {
+struct ParsedResource;
+
+struct ResourceParserOptions {
+ /**
+ * Optional product name by which to filter resources.
+ * This is like a preprocessor definition in that we strip out resources
+ * that don't match before we compile them.
+ */
+ Maybe<std::u16string> product;
+
+ /**
+ * Whether the default setting for this parser is to allow translation.
+ */
+ bool translatable = true;
+};
+
/*
* Parses an XML file for resources and adds them to a ResourceTable.
*/
class ResourceParser {
public:
- /*
- * Extracts the package, type, and name from a string of the format:
- *
- * [package:]type/name
- *
- * where the package can be empty. Validation must be performed on each
- * individual extracted piece to verify that the pieces are valid.
- */
- static void extractResourceName(const StringPiece16& str, StringPiece16* outPackage,
- StringPiece16* outType, StringPiece16* outEntry);
-
- /*
- * Returns true if the string was parsed as a reference (@[+][package:]type/name), with
- * `outReference` set to the parsed reference.
- *
- * If '+' was present in the reference, `outCreate` is set to true.
- * If '*' was present in the reference, `outPrivate` is set to true.
- */
- static bool tryParseReference(const StringPiece16& str, ResourceNameRef* outReference,
- bool* outCreate, bool* outPrivate);
-
- /*
- * Returns true if the string was parsed as an attribute reference (?[package:]type/name),
- * with `outReference` set to the parsed reference.
- */
- static bool tryParseAttributeReference(const StringPiece16& str,
- ResourceNameRef* outReference);
-
- /*
- * Returns true if the string `str` was parsed as a valid reference to a style.
- * The format for a style parent is slightly more flexible than a normal reference:
- *
- * @[package:]style/<entry> or
- * ?[package:]style/<entry> or
- * <package>:[style/]<entry>
- */
- static bool parseStyleParentReference(const StringPiece16& str, Reference* outReference,
- std::string* outError);
-
- /*
- * Returns a Reference object if the string was parsed as a resource or attribute reference,
- * ( @[+][package:]type/name | ?[package:]type/name ) setting outCreate to true if
- * the '+' was present in the string.
- */
- static std::unique_ptr<Reference> tryParseReference(const StringPiece16& str,
- bool* outCreate);
-
- /*
- * Returns a BinaryPrimitve object representing @null or @empty if the string was parsed
- * as one.
- */
- static std::unique_ptr<BinaryPrimitive> tryParseNullOrEmpty(const StringPiece16& str);
-
- /*
- * Returns a BinaryPrimitve object representing a color if the string was parsed
- * as one.
- */
- static std::unique_ptr<BinaryPrimitive> tryParseColor(const StringPiece16& str);
-
- /*
- * Returns a BinaryPrimitve object representing a boolean if the string was parsed
- * as one.
- */
- static std::unique_ptr<BinaryPrimitive> tryParseBool(const StringPiece16& str);
-
- /*
- * Returns a BinaryPrimitve object representing an integer if the string was parsed
- * as one.
- */
- static std::unique_ptr<BinaryPrimitive> tryParseInt(const StringPiece16& str);
-
- /*
- * Returns a BinaryPrimitve object representing a floating point number
- * (float, dimension, etc) if the string was parsed as one.
- */
- static std::unique_ptr<BinaryPrimitive> tryParseFloat(const StringPiece16& str);
-
- /*
- * Returns a BinaryPrimitve object representing an enum symbol if the string was parsed
- * as one.
- */
- static std::unique_ptr<BinaryPrimitive> tryParseEnumSymbol(const Attribute& enumAttr,
- const StringPiece16& str);
-
- /*
- * Returns a BinaryPrimitve object representing a flag symbol if the string was parsed
- * as one.
- */
- static std::unique_ptr<BinaryPrimitive> tryParseFlagSymbol(const Attribute& enumAttr,
- const StringPiece16& str);
- /*
- * Try to convert a string to an Item for the given attribute. The attribute will
- * restrict what values the string can be converted to.
- * The callback function onCreateReference is called when the parsed item is a
- * reference to an ID that must be created (@+id/foo).
- */
- static std::unique_ptr<Item> parseItemForAttribute(
- const StringPiece16& value, const Attribute& attr,
- std::function<void(const ResourceName&)> onCreateReference = {});
-
- static std::unique_ptr<Item> parseItemForAttribute(
- const StringPiece16& value, uint32_t typeMask,
- std::function<void(const ResourceName&)> onCreateReference = {});
-
- static uint32_t androidTypeToAttributeTypeMask(uint16_t type);
-
- ResourceParser(const std::shared_ptr<ResourceTable>& table, const Source& source,
- const ConfigDescription& config, const std::shared_ptr<XmlPullParser>& parser);
+ ResourceParser(IDiagnostics* diag, ResourceTable* table, const Source& source,
+ const ConfigDescription& config, const ResourceParserOptions& options = {});
ResourceParser(const ResourceParser&) = delete; // No copy.
- bool parse();
+ bool parse(XmlPullParser* parser);
private:
/*
@@ -155,39 +66,39 @@
* contains the escaped and whitespace trimmed text, while `outRawString`
* contains the unescaped text. Returns true on success.
*/
- bool flattenXmlSubtree(XmlPullParser* parser, std::u16string* outRawString,\
+ bool flattenXmlSubtree(XmlPullParser* parser, std::u16string* outRawString,
StyleString* outStyleString);
/*
- * Parses the XML subtree and converts it to an Item. The type of Item that can be
- * parsed is denoted by the `typeMask`. If `allowRawValue` is true and the subtree
- * can not be parsed as a regular Item, then a RawString is returned. Otherwise
- * this returns nullptr.
+ * Parses the XML subtree and returns an Item.
+ * The type of Item that can be parsed is denoted by the `typeMask`.
+ * If `allowRawValue` is true and the subtree can not be parsed as a regular Item, then a
+ * RawString is returned. Otherwise this returns false;
*/
- std::unique_ptr<Item> parseXml(XmlPullParser* parser, uint32_t typeMask, bool allowRawValue);
+ std::unique_ptr<Item> parseXml(XmlPullParser* parser, const uint32_t typeMask,
+ const bool allowRawValue);
bool parseResources(XmlPullParser* parser);
- bool parseString(XmlPullParser* parser, const ResourceNameRef& resourceName);
- bool parseColor(XmlPullParser* parser, const ResourceNameRef& resourceName);
- bool parsePrimitive(XmlPullParser* parser, const ResourceNameRef& resourceName);
- bool parsePublic(XmlPullParser* parser, const StringPiece16& name);
- bool parseAttr(XmlPullParser* parser, const ResourceNameRef& resourceName);
- std::unique_ptr<Attribute> parseAttrImpl(XmlPullParser* parser,
- ResourceName* resourceName,
- bool weak);
- bool parseEnumOrFlagItem(XmlPullParser* parser, const StringPiece16& tag,
- Attribute::Symbol* outSymbol);
- bool parseStyle(XmlPullParser* parser, const ResourceNameRef& resourceName);
- bool parseUntypedItem(XmlPullParser* parser, Style& style);
- bool parseDeclareStyleable(XmlPullParser* parser, const ResourceNameRef& resourceName);
- bool parseArray(XmlPullParser* parser, const ResourceNameRef& resourceName, uint32_t typeMask);
- bool parsePlural(XmlPullParser* parser, const ResourceNameRef& resourceName);
+ bool parseString(XmlPullParser* parser, ParsedResource* outResource);
+ bool parseColor(XmlPullParser* parser, ParsedResource* outResource);
+ bool parsePrimitive(XmlPullParser* parser, ParsedResource* outResource);
+ bool parsePublic(XmlPullParser* parser, ParsedResource* outResource);
+ bool parsePublicGroup(XmlPullParser* parser, ParsedResource* outResource);
+ bool parseSymbol(XmlPullParser* parser, ParsedResource* outResource);
+ bool parseAttr(XmlPullParser* parser, ParsedResource* outResource);
+ bool parseAttrImpl(XmlPullParser* parser, ParsedResource* outResource, bool weak);
+ Maybe<Attribute::Symbol> parseEnumOrFlagItem(XmlPullParser* parser, const StringPiece16& tag);
+ bool parseStyle(XmlPullParser* parser, ParsedResource* outResource);
+ bool parseStyleItem(XmlPullParser* parser, Style* style);
+ bool parseDeclareStyleable(XmlPullParser* parser, ParsedResource* outResource);
+ bool parseArray(XmlPullParser* parser, ParsedResource* outResource, uint32_t typeMask);
+ bool parsePlural(XmlPullParser* parser, ParsedResource* outResource);
- std::shared_ptr<ResourceTable> mTable;
+ IDiagnostics* mDiag;
+ ResourceTable* mTable;
Source mSource;
ConfigDescription mConfig;
- SourceLogger mLogger;
- std::shared_ptr<XmlPullParser> mParser;
+ ResourceParserOptions mOptions;
};
} // namespace aapt
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index a93d0ff..b59eb95 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -16,8 +16,11 @@
#include "ResourceParser.h"
#include "ResourceTable.h"
+#include "ResourceUtils.h"
#include "ResourceValues.h"
-#include "SourceXmlPullParser.h"
+#include "XmlPullParser.h"
+
+#include "test/Context.h"
#include <gtest/gtest.h>
#include <sstream>
@@ -27,156 +30,45 @@
constexpr const char* kXmlPreamble = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
-TEST(ResourceParserReferenceTest, ParseReferenceWithNoPackage) {
- ResourceNameRef expected = { {}, ResourceType::kColor, u"foo" };
- ResourceNameRef actual;
- bool create = false;
- bool privateRef = false;
- EXPECT_TRUE(ResourceParser::tryParseReference(u"@color/foo", &actual, &create, &privateRef));
- EXPECT_EQ(expected, actual);
- EXPECT_FALSE(create);
- EXPECT_FALSE(privateRef);
-}
-
-TEST(ResourceParserReferenceTest, ParseReferenceWithPackage) {
- ResourceNameRef expected = { u"android", ResourceType::kColor, u"foo" };
- ResourceNameRef actual;
- bool create = false;
- bool privateRef = false;
- EXPECT_TRUE(ResourceParser::tryParseReference(u"@android:color/foo", &actual, &create,
- &privateRef));
- EXPECT_EQ(expected, actual);
- EXPECT_FALSE(create);
- EXPECT_FALSE(privateRef);
-}
-
-TEST(ResourceParserReferenceTest, ParseReferenceWithSurroundingWhitespace) {
- ResourceNameRef expected = { u"android", ResourceType::kColor, u"foo" };
- ResourceNameRef actual;
- bool create = false;
- bool privateRef = false;
- EXPECT_TRUE(ResourceParser::tryParseReference(u"\t @android:color/foo\n \n\t", &actual,
- &create, &privateRef));
- EXPECT_EQ(expected, actual);
- EXPECT_FALSE(create);
- EXPECT_FALSE(privateRef);
-}
-
-TEST(ResourceParserReferenceTest, ParseAutoCreateIdReference) {
- ResourceNameRef expected = { u"android", ResourceType::kId, u"foo" };
- ResourceNameRef actual;
- bool create = false;
- bool privateRef = false;
- EXPECT_TRUE(ResourceParser::tryParseReference(u"@+android:id/foo", &actual, &create,
- &privateRef));
- EXPECT_EQ(expected, actual);
- EXPECT_TRUE(create);
- EXPECT_FALSE(privateRef);
-}
-
-TEST(ResourceParserReferenceTest, ParsePrivateReference) {
- ResourceNameRef expected = { u"android", ResourceType::kId, u"foo" };
- ResourceNameRef actual;
- bool create = false;
- bool privateRef = false;
- EXPECT_TRUE(ResourceParser::tryParseReference(u"@*android:id/foo", &actual, &create,
- &privateRef));
- EXPECT_EQ(expected, actual);
- EXPECT_FALSE(create);
- EXPECT_TRUE(privateRef);
-}
-
-TEST(ResourceParserReferenceTest, FailToParseAutoCreateNonIdReference) {
- bool create = false;
- bool privateRef = false;
- ResourceNameRef actual;
- EXPECT_FALSE(ResourceParser::tryParseReference(u"@+android:color/foo", &actual, &create,
- &privateRef));
-}
-
-TEST(ResourceParserReferenceTest, ParseStyleParentReference) {
- Reference ref;
- std::string errStr;
- EXPECT_TRUE(ResourceParser::parseStyleParentReference(u"@android:style/foo", &ref, &errStr));
- EXPECT_EQ(ref.name, (ResourceName{ u"android", ResourceType::kStyle, u"foo" }));
-
- EXPECT_TRUE(ResourceParser::parseStyleParentReference(u"@style/foo", &ref, &errStr));
- EXPECT_EQ(ref.name, (ResourceName{ {}, ResourceType::kStyle, u"foo" }));
-
- EXPECT_TRUE(ResourceParser::parseStyleParentReference(u"?android:style/foo", &ref, &errStr));
- EXPECT_EQ(ref.name, (ResourceName{ u"android", ResourceType::kStyle, u"foo" }));
-
- EXPECT_TRUE(ResourceParser::parseStyleParentReference(u"?style/foo", &ref, &errStr));
- EXPECT_EQ(ref.name, (ResourceName{ {}, ResourceType::kStyle, u"foo" }));
-
- EXPECT_TRUE(ResourceParser::parseStyleParentReference(u"android:style/foo", &ref, &errStr));
- EXPECT_EQ(ref.name, (ResourceName{ u"android", ResourceType::kStyle, u"foo" }));
-
- EXPECT_TRUE(ResourceParser::parseStyleParentReference(u"android:foo", &ref, &errStr));
- EXPECT_EQ(ref.name, (ResourceName{ u"android", ResourceType::kStyle, u"foo" }));
-
- EXPECT_TRUE(ResourceParser::parseStyleParentReference(u"foo", &ref, &errStr));
- EXPECT_EQ(ref.name, (ResourceName{ {}, ResourceType::kStyle, u"foo" }));
+TEST(ResourceParserSingleTest, FailToParseWithNoRootResourcesElement) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+ std::stringstream input(kXmlPreamble);
+ input << "<attr name=\"foo\"/>" << std::endl;
+ ResourceTable table;
+ ResourceParser parser(context->getDiagnostics(), &table, Source{ "test" }, {});
+ XmlPullParser xmlParser(input);
+ ASSERT_FALSE(parser.parse(&xmlParser));
}
struct ResourceParserTest : public ::testing::Test {
- virtual void SetUp() override {
- mTable = std::make_shared<ResourceTable>();
- mTable->setPackage(u"android");
+ ResourceTable mTable;
+ std::unique_ptr<IAaptContext> mContext;
+
+ void SetUp() override {
+ mContext = test::ContextBuilder().build();
}
- ::testing::AssertionResult testParse(const StringPiece& str) {
+ ::testing::AssertionResult testParse(const StringPiece& str,
+ Maybe<std::u16string> product = {}) {
std::stringstream input(kXmlPreamble);
input << "<resources>\n" << str << "\n</resources>" << std::endl;
- ResourceParser parser(mTable, Source{ "test" }, {},
- std::make_shared<SourceXmlPullParser>(input));
- if (parser.parse()) {
+ ResourceParserOptions parserOptions;
+ parserOptions.product = product;
+ ResourceParser parser(mContext->getDiagnostics(), &mTable, Source{ "test" }, {},
+ parserOptions);
+ XmlPullParser xmlParser(input);
+ if (parser.parse(&xmlParser)) {
return ::testing::AssertionSuccess();
}
return ::testing::AssertionFailure();
}
-
- template <typename T>
- const T* findResource(const ResourceNameRef& name, const ConfigDescription& config) {
- using std::begin;
- using std::end;
-
- const ResourceTableType* type;
- const ResourceEntry* entry;
- std::tie(type, entry) = mTable->findResource(name);
- if (!type || !entry) {
- return nullptr;
- }
-
- for (const auto& configValue : entry->values) {
- if (configValue.config == config) {
- return dynamic_cast<const T*>(configValue.value.get());
- }
- }
- return nullptr;
- }
-
- template <typename T>
- const T* findResource(const ResourceNameRef& name) {
- return findResource<T>(name, {});
- }
-
- std::shared_ptr<ResourceTable> mTable;
};
-TEST_F(ResourceParserTest, FailToParseWithNoRootResourcesElement) {
- std::stringstream input(kXmlPreamble);
- input << "<attr name=\"foo\"/>" << std::endl;
- ResourceParser parser(mTable, {}, {}, std::make_shared<SourceXmlPullParser>(input));
- ASSERT_FALSE(parser.parse());
-}
-
TEST_F(ResourceParserTest, ParseQuotedString) {
std::string input = "<string name=\"foo\"> \" hey there \" </string>";
ASSERT_TRUE(testParse(input));
- const String* str = findResource<String>(ResourceName{
- u"android", ResourceType::kString, u"foo"});
+ String* str = test::getValue<String>(&mTable, u"@string/foo");
ASSERT_NE(nullptr, str);
EXPECT_EQ(std::u16string(u" hey there "), *str->value);
}
@@ -185,12 +77,30 @@
std::string input = "<string name=\"foo\">\\?123</string>";
ASSERT_TRUE(testParse(input));
- const String* str = findResource<String>(ResourceName{
- u"android", ResourceType::kString, u"foo" });
+ String* str = test::getValue<String>(&mTable, u"@string/foo");
ASSERT_NE(nullptr, str);
EXPECT_EQ(std::u16string(u"?123"), *str->value);
}
+TEST_F(ResourceParserTest, ParseFormattedString) {
+ std::string input = "<string name=\"foo\">%d %s</string>";
+ ASSERT_FALSE(testParse(input));
+
+ input = "<string name=\"foo\">%1$d %2$s</string>";
+ ASSERT_TRUE(testParse(input));
+}
+
+TEST_F(ResourceParserTest, IgnoreXliffTags) {
+ std::string input = "<string name=\"foo\" \n"
+ " xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">\n"
+ " There are <xliff:g id=\"count\">%1$d</xliff:g> apples</string>";
+ ASSERT_TRUE(testParse(input));
+
+ String* str = test::getValue<String>(&mTable, u"@string/foo");
+ ASSERT_NE(nullptr, str);
+ EXPECT_EQ(StringPiece16(u"There are %1$d apples"), StringPiece16(*str->value));
+}
+
TEST_F(ResourceParserTest, ParseNull) {
std::string input = "<integer name=\"foo\">@null</integer>";
ASSERT_TRUE(testParse(input));
@@ -199,8 +109,7 @@
// a non-existing value, and this causes problems in styles when trying to resolve
// an attribute. Null values must be encoded as android::Res_value::TYPE_REFERENCE
// with a data value of 0.
- const BinaryPrimitive* integer = findResource<BinaryPrimitive>(ResourceName{
- u"android", ResourceType::kInteger, u"foo" });
+ BinaryPrimitive* integer = test::getValue<BinaryPrimitive>(&mTable, u"@integer/foo");
ASSERT_NE(nullptr, integer);
EXPECT_EQ(uint16_t(android::Res_value::TYPE_REFERENCE), integer->value.dataType);
EXPECT_EQ(0u, integer->value.data);
@@ -210,8 +119,7 @@
std::string input = "<integer name=\"foo\">@empty</integer>";
ASSERT_TRUE(testParse(input));
- const BinaryPrimitive* integer = findResource<BinaryPrimitive>(ResourceName{
- u"android", ResourceType::kInteger, u"foo" });
+ BinaryPrimitive* integer = test::getValue<BinaryPrimitive>(&mTable, u"@integer/foo");
ASSERT_NE(nullptr, integer);
EXPECT_EQ(uint16_t(android::Res_value::TYPE_NULL), integer->value.dataType);
EXPECT_EQ(uint32_t(android::Res_value::DATA_NULL_EMPTY), integer->value.data);
@@ -222,14 +130,12 @@
"<attr name=\"bar\"/>";
ASSERT_TRUE(testParse(input));
- const Attribute* attr = findResource<Attribute>(ResourceName{
- u"android", ResourceType::kAttr, u"foo"});
- EXPECT_NE(nullptr, attr);
+ Attribute* attr = test::getValue<Attribute>(&mTable, u"@attr/foo");
+ ASSERT_NE(nullptr, attr);
EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->typeMask);
- attr = findResource<Attribute>(ResourceName{
- u"android", ResourceType::kAttr, u"bar"});
- EXPECT_NE(nullptr, attr);
+ attr = test::getValue<Attribute>(&mTable, u"@attr/bar");
+ ASSERT_NE(nullptr, attr);
EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_ANY), attr->typeMask);
}
@@ -240,8 +146,7 @@
"<attr name=\"foo\" format=\"string\"/>";
ASSERT_TRUE(testParse(input));
- const Attribute* attr = findResource<Attribute>(ResourceName{
- u"android", ResourceType::kAttr, u"foo"});
+ Attribute* attr = test::getValue<Attribute>(&mTable, u"@attr/foo");
ASSERT_NE(nullptr, attr);
EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->typeMask);
}
@@ -255,8 +160,7 @@
"</declare-styleable>";
ASSERT_TRUE(testParse(input));
- const Attribute* attr = findResource<Attribute>(ResourceName{
- u"android", ResourceType::kAttr, u"foo"});
+ Attribute* attr = test::getValue<Attribute>(&mTable, u"@attr/foo");
ASSERT_NE(nullptr, attr);
EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_BOOLEAN), attr->typeMask);
}
@@ -269,19 +173,21 @@
"</attr>";
ASSERT_TRUE(testParse(input));
- const Attribute* enumAttr = findResource<Attribute>(ResourceName{
- u"android", ResourceType::kAttr, u"foo"});
+ Attribute* enumAttr = test::getValue<Attribute>(&mTable, u"@attr/foo");
ASSERT_NE(enumAttr, nullptr);
EXPECT_EQ(enumAttr->typeMask, android::ResTable_map::TYPE_ENUM);
ASSERT_EQ(enumAttr->symbols.size(), 3u);
- EXPECT_EQ(enumAttr->symbols[0].symbol.name.entry, u"bar");
+ AAPT_ASSERT_TRUE(enumAttr->symbols[0].symbol.name);
+ EXPECT_EQ(enumAttr->symbols[0].symbol.name.value().entry, u"bar");
EXPECT_EQ(enumAttr->symbols[0].value, 0u);
- EXPECT_EQ(enumAttr->symbols[1].symbol.name.entry, u"bat");
+ AAPT_ASSERT_TRUE(enumAttr->symbols[1].symbol.name);
+ EXPECT_EQ(enumAttr->symbols[1].symbol.name.value().entry, u"bat");
EXPECT_EQ(enumAttr->symbols[1].value, 1u);
- EXPECT_EQ(enumAttr->symbols[2].symbol.name.entry, u"baz");
+ AAPT_ASSERT_TRUE(enumAttr->symbols[2].symbol.name);
+ EXPECT_EQ(enumAttr->symbols[2].symbol.name.value().entry, u"baz");
EXPECT_EQ(enumAttr->symbols[2].value, 2u);
}
@@ -293,23 +199,25 @@
"</attr>";
ASSERT_TRUE(testParse(input));
- const Attribute* flagAttr = findResource<Attribute>(ResourceName{
- u"android", ResourceType::kAttr, u"foo"});
+ Attribute* flagAttr = test::getValue<Attribute>(&mTable, u"@attr/foo");
ASSERT_NE(flagAttr, nullptr);
EXPECT_EQ(flagAttr->typeMask, android::ResTable_map::TYPE_FLAGS);
ASSERT_EQ(flagAttr->symbols.size(), 3u);
- EXPECT_EQ(flagAttr->symbols[0].symbol.name.entry, u"bar");
+ AAPT_ASSERT_TRUE(flagAttr->symbols[0].symbol.name);
+ EXPECT_EQ(flagAttr->symbols[0].symbol.name.value().entry, u"bar");
EXPECT_EQ(flagAttr->symbols[0].value, 0u);
- EXPECT_EQ(flagAttr->symbols[1].symbol.name.entry, u"bat");
+ AAPT_ASSERT_TRUE(flagAttr->symbols[1].symbol.name);
+ EXPECT_EQ(flagAttr->symbols[1].symbol.name.value().entry, u"bat");
EXPECT_EQ(flagAttr->symbols[1].value, 1u);
- EXPECT_EQ(flagAttr->symbols[2].symbol.name.entry, u"baz");
+ AAPT_ASSERT_TRUE(flagAttr->symbols[2].symbol.name);
+ EXPECT_EQ(flagAttr->symbols[2].symbol.name.value().entry, u"baz");
EXPECT_EQ(flagAttr->symbols[2].value, 2u);
- std::unique_ptr<BinaryPrimitive> flagValue =
- ResourceParser::tryParseFlagSymbol(*flagAttr, u"baz|bat");
+ std::unique_ptr<BinaryPrimitive> flagValue = ResourceUtils::tryParseFlagSymbol(flagAttr,
+ u"baz|bat");
ASSERT_NE(flagValue, nullptr);
EXPECT_EQ(flagValue->value.data, 1u | 2u);
}
@@ -331,28 +239,32 @@
"</style>";
ASSERT_TRUE(testParse(input));
- const Style* style = findResource<Style>(ResourceName{
- u"android", ResourceType::kStyle, u"foo"});
+ Style* style = test::getValue<Style>(&mTable, u"@style/foo");
ASSERT_NE(style, nullptr);
- EXPECT_EQ(ResourceNameRef(u"android", ResourceType::kStyle, u"fu"), style->parent.name);
- ASSERT_EQ(style->entries.size(), 3u);
+ AAPT_ASSERT_TRUE(style->parent);
+ AAPT_ASSERT_TRUE(style->parent.value().name);
+ EXPECT_EQ(test::parseNameOrDie(u"@style/fu"), style->parent.value().name.value());
+ ASSERT_EQ(3u, style->entries.size());
- EXPECT_EQ(style->entries[0].key.name,
- (ResourceName{ u"android", ResourceType::kAttr, u"bar" }));
- EXPECT_EQ(style->entries[1].key.name,
- (ResourceName{ u"android", ResourceType::kAttr, u"bat" }));
- EXPECT_EQ(style->entries[2].key.name,
- (ResourceName{ u"android", ResourceType::kAttr, u"baz" }));
+ AAPT_ASSERT_TRUE(style->entries[0].key.name);
+ EXPECT_EQ(test::parseNameOrDie(u"@attr/bar"), style->entries[0].key.name.value());
+
+ AAPT_ASSERT_TRUE(style->entries[1].key.name);
+ EXPECT_EQ(test::parseNameOrDie(u"@attr/bat"), style->entries[1].key.name.value());
+
+ AAPT_ASSERT_TRUE(style->entries[2].key.name);
+ EXPECT_EQ(test::parseNameOrDie(u"@attr/baz"), style->entries[2].key.name.value());
}
TEST_F(ResourceParserTest, ParseStyleWithShorthandParent) {
std::string input = "<style name=\"foo\" parent=\"com.app:Theme\"/>";
ASSERT_TRUE(testParse(input));
- const Style* style = findResource<Style>(
- ResourceName{ u"android", ResourceType::kStyle, u"foo" });
+ Style* style = test::getValue<Style>(&mTable, u"@style/foo");
ASSERT_NE(style, nullptr);
- EXPECT_EQ(ResourceNameRef(u"com.app", ResourceType::kStyle, u"Theme"), style->parent.name);
+ AAPT_ASSERT_TRUE(style->parent);
+ AAPT_ASSERT_TRUE(style->parent.value().name);
+ EXPECT_EQ(test::parseNameOrDie(u"@com.app:style/Theme"), style->parent.value().name.value());
}
TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedParent) {
@@ -360,10 +272,11 @@
" name=\"foo\" parent=\"app:Theme\"/>";
ASSERT_TRUE(testParse(input));
- const Style* style = findResource<Style>(ResourceName{
- u"android", ResourceType::kStyle, u"foo" });
+ Style* style = test::getValue<Style>(&mTable, u"@style/foo");
ASSERT_NE(style, nullptr);
- EXPECT_EQ(ResourceNameRef(u"android", ResourceType::kStyle, u"Theme"), style->parent.name);
+ AAPT_ASSERT_TRUE(style->parent);
+ AAPT_ASSERT_TRUE(style->parent.value().name);
+ EXPECT_EQ(test::parseNameOrDie(u"@android:style/Theme"), style->parent.value().name.value());
}
TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedItems) {
@@ -373,22 +286,21 @@
"</style>";
ASSERT_TRUE(testParse(input));
- const Style* style = findResource<Style>(ResourceName{
- u"android", ResourceType::kStyle, u"foo" });
+ Style* style = test::getValue<Style>(&mTable, u"@style/foo");
ASSERT_NE(style, nullptr);
ASSERT_EQ(1u, style->entries.size());
- EXPECT_EQ(ResourceNameRef(u"android", ResourceType::kAttr, u"bar"),
- style->entries[0].key.name);
+ EXPECT_EQ(test::parseNameOrDie(u"@android:attr/bar"), style->entries[0].key.name.value());
}
TEST_F(ResourceParserTest, ParseStyleWithInferredParent) {
std::string input = "<style name=\"foo.bar\"/>";
ASSERT_TRUE(testParse(input));
- const Style* style = findResource<Style>(ResourceName{
- u"android", ResourceType::kStyle, u"foo.bar" });
+ Style* style = test::getValue<Style>(&mTable, u"@style/foo.bar");
ASSERT_NE(style, nullptr);
- EXPECT_EQ(style->parent.name, (ResourceName{ u"android", ResourceType::kStyle, u"foo" }));
+ AAPT_ASSERT_TRUE(style->parent);
+ AAPT_ASSERT_TRUE(style->parent.value().name);
+ EXPECT_EQ(style->parent.value().name.value(), test::parseNameOrDie(u"@style/foo"));
EXPECT_TRUE(style->parentInferred);
}
@@ -396,10 +308,9 @@
std::string input = "<style name=\"foo.bar\" parent=\"\"/>";
ASSERT_TRUE(testParse(input));
- const Style* style = findResource<Style>(ResourceName{
- u"android", ResourceType::kStyle, u"foo.bar" });
+ Style* style = test::getValue<Style>(&mTable, u"@style/foo.bar");
ASSERT_NE(style, nullptr);
- EXPECT_FALSE(style->parent.name.isValid());
+ AAPT_EXPECT_FALSE(style->parent);
EXPECT_FALSE(style->parentInferred);
}
@@ -407,7 +318,7 @@
std::string input = "<string name=\"foo\">@+id/bar</string>";
ASSERT_TRUE(testParse(input));
- const Id* id = findResource<Id>(ResourceName{ u"android", ResourceType::kId, u"bar"});
+ Id* id = test::getValue<Id>(&mTable, u"@id/bar");
ASSERT_NE(id, nullptr);
}
@@ -415,25 +326,38 @@
std::string input = "<declare-styleable name=\"foo\">\n"
" <attr name=\"bar\" />\n"
" <attr name=\"bat\" format=\"string|reference\"/>\n"
+ " <attr name=\"baz\">\n"
+ " <enum name=\"foo\" value=\"1\"/>\n"
+ " </attr>\n"
"</declare-styleable>";
ASSERT_TRUE(testParse(input));
- const Attribute* attr = findResource<Attribute>(ResourceName{
- u"android", ResourceType::kAttr, u"bar"});
+ Maybe<ResourceTable::SearchResult> result =
+ mTable.findResource(test::parseNameOrDie(u"@styleable/foo"));
+ AAPT_ASSERT_TRUE(result);
+ EXPECT_EQ(SymbolState::kPublic, result.value().entry->symbolStatus.state);
+
+ Attribute* attr = test::getValue<Attribute>(&mTable, u"@attr/bar");
ASSERT_NE(attr, nullptr);
EXPECT_TRUE(attr->isWeak());
- attr = findResource<Attribute>(ResourceName{ u"android", ResourceType::kAttr, u"bat"});
+ attr = test::getValue<Attribute>(&mTable, u"@attr/bat");
ASSERT_NE(attr, nullptr);
EXPECT_TRUE(attr->isWeak());
- const Styleable* styleable = findResource<Styleable>(ResourceName{
- u"android", ResourceType::kStyleable, u"foo" });
+ attr = test::getValue<Attribute>(&mTable, u"@attr/baz");
+ ASSERT_NE(attr, nullptr);
+ EXPECT_TRUE(attr->isWeak());
+ EXPECT_EQ(1u, attr->symbols.size());
+
+ EXPECT_NE(nullptr, test::getValue<Id>(&mTable, u"@id/foo"));
+
+ Styleable* styleable = test::getValue<Styleable>(&mTable, u"@styleable/foo");
ASSERT_NE(styleable, nullptr);
- ASSERT_EQ(2u, styleable->entries.size());
+ ASSERT_EQ(3u, styleable->entries.size());
- EXPECT_EQ((ResourceName{u"android", ResourceType::kAttr, u"bar"}), styleable->entries[0].name);
- EXPECT_EQ((ResourceName{u"android", ResourceType::kAttr, u"bat"}), styleable->entries[1].name);
+ EXPECT_EQ(test::parseNameOrDie(u"@attr/bar"), styleable->entries[0].name.value());
+ EXPECT_EQ(test::parseNameOrDie(u"@attr/bat"), styleable->entries[1].name.value());
}
TEST_F(ResourceParserTest, ParseArray) {
@@ -444,14 +368,21 @@
"</array>";
ASSERT_TRUE(testParse(input));
- const Array* array = findResource<Array>(ResourceName{
- u"android", ResourceType::kArray, u"foo" });
+ Array* array = test::getValue<Array>(&mTable, u"@array/foo");
ASSERT_NE(array, nullptr);
ASSERT_EQ(3u, array->items.size());
- EXPECT_NE(nullptr, dynamic_cast<const Reference*>(array->items[0].get()));
- EXPECT_NE(nullptr, dynamic_cast<const String*>(array->items[1].get()));
- EXPECT_NE(nullptr, dynamic_cast<const BinaryPrimitive*>(array->items[2].get()));
+ EXPECT_NE(nullptr, valueCast<Reference>(array->items[0].get()));
+ EXPECT_NE(nullptr, valueCast<String>(array->items[1].get()));
+ EXPECT_NE(nullptr, valueCast<BinaryPrimitive>(array->items[2].get()));
+}
+
+TEST_F(ResourceParserTest, ParseStringArray) {
+ std::string input = "<string-array name=\"foo\">\n"
+ " <item>\"Werk\"</item>\n"
+ "</string-array>\n";
+ ASSERT_TRUE(testParse(input));
+ EXPECT_NE(nullptr, test::getValue<Array>(&mTable, u"@array/foo"));
}
TEST_F(ResourceParserTest, ParsePlural) {
@@ -463,18 +394,67 @@
}
TEST_F(ResourceParserTest, ParseCommentsWithResource) {
- std::string input = "<!-- This is a comment -->\n"
+ std::string input = "<!--This is a comment-->\n"
"<string name=\"foo\">Hi</string>";
ASSERT_TRUE(testParse(input));
- const ResourceTableType* type;
- const ResourceEntry* entry;
- std::tie(type, entry) = mTable->findResource(ResourceName{
- u"android", ResourceType::kString, u"foo"});
- ASSERT_NE(type, nullptr);
- ASSERT_NE(entry, nullptr);
- ASSERT_FALSE(entry->values.empty());
- EXPECT_EQ(entry->values.front().comment, u"This is a comment");
+ String* value = test::getValue<String>(&mTable, u"@string/foo");
+ ASSERT_NE(nullptr, value);
+ EXPECT_EQ(value->getComment(), u"This is a comment");
+}
+
+TEST_F(ResourceParserTest, DoNotCombineMultipleComments) {
+ std::string input = "<!--One-->\n"
+ "<!--Two-->\n"
+ "<string name=\"foo\">Hi</string>";
+
+ ASSERT_TRUE(testParse(input));
+
+ String* value = test::getValue<String>(&mTable, u"@string/foo");
+ ASSERT_NE(nullptr, value);
+ EXPECT_EQ(value->getComment(), u"Two");
+}
+
+TEST_F(ResourceParserTest, IgnoreCommentBeforeEndTag) {
+ std::string input = "<!--One-->\n"
+ "<string name=\"foo\">\n"
+ " Hi\n"
+ "<!--Two-->\n"
+ "</string>";
+
+ ASSERT_TRUE(testParse(input));
+
+ String* value = test::getValue<String>(&mTable, u"@string/foo");
+ ASSERT_NE(nullptr, value);
+ EXPECT_EQ(value->getComment(), u"One");
+}
+
+TEST_F(ResourceParserTest, ParseNestedComments) {
+ // We only care about declare-styleable and enum/flag attributes because comments
+ // from those end up in R.java
+ std::string input = R"EOF(
+ <declare-styleable name="foo">
+ <!-- The name of the bar -->
+ <attr name="barName" format="string|reference" />
+ </declare-styleable>
+
+ <attr name="foo">
+ <!-- The very first -->
+ <enum name="one" value="1" />
+ </attr>)EOF";
+ ASSERT_TRUE(testParse(input));
+
+ Styleable* styleable = test::getValue<Styleable>(&mTable, u"@styleable/foo");
+ ASSERT_NE(nullptr, styleable);
+ ASSERT_EQ(1u, styleable->entries.size());
+
+ EXPECT_EQ(StringPiece16(u"The name of the bar"), styleable->entries.front().getComment());
+
+ Attribute* attr = test::getValue<Attribute>(&mTable, u"@attr/foo");
+ ASSERT_NE(nullptr, attr);
+ ASSERT_EQ(1u, attr->symbols.size());
+
+ EXPECT_EQ(StringPiece16(u"The very first"), attr->symbols.front().symbol.getComment());
}
/*
@@ -485,8 +465,68 @@
std::string input = "<public type=\"id\" name=\"foo\"/>";
ASSERT_TRUE(testParse(input));
- const Id* id = findResource<Id>(ResourceName{ u"android", ResourceType::kId, u"foo" });
+ Id* id = test::getValue<Id>(&mTable, u"@id/foo");
ASSERT_NE(nullptr, id);
}
+TEST_F(ResourceParserTest, FilterProductsThatDontMatch) {
+ std::string input = "<string name=\"foo\" product=\"phone\">hi</string>\n"
+ "<string name=\"foo\" product=\"no-sdcard\">ho</string>\n"
+ "<string name=\"bar\" product=\"\">wee</string>\n"
+ "<string name=\"baz\">woo</string>\n";
+ ASSERT_TRUE(testParse(input, std::u16string(u"no-sdcard")));
+
+ String* fooStr = test::getValue<String>(&mTable, u"@string/foo");
+ ASSERT_NE(nullptr, fooStr);
+ EXPECT_EQ(StringPiece16(u"ho"), *fooStr->value);
+
+ EXPECT_NE(nullptr, test::getValue<String>(&mTable, u"@string/bar"));
+ EXPECT_NE(nullptr, test::getValue<String>(&mTable, u"@string/baz"));
+}
+
+TEST_F(ResourceParserTest, FailWhenProductFilterStripsOutAllVersionsOfResource) {
+ std::string input = "<string name=\"foo\" product=\"tablet\">hello</string>\n";
+ ASSERT_FALSE(testParse(input, std::u16string(u"phone")));
+}
+
+TEST_F(ResourceParserTest, AutoIncrementIdsInPublicGroup) {
+ std::string input = R"EOF(
+ <public-group type="attr" first-id="0x01010040">
+ <public name="foo" />
+ <public name="bar" />
+ </public-group>)EOF";
+ ASSERT_TRUE(testParse(input));
+
+ Maybe<ResourceTable::SearchResult> result = mTable.findResource(
+ test::parseNameOrDie(u"@attr/foo"));
+ AAPT_ASSERT_TRUE(result);
+
+ AAPT_ASSERT_TRUE(result.value().package->id);
+ AAPT_ASSERT_TRUE(result.value().type->id);
+ AAPT_ASSERT_TRUE(result.value().entry->id);
+ ResourceId actualId(result.value().package->id.value(),
+ result.value().type->id.value(),
+ result.value().entry->id.value());
+ EXPECT_EQ(ResourceId(0x01010040), actualId);
+
+ result = mTable.findResource(test::parseNameOrDie(u"@attr/bar"));
+ AAPT_ASSERT_TRUE(result);
+
+ AAPT_ASSERT_TRUE(result.value().package->id);
+ AAPT_ASSERT_TRUE(result.value().type->id);
+ AAPT_ASSERT_TRUE(result.value().entry->id);
+ actualId = ResourceId(result.value().package->id.value(),
+ result.value().type->id.value(),
+ result.value().entry->id.value());
+ EXPECT_EQ(ResourceId(0x01010041), actualId);
+}
+
+TEST_F(ResourceParserTest, ExternalTypesShouldOnlyBeReferences) {
+ std::string input = R"EOF(<item type="layout" name="foo">@layout/bar</item>)EOF";
+ ASSERT_TRUE(testParse(input));
+
+ input = R"EOF(<item type="layout" name="bar">"this is a string"</item>)EOF";
+ ASSERT_FALSE(testParse(input));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index c93ecc7..deafe20 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -15,11 +15,13 @@
*/
#include "ConfigDescription.h"
-#include "Logger.h"
#include "NameMangler.h"
#include "ResourceTable.h"
#include "ResourceValues.h"
-#include "Util.h"
+#include "ValueVisitor.h"
+
+#include "util/Comparators.h"
+#include "util/Util.h"
#include <algorithm>
#include <androidfw/ResourceTypes.h>
@@ -29,73 +31,113 @@
namespace aapt {
-static bool compareConfigs(const ResourceConfigValue& lhs, const ConfigDescription& rhs) {
- return lhs.config < rhs;
-}
-
static bool lessThanType(const std::unique_ptr<ResourceTableType>& lhs, ResourceType rhs) {
return lhs->type < rhs;
}
-static bool lessThanEntry(const std::unique_ptr<ResourceEntry>& lhs, const StringPiece16& rhs) {
+template <typename T>
+static bool lessThanStructWithName(const std::unique_ptr<T>& lhs,
+ const StringPiece16& rhs) {
return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0;
}
-ResourceTable::ResourceTable() : mPackageId(kUnsetPackageId) {
- // Make sure attrs always have type ID 1.
- findOrCreateType(ResourceType::kAttr)->typeId = 1;
+ResourceTablePackage* ResourceTable::findPackage(const StringPiece16& name) {
+ const auto last = packages.end();
+ auto iter = std::lower_bound(packages.begin(), last, name,
+ lessThanStructWithName<ResourceTablePackage>);
+ if (iter != last && name == (*iter)->name) {
+ return iter->get();
+ }
+ return nullptr;
}
-std::unique_ptr<ResourceTableType>& ResourceTable::findOrCreateType(ResourceType type) {
- auto last = mTypes.end();
- auto iter = std::lower_bound(mTypes.begin(), last, type, lessThanType);
- if (iter != last) {
- if ((*iter)->type == type) {
- return *iter;
+ResourceTablePackage* ResourceTable::findPackageById(uint8_t id) {
+ for (auto& package : packages) {
+ if (package->id && package->id.value() == id) {
+ return package.get();
}
}
- return *mTypes.emplace(iter, new ResourceTableType{ type });
+ return nullptr;
}
-std::unique_ptr<ResourceEntry>& ResourceTable::findOrCreateEntry(
- std::unique_ptr<ResourceTableType>& type, const StringPiece16& name) {
- auto last = type->entries.end();
- auto iter = std::lower_bound(type->entries.begin(), last, name, lessThanEntry);
- if (iter != last) {
- if (name == (*iter)->name) {
- return *iter;
- }
+ResourceTablePackage* ResourceTable::createPackage(const StringPiece16& name, Maybe<uint8_t> id) {
+ ResourceTablePackage* package = findOrCreatePackage(name);
+ if (id && !package->id) {
+ package->id = id;
+ return package;
}
- return *type->entries.emplace(iter, new ResourceEntry{ name });
+
+ if (id && package->id && package->id.value() != id.value()) {
+ return nullptr;
+ }
+ return package;
}
-struct IsAttributeVisitor : ConstValueVisitor {
- bool isAttribute = false;
-
- void visit(const Attribute&, ValueVisitorArgs&) override {
- isAttribute = true;
+ResourceTablePackage* ResourceTable::findOrCreatePackage(const StringPiece16& name) {
+ const auto last = packages.end();
+ auto iter = std::lower_bound(packages.begin(), last, name,
+ lessThanStructWithName<ResourceTablePackage>);
+ if (iter != last && name == (*iter)->name) {
+ return iter->get();
}
- operator bool() {
- return isAttribute;
+ std::unique_ptr<ResourceTablePackage> newPackage = util::make_unique<ResourceTablePackage>();
+ newPackage->name = name.toString();
+ return packages.emplace(iter, std::move(newPackage))->get();
+}
+
+ResourceTableType* ResourceTablePackage::findType(ResourceType type) {
+ const auto last = types.end();
+ auto iter = std::lower_bound(types.begin(), last, type, lessThanType);
+ if (iter != last && (*iter)->type == type) {
+ return iter->get();
}
-};
+ return nullptr;
+}
+
+ResourceTableType* ResourceTablePackage::findOrCreateType(ResourceType type) {
+ const auto last = types.end();
+ auto iter = std::lower_bound(types.begin(), last, type, lessThanType);
+ if (iter != last && (*iter)->type == type) {
+ return iter->get();
+ }
+ return types.emplace(iter, new ResourceTableType{ type })->get();
+}
+
+ResourceEntry* ResourceTableType::findEntry(const StringPiece16& name) {
+ const auto last = entries.end();
+ auto iter = std::lower_bound(entries.begin(), last, name,
+ lessThanStructWithName<ResourceEntry>);
+ if (iter != last && name == (*iter)->name) {
+ return iter->get();
+ }
+ return nullptr;
+}
+
+ResourceEntry* ResourceTableType::findOrCreateEntry(const StringPiece16& name) {
+ auto last = entries.end();
+ auto iter = std::lower_bound(entries.begin(), last, name,
+ lessThanStructWithName<ResourceEntry>);
+ if (iter != last && name == (*iter)->name) {
+ return iter->get();
+ }
+ return entries.emplace(iter, new ResourceEntry{ name })->get();
+}
/**
* The default handler for collisions. A return value of -1 means keep the
* existing value, 0 means fail, and +1 means take the incoming value.
*/
-static int defaultCollisionHandler(const Value& existing, const Value& incoming) {
- IsAttributeVisitor existingIsAttr, incomingIsAttr;
- existing.accept(existingIsAttr, {});
- incoming.accept(incomingIsAttr, {});
+int ResourceTable::resolveValueCollision(Value* existing, Value* incoming) {
+ Attribute* existingAttr = valueCast<Attribute>(existing);
+ Attribute* incomingAttr = valueCast<Attribute>(incoming);
- if (!incomingIsAttr) {
- if (incoming.isWeak()) {
+ if (!incomingAttr) {
+ if (incoming->isWeak()) {
// We're trying to add a weak resource but a resource
// already exists. Keep the existing.
return -1;
- } else if (existing.isWeak()) {
+ } else if (existing->isWeak()) {
// Override the weak resource with the new strong resource.
return 1;
}
@@ -104,8 +146,8 @@
return 0;
}
- if (!existingIsAttr) {
- if (existing.isWeak()) {
+ if (!existingAttr) {
+ if (existing->isWeak()) {
// The existing value is not an attribute and it is weak,
// so take the incoming attribute value.
return 1;
@@ -115,27 +157,27 @@
return 0;
}
+ assert(incomingAttr && existingAttr);
+
//
// Attribute specific handling. At this point we know both
// values are attributes. Since we can declare and define
// attributes all-over, we do special handling to see
// which definition sticks.
//
- const Attribute& existingAttr = static_cast<const Attribute&>(existing);
- const Attribute& incomingAttr = static_cast<const Attribute&>(incoming);
- if (existingAttr.typeMask == incomingAttr.typeMask) {
+ if (existingAttr->typeMask == incomingAttr->typeMask) {
// The two attributes are both DECLs, but they are plain attributes
// with the same formats.
// Keep the strongest one.
- return existingAttr.isWeak() ? 1 : -1;
+ return existingAttr->isWeak() ? 1 : -1;
}
- if (existingAttr.isWeak() && existingAttr.typeMask == android::ResTable_map::TYPE_ANY) {
+ if (existingAttr->isWeak() && existingAttr->typeMask == android::ResTable_map::TYPE_ANY) {
// Any incoming attribute is better than this.
return 1;
}
- if (incomingAttr.isWeak() && incomingAttr.typeMask == android::ResTable_map::TYPE_ANY) {
+ if (incomingAttr->isWeak() && incomingAttr->typeMask == android::ResTable_map::TYPE_ANY) {
// The incoming attribute may be a USE instead of a DECL.
// Keep the existing attribute.
return -1;
@@ -147,284 +189,239 @@
static constexpr const char16_t* kValidNameMangledChars = u"._-$";
bool ResourceTable::addResource(const ResourceNameRef& name, const ConfigDescription& config,
- const SourceLine& source, std::unique_ptr<Value> value) {
- return addResourceImpl(name, ResourceId{}, config, source, std::move(value), kValidNameChars);
+ std::unique_ptr<Value> value, IDiagnostics* diag) {
+ return addResourceImpl(name, {}, config, std::move(value), kValidNameChars, diag);
}
bool ResourceTable::addResource(const ResourceNameRef& name, const ResourceId resId,
- const ConfigDescription& config, const SourceLine& source,
- std::unique_ptr<Value> value) {
- return addResourceImpl(name, resId, config, source, std::move(value), kValidNameChars);
+ const ConfigDescription& config, std::unique_ptr<Value> value,
+ IDiagnostics* diag) {
+ return addResourceImpl(name, resId, config, std::move(value), kValidNameChars, diag);
+}
+
+bool ResourceTable::addFileReference(const ResourceNameRef& name, const ConfigDescription& config,
+ const Source& source, const StringPiece16& path,
+ IDiagnostics* diag) {
+ std::unique_ptr<FileReference> fileRef = util::make_unique<FileReference>(
+ stringPool.makeRef(path));
+ fileRef->setSource(source);
+ return addResourceImpl(name, ResourceId{}, config, std::move(fileRef), kValidNameChars, diag);
}
bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name,
const ConfigDescription& config,
- const SourceLine& source,
- std::unique_ptr<Value> value) {
- return addResourceImpl(name, ResourceId{}, config, source, std::move(value),
- kValidNameMangledChars);
+ std::unique_ptr<Value> value,
+ IDiagnostics* diag) {
+ return addResourceImpl(name, ResourceId{}, config, std::move(value), kValidNameMangledChars,
+ diag);
+}
+
+bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name,
+ const ResourceId id,
+ const ConfigDescription& config,
+ std::unique_ptr<Value> value,
+ IDiagnostics* diag) {
+ return addResourceImpl(name, id, config, std::move(value), kValidNameMangledChars, diag);
}
bool ResourceTable::addResourceImpl(const ResourceNameRef& name, const ResourceId resId,
- const ConfigDescription& config, const SourceLine& source,
- std::unique_ptr<Value> value, const char16_t* validChars) {
- if (!name.package.empty() && name.package != mPackage) {
- Logger::error(source)
- << "resource '"
- << name
- << "' has incompatible package. Must be '"
- << mPackage
- << "'."
- << std::endl;
- return false;
- }
+ const ConfigDescription& config, std::unique_ptr<Value> value,
+ const char16_t* validChars, IDiagnostics* diag) {
+ assert(value && "value can't be nullptr");
+ assert(diag && "diagnostics can't be nullptr");
auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars);
if (badCharIter != name.entry.end()) {
- Logger::error(source)
- << "resource '"
- << name
- << "' has invalid entry name '"
- << name.entry
- << "'. Invalid character '"
- << StringPiece16(badCharIter, 1)
- << "'."
- << std::endl;
+ diag->error(DiagMessage(value->getSource())
+ << "resource '"
+ << name
+ << "' has invalid entry name '"
+ << name.entry
+ << "'. Invalid character '"
+ << StringPiece16(badCharIter, 1)
+ << "'");
return false;
}
- std::unique_ptr<ResourceTableType>& type = findOrCreateType(name.type);
- if (resId.isValid() && type->typeId != ResourceTableType::kUnsetTypeId &&
- type->typeId != resId.typeId()) {
- Logger::error(source)
- << "trying to add resource '"
- << name
- << "' with ID "
- << resId
- << " but type '"
- << type->type
- << "' already has ID "
- << std::hex << type->typeId << std::dec
- << "."
- << std::endl;
+ ResourceTablePackage* package = findOrCreatePackage(name.package);
+ if (resId.isValid() && package->id && package->id.value() != resId.packageId()) {
+ diag->error(DiagMessage(value->getSource())
+ << "trying to add resource '"
+ << name
+ << "' with ID "
+ << resId
+ << " but package '"
+ << package->name
+ << "' already has ID "
+ << std::hex << (int) package->id.value() << std::dec);
return false;
}
- std::unique_ptr<ResourceEntry>& entry = findOrCreateEntry(type, name.entry);
- if (resId.isValid() && entry->entryId != ResourceEntry::kUnsetEntryId &&
- entry->entryId != resId.entryId()) {
- Logger::error(source)
- << "trying to add resource '"
- << name
- << "' with ID "
- << resId
- << " but resource already has ID "
- << ResourceId(mPackageId, type->typeId, entry->entryId)
- << "."
- << std::endl;
+ ResourceTableType* type = package->findOrCreateType(name.type);
+ if (resId.isValid() && type->id && type->id.value() != resId.typeId()) {
+ diag->error(DiagMessage(value->getSource())
+ << "trying to add resource '"
+ << name
+ << "' with ID "
+ << resId
+ << " but type '"
+ << type->type
+ << "' already has ID "
+ << std::hex << (int) type->id.value() << std::dec);
return false;
}
- const auto endIter = std::end(entry->values);
- auto iter = std::lower_bound(std::begin(entry->values), endIter, config, compareConfigs);
+ ResourceEntry* entry = type->findOrCreateEntry(name.entry);
+ if (resId.isValid() && entry->id && entry->id.value() != resId.entryId()) {
+ diag->error(DiagMessage(value->getSource())
+ << "trying to add resource '"
+ << name
+ << "' with ID "
+ << resId
+ << " but resource already has ID "
+ << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
+ return false;
+ }
+
+ const auto endIter = entry->values.end();
+ auto iter = std::lower_bound(entry->values.begin(), endIter, config, cmp::lessThanConfig);
if (iter == endIter || iter->config != config) {
// This resource did not exist before, add it.
- entry->values.insert(iter, ResourceConfigValue{ config, source, {}, std::move(value) });
+ entry->values.insert(iter, ResourceConfigValue{ config, std::move(value) });
} else {
- int collisionResult = defaultCollisionHandler(*iter->value, *value);
+ int collisionResult = resolveValueCollision(iter->value.get(), value.get());
if (collisionResult > 0) {
// Take the incoming value.
- *iter = ResourceConfigValue{ config, source, {}, std::move(value) };
+ iter->value = std::move(value);
} else if (collisionResult == 0) {
- Logger::error(source)
- << "duplicate value for resource '" << name << "' "
- << "with config '" << iter->config << "'."
- << std::endl;
-
- Logger::error(iter->source)
- << "resource previously defined here."
- << std::endl;
+ diag->error(DiagMessage(value->getSource())
+ << "duplicate value for resource '" << name << "' "
+ << "with config '" << config << "'");
+ diag->error(DiagMessage(iter->value->getSource())
+ << "resource previously defined here");
return false;
}
}
if (resId.isValid()) {
- type->typeId = resId.typeId();
- entry->entryId = resId.entryId();
+ package->id = resId.packageId();
+ type->id = resId.typeId();
+ entry->id = resId.entryId();
}
return true;
}
-bool ResourceTable::markPublic(const ResourceNameRef& name, const ResourceId resId,
- const SourceLine& source) {
- return markPublicImpl(name, resId, source, kValidNameChars);
+bool ResourceTable::setSymbolState(const ResourceNameRef& name, const ResourceId resId,
+ const Symbol& symbol, IDiagnostics* diag) {
+ return setSymbolStateImpl(name, resId, symbol, kValidNameChars, diag);
}
-bool ResourceTable::markPublicAllowMangled(const ResourceNameRef& name, const ResourceId resId,
- const SourceLine& source) {
- return markPublicImpl(name, resId, source, kValidNameMangledChars);
+bool ResourceTable::setSymbolStateAllowMangled(const ResourceNameRef& name,
+ const ResourceId resId,
+ const Symbol& symbol, IDiagnostics* diag) {
+ return setSymbolStateImpl(name, resId, symbol, kValidNameMangledChars, diag);
}
-bool ResourceTable::markPublicImpl(const ResourceNameRef& name, const ResourceId resId,
- const SourceLine& source, const char16_t* validChars) {
- if (!name.package.empty() && name.package != mPackage) {
- Logger::error(source)
- << "resource '"
- << name
- << "' has incompatible package. Must be '"
- << mPackage
- << "'."
- << std::endl;
- return false;
+bool ResourceTable::setSymbolStateImpl(const ResourceNameRef& name, const ResourceId resId,
+ const Symbol& symbol, const char16_t* validChars,
+ IDiagnostics* diag) {
+ assert(diag && "diagnostics can't be nullptr");
+
+ if (symbol.state == SymbolState::kUndefined) {
+ // Nothing to do.
+ return true;
}
auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars);
if (badCharIter != name.entry.end()) {
- Logger::error(source)
- << "resource '"
- << name
- << "' has invalid entry name '"
- << name.entry
- << "'. Invalid character '"
- << StringPiece16(badCharIter, 1)
- << "'."
- << std::endl;
+ diag->error(DiagMessage(symbol.source)
+ << "resource '"
+ << name
+ << "' has invalid entry name '"
+ << name.entry
+ << "'. Invalid character '"
+ << StringPiece16(badCharIter, 1)
+ << "'");
return false;
}
- std::unique_ptr<ResourceTableType>& type = findOrCreateType(name.type);
- if (resId.isValid() && type->typeId != ResourceTableType::kUnsetTypeId &&
- type->typeId != resId.typeId()) {
- Logger::error(source)
- << "trying to make resource '"
- << name
- << "' public with ID "
- << resId
- << " but type '"
- << type->type
- << "' already has ID "
- << std::hex << type->typeId << std::dec
- << "."
- << std::endl;
+ ResourceTablePackage* package = findOrCreatePackage(name.package);
+ if (resId.isValid() && package->id && package->id.value() != resId.packageId()) {
+ diag->error(DiagMessage(symbol.source)
+ << "trying to add resource '"
+ << name
+ << "' with ID "
+ << resId
+ << " but package '"
+ << package->name
+ << "' already has ID "
+ << std::hex << (int) package->id.value() << std::dec);
return false;
}
- std::unique_ptr<ResourceEntry>& entry = findOrCreateEntry(type, name.entry);
- if (resId.isValid() && entry->entryId != ResourceEntry::kUnsetEntryId &&
- entry->entryId != resId.entryId()) {
- Logger::error(source)
- << "trying to make resource '"
- << name
- << "' public with ID "
- << resId
- << " but resource already has ID "
- << ResourceId(mPackageId, type->typeId, entry->entryId)
- << "."
- << std::endl;
+ ResourceTableType* type = package->findOrCreateType(name.type);
+ if (resId.isValid() && type->id && type->id.value() != resId.typeId()) {
+ diag->error(DiagMessage(symbol.source)
+ << "trying to add resource '"
+ << name
+ << "' with ID "
+ << resId
+ << " but type '"
+ << type->type
+ << "' already has ID "
+ << std::hex << (int) type->id.value() << std::dec);
return false;
}
- type->publicStatus.isPublic = true;
- entry->publicStatus.isPublic = true;
- entry->publicStatus.source = source;
+ ResourceEntry* entry = type->findOrCreateEntry(name.entry);
+ if (resId.isValid() && entry->id && entry->id.value() != resId.entryId()) {
+ diag->error(DiagMessage(symbol.source)
+ << "trying to add resource '"
+ << name
+ << "' with ID "
+ << resId
+ << " but resource already has ID "
+ << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
+ return false;
+ }
+
+ // Only mark the type state as public, it doesn't care about being private.
+ if (symbol.state == SymbolState::kPublic) {
+ type->symbolStatus.state = SymbolState::kPublic;
+ }
+
+ // Downgrading to a private symbol from a public one is not allowed.
+ if (entry->symbolStatus.state != SymbolState::kPublic) {
+ if (entry->symbolStatus.state != symbol.state) {
+ entry->symbolStatus = std::move(symbol);
+ }
+ }
if (resId.isValid()) {
- type->typeId = resId.typeId();
- entry->entryId = resId.entryId();
+ package->id = resId.packageId();
+ type->id = resId.typeId();
+ entry->id = resId.entryId();
}
return true;
}
-bool ResourceTable::merge(ResourceTable&& other) {
- const bool mangleNames = mPackage != other.getPackage();
- std::u16string mangledName;
-
- for (auto& otherType : other) {
- std::unique_ptr<ResourceTableType>& type = findOrCreateType(otherType->type);
- if (otherType->publicStatus.isPublic) {
- if (type->publicStatus.isPublic && type->typeId != otherType->typeId) {
- Logger::error() << "can not merge type '" << type->type
- << "': conflicting public IDs "
- << "(" << type->typeId << " vs " << otherType->typeId << ")."
- << std::endl;
- return false;
- }
- type->publicStatus = std::move(otherType->publicStatus);
- type->typeId = otherType->typeId;
- }
-
- for (auto& otherEntry : otherType->entries) {
- const std::u16string* nameToAdd = &otherEntry->name;
- if (mangleNames) {
- mangledName = otherEntry->name;
- NameMangler::mangle(other.getPackage(), &mangledName);
- nameToAdd = &mangledName;
- }
-
- std::unique_ptr<ResourceEntry>& entry = findOrCreateEntry(type, *nameToAdd);
- if (otherEntry->publicStatus.isPublic) {
- if (entry->publicStatus.isPublic && entry->entryId != otherEntry->entryId) {
- Logger::error() << "can not merge entry '" << type->type << "/" << entry->name
- << "': conflicting public IDs "
- << "(" << entry->entryId << " vs " << entry->entryId << ")."
- << std::endl;
- return false;
- }
- entry->publicStatus = std::move(otherEntry->publicStatus);
- entry->entryId = otherEntry->entryId;
- }
-
- for (ResourceConfigValue& otherValue : otherEntry->values) {
- auto iter = std::lower_bound(entry->values.begin(), entry->values.end(),
- otherValue.config, compareConfigs);
- if (iter != entry->values.end() && iter->config == otherValue.config) {
- int collisionResult = defaultCollisionHandler(*iter->value, *otherValue.value);
- if (collisionResult > 0) {
- // Take the incoming value.
- iter->source = std::move(otherValue.source);
- iter->comment = std::move(otherValue.comment);
- iter->value = std::unique_ptr<Value>(otherValue.value->clone(&mValuePool));
- } else if (collisionResult == 0) {
- ResourceNameRef resourceName = { mPackage, type->type, entry->name };
- Logger::error(otherValue.source)
- << "resource '" << resourceName << "' has a conflicting value for "
- << "configuration (" << otherValue.config << ")."
- << std::endl;
- Logger::note(iter->source) << "originally defined here." << std::endl;
- return false;
- }
- } else {
- entry->values.insert(iter, ResourceConfigValue{
- otherValue.config,
- std::move(otherValue.source),
- std::move(otherValue.comment),
- std::unique_ptr<Value>(otherValue.value->clone(&mValuePool)),
- });
- }
- }
- }
- }
- return true;
-}
-
-std::tuple<const ResourceTableType*, const ResourceEntry*>
-ResourceTable::findResource(const ResourceNameRef& name) const {
- if (name.package != mPackage) {
+Maybe<ResourceTable::SearchResult>
+ResourceTable::findResource(const ResourceNameRef& name) {
+ ResourceTablePackage* package = findPackage(name.package);
+ if (!package) {
return {};
}
- auto iter = std::lower_bound(mTypes.begin(), mTypes.end(), name.type, lessThanType);
- if (iter == mTypes.end() || (*iter)->type != name.type) {
+ ResourceTableType* type = package->findType(name.type);
+ if (!type) {
return {};
}
- const std::unique_ptr<ResourceTableType>& type = *iter;
- auto iter2 = std::lower_bound(type->entries.begin(), type->entries.end(), name.entry,
- lessThanEntry);
- if (iter2 == type->entries.end() || name.entry != (*iter2)->name) {
+ ResourceEntry* entry = type->findEntry(name.entry);
+ if (!entry) {
return {};
}
- return std::make_tuple(iter->get(), iter2->get());
+ return SearchResult{ package, type, entry };
}
} // namespace aapt
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index 706f56a..980504b 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -18,6 +18,7 @@
#define AAPT_RESOURCE_TABLE_H
#include "ConfigDescription.h"
+#include "Diagnostics.h"
#include "Resource.h"
#include "ResourceValues.h"
#include "Source.h"
@@ -30,22 +31,26 @@
namespace aapt {
+enum class SymbolState {
+ kUndefined,
+ kPublic,
+ kPrivate
+};
+
/**
* The Public status of a resource.
*/
-struct Public {
- bool isPublic = false;
- SourceLine source;
+struct Symbol {
+ SymbolState state = SymbolState::kUndefined;
+ Source source;
std::u16string comment;
};
/**
- * The resource value for a specific configuration.
+ * Represents a value defined for a given configuration.
*/
struct ResourceConfigValue {
ConfigDescription config;
- SourceLine source;
- std::u16string comment;
std::unique_ptr<Value> value;
};
@@ -54,10 +59,6 @@
* varying values for each defined configuration.
*/
struct ResourceEntry {
- enum {
- kUnsetEntryId = 0xffffffffu
- };
-
/**
* The name of the resource. Immutable, as
* this determines the order of this resource
@@ -68,21 +69,20 @@
/**
* The entry ID for this resource.
*/
- size_t entryId;
+ Maybe<uint16_t> id;
/**
* Whether this resource is public (and must maintain the same
* entry ID across builds).
*/
- Public publicStatus;
+ Symbol symbolStatus;
/**
* The resource's values for each configuration.
*/
std::vector<ResourceConfigValue> values;
- inline ResourceEntry(const StringPiece16& _name);
- inline ResourceEntry(const ResourceEntry* rhs);
+ ResourceEntry(const StringPiece16& name) : name(name.toString()) { }
};
/**
@@ -90,10 +90,6 @@
* for this type.
*/
struct ResourceTableType {
- enum {
- kUnsetTypeId = 0xffffffffu
- };
-
/**
* The logical type of resource (string, drawable, layout, etc.).
*/
@@ -102,21 +98,43 @@
/**
* The type ID for this resource.
*/
- size_t typeId;
+ Maybe<uint8_t> id;
/**
* Whether this type is public (and must maintain the same
* type ID across builds).
*/
- Public publicStatus;
+ Symbol symbolStatus;
/**
* List of resources for this type.
*/
std::vector<std::unique_ptr<ResourceEntry>> entries;
- ResourceTableType(const ResourceType _type);
- ResourceTableType(const ResourceTableType* rhs);
+ explicit ResourceTableType(const ResourceType type) : type(type) { }
+
+ ResourceEntry* findEntry(const StringPiece16& name);
+
+ ResourceEntry* findOrCreateEntry(const StringPiece16& name);
+};
+
+enum class PackageType {
+ System,
+ Vendor,
+ App,
+ Dynamic
+};
+
+struct ResourceTablePackage {
+ PackageType type = PackageType::App;
+ Maybe<uint8_t> id;
+ std::u16string name;
+
+ std::vector<std::unique_ptr<ResourceTableType>> types;
+
+ ResourceTableType* findType(ResourceType type);
+
+ ResourceTableType* findOrCreateType(const ResourceType type);
};
/**
@@ -125,23 +143,27 @@
*/
class ResourceTable {
public:
- using iterator = std::vector<std::unique_ptr<ResourceTableType>>::iterator;
- using const_iterator = std::vector<std::unique_ptr<ResourceTableType>>::const_iterator;
+ ResourceTable() = default;
+ ResourceTable(const ResourceTable&) = delete;
+ ResourceTable& operator=(const ResourceTable&) = delete;
- enum {
- kUnsetPackageId = 0xffffffff
- };
-
- ResourceTable();
-
- size_t getPackageId() const;
- void setPackageId(size_t packageId);
-
- const std::u16string& getPackage() const;
- void setPackage(const StringPiece16& package);
+ /**
+ * When a collision of resources occurs, this method decides which value to keep.
+ * Returns -1 if the existing value should be chosen.
+ * Returns 0 if the collision can not be resolved (error).
+ * Returns 1 if the incoming value should be chosen.
+ */
+ static int resolveValueCollision(Value* existing, Value* incoming);
bool addResource(const ResourceNameRef& name, const ConfigDescription& config,
- const SourceLine& source, std::unique_ptr<Value> value);
+ std::unique_ptr<Value> value, IDiagnostics* diag);
+
+ bool addResource(const ResourceNameRef& name, const ResourceId resId,
+ const ConfigDescription& config, std::unique_ptr<Value> value,
+ IDiagnostics* diag);
+
+ bool addFileReference(const ResourceNameRef& name, const ConfigDescription& config,
+ const Source& source, const StringPiece16& path, IDiagnostics* diag);
/**
* Same as addResource, but doesn't verify the validity of the name. This is used
@@ -149,129 +171,63 @@
* names.
*/
bool addResourceAllowMangled(const ResourceNameRef& name, const ConfigDescription& config,
- const SourceLine& source, std::unique_ptr<Value> value);
+ std::unique_ptr<Value> value, IDiagnostics* diag);
- bool addResource(const ResourceNameRef& name, const ResourceId resId,
- const ConfigDescription& config, const SourceLine& source,
- std::unique_ptr<Value> value);
+ bool addResourceAllowMangled(const ResourceNameRef& name, const ResourceId id,
+ const ConfigDescription& config, std::unique_ptr<Value> value,
+ IDiagnostics* diag);
- bool markPublic(const ResourceNameRef& name, const ResourceId resId, const SourceLine& source);
- bool markPublicAllowMangled(const ResourceNameRef& name, const ResourceId resId,
- const SourceLine& source);
+ bool setSymbolState(const ResourceNameRef& name, const ResourceId resId,
+ const Symbol& symbol, IDiagnostics* diag);
- /*
- * Merges the resources from `other` into this table, mangling the names of the resources
- * if `other` has a different package name.
- */
- bool merge(ResourceTable&& other);
+ bool setSymbolStateAllowMangled(const ResourceNameRef& name, const ResourceId resId,
+ const Symbol& symbol, IDiagnostics* diag);
+
+ struct SearchResult {
+ ResourceTablePackage* package;
+ ResourceTableType* type;
+ ResourceEntry* entry;
+ };
+
+ Maybe<SearchResult> findResource(const ResourceNameRef& name);
/**
- * Returns the string pool used by this ResourceTable.
- * Values that reference strings should use this pool to create
- * their strings.
+ * The string pool used by this resource table. Values that reference strings must use
+ * this pool to create their strings.
+ *
+ * NOTE: `stringPool` must come before `packages` so that it is destroyed after.
+ * When `string pool` references are destroyed (as they will be when `packages`
+ * is destroyed), they decrement a refCount, which would cause invalid
+ * memory access if the pool was already destroyed.
*/
- StringPool& getValueStringPool();
- const StringPool& getValueStringPool() const;
+ StringPool stringPool;
- std::tuple<const ResourceTableType*, const ResourceEntry*>
- findResource(const ResourceNameRef& name) const;
+ /**
+ * The list of packages in this table, sorted alphabetically by package name.
+ */
+ std::vector<std::unique_ptr<ResourceTablePackage>> packages;
- iterator begin();
- iterator end();
- const_iterator begin() const;
- const_iterator end() const;
+ /**
+ * Returns the package struct with the given name, or nullptr if such a package does not
+ * exist. The empty string is a valid package and typically is used to represent the
+ * 'current' package before it is known to the ResourceTable.
+ */
+ ResourceTablePackage* findPackage(const StringPiece16& name);
+
+ ResourceTablePackage* findPackageById(uint8_t id);
+
+ ResourceTablePackage* createPackage(const StringPiece16& name, Maybe<uint8_t> id = {});
private:
- std::unique_ptr<ResourceTableType>& findOrCreateType(ResourceType type);
- std::unique_ptr<ResourceEntry>& findOrCreateEntry(std::unique_ptr<ResourceTableType>& type,
- const StringPiece16& name);
+ ResourceTablePackage* findOrCreatePackage(const StringPiece16& name);
- bool addResourceImpl(const ResourceNameRef& name, const ResourceId resId,
- const ConfigDescription& config, const SourceLine& source,
- std::unique_ptr<Value> value, const char16_t* validChars);
- bool markPublicImpl(const ResourceNameRef& name, const ResourceId resId,
- const SourceLine& source, const char16_t* validChars);
-
- std::u16string mPackage;
- size_t mPackageId;
-
- // StringPool must come before mTypes so that it is destroyed after.
- // When StringPool references are destroyed (as they will be when mTypes
- // is destroyed), they decrement a refCount, which would cause invalid
- // memory access if the pool was already destroyed.
- StringPool mValuePool;
-
- std::vector<std::unique_ptr<ResourceTableType>> mTypes;
+ bool addResourceImpl(const ResourceNameRef& name, ResourceId resId,
+ const ConfigDescription& config, std::unique_ptr<Value> value,
+ const char16_t* validChars, IDiagnostics* diag);
+ bool setSymbolStateImpl(const ResourceNameRef& name, ResourceId resId,
+ const Symbol& symbol, const char16_t* validChars, IDiagnostics* diag);
};
-//
-// ResourceEntry implementation.
-//
-
-inline ResourceEntry::ResourceEntry(const StringPiece16& _name) :
- name(_name.toString()), entryId(kUnsetEntryId) {
-}
-
-inline ResourceEntry::ResourceEntry(const ResourceEntry* rhs) :
- name(rhs->name), entryId(rhs->entryId), publicStatus(rhs->publicStatus) {
-}
-
-//
-// ResourceTableType implementation.
-//
-
-inline ResourceTableType::ResourceTableType(const ResourceType _type) :
- type(_type), typeId(kUnsetTypeId) {
-}
-
-inline ResourceTableType::ResourceTableType(const ResourceTableType* rhs) :
- type(rhs->type), typeId(rhs->typeId), publicStatus(rhs->publicStatus) {
-}
-
-//
-// ResourceTable implementation.
-//
-
-inline StringPool& ResourceTable::getValueStringPool() {
- return mValuePool;
-}
-
-inline const StringPool& ResourceTable::getValueStringPool() const {
- return mValuePool;
-}
-
-inline ResourceTable::iterator ResourceTable::begin() {
- return mTypes.begin();
-}
-
-inline ResourceTable::iterator ResourceTable::end() {
- return mTypes.end();
-}
-
-inline ResourceTable::const_iterator ResourceTable::begin() const {
- return mTypes.begin();
-}
-
-inline ResourceTable::const_iterator ResourceTable::end() const {
- return mTypes.end();
-}
-
-inline const std::u16string& ResourceTable::getPackage() const {
- return mPackage;
-}
-
-inline size_t ResourceTable::getPackageId() const {
- return mPackageId;
-}
-
-inline void ResourceTable::setPackage(const StringPiece16& package) {
- mPackage = package.toString();
-}
-
-inline void ResourceTable::setPackageId(size_t packageId) {
- mPackageId = packageId;
-}
-
} // namespace aapt
#endif // AAPT_RESOURCE_TABLE_H
diff --git a/tools/aapt2/ResourceTableResolver.cpp b/tools/aapt2/ResourceTableResolver.cpp
deleted file mode 100644
index 910c2c0..0000000
--- a/tools/aapt2/ResourceTableResolver.cpp
+++ /dev/null
@@ -1,202 +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.
- */
-
-#include "Maybe.h"
-#include "NameMangler.h"
-#include "Resource.h"
-#include "ResourceTable.h"
-#include "ResourceTableResolver.h"
-#include "ResourceValues.h"
-#include "Util.h"
-
-#include <androidfw/AssetManager.h>
-#include <androidfw/ResourceTypes.h>
-#include <memory>
-#include <vector>
-
-namespace aapt {
-
-ResourceTableResolver::ResourceTableResolver(
- std::shared_ptr<const ResourceTable> table,
- const std::vector<std::shared_ptr<const android::AssetManager>>& sources) :
- mTable(table), mSources(sources) {
- for (const auto& assetManager : mSources) {
- const android::ResTable& resTable = assetManager->getResources(false);
- const size_t packageCount = resTable.getBasePackageCount();
- for (size_t i = 0; i < packageCount; i++) {
- std::u16string packageName = resTable.getBasePackageName(i).string();
- mIncludedPackages.insert(std::move(packageName));
- }
- }
-}
-
-Maybe<ResourceId> ResourceTableResolver::findId(const ResourceName& name) {
- Maybe<Entry> result = findAttribute(name);
- if (result) {
- return result.value().id;
- }
- return {};
-}
-
-Maybe<IResolver::Entry> ResourceTableResolver::findAttribute(const ResourceName& name) {
- auto cacheIter = mCache.find(name);
- if (cacheIter != std::end(mCache)) {
- return Entry{ cacheIter->second.id, cacheIter->second.attr.get() };
- }
-
- ResourceName mangledName;
- const ResourceName* nameToSearch = &name;
- if (name.package != mTable->getPackage()) {
- // This may be a reference to an included resource or
- // to a mangled resource.
- if (mIncludedPackages.find(name.package) == mIncludedPackages.end()) {
- // This is not in our included set, so mangle the name and
- // check for that.
- mangledName.entry = name.entry;
- NameMangler::mangle(name.package, &mangledName.entry);
- mangledName.package = mTable->getPackage();
- mangledName.type = name.type;
- nameToSearch = &mangledName;
- } else {
- const CacheEntry* cacheEntry = buildCacheEntry(name);
- if (cacheEntry) {
- return Entry{ cacheEntry->id, cacheEntry->attr.get() };
- }
- return {};
- }
- }
-
- const ResourceTableType* type;
- const ResourceEntry* entry;
- std::tie(type, entry) = mTable->findResource(*nameToSearch);
- if (type && entry) {
- Entry result = {};
- if (mTable->getPackageId() != ResourceTable::kUnsetPackageId &&
- type->typeId != ResourceTableType::kUnsetTypeId &&
- entry->entryId != ResourceEntry::kUnsetEntryId) {
- result.id = ResourceId(mTable->getPackageId(), type->typeId, entry->entryId);
- }
-
- if (!entry->values.empty()) {
- visitFunc<Attribute>(*entry->values.front().value, [&result](Attribute& attr) {
- result.attr = &attr;
- });
- }
- return result;
- }
- return {};
-}
-
-Maybe<ResourceName> ResourceTableResolver::findName(ResourceId resId) {
- for (const auto& assetManager : mSources) {
- const android::ResTable& table = assetManager->getResources(false);
-
- android::ResTable::resource_name resourceName;
- if (!table.getResourceName(resId.id, false, &resourceName)) {
- continue;
- }
-
- const ResourceType* type = parseResourceType(StringPiece16(resourceName.type,
- resourceName.typeLen));
- assert(type);
- return ResourceName{
- { resourceName.package, resourceName.packageLen },
- *type,
- { resourceName.name, resourceName.nameLen } };
- }
- return {};
-}
-
-/**
- * This is called when we need to lookup a resource name in the AssetManager.
- * Since the values in the AssetManager are not parsed like in a ResourceTable,
- * we must create Attribute objects here if we find them.
- */
-const ResourceTableResolver::CacheEntry* ResourceTableResolver::buildCacheEntry(
- const ResourceName& name) {
- for (const auto& assetManager : mSources) {
- const android::ResTable& table = assetManager->getResources(false);
-
- const StringPiece16 type16 = toString(name.type);
- ResourceId resId {
- table.identifierForName(
- name.entry.data(), name.entry.size(),
- type16.data(), type16.size(),
- name.package.data(), name.package.size())
- };
-
- if (!resId.isValid()) {
- continue;
- }
-
- CacheEntry& entry = mCache[name];
- entry.id = resId;
-
- //
- // Now check to see if this resource is an Attribute.
- //
-
- const android::ResTable::bag_entry* bagBegin;
- ssize_t bags = table.lockBag(resId.id, &bagBegin);
- if (bags < 1) {
- table.unlockBag(bagBegin);
- return &entry;
- }
-
- // Look for the ATTR_TYPE key in the bag and check the types it supports.
- uint32_t attrTypeMask = 0;
- for (ssize_t i = 0; i < bags; i++) {
- if (bagBegin[i].map.name.ident == android::ResTable_map::ATTR_TYPE) {
- attrTypeMask = bagBegin[i].map.value.data;
- }
- }
-
- entry.attr = util::make_unique<Attribute>(false);
-
- if (attrTypeMask & android::ResTable_map::TYPE_ENUM ||
- attrTypeMask & android::ResTable_map::TYPE_FLAGS) {
- for (ssize_t i = 0; i < bags; i++) {
- if (Res_INTERNALID(bagBegin[i].map.name.ident)) {
- // Internal IDs are special keys, which are not enum/flag symbols, so skip.
- continue;
- }
-
- android::ResTable::resource_name symbolName;
- bool result = table.getResourceName(bagBegin[i].map.name.ident, false,
- &symbolName);
- assert(result);
- const ResourceType* type = parseResourceType(
- StringPiece16(symbolName.type, symbolName.typeLen));
- assert(type);
-
- entry.attr->symbols.push_back(Attribute::Symbol{
- Reference(ResourceNameRef(
- StringPiece16(symbolName.package, symbolName.packageLen),
- *type,
- StringPiece16(symbolName.name, symbolName.nameLen))),
- bagBegin[i].map.value.data
- });
- }
- }
-
- entry.attr->typeMask |= attrTypeMask;
- table.unlockBag(bagBegin);
- return &entry;
- }
- return nullptr;
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/ResourceTableResolver.h b/tools/aapt2/ResourceTableResolver.h
deleted file mode 100644
index 8f6b0b5..0000000
--- a/tools/aapt2/ResourceTableResolver.h
+++ /dev/null
@@ -1,70 +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_RESOURCE_TABLE_RESOLVER_H
-#define AAPT_RESOURCE_TABLE_RESOLVER_H
-
-#include "Maybe.h"
-#include "Resolver.h"
-#include "Resource.h"
-#include "ResourceTable.h"
-#include "ResourceValues.h"
-
-#include <androidfw/AssetManager.h>
-#include <memory>
-#include <vector>
-#include <unordered_set>
-
-namespace aapt {
-
-/**
- * Encapsulates the search of library sources as well as the local ResourceTable.
- */
-class ResourceTableResolver : public IResolver {
-public:
- /**
- * Creates a resolver with a local ResourceTable and an AssetManager
- * loaded with library packages.
- */
- ResourceTableResolver(
- std::shared_ptr<const ResourceTable> table,
- const std::vector<std::shared_ptr<const android::AssetManager>>& sources);
-
- ResourceTableResolver(const ResourceTableResolver&) = delete; // Not copyable.
-
- virtual Maybe<ResourceId> findId(const ResourceName& name) override;
-
- virtual Maybe<Entry> findAttribute(const ResourceName& name) override;
-
- virtual Maybe<ResourceName> findName(ResourceId resId) override;
-
-private:
- struct CacheEntry {
- ResourceId id;
- std::unique_ptr<Attribute> attr;
- };
-
- const CacheEntry* buildCacheEntry(const ResourceName& name);
-
- std::shared_ptr<const ResourceTable> mTable;
- std::vector<std::shared_ptr<const android::AssetManager>> mSources;
- std::map<ResourceName, CacheEntry> mCache;
- std::unordered_set<std::u16string> mIncludedPackages;
-};
-
-} // namespace aapt
-
-#endif // AAPT_RESOURCE_TABLE_RESOLVER_H
diff --git a/tools/aapt2/ResourceTable_test.cpp b/tools/aapt2/ResourceTable_test.cpp
index 06d8699..42508fe 100644
--- a/tools/aapt2/ResourceTable_test.cpp
+++ b/tools/aapt2/ResourceTable_test.cpp
@@ -14,9 +14,12 @@
* limitations under the License.
*/
+#include "Diagnostics.h"
#include "ResourceTable.h"
#include "ResourceValues.h"
-#include "Util.h"
+#include "util/Util.h"
+
+#include "test/Builders.h"
#include <algorithm>
#include <gtest/gtest.h>
@@ -25,204 +28,100 @@
namespace aapt {
-struct TestValue : public Value {
- std::u16string value;
+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 {}
+ };
- TestValue(StringPiece16 str) : value(str.toString()) {
- }
-
- TestValue* clone(StringPool* /*newPool*/) const override {
- return new TestValue(value);
- }
-
- void print(std::ostream& out) const override {
- out << "(test) " << value;
- }
-
- virtual void accept(ValueVisitor&, ValueVisitorArgs&&) override {}
- virtual void accept(ConstValueVisitor&, ValueVisitorArgs&&) const override {}
+ EmptyDiagnostics mDiagnostics;
};
-struct TestWeakValue : public Value {
- bool isWeak() const override {
- return true;
- }
-
- TestWeakValue* clone(StringPool* /*newPool*/) const override {
- return new TestWeakValue();
- }
-
- void print(std::ostream& out) const override {
- out << "(test) [weak]";
- }
-
- virtual void accept(ValueVisitor&, ValueVisitorArgs&&) override {}
- virtual void accept(ConstValueVisitor&, ValueVisitorArgs&&) const override {}
-};
-
-TEST(ResourceTableTest, FailToAddResourceWithBadName) {
+TEST_F(ResourceTableTest, FailToAddResourceWithBadName) {
ResourceTable table;
- table.setPackage(u"android");
EXPECT_FALSE(table.addResource(
- ResourceNameRef{ u"android", ResourceType::kId, u"hey,there" },
- {}, SourceLine{ "test.xml", 21 },
- util::make_unique<TestValue>(u"rawValue")));
+ ResourceNameRef(u"android", ResourceType::kId, u"hey,there"),
+ ConfigDescription{},
+ test::ValueBuilder<Id>().setSource("test.xml", 21u).build(),
+ &mDiagnostics));
EXPECT_FALSE(table.addResource(
- ResourceNameRef{ u"android", ResourceType::kId, u"hey:there" },
- {}, SourceLine{ "test.xml", 21 },
- util::make_unique<TestValue>(u"rawValue")));
+ ResourceNameRef(u"android", ResourceType::kId, u"hey:there"),
+ ConfigDescription{},
+ test::ValueBuilder<Id>().setSource("test.xml", 21u).build(),
+ &mDiagnostics));
}
-TEST(ResourceTableTest, AddOneResource) {
- const std::u16string kAndroidPackage = u"android";
-
+TEST_F(ResourceTableTest, AddOneResource) {
ResourceTable table;
- table.setPackage(kAndroidPackage);
- const ResourceName name = { kAndroidPackage, ResourceType::kAttr, u"id" };
+ EXPECT_TRUE(table.addResource(test::parseNameOrDie(u"@android:attr/id"),
+ ConfigDescription{},
+ test::ValueBuilder<Id>()
+ .setSource("test/path/file.xml", 23u).build(),
+ &mDiagnostics));
- EXPECT_TRUE(table.addResource(name, {}, SourceLine{ "test/path/file.xml", 23 },
- util::make_unique<TestValue>(u"rawValue")));
-
- const ResourceTableType* type;
- const ResourceEntry* entry;
- std::tie(type, entry) = table.findResource(name);
- ASSERT_NE(nullptr, type);
- ASSERT_NE(nullptr, entry);
- EXPECT_EQ(name.entry, entry->name);
-
- ASSERT_NE(std::end(entry->values),
- std::find_if(std::begin(entry->values), std::end(entry->values),
- [](const ResourceConfigValue& val) -> bool {
- return val.config == ConfigDescription{};
- }));
+ ASSERT_NE(nullptr, test::getValue<Id>(&table, u"@android:attr/id"));
}
-TEST(ResourceTableTest, AddMultipleResources) {
- const std::u16string kAndroidPackage = u"android";
+TEST_F(ResourceTableTest, AddMultipleResources) {
ResourceTable table;
- table.setPackage(kAndroidPackage);
ConfigDescription config;
ConfigDescription languageConfig;
memcpy(languageConfig.language, "pl", sizeof(languageConfig.language));
EXPECT_TRUE(table.addResource(
- ResourceName{ kAndroidPackage, ResourceType::kAttr, u"layout_width" },
- config, SourceLine{ "test/path/file.xml", 10 },
- util::make_unique<TestValue>(u"rawValue")));
+ test::parseNameOrDie(u"@android:attr/layout_width"),
+ config,
+ test::ValueBuilder<Id>().setSource("test/path/file.xml", 10u).build(),
+ &mDiagnostics));
EXPECT_TRUE(table.addResource(
- ResourceName{ kAndroidPackage, ResourceType::kAttr, u"id" },
- config, SourceLine{ "test/path/file.xml", 12 },
- util::make_unique<TestValue>(u"rawValue")));
+ test::parseNameOrDie(u"@android:attr/id"),
+ config,
+ test::ValueBuilder<Id>().setSource("test/path/file.xml", 12u).build(),
+ &mDiagnostics));
EXPECT_TRUE(table.addResource(
- ResourceName{ kAndroidPackage, ResourceType::kString, u"ok" },
- config, SourceLine{ "test/path/file.xml", 14 },
- util::make_unique<TestValue>(u"Ok")));
+ test::parseNameOrDie(u"@android:string/ok"),
+ config,
+ test::ValueBuilder<Id>().setSource("test/path/file.xml", 14u).build(),
+ &mDiagnostics));
EXPECT_TRUE(table.addResource(
- ResourceName{ kAndroidPackage, ResourceType::kString, u"ok" },
- languageConfig, SourceLine{ "test/path/file.xml", 20 },
- util::make_unique<TestValue>(u"Tak")));
+ test::parseNameOrDie(u"@android:string/ok"),
+ languageConfig,
+ test::ValueBuilder<BinaryPrimitive>(android::Res_value{})
+ .setSource("test/path/file.xml", 20u)
+ .build(),
+ &mDiagnostics));
- const auto endTypeIter = std::end(table);
- auto typeIter = std::begin(table);
-
- ASSERT_NE(endTypeIter, typeIter);
- EXPECT_EQ(ResourceType::kAttr, (*typeIter)->type);
-
- {
- const std::unique_ptr<ResourceTableType>& type = *typeIter;
- const auto endEntryIter = std::end(type->entries);
- auto entryIter = std::begin(type->entries);
- ASSERT_NE(endEntryIter, entryIter);
- EXPECT_EQ(std::u16string(u"id"), (*entryIter)->name);
-
- ++entryIter;
- ASSERT_NE(endEntryIter, entryIter);
- EXPECT_EQ(std::u16string(u"layout_width"), (*entryIter)->name);
-
- ++entryIter;
- ASSERT_EQ(endEntryIter, entryIter);
- }
-
- ++typeIter;
- ASSERT_NE(endTypeIter, typeIter);
- EXPECT_EQ(ResourceType::kString, (*typeIter)->type);
-
- {
- const std::unique_ptr<ResourceTableType>& type = *typeIter;
- const auto endEntryIter = std::end(type->entries);
- auto entryIter = std::begin(type->entries);
- ASSERT_NE(endEntryIter, entryIter);
- EXPECT_EQ(std::u16string(u"ok"), (*entryIter)->name);
-
- {
- const std::unique_ptr<ResourceEntry>& entry = *entryIter;
- const auto endConfigIter = std::end(entry->values);
- auto configIter = std::begin(entry->values);
-
- ASSERT_NE(endConfigIter, configIter);
- EXPECT_EQ(config, configIter->config);
- const TestValue* value =
- dynamic_cast<const TestValue*>(configIter->value.get());
- ASSERT_NE(nullptr, value);
- EXPECT_EQ(std::u16string(u"Ok"), value->value);
-
- ++configIter;
- ASSERT_NE(endConfigIter, configIter);
- EXPECT_EQ(languageConfig, configIter->config);
- EXPECT_NE(nullptr, configIter->value);
-
- value = dynamic_cast<const TestValue*>(configIter->value.get());
- ASSERT_NE(nullptr, value);
- EXPECT_EQ(std::u16string(u"Tak"), value->value);
-
- ++configIter;
- EXPECT_EQ(endConfigIter, configIter);
- }
-
- ++entryIter;
- ASSERT_EQ(endEntryIter, entryIter);
- }
-
- ++typeIter;
- EXPECT_EQ(endTypeIter, typeIter);
+ ASSERT_NE(nullptr, test::getValue<Id>(&table, u"@android:attr/layout_width"));
+ ASSERT_NE(nullptr, test::getValue<Id>(&table, u"@android:attr/id"));
+ ASSERT_NE(nullptr, test::getValue<Id>(&table, u"@android:string/ok"));
+ ASSERT_NE(nullptr, test::getValueForConfig<BinaryPrimitive>(&table, u"@android:string/ok",
+ languageConfig));
}
-TEST(ResourceTableTest, OverrideWeakResourceValue) {
- const std::u16string kAndroid = u"android";
-
+TEST_F(ResourceTableTest, OverrideWeakResourceValue) {
ResourceTable table;
- table.setPackage(kAndroid);
- table.setPackageId(0x01);
- ASSERT_TRUE(table.addResource(
- ResourceName{ kAndroid, ResourceType::kAttr, u"foo" },
- {}, {}, util::make_unique<TestWeakValue>()));
+ ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:attr/foo"), ConfigDescription{},
+ util::make_unique<Attribute>(true), &mDiagnostics));
- const ResourceTableType* type;
- const ResourceEntry* entry;
- std::tie(type, entry) = table.findResource(
- ResourceNameRef{ kAndroid, ResourceType::kAttr, u"foo" });
- ASSERT_NE(nullptr, type);
- ASSERT_NE(nullptr, entry);
- ASSERT_EQ(entry->values.size(), 1u);
- EXPECT_TRUE(entry->values.front().value->isWeak());
+ Attribute* attr = test::getValue<Attribute>(&table, u"@android:attr/foo");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_TRUE(attr->isWeak());
- ASSERT_TRUE(table.addResource(ResourceName{ kAndroid, ResourceType::kAttr, u"foo" }, {}, {},
- util::make_unique<TestValue>(u"bar")));
+ ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:attr/foo"), ConfigDescription{},
+ util::make_unique<Attribute>(false), &mDiagnostics));
- std::tie(type, entry) = table.findResource(
- ResourceNameRef{ kAndroid, ResourceType::kAttr, u"foo" });
- ASSERT_NE(nullptr, type);
- ASSERT_NE(nullptr, entry);
- ASSERT_EQ(entry->values.size(), 1u);
- EXPECT_FALSE(entry->values.front().value->isWeak());
+ attr = test::getValue<Attribute>(&table, u"@android:attr/foo");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_FALSE(attr->isWeak());
}
} // namespace aapt
diff --git a/tools/aapt2/ResourceTypeExtensions.h b/tools/aapt2/ResourceTypeExtensions.h
deleted file mode 100644
index dcbe923..0000000
--- a/tools/aapt2/ResourceTypeExtensions.h
+++ /dev/null
@@ -1,147 +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_RESOURCE_TYPE_EXTENSIONS_H
-#define AAPT_RESOURCE_TYPE_EXTENSIONS_H
-
-#include <androidfw/ResourceTypes.h>
-
-namespace aapt {
-
-/**
- * New android::ResChunk_header types defined
- * for AAPT to use.
- *
- * TODO(adamlesinski): Consider reserving these
- * enums in androidfw/ResourceTypes.h to avoid
- * future collisions.
- */
-enum {
- RES_TABLE_PUBLIC_TYPE = 0x000d,
-
- /**
- * A chunk that holds the string pool
- * for source entries (path/to/source:line).
- */
- RES_TABLE_SOURCE_POOL_TYPE = 0x000e,
-
- /**
- * A chunk holding names of externally
- * defined symbols and offsets to where
- * they are referenced in the table.
- */
- RES_TABLE_SYMBOL_TABLE_TYPE = 0x000f,
-};
-
-/**
- * New resource types that are meant to only be used
- * by AAPT and will not end up on the device.
- */
-struct ExtendedTypes {
- enum {
- /**
- * A raw string value that hasn't had its escape sequences
- * processed nor whitespace removed.
- */
- TYPE_RAW_STRING = 0xfe
- };
-};
-
-struct Public_header {
- android::ResChunk_header header;
-
- /**
- * The ID of the type this structure refers to.
- */
- uint8_t typeId;
-
- /**
- * Reserved. Must be 0.
- */
- uint8_t res0;
-
- /**
- * Reserved. Must be 0.
- */
- uint16_t res1;
-
- /**
- * Number of public entries.
- */
- uint32_t count;
-};
-
-struct Public_entry {
- uint16_t entryId;
- uint16_t res0;
- android::ResStringPool_ref key;
- android::ResStringPool_ref source;
- uint32_t sourceLine;
-};
-
-/**
- * A chunk with type RES_TABLE_SYMBOL_TABLE_TYPE.
- * Following the header are count number of SymbolTable_entry
- * structures, followed by an android::ResStringPool_header.
- */
-struct SymbolTable_header {
- android::ResChunk_header header;
-
- /**
- * Number of SymbolTable_entry structures following
- * this header.
- */
- uint32_t count;
-};
-
-struct SymbolTable_entry {
- /**
- * Offset from the beginning of the resource table
- * where the symbol entry is referenced.
- */
- uint32_t offset;
-
- /**
- * The index into the string pool where the name of this
- * symbol exists.
- */
- uint32_t stringIndex;
-};
-
-/**
- * A structure representing the source of a resourc entry.
- * Appears after an android::ResTable_entry or android::ResTable_map_entry.
- *
- * TODO(adamlesinski): This causes some issues when runtime code checks
- * the size of an android::ResTable_entry. It assumes it is an
- * android::ResTable_map_entry if the size is bigger than an android::ResTable_entry
- * which may not be true if this structure is present.
- */
-struct ResTable_entry_source {
- /**
- * Index into the source string pool.
- */
- uint32_t pathIndex;
-
- /**
- * Line number this resource was defined on.
- */
- uint32_t line;
-};
-
-} // namespace aapt
-
-#endif // AAPT_RESOURCE_TYPE_EXTENSIONS_H
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
new file mode 100644
index 0000000..d3c3c10
--- /dev/null
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -0,0 +1,505 @@
+/*
+ * 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.
+ */
+
+#include "ResourceUtils.h"
+#include "util/Util.h"
+
+#include <androidfw/ResourceTypes.h>
+#include <sstream>
+
+namespace aapt {
+namespace ResourceUtils {
+
+void extractResourceName(const StringPiece16& str, StringPiece16* outPackage,
+ StringPiece16* outType, StringPiece16* outEntry) {
+ const char16_t* start = str.data();
+ const char16_t* end = start + str.size();
+ const char16_t* current = start;
+ while (current != end) {
+ if (outType->size() == 0 && *current == u'/') {
+ outType->assign(start, current - start);
+ start = current + 1;
+ } else if (outPackage->size() == 0 && *current == u':') {
+ outPackage->assign(start, current - start);
+ start = current + 1;
+ }
+ current++;
+ }
+ outEntry->assign(start, end - start);
+}
+
+bool tryParseReference(const StringPiece16& str, ResourceNameRef* outRef, bool* outCreate,
+ bool* outPrivate) {
+ StringPiece16 trimmedStr(util::trimWhitespace(str));
+ if (trimmedStr.empty()) {
+ return false;
+ }
+
+ bool create = false;
+ bool priv = false;
+ if (trimmedStr.data()[0] == u'@') {
+ size_t offset = 1;
+ if (trimmedStr.data()[1] == u'+') {
+ create = true;
+ offset += 1;
+ } else if (trimmedStr.data()[1] == u'*') {
+ priv = true;
+ offset += 1;
+ }
+ StringPiece16 package;
+ StringPiece16 type;
+ StringPiece16 entry;
+ extractResourceName(trimmedStr.substr(offset, trimmedStr.size() - offset), &package, &type,
+ &entry);
+
+ const ResourceType* parsedType = parseResourceType(type);
+ if (!parsedType) {
+ return false;
+ }
+
+ if (create && *parsedType != ResourceType::kId) {
+ return false;
+ }
+
+ if (outRef != nullptr) {
+ outRef->package = package;
+ outRef->type = *parsedType;
+ outRef->entry = entry;
+ }
+ if (outCreate) {
+ *outCreate = create;
+ }
+ if (outPrivate) {
+ *outPrivate = priv;
+ }
+ return true;
+ }
+ return false;
+}
+
+bool isReference(const StringPiece16& str) {
+ return tryParseReference(str, nullptr, nullptr, nullptr);
+}
+
+bool tryParseAttributeReference(const StringPiece16& str, ResourceNameRef* outRef) {
+ StringPiece16 trimmedStr(util::trimWhitespace(str));
+ if (trimmedStr.empty()) {
+ return false;
+ }
+
+ if (*trimmedStr.data() == u'?') {
+ StringPiece16 package;
+ StringPiece16 type;
+ StringPiece16 entry;
+ extractResourceName(trimmedStr.substr(1, trimmedStr.size() - 1), &package, &type, &entry);
+
+ if (!type.empty() && type != u"attr") {
+ return false;
+ }
+
+ outRef->package = package;
+ outRef->type = ResourceType::kAttr;
+ outRef->entry = entry;
+ return true;
+ }
+ return false;
+}
+
+/*
+ * Style parent's are a bit different. We accept the following formats:
+ *
+ * @[package:]style/<entry>
+ * ?[package:]style/<entry>
+ * <package>:[style/]<entry>
+ * [package:style/]<entry>
+ */
+Maybe<Reference> parseStyleParentReference(const StringPiece16& str, std::string* outError) {
+ if (str.empty()) {
+ return {};
+ }
+
+ StringPiece16 name = str;
+
+ bool hasLeadingIdentifiers = false;
+ bool privateRef = false;
+
+ // Skip over these identifiers. A style's parent is a normal reference.
+ if (name.data()[0] == u'@' || name.data()[0] == u'?') {
+ hasLeadingIdentifiers = true;
+ name = name.substr(1, name.size() - 1);
+ if (name.data()[0] == u'*') {
+ privateRef = true;
+ name = name.substr(1, name.size() - 1);
+ }
+ }
+
+ ResourceNameRef ref;
+ ref.type = ResourceType::kStyle;
+
+ StringPiece16 typeStr;
+ extractResourceName(name, &ref.package, &typeStr, &ref.entry);
+ if (!typeStr.empty()) {
+ // If we have a type, make sure it is a Style.
+ const ResourceType* parsedType = parseResourceType(typeStr);
+ if (!parsedType || *parsedType != ResourceType::kStyle) {
+ std::stringstream err;
+ err << "invalid resource type '" << typeStr << "' for parent of style";
+ *outError = err.str();
+ return {};
+ }
+ } else {
+ // No type was defined, this should not have a leading identifier.
+ if (hasLeadingIdentifiers) {
+ std::stringstream err;
+ err << "invalid parent reference '" << str << "'";
+ *outError = err.str();
+ return {};
+ }
+ }
+
+ if (!hasLeadingIdentifiers && ref.package.empty() && !typeStr.empty()) {
+ std::stringstream err;
+ err << "invalid parent reference '" << str << "'";
+ *outError = err.str();
+ return {};
+ }
+
+ Reference result(ref);
+ result.privateReference = privateRef;
+ return result;
+}
+
+std::unique_ptr<Reference> tryParseReference(const StringPiece16& str, bool* outCreate) {
+ ResourceNameRef ref;
+ bool privateRef = false;
+ if (tryParseReference(str, &ref, outCreate, &privateRef)) {
+ std::unique_ptr<Reference> value = util::make_unique<Reference>(ref);
+ value->privateReference = privateRef;
+ return value;
+ }
+
+ if (tryParseAttributeReference(str, &ref)) {
+ if (outCreate) {
+ *outCreate = false;
+ }
+ return util::make_unique<Reference>(ref, Reference::Type::kAttribute);
+ }
+ return {};
+}
+
+std::unique_ptr<BinaryPrimitive> tryParseNullOrEmpty(const StringPiece16& str) {
+ StringPiece16 trimmedStr(util::trimWhitespace(str));
+ android::Res_value value = { };
+ if (trimmedStr == u"@null") {
+ // TYPE_NULL with data set to 0 is interpreted by the runtime as an error.
+ // Instead we set the data type to TYPE_REFERENCE with a value of 0.
+ value.dataType = android::Res_value::TYPE_REFERENCE;
+ } else if (trimmedStr == u"@empty") {
+ // TYPE_NULL with value of DATA_NULL_EMPTY is handled fine by the runtime.
+ value.dataType = android::Res_value::TYPE_NULL;
+ value.data = android::Res_value::DATA_NULL_EMPTY;
+ } else {
+ return {};
+ }
+ return util::make_unique<BinaryPrimitive>(value);
+}
+
+std::unique_ptr<BinaryPrimitive> tryParseEnumSymbol(const Attribute* enumAttr,
+ const StringPiece16& str) {
+ StringPiece16 trimmedStr(util::trimWhitespace(str));
+ for (const Attribute::Symbol& symbol : enumAttr->symbols) {
+ // Enum symbols are stored as @package:id/symbol resources,
+ // so we need to match against the 'entry' part of the identifier.
+ const ResourceName& enumSymbolResourceName = symbol.symbol.name.value();
+ if (trimmedStr == enumSymbolResourceName.entry) {
+ android::Res_value value = { };
+ value.dataType = android::Res_value::TYPE_INT_DEC;
+ value.data = symbol.value;
+ return util::make_unique<BinaryPrimitive>(value);
+ }
+ }
+ return {};
+}
+
+std::unique_ptr<BinaryPrimitive> tryParseFlagSymbol(const Attribute* flagAttr,
+ const StringPiece16& str) {
+ android::Res_value flags = { };
+ flags.dataType = android::Res_value::TYPE_INT_DEC;
+
+ for (StringPiece16 part : util::tokenize(str, u'|')) {
+ StringPiece16 trimmedPart = util::trimWhitespace(part);
+
+ bool flagSet = false;
+ for (const Attribute::Symbol& symbol : flagAttr->symbols) {
+ // Flag symbols are stored as @package:id/symbol resources,
+ // so we need to match against the 'entry' part of the identifier.
+ const ResourceName& flagSymbolResourceName = symbol.symbol.name.value();
+ if (trimmedPart == flagSymbolResourceName.entry) {
+ flags.data |= symbol.value;
+ flagSet = true;
+ break;
+ }
+ }
+
+ if (!flagSet) {
+ return {};
+ }
+ }
+ return util::make_unique<BinaryPrimitive>(flags);
+}
+
+static uint32_t parseHex(char16_t c, bool* outError) {
+ if (c >= u'0' && c <= u'9') {
+ return c - u'0';
+ } else if (c >= u'a' && c <= u'f') {
+ return c - u'a' + 0xa;
+ } else if (c >= u'A' && c <= u'F') {
+ return c - u'A' + 0xa;
+ } else {
+ *outError = true;
+ return 0xffffffffu;
+ }
+}
+
+std::unique_ptr<BinaryPrimitive> tryParseColor(const StringPiece16& str) {
+ StringPiece16 colorStr(util::trimWhitespace(str));
+ const char16_t* start = colorStr.data();
+ const size_t len = colorStr.size();
+ if (len == 0 || start[0] != u'#') {
+ return {};
+ }
+
+ android::Res_value value = { };
+ bool error = false;
+ if (len == 4) {
+ value.dataType = android::Res_value::TYPE_INT_COLOR_RGB4;
+ value.data = 0xff000000u;
+ value.data |= parseHex(start[1], &error) << 20;
+ value.data |= parseHex(start[1], &error) << 16;
+ value.data |= parseHex(start[2], &error) << 12;
+ value.data |= parseHex(start[2], &error) << 8;
+ value.data |= parseHex(start[3], &error) << 4;
+ value.data |= parseHex(start[3], &error);
+ } else if (len == 5) {
+ value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB4;
+ value.data |= parseHex(start[1], &error) << 28;
+ value.data |= parseHex(start[1], &error) << 24;
+ value.data |= parseHex(start[2], &error) << 20;
+ value.data |= parseHex(start[2], &error) << 16;
+ value.data |= parseHex(start[3], &error) << 12;
+ value.data |= parseHex(start[3], &error) << 8;
+ value.data |= parseHex(start[4], &error) << 4;
+ value.data |= parseHex(start[4], &error);
+ } else if (len == 7) {
+ value.dataType = android::Res_value::TYPE_INT_COLOR_RGB8;
+ value.data = 0xff000000u;
+ value.data |= parseHex(start[1], &error) << 20;
+ value.data |= parseHex(start[2], &error) << 16;
+ value.data |= parseHex(start[3], &error) << 12;
+ value.data |= parseHex(start[4], &error) << 8;
+ value.data |= parseHex(start[5], &error) << 4;
+ value.data |= parseHex(start[6], &error);
+ } else if (len == 9) {
+ value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB8;
+ value.data |= parseHex(start[1], &error) << 28;
+ value.data |= parseHex(start[2], &error) << 24;
+ value.data |= parseHex(start[3], &error) << 20;
+ value.data |= parseHex(start[4], &error) << 16;
+ value.data |= parseHex(start[5], &error) << 12;
+ value.data |= parseHex(start[6], &error) << 8;
+ value.data |= parseHex(start[7], &error) << 4;
+ value.data |= parseHex(start[8], &error);
+ } else {
+ return {};
+ }
+ return error ? std::unique_ptr<BinaryPrimitive>() : util::make_unique<BinaryPrimitive>(value);
+}
+
+bool tryParseBool(const StringPiece16& str, bool* outValue) {
+ StringPiece16 trimmedStr(util::trimWhitespace(str));
+ if (trimmedStr == u"true" || trimmedStr == u"TRUE") {
+ if (outValue) {
+ *outValue = true;
+ }
+ return true;
+ } else if (trimmedStr == u"false" || trimmedStr == u"FALSE") {
+ if (outValue) {
+ *outValue = false;
+ }
+ return true;
+ }
+ return false;
+}
+
+std::unique_ptr<BinaryPrimitive> tryParseBool(const StringPiece16& str) {
+ bool result = false;
+ if (tryParseBool(str, &result)) {
+ android::Res_value value = {};
+ value.dataType = android::Res_value::TYPE_INT_BOOLEAN;
+
+ if (result) {
+ value.data = 0xffffffffu;
+ } else {
+ value.data = 0;
+ }
+ return util::make_unique<BinaryPrimitive>(value);
+ }
+ return {};
+}
+
+std::unique_ptr<BinaryPrimitive> tryParseInt(const StringPiece16& str) {
+ android::Res_value value;
+ if (!android::ResTable::stringToInt(str.data(), str.size(), &value)) {
+ return {};
+ }
+ return util::make_unique<BinaryPrimitive>(value);
+}
+
+std::unique_ptr<BinaryPrimitive> tryParseFloat(const StringPiece16& str) {
+ android::Res_value value;
+ if (!android::ResTable::stringToFloat(str.data(), str.size(), &value)) {
+ return {};
+ }
+ return util::make_unique<BinaryPrimitive>(value);
+}
+
+uint32_t androidTypeToAttributeTypeMask(uint16_t type) {
+ switch (type) {
+ case android::Res_value::TYPE_NULL:
+ case android::Res_value::TYPE_REFERENCE:
+ case android::Res_value::TYPE_ATTRIBUTE:
+ case android::Res_value::TYPE_DYNAMIC_REFERENCE:
+ return android::ResTable_map::TYPE_REFERENCE;
+
+ case android::Res_value::TYPE_STRING:
+ return android::ResTable_map::TYPE_STRING;
+
+ case android::Res_value::TYPE_FLOAT:
+ return android::ResTable_map::TYPE_FLOAT;
+
+ case android::Res_value::TYPE_DIMENSION:
+ return android::ResTable_map::TYPE_DIMENSION;
+
+ case android::Res_value::TYPE_FRACTION:
+ return android::ResTable_map::TYPE_FRACTION;
+
+ case android::Res_value::TYPE_INT_DEC:
+ case android::Res_value::TYPE_INT_HEX:
+ return android::ResTable_map::TYPE_INTEGER | android::ResTable_map::TYPE_ENUM
+ | android::ResTable_map::TYPE_FLAGS;
+
+ case android::Res_value::TYPE_INT_BOOLEAN:
+ return android::ResTable_map::TYPE_BOOLEAN;
+
+ case android::Res_value::TYPE_INT_COLOR_ARGB8:
+ case android::Res_value::TYPE_INT_COLOR_RGB8:
+ case android::Res_value::TYPE_INT_COLOR_ARGB4:
+ case android::Res_value::TYPE_INT_COLOR_RGB4:
+ return android::ResTable_map::TYPE_COLOR;
+
+ default:
+ return 0;
+ };
+}
+
+std::unique_ptr<Item> parseItemForAttribute(
+ const StringPiece16& value, uint32_t typeMask,
+ std::function<void(const ResourceName&)> onCreateReference) {
+ std::unique_ptr<BinaryPrimitive> nullOrEmpty = tryParseNullOrEmpty(value);
+ if (nullOrEmpty) {
+ return std::move(nullOrEmpty);
+ }
+
+ bool create = false;
+ std::unique_ptr<Reference> reference = tryParseReference(value, &create);
+ if (reference) {
+ if (create && onCreateReference) {
+ onCreateReference(reference->name.value());
+ }
+ return std::move(reference);
+ }
+
+ if (typeMask & android::ResTable_map::TYPE_COLOR) {
+ // Try parsing this as a color.
+ std::unique_ptr<BinaryPrimitive> color = tryParseColor(value);
+ if (color) {
+ return std::move(color);
+ }
+ }
+
+ if (typeMask & android::ResTable_map::TYPE_BOOLEAN) {
+ // Try parsing this as a boolean.
+ std::unique_ptr<BinaryPrimitive> boolean = tryParseBool(value);
+ if (boolean) {
+ return std::move(boolean);
+ }
+ }
+
+ if (typeMask & android::ResTable_map::TYPE_INTEGER) {
+ // Try parsing this as an integer.
+ std::unique_ptr<BinaryPrimitive> integer = tryParseInt(value);
+ if (integer) {
+ return std::move(integer);
+ }
+ }
+
+ const uint32_t floatMask = android::ResTable_map::TYPE_FLOAT
+ | android::ResTable_map::TYPE_DIMENSION | android::ResTable_map::TYPE_FRACTION;
+ if (typeMask & floatMask) {
+ // Try parsing this as a float.
+ std::unique_ptr<BinaryPrimitive> floatingPoint = tryParseFloat(value);
+ if (floatingPoint) {
+ if (typeMask & androidTypeToAttributeTypeMask(floatingPoint->value.dataType)) {
+ return std::move(floatingPoint);
+ }
+ }
+ }
+ return {};
+}
+
+/**
+ * We successively try to parse the string as a resource type that the Attribute
+ * allows.
+ */
+std::unique_ptr<Item> parseItemForAttribute(
+ const StringPiece16& str, const Attribute* attr,
+ std::function<void(const ResourceName&)> onCreateReference) {
+ const uint32_t typeMask = attr->typeMask;
+ std::unique_ptr<Item> value = parseItemForAttribute(str, typeMask, onCreateReference);
+ if (value) {
+ return value;
+ }
+
+ if (typeMask & android::ResTable_map::TYPE_ENUM) {
+ // Try parsing this as an enum.
+ std::unique_ptr<BinaryPrimitive> enumValue = tryParseEnumSymbol(attr, str);
+ if (enumValue) {
+ return std::move(enumValue);
+ }
+ }
+
+ if (typeMask & android::ResTable_map::TYPE_FLAGS) {
+ // Try parsing this as a flag.
+ std::unique_ptr<BinaryPrimitive> flagValue = tryParseFlagSymbol(attr, str);
+ if (flagValue) {
+ return std::move(flagValue);
+ }
+ }
+ return {};
+}
+
+} // namespace ResourceUtils
+} // namespace aapt
diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h
new file mode 100644
index 0000000..851edc8
--- /dev/null
+++ b/tools/aapt2/ResourceUtils.h
@@ -0,0 +1,147 @@
+/*
+ * 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_RESOURCEUTILS_H
+#define AAPT_RESOURCEUTILS_H
+
+#include "Resource.h"
+#include "ResourceValues.h"
+#include "util/StringPiece.h"
+
+#include <functional>
+#include <memory>
+
+namespace aapt {
+namespace ResourceUtils {
+
+/*
+ * Extracts the package, type, and name from a string of the format:
+ *
+ * [package:]type/name
+ *
+ * where the package can be empty. Validation must be performed on each
+ * individual extracted piece to verify that the pieces are valid.
+ */
+void extractResourceName(const StringPiece16& str, StringPiece16* outPackage,
+ StringPiece16* outType, StringPiece16* outEntry);
+
+/*
+ * Returns true if the string was parsed as a reference (@[+][package:]type/name), with
+ * `outReference` set to the parsed reference.
+ *
+ * If '+' was present in the reference, `outCreate` is set to true.
+ * If '*' was present in the reference, `outPrivate` is set to true.
+ */
+bool tryParseReference(const StringPiece16& str, ResourceNameRef* outReference,
+ bool* outCreate = nullptr, bool* outPrivate = nullptr);
+
+/*
+ * Returns true if the string is in the form of a resource reference (@[+][package:]type/name).
+ */
+bool isReference(const StringPiece16& str);
+
+/*
+ * Returns true if the string was parsed as an attribute reference (?[package:]type/name),
+ * with `outReference` set to the parsed reference.
+ */
+bool tryParseAttributeReference(const StringPiece16& str, ResourceNameRef* outReference);
+
+/**
+ * Returns true if the value is a boolean, putting the result in `outValue`.
+ */
+bool tryParseBool(const StringPiece16& str, bool* outValue);
+
+/*
+ * Returns a Reference, or None Maybe instance if the string `str` was parsed as a
+ * valid reference to a style.
+ * The format for a style parent is slightly more flexible than a normal reference:
+ *
+ * @[package:]style/<entry> or
+ * ?[package:]style/<entry> or
+ * <package>:[style/]<entry>
+ */
+Maybe<Reference> parseStyleParentReference(const StringPiece16& str, std::string* outError);
+
+/*
+ * Returns a Reference object if the string was parsed as a resource or attribute reference,
+ * ( @[+][package:]type/name | ?[package:]type/name ) setting outCreate to true if
+ * the '+' was present in the string.
+ */
+std::unique_ptr<Reference> tryParseReference(const StringPiece16& str, bool* outCreate = nullptr);
+
+/*
+ * Returns a BinaryPrimitve object representing @null or @empty if the string was parsed
+ * as one.
+ */
+std::unique_ptr<BinaryPrimitive> tryParseNullOrEmpty(const StringPiece16& str);
+
+/*
+ * Returns a BinaryPrimitve object representing a color if the string was parsed
+ * as one.
+ */
+std::unique_ptr<BinaryPrimitive> tryParseColor(const StringPiece16& str);
+
+/*
+ * Returns a BinaryPrimitve object representing a boolean if the string was parsed
+ * as one.
+ */
+std::unique_ptr<BinaryPrimitive> tryParseBool(const StringPiece16& str);
+
+/*
+ * Returns a BinaryPrimitve object representing an integer if the string was parsed
+ * as one.
+ */
+std::unique_ptr<BinaryPrimitive> tryParseInt(const StringPiece16& str);
+
+/*
+ * Returns a BinaryPrimitve object representing a floating point number
+ * (float, dimension, etc) if the string was parsed as one.
+ */
+std::unique_ptr<BinaryPrimitive> tryParseFloat(const StringPiece16& str);
+
+/*
+ * Returns a BinaryPrimitve object representing an enum symbol if the string was parsed
+ * as one.
+ */
+std::unique_ptr<BinaryPrimitive> tryParseEnumSymbol(const Attribute* enumAttr,
+ const StringPiece16& str);
+
+/*
+ * Returns a BinaryPrimitve object representing a flag symbol if the string was parsed
+ * as one.
+ */
+std::unique_ptr<BinaryPrimitive> tryParseFlagSymbol(const Attribute* enumAttr,
+ const StringPiece16& str);
+/*
+ * Try to convert a string to an Item for the given attribute. The attribute will
+ * restrict what values the string can be converted to.
+ * The callback function onCreateReference is called when the parsed item is a
+ * reference to an ID that must be created (@+id/foo).
+ */
+std::unique_ptr<Item> parseItemForAttribute(
+ const StringPiece16& value, const Attribute* attr,
+ std::function<void(const ResourceName&)> onCreateReference = {});
+
+std::unique_ptr<Item> parseItemForAttribute(
+ const StringPiece16& value, uint32_t typeMask,
+ std::function<void(const ResourceName&)> onCreateReference = {});
+
+uint32_t androidTypeToAttributeTypeMask(uint16_t type);
+
+} // namespace ResourceUtils
+} // namespace aapt
+
+#endif /* AAPT_RESOURCEUTILS_H */
diff --git a/tools/aapt2/ResourceUtils_test.cpp b/tools/aapt2/ResourceUtils_test.cpp
new file mode 100644
index 0000000..7de8f41
--- /dev/null
+++ b/tools/aapt2/ResourceUtils_test.cpp
@@ -0,0 +1,127 @@
+/*
+ * 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.
+ */
+
+#include "Resource.h"
+#include "ResourceUtils.h"
+
+#include "test/Common.h"
+
+#include <gtest/gtest.h>
+
+namespace aapt {
+
+TEST(ResourceUtilsTest, ParseReferenceWithNoPackage) {
+ ResourceNameRef expected = { {}, ResourceType::kColor, u"foo" };
+ ResourceNameRef actual;
+ bool create = false;
+ bool privateRef = false;
+ EXPECT_TRUE(ResourceUtils::tryParseReference(u"@color/foo", &actual, &create, &privateRef));
+ EXPECT_EQ(expected, actual);
+ EXPECT_FALSE(create);
+ EXPECT_FALSE(privateRef);
+}
+
+TEST(ResourceUtilsTest, ParseReferenceWithPackage) {
+ ResourceNameRef expected = { u"android", ResourceType::kColor, u"foo" };
+ ResourceNameRef actual;
+ bool create = false;
+ bool privateRef = false;
+ EXPECT_TRUE(ResourceUtils::tryParseReference(u"@android:color/foo", &actual, &create,
+ &privateRef));
+ EXPECT_EQ(expected, actual);
+ EXPECT_FALSE(create);
+ EXPECT_FALSE(privateRef);
+}
+
+TEST(ResourceUtilsTest, ParseReferenceWithSurroundingWhitespace) {
+ ResourceNameRef expected = { u"android", ResourceType::kColor, u"foo" };
+ ResourceNameRef actual;
+ bool create = false;
+ bool privateRef = false;
+ EXPECT_TRUE(ResourceUtils::tryParseReference(u"\t @android:color/foo\n \n\t", &actual,
+ &create, &privateRef));
+ EXPECT_EQ(expected, actual);
+ EXPECT_FALSE(create);
+ EXPECT_FALSE(privateRef);
+}
+
+TEST(ResourceUtilsTest, ParseAutoCreateIdReference) {
+ ResourceNameRef expected = { u"android", ResourceType::kId, u"foo" };
+ ResourceNameRef actual;
+ bool create = false;
+ bool privateRef = false;
+ EXPECT_TRUE(ResourceUtils::tryParseReference(u"@+android:id/foo", &actual, &create,
+ &privateRef));
+ EXPECT_EQ(expected, actual);
+ EXPECT_TRUE(create);
+ EXPECT_FALSE(privateRef);
+}
+
+TEST(ResourceUtilsTest, ParsePrivateReference) {
+ ResourceNameRef expected = { u"android", ResourceType::kId, u"foo" };
+ ResourceNameRef actual;
+ bool create = false;
+ bool privateRef = false;
+ EXPECT_TRUE(ResourceUtils::tryParseReference(u"@*android:id/foo", &actual, &create,
+ &privateRef));
+ EXPECT_EQ(expected, actual);
+ EXPECT_FALSE(create);
+ EXPECT_TRUE(privateRef);
+}
+
+TEST(ResourceUtilsTest, FailToParseAutoCreateNonIdReference) {
+ bool create = false;
+ bool privateRef = false;
+ ResourceNameRef actual;
+ EXPECT_FALSE(ResourceUtils::tryParseReference(u"@+android:color/foo", &actual, &create,
+ &privateRef));
+}
+
+TEST(ResourceUtilsTest, ParseStyleParentReference) {
+ const ResourceName kAndroidStyleFooName = { u"android", ResourceType::kStyle, u"foo" };
+ const ResourceName kStyleFooName = { {}, ResourceType::kStyle, u"foo" };
+
+ std::string errStr;
+ Maybe<Reference> ref = ResourceUtils::parseStyleParentReference(u"@android:style/foo", &errStr);
+ AAPT_ASSERT_TRUE(ref);
+ EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
+
+ ref = ResourceUtils::parseStyleParentReference(u"@style/foo", &errStr);
+ AAPT_ASSERT_TRUE(ref);
+ EXPECT_EQ(ref.value().name.value(), kStyleFooName);
+
+ ref = ResourceUtils::parseStyleParentReference(u"?android:style/foo", &errStr);
+ AAPT_ASSERT_TRUE(ref);
+ EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
+
+ ref = ResourceUtils::parseStyleParentReference(u"?style/foo", &errStr);
+ AAPT_ASSERT_TRUE(ref);
+ EXPECT_EQ(ref.value().name.value(), kStyleFooName);
+
+ ref = ResourceUtils::parseStyleParentReference(u"android:style/foo", &errStr);
+ AAPT_ASSERT_TRUE(ref);
+ EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
+
+ ref = ResourceUtils::parseStyleParentReference(u"android:foo", &errStr);
+ AAPT_ASSERT_TRUE(ref);
+ EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
+
+ ref = ResourceUtils::parseStyleParentReference(u"foo", &errStr);
+ AAPT_ASSERT_TRUE(ref);
+ EXPECT_EQ(ref.value().name.value(), kStyleFooName);
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index aabb375..8acff0d 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -15,42 +15,49 @@
*/
#include "Resource.h"
-#include "ResourceTypeExtensions.h"
#include "ResourceValues.h"
-#include "Util.h"
+#include "ValueVisitor.h"
+
+#include "util/Util.h"
+#include "flatten/ResourceTypeExtensions.h"
#include <androidfw/ResourceTypes.h>
#include <limits>
namespace aapt {
-bool Value::isItem() const {
- return false;
+template <typename Derived>
+void BaseValue<Derived>::accept(RawValueVisitor* visitor) {
+ visitor->visit(static_cast<Derived*>(this));
+}
+
+template <typename Derived>
+void BaseItem<Derived>::accept(RawValueVisitor* visitor) {
+ visitor->visit(static_cast<Derived*>(this));
}
bool Value::isWeak() const {
return false;
}
-bool Item::isItem() const {
- return true;
-}
-
RawString::RawString(const StringPool::Ref& ref) : value(ref) {
}
RawString* RawString::clone(StringPool* newPool) const {
- return new RawString(newPool->makeRef(*value));
+ RawString* rs = new RawString(newPool->makeRef(*value));
+ rs->mComment = mComment;
+ rs->mSource = mSource;
+ return rs;
}
-bool RawString::flatten(android::Res_value& outValue) const {
- outValue.dataType = ExtendedTypes::TYPE_RAW_STRING;
- outValue.data = static_cast<uint32_t>(value.getIndex());
+bool RawString::flatten(android::Res_value* outValue) const {
+ outValue->dataType = ExtendedTypes::TYPE_RAW_STRING;
+ outValue->data = util::hostToDevice32(static_cast<uint32_t>(value.getIndex()));
return true;
}
-void RawString::print(std::ostream& out) const {
- out << "(raw string) " << *value;
+void RawString::print(std::ostream* out) const {
+ *out << "(raw string) " << *value;
}
Reference::Reference() : referenceType(Reference::Type::kResource) {
@@ -63,36 +70,38 @@
Reference::Reference(const ResourceId& i, Type type) : id(i), referenceType(type) {
}
-bool Reference::flatten(android::Res_value& outValue) const {
- outValue.dataType = (referenceType == Reference::Type::kResource)
+bool Reference::flatten(android::Res_value* outValue) const {
+ outValue->dataType = (referenceType == Reference::Type::kResource)
? android::Res_value::TYPE_REFERENCE
: android::Res_value::TYPE_ATTRIBUTE;
- outValue.data = id.id;
+ outValue->data = util::hostToDevice32(id ? id.value().id : 0);
return true;
}
Reference* Reference::clone(StringPool* /*newPool*/) const {
Reference* ref = new Reference();
+ ref->mComment = mComment;
+ ref->mSource = mSource;
ref->referenceType = referenceType;
ref->name = name;
ref->id = id;
return ref;
}
-void Reference::print(std::ostream& out) const {
- out << "(reference) ";
+void Reference::print(std::ostream* out) const {
+ *out << "(reference) ";
if (referenceType == Reference::Type::kResource) {
- out << "@";
+ *out << "@";
} else {
- out << "?";
+ *out << "?";
}
- if (name.isValid()) {
- out << name;
+ if (name) {
+ *out << name.value();
}
- if (id.isValid() || Res_INTERNALID(id.id)) {
- out << " " << id;
+ if (id && !Res_INTERNALID(id.value().id)) {
+ *out << " " << id.value();
}
}
@@ -100,119 +109,140 @@
return true;
}
-bool Id::flatten(android::Res_value& out) const {
- out.dataType = android::Res_value::TYPE_INT_BOOLEAN;
- out.data = 0;
+bool Id::flatten(android::Res_value* out) const {
+ out->dataType = android::Res_value::TYPE_INT_BOOLEAN;
+ out->data = util::hostToDevice32(0);
return true;
}
Id* Id::clone(StringPool* /*newPool*/) const {
- return new Id();
+ Id* id = new Id();
+ id->mComment = mComment;
+ id->mSource = mSource;
+ return id;
}
-void Id::print(std::ostream& out) const {
- out << "(id)";
+void Id::print(std::ostream* out) const {
+ *out << "(id)";
}
String::String(const StringPool::Ref& ref) : value(ref) {
}
-bool String::flatten(android::Res_value& outValue) const {
- // Verify that our StringPool index is within encodeable limits.
+bool String::flatten(android::Res_value* outValue) const {
+ // Verify that our StringPool index is within encode-able limits.
if (value.getIndex() > std::numeric_limits<uint32_t>::max()) {
return false;
}
- outValue.dataType = android::Res_value::TYPE_STRING;
- outValue.data = static_cast<uint32_t>(value.getIndex());
+ outValue->dataType = android::Res_value::TYPE_STRING;
+ outValue->data = util::hostToDevice32(static_cast<uint32_t>(value.getIndex()));
return true;
}
String* String::clone(StringPool* newPool) const {
- return new String(newPool->makeRef(*value));
+ String* str = new String(newPool->makeRef(*value));
+ str->mComment = mComment;
+ str->mSource = mSource;
+ return str;
}
-void String::print(std::ostream& out) const {
- out << "(string) \"" << *value << "\"";
+void String::print(std::ostream* out) const {
+ *out << "(string) \"" << *value << "\"";
}
StyledString::StyledString(const StringPool::StyleRef& ref) : value(ref) {
}
-bool StyledString::flatten(android::Res_value& outValue) const {
+bool StyledString::flatten(android::Res_value* outValue) const {
if (value.getIndex() > std::numeric_limits<uint32_t>::max()) {
return false;
}
- outValue.dataType = android::Res_value::TYPE_STRING;
- outValue.data = static_cast<uint32_t>(value.getIndex());
+ outValue->dataType = android::Res_value::TYPE_STRING;
+ outValue->data = util::hostToDevice32(static_cast<uint32_t>(value.getIndex()));
return true;
}
StyledString* StyledString::clone(StringPool* newPool) const {
- return new StyledString(newPool->makeRef(value));
+ StyledString* str = new StyledString(newPool->makeRef(value));
+ str->mComment = mComment;
+ str->mSource = mSource;
+ return str;
}
-void StyledString::print(std::ostream& out) const {
- out << "(styled string) \"" << *value->str << "\"";
+void StyledString::print(std::ostream* out) const {
+ *out << "(styled string) \"" << *value->str << "\"";
}
FileReference::FileReference(const StringPool::Ref& _path) : path(_path) {
}
-bool FileReference::flatten(android::Res_value& outValue) const {
+bool FileReference::flatten(android::Res_value* outValue) const {
if (path.getIndex() > std::numeric_limits<uint32_t>::max()) {
return false;
}
- outValue.dataType = android::Res_value::TYPE_STRING;
- outValue.data = static_cast<uint32_t>(path.getIndex());
+ outValue->dataType = android::Res_value::TYPE_STRING;
+ outValue->data = util::hostToDevice32(static_cast<uint32_t>(path.getIndex()));
return true;
}
FileReference* FileReference::clone(StringPool* newPool) const {
- return new FileReference(newPool->makeRef(*path));
+ FileReference* fr = new FileReference(newPool->makeRef(*path));
+ fr->mComment = mComment;
+ fr->mSource = mSource;
+ return fr;
}
-void FileReference::print(std::ostream& out) const {
- out << "(file) " << *path;
+void FileReference::print(std::ostream* out) const {
+ *out << "(file) " << *path;
}
BinaryPrimitive::BinaryPrimitive(const android::Res_value& val) : value(val) {
}
-bool BinaryPrimitive::flatten(android::Res_value& outValue) const {
- outValue = value;
+BinaryPrimitive::BinaryPrimitive(uint8_t dataType, uint32_t data) {
+ value.dataType = dataType;
+ value.data = data;
+}
+
+bool BinaryPrimitive::flatten(android::Res_value* outValue) const {
+ outValue->dataType = value.dataType;
+ outValue->data = util::hostToDevice32(value.data);
return true;
}
BinaryPrimitive* BinaryPrimitive::clone(StringPool* /*newPool*/) const {
- return new BinaryPrimitive(value);
+ BinaryPrimitive* bp = new BinaryPrimitive(value);
+ bp->mComment = mComment;
+ bp->mSource = mSource;
+ return bp;
}
-void BinaryPrimitive::print(std::ostream& out) const {
+void BinaryPrimitive::print(std::ostream* out) const {
switch (value.dataType) {
case android::Res_value::TYPE_NULL:
- out << "(null)";
+ *out << "(null)";
break;
case android::Res_value::TYPE_INT_DEC:
- out << "(integer) " << value.data;
+ *out << "(integer) " << value.data;
break;
case android::Res_value::TYPE_INT_HEX:
- out << "(integer) " << std::hex << value.data << std::dec;
+ *out << "(integer) " << std::hex << value.data << std::dec;
break;
case android::Res_value::TYPE_INT_BOOLEAN:
- out << "(boolean) " << (value.data != 0 ? "true" : "false");
+ *out << "(boolean) " << (value.data != 0 ? "true" : "false");
break;
case android::Res_value::TYPE_INT_COLOR_ARGB8:
case android::Res_value::TYPE_INT_COLOR_RGB8:
case android::Res_value::TYPE_INT_COLOR_ARGB4:
case android::Res_value::TYPE_INT_COLOR_RGB4:
- out << "(color) #" << std::hex << value.data << std::dec;
+ *out << "(color) #" << std::hex << value.data << std::dec;
break;
default:
- out << "(unknown 0x" << std::hex << (int) value.dataType << ") 0x"
- << std::hex << value.data << std::dec;
+ *out << "(unknown 0x" << std::hex << (int) value.dataType << ") 0x"
+ << std::hex << value.data << std::dec;
break;
}
}
@@ -226,14 +256,16 @@
Attribute* Attribute::clone(StringPool* /*newPool*/) const {
Attribute* attr = new Attribute(weak);
+ attr->mComment = mComment;
+ attr->mSource = mSource;
attr->typeMask = typeMask;
std::copy(symbols.begin(), symbols.end(), std::back_inserter(attr->symbols));
return attr;
}
-void Attribute::printMask(std::ostream& out) const {
+void Attribute::printMask(std::ostream* out) const {
if (typeMask == android::ResTable_map::TYPE_ANY) {
- out << "any";
+ *out << "any";
return;
}
@@ -242,103 +274,105 @@
if (!set) {
set = true;
} else {
- out << "|";
+ *out << "|";
}
- out << "reference";
+ *out << "reference";
}
if ((typeMask & android::ResTable_map::TYPE_STRING) != 0) {
if (!set) {
set = true;
} else {
- out << "|";
+ *out << "|";
}
- out << "string";
+ *out << "string";
}
if ((typeMask & android::ResTable_map::TYPE_INTEGER) != 0) {
if (!set) {
set = true;
} else {
- out << "|";
+ *out << "|";
}
- out << "integer";
+ *out << "integer";
}
if ((typeMask & android::ResTable_map::TYPE_BOOLEAN) != 0) {
if (!set) {
set = true;
} else {
- out << "|";
+ *out << "|";
}
- out << "boolean";
+ *out << "boolean";
}
if ((typeMask & android::ResTable_map::TYPE_COLOR) != 0) {
if (!set) {
set = true;
} else {
- out << "|";
+ *out << "|";
}
- out << "color";
+ *out << "color";
}
if ((typeMask & android::ResTable_map::TYPE_FLOAT) != 0) {
if (!set) {
set = true;
} else {
- out << "|";
+ *out << "|";
}
- out << "float";
+ *out << "float";
}
if ((typeMask & android::ResTable_map::TYPE_DIMENSION) != 0) {
if (!set) {
set = true;
} else {
- out << "|";
+ *out << "|";
}
- out << "dimension";
+ *out << "dimension";
}
if ((typeMask & android::ResTable_map::TYPE_FRACTION) != 0) {
if (!set) {
set = true;
} else {
- out << "|";
+ *out << "|";
}
- out << "fraction";
+ *out << "fraction";
}
if ((typeMask & android::ResTable_map::TYPE_ENUM) != 0) {
if (!set) {
set = true;
} else {
- out << "|";
+ *out << "|";
}
- out << "enum";
+ *out << "enum";
}
if ((typeMask & android::ResTable_map::TYPE_FLAGS) != 0) {
if (!set) {
set = true;
} else {
- out << "|";
+ *out << "|";
}
- out << "flags";
+ *out << "flags";
}
}
-void Attribute::print(std::ostream& out) const {
- out << "(attr) ";
+void Attribute::print(std::ostream* out) const {
+ *out << "(attr) ";
printMask(out);
- out << " ["
- << util::joiner(symbols.begin(), symbols.end(), ", ")
- << "]";
+ if (!symbols.empty()) {
+ *out << " ["
+ << util::joiner(symbols.begin(), symbols.end(), ", ")
+ << "]";
+ }
if (weak) {
- out << " [weak]";
+ *out << " [weak]";
}
}
@@ -346,6 +380,8 @@
Style* style = new Style();
style->parent = parent;
style->parentInferred = parentInferred;
+ style->mComment = mComment;
+ style->mSource = mSource;
for (auto& entry : entries) {
style->entries.push_back(Entry{
entry.key,
@@ -355,38 +391,47 @@
return style;
}
-void Style::print(std::ostream& out) const {
- out << "(style) ";
- if (!parent.name.entry.empty()) {
- out << parent.name;
+void Style::print(std::ostream* out) const {
+ *out << "(style) ";
+ if (parent && parent.value().name) {
+ *out << parent.value().name.value();
}
- out << " ["
+ *out << " ["
<< util::joiner(entries.begin(), entries.end(), ", ")
<< "]";
}
static ::std::ostream& operator<<(::std::ostream& out, const Style::Entry& value) {
- out << value.key.name << " = ";
- value.value->print(out);
+ if (value.key.name) {
+ out << value.key.name.value();
+ } else {
+ out << "???";
+ }
+ out << " = ";
+ value.value->print(&out);
return out;
}
Array* Array::clone(StringPool* newPool) const {
Array* array = new Array();
+ array->mComment = mComment;
+ array->mSource = mSource;
for (auto& item : items) {
array->items.emplace_back(std::unique_ptr<Item>(item->clone(newPool)));
}
return array;
}
-void Array::print(std::ostream& out) const {
- out << "(array) ["
+void Array::print(std::ostream* out) const {
+ *out << "(array) ["
<< util::joiner(items.begin(), items.end(), ", ")
<< "]";
}
Plural* Plural::clone(StringPool* newPool) const {
Plural* p = new Plural();
+ p->mComment = mComment;
+ p->mSource = mSource;
const size_t count = values.size();
for (size_t i = 0; i < count; i++) {
if (values[i]) {
@@ -396,8 +441,8 @@
return p;
}
-void Plural::print(std::ostream& out) const {
- out << "(plural)";
+void Plural::print(std::ostream* out) const {
+ *out << "(plural)";
}
static ::std::ostream& operator<<(::std::ostream& out, const std::unique_ptr<Item>& item) {
@@ -406,12 +451,14 @@
Styleable* Styleable::clone(StringPool* /*newPool*/) const {
Styleable* styleable = new Styleable();
+ styleable->mComment = mComment;
+ styleable->mSource = mSource;
std::copy(entries.begin(), entries.end(), std::back_inserter(styleable->entries));
return styleable;
}
-void Styleable::print(std::ostream& out) const {
- out << "(styleable) " << " ["
+void Styleable::print(std::ostream* out) const {
+ *out << "(styleable) " << " ["
<< util::joiner(entries.begin(), entries.end(), ", ")
<< "]";
}
diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h
index ef6594e..7ae346a 100644
--- a/tools/aapt2/ResourceValues.h
+++ b/tools/aapt2/ResourceValues.h
@@ -17,6 +17,7 @@
#ifndef AAPT_RESOURCE_VALUES_H
#define AAPT_RESOURCE_VALUES_H
+#include "util/Maybe.h"
#include "Resource.h"
#include "StringPool.h"
@@ -27,9 +28,7 @@
namespace aapt {
-struct ValueVisitor;
-struct ConstValueVisitor;
-struct ValueVisitorArgs;
+struct RawValueVisitor;
/**
* A resource value. This is an all-encompassing representation
@@ -39,26 +38,48 @@
* but it is the simplest strategy.
*/
struct Value {
- /**
- * Whether or not this is an Item.
- */
- virtual bool isItem() const;
+ virtual ~Value() = default;
/**
- * Whether this value is weak and can be overriden without
+ * Whether this value is weak and can be overridden without
* warning or error. Default for base class is false.
*/
virtual bool isWeak() const;
/**
- * Calls the appropriate overload of ValueVisitor.
+ * Returns the source where this value was defined.
*/
- virtual void accept(ValueVisitor& visitor, ValueVisitorArgs&& args) = 0;
+ const Source& getSource() const {
+ return mSource;
+ }
+
+ void setSource(const Source& source) {
+ mSource = source;
+ }
+
+ void setSource(Source&& source) {
+ mSource = std::move(source);
+ }
/**
- * Const version of accept().
+ * Returns the comment that was associated with this resource.
*/
- virtual void accept(ConstValueVisitor& visitor, ValueVisitorArgs&& args) const = 0;
+ StringPiece16 getComment() const {
+ return mComment;
+ }
+
+ void setComment(const StringPiece16& str) {
+ mComment = str.toString();
+ }
+
+ void setComment(std::u16string&& str) {
+ mComment = std::move(str);
+ }
+
+ /**
+ * Calls the appropriate overload of ValueVisitor.
+ */
+ virtual void accept(RawValueVisitor* visitor) = 0;
/**
* Clone the value.
@@ -68,7 +89,11 @@
/**
* Human readable printout of this value.
*/
- virtual void print(std::ostream& out) const = 0;
+ virtual void print(std::ostream* out) const = 0;
+
+protected:
+ Source mSource;
+ std::u16string mComment;
};
/**
@@ -76,8 +101,7 @@
*/
template <typename Derived>
struct BaseValue : public Value {
- virtual void accept(ValueVisitor& visitor, ValueVisitorArgs&& args) override;
- virtual void accept(ConstValueVisitor& visitor, ValueVisitorArgs&& args) const override;
+ void accept(RawValueVisitor* visitor) override;
};
/**
@@ -85,20 +109,15 @@
*/
struct Item : public Value {
/**
- * An Item is, of course, an Item.
- */
- virtual bool isItem() const override;
-
- /**
* Clone the Item.
*/
virtual Item* clone(StringPool* newPool) const override = 0;
/**
* Fills in an android::Res_value structure with this Item's binary representation.
- * Returns false if an error ocurred.
+ * Returns false if an error occurred.
*/
- virtual bool flatten(android::Res_value& outValue) const = 0;
+ virtual bool flatten(android::Res_value* outValue) const = 0;
};
/**
@@ -106,8 +125,7 @@
*/
template <typename Derived>
struct BaseItem : public Item {
- virtual void accept(ValueVisitor& visitor, ValueVisitorArgs&& args) override;
- virtual void accept(ConstValueVisitor& visitor, ValueVisitorArgs&& args) const override;
+ void accept(RawValueVisitor* visitor) override;
};
/**
@@ -122,8 +140,8 @@
kAttribute,
};
- ResourceName name;
- ResourceId id;
+ Maybe<ResourceName> name;
+ Maybe<ResourceId> id;
Reference::Type referenceType;
bool privateReference = false;
@@ -131,9 +149,9 @@
Reference(const ResourceNameRef& n, Type type = Type::kResource);
Reference(const ResourceId& i, Type type = Type::kResource);
- bool flatten(android::Res_value& outValue) const override;
+ bool flatten(android::Res_value* outValue) const override;
Reference* clone(StringPool* newPool) const override;
- void print(std::ostream& out) const override;
+ void print(std::ostream* out) const override;
};
/**
@@ -141,9 +159,9 @@
*/
struct Id : public BaseItem<Id> {
bool isWeak() const override;
- bool flatten(android::Res_value& out) const override;
+ bool flatten(android::Res_value* out) const override;
Id* clone(StringPool* newPool) const override;
- void print(std::ostream& out) const override;
+ void print(std::ostream* out) const override;
};
/**
@@ -156,9 +174,9 @@
RawString(const StringPool::Ref& ref);
- bool flatten(android::Res_value& outValue) const override;
+ bool flatten(android::Res_value* outValue) const override;
RawString* clone(StringPool* newPool) const override;
- void print(std::ostream& out) const override;
+ void print(std::ostream* out) const override;
};
struct String : public BaseItem<String> {
@@ -166,9 +184,9 @@
String(const StringPool::Ref& ref);
- bool flatten(android::Res_value& outValue) const override;
+ bool flatten(android::Res_value* outValue) const override;
String* clone(StringPool* newPool) const override;
- void print(std::ostream& out) const override;
+ void print(std::ostream* out) const override;
};
struct StyledString : public BaseItem<StyledString> {
@@ -176,9 +194,9 @@
StyledString(const StringPool::StyleRef& ref);
- bool flatten(android::Res_value& outValue) const override;
+ bool flatten(android::Res_value* outValue) const override;
StyledString* clone(StringPool* newPool) const override;
- void print(std::ostream& out) const override;
+ void print(std::ostream* out) const override;
};
struct FileReference : public BaseItem<FileReference> {
@@ -187,9 +205,9 @@
FileReference() = default;
FileReference(const StringPool::Ref& path);
- bool flatten(android::Res_value& outValue) const override;
+ bool flatten(android::Res_value* outValue) const override;
FileReference* clone(StringPool* newPool) const override;
- void print(std::ostream& out) const override;
+ void print(std::ostream* out) const override;
};
/**
@@ -200,10 +218,11 @@
BinaryPrimitive() = default;
BinaryPrimitive(const android::Res_value& val);
+ BinaryPrimitive(uint8_t dataType, uint32_t data);
- bool flatten(android::Res_value& outValue) const override;
+ bool flatten(android::Res_value* outValue) const override;
BinaryPrimitive* clone(StringPool* newPool) const override;
- void print(std::ostream& out) const override;
+ void print(std::ostream* out) const override;
};
struct Attribute : public BaseValue<Attribute> {
@@ -212,7 +231,7 @@
uint32_t value;
};
- bool weak;
+ bool weak;
uint32_t typeMask;
uint32_t minInt;
uint32_t maxInt;
@@ -221,9 +240,9 @@
Attribute(bool w, uint32_t t = 0u);
bool isWeak() const override;
- virtual Attribute* clone(StringPool* newPool) const override;
- void printMask(std::ostream& out) const;
- virtual void print(std::ostream& out) const override;
+ Attribute* clone(StringPool* newPool) const override;
+ void printMask(std::ostream* out) const;
+ void print(std::ostream* out) const override;
};
struct Style : public BaseValue<Style> {
@@ -232,7 +251,7 @@
std::unique_ptr<Item> value;
};
- Reference parent;
+ Maybe<Reference> parent;
/**
* If set to true, the parent was auto inferred from the
@@ -243,14 +262,14 @@
std::vector<Entry> entries;
Style* clone(StringPool* newPool) const override;
- void print(std::ostream& out) const override;
+ void print(std::ostream* out) const override;
};
struct Array : public BaseValue<Array> {
std::vector<std::unique_ptr<Item>> items;
Array* clone(StringPool* newPool) const override;
- void print(std::ostream& out) const override;
+ void print(std::ostream* out) const override;
};
struct Plural : public BaseValue<Plural> {
@@ -267,180 +286,31 @@
std::array<std::unique_ptr<Item>, Count> values;
Plural* clone(StringPool* newPool) const override;
- void print(std::ostream& out) const override;
+ void print(std::ostream* out) const override;
};
struct Styleable : public BaseValue<Styleable> {
std::vector<Reference> entries;
Styleable* clone(StringPool* newPool) const override;
- void print(std::ostream& out) const override;
+ void print(std::ostream* out) const override;
};
/**
* Stream operator for printing Value objects.
*/
inline ::std::ostream& operator<<(::std::ostream& out, const Value& value) {
- value.print(out);
+ value.print(&out);
return out;
}
inline ::std::ostream& operator<<(::std::ostream& out, const Attribute::Symbol& s) {
- return out << s.symbol.name.entry << "=" << s.value;
-}
-
-/**
- * The argument object that gets passed through the value
- * back to the ValueVisitor. Subclasses of ValueVisitor should
- * subclass ValueVisitorArgs to contain the data they need
- * to operate.
- */
-struct ValueVisitorArgs {};
-
-/**
- * Visits a value and runs the appropriate method based on its type.
- */
-struct ValueVisitor {
- virtual void visit(Reference& reference, ValueVisitorArgs& args) {
- visitItem(reference, args);
+ if (s.symbol.name) {
+ out << s.symbol.name.value().entry;
+ } else {
+ out << "???";
}
-
- virtual void visit(RawString& string, ValueVisitorArgs& args) {
- visitItem(string, args);
- }
-
- virtual void visit(String& string, ValueVisitorArgs& args) {
- visitItem(string, args);
- }
-
- virtual void visit(StyledString& string, ValueVisitorArgs& args) {
- visitItem(string, args);
- }
-
- virtual void visit(FileReference& file, ValueVisitorArgs& args) {
- visitItem(file, args);
- }
-
- virtual void visit(Id& id, ValueVisitorArgs& args) {
- visitItem(id, args);
- }
-
- virtual void visit(BinaryPrimitive& primitive, ValueVisitorArgs& args) {
- visitItem(primitive, args);
- }
-
- virtual void visit(Attribute& attr, ValueVisitorArgs& args) {}
- virtual void visit(Style& style, ValueVisitorArgs& args) {}
- virtual void visit(Array& array, ValueVisitorArgs& args) {}
- virtual void visit(Plural& array, ValueVisitorArgs& args) {}
- virtual void visit(Styleable& styleable, ValueVisitorArgs& args) {}
-
- virtual void visitItem(Item& item, ValueVisitorArgs& args) {}
-};
-
-/**
- * Const version of ValueVisitor.
- */
-struct ConstValueVisitor {
- virtual void visit(const Reference& reference, ValueVisitorArgs& args) {
- visitItem(reference, args);
- }
-
- virtual void visit(const RawString& string, ValueVisitorArgs& args) {
- visitItem(string, args);
- }
-
- virtual void visit(const String& string, ValueVisitorArgs& args) {
- visitItem(string, args);
- }
-
- virtual void visit(const StyledString& string, ValueVisitorArgs& args) {
- visitItem(string, args);
- }
-
- virtual void visit(const FileReference& file, ValueVisitorArgs& args) {
- visitItem(file, args);
- }
-
- virtual void visit(const Id& id, ValueVisitorArgs& args) {
- visitItem(id, args);
- }
-
- virtual void visit(const BinaryPrimitive& primitive, ValueVisitorArgs& args) {
- visitItem(primitive, args);
- }
-
- virtual void visit(const Attribute& attr, ValueVisitorArgs& args) {}
- virtual void visit(const Style& style, ValueVisitorArgs& args) {}
- virtual void visit(const Array& array, ValueVisitorArgs& args) {}
- virtual void visit(const Plural& array, ValueVisitorArgs& args) {}
- virtual void visit(const Styleable& styleable, ValueVisitorArgs& args) {}
-
- virtual void visitItem(const Item& item, ValueVisitorArgs& args) {}
-};
-
-/**
- * Convenience Visitor that forwards a specific type to a function.
- * Args are not used as the function can bind variables. Do not use
- * directly, use the wrapper visitFunc() method.
- */
-template <typename T, typename TFunc>
-struct ValueVisitorFunc : ValueVisitor {
- TFunc func;
-
- ValueVisitorFunc(TFunc f) : func(f) {
- }
-
- void visit(T& value, ValueVisitorArgs&) override {
- func(value);
- }
-};
-
-/**
- * Const version of ValueVisitorFunc.
- */
-template <typename T, typename TFunc>
-struct ConstValueVisitorFunc : ConstValueVisitor {
- TFunc func;
-
- ConstValueVisitorFunc(TFunc f) : func(f) {
- }
-
- void visit(const T& value, ValueVisitorArgs&) override {
- func(value);
- }
-};
-
-template <typename T, typename TFunc>
-void visitFunc(Value& value, TFunc f) {
- ValueVisitorFunc<T, TFunc> visitor(f);
- value.accept(visitor, ValueVisitorArgs{});
-}
-
-template <typename T, typename TFunc>
-void visitFunc(const Value& value, TFunc f) {
- ConstValueVisitorFunc<T, TFunc> visitor(f);
- value.accept(visitor, ValueVisitorArgs{});
-}
-
-template <typename Derived>
-void BaseValue<Derived>::accept(ValueVisitor& visitor, ValueVisitorArgs&& args) {
- visitor.visit(static_cast<Derived&>(*this), args);
-}
-
-template <typename Derived>
-void BaseValue<Derived>::accept(ConstValueVisitor& visitor, ValueVisitorArgs&& args) const {
- visitor.visit(static_cast<const Derived&>(*this), args);
-}
-
-template <typename Derived>
-void BaseItem<Derived>::accept(ValueVisitor& visitor, ValueVisitorArgs&& args) {
- visitor.visit(static_cast<Derived&>(*this), args);
-}
-
-template <typename Derived>
-void BaseItem<Derived>::accept(ConstValueVisitor& visitor, ValueVisitorArgs&& args) const {
- visitor.visit(static_cast<const Derived&>(*this), args);
+ return out << "=" << s.value;
}
} // namespace aapt
diff --git a/tools/aapt2/Resource_test.cpp b/tools/aapt2/Resource_test.cpp
index d957999..48dc521 100644
--- a/tools/aapt2/Resource_test.cpp
+++ b/tools/aapt2/Resource_test.cpp
@@ -69,10 +69,6 @@
ASSERT_NE(type, nullptr);
EXPECT_EQ(*type, ResourceType::kInteger);
- type = parseResourceType(u"integer-array");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kIntegerArray);
-
type = parseResourceType(u"interpolator");
ASSERT_NE(type, nullptr);
EXPECT_EQ(*type, ResourceType::kInterpolator);
diff --git a/tools/aapt2/ScopedXmlPullParser.cpp b/tools/aapt2/ScopedXmlPullParser.cpp
deleted file mode 100644
index 48da93e..0000000
--- a/tools/aapt2/ScopedXmlPullParser.cpp
+++ /dev/null
@@ -1,104 +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.
- */
-
-#include "ScopedXmlPullParser.h"
-
-#include <string>
-
-namespace aapt {
-
-ScopedXmlPullParser::ScopedXmlPullParser(XmlPullParser* parser) :
- mParser(parser), mDepth(parser->getDepth()), mDone(false) {
-}
-
-ScopedXmlPullParser::~ScopedXmlPullParser() {
- while (isGoodEvent(next()));
-}
-
-XmlPullParser::Event ScopedXmlPullParser::next() {
- if (mDone) {
- return Event::kEndDocument;
- }
-
- const Event event = mParser->next();
- if (mParser->getDepth() <= mDepth) {
- mDone = true;
- }
- return event;
-}
-
-XmlPullParser::Event ScopedXmlPullParser::getEvent() const {
- return mParser->getEvent();
-}
-
-const std::string& ScopedXmlPullParser::getLastError() const {
- return mParser->getLastError();
-}
-
-const std::u16string& ScopedXmlPullParser::getComment() const {
- return mParser->getComment();
-}
-
-size_t ScopedXmlPullParser::getLineNumber() const {
- return mParser->getLineNumber();
-}
-
-size_t ScopedXmlPullParser::getDepth() const {
- const size_t depth = mParser->getDepth();
- if (depth < mDepth) {
- return 0;
- }
- return depth - mDepth;
-}
-
-const std::u16string& ScopedXmlPullParser::getText() const {
- return mParser->getText();
-}
-
-const std::u16string& ScopedXmlPullParser::getNamespacePrefix() const {
- return mParser->getNamespacePrefix();
-}
-
-const std::u16string& ScopedXmlPullParser::getNamespaceUri() const {
- return mParser->getNamespaceUri();
-}
-
-bool ScopedXmlPullParser::applyPackageAlias(std::u16string* package,
- const std::u16string& defaultPackage) const {
- return mParser->applyPackageAlias(package, defaultPackage);
-}
-
-const std::u16string& ScopedXmlPullParser::getElementNamespace() const {
- return mParser->getElementNamespace();
-}
-
-const std::u16string& ScopedXmlPullParser::getElementName() const {
- return mParser->getElementName();
-}
-
-size_t ScopedXmlPullParser::getAttributeCount() const {
- return mParser->getAttributeCount();
-}
-
-XmlPullParser::const_iterator ScopedXmlPullParser::beginAttributes() const {
- return mParser->beginAttributes();
-}
-
-XmlPullParser::const_iterator ScopedXmlPullParser::endAttributes() const {
- return mParser->endAttributes();
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/ScopedXmlPullParser.h b/tools/aapt2/ScopedXmlPullParser.h
deleted file mode 100644
index a040f60..0000000
--- a/tools/aapt2/ScopedXmlPullParser.h
+++ /dev/null
@@ -1,85 +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_SCOPED_XML_PULL_PARSER_H
-#define AAPT_SCOPED_XML_PULL_PARSER_H
-
-#include "XmlPullParser.h"
-
-#include <string>
-
-namespace aapt {
-
-/**
- * An XmlPullParser that will not read past the depth
- * of the underlying parser. When this parser is destroyed,
- * it moves the underlying parser to the same depth it
- * started with.
- *
- * You can write code like this:
- *
- * while (XmlPullParser::isGoodEvent(parser.next())) {
- * if (parser.getEvent() != XmlPullParser::Event::StartElement) {
- * continue;
- * }
- *
- * ScopedXmlPullParser scoped(parser);
- * if (parser.getElementName() == u"id") {
- * // do work.
- * } else {
- * // do nothing, as all the sub elements will be skipped
- * // when scoped goes out of scope.
- * }
- * }
- */
-class ScopedXmlPullParser : public XmlPullParser {
-public:
- ScopedXmlPullParser(XmlPullParser* parser);
- ScopedXmlPullParser(const ScopedXmlPullParser&) = delete;
- ScopedXmlPullParser& operator=(const ScopedXmlPullParser&) = delete;
- ~ScopedXmlPullParser();
-
- Event getEvent() const override;
- const std::string& getLastError() const override;
- Event next() override;
-
- const std::u16string& getComment() const override;
- size_t getLineNumber() const override;
- size_t getDepth() const override;
-
- const std::u16string& getText() const override;
-
- const std::u16string& getNamespacePrefix() const override;
- const std::u16string& getNamespaceUri() const override;
- bool applyPackageAlias(std::u16string* package, const std::u16string& defaultPackage)
- const override;
-
- const std::u16string& getElementNamespace() const override;
- const std::u16string& getElementName() const override;
-
- const_iterator beginAttributes() const override;
- const_iterator endAttributes() const override;
- size_t getAttributeCount() const override;
-
-private:
- XmlPullParser* mParser;
- size_t mDepth;
- bool mDone;
-};
-
-} // namespace aapt
-
-#endif // AAPT_SCOPED_XML_PULL_PARSER_H
diff --git a/tools/aapt2/ScopedXmlPullParser_test.cpp b/tools/aapt2/ScopedXmlPullParser_test.cpp
deleted file mode 100644
index 342f305..0000000
--- a/tools/aapt2/ScopedXmlPullParser_test.cpp
+++ /dev/null
@@ -1,106 +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.
- */
-
-#include "ScopedXmlPullParser.h"
-#include "SourceXmlPullParser.h"
-
-#include <gtest/gtest.h>
-#include <sstream>
-#include <string>
-
-namespace aapt {
-
-TEST(ScopedXmlPullParserTest, StopIteratingAtNoNZeroDepth) {
- std::stringstream input;
- input << "<?xml version=\"1.0\" encoding=\"utf-8\"?>" << std::endl
- << "<resources><string></string></resources>" << std::endl;
-
- SourceXmlPullParser sourceParser(input);
- EXPECT_EQ(XmlPullParser::Event::kStartElement, sourceParser.next());
- EXPECT_EQ(std::u16string(u"resources"), sourceParser.getElementName());
-
- EXPECT_EQ(XmlPullParser::Event::kStartElement, sourceParser.next());
- EXPECT_EQ(std::u16string(u"string"), sourceParser.getElementName());
-
- {
- ScopedXmlPullParser scopedParser(&sourceParser);
- EXPECT_EQ(XmlPullParser::Event::kEndElement, scopedParser.next());
- EXPECT_EQ(std::u16string(u"string"), sourceParser.getElementName());
-
- EXPECT_EQ(XmlPullParser::Event::kEndDocument, scopedParser.next());
- }
-
- EXPECT_EQ(XmlPullParser::Event::kEndElement, sourceParser.next());
- EXPECT_EQ(std::u16string(u"resources"), sourceParser.getElementName());
-
- EXPECT_EQ(XmlPullParser::Event::kEndDocument, sourceParser.next());
-}
-
-TEST(ScopedXmlPullParserTest, FinishCurrentElementOnDestruction) {
- std::stringstream input;
- input << "<?xml version=\"1.0\" encoding=\"utf-8\"?>" << std::endl
- << "<resources><string></string></resources>" << std::endl;
-
- SourceXmlPullParser sourceParser(input);
- EXPECT_EQ(XmlPullParser::Event::kStartElement, sourceParser.next());
- EXPECT_EQ(std::u16string(u"resources"), sourceParser.getElementName());
-
- EXPECT_EQ(XmlPullParser::Event::kStartElement, sourceParser.next());
- EXPECT_EQ(std::u16string(u"string"), sourceParser.getElementName());
-
- {
- ScopedXmlPullParser scopedParser(&sourceParser);
- EXPECT_EQ(std::u16string(u"string"), sourceParser.getElementName());
- }
-
- EXPECT_EQ(XmlPullParser::Event::kEndElement, sourceParser.next());
- EXPECT_EQ(std::u16string(u"resources"), sourceParser.getElementName());
-
- EXPECT_EQ(XmlPullParser::Event::kEndDocument, sourceParser.next());
-}
-
-TEST(ScopedXmlPullParserTest, NestedParsersOperateCorrectly) {
- std::stringstream input;
- input << "<?xml version=\"1.0\" encoding=\"utf-8\"?>" << std::endl
- << "<resources><string><foo></foo></string></resources>" << std::endl;
-
- SourceXmlPullParser sourceParser(input);
- EXPECT_EQ(XmlPullParser::Event::kStartElement, sourceParser.next());
- EXPECT_EQ(std::u16string(u"resources"), sourceParser.getElementName());
-
- EXPECT_EQ(XmlPullParser::Event::kStartElement, sourceParser.next());
- EXPECT_EQ(std::u16string(u"string"), sourceParser.getElementName());
-
- {
- ScopedXmlPullParser scopedParser(&sourceParser);
- EXPECT_EQ(std::u16string(u"string"), scopedParser.getElementName());
- while (XmlPullParser::isGoodEvent(scopedParser.next())) {
- if (scopedParser.getEvent() != XmlPullParser::Event::kStartElement) {
- continue;
- }
-
- ScopedXmlPullParser subScopedParser(&scopedParser);
- EXPECT_EQ(std::u16string(u"foo"), subScopedParser.getElementName());
- }
- }
-
- EXPECT_EQ(XmlPullParser::Event::kEndElement, sourceParser.next());
- EXPECT_EQ(std::u16string(u"resources"), sourceParser.getElementName());
-
- EXPECT_EQ(XmlPullParser::Event::kEndDocument, sourceParser.next());
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/SdkConstants.cpp b/tools/aapt2/SdkConstants.cpp
index 9bdae49..c2a22bf 100644
--- a/tools/aapt2/SdkConstants.cpp
+++ b/tools/aapt2/SdkConstants.cpp
@@ -34,8 +34,9 @@
{ 0x02bd, SDK_FROYO },
{ 0x02cb, SDK_GINGERBREAD },
{ 0x0361, SDK_HONEYCOMB },
- { 0x0366, SDK_HONEYCOMB_MR1 },
- { 0x03a6, SDK_HONEYCOMB_MR2 },
+ { 0x0363, SDK_HONEYCOMB_MR1 },
+ { 0x0366, SDK_HONEYCOMB_MR2 },
+ { 0x03a6, SDK_ICE_CREAM_SANDWICH },
{ 0x03ae, SDK_JELLY_BEAN },
{ 0x03cc, SDK_JELLY_BEAN_MR1 },
{ 0x03da, SDK_JELLY_BEAN_MR2 },
diff --git a/tools/aapt2/SdkConstants.h b/tools/aapt2/SdkConstants.h
index 803da03..282ed9a 100644
--- a/tools/aapt2/SdkConstants.h
+++ b/tools/aapt2/SdkConstants.h
@@ -42,6 +42,7 @@
SDK_KITKAT_WATCH = 20,
SDK_LOLLIPOP = 21,
SDK_LOLLIPOP_MR1 = 22,
+ SDK_MARSHMALLOW = 23,
};
size_t findAttributeSdkLevel(ResourceId id);
diff --git a/tools/aapt2/Source.h b/tools/aapt2/Source.h
index 3606488..8af203c 100644
--- a/tools/aapt2/Source.h
+++ b/tools/aapt2/Source.h
@@ -17,72 +17,58 @@
#ifndef AAPT_SOURCE_H
#define AAPT_SOURCE_H
+#include "util/Maybe.h"
+#include "util/StringPiece.h"
+
#include <ostream>
#include <string>
-#include <tuple>
namespace aapt {
-struct SourceLineColumn;
-struct SourceLine;
-
/**
* Represents a file on disk. Used for logging and
* showing errors.
*/
struct Source {
std::string path;
+ Maybe<size_t> line;
- inline SourceLine line(size_t line) const;
-};
+ Source() = default;
-/**
- * Represents a file on disk and a line number in that file.
- * Used for logging and showing errors.
- */
-struct SourceLine {
- std::string path;
- size_t line;
+ inline Source(const StringPiece& path) : path(path.toString()) {
+ }
- inline SourceLineColumn column(size_t column) const;
-};
+ inline Source(const StringPiece& path, size_t line) : path(path.toString()), line(line) {
+ }
-/**
- * Represents a file on disk and a line:column number in that file.
- * Used for logging and showing errors.
- */
-struct SourceLineColumn {
- std::string path;
- size_t line;
- size_t column;
+ inline Source withLine(size_t line) const {
+ return Source(path, line);
+ }
};
//
// Implementations
//
-SourceLine Source::line(size_t line) const {
- return SourceLine{ path, line };
-}
-
-SourceLineColumn SourceLine::column(size_t column) const {
- return SourceLineColumn{ path, line, column };
-}
-
inline ::std::ostream& operator<<(::std::ostream& out, const Source& source) {
- return out << source.path;
+ out << source.path;
+ if (source.line) {
+ out << ":" << source.line.value();
+ }
+ return out;
}
-inline ::std::ostream& operator<<(::std::ostream& out, const SourceLine& source) {
- return out << source.path << ":" << source.line;
-}
-
-inline ::std::ostream& operator<<(::std::ostream& out, const SourceLineColumn& source) {
- return out << source.path << ":" << source.line << ":" << source.column;
-}
-
-inline bool operator<(const SourceLine& lhs, const SourceLine& rhs) {
- return std::tie(lhs.path, lhs.line) < std::tie(rhs.path, rhs.line);
+inline bool operator<(const Source& lhs, const Source& rhs) {
+ int cmp = lhs.path.compare(rhs.path);
+ if (cmp < 0) return true;
+ if (cmp > 0) return false;
+ if (lhs.line) {
+ if (rhs.line) {
+ return lhs.line.value() < rhs.line.value();
+ }
+ return false;
+ }
+ return bool(rhs.line);
}
} // namespace aapt
diff --git a/tools/aapt2/SourceXmlPullParser.cpp b/tools/aapt2/SourceXmlPullParser.cpp
deleted file mode 100644
index 8099044..0000000
--- a/tools/aapt2/SourceXmlPullParser.cpp
+++ /dev/null
@@ -1,283 +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.
- */
-
-#include <iostream>
-#include <string>
-
-#include "Maybe.h"
-#include "SourceXmlPullParser.h"
-#include "Util.h"
-
-namespace aapt {
-
-constexpr char kXmlNamespaceSep = 1;
-
-SourceXmlPullParser::SourceXmlPullParser(std::istream& in) : mIn(in), mEmpty(), mDepth(0) {
- mParser = XML_ParserCreateNS(nullptr, kXmlNamespaceSep);
- XML_SetUserData(mParser, this);
- XML_SetElementHandler(mParser, startElementHandler, endElementHandler);
- XML_SetNamespaceDeclHandler(mParser, startNamespaceHandler, endNamespaceHandler);
- XML_SetCharacterDataHandler(mParser, characterDataHandler);
- XML_SetCommentHandler(mParser, commentDataHandler);
- mEventQueue.push(EventData{ Event::kStartDocument, 0, mDepth++ });
-}
-
-SourceXmlPullParser::~SourceXmlPullParser() {
- XML_ParserFree(mParser);
-}
-
-SourceXmlPullParser::Event SourceXmlPullParser::next() {
- const Event currentEvent = getEvent();
- if (currentEvent == Event::kBadDocument || currentEvent == Event::kEndDocument) {
- return currentEvent;
- }
-
- mEventQueue.pop();
- while (mEventQueue.empty()) {
- mIn.read(mBuffer, sizeof(mBuffer) / sizeof(*mBuffer));
-
- const bool done = mIn.eof();
- if (mIn.bad() && !done) {
- mLastError = strerror(errno);
- mEventQueue.push(EventData{ Event::kBadDocument });
- continue;
- }
-
- if (XML_Parse(mParser, mBuffer, mIn.gcount(), done) == XML_STATUS_ERROR) {
- mLastError = XML_ErrorString(XML_GetErrorCode(mParser));
- mEventQueue.push(EventData{ Event::kBadDocument });
- continue;
- }
-
- if (done) {
- mEventQueue.push(EventData{ Event::kEndDocument, 0, 0 });
- }
- }
-
- Event event = getEvent();
-
- // Record namespace prefixes and package names so that we can do our own
- // handling of references that use namespace aliases.
- if (event == Event::kStartNamespace || event == Event::kEndNamespace) {
- Maybe<std::u16string> result = util::extractPackageFromNamespace(getNamespaceUri());
- if (event == Event::kStartNamespace) {
- if (result) {
- mPackageAliases.emplace_back(getNamespacePrefix(), result.value());
- }
- } else {
- if (result) {
- assert(mPackageAliases.back().second == result.value());
- mPackageAliases.pop_back();
- }
- }
- }
-
- return event;
-}
-
-SourceXmlPullParser::Event SourceXmlPullParser::getEvent() const {
- return mEventQueue.front().event;
-}
-
-const std::string& SourceXmlPullParser::getLastError() const {
- return mLastError;
-}
-
-const std::u16string& SourceXmlPullParser::getComment() const {
- return mEventQueue.front().comment;
-}
-
-size_t SourceXmlPullParser::getLineNumber() const {
- return mEventQueue.front().lineNumber;
-}
-
-size_t SourceXmlPullParser::getDepth() const {
- return mEventQueue.front().depth;
-}
-
-const std::u16string& SourceXmlPullParser::getText() const {
- if (getEvent() != Event::kText) {
- return mEmpty;
- }
- return mEventQueue.front().data1;
-}
-
-const std::u16string& SourceXmlPullParser::getNamespacePrefix() const {
- const Event currentEvent = getEvent();
- if (currentEvent != Event::kStartNamespace && currentEvent != Event::kEndNamespace) {
- return mEmpty;
- }
- return mEventQueue.front().data1;
-}
-
-const std::u16string& SourceXmlPullParser::getNamespaceUri() const {
- const Event currentEvent = getEvent();
- if (currentEvent != Event::kStartNamespace && currentEvent != Event::kEndNamespace) {
- return mEmpty;
- }
- return mEventQueue.front().data2;
-}
-
-bool SourceXmlPullParser::applyPackageAlias(std::u16string* package,
- const std::u16string& defaultPackage) const {
- const auto endIter = mPackageAliases.rend();
- for (auto iter = mPackageAliases.rbegin(); iter != endIter; ++iter) {
- if (iter->first == *package) {
- if (iter->second.empty()) {
- *package = defaultPackage;
- } else {
- *package = iter->second;
- }
- return true;
- }
- }
- return false;
-}
-
-const std::u16string& SourceXmlPullParser::getElementNamespace() const {
- const Event currentEvent = getEvent();
- if (currentEvent != Event::kStartElement && currentEvent != Event::kEndElement) {
- return mEmpty;
- }
- return mEventQueue.front().data1;
-}
-
-const std::u16string& SourceXmlPullParser::getElementName() const {
- const Event currentEvent = getEvent();
- if (currentEvent != Event::kStartElement && currentEvent != Event::kEndElement) {
- return mEmpty;
- }
- return mEventQueue.front().data2;
-}
-
-XmlPullParser::const_iterator SourceXmlPullParser::beginAttributes() const {
- return mEventQueue.front().attributes.begin();
-}
-
-XmlPullParser::const_iterator SourceXmlPullParser::endAttributes() const {
- return mEventQueue.front().attributes.end();
-}
-
-size_t SourceXmlPullParser::getAttributeCount() const {
- if (getEvent() != Event::kStartElement) {
- return 0;
- }
- return mEventQueue.front().attributes.size();
-}
-
-/**
- * Extracts the namespace and name of an expanded element or attribute name.
- */
-static void splitName(const char* name, std::u16string& outNs, std::u16string& outName) {
- const char* p = name;
- while (*p != 0 && *p != kXmlNamespaceSep) {
- p++;
- }
-
- if (*p == 0) {
- outNs = std::u16string();
- outName = util::utf8ToUtf16(name);
- } else {
- outNs = util::utf8ToUtf16(StringPiece(name, (p - name)));
- outName = util::utf8ToUtf16(p + 1);
- }
-}
-
-void XMLCALL SourceXmlPullParser::startNamespaceHandler(void* userData, const char* prefix,
- const char* uri) {
- SourceXmlPullParser* parser = reinterpret_cast<SourceXmlPullParser*>(userData);
- std::u16string namespaceUri = uri != nullptr ? util::utf8ToUtf16(uri) : std::u16string();
- parser->mNamespaceUris.push(namespaceUri);
- parser->mEventQueue.push(EventData{
- Event::kStartNamespace,
- XML_GetCurrentLineNumber(parser->mParser),
- parser->mDepth++,
- prefix != nullptr ? util::utf8ToUtf16(prefix) : std::u16string(),
- namespaceUri
- });
-}
-
-void XMLCALL SourceXmlPullParser::startElementHandler(void* userData, const char* name,
- const char** attrs) {
- SourceXmlPullParser* parser = reinterpret_cast<SourceXmlPullParser*>(userData);
-
- EventData data = {
- Event::kStartElement, XML_GetCurrentLineNumber(parser->mParser), parser->mDepth++
- };
- splitName(name, data.data1, data.data2);
-
- while (*attrs) {
- Attribute attribute;
- splitName(*attrs++, attribute.namespaceUri, attribute.name);
- attribute.value = util::utf8ToUtf16(*attrs++);
-
- // Insert in sorted order.
- auto iter = std::lower_bound(data.attributes.begin(), data.attributes.end(), attribute);
- data.attributes.insert(iter, std::move(attribute));
- }
-
- // Move the structure into the queue (no copy).
- parser->mEventQueue.push(std::move(data));
-}
-
-void XMLCALL SourceXmlPullParser::characterDataHandler(void* userData, const char* s, int len) {
- SourceXmlPullParser* parser = reinterpret_cast<SourceXmlPullParser*>(userData);
-
- parser->mEventQueue.push(EventData{
- Event::kText,
- XML_GetCurrentLineNumber(parser->mParser),
- parser->mDepth,
- util::utf8ToUtf16(StringPiece(s, len))
- });
-}
-
-void XMLCALL SourceXmlPullParser::endElementHandler(void* userData, const char* name) {
- SourceXmlPullParser* parser = reinterpret_cast<SourceXmlPullParser*>(userData);
-
- EventData data = {
- Event::kEndElement, XML_GetCurrentLineNumber(parser->mParser), --(parser->mDepth)
- };
- splitName(name, data.data1, data.data2);
-
- // Move the data into the queue (no copy).
- parser->mEventQueue.push(std::move(data));
-}
-
-void XMLCALL SourceXmlPullParser::endNamespaceHandler(void* userData, const char* prefix) {
- SourceXmlPullParser* parser = reinterpret_cast<SourceXmlPullParser*>(userData);
-
- parser->mEventQueue.push(EventData{
- Event::kEndNamespace,
- XML_GetCurrentLineNumber(parser->mParser),
- --(parser->mDepth),
- prefix != nullptr ? util::utf8ToUtf16(prefix) : std::u16string(),
- parser->mNamespaceUris.top()
- });
- parser->mNamespaceUris.pop();
-}
-
-void XMLCALL SourceXmlPullParser::commentDataHandler(void* userData, const char* comment) {
- SourceXmlPullParser* parser = reinterpret_cast<SourceXmlPullParser*>(userData);
-
- parser->mEventQueue.push(EventData{
- Event::kComment,
- XML_GetCurrentLineNumber(parser->mParser),
- parser->mDepth,
- util::utf8ToUtf16(comment)
- });
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/SourceXmlPullParser.h b/tools/aapt2/SourceXmlPullParser.h
deleted file mode 100644
index d8ed459..0000000
--- a/tools/aapt2/SourceXmlPullParser.h
+++ /dev/null
@@ -1,91 +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_SOURCE_XML_PULL_PARSER_H
-#define AAPT_SOURCE_XML_PULL_PARSER_H
-
-#include "XmlPullParser.h"
-
-#include <istream>
-#include <expat.h>
-#include <queue>
-#include <stack>
-#include <string>
-#include <vector>
-
-namespace aapt {
-
-class SourceXmlPullParser : public XmlPullParser {
-public:
- SourceXmlPullParser(std::istream& in);
- SourceXmlPullParser(const SourceXmlPullParser& rhs) = delete;
- ~SourceXmlPullParser();
-
- Event getEvent() const override;
- const std::string& getLastError() const override ;
- Event next() override ;
-
- const std::u16string& getComment() const override;
- size_t getLineNumber() const override;
- size_t getDepth() const override;
-
- const std::u16string& getText() const override;
-
- const std::u16string& getNamespacePrefix() const override;
- const std::u16string& getNamespaceUri() const override;
- bool applyPackageAlias(std::u16string* package,
- const std::u16string& defaultPackage) const override;
-
-
- const std::u16string& getElementNamespace() const override;
- const std::u16string& getElementName() const override;
-
- const_iterator beginAttributes() const override;
- const_iterator endAttributes() const override;
- size_t getAttributeCount() const override;
-
-private:
- static void XMLCALL startNamespaceHandler(void* userData, const char* prefix, const char* uri);
- static void XMLCALL startElementHandler(void* userData, const char* name, const char** attrs);
- static void XMLCALL characterDataHandler(void* userData, const char* s, int len);
- static void XMLCALL endElementHandler(void* userData, const char* name);
- static void XMLCALL endNamespaceHandler(void* userData, const char* prefix);
- static void XMLCALL commentDataHandler(void* userData, const char* comment);
-
- struct EventData {
- Event event;
- size_t lineNumber;
- size_t depth;
- std::u16string data1;
- std::u16string data2;
- std::u16string comment;
- std::vector<Attribute> attributes;
- };
-
- std::istream& mIn;
- XML_Parser mParser;
- char mBuffer[16384];
- std::queue<EventData> mEventQueue;
- std::string mLastError;
- const std::u16string mEmpty;
- size_t mDepth;
- std::stack<std::u16string> mNamespaceUris;
- std::vector<std::pair<std::u16string, std::u16string>> mPackageAliases;
-};
-
-} // namespace aapt
-
-#endif // AAPT_SOURCE_XML_PULL_PARSER_H
diff --git a/tools/aapt2/StringPiece.h b/tools/aapt2/StringPiece.h
deleted file mode 100644
index 8cbdeae..0000000
--- a/tools/aapt2/StringPiece.h
+++ /dev/null
@@ -1,237 +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_STRING_PIECE_H
-#define AAPT_STRING_PIECE_H
-
-#include <ostream>
-#include <string>
-#include <utils/String8.h>
-#include <utils/Unicode.h>
-
-namespace aapt {
-
-/**
- * Read only wrapper around basic C strings.
- * Prevents excessive copying.
- *
- * WARNING: When creating from std::basic_string<>, moving the original
- * std::basic_string<> will invalidate the data held in a BasicStringPiece<>.
- * BasicStringPiece<> should only be used transitively.
- */
-template <typename TChar>
-class BasicStringPiece {
-public:
- using const_iterator = const TChar*;
-
- BasicStringPiece();
- BasicStringPiece(const BasicStringPiece<TChar>& str);
- BasicStringPiece(const std::basic_string<TChar>& str);
- BasicStringPiece(const TChar* str);
- BasicStringPiece(const TChar* str, size_t len);
-
- BasicStringPiece<TChar>& operator=(const BasicStringPiece<TChar>& rhs);
- BasicStringPiece<TChar>& assign(const TChar* str, size_t len);
-
- BasicStringPiece<TChar> substr(size_t start, size_t len) const;
- BasicStringPiece<TChar> substr(BasicStringPiece<TChar>::const_iterator begin,
- BasicStringPiece<TChar>::const_iterator end) const;
-
- const TChar* data() const;
- size_t length() const;
- size_t size() const;
- bool empty() const;
- std::basic_string<TChar> toString() const;
-
- int compare(const BasicStringPiece<TChar>& rhs) const;
- bool operator<(const BasicStringPiece<TChar>& rhs) const;
- bool operator>(const BasicStringPiece<TChar>& rhs) const;
- bool operator==(const BasicStringPiece<TChar>& rhs) const;
- bool operator!=(const BasicStringPiece<TChar>& rhs) const;
-
- const_iterator begin() const;
- const_iterator end() const;
-
-private:
- const TChar* mData;
- size_t mLength;
-};
-
-using StringPiece = BasicStringPiece<char>;
-using StringPiece16 = BasicStringPiece<char16_t>;
-
-//
-// BasicStringPiece implementation.
-//
-
-template <typename TChar>
-inline BasicStringPiece<TChar>::BasicStringPiece() : mData(nullptr) , mLength(0) {
-}
-
-template <typename TChar>
-inline BasicStringPiece<TChar>::BasicStringPiece(const BasicStringPiece<TChar>& str) :
- mData(str.mData), mLength(str.mLength) {
-}
-
-template <typename TChar>
-inline BasicStringPiece<TChar>::BasicStringPiece(const std::basic_string<TChar>& str) :
- mData(str.data()), mLength(str.length()) {
-}
-
-template <>
-inline BasicStringPiece<char>::BasicStringPiece(const char* str) :
- mData(str), mLength(str != nullptr ? strlen(str) : 0) {
-}
-
-template <>
-inline BasicStringPiece<char16_t>::BasicStringPiece(const char16_t* str) :
- mData(str), mLength(str != nullptr ? strlen16(str) : 0) {
-}
-
-template <typename TChar>
-inline BasicStringPiece<TChar>::BasicStringPiece(const TChar* str, size_t len) :
- mData(str), mLength(len) {
-}
-
-template <typename TChar>
-inline BasicStringPiece<TChar>& BasicStringPiece<TChar>::operator=(
- const BasicStringPiece<TChar>& rhs) {
- mData = rhs.mData;
- mLength = rhs.mLength;
- return *this;
-}
-
-template <typename TChar>
-inline BasicStringPiece<TChar>& BasicStringPiece<TChar>::assign(const TChar* str, size_t len) {
- mData = str;
- mLength = len;
- return *this;
-}
-
-
-template <typename TChar>
-inline BasicStringPiece<TChar> BasicStringPiece<TChar>::substr(size_t start, size_t len) const {
- if (start + len > mLength) {
- return BasicStringPiece<TChar>();
- }
- return BasicStringPiece<TChar>(mData + start, len);
-}
-
-template <typename TChar>
-inline BasicStringPiece<TChar> BasicStringPiece<TChar>::substr(
- BasicStringPiece<TChar>::const_iterator begin,
- BasicStringPiece<TChar>::const_iterator end) const {
- return BasicStringPiece<TChar>(begin, end - begin);
-}
-
-template <typename TChar>
-inline const TChar* BasicStringPiece<TChar>::data() const {
- return mData;
-}
-
-template <typename TChar>
-inline size_t BasicStringPiece<TChar>::length() const {
- return mLength;
-}
-
-template <typename TChar>
-inline size_t BasicStringPiece<TChar>::size() const {
- return mLength;
-}
-
-template <typename TChar>
-inline bool BasicStringPiece<TChar>::empty() const {
- return mLength == 0;
-}
-
-template <typename TChar>
-inline std::basic_string<TChar> BasicStringPiece<TChar>::toString() const {
- return std::basic_string<TChar>(mData, mLength);
-}
-
-template <>
-inline int BasicStringPiece<char>::compare(const BasicStringPiece<char>& rhs) const {
- const char nullStr = '\0';
- const char* b1 = mData != nullptr ? mData : &nullStr;
- const char* e1 = b1 + mLength;
- const char* b2 = rhs.mData != nullptr ? rhs.mData : &nullStr;
- const char* e2 = b2 + rhs.mLength;
-
- while (b1 < e1 && b2 < e2) {
- const int d = static_cast<int>(*b1++) - static_cast<int>(*b2++);
- if (d) {
- return d;
- }
- }
- return static_cast<int>(mLength - rhs.mLength);
-}
-
-inline ::std::ostream& operator<<(::std::ostream& out, const BasicStringPiece<char16_t>& str) {
- android::String8 utf8(str.data(), str.size());
- return out.write(utf8.string(), utf8.size());
-}
-
-
-template <>
-inline int BasicStringPiece<char16_t>::compare(const BasicStringPiece<char16_t>& rhs) const {
- const char16_t nullStr = u'\0';
- const char16_t* b1 = mData != nullptr ? mData : &nullStr;
- const char16_t* b2 = rhs.mData != nullptr ? rhs.mData : &nullStr;
- return strzcmp16(b1, mLength, b2, rhs.mLength);
-}
-
-template <typename TChar>
-inline bool BasicStringPiece<TChar>::operator<(const BasicStringPiece<TChar>& rhs) const {
- return compare(rhs) < 0;
-}
-
-template <typename TChar>
-inline bool BasicStringPiece<TChar>::operator>(const BasicStringPiece<TChar>& rhs) const {
- return compare(rhs) > 0;
-}
-
-template <typename TChar>
-inline bool BasicStringPiece<TChar>::operator==(const BasicStringPiece<TChar>& rhs) const {
- return compare(rhs) == 0;
-}
-
-template <typename TChar>
-inline bool BasicStringPiece<TChar>::operator!=(const BasicStringPiece<TChar>& rhs) const {
- return compare(rhs) != 0;
-}
-
-template <typename TChar>
-inline typename BasicStringPiece<TChar>::const_iterator BasicStringPiece<TChar>::begin() const {
- return mData;
-}
-
-template <typename TChar>
-inline typename BasicStringPiece<TChar>::const_iterator BasicStringPiece<TChar>::end() const {
- return mData + mLength;
-}
-
-inline ::std::ostream& operator<<(::std::ostream& out, const BasicStringPiece<char>& str) {
- return out.write(str.data(), str.size());
-}
-
-} // namespace aapt
-
-inline ::std::ostream& operator<<(::std::ostream& out, const std::u16string& str) {
- android::String8 utf8(str.data(), str.size());
- return out.write(utf8.string(), utf8.size());
-}
-
-#endif // AAPT_STRING_PIECE_H
diff --git a/tools/aapt2/StringPiece_test.cpp b/tools/aapt2/StringPiece_test.cpp
deleted file mode 100644
index 43f7a37..0000000
--- a/tools/aapt2/StringPiece_test.cpp
+++ /dev/null
@@ -1,62 +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.
- */
-
-#include <algorithm>
-#include <gtest/gtest.h>
-#include <string>
-#include <vector>
-
-#include "StringPiece.h"
-
-namespace aapt {
-
-TEST(StringPieceTest, CompareNonNullTerminatedPiece) {
- StringPiece a("hello world", 5);
- StringPiece b("hello moon", 5);
- EXPECT_EQ(a, b);
-
- StringPiece16 a16(u"hello world", 5);
- StringPiece16 b16(u"hello moon", 5);
- EXPECT_EQ(a16, b16);
-}
-
-TEST(StringPieceTest, PiecesHaveCorrectSortOrder) {
- std::u16string testing(u"testing");
- std::u16string banana(u"banana");
- std::u16string car(u"car");
-
- EXPECT_TRUE(StringPiece16(testing) > banana);
- EXPECT_TRUE(StringPiece16(testing) > car);
- EXPECT_TRUE(StringPiece16(banana) < testing);
- EXPECT_TRUE(StringPiece16(banana) < car);
- EXPECT_TRUE(StringPiece16(car) < testing);
- EXPECT_TRUE(StringPiece16(car) > banana);
-}
-
-TEST(StringPieceTest, PiecesHaveCorrectSortOrderUtf8) {
- std::string testing("testing");
- std::string banana("banana");
- std::string car("car");
-
- EXPECT_TRUE(StringPiece(testing) > banana);
- EXPECT_TRUE(StringPiece(testing) > car);
- EXPECT_TRUE(StringPiece(banana) < testing);
- EXPECT_TRUE(StringPiece(banana) < car);
- EXPECT_TRUE(StringPiece(car) < testing);
- EXPECT_TRUE(StringPiece(car) > banana);
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/StringPool.cpp b/tools/aapt2/StringPool.cpp
index c19aa98..8552f47 100644
--- a/tools/aapt2/StringPool.cpp
+++ b/tools/aapt2/StringPool.cpp
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-#include "BigBuffer.h"
-#include "StringPiece.h"
+#include "util/BigBuffer.h"
+#include "util/StringPiece.h"
#include "StringPool.h"
-#include "Util.h"
+#include "util/Util.h"
#include <algorithm>
#include <androidfw/ResourceTypes.h>
@@ -219,7 +219,7 @@
auto indexIter = std::begin(mIndexedStrings);
while (indexIter != iterEnd) {
if (indexIter->second->ref <= 0) {
- mIndexedStrings.erase(indexIter++);
+ indexIter = mIndexedStrings.erase(indexIter);
} else {
++indexIter;
}
@@ -241,6 +241,12 @@
// a deleted string from the StyleEntry.
mStrings.erase(endIter2, std::end(mStrings));
mStyles.erase(endIter3, std::end(mStyles));
+
+ // Reassign the indices.
+ const size_t len = mStrings.size();
+ for (size_t index = 0; index < len; index++) {
+ mStrings[index]->index = index;
+ }
}
void StringPool::sort(const std::function<bool(const Entry&, const Entry&)>& cmp) {
diff --git a/tools/aapt2/StringPool.h b/tools/aapt2/StringPool.h
index 14304a6..509e304 100644
--- a/tools/aapt2/StringPool.h
+++ b/tools/aapt2/StringPool.h
@@ -17,9 +17,9 @@
#ifndef AAPT_STRING_POOL_H
#define AAPT_STRING_POOL_H
-#include "BigBuffer.h"
+#include "util/BigBuffer.h"
#include "ConfigDescription.h"
-#include "StringPiece.h"
+#include "util/StringPiece.h"
#include <functional>
#include <map>
diff --git a/tools/aapt2/StringPool_test.cpp b/tools/aapt2/StringPool_test.cpp
index 9552937..c722fbe 100644
--- a/tools/aapt2/StringPool_test.cpp
+++ b/tools/aapt2/StringPool_test.cpp
@@ -15,7 +15,7 @@
*/
#include "StringPool.h"
-#include "Util.h"
+#include "util/Util.h"
#include <gtest/gtest.h>
#include <string>
@@ -67,15 +67,23 @@
TEST(StringPoolTest, PruneStringsWithNoReferences) {
StringPool pool;
+ StringPool::Ref refA = pool.makeRef(u"foo");
{
StringPool::Ref ref = pool.makeRef(u"wut");
EXPECT_EQ(*ref, u"wut");
- EXPECT_EQ(1u, pool.size());
+ EXPECT_EQ(2u, pool.size());
}
+ StringPool::Ref refB = pool.makeRef(u"bar");
- EXPECT_EQ(1u, pool.size());
+ EXPECT_EQ(3u, pool.size());
pool.prune();
- EXPECT_EQ(0u, pool.size());
+ EXPECT_EQ(2u, pool.size());
+ StringPool::const_iterator iter = begin(pool);
+ EXPECT_EQ((*iter)->value, u"foo");
+ EXPECT_LT((*iter)->index, 2u);
+ ++iter;
+ EXPECT_EQ((*iter)->value, u"bar");
+ EXPECT_LT((*iter)->index, 2u);
}
TEST(StringPoolTest, SortAndMaintainIndexesInReferences) {
diff --git a/tools/aapt2/TableFlattener.cpp b/tools/aapt2/TableFlattener.cpp
deleted file mode 100644
index b7c04f0..0000000
--- a/tools/aapt2/TableFlattener.cpp
+++ /dev/null
@@ -1,570 +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.
- */
-
-#include "BigBuffer.h"
-#include "ConfigDescription.h"
-#include "Logger.h"
-#include "ResourceTable.h"
-#include "ResourceTypeExtensions.h"
-#include "ResourceValues.h"
-#include "StringPool.h"
-#include "TableFlattener.h"
-#include "Util.h"
-
-#include <algorithm>
-#include <androidfw/ResourceTypes.h>
-#include <sstream>
-
-namespace aapt {
-
-struct FlatEntry {
- const ResourceEntry* entry;
- const Value* value;
- uint32_t entryKey;
- uint32_t sourcePathKey;
- uint32_t sourceLine;
-};
-
-/**
- * Visitor that knows how to encode Map values.
- */
-class MapFlattener : public ConstValueVisitor {
-public:
- MapFlattener(BigBuffer* out, const FlatEntry& flatEntry, SymbolEntryVector* symbols) :
- mOut(out), mSymbols(symbols) {
- mMap = mOut->nextBlock<android::ResTable_map_entry>();
- mMap->key.index = flatEntry.entryKey;
- mMap->flags = android::ResTable_entry::FLAG_COMPLEX;
- if (flatEntry.entry->publicStatus.isPublic) {
- mMap->flags |= android::ResTable_entry::FLAG_PUBLIC;
- }
- if (flatEntry.value->isWeak()) {
- mMap->flags |= android::ResTable_entry::FLAG_WEAK;
- }
-
- ResTable_entry_source* sourceBlock = mOut->nextBlock<ResTable_entry_source>();
- sourceBlock->pathIndex = flatEntry.sourcePathKey;
- sourceBlock->line = flatEntry.sourceLine;
-
- mMap->size = sizeof(*mMap) + sizeof(*sourceBlock);
- }
-
- void flattenParent(const Reference& ref) {
- if (!ref.id.isValid()) {
- mSymbols->push_back({
- ResourceNameRef(ref.name),
- (mOut->size() - mMap->size) + sizeof(*mMap) - sizeof(android::ResTable_entry)
- });
- }
- mMap->parent.ident = ref.id.id;
- }
-
- void flattenEntry(const Reference& key, const Item& value) {
- mMap->count++;
-
- android::ResTable_map* outMapEntry = mOut->nextBlock<android::ResTable_map>();
-
- // Write the key.
- if (!Res_INTERNALID(key.id.id) && !key.id.isValid()) {
- assert(!key.name.entry.empty());
- mSymbols->push_back(std::make_pair(ResourceNameRef(key.name),
- mOut->size() - sizeof(*outMapEntry)));
- }
- outMapEntry->name.ident = key.id.id;
-
- // Write the value.
- value.flatten(outMapEntry->value);
-
- if (outMapEntry->value.data == 0x0) {
- visitFunc<Reference>(value, [&](const Reference& reference) {
- mSymbols->push_back(std::make_pair(ResourceNameRef(reference.name),
- mOut->size() - sizeof(outMapEntry->value.data)));
- });
- }
- outMapEntry->value.size = sizeof(outMapEntry->value);
- }
-
- void flattenValueOnly(const Item& value) {
- mMap->count++;
-
- android::ResTable_map* outMapEntry = mOut->nextBlock<android::ResTable_map>();
-
- // Write the value.
- value.flatten(outMapEntry->value);
-
- if (outMapEntry->value.data == 0x0) {
- visitFunc<Reference>(value, [&](const Reference& reference) {
- mSymbols->push_back(std::make_pair(ResourceNameRef(reference.name),
- mOut->size() - sizeof(outMapEntry->value.data)));
- });
- }
- outMapEntry->value.size = sizeof(outMapEntry->value);
- }
-
- static bool compareStyleEntries(const Style::Entry* lhs, const Style::Entry* rhs) {
- return lhs->key.id < rhs->key.id;
- }
-
- void visit(const Style& style, ValueVisitorArgs&) override {
- if (style.parent.name.isValid()) {
- flattenParent(style.parent);
- }
-
- // First sort the entries by ID.
- std::vector<const Style::Entry*> sortedEntries;
- for (const auto& styleEntry : style.entries) {
- auto iter = std::lower_bound(sortedEntries.begin(), sortedEntries.end(),
- &styleEntry, compareStyleEntries);
- sortedEntries.insert(iter, &styleEntry);
- }
-
- for (const Style::Entry* styleEntry : sortedEntries) {
- flattenEntry(styleEntry->key, *styleEntry->value);
- }
- }
-
- void visit(const Attribute& attr, ValueVisitorArgs&) override {
- android::Res_value tempVal;
- tempVal.dataType = android::Res_value::TYPE_INT_DEC;
- tempVal.data = attr.typeMask;
- flattenEntry(Reference(ResourceId{android::ResTable_map::ATTR_TYPE}),
- BinaryPrimitive(tempVal));
-
- for (const auto& symbol : attr.symbols) {
- tempVal.data = symbol.value;
- flattenEntry(symbol.symbol, BinaryPrimitive(tempVal));
- }
- }
-
- void visit(const Styleable& styleable, ValueVisitorArgs&) override {
- for (const auto& attr : styleable.entries) {
- flattenEntry(attr, BinaryPrimitive(android::Res_value{}));
- }
- }
-
- void visit(const Array& array, ValueVisitorArgs&) override {
- for (const auto& item : array.items) {
- flattenValueOnly(*item);
- }
- }
-
- void visit(const Plural& plural, ValueVisitorArgs&) override {
- const size_t count = plural.values.size();
- for (size_t i = 0; i < count; i++) {
- if (!plural.values[i]) {
- continue;
- }
-
- ResourceId q;
- switch (i) {
- case Plural::Zero:
- q.id = android::ResTable_map::ATTR_ZERO;
- break;
-
- case Plural::One:
- q.id = android::ResTable_map::ATTR_ONE;
- break;
-
- case Plural::Two:
- q.id = android::ResTable_map::ATTR_TWO;
- break;
-
- case Plural::Few:
- q.id = android::ResTable_map::ATTR_FEW;
- break;
-
- case Plural::Many:
- q.id = android::ResTable_map::ATTR_MANY;
- break;
-
- case Plural::Other:
- q.id = android::ResTable_map::ATTR_OTHER;
- break;
-
- default:
- assert(false);
- break;
- }
-
- flattenEntry(Reference(q), *plural.values[i]);
- }
- }
-
-private:
- BigBuffer* mOut;
- SymbolEntryVector* mSymbols;
- android::ResTable_map_entry* mMap;
-};
-
-/**
- * Flattens a value, with special handling for References.
- */
-struct ValueFlattener : ConstValueVisitor {
- ValueFlattener(BigBuffer* out, SymbolEntryVector* symbols) :
- result(false), mOut(out), mOutValue(nullptr), mSymbols(symbols) {
- mOutValue = mOut->nextBlock<android::Res_value>();
- }
-
- virtual void visit(const Reference& ref, ValueVisitorArgs& a) override {
- visitItem(ref, a);
- if (mOutValue->data == 0x0) {
- mSymbols->push_back({
- ResourceNameRef(ref.name),
- mOut->size() - sizeof(mOutValue->data)});
- }
- }
-
- virtual void visitItem(const Item& item, ValueVisitorArgs&) override {
- result = item.flatten(*mOutValue);
- mOutValue->res0 = 0;
- mOutValue->size = sizeof(*mOutValue);
- }
-
- bool result;
-
-private:
- BigBuffer* mOut;
- android::Res_value* mOutValue;
- SymbolEntryVector* mSymbols;
-};
-
-TableFlattener::TableFlattener(Options options)
-: mOptions(options) {
-}
-
-bool TableFlattener::flattenValue(BigBuffer* out, const FlatEntry& flatEntry,
- SymbolEntryVector* symbols) {
- if (flatEntry.value->isItem()) {
- android::ResTable_entry* entry = out->nextBlock<android::ResTable_entry>();
-
- if (flatEntry.entry->publicStatus.isPublic) {
- entry->flags |= android::ResTable_entry::FLAG_PUBLIC;
- }
-
- if (flatEntry.value->isWeak()) {
- entry->flags |= android::ResTable_entry::FLAG_WEAK;
- }
-
- entry->key.index = flatEntry.entryKey;
- entry->size = sizeof(*entry);
-
- if (mOptions.useExtendedChunks) {
- // Write the extra source block. This will be ignored by
- // the Android runtime.
- ResTable_entry_source* sourceBlock = out->nextBlock<ResTable_entry_source>();
- sourceBlock->pathIndex = flatEntry.sourcePathKey;
- sourceBlock->line = flatEntry.sourceLine;
- entry->size += sizeof(*sourceBlock);
- }
-
- const Item* item = static_cast<const Item*>(flatEntry.value);
- ValueFlattener flattener(out, symbols);
- item->accept(flattener, {});
- return flattener.result;
- }
-
- MapFlattener flattener(out, flatEntry, symbols);
- flatEntry.value->accept(flattener, {});
- return true;
-}
-
-bool TableFlattener::flatten(BigBuffer* out, const ResourceTable& table) {
- const size_t beginning = out->size();
-
- if (table.getPackageId() == ResourceTable::kUnsetPackageId) {
- Logger::error()
- << "ResourceTable has no package ID set."
- << std::endl;
- return false;
- }
-
- SymbolEntryVector symbolEntries;
-
- StringPool typePool;
- StringPool keyPool;
- StringPool sourcePool;
-
- // Sort the types by their IDs. They will be inserted into the StringPool
- // in this order.
- std::vector<ResourceTableType*> sortedTypes;
- for (const auto& type : table) {
- if (type->type == ResourceType::kStyleable && !mOptions.useExtendedChunks) {
- continue;
- }
-
- auto iter = std::lower_bound(std::begin(sortedTypes), std::end(sortedTypes), type.get(),
- [](const ResourceTableType* lhs, const ResourceTableType* rhs) -> bool {
- return lhs->typeId < rhs->typeId;
- });
- sortedTypes.insert(iter, type.get());
- }
-
- BigBuffer typeBlock(1024);
- size_t expectedTypeId = 1;
- for (const ResourceTableType* type : sortedTypes) {
- if (type->typeId == ResourceTableType::kUnsetTypeId
- || type->typeId == 0) {
- Logger::error()
- << "resource type '"
- << type->type
- << "' from package '"
- << table.getPackage()
- << "' has no ID."
- << std::endl;
- return false;
- }
-
- // If there is a gap in the type IDs, fill in the StringPool
- // with empty values until we reach the ID we expect.
- while (type->typeId > expectedTypeId) {
- std::u16string typeName(u"?");
- typeName += expectedTypeId;
- typePool.makeRef(typeName);
- expectedTypeId++;
- }
- expectedTypeId++;
- typePool.makeRef(toString(type->type));
-
- android::ResTable_typeSpec* spec = typeBlock.nextBlock<android::ResTable_typeSpec>();
- spec->header.type = android::RES_TABLE_TYPE_SPEC_TYPE;
- spec->header.headerSize = sizeof(*spec);
- spec->header.size = spec->header.headerSize + (type->entries.size() * sizeof(uint32_t));
- spec->id = type->typeId;
- spec->entryCount = type->entries.size();
-
- if (type->entries.empty()) {
- continue;
- }
-
- // Reserve space for the masks of each resource in this type. These
- // show for which configuration axis the resource changes.
- uint32_t* configMasks = typeBlock.nextBlock<uint32_t>(type->entries.size());
-
- // Sort the entries by entry ID and write their configuration masks.
- std::vector<ResourceEntry*> entries;
- const size_t entryCount = type->entries.size();
- for (size_t entryIndex = 0; entryIndex < entryCount; entryIndex++) {
- const auto& entry = type->entries[entryIndex];
-
- if (entry->entryId == ResourceEntry::kUnsetEntryId) {
- Logger::error()
- << "resource '"
- << ResourceName{ table.getPackage(), type->type, entry->name }
- << "' has no ID."
- << std::endl;
- return false;
- }
-
- auto iter = std::lower_bound(std::begin(entries), std::end(entries), entry.get(),
- [](const ResourceEntry* lhs, const ResourceEntry* rhs) -> bool {
- return lhs->entryId < rhs->entryId;
- });
- entries.insert(iter, entry.get());
-
- // Populate the config masks for this entry.
- if (entry->publicStatus.isPublic) {
- configMasks[entry->entryId] |= android::ResTable_typeSpec::SPEC_PUBLIC;
- }
-
- const size_t configCount = entry->values.size();
- for (size_t i = 0; i < configCount; i++) {
- const ConfigDescription& config = entry->values[i].config;
- for (size_t j = i + 1; j < configCount; j++) {
- configMasks[entry->entryId] |= config.diff(entry->values[j].config);
- }
- }
- }
-
- const size_t beforePublicHeader = typeBlock.size();
- Public_header* publicHeader = nullptr;
- if (mOptions.useExtendedChunks) {
- publicHeader = typeBlock.nextBlock<Public_header>();
- publicHeader->header.type = RES_TABLE_PUBLIC_TYPE;
- publicHeader->header.headerSize = sizeof(*publicHeader);
- publicHeader->typeId = type->typeId;
- }
-
- // The binary resource table lists resource entries for each configuration.
- // We store them inverted, where a resource entry lists the values for each
- // configuration available. Here we reverse this to match the binary table.
- std::map<ConfigDescription, std::vector<FlatEntry>> data;
- for (const ResourceEntry* entry : entries) {
- size_t keyIndex = keyPool.makeRef(entry->name).getIndex();
-
- if (keyIndex > std::numeric_limits<uint32_t>::max()) {
- Logger::error()
- << "resource key string pool exceeded max size."
- << std::endl;
- return false;
- }
-
- if (publicHeader && entry->publicStatus.isPublic) {
- // Write the public status of this entry.
- Public_entry* publicEntry = typeBlock.nextBlock<Public_entry>();
- publicEntry->entryId = static_cast<uint32_t>(entry->entryId);
- publicEntry->key.index = static_cast<uint32_t>(keyIndex);
- publicEntry->source.index = static_cast<uint32_t>(sourcePool.makeRef(
- util::utf8ToUtf16(entry->publicStatus.source.path)).getIndex());
- publicEntry->sourceLine = static_cast<uint32_t>(entry->publicStatus.source.line);
- publicHeader->count += 1;
- }
-
- for (const auto& configValue : entry->values) {
- data[configValue.config].push_back(FlatEntry{
- entry,
- configValue.value.get(),
- static_cast<uint32_t>(keyIndex),
- static_cast<uint32_t>(sourcePool.makeRef(util::utf8ToUtf16(
- configValue.source.path)).getIndex()),
- static_cast<uint32_t>(configValue.source.line)
- });
- }
- }
-
- if (publicHeader) {
- typeBlock.align4();
- publicHeader->header.size =
- static_cast<uint32_t>(typeBlock.size() - beforePublicHeader);
- }
-
- // Begin flattening a configuration for the current type.
- for (const auto& entry : data) {
- const size_t typeHeaderStart = typeBlock.size();
- android::ResTable_type* typeHeader = typeBlock.nextBlock<android::ResTable_type>();
- typeHeader->header.type = android::RES_TABLE_TYPE_TYPE;
- typeHeader->header.headerSize = sizeof(*typeHeader);
- typeHeader->id = type->typeId;
- typeHeader->entryCount = type->entries.size();
- typeHeader->entriesStart = typeHeader->header.headerSize
- + (sizeof(uint32_t) * type->entries.size());
- typeHeader->config = entry.first;
-
- uint32_t* indices = typeBlock.nextBlock<uint32_t>(type->entries.size());
- memset(indices, 0xff, type->entries.size() * sizeof(uint32_t));
-
- const size_t entryStart = typeBlock.size();
- for (const FlatEntry& flatEntry : entry.second) {
- assert(flatEntry.entry->entryId < type->entries.size());
- indices[flatEntry.entry->entryId] = typeBlock.size() - entryStart;
- if (!flattenValue(&typeBlock, flatEntry, &symbolEntries)) {
- Logger::error()
- << "failed to flatten resource '"
- << ResourceNameRef {
- table.getPackage(), type->type, flatEntry.entry->name }
- << "' for configuration '"
- << entry.first
- << "'."
- << std::endl;
- return false;
- }
- }
-
- typeBlock.align4();
- typeHeader->header.size = typeBlock.size() - typeHeaderStart;
- }
- }
-
- const size_t beforeTable = out->size();
- android::ResTable_header* header = out->nextBlock<android::ResTable_header>();
- header->header.type = android::RES_TABLE_TYPE;
- header->header.headerSize = sizeof(*header);
- header->packageCount = 1;
-
- SymbolTable_entry* symbolEntryData = nullptr;
- if (!symbolEntries.empty() && mOptions.useExtendedChunks) {
- const size_t beforeSymbolTable = out->size();
- StringPool symbolPool;
- SymbolTable_header* symbolHeader = out->nextBlock<SymbolTable_header>();
- symbolHeader->header.type = RES_TABLE_SYMBOL_TABLE_TYPE;
- symbolHeader->header.headerSize = sizeof(*symbolHeader);
- symbolHeader->count = symbolEntries.size();
-
- symbolEntryData = out->nextBlock<SymbolTable_entry>(symbolHeader->count);
-
- size_t i = 0;
- for (const auto& entry : symbolEntries) {
- symbolEntryData[i].offset = entry.second;
- StringPool::Ref ref = symbolPool.makeRef(
- entry.first.package.toString() + u":" +
- toString(entry.first.type).toString() + u"/" +
- entry.first.entry.toString());
- symbolEntryData[i].stringIndex = ref.getIndex();
- i++;
- }
-
- StringPool::flattenUtf8(out, symbolPool);
- out->align4();
- symbolHeader->header.size = out->size() - beforeSymbolTable;
- }
-
- if (sourcePool.size() > 0 && mOptions.useExtendedChunks) {
- const size_t beforeSourcePool = out->size();
- android::ResChunk_header* sourceHeader = out->nextBlock<android::ResChunk_header>();
- sourceHeader->type = RES_TABLE_SOURCE_POOL_TYPE;
- sourceHeader->headerSize = sizeof(*sourceHeader);
- StringPool::flattenUtf8(out, sourcePool);
- out->align4();
- sourceHeader->size = out->size() - beforeSourcePool;
- }
-
- StringPool::flattenUtf8(out, table.getValueStringPool());
-
- const size_t beforePackageIndex = out->size();
- android::ResTable_package* package = out->nextBlock<android::ResTable_package>();
- package->header.type = android::RES_TABLE_PACKAGE_TYPE;
- package->header.headerSize = sizeof(*package);
-
- if (table.getPackageId() > std::numeric_limits<uint8_t>::max()) {
- Logger::error()
- << "package ID 0x'"
- << std::hex << table.getPackageId() << std::dec
- << "' is invalid."
- << std::endl;
- return false;
- }
- package->id = table.getPackageId();
-
- if (table.getPackage().size() >= sizeof(package->name) / sizeof(package->name[0])) {
- Logger::error()
- << "package name '"
- << table.getPackage()
- << "' is too long."
- << std::endl;
- return false;
- }
- memcpy(package->name, reinterpret_cast<const uint16_t*>(table.getPackage().data()),
- table.getPackage().length() * sizeof(char16_t));
- package->name[table.getPackage().length()] = 0;
-
- package->typeStrings = package->header.headerSize;
- StringPool::flattenUtf16(out, typePool);
- package->keyStrings = out->size() - beforePackageIndex;
- StringPool::flattenUtf16(out, keyPool);
-
- if (symbolEntryData != nullptr) {
- for (size_t i = 0; i < symbolEntries.size(); i++) {
- symbolEntryData[i].offset += out->size() - beginning;
- }
- }
-
- out->appendBuffer(std::move(typeBlock));
-
- package->header.size = out->size() - beforePackageIndex;
- header->header.size = out->size() - beforeTable;
- return true;
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/TableFlattener.h b/tools/aapt2/TableFlattener.h
deleted file mode 100644
index ccbb737..0000000
--- a/tools/aapt2/TableFlattener.h
+++ /dev/null
@@ -1,61 +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_TABLE_FLATTENER_H
-#define AAPT_TABLE_FLATTENER_H
-
-#include "BigBuffer.h"
-#include "ResourceTable.h"
-
-namespace aapt {
-
-using SymbolEntryVector = std::vector<std::pair<ResourceNameRef, uint32_t>>;
-
-struct FlatEntry;
-
-/**
- * Flattens a ResourceTable into a binary format suitable
- * for loading into a ResTable on the host or device.
- */
-struct TableFlattener {
- /**
- * A set of options for this TableFlattener.
- */
- struct Options {
- /**
- * Specifies whether to output extended chunks, like
- * source information and mising symbol entries. Default
- * is true.
- *
- * Set this to false when emitting the final table to be used
- * on device.
- */
- bool useExtendedChunks = true;
- };
-
- TableFlattener(Options options);
-
- bool flatten(BigBuffer* out, const ResourceTable& table);
-
-private:
- bool flattenValue(BigBuffer* out, const FlatEntry& flatEntry, SymbolEntryVector* symbols);
-
- Options mOptions;
-};
-
-} // namespace aapt
-
-#endif // AAPT_TABLE_FLATTENER_H
diff --git a/tools/aapt2/Util.cpp b/tools/aapt2/Util.cpp
deleted file mode 100644
index ca352e0..0000000
--- a/tools/aapt2/Util.cpp
+++ /dev/null
@@ -1,342 +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.
- */
-
-#include "BigBuffer.h"
-#include "Maybe.h"
-#include "StringPiece.h"
-#include "Util.h"
-
-#include <algorithm>
-#include <ostream>
-#include <string>
-#include <utils/Unicode.h>
-#include <vector>
-
-namespace aapt {
-namespace util {
-
-constexpr const char16_t* kSchemaAuto = u"http://schemas.android.com/apk/res-auto";
-constexpr const char16_t* kSchemaPrefix = u"http://schemas.android.com/apk/res/";
-
-static std::vector<std::string> splitAndTransform(const StringPiece& str, char sep,
- const std::function<char(char)>& f) {
- std::vector<std::string> parts;
- const StringPiece::const_iterator end = std::end(str);
- StringPiece::const_iterator start = std::begin(str);
- StringPiece::const_iterator current;
- do {
- current = std::find(start, end, sep);
- parts.emplace_back(str.substr(start, current).toString());
- if (f) {
- std::string& part = parts.back();
- std::transform(part.begin(), part.end(), part.begin(), f);
- }
- start = current + 1;
- } while (current != end);
- return parts;
-}
-
-std::vector<std::string> split(const StringPiece& str, char sep) {
- return splitAndTransform(str, sep, nullptr);
-}
-
-std::vector<std::string> splitAndLowercase(const StringPiece& str, char sep) {
- return splitAndTransform(str, sep, ::tolower);
-}
-
-StringPiece16 trimWhitespace(const StringPiece16& str) {
- if (str.size() == 0 || str.data() == nullptr) {
- return str;
- }
-
- const char16_t* start = str.data();
- const char16_t* end = str.data() + str.length();
-
- while (start != end && util::isspace16(*start)) {
- start++;
- }
-
- while (end != start && util::isspace16(*(end - 1))) {
- end--;
- }
-
- return StringPiece16(start, end - start);
-}
-
-StringPiece16::const_iterator findNonAlphaNumericAndNotInSet(const StringPiece16& str,
- const StringPiece16& allowedChars) {
- const auto endIter = str.end();
- for (auto iter = str.begin(); iter != endIter; ++iter) {
- char16_t c = *iter;
- if ((c >= u'a' && c <= u'z') ||
- (c >= u'A' && c <= u'Z') ||
- (c >= u'0' && c <= u'9')) {
- continue;
- }
-
- bool match = false;
- for (char16_t i : allowedChars) {
- if (c == i) {
- match = true;
- break;
- }
- }
-
- if (!match) {
- return iter;
- }
- }
- return endIter;
-}
-
-bool isJavaClassName(const StringPiece16& str) {
- size_t pieces = 0;
- for (const StringPiece16& piece : tokenize(str, u'.')) {
- pieces++;
- if (piece.empty()) {
- return false;
- }
-
- // Can't have starting or trailing $ character.
- if (piece.data()[0] == u'$' || piece.data()[piece.size() - 1] == u'$') {
- return false;
- }
-
- if (findNonAlphaNumericAndNotInSet(piece, u"$_") != piece.end()) {
- return false;
- }
- }
- return pieces >= 2;
-}
-
-Maybe<std::u16string> getFullyQualifiedClassName(const StringPiece16& package,
- const StringPiece16& className) {
- if (className.empty()) {
- return {};
- }
-
- if (util::isJavaClassName(className)) {
- return className.toString();
- }
-
- if (package.empty()) {
- return {};
- }
-
- std::u16string result(package.data(), package.size());
- if (className.data()[0] != u'.') {
- result += u'.';
- }
- result.append(className.data(), className.size());
- if (!isJavaClassName(result)) {
- return {};
- }
- return result;
-}
-
-static Maybe<char16_t> parseUnicodeCodepoint(const char16_t** start, const char16_t* end) {
- char16_t code = 0;
- for (size_t i = 0; i < 4 && *start != end; i++, (*start)++) {
- char16_t c = **start;
- int a;
- if (c >= '0' && c <= '9') {
- a = c - '0';
- } else if (c >= 'a' && c <= 'f') {
- a = c - 'a' + 10;
- } else if (c >= 'A' && c <= 'F') {
- a = c - 'A' + 10;
- } else {
- return make_nothing<char16_t>();
- }
- code = (code << 4) | a;
- }
- return make_value(code);
-}
-
-StringBuilder& StringBuilder::append(const StringPiece16& str) {
- if (!mError.empty()) {
- return *this;
- }
-
- const char16_t* const end = str.end();
- const char16_t* start = str.begin();
- const char16_t* current = start;
- while (current != end) {
- if (mLastCharWasEscape) {
- switch (*current) {
- case u't':
- mStr += u'\t';
- break;
- case u'n':
- mStr += u'\n';
- break;
- case u'#':
- mStr += u'#';
- break;
- case u'@':
- mStr += u'@';
- break;
- case u'?':
- mStr += u'?';
- break;
- case u'"':
- mStr += u'"';
- break;
- case u'\'':
- mStr += u'\'';
- break;
- case u'\\':
- mStr += u'\\';
- break;
- case u'u': {
- current++;
- Maybe<char16_t> c = parseUnicodeCodepoint(¤t, end);
- if (!c) {
- mError = "invalid unicode escape sequence";
- return *this;
- }
- mStr += c.value();
- current -= 1;
- break;
- }
-
- default:
- // Ignore.
- break;
- }
- mLastCharWasEscape = false;
- start = current + 1;
- } else if (*current == u'"') {
- if (!mQuote && mTrailingSpace) {
- // We found an opening quote, and we have
- // trailing space, so we should append that
- // space now.
- if (mTrailingSpace) {
- // We had trailing whitespace, so
- // replace with a single space.
- if (!mStr.empty()) {
- mStr += u' ';
- }
- mTrailingSpace = false;
- }
- }
- mQuote = !mQuote;
- mStr.append(start, current - start);
- start = current + 1;
- } else if (*current == u'\'' && !mQuote) {
- // This should be escaped.
- mError = "unescaped apostrophe";
- return *this;
- } else if (*current == u'\\') {
- // This is an escape sequence, convert to the real value.
- if (!mQuote && mTrailingSpace) {
- // We had trailing whitespace, so
- // replace with a single space.
- if (!mStr.empty()) {
- mStr += u' ';
- }
- mTrailingSpace = false;
- }
- mStr.append(start, current - start);
- start = current + 1;
- mLastCharWasEscape = true;
- } else if (!mQuote) {
- // This is not quoted text, so look for whitespace.
- if (isspace16(*current)) {
- // We found whitespace, see if we have seen some
- // before.
- if (!mTrailingSpace) {
- // We didn't see a previous adjacent space,
- // so mark that we did.
- mTrailingSpace = true;
- mStr.append(start, current - start);
- }
-
- // Keep skipping whitespace.
- start = current + 1;
- } else if (mTrailingSpace) {
- // We saw trailing space before, so replace all
- // that trailing space with one space.
- if (!mStr.empty()) {
- mStr += u' ';
- }
- mTrailingSpace = false;
- }
- }
- current++;
- }
- mStr.append(start, end - start);
- return *this;
-}
-
-std::u16string utf8ToUtf16(const StringPiece& utf8) {
- ssize_t utf16Length = utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(utf8.data()),
- utf8.length());
- if (utf16Length <= 0) {
- return {};
- }
-
- std::u16string utf16;
- utf16.resize(utf16Length);
- utf8_to_utf16(reinterpret_cast<const uint8_t*>(utf8.data()), utf8.length(), &*utf16.begin());
- return utf16;
-}
-
-std::string utf16ToUtf8(const StringPiece16& utf16) {
- ssize_t utf8Length = utf16_to_utf8_length(utf16.data(), utf16.length());
- if (utf8Length <= 0) {
- return {};
- }
-
- std::string utf8;
- utf8.resize(utf8Length);
- utf16_to_utf8(utf16.data(), utf16.length(), &*utf8.begin());
- return utf8;
-}
-
-bool writeAll(std::ostream& out, const BigBuffer& buffer) {
- for (const auto& b : buffer) {
- if (!out.write(reinterpret_cast<const char*>(b.buffer.get()), b.size)) {
- return false;
- }
- }
- return true;
-}
-
-std::unique_ptr<uint8_t[]> copy(const BigBuffer& buffer) {
- std::unique_ptr<uint8_t[]> data = std::unique_ptr<uint8_t[]>(new uint8_t[buffer.size()]);
- uint8_t* p = data.get();
- for (const auto& block : buffer) {
- memcpy(p, block.buffer.get(), block.size);
- p += block.size;
- }
- return data;
-}
-
-Maybe<std::u16string> extractPackageFromNamespace(const std::u16string& namespaceUri) {
- if (stringStartsWith<char16_t>(namespaceUri, kSchemaPrefix)) {
- StringPiece16 schemaPrefix = kSchemaPrefix;
- StringPiece16 package = namespaceUri;
- return package.substr(schemaPrefix.size(), package.size() - schemaPrefix.size())
- .toString();
- } else if (namespaceUri == kSchemaAuto) {
- return std::u16string();
- }
- return {};
-}
-
-} // namespace util
-} // namespace aapt
diff --git a/tools/aapt2/Util.h b/tools/aapt2/Util.h
deleted file mode 100644
index 7ec6b03..0000000
--- a/tools/aapt2/Util.h
+++ /dev/null
@@ -1,321 +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_UTIL_H
-#define AAPT_UTIL_H
-
-#include "BigBuffer.h"
-#include "Maybe.h"
-#include "StringPiece.h"
-
-#include <androidfw/ResourceTypes.h>
-#include <functional>
-#include <memory>
-#include <ostream>
-#include <string>
-#include <vector>
-
-namespace aapt {
-namespace util {
-
-std::vector<std::string> split(const StringPiece& str, char sep);
-std::vector<std::string> splitAndLowercase(const StringPiece& str, char sep);
-
-/**
- * Returns true if the string starts with prefix.
- */
-template <typename T>
-bool stringStartsWith(const BasicStringPiece<T>& str, const BasicStringPiece<T>& prefix) {
- if (str.size() < prefix.size()) {
- return false;
- }
- return str.substr(0, prefix.size()) == prefix;
-}
-
-/**
- * Returns true if the string ends with suffix.
- */
-template <typename T>
-bool stringEndsWith(const BasicStringPiece<T>& str, const BasicStringPiece<T>& suffix) {
- if (str.size() < suffix.size()) {
- return false;
- }
- return str.substr(str.size() - suffix.size(), suffix.size()) == suffix;
-}
-
-/**
- * Creates a new StringPiece16 that points to a substring
- * of the original string without leading or trailing whitespace.
- */
-StringPiece16 trimWhitespace(const StringPiece16& str);
-
-/**
- * UTF-16 isspace(). It basically checks for lower range characters that are
- * whitespace.
- */
-inline bool isspace16(char16_t c) {
- return c < 0x0080 && isspace(c);
-}
-
-/**
- * Returns an iterator to the first character that is not alpha-numeric and that
- * is not in the allowedChars set.
- */
-StringPiece16::const_iterator findNonAlphaNumericAndNotInSet(const StringPiece16& str,
- const StringPiece16& allowedChars);
-
-/**
- * Tests that the string is a valid Java class name.
- */
-bool isJavaClassName(const StringPiece16& str);
-
-/**
- * Converts the class name to a fully qualified class name from the given `package`. Ex:
- *
- * asdf --> package.asdf
- * .asdf --> package.asdf
- * .a.b --> package.a.b
- * asdf.adsf --> asdf.adsf
- */
-Maybe<std::u16string> getFullyQualifiedClassName(const StringPiece16& package,
- const StringPiece16& className);
-
-
-/**
- * Makes a std::unique_ptr<> with the template parameter inferred by the compiler.
- * This will be present in C++14 and can be removed then.
- */
-template <typename T, class... Args>
-std::unique_ptr<T> make_unique(Args&&... args) {
- return std::unique_ptr<T>(new T{std::forward<Args>(args)...});
-}
-
-/**
- * Writes a set of items to the std::ostream, joining the times with the provided
- * separator.
- */
-template <typename Iterator>
-::std::function<::std::ostream&(::std::ostream&)> joiner(Iterator begin, Iterator end,
- const char* sep) {
- return [begin, end, sep](::std::ostream& out) -> ::std::ostream& {
- for (auto iter = begin; iter != end; ++iter) {
- if (iter != begin) {
- out << sep;
- }
- out << *iter;
- }
- return out;
- };
-}
-
-inline ::std::function<::std::ostream&(::std::ostream&)> formatSize(size_t size) {
- return [size](::std::ostream& out) -> ::std::ostream& {
- constexpr size_t K = 1024u;
- constexpr size_t M = K * K;
- constexpr size_t G = M * K;
- if (size < K) {
- out << size << "B";
- } else if (size < M) {
- out << (double(size) / K) << " KiB";
- } else if (size < G) {
- out << (double(size) / M) << " MiB";
- } else {
- out << (double(size) / G) << " GiB";
- }
- return out;
- };
-}
-
-/**
- * Helper method to extract a string from a StringPool.
- */
-inline StringPiece16 getString(const android::ResStringPool& pool, size_t idx) {
- size_t len;
- const char16_t* str = pool.stringAt(idx, &len);
- if (str != nullptr) {
- return StringPiece16(str, len);
- }
- return StringPiece16();
-}
-
-class StringBuilder {
-public:
- StringBuilder& append(const StringPiece16& str);
- const std::u16string& str() const;
- const std::string& error() const;
- operator bool() const;
-
-private:
- std::u16string mStr;
- bool mQuote = false;
- bool mTrailingSpace = false;
- bool mLastCharWasEscape = false;
- std::string mError;
-};
-
-inline const std::u16string& StringBuilder::str() const {
- return mStr;
-}
-
-inline const std::string& StringBuilder::error() const {
- return mError;
-}
-
-inline StringBuilder::operator bool() const {
- return mError.empty();
-}
-
-/**
- * Converts a UTF8 string to a UTF16 string.
- */
-std::u16string utf8ToUtf16(const StringPiece& utf8);
-std::string utf16ToUtf8(const StringPiece16& utf8);
-
-/**
- * Writes the entire BigBuffer to the output stream.
- */
-bool writeAll(std::ostream& out, const BigBuffer& buffer);
-
-/*
- * Copies the entire BigBuffer into a single buffer.
- */
-std::unique_ptr<uint8_t[]> copy(const BigBuffer& buffer);
-
-/**
- * A Tokenizer implemented as an iterable collection. It does not allocate
- * any memory on the heap nor use standard containers.
- */
-template <typename Char>
-class Tokenizer {
-public:
- class iterator {
- public:
- iterator(const iterator&) = default;
- iterator& operator=(const iterator&) = default;
-
- iterator& operator++();
- BasicStringPiece<Char> operator*();
- bool operator==(const iterator& rhs) const;
- bool operator!=(const iterator& rhs) const;
-
- private:
- friend class Tokenizer<Char>;
-
- iterator(BasicStringPiece<Char> s, Char sep, BasicStringPiece<Char> tok);
-
- BasicStringPiece<Char> str;
- Char separator;
- BasicStringPiece<Char> token;
- };
-
- Tokenizer(BasicStringPiece<Char> str, Char sep);
- iterator begin();
- iterator end();
-
-private:
- const iterator mBegin;
- const iterator mEnd;
-};
-
-template <typename Char>
-inline Tokenizer<Char> tokenize(BasicStringPiece<Char> str, Char sep) {
- return Tokenizer<Char>(str, sep);
-}
-
-template <typename Char>
-typename Tokenizer<Char>::iterator& Tokenizer<Char>::iterator::operator++() {
- const Char* start = token.end();
- const Char* end = str.end();
- if (start == end) {
- token.assign(token.end(), 0);
- return *this;
- }
-
- start += 1;
- const Char* current = start;
- while (current != end) {
- if (*current == separator) {
- token.assign(start, current - start);
- return *this;
- }
- ++current;
- }
- token.assign(start, end - start);
- return *this;
-}
-
-template <typename Char>
-inline BasicStringPiece<Char> Tokenizer<Char>::iterator::operator*() {
- return token;
-}
-
-template <typename Char>
-inline bool Tokenizer<Char>::iterator::operator==(const iterator& rhs) const {
- // We check equality here a bit differently.
- // We need to know that the addresses are the same.
- return token.begin() == rhs.token.begin() && token.end() == rhs.token.end();
-}
-
-template <typename Char>
-inline bool Tokenizer<Char>::iterator::operator!=(const iterator& rhs) const {
- return !(*this == rhs);
-}
-
-template <typename Char>
-inline Tokenizer<Char>::iterator::iterator(BasicStringPiece<Char> s, Char sep,
- BasicStringPiece<Char> tok) :
- str(s), separator(sep), token(tok) {
-}
-
-template <typename Char>
-inline typename Tokenizer<Char>::iterator Tokenizer<Char>::begin() {
- return mBegin;
-}
-
-template <typename Char>
-inline typename Tokenizer<Char>::iterator Tokenizer<Char>::end() {
- return mEnd;
-}
-
-template <typename Char>
-inline Tokenizer<Char>::Tokenizer(BasicStringPiece<Char> str, Char sep) :
- mBegin(++iterator(str, sep, BasicStringPiece<Char>(str.begin() - 1, 0))),
- mEnd(str, sep, BasicStringPiece<Char>(str.end(), 0)) {
-}
-
-/**
- * Returns a package name if the namespace URI is of the form:
- * http://schemas.android.com/apk/res/<package>
- *
- * Special case: if namespaceUri is http://schemas.android.com/apk/res-auto,
- * returns an empty package name.
- */
-Maybe<std::u16string> extractPackageFromNamespace(const std::u16string& namespaceUri);
-
-} // namespace util
-
-/**
- * Stream operator for functions. Calls the function with the stream as an argument.
- * In the aapt namespace for lookup.
- */
-inline ::std::ostream& operator<<(::std::ostream& out,
- ::std::function<::std::ostream&(::std::ostream&)> f) {
- return f(out);
-}
-
-} // namespace aapt
-
-#endif // AAPT_UTIL_H
diff --git a/tools/aapt2/Util_test.cpp b/tools/aapt2/Util_test.cpp
deleted file mode 100644
index 92f2a1c..0000000
--- a/tools/aapt2/Util_test.cpp
+++ /dev/null
@@ -1,143 +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.
- */
-
-#include <gtest/gtest.h>
-#include <string>
-
-#include "StringPiece.h"
-#include "Util.h"
-
-namespace aapt {
-
-TEST(UtilTest, TrimOnlyWhitespace) {
- const std::u16string full = u"\n ";
-
- StringPiece16 trimmed = util::trimWhitespace(full);
- EXPECT_TRUE(trimmed.empty());
- EXPECT_EQ(0u, trimmed.size());
-}
-
-TEST(UtilTest, StringEndsWith) {
- EXPECT_TRUE(util::stringEndsWith<char>("hello.xml", ".xml"));
-}
-
-TEST(UtilTest, StringStartsWith) {
- EXPECT_TRUE(util::stringStartsWith<char>("hello.xml", "he"));
-}
-
-TEST(UtilTest, StringBuilderSplitEscapeSequence) {
- EXPECT_EQ(StringPiece16(u"this is a new\nline."),
- util::StringBuilder().append(u"this is a new\\")
- .append(u"nline.")
- .str());
-}
-
-TEST(UtilTest, StringBuilderWhitespaceRemoval) {
- EXPECT_EQ(StringPiece16(u"hey guys this is so cool"),
- util::StringBuilder().append(u" hey guys ")
- .append(u" this is so cool ")
- .str());
-
- EXPECT_EQ(StringPiece16(u" wow, so many \t spaces. what?"),
- util::StringBuilder().append(u" \" wow, so many \t ")
- .append(u"spaces. \"what? ")
- .str());
-
- EXPECT_EQ(StringPiece16(u"where is the pie?"),
- util::StringBuilder().append(u" where \t ")
- .append(u" \nis the "" pie?")
- .str());
-}
-
-TEST(UtilTest, StringBuilderEscaping) {
- EXPECT_EQ(StringPiece16(u"hey guys\n this \t is so\\ cool"),
- util::StringBuilder().append(u" hey guys\\n ")
- .append(u" this \\t is so\\\\ cool ")
- .str());
-
- EXPECT_EQ(StringPiece16(u"@?#\\\'"),
- util::StringBuilder().append(u"\\@\\?\\#\\\\\\'")
- .str());
-}
-
-TEST(UtilTest, StringBuilderMisplacedQuote) {
- util::StringBuilder builder{};
- EXPECT_FALSE(builder.append(u"they're coming!"));
-}
-
-TEST(UtilTest, StringBuilderUnicodeCodes) {
- EXPECT_EQ(StringPiece16(u"\u00AF\u0AF0 woah"),
- util::StringBuilder().append(u"\\u00AF\\u0AF0 woah")
- .str());
-
- EXPECT_FALSE(util::StringBuilder().append(u"\\u00 yo"));
-}
-
-TEST(UtilTest, TokenizeInput) {
- auto tokenizer = util::tokenize(StringPiece16(u"this| is|the|end"), u'|');
- auto iter = tokenizer.begin();
- ASSERT_EQ(*iter, StringPiece16(u"this"));
- ++iter;
- ASSERT_EQ(*iter, StringPiece16(u" is"));
- ++iter;
- ASSERT_EQ(*iter, StringPiece16(u"the"));
- ++iter;
- ASSERT_EQ(*iter, StringPiece16(u"end"));
- ++iter;
- ASSERT_EQ(tokenizer.end(), iter);
-}
-
-TEST(UtilTest, IsJavaClassName) {
- EXPECT_TRUE(util::isJavaClassName(u"android.test.Class"));
- EXPECT_TRUE(util::isJavaClassName(u"android.test.Class$Inner"));
- EXPECT_TRUE(util::isJavaClassName(u"android_test.test.Class"));
- EXPECT_TRUE(util::isJavaClassName(u"_android_.test._Class_"));
- EXPECT_FALSE(util::isJavaClassName(u"android.test.$Inner"));
- EXPECT_FALSE(util::isJavaClassName(u"android.test.Inner$"));
- EXPECT_FALSE(util::isJavaClassName(u".test.Class"));
- EXPECT_FALSE(util::isJavaClassName(u"android"));
-}
-
-TEST(UtilTest, FullyQualifiedClassName) {
- Maybe<std::u16string> res = util::getFullyQualifiedClassName(u"android", u"asdf");
- ASSERT_TRUE(res);
- EXPECT_EQ(res.value(), u"android.asdf");
-
- res = util::getFullyQualifiedClassName(u"android", u".asdf");
- ASSERT_TRUE(res);
- EXPECT_EQ(res.value(), u"android.asdf");
-
- res = util::getFullyQualifiedClassName(u"android", u".a.b");
- ASSERT_TRUE(res);
- EXPECT_EQ(res.value(), u"android.a.b");
-
- res = util::getFullyQualifiedClassName(u"android", u"a.b");
- ASSERT_TRUE(res);
- EXPECT_EQ(res.value(), u"a.b");
-
- res = util::getFullyQualifiedClassName(u"", u"a.b");
- ASSERT_TRUE(res);
- EXPECT_EQ(res.value(), u"a.b");
-
- res = util::getFullyQualifiedClassName(u"", u"");
- ASSERT_FALSE(res);
-
- res = util::getFullyQualifiedClassName(u"android", u"./Apple");
- ASSERT_FALSE(res);
-}
-
-
-} // namespace aapt
diff --git a/tools/aapt2/ValueVisitor.h b/tools/aapt2/ValueVisitor.h
new file mode 100644
index 0000000..94042e3
--- /dev/null
+++ b/tools/aapt2/ValueVisitor.h
@@ -0,0 +1,145 @@
+/*
+ * 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_VALUE_VISITOR_H
+#define AAPT_VALUE_VISITOR_H
+
+#include "ResourceValues.h"
+
+namespace aapt {
+
+/**
+ * Visits a value and invokes the appropriate method based on its type. Does not traverse
+ * into compound types. Use ValueVisitor for that.
+ */
+struct RawValueVisitor {
+ virtual ~RawValueVisitor() = default;
+
+ virtual void visitItem(Item* value) {}
+ virtual void visit(Reference* value) { visitItem(value); }
+ virtual void visit(RawString* value) { visitItem(value); }
+ virtual void visit(String* value) { visitItem(value); }
+ virtual void visit(StyledString* value) { visitItem(value); }
+ virtual void visit(FileReference* value) { visitItem(value); }
+ virtual void visit(Id* value) { visitItem(value); }
+ virtual void visit(BinaryPrimitive* value) { visitItem(value); }
+
+ virtual void visit(Attribute* value) {}
+ virtual void visit(Style* value) {}
+ virtual void visit(Array* value) {}
+ virtual void visit(Plural* value) {}
+ virtual void visit(Styleable* value) {}
+};
+
+#define DECL_VISIT_COMPOUND_VALUE(T) \
+ virtual void visit(T* value) { \
+ visitSubValues(value); \
+ }
+
+/**
+ * Visits values, and if they are compound values, visits the components as well.
+ */
+struct ValueVisitor : public RawValueVisitor {
+ // The compiler will think we're hiding an overload, when we actually intend
+ // to call into RawValueVisitor. This will expose the visit methods in the super
+ // class so the compiler knows we are trying to call them.
+ using RawValueVisitor::visit;
+
+ void visitSubValues(Attribute* attribute) {
+ for (Attribute::Symbol& symbol : attribute->symbols) {
+ visit(&symbol.symbol);
+ }
+ }
+
+ void visitSubValues(Style* style) {
+ if (style->parent) {
+ visit(&style->parent.value());
+ }
+
+ for (Style::Entry& entry : style->entries) {
+ visit(&entry.key);
+ entry.value->accept(this);
+ }
+ }
+
+ void visitSubValues(Array* array) {
+ for (std::unique_ptr<Item>& item : array->items) {
+ item->accept(this);
+ }
+ }
+
+ void visitSubValues(Plural* plural) {
+ for (std::unique_ptr<Item>& item : plural->values) {
+ if (item) {
+ item->accept(this);
+ }
+ }
+ }
+
+ void visitSubValues(Styleable* styleable) {
+ for (Reference& reference : styleable->entries) {
+ visit(&reference);
+ }
+ }
+
+ DECL_VISIT_COMPOUND_VALUE(Attribute);
+ DECL_VISIT_COMPOUND_VALUE(Style);
+ DECL_VISIT_COMPOUND_VALUE(Array);
+ DECL_VISIT_COMPOUND_VALUE(Plural);
+ DECL_VISIT_COMPOUND_VALUE(Styleable);
+};
+
+/**
+ * Do not use directly. Helper struct for dyn_cast.
+ */
+template <typename T>
+struct DynCastVisitor : public RawValueVisitor {
+ T* value = nullptr;
+
+ void visit(T* v) override {
+ value = v;
+ }
+};
+
+/**
+ * Specialization that checks if the value is an Item.
+ */
+template <>
+struct DynCastVisitor<Item> : public RawValueVisitor {
+ Item* value = nullptr;
+
+ void visitItem(Item* item) override {
+ value = item;
+ }
+};
+
+/**
+ * Returns a valid pointer to T if the Value is of subtype T.
+ * Otherwise, returns nullptr.
+ */
+template <typename T>
+T* valueCast(Value* value) {
+ if (!value) {
+ return nullptr;
+ }
+ DynCastVisitor<T> visitor;
+ value->accept(&visitor);
+ return visitor.value;
+}
+
+} // namespace aapt
+
+#endif // AAPT_VALUE_VISITOR_H
diff --git a/tools/aapt2/ValueVisitor_test.cpp b/tools/aapt2/ValueVisitor_test.cpp
new file mode 100644
index 0000000..1624079
--- /dev/null
+++ b/tools/aapt2/ValueVisitor_test.cpp
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+#include <string>
+
+#include "ResourceValues.h"
+#include "util/Util.h"
+#include "ValueVisitor.h"
+#include "test/Builders.h"
+
+namespace aapt {
+
+struct SingleReferenceVisitor : public ValueVisitor {
+ using ValueVisitor::visit;
+
+ Reference* visited = nullptr;
+
+ void visit(Reference* ref) override {
+ visited = ref;
+ }
+};
+
+struct StyleVisitor : public ValueVisitor {
+ using ValueVisitor::visit;
+
+ std::list<Reference*> visitedRefs;
+ Style* visitedStyle = nullptr;
+
+ void visit(Reference* ref) override {
+ visitedRefs.push_back(ref);
+ }
+
+ void visit(Style* style) override {
+ visitedStyle = style;
+ ValueVisitor::visit(style);
+ }
+};
+
+TEST(ValueVisitorTest, VisitsReference) {
+ Reference ref(ResourceName{u"android", ResourceType::kAttr, u"foo"});
+ SingleReferenceVisitor visitor;
+ ref.accept(&visitor);
+
+ EXPECT_EQ(visitor.visited, &ref);
+}
+
+TEST(ValueVisitorTest, VisitsReferencesInStyle) {
+ std::unique_ptr<Style> style = test::StyleBuilder()
+ .setParent(u"@android:style/foo")
+ .addItem(u"@android:attr/one", test::buildReference(u"@android:id/foo"))
+ .build();
+
+ StyleVisitor visitor;
+ style->accept(&visitor);
+
+ ASSERT_EQ(style.get(), visitor.visitedStyle);
+
+ // Entry attribute references, plus the parent reference, plus one value reference.
+ ASSERT_EQ(style->entries.size() + 2, visitor.visitedRefs.size());
+}
+
+TEST(ValueVisitorTest, ValueCast) {
+ std::unique_ptr<Reference> ref = test::buildReference(u"@android:color/white");
+ EXPECT_NE(valueCast<Reference>(ref.get()), nullptr);
+
+ std::unique_ptr<Style> style = test::StyleBuilder()
+ .addItem(u"@android:attr/foo", test::buildReference(u"@android:color/black"))
+ .build();
+ EXPECT_NE(valueCast<Style>(style.get()), nullptr);
+ EXPECT_EQ(valueCast<Reference>(style.get()), nullptr);
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/XliffXmlPullParser.cpp b/tools/aapt2/XliffXmlPullParser.cpp
deleted file mode 100644
index 31115f2..0000000
--- a/tools/aapt2/XliffXmlPullParser.cpp
+++ /dev/null
@@ -1,113 +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.
- */
-
-#include "XliffXmlPullParser.h"
-
-#include <string>
-
-namespace aapt {
-
-XliffXmlPullParser::XliffXmlPullParser(const std::shared_ptr<XmlPullParser>& parser) :
- mParser(parser) {
-}
-
-XmlPullParser::Event XliffXmlPullParser::next() {
- while (XmlPullParser::isGoodEvent(mParser->next())) {
- Event event = mParser->getEvent();
- if (event != Event::kStartElement && event != Event::kEndElement) {
- break;
- }
-
- if (mParser->getElementNamespace() !=
- u"urn:oasis:names:tc:xliff:document:1.2") {
- break;
- }
-
- const std::u16string& name = mParser->getElementName();
- if (name != u"bpt"
- && name != u"ept"
- && name != u"it"
- && name != u"ph"
- && name != u"g"
- && name != u"bx"
- && name != u"ex"
- && name != u"x") {
- break;
- }
-
- // We hit a tag that was ignored, so get the next event.
- }
- return mParser->getEvent();
-}
-
-XmlPullParser::Event XliffXmlPullParser::getEvent() const {
- return mParser->getEvent();
-}
-
-const std::string& XliffXmlPullParser::getLastError() const {
- return mParser->getLastError();
-}
-
-const std::u16string& XliffXmlPullParser::getComment() const {
- return mParser->getComment();
-}
-
-size_t XliffXmlPullParser::getLineNumber() const {
- return mParser->getLineNumber();
-}
-
-size_t XliffXmlPullParser::getDepth() const {
- return mParser->getDepth();
-}
-
-const std::u16string& XliffXmlPullParser::getText() const {
- return mParser->getText();
-}
-
-const std::u16string& XliffXmlPullParser::getNamespacePrefix() const {
- return mParser->getNamespacePrefix();
-}
-
-const std::u16string& XliffXmlPullParser::getNamespaceUri() const {
- return mParser->getNamespaceUri();
-}
-
-bool XliffXmlPullParser::applyPackageAlias(std::u16string* package,
- const std::u16string& defaultPackage) const {
- return mParser->applyPackageAlias(package, defaultPackage);
-}
-
-const std::u16string& XliffXmlPullParser::getElementNamespace() const {
- return mParser->getElementNamespace();
-}
-
-const std::u16string& XliffXmlPullParser::getElementName() const {
- return mParser->getElementName();
-}
-
-size_t XliffXmlPullParser::getAttributeCount() const {
- return mParser->getAttributeCount();
-}
-
-XmlPullParser::const_iterator XliffXmlPullParser::beginAttributes() const {
- return mParser->beginAttributes();
-}
-
-XmlPullParser::const_iterator XliffXmlPullParser::endAttributes() const {
- return mParser->endAttributes();
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/XliffXmlPullParser.h b/tools/aapt2/XliffXmlPullParser.h
deleted file mode 100644
index 7791227..0000000
--- a/tools/aapt2/XliffXmlPullParser.h
+++ /dev/null
@@ -1,64 +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_XLIFF_XML_PULL_PARSER_H
-#define AAPT_XLIFF_XML_PULL_PARSER_H
-
-#include "XmlPullParser.h"
-
-#include <memory>
-#include <string>
-
-namespace aapt {
-
-/**
- * Strips xliff elements and provides the caller with a view of the
- * underlying XML without xliff.
- */
-class XliffXmlPullParser : public XmlPullParser {
-public:
- XliffXmlPullParser(const std::shared_ptr<XmlPullParser>& parser);
- XliffXmlPullParser(const XliffXmlPullParser& rhs) = delete;
-
- Event getEvent() const override;
- const std::string& getLastError() const override;
- Event next() override;
-
- const std::u16string& getComment() const override;
- size_t getLineNumber() const override;
- size_t getDepth() const override;
-
- const std::u16string& getText() const override;
-
- const std::u16string& getNamespacePrefix() const override;
- const std::u16string& getNamespaceUri() const override;
- bool applyPackageAlias(std::u16string* package, const std::u16string& defaultPackage)
- const override;
-
- const std::u16string& getElementNamespace() const override;
- const std::u16string& getElementName() const override;
-
- const_iterator beginAttributes() const override;
- const_iterator endAttributes() const override;
- size_t getAttributeCount() const override;
-
-private:
- std::shared_ptr<XmlPullParser> mParser;
-};
-
-} // namespace aapt
-
-#endif // AAPT_XLIFF_XML_PULL_PARSER_H
diff --git a/tools/aapt2/XliffXmlPullParser_test.cpp b/tools/aapt2/XliffXmlPullParser_test.cpp
deleted file mode 100644
index f9030724..0000000
--- a/tools/aapt2/XliffXmlPullParser_test.cpp
+++ /dev/null
@@ -1,75 +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.
- */
-
-#include "SourceXmlPullParser.h"
-#include "XliffXmlPullParser.h"
-
-#include <gtest/gtest.h>
-#include <sstream>
-#include <string>
-
-namespace aapt {
-
-TEST(XliffXmlPullParserTest, IgnoreXliffTags) {
- std::stringstream input;
- input << "<?xml version=\"1.0\" encoding=\"utf-8\"?>" << std::endl
- << "<resources xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">" << std::endl
- << "<string name=\"foo\">"
- << "Hey <xliff:g><xliff:it>there</xliff:it></xliff:g> world</string>" << std::endl
- << "</resources>" << std::endl;
- std::shared_ptr<XmlPullParser> sourceParser = std::make_shared<SourceXmlPullParser>(input);
- XliffXmlPullParser parser(sourceParser);
- EXPECT_EQ(XmlPullParser::Event::kStartDocument, parser.getEvent());
-
- EXPECT_EQ(XmlPullParser::Event::kStartNamespace, parser.next());
- EXPECT_EQ(parser.getNamespaceUri(), u"urn:oasis:names:tc:xliff:document:1.2");
- EXPECT_EQ(parser.getNamespacePrefix(), u"xliff");
-
- EXPECT_EQ(XmlPullParser::Event::kStartElement, parser.next());
- EXPECT_EQ(parser.getElementNamespace(), u"");
- EXPECT_EQ(parser.getElementName(), u"resources");
- EXPECT_EQ(XmlPullParser::Event::kText, parser.next()); // Account for newline/whitespace.
-
- EXPECT_EQ(XmlPullParser::Event::kStartElement, parser.next());
- EXPECT_EQ(parser.getElementNamespace(), u"");
- EXPECT_EQ(parser.getElementName(), u"string");
-
- EXPECT_EQ(XmlPullParser::Event::kText, parser.next());
- EXPECT_EQ(parser.getText(), u"Hey ");
-
- EXPECT_EQ(XmlPullParser::Event::kText, parser.next());
- EXPECT_EQ(parser.getText(), u"there");
-
- EXPECT_EQ(XmlPullParser::Event::kText, parser.next());
- EXPECT_EQ(parser.getText(), u" world");
-
- EXPECT_EQ(XmlPullParser::Event::kEndElement, parser.next());
- EXPECT_EQ(parser.getElementNamespace(), u"");
- EXPECT_EQ(parser.getElementName(), u"string");
- EXPECT_EQ(XmlPullParser::Event::kText, parser.next()); // Account for newline/whitespace.
-
- EXPECT_EQ(XmlPullParser::Event::kEndElement, parser.next());
- EXPECT_EQ(parser.getElementNamespace(), u"");
- EXPECT_EQ(parser.getElementName(), u"resources");
-
- EXPECT_EQ(XmlPullParser::Event::kEndNamespace, parser.next());
- EXPECT_EQ(parser.getNamespacePrefix(), u"xliff");
- EXPECT_EQ(parser.getNamespaceUri(), u"urn:oasis:names:tc:xliff:document:1.2");
-
- EXPECT_EQ(XmlPullParser::Event::kEndDocument, parser.next());
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/XmlDom.cpp b/tools/aapt2/XmlDom.cpp
index b8b2d12..b769c76 100644
--- a/tools/aapt2/XmlDom.cpp
+++ b/tools/aapt2/XmlDom.cpp
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#include "Logger.h"
-#include "Util.h"
+#include "util/Util.h"
#include "XmlDom.h"
#include "XmlPullParser.h"
@@ -65,7 +64,7 @@
stack->root = std::move(node);
}
- if (thisNode->type != NodeType::kText) {
+ if (!nodeCast<Text>(thisNode)) {
stack->nodeStack.push(thisNode);
}
}
@@ -126,7 +125,7 @@
Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
assert(!stack->nodeStack.empty());
- stack->nodeStack.top()->comment = std::move(stack->pendingComment);
+ //stack->nodeStack.top()->comment = std::move(stack->pendingComment);
stack->nodeStack.pop();
}
@@ -143,8 +142,7 @@
Node* currentParent = stack->nodeStack.top();
if (!currentParent->children.empty()) {
Node* lastChild = currentParent->children.back().get();
- if (lastChild->type == NodeType::kText) {
- Text* text = static_cast<Text*>(lastChild);
+ if (Text* text = nodeCast<Text>(lastChild)) {
text->text += util::utf8ToUtf16(StringPiece(s, len));
return;
}
@@ -166,7 +164,7 @@
stack->pendingComment += util::utf8ToUtf16(comment);
}
-std::unique_ptr<Node> inflate(std::istream* in, SourceLogger* logger) {
+std::unique_ptr<XmlResource> inflate(std::istream* in, IDiagnostics* diag, const Source& source) {
Stack stack;
XML_Parser parser = XML_ParserCreateNS(nullptr, kXmlNamespaceSep);
@@ -182,20 +180,23 @@
in->read(buffer, sizeof(buffer) / sizeof(buffer[0]));
if (in->bad() && !in->eof()) {
stack.root = {};
- logger->error() << strerror(errno) << std::endl;
+ diag->error(DiagMessage(source) << strerror(errno));
break;
}
if (XML_Parse(parser, buffer, in->gcount(), in->eof()) == XML_STATUS_ERROR) {
stack.root = {};
- logger->error(XML_GetCurrentLineNumber(parser))
- << XML_ErrorString(XML_GetErrorCode(parser)) << std::endl;
+ diag->error(DiagMessage(source.withLine(XML_GetCurrentLineNumber(parser)))
+ << XML_ErrorString(XML_GetErrorCode(parser)));
break;
}
}
XML_ParserFree(parser);
- return std::move(stack.root);
+ if (stack.root) {
+ return util::make_unique<XmlResource>(ResourceFile{ {}, {}, source }, std::move(stack.root));
+ }
+ return {};
}
static void copyAttributes(Element* el, android::ResXMLParser* parser) {
@@ -224,7 +225,8 @@
}
}
-std::unique_ptr<Node> inflate(const void* data, size_t dataLen, SourceLogger* logger) {
+std::unique_ptr<XmlResource> inflate(const void* data, size_t dataLen, IDiagnostics* diag,
+ const Source& source) {
std::unique_ptr<Node> root;
std::stack<Node*> nodeStack;
@@ -307,15 +309,28 @@
nodeStack.top()->addChild(std::move(newNode));
}
- if (thisNode->type != NodeType::kText) {
+ if (!nodeCast<Text>(thisNode)) {
nodeStack.push(thisNode);
}
}
}
- return root;
+ return util::make_unique<XmlResource>(ResourceFile{}, std::move(root));
}
-Node::Node(NodeType type) : type(type), parent(nullptr), lineNumber(0), columnNumber(0) {
+Element* findRootElement(Node* node) {
+ if (!node) {
+ return nullptr;
+ }
+
+ Element* el = nullptr;
+ while ((el = nodeCast<Element>(node)) == nullptr) {
+ if (node->children.empty()) {
+ return nullptr;
+ }
+ // We are looking for the first element, and namespaces can only have one child.
+ node = node->children.front().get();
+ }
+ return el;
}
void Node::addChild(std::unique_ptr<Node> child) {
@@ -323,39 +338,6 @@
children.push_back(std::move(child));
}
-Namespace::Namespace() : BaseNode(NodeType::kNamespace) {
-}
-
-std::unique_ptr<Node> Namespace::clone() const {
- Namespace* ns = new Namespace();
- ns->lineNumber = lineNumber;
- ns->columnNumber = columnNumber;
- ns->comment = comment;
- ns->namespacePrefix = namespacePrefix;
- ns->namespaceUri = namespaceUri;
- for (auto& child : children) {
- ns->addChild(child->clone());
- }
- return std::unique_ptr<Node>(ns);
-}
-
-Element::Element() : BaseNode(NodeType::kElement) {
-}
-
-std::unique_ptr<Node> Element::clone() const {
- Element* el = new Element();
- el->lineNumber = lineNumber;
- el->columnNumber = columnNumber;
- el->comment = comment;
- el->namespaceUri = namespaceUri;
- el->name = name;
- el->attributes = attributes;
- for (auto& child : children) {
- el->addChild(child->clone());
- }
- return std::unique_ptr<Node>(el);
-}
-
Attribute* Element::findAttribute(const StringPiece16& ns, const StringPiece16& name) {
for (auto& attr : attributes) {
if (ns == attr.namespaceUri && name == attr.name) {
@@ -366,29 +348,29 @@
}
Element* Element::findChild(const StringPiece16& ns, const StringPiece16& name) {
- return findChildWithAttribute(ns, name, nullptr);
+ return findChildWithAttribute(ns, name, {}, {}, {});
}
Element* Element::findChildWithAttribute(const StringPiece16& ns, const StringPiece16& name,
- const Attribute* reqAttr) {
+ const StringPiece16& attrNs, const StringPiece16& attrName,
+ const StringPiece16& attrValue) {
for (auto& childNode : children) {
Node* child = childNode.get();
- while (child->type == NodeType::kNamespace) {
+ while (nodeCast<Namespace>(child)) {
if (child->children.empty()) {
break;
}
child = child->children[0].get();
}
- if (child->type == NodeType::kElement) {
- Element* el = static_cast<Element*>(child);
+ if (Element* el = nodeCast<Element>(child)) {
if (ns == el->namespaceUri && name == el->name) {
- if (!reqAttr) {
+ if (attrNs.empty() && attrName.empty()) {
return el;
}
- Attribute* attrName = el->findAttribute(reqAttr->namespaceUri, reqAttr->name);
- if (attrName && attrName->value == reqAttr->value) {
+ Attribute* attr = el->findAttribute(attrNs, attrName);
+ if (attr && attrValue == attr->value) {
return el;
}
}
@@ -401,31 +383,19 @@
std::vector<Element*> elements;
for (auto& childNode : children) {
Node* child = childNode.get();
- while (child->type == NodeType::kNamespace) {
+ while (nodeCast<Namespace>(child)) {
if (child->children.empty()) {
break;
}
child = child->children[0].get();
}
- if (child->type == NodeType::kElement) {
- elements.push_back(static_cast<Element*>(child));
+ if (Element* el = nodeCast<Element>(child)) {
+ elements.push_back(el);
}
}
return elements;
}
-Text::Text() : BaseNode(NodeType::kText) {
-}
-
-std::unique_ptr<Node> Text::clone() const {
- Text* el = new Text();
- el->lineNumber = lineNumber;
- el->columnNumber = columnNumber;
- el->comment = comment;
- el->text = text;
- return std::unique_ptr<Node>(el);
-}
-
} // namespace xml
} // namespace aapt
diff --git a/tools/aapt2/XmlDom.h b/tools/aapt2/XmlDom.h
index 035e7c4..9a46bcb 100644
--- a/tools/aapt2/XmlDom.h
+++ b/tools/aapt2/XmlDom.h
@@ -17,8 +17,13 @@
#ifndef AAPT_XML_DOM_H
#define AAPT_XML_DOM_H
-#include "Logger.h"
-#include "StringPiece.h"
+#include "Diagnostics.h"
+#include "Resource.h"
+#include "ResourceValues.h"
+#include "util/StringPiece.h"
+#include "util/Util.h"
+
+#include "process/IResourceTableConsumer.h"
#include <istream>
#include <expat.h>
@@ -29,7 +34,9 @@
namespace aapt {
namespace xml {
-struct Visitor;
+constexpr const char16_t* kSchemaAndroid = u"http://schemas.android.com/apk/res/android";
+
+struct RawVisitor;
/**
* The type of node. Can be used to downcast to the concrete XML node
@@ -45,17 +52,14 @@
* Base class for all XML nodes.
*/
struct Node {
- NodeType type;
- Node* parent;
- size_t lineNumber;
- size_t columnNumber;
+ Node* parent = nullptr;
+ size_t lineNumber = 0;
+ size_t columnNumber = 0;
std::u16string comment;
std::vector<std::unique_ptr<Node>> children;
- Node(NodeType type);
void addChild(std::unique_ptr<Node> child);
- virtual std::unique_ptr<Node> clone() const = 0;
- virtual void accept(Visitor* visitor) = 0;
+ virtual void accept(RawVisitor* visitor) = 0;
virtual ~Node() {}
};
@@ -65,8 +69,7 @@
*/
template <typename Derived>
struct BaseNode : public Node {
- BaseNode(NodeType t);
- virtual void accept(Visitor* visitor) override;
+ virtual void accept(RawVisitor* visitor) override;
};
/**
@@ -75,9 +78,11 @@
struct Namespace : public BaseNode<Namespace> {
std::u16string namespacePrefix;
std::u16string namespaceUri;
+};
- Namespace();
- virtual std::unique_ptr<Node> clone() const override;
+struct AaptAttribute {
+ ResourceId id;
+ aapt::Attribute attribute;
};
/**
@@ -87,6 +92,9 @@
std::u16string namespaceUri;
std::u16string name;
std::u16string value;
+
+ Maybe<AaptAttribute> compiledAttribute;
+ std::unique_ptr<Item> compiledValue;
};
/**
@@ -97,12 +105,12 @@
std::u16string name;
std::vector<Attribute> attributes;
- Element();
- virtual std::unique_ptr<Node> clone() const override;
Attribute* findAttribute(const StringPiece16& ns, const StringPiece16& name);
xml::Element* findChild(const StringPiece16& ns, const StringPiece16& name);
xml::Element* findChildWithAttribute(const StringPiece16& ns, const StringPiece16& name,
- const xml::Attribute* reqAttr);
+ const StringPiece16& attrNs,
+ const StringPiece16& attrName,
+ const StringPiece16& attrValue);
std::vector<xml::Element*> getChildElements();
};
@@ -111,41 +119,135 @@
*/
struct Text : public BaseNode<Text> {
std::u16string text;
-
- Text();
- virtual std::unique_ptr<Node> clone() const override;
};
/**
* Inflates an XML DOM from a text stream, logging errors to the logger.
* Returns the root node on success, or nullptr on failure.
*/
-std::unique_ptr<Node> inflate(std::istream* in, SourceLogger* logger);
+std::unique_ptr<XmlResource> inflate(std::istream* in, IDiagnostics* diag, const Source& source);
/**
* Inflates an XML DOM from a binary ResXMLTree, logging errors to the logger.
* Returns the root node on success, or nullptr on failure.
*/
-std::unique_ptr<Node> inflate(const void* data, size_t dataLen, SourceLogger* logger);
+std::unique_ptr<XmlResource> inflate(const void* data, size_t dataLen, IDiagnostics* diag,
+ const Source& source);
+
+Element* findRootElement(Node* node);
/**
- * A visitor interface for the different XML Node subtypes.
+ * A visitor interface for the different XML Node subtypes. This will not traverse into
+ * children. Use Visitor for that.
*/
-struct Visitor {
- virtual void visit(Namespace* node) = 0;
- virtual void visit(Element* node) = 0;
- virtual void visit(Text* text) = 0;
+struct RawVisitor {
+ virtual ~RawVisitor() = default;
+
+ virtual void visit(Namespace* node) {}
+ virtual void visit(Element* node) {}
+ virtual void visit(Text* text) {}
+};
+
+/**
+ * Visitor whose default implementation visits the children nodes of any node.
+ */
+struct Visitor : public RawVisitor {
+ using RawVisitor::visit;
+
+ void visit(Namespace* node) override {
+ visitChildren(node);
+ }
+
+ void visit(Element* node) override {
+ visitChildren(node);
+ }
+
+ void visit(Text* text) override {
+ visitChildren(text);
+ }
+
+ void visitChildren(Node* node) {
+ for (auto& child : node->children) {
+ child->accept(this);
+ }
+ }
+};
+
+/**
+ * An XML DOM visitor that will record the package name for a namespace prefix.
+ */
+class PackageAwareVisitor : public Visitor, public IPackageDeclStack {
+private:
+ struct PackageDecl {
+ std::u16string prefix;
+ std::u16string package;
+ };
+
+ std::vector<PackageDecl> mPackageDecls;
+
+public:
+ using Visitor::visit;
+
+ void visit(Namespace* ns) override {
+ bool added = false;
+ {
+ Maybe<std::u16string> package = util::extractPackageFromNamespace(ns->namespaceUri);
+ if (package) {
+ mPackageDecls.push_back(PackageDecl{ ns->namespacePrefix, package.value() });
+ added = true;
+ }
+ }
+
+ Visitor::visit(ns);
+
+ if (added) {
+ mPackageDecls.pop_back();
+ }
+ }
+
+ Maybe<ResourceName> transformPackage(const ResourceName& name,
+ const StringPiece16& localPackage) const override {
+ if (name.package.empty()) {
+ return ResourceName{ localPackage.toString(), name.type, name.entry };
+ }
+
+ const auto rend = mPackageDecls.rend();
+ for (auto iter = mPackageDecls.rbegin(); iter != rend; ++iter) {
+ if (name.package == iter->prefix) {
+ if (iter->package.empty()) {
+ return ResourceName{ localPackage.toString(), name.type, name.entry };
+ } else {
+ return ResourceName{ iter->package, name.type, name.entry };
+ }
+ }
+ }
+ return {};
+ }
};
// Implementations
template <typename Derived>
-BaseNode<Derived>::BaseNode(NodeType type) : Node(type) {
+void BaseNode<Derived>::accept(RawVisitor* visitor) {
+ visitor->visit(static_cast<Derived*>(this));
}
-template <typename Derived>
-void BaseNode<Derived>::accept(Visitor* visitor) {
- visitor->visit(static_cast<Derived*>(this));
+template <typename T>
+struct NodeCastImpl : public RawVisitor {
+ using RawVisitor::visit;
+
+ T* value = nullptr;
+
+ void visit(T* v) override {
+ value = v;
+ }
+};
+
+template <typename T>
+T* nodeCast(Node* node) {
+ NodeCastImpl<T> visitor;
+ node->accept(&visitor);
+ return visitor.value;
}
} // namespace xml
diff --git a/tools/aapt2/XmlDom_test.cpp b/tools/aapt2/XmlDom_test.cpp
index 0217144..a1b9ed0 100644
--- a/tools/aapt2/XmlDom_test.cpp
+++ b/tools/aapt2/XmlDom_test.cpp
@@ -36,12 +36,13 @@
</Layout>
)EOF";
- SourceLogger logger(Source{ "/test/path" });
- std::unique_ptr<xml::Node> root = xml::inflate(&in, &logger);
- ASSERT_NE(root, nullptr);
+ const Source source = { "test.xml" };
+ StdErrDiagnostics diag;
+ std::unique_ptr<XmlResource> doc = xml::inflate(&in, &diag, source);
+ ASSERT_NE(doc, nullptr);
- EXPECT_EQ(root->type, xml::NodeType::kNamespace);
- xml::Namespace* ns = static_cast<xml::Namespace*>(root.get());
+ xml::Namespace* ns = xml::nodeCast<xml::Namespace>(doc->root.get());
+ ASSERT_NE(ns, nullptr);
EXPECT_EQ(ns->namespaceUri, u"http://schemas.android.com/apk/res/android");
EXPECT_EQ(ns->namespacePrefix, u"android");
}
diff --git a/tools/aapt2/XmlFlattener.cpp b/tools/aapt2/XmlFlattener.cpp
deleted file mode 100644
index 56b5613d..0000000
--- a/tools/aapt2/XmlFlattener.cpp
+++ /dev/null
@@ -1,574 +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.
- */
-
-#include "BigBuffer.h"
-#include "Logger.h"
-#include "Maybe.h"
-#include "Resolver.h"
-#include "Resource.h"
-#include "ResourceParser.h"
-#include "ResourceValues.h"
-#include "SdkConstants.h"
-#include "Source.h"
-#include "StringPool.h"
-#include "Util.h"
-#include "XmlFlattener.h"
-
-#include <androidfw/ResourceTypes.h>
-#include <limits>
-#include <map>
-#include <string>
-#include <vector>
-
-namespace aapt {
-namespace xml {
-
-constexpr uint32_t kLowPriority = 0xffffffffu;
-
-// A vector that maps String refs to their final destination in the out buffer.
-using FlatStringRefList = std::vector<std::pair<StringPool::Ref, android::ResStringPool_ref*>>;
-
-struct XmlFlattener : public Visitor {
- XmlFlattener(BigBuffer* outBuffer, StringPool* pool, FlatStringRefList* stringRefs,
- const std::u16string& defaultPackage) :
- mOut(outBuffer), mPool(pool), mStringRefs(stringRefs),
- mDefaultPackage(defaultPackage) {
- }
-
- // No copying.
- XmlFlattener(const XmlFlattener&) = delete;
- XmlFlattener& operator=(const XmlFlattener&) = delete;
-
- void writeNamespace(Namespace* node, uint16_t type) {
- const size_t startIndex = mOut->size();
- android::ResXMLTree_node* flatNode = mOut->nextBlock<android::ResXMLTree_node>();
- android::ResXMLTree_namespaceExt* flatNs =
- mOut->nextBlock<android::ResXMLTree_namespaceExt>();
- mOut->align4();
-
- flatNode->header = { type, sizeof(*flatNode), (uint32_t)(mOut->size() - startIndex) };
- flatNode->lineNumber = node->lineNumber;
- flatNode->comment.index = -1;
- addString(node->namespacePrefix, kLowPriority, &flatNs->prefix);
- addString(node->namespaceUri, kLowPriority, &flatNs->uri);
- }
-
- virtual void visit(Namespace* node) override {
- // Extract the package/prefix from this namespace node.
- Maybe<std::u16string> package = util::extractPackageFromNamespace(node->namespaceUri);
- if (package) {
- mPackageAliases.emplace_back(
- node->namespacePrefix,
- package.value().empty() ? mDefaultPackage : package.value());
- }
-
- writeNamespace(node, android::RES_XML_START_NAMESPACE_TYPE);
- for (const auto& child : node->children) {
- child->accept(this);
- }
- writeNamespace(node, android::RES_XML_END_NAMESPACE_TYPE);
-
- if (package) {
- mPackageAliases.pop_back();
- }
- }
-
- virtual void visit(Text* node) override {
- if (util::trimWhitespace(node->text).empty()) {
- return;
- }
-
- const size_t startIndex = mOut->size();
- android::ResXMLTree_node* flatNode = mOut->nextBlock<android::ResXMLTree_node>();
- android::ResXMLTree_cdataExt* flatText = mOut->nextBlock<android::ResXMLTree_cdataExt>();
- mOut->align4();
-
- const uint16_t type = android::RES_XML_CDATA_TYPE;
- flatNode->header = { type, sizeof(*flatNode), (uint32_t)(mOut->size() - startIndex) };
- flatNode->lineNumber = node->lineNumber;
- flatNode->comment.index = -1;
- addString(node->text, kLowPriority, &flatText->data);
- }
-
- virtual void visit(Element* node) override {
- const size_t startIndex = mOut->size();
- android::ResXMLTree_node* flatNode = mOut->nextBlock<android::ResXMLTree_node>();
- android::ResXMLTree_attrExt* flatElem = mOut->nextBlock<android::ResXMLTree_attrExt>();
-
- const uint16_t type = android::RES_XML_START_ELEMENT_TYPE;
- flatNode->header = { type, sizeof(*flatNode), 0 };
- flatNode->lineNumber = node->lineNumber;
- flatNode->comment.index = -1;
-
- addString(node->namespaceUri, kLowPriority, &flatElem->ns);
- addString(node->name, kLowPriority, &flatElem->name);
- flatElem->attributeStart = sizeof(*flatElem);
- flatElem->attributeSize = sizeof(android::ResXMLTree_attribute);
- flatElem->attributeCount = node->attributes.size();
-
- if (!writeAttributes(mOut, node, flatElem)) {
- mError = true;
- }
-
- mOut->align4();
- flatNode->header.size = (uint32_t)(mOut->size() - startIndex);
-
- for (const auto& child : node->children) {
- child->accept(this);
- }
-
- const size_t startEndIndex = mOut->size();
- android::ResXMLTree_node* flatEndNode = mOut->nextBlock<android::ResXMLTree_node>();
- android::ResXMLTree_endElementExt* flatEndElem =
- mOut->nextBlock<android::ResXMLTree_endElementExt>();
- mOut->align4();
-
- const uint16_t endType = android::RES_XML_END_ELEMENT_TYPE;
- flatEndNode->header = { endType, sizeof(*flatEndNode),
- (uint32_t)(mOut->size() - startEndIndex) };
- flatEndNode->lineNumber = node->lineNumber;
- flatEndNode->comment.index = -1;
-
- addString(node->namespaceUri, kLowPriority, &flatEndElem->ns);
- addString(node->name, kLowPriority, &flatEndElem->name);
- }
-
- bool success() const {
- return !mError;
- }
-
-protected:
- void addString(const StringPiece16& str, uint32_t priority, android::ResStringPool_ref* dest) {
- if (!str.empty()) {
- mStringRefs->emplace_back(mPool->makeRef(str, StringPool::Context{ priority }), dest);
- } else {
- // The device doesn't think a string of size 0 is the same as null.
- dest->index = -1;
- }
- }
-
- void addString(const StringPool::Ref& ref, android::ResStringPool_ref* dest) {
- mStringRefs->emplace_back(ref, dest);
- }
-
- Maybe<std::u16string> getPackageAlias(const std::u16string& prefix) {
- const auto endIter = mPackageAliases.rend();
- for (auto iter = mPackageAliases.rbegin(); iter != endIter; ++iter) {
- if (iter->first == prefix) {
- return iter->second;
- }
- }
- return {};
- }
-
- const std::u16string& getDefaultPackage() const {
- return mDefaultPackage;
- }
-
- /**
- * Subclasses override this to deal with attributes. Attributes can be flattened as
- * raw values or as resources.
- */
- virtual bool writeAttributes(BigBuffer* out, Element* node,
- android::ResXMLTree_attrExt* flatElem) = 0;
-
-private:
- BigBuffer* mOut;
- StringPool* mPool;
- FlatStringRefList* mStringRefs;
- std::u16string mDefaultPackage;
- bool mError = false;
- std::vector<std::pair<std::u16string, std::u16string>> mPackageAliases;
-};
-
-/**
- * Flattens XML, encoding the attributes as raw strings. This is used in the compile phase.
- */
-struct CompileXmlFlattener : public XmlFlattener {
- CompileXmlFlattener(BigBuffer* outBuffer, StringPool* pool, FlatStringRefList* stringRefs,
- const std::u16string& defaultPackage) :
- XmlFlattener(outBuffer, pool, stringRefs, defaultPackage) {
- }
-
- virtual bool writeAttributes(BigBuffer* out, Element* node,
- android::ResXMLTree_attrExt* flatElem) override {
- flatElem->attributeCount = node->attributes.size();
- if (node->attributes.empty()) {
- return true;
- }
-
- android::ResXMLTree_attribute* flatAttrs = out->nextBlock<android::ResXMLTree_attribute>(
- node->attributes.size());
- for (const Attribute& attr : node->attributes) {
- addString(attr.namespaceUri, kLowPriority, &flatAttrs->ns);
- addString(attr.name, kLowPriority, &flatAttrs->name);
- addString(attr.value, kLowPriority, &flatAttrs->rawValue);
- flatAttrs++;
- }
- return true;
- }
-};
-
-struct AttributeToFlatten {
- uint32_t resourceId = 0;
- const Attribute* xmlAttr = nullptr;
- const ::aapt::Attribute* resourceAttr = nullptr;
-};
-
-static bool lessAttributeId(const AttributeToFlatten& a, uint32_t id) {
- return a.resourceId < id;
-}
-
-/**
- * Flattens XML, encoding the attributes as resources.
- */
-struct LinkedXmlFlattener : public XmlFlattener {
- LinkedXmlFlattener(BigBuffer* outBuffer, StringPool* pool,
- std::map<std::u16string, StringPool>* packagePools,
- FlatStringRefList* stringRefs,
- const std::u16string& defaultPackage,
- const std::shared_ptr<IResolver>& resolver,
- SourceLogger* logger,
- const FlattenOptions& options) :
- XmlFlattener(outBuffer, pool, stringRefs, defaultPackage), mResolver(resolver),
- mLogger(logger), mPackagePools(packagePools), mOptions(options) {
- }
-
- virtual bool writeAttributes(BigBuffer* out, Element* node,
- android::ResXMLTree_attrExt* flatElem) override {
- bool error = false;
- std::vector<AttributeToFlatten> sortedAttributes;
- uint32_t nextAttributeId = 0x80000000u;
-
- // Sort and filter attributes by their resource ID.
- for (const Attribute& attr : node->attributes) {
- AttributeToFlatten attrToFlatten;
- attrToFlatten.xmlAttr = &attr;
-
- Maybe<std::u16string> package = util::extractPackageFromNamespace(attr.namespaceUri);
- if (package) {
- // Find the Attribute object via our Resolver.
- ResourceName attrName = { package.value(), ResourceType::kAttr, attr.name };
- if (attrName.package.empty()) {
- attrName.package = getDefaultPackage();
- }
-
- Maybe<IResolver::Entry> result = mResolver->findAttribute(attrName);
- if (!result || !result.value().id.isValid() || !result.value().attr) {
- error = true;
- mLogger->error(node->lineNumber)
- << "unresolved attribute '" << attrName << "'."
- << std::endl;
- } else {
- attrToFlatten.resourceId = result.value().id.id;
- attrToFlatten.resourceAttr = result.value().attr;
-
- size_t sdk = findAttributeSdkLevel(attrToFlatten.resourceId);
- if (mOptions.maxSdkAttribute && sdk > mOptions.maxSdkAttribute.value()) {
- // We need to filter this attribute out.
- mSmallestFilteredSdk = std::min(mSmallestFilteredSdk, sdk);
- continue;
- }
- }
- }
-
- if (attrToFlatten.resourceId == 0) {
- // Attributes that have no resource ID (because they don't belong to a
- // package) should appear after those that do have resource IDs. Assign
- // them some integer value that will appear after.
- attrToFlatten.resourceId = nextAttributeId++;
- }
-
- // Insert the attribute into the sorted vector.
- auto iter = std::lower_bound(sortedAttributes.begin(), sortedAttributes.end(),
- attrToFlatten.resourceId, lessAttributeId);
- sortedAttributes.insert(iter, std::move(attrToFlatten));
- }
-
- flatElem->attributeCount = sortedAttributes.size();
- if (sortedAttributes.empty()) {
- return true;
- }
-
- android::ResXMLTree_attribute* flatAttr = out->nextBlock<android::ResXMLTree_attribute>(
- sortedAttributes.size());
-
- // Now that we have sorted the attributes into their final encoded order, it's time
- // to actually write them out.
- uint16_t attributeIndex = 1;
- for (const AttributeToFlatten& attrToFlatten : sortedAttributes) {
- Maybe<std::u16string> package = util::extractPackageFromNamespace(
- attrToFlatten.xmlAttr->namespaceUri);
-
- // Assign the indices for specific attributes.
- if (package && package.value() == u"android" && attrToFlatten.xmlAttr->name == u"id") {
- flatElem->idIndex = attributeIndex;
- } else if (attrToFlatten.xmlAttr->namespaceUri.empty()) {
- if (attrToFlatten.xmlAttr->name == u"class") {
- flatElem->classIndex = attributeIndex;
- } else if (attrToFlatten.xmlAttr->name == u"style") {
- flatElem->styleIndex = attributeIndex;
- }
- }
- attributeIndex++;
-
- // Add the namespaceUri and name to the list of StringRefs to encode.
- addString(attrToFlatten.xmlAttr->namespaceUri, kLowPriority, &flatAttr->ns);
- flatAttr->rawValue.index = -1;
-
- if (!attrToFlatten.resourceAttr) {
- addString(attrToFlatten.xmlAttr->name, kLowPriority, &flatAttr->name);
- } else {
- // We've already extracted the package successfully before.
- assert(package);
-
- // Attribute names are stored without packages, but we use
- // their StringPool index to lookup their resource IDs.
- // This will cause collisions, so we can't dedupe
- // attribute names from different packages. We use separate
- // pools that we later combine.
- //
- // Lookup the StringPool for this package and make the reference there.
- StringPool::Ref nameRef = (*mPackagePools)[package.value()].makeRef(
- attrToFlatten.xmlAttr->name,
- StringPool::Context{ attrToFlatten.resourceId });
-
- // Add it to the list of strings to flatten.
- addString(nameRef, &flatAttr->name);
-
- if (mOptions.keepRawValues) {
- // Keep raw values (this is for static libraries).
- // TODO(with a smarter inflater for binary XML, we can do without this).
- addString(attrToFlatten.xmlAttr->value, kLowPriority, &flatAttr->rawValue);
- }
- }
-
- error |= !flattenItem(node, attrToFlatten.xmlAttr->value, attrToFlatten.resourceAttr,
- flatAttr);
- flatAttr->typedValue.size = sizeof(flatAttr->typedValue);
- flatAttr++;
- }
- return !error;
- }
-
- Maybe<size_t> getSmallestFilteredSdk() const {
- if (mSmallestFilteredSdk == std::numeric_limits<size_t>::max()) {
- return {};
- }
- return mSmallestFilteredSdk;
- }
-
-private:
- bool flattenItem(const Node* el, const std::u16string& value, const ::aapt::Attribute* attr,
- android::ResXMLTree_attribute* flatAttr) {
- std::unique_ptr<Item> item;
- if (!attr) {
- bool create = false;
- item = ResourceParser::tryParseReference(value, &create);
- if (!item) {
- flatAttr->typedValue.dataType = android::Res_value::TYPE_STRING;
- addString(value, kLowPriority, &flatAttr->rawValue);
- addString(value, kLowPriority, reinterpret_cast<android::ResStringPool_ref*>(
- &flatAttr->typedValue.data));
- return true;
- }
- } else {
- item = ResourceParser::parseItemForAttribute(value, *attr);
- if (!item) {
- if (!(attr->typeMask & android::ResTable_map::TYPE_STRING)) {
- mLogger->error(el->lineNumber)
- << "'"
- << value
- << "' is not compatible with attribute '"
- << *attr
- << "'."
- << std::endl;
- return false;
- }
-
- flatAttr->typedValue.dataType = android::Res_value::TYPE_STRING;
- addString(value, kLowPriority, &flatAttr->rawValue);
- addString(value, kLowPriority, reinterpret_cast<android::ResStringPool_ref*>(
- &flatAttr->typedValue.data));
- return true;
- }
- }
-
- assert(item);
-
- bool error = false;
-
- // If this is a reference, resolve the name into an ID.
- visitFunc<Reference>(*item, [&](Reference& reference) {
- // First see if we can convert the package name from a prefix to a real
- // package name.
- ResourceName realName = reference.name;
- if (!realName.package.empty()) {
- Maybe<std::u16string> package = getPackageAlias(realName.package);
- if (package) {
- realName.package = package.value();
- }
- } else {
- realName.package = getDefaultPackage();
- }
-
- Maybe<ResourceId> result = mResolver->findId(realName);
- if (!result || !result.value().isValid()) {
- std::ostream& out = mLogger->error(el->lineNumber)
- << "unresolved reference '"
- << reference.name
- << "'";
- if (realName != reference.name) {
- out << " (aka '" << realName << "')";
- }
- out << "'." << std::endl;
- error = true;
- } else {
- reference.id = result.value();
- }
- });
-
- if (error) {
- return false;
- }
-
- item->flatten(flatAttr->typedValue);
- return true;
- }
-
- std::shared_ptr<IResolver> mResolver;
- SourceLogger* mLogger;
- std::map<std::u16string, StringPool>* mPackagePools;
- FlattenOptions mOptions;
- size_t mSmallestFilteredSdk = std::numeric_limits<size_t>::max();
-};
-
-/**
- * The binary XML file expects the StringPool to appear first, but we haven't collected the
- * strings yet. We write to a temporary BigBuffer while parsing the input, adding strings
- * we encounter to the StringPool. At the end, we write the StringPool to the given BigBuffer and
- * then move the data from the temporary BigBuffer into the given one. This incurs no
- * copies as the given BigBuffer simply takes ownership of the data.
- */
-static void flattenXml(StringPool* pool, FlatStringRefList* stringRefs, BigBuffer* outBuffer,
- BigBuffer&& xmlTreeBuffer) {
- // Sort the string pool so that attribute resource IDs show up first.
- pool->sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
- return a.context.priority < b.context.priority;
- });
-
- // Now we flatten the string pool references into the correct places.
- for (const auto& refEntry : *stringRefs) {
- refEntry.second->index = refEntry.first.getIndex();
- }
-
- // Write the XML header.
- const size_t beforeXmlTreeIndex = outBuffer->size();
- android::ResXMLTree_header* header = outBuffer->nextBlock<android::ResXMLTree_header>();
- header->header.type = android::RES_XML_TYPE;
- header->header.headerSize = sizeof(*header);
-
- // Flatten the StringPool.
- StringPool::flattenUtf16(outBuffer, *pool);
-
- // Write the array of resource IDs, indexed by StringPool order.
- const size_t beforeResIdMapIndex = outBuffer->size();
- android::ResChunk_header* resIdMapChunk = outBuffer->nextBlock<android::ResChunk_header>();
- resIdMapChunk->type = android::RES_XML_RESOURCE_MAP_TYPE;
- resIdMapChunk->headerSize = sizeof(*resIdMapChunk);
- for (const auto& str : *pool) {
- ResourceId id { str->context.priority };
- if (id.id == kLowPriority || !id.isValid()) {
- // When we see the first non-resource ID,
- // we're done.
- break;
- }
-
- *outBuffer->nextBlock<uint32_t>() = id.id;
- }
- resIdMapChunk->size = outBuffer->size() - beforeResIdMapIndex;
-
- // Move the temporary BigBuffer into outBuffer.
- outBuffer->appendBuffer(std::move(xmlTreeBuffer));
- header->header.size = outBuffer->size() - beforeXmlTreeIndex;
-}
-
-bool flatten(Node* root, const std::u16string& defaultPackage, BigBuffer* outBuffer) {
- StringPool pool;
-
- // This will hold the StringRefs and the location in which to write the index.
- // Once we sort the StringPool, we can assign the updated indices
- // to the correct data locations.
- FlatStringRefList stringRefs;
-
- // Since we don't know the size of the final StringPool, we write to this
- // temporary BigBuffer, which we will append to outBuffer later.
- BigBuffer out(1024);
-
- CompileXmlFlattener flattener(&out, &pool, &stringRefs, defaultPackage);
- root->accept(&flattener);
-
- if (!flattener.success()) {
- return false;
- }
-
- flattenXml(&pool, &stringRefs, outBuffer, std::move(out));
- return true;
-};
-
-Maybe<size_t> flattenAndLink(const Source& source, Node* root,
- const std::u16string& defaultPackage,
- const std::shared_ptr<IResolver>& resolver,
- const FlattenOptions& options, BigBuffer* outBuffer) {
- SourceLogger logger(source);
- StringPool pool;
-
- // Attribute names are stored without packages, but we use
- // their StringPool index to lookup their resource IDs.
- // This will cause collisions, so we can't dedupe
- // attribute names from different packages. We use separate
- // pools that we later combine.
- std::map<std::u16string, StringPool> packagePools;
-
- FlatStringRefList stringRefs;
-
- // Since we don't know the size of the final StringPool, we write to this
- // temporary BigBuffer, which we will append to outBuffer later.
- BigBuffer out(1024);
-
- LinkedXmlFlattener flattener(&out, &pool, &packagePools, &stringRefs, defaultPackage, resolver,
- &logger, options);
- root->accept(&flattener);
-
- if (!flattener.success()) {
- return {};
- }
-
- // Merge the package pools into the main pool.
- for (auto& packagePoolEntry : packagePools) {
- pool.merge(std::move(packagePoolEntry.second));
- }
-
- flattenXml(&pool, &stringRefs, outBuffer, std::move(out));
-
- if (flattener.getSmallestFilteredSdk()) {
- return flattener.getSmallestFilteredSdk();
- }
- return 0;
-}
-
-} // namespace xml
-} // namespace aapt
diff --git a/tools/aapt2/XmlFlattener.h b/tools/aapt2/XmlFlattener.h
deleted file mode 100644
index 4ece0a3..0000000
--- a/tools/aapt2/XmlFlattener.h
+++ /dev/null
@@ -1,69 +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_XML_FLATTENER_H
-#define AAPT_XML_FLATTENER_H
-
-#include "BigBuffer.h"
-#include "Maybe.h"
-#include "Resolver.h"
-#include "Source.h"
-#include "XmlDom.h"
-
-#include <string>
-
-namespace aapt {
-namespace xml {
-
-/**
- * Flattens an XML file into a binary representation parseable by
- * the Android resource system.
- */
-bool flatten(Node* root, const std::u16string& defaultPackage, BigBuffer* outBuffer);
-
-/**
- * Options for flattenAndLink.
- */
-struct FlattenOptions {
- /**
- * Keep attribute raw string values along with typed values.
- */
- bool keepRawValues = false;
-
- /**
- * If set, any attribute introduced in a later SDK will not be encoded.
- */
- Maybe<size_t> maxSdkAttribute;
-};
-
-/**
- * Like flatten(Node*,BigBuffer*), but references to resources are checked
- * and string values are transformed to typed data where possible.
- *
- * `defaultPackage` is used when a reference has no package or the namespace URI
- * "http://schemas.android.com/apk/res-auto" is used.
- *
- * `resolver` is used to resolve references to resources.
- */
-Maybe<size_t> flattenAndLink(const Source& source, Node* root,
- const std::u16string& defaultPackage,
- const std::shared_ptr<IResolver>& resolver,
- const FlattenOptions& options, BigBuffer* outBuffer);
-
-} // namespace xml
-} // namespace aapt
-
-#endif // AAPT_XML_FLATTENER_H
diff --git a/tools/aapt2/XmlFlattener_test.cpp b/tools/aapt2/XmlFlattener_test.cpp
deleted file mode 100644
index 8915d24..0000000
--- a/tools/aapt2/XmlFlattener_test.cpp
+++ /dev/null
@@ -1,232 +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.
- */
-
-#include "MockResolver.h"
-#include "ResourceTable.h"
-#include "ResourceValues.h"
-#include "Util.h"
-#include "XmlFlattener.h"
-
-#include <androidfw/AssetManager.h>
-#include <androidfw/ResourceTypes.h>
-#include <gtest/gtest.h>
-#include <sstream>
-#include <string>
-
-using namespace android;
-
-namespace aapt {
-namespace xml {
-
-constexpr const char* kXmlPreamble = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
-
-class XmlFlattenerTest : public ::testing::Test {
-public:
- virtual void SetUp() override {
- mResolver = std::make_shared<MockResolver>(
- std::make_shared<ResourceTable>(),
- std::map<ResourceName, ResourceId>({
- { ResourceName{ u"android", ResourceType::kAttr, u"attr" },
- ResourceId{ 0x01010000u } },
- { ResourceName{ u"android", ResourceType::kId, u"id" },
- ResourceId{ 0x01020000u } },
- { ResourceName{ u"com.lib", ResourceType::kAttr, u"attr" },
- ResourceId{ 0x01010001u } },
- { ResourceName{ u"com.lib", ResourceType::kId, u"id" },
- ResourceId{ 0x01020001u } }}));
- }
-
- ::testing::AssertionResult testFlatten(const std::string& in, ResXMLTree* outTree) {
- std::stringstream input(kXmlPreamble);
- input << in << std::endl;
-
- SourceLogger logger(Source{ "test.xml" });
- std::unique_ptr<Node> root = inflate(&input, &logger);
- if (!root) {
- return ::testing::AssertionFailure();
- }
-
- BigBuffer outBuffer(1024);
- if (!flattenAndLink(Source{ "test.xml" }, root.get(), std::u16string(u"android"),
- mResolver, {}, &outBuffer)) {
- return ::testing::AssertionFailure();
- }
-
- std::unique_ptr<uint8_t[]> data = util::copy(outBuffer);
- if (outTree->setTo(data.get(), outBuffer.size(), true) != NO_ERROR) {
- return ::testing::AssertionFailure();
- }
- return ::testing::AssertionSuccess();
- }
-
- std::shared_ptr<IResolver> mResolver;
-};
-
-TEST_F(XmlFlattenerTest, ParseSimpleView) {
- std::string input = R"EOF(
- <View xmlns:android="http://schemas.android.com/apk/res/android"
- android:attr="@id/id"
- class="str"
- style="@id/id">
- </View>
- )EOF";
- ResXMLTree tree;
- ASSERT_TRUE(testFlatten(input, &tree));
-
- while (tree.next() != ResXMLTree::START_TAG) {
- ASSERT_NE(tree.getEventType(), ResXMLTree::END_DOCUMENT);
- ASSERT_NE(tree.getEventType(), ResXMLTree::BAD_DOCUMENT);
- }
-
- const StringPiece16 androidNs = u"http://schemas.android.com/apk/res/android";
- const StringPiece16 attrName = u"attr";
- ssize_t idx = tree.indexOfAttribute(androidNs.data(), androidNs.size(), attrName.data(),
- attrName.size());
- ASSERT_GE(idx, 0);
- EXPECT_EQ(tree.getAttributeNameResID(idx), 0x01010000u);
- EXPECT_EQ(tree.getAttributeDataType(idx), android::Res_value::TYPE_REFERENCE);
-
- const StringPiece16 class16 = u"class";
- idx = tree.indexOfAttribute(nullptr, 0, class16.data(), class16.size());
- ASSERT_GE(idx, 0);
- EXPECT_EQ(tree.getAttributeNameResID(idx), 0u);
- EXPECT_EQ(tree.getAttributeDataType(idx), android::Res_value::TYPE_STRING);
- EXPECT_EQ(tree.getAttributeData(idx), tree.getAttributeValueStringID(idx));
-
- const StringPiece16 style16 = u"style";
- idx = tree.indexOfAttribute(nullptr, 0, style16.data(), style16.size());
- ASSERT_GE(idx, 0);
- EXPECT_EQ(tree.getAttributeNameResID(idx), 0u);
- EXPECT_EQ(tree.getAttributeDataType(idx), android::Res_value::TYPE_REFERENCE);
- EXPECT_EQ((uint32_t) tree.getAttributeData(idx), 0x01020000u);
- EXPECT_EQ(tree.getAttributeValueStringID(idx), -1);
-
- while (tree.next() != ResXMLTree::END_DOCUMENT) {
- ASSERT_NE(tree.getEventType(), ResXMLTree::BAD_DOCUMENT);
- }
-}
-
-TEST_F(XmlFlattenerTest, ParseViewWithPackageAlias) {
- std::string input = "<View xmlns:ns1=\"http://schemas.android.com/apk/res/android\"\n"
- " xmlns:ns2=\"http://schemas.android.com/apk/res/android\"\n"
- " ns1:attr=\"@ns2:id/id\">\n"
- "</View>";
- ResXMLTree tree;
- ASSERT_TRUE(testFlatten(input, &tree));
-
- while (tree.next() != ResXMLTree::END_DOCUMENT) {
- ASSERT_NE(tree.getEventType(), ResXMLTree::BAD_DOCUMENT);
- }
-}
-
-::testing::AssertionResult attributeNameAndValueEquals(ResXMLTree* tree, size_t index,
- ResourceId nameId, ResourceId valueId) {
- if (index >= tree->getAttributeCount()) {
- return ::testing::AssertionFailure() << "index " << index << " is out of bounds ("
- << tree->getAttributeCount() << ")";
- }
-
- if (tree->getAttributeNameResID(index) != nameId.id) {
- return ::testing::AssertionFailure()
- << "attribute at index " << index << " has ID "
- << ResourceId{ (uint32_t) tree->getAttributeNameResID(index) }
- << ". Expected ID " << nameId;
- }
-
- if (tree->getAttributeDataType(index) != Res_value::TYPE_REFERENCE) {
- return ::testing::AssertionFailure() << "attribute at index " << index << " has value of "
- << "type " << std::hex
- << tree->getAttributeDataType(index) << std::dec
- << ". Expected reference (" << std::hex
- << Res_value::TYPE_REFERENCE << std::dec << ")";
- }
-
- if ((uint32_t) tree->getAttributeData(index) != valueId.id) {
- return ::testing::AssertionFailure()
- << "attribute at index " << index << " has value " << "with ID "
- << ResourceId{ (uint32_t) tree->getAttributeData(index) }
- << ". Expected ID " << valueId;
- }
- return ::testing::AssertionSuccess();
-}
-
-TEST_F(XmlFlattenerTest, ParseViewWithShadowedPackageAlias) {
- std::string input = "<View xmlns:app=\"http://schemas.android.com/apk/res/android\"\n"
- " app:attr=\"@app:id/id\">\n"
- " <View xmlns:app=\"http://schemas.android.com/apk/res/com.lib\"\n"
- " app:attr=\"@app:id/id\"/>\n"
- "</View>";
- ResXMLTree tree;
- ASSERT_TRUE(testFlatten(input, &tree));
-
- while (tree.next() != ResXMLTree::START_TAG) {
- ASSERT_NE(tree.getEventType(), ResXMLTree::BAD_DOCUMENT);
- ASSERT_NE(tree.getEventType(), ResXMLTree::END_DOCUMENT);
- }
-
- ASSERT_TRUE(attributeNameAndValueEquals(&tree, 0u, ResourceId{ 0x01010000u },
- ResourceId{ 0x01020000u }));
-
- while (tree.next() != ResXMLTree::START_TAG) {
- ASSERT_NE(tree.getEventType(), ResXMLTree::BAD_DOCUMENT);
- ASSERT_NE(tree.getEventType(), ResXMLTree::END_DOCUMENT);
- }
-
- ASSERT_TRUE(attributeNameAndValueEquals(&tree, 0u, ResourceId{ 0x01010001u },
- ResourceId{ 0x01020001u }));
-}
-
-TEST_F(XmlFlattenerTest, ParseViewWithLocalPackageAndAliasOfTheSameName) {
- std::string input = "<View xmlns:android=\"http://schemas.android.com/apk/res/com.lib\"\n"
- " android:attr=\"@id/id\"/>";
- ResXMLTree tree;
- ASSERT_TRUE(testFlatten(input, &tree));
-
- while (tree.next() != ResXMLTree::START_TAG) {
- ASSERT_NE(tree.getEventType(), ResXMLTree::BAD_DOCUMENT);
- ASSERT_NE(tree.getEventType(), ResXMLTree::END_DOCUMENT);
- }
-
- // We expect the 'android:attr' to be converted to 'com.lib:attr' due to the namespace
- // assignment.
- // However, we didn't give '@id/id' a package, so it should use the default package
- // 'android', and not be converted from 'android' to 'com.lib'.
- ASSERT_TRUE(attributeNameAndValueEquals(&tree, 0u, ResourceId{ 0x01010001u },
- ResourceId{ 0x01020000u }));
-}
-
-/*
- * The device ResXMLParser in libandroidfw differentiates between empty namespace and null
- * namespace.
- */
-TEST_F(XmlFlattenerTest, NoNamespaceIsNotTheSameAsEmptyNamespace) {
- std::string input = "<View xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
- " package=\"android\"/>";
-
- ResXMLTree tree;
- ASSERT_TRUE(testFlatten(input, &tree));
-
- while (tree.next() != ResXMLTree::START_TAG) {
- ASSERT_NE(tree.getEventType(), ResXMLTree::BAD_DOCUMENT);
- ASSERT_NE(tree.getEventType(), ResXMLTree::END_DOCUMENT);
- }
-
- const StringPiece16 kPackage = u"package";
- EXPECT_GE(tree.indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size()), 0);
-}
-
-} // namespace xml
-} // namespace aapt
diff --git a/tools/aapt2/XmlPullParser.cpp b/tools/aapt2/XmlPullParser.cpp
new file mode 100644
index 0000000..cff935c
--- /dev/null
+++ b/tools/aapt2/XmlPullParser.cpp
@@ -0,0 +1,286 @@
+/*
+ * 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.
+ */
+
+#include "util/Maybe.h"
+#include "util/Util.h"
+#include "XmlPullParser.h"
+
+#include <iostream>
+#include <string>
+
+namespace aapt {
+
+constexpr char kXmlNamespaceSep = 1;
+
+XmlPullParser::XmlPullParser(std::istream& in) : mIn(in), mEmpty(), mDepth(0) {
+ mParser = XML_ParserCreateNS(nullptr, kXmlNamespaceSep);
+ XML_SetUserData(mParser, this);
+ XML_SetElementHandler(mParser, startElementHandler, endElementHandler);
+ XML_SetNamespaceDeclHandler(mParser, startNamespaceHandler, endNamespaceHandler);
+ XML_SetCharacterDataHandler(mParser, characterDataHandler);
+ XML_SetCommentHandler(mParser, commentDataHandler);
+ mEventQueue.push(EventData{ Event::kStartDocument, 0, mDepth++ });
+}
+
+XmlPullParser::~XmlPullParser() {
+ XML_ParserFree(mParser);
+}
+
+XmlPullParser::Event XmlPullParser::next() {
+ const Event currentEvent = getEvent();
+ if (currentEvent == Event::kBadDocument || currentEvent == Event::kEndDocument) {
+ return currentEvent;
+ }
+
+ mEventQueue.pop();
+ while (mEventQueue.empty()) {
+ mIn.read(mBuffer, sizeof(mBuffer) / sizeof(*mBuffer));
+
+ const bool done = mIn.eof();
+ if (mIn.bad() && !done) {
+ mLastError = strerror(errno);
+ mEventQueue.push(EventData{ Event::kBadDocument });
+ continue;
+ }
+
+ if (XML_Parse(mParser, mBuffer, mIn.gcount(), done) == XML_STATUS_ERROR) {
+ mLastError = XML_ErrorString(XML_GetErrorCode(mParser));
+ mEventQueue.push(EventData{ Event::kBadDocument });
+ continue;
+ }
+
+ if (done) {
+ mEventQueue.push(EventData{ Event::kEndDocument, 0, 0 });
+ }
+ }
+
+ Event event = getEvent();
+
+ // Record namespace prefixes and package names so that we can do our own
+ // handling of references that use namespace aliases.
+ if (event == Event::kStartNamespace || event == Event::kEndNamespace) {
+ Maybe<std::u16string> result = util::extractPackageFromNamespace(getNamespaceUri());
+ if (event == Event::kStartNamespace) {
+ if (result) {
+ mPackageAliases.emplace_back(getNamespacePrefix(), result.value());
+ }
+ } else {
+ if (result) {
+ assert(mPackageAliases.back().second == result.value());
+ mPackageAliases.pop_back();
+ }
+ }
+ }
+
+ return event;
+}
+
+XmlPullParser::Event XmlPullParser::getEvent() const {
+ return mEventQueue.front().event;
+}
+
+const std::string& XmlPullParser::getLastError() const {
+ return mLastError;
+}
+
+const std::u16string& XmlPullParser::getComment() const {
+ return mEventQueue.front().data1;
+}
+
+size_t XmlPullParser::getLineNumber() const {
+ return mEventQueue.front().lineNumber;
+}
+
+size_t XmlPullParser::getDepth() const {
+ return mEventQueue.front().depth;
+}
+
+const std::u16string& XmlPullParser::getText() const {
+ if (getEvent() != Event::kText) {
+ return mEmpty;
+ }
+ return mEventQueue.front().data1;
+}
+
+const std::u16string& XmlPullParser::getNamespacePrefix() const {
+ const Event currentEvent = getEvent();
+ if (currentEvent != Event::kStartNamespace && currentEvent != Event::kEndNamespace) {
+ return mEmpty;
+ }
+ return mEventQueue.front().data1;
+}
+
+const std::u16string& XmlPullParser::getNamespaceUri() const {
+ const Event currentEvent = getEvent();
+ if (currentEvent != Event::kStartNamespace && currentEvent != Event::kEndNamespace) {
+ return mEmpty;
+ }
+ return mEventQueue.front().data2;
+}
+
+Maybe<ResourceName> XmlPullParser::transformPackage(
+ const ResourceName& name, const StringPiece16& localPackage) const {
+ if (name.package.empty()) {
+ return ResourceName{ localPackage.toString(), name.type, name.entry };
+ }
+
+ const auto endIter = mPackageAliases.rend();
+ for (auto iter = mPackageAliases.rbegin(); iter != endIter; ++iter) {
+ if (name.package == iter->first) {
+ if (iter->second.empty()) {
+ return ResourceName{ localPackage.toString(), name.type, name.entry };
+ } else {
+ return ResourceName{ iter->second, name.type, name.entry };
+ }
+ }
+ }
+ return {};
+}
+
+const std::u16string& XmlPullParser::getElementNamespace() const {
+ const Event currentEvent = getEvent();
+ if (currentEvent != Event::kStartElement && currentEvent != Event::kEndElement) {
+ return mEmpty;
+ }
+ return mEventQueue.front().data1;
+}
+
+const std::u16string& XmlPullParser::getElementName() const {
+ const Event currentEvent = getEvent();
+ if (currentEvent != Event::kStartElement && currentEvent != Event::kEndElement) {
+ return mEmpty;
+ }
+ return mEventQueue.front().data2;
+}
+
+XmlPullParser::const_iterator XmlPullParser::beginAttributes() const {
+ return mEventQueue.front().attributes.begin();
+}
+
+XmlPullParser::const_iterator XmlPullParser::endAttributes() const {
+ return mEventQueue.front().attributes.end();
+}
+
+size_t XmlPullParser::getAttributeCount() const {
+ if (getEvent() != Event::kStartElement) {
+ return 0;
+ }
+ return mEventQueue.front().attributes.size();
+}
+
+/**
+ * Extracts the namespace and name of an expanded element or attribute name.
+ */
+static void splitName(const char* name, std::u16string& outNs, std::u16string& outName) {
+ const char* p = name;
+ while (*p != 0 && *p != kXmlNamespaceSep) {
+ p++;
+ }
+
+ if (*p == 0) {
+ outNs = std::u16string();
+ outName = util::utf8ToUtf16(name);
+ } else {
+ outNs = util::utf8ToUtf16(StringPiece(name, (p - name)));
+ outName = util::utf8ToUtf16(p + 1);
+ }
+}
+
+void XMLCALL XmlPullParser::startNamespaceHandler(void* userData, const char* prefix,
+ const char* uri) {
+ XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(userData);
+ std::u16string namespaceUri = uri != nullptr ? util::utf8ToUtf16(uri) : std::u16string();
+ parser->mNamespaceUris.push(namespaceUri);
+ parser->mEventQueue.push(EventData{
+ Event::kStartNamespace,
+ XML_GetCurrentLineNumber(parser->mParser),
+ parser->mDepth++,
+ prefix != nullptr ? util::utf8ToUtf16(prefix) : std::u16string(),
+ namespaceUri
+ });
+}
+
+void XMLCALL XmlPullParser::startElementHandler(void* userData, const char* name,
+ const char** attrs) {
+ XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(userData);
+
+ EventData data = {
+ Event::kStartElement, XML_GetCurrentLineNumber(parser->mParser), parser->mDepth++
+ };
+ splitName(name, data.data1, data.data2);
+
+ while (*attrs) {
+ Attribute attribute;
+ splitName(*attrs++, attribute.namespaceUri, attribute.name);
+ attribute.value = util::utf8ToUtf16(*attrs++);
+
+ // Insert in sorted order.
+ auto iter = std::lower_bound(data.attributes.begin(), data.attributes.end(), attribute);
+ data.attributes.insert(iter, std::move(attribute));
+ }
+
+ // Move the structure into the queue (no copy).
+ parser->mEventQueue.push(std::move(data));
+}
+
+void XMLCALL XmlPullParser::characterDataHandler(void* userData, const char* s, int len) {
+ XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(userData);
+
+ parser->mEventQueue.push(EventData{
+ Event::kText,
+ XML_GetCurrentLineNumber(parser->mParser),
+ parser->mDepth,
+ util::utf8ToUtf16(StringPiece(s, len))
+ });
+}
+
+void XMLCALL XmlPullParser::endElementHandler(void* userData, const char* name) {
+ XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(userData);
+
+ EventData data = {
+ Event::kEndElement, XML_GetCurrentLineNumber(parser->mParser), --(parser->mDepth)
+ };
+ splitName(name, data.data1, data.data2);
+
+ // Move the data into the queue (no copy).
+ parser->mEventQueue.push(std::move(data));
+}
+
+void XMLCALL XmlPullParser::endNamespaceHandler(void* userData, const char* prefix) {
+ XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(userData);
+
+ parser->mEventQueue.push(EventData{
+ Event::kEndNamespace,
+ XML_GetCurrentLineNumber(parser->mParser),
+ --(parser->mDepth),
+ prefix != nullptr ? util::utf8ToUtf16(prefix) : std::u16string(),
+ parser->mNamespaceUris.top()
+ });
+ parser->mNamespaceUris.pop();
+}
+
+void XMLCALL XmlPullParser::commentDataHandler(void* userData, const char* comment) {
+ XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(userData);
+
+ parser->mEventQueue.push(EventData{
+ Event::kComment,
+ XML_GetCurrentLineNumber(parser->mParser),
+ parser->mDepth,
+ util::utf8ToUtf16(comment)
+ });
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/XmlPullParser.h b/tools/aapt2/XmlPullParser.h
index accfd30a..a0ce21d 100644
--- a/tools/aapt2/XmlPullParser.h
+++ b/tools/aapt2/XmlPullParser.h
@@ -17,16 +17,24 @@
#ifndef AAPT_XML_PULL_PARSER_H
#define AAPT_XML_PULL_PARSER_H
+#include "util/Maybe.h"
+#include "Resource.h"
+#include "util/StringPiece.h"
+
+#include "process/IResourceTableConsumer.h"
+
#include <algorithm>
+#include <expat.h>
+#include <istream>
#include <ostream>
+#include <queue>
+#include <stack>
#include <string>
#include <vector>
-#include "StringPiece.h"
-
namespace aapt {
-class XmlPullParser {
+class XmlPullParser : public IPackageDeclStack {
public:
enum class Event {
kBadDocument,
@@ -41,43 +49,51 @@
kComment,
};
- static void skipCurrentElement(XmlPullParser* parser);
+ /**
+ * Skips to the next direct descendant node of the given startDepth,
+ * skipping namespace nodes.
+ *
+ * When nextChildNode returns true, you can expect Comments, Text, and StartElement events.
+ */
+ static bool nextChildNode(XmlPullParser* parser, size_t startDepth);
+ static bool skipCurrentElement(XmlPullParser* parser);
static bool isGoodEvent(Event event);
- virtual ~XmlPullParser() {}
+ XmlPullParser(std::istream& in);
+ virtual ~XmlPullParser();
/**
* Returns the current event that is being processed.
*/
- virtual Event getEvent() const = 0;
+ Event getEvent() const;
- virtual const std::string& getLastError() const = 0;
+ const std::string& getLastError() const;
/**
* Note, unlike XmlPullParser, the first call to next() will return
* StartElement of the first element.
*/
- virtual Event next() = 0;
+ Event next();
//
// These are available for all nodes.
//
- virtual const std::u16string& getComment() const = 0;
- virtual size_t getLineNumber() const = 0;
- virtual size_t getDepth() const = 0;
+ const std::u16string& getComment() const;
+ size_t getLineNumber() const;
+ size_t getDepth() const;
/**
* Returns the character data for a Text event.
*/
- virtual const std::u16string& getText() const = 0;
+ const std::u16string& getText() const;
//
// Namespace prefix and URI are available for StartNamespace and EndNamespace.
//
- virtual const std::u16string& getNamespacePrefix() const = 0;
- virtual const std::u16string& getNamespaceUri() const = 0;
+ const std::u16string& getNamespacePrefix() const;
+ const std::u16string& getNamespaceUri() const;
/*
* Uses the current stack of namespaces to resolve the package. Eg:
@@ -90,15 +106,17 @@
* If xmlns:app="http://schemas.android.com/apk/res-auto", then
* 'package' will be set to 'defaultPackage'.
*/
- virtual bool applyPackageAlias(std::u16string* package,
- const std::u16string& defaultPackage) const = 0;
+ //
//
// These are available for StartElement and EndElement.
//
- virtual const std::u16string& getElementNamespace() const = 0;
- virtual const std::u16string& getElementName() const = 0;
+ const std::u16string& getElementNamespace() const;
+ const std::u16string& getElementName() const;
+
+ Maybe<ResourceName> transformPackage(const ResourceName& name,
+ const StringPiece16& localPackage) const override;
//
// Remaining methods are for retrieving information about attributes
@@ -121,10 +139,37 @@
using const_iterator = std::vector<Attribute>::const_iterator;
- virtual const_iterator beginAttributes() const = 0;
- virtual const_iterator endAttributes() const = 0;
- virtual size_t getAttributeCount() const = 0;
+ const_iterator beginAttributes() const;
+ const_iterator endAttributes() const;
+ size_t getAttributeCount() const;
const_iterator findAttribute(StringPiece16 namespaceUri, StringPiece16 name) const;
+
+private:
+ static void XMLCALL startNamespaceHandler(void* userData, const char* prefix, const char* uri);
+ static void XMLCALL startElementHandler(void* userData, const char* name, const char** attrs);
+ static void XMLCALL characterDataHandler(void* userData, const char* s, int len);
+ static void XMLCALL endElementHandler(void* userData, const char* name);
+ static void XMLCALL endNamespaceHandler(void* userData, const char* prefix);
+ static void XMLCALL commentDataHandler(void* userData, const char* comment);
+
+ struct EventData {
+ Event event;
+ size_t lineNumber;
+ size_t depth;
+ std::u16string data1;
+ std::u16string data2;
+ std::vector<Attribute> attributes;
+ };
+
+ std::istream& mIn;
+ XML_Parser mParser;
+ char mBuffer[16384];
+ std::queue<EventData> mEventQueue;
+ std::string mLastError;
+ const std::u16string mEmpty;
+ size_t mDepth;
+ std::stack<std::u16string> mNamespaceUris;
+ std::vector<std::pair<std::u16string, std::u16string>> mPackageAliases;
};
//
@@ -146,13 +191,35 @@
return out;
}
-inline void XmlPullParser::skipCurrentElement(XmlPullParser* parser) {
+inline bool XmlPullParser::nextChildNode(XmlPullParser* parser, size_t startDepth) {
+ Event event;
+
+ // First get back to the start depth.
+ while (isGoodEvent(event = parser->next()) && parser->getDepth() > startDepth + 1) {}
+
+ // Now look for the first good node.
+ while ((event != Event::kEndElement || parser->getDepth() > startDepth) && isGoodEvent(event)) {
+ switch (event) {
+ case Event::kText:
+ case Event::kComment:
+ case Event::kStartElement:
+ return true;
+ default:
+ break;
+ }
+ event = parser->next();
+ }
+ return false;
+}
+
+inline bool XmlPullParser::skipCurrentElement(XmlPullParser* parser) {
int depth = 1;
while (depth > 0) {
switch (parser->next()) {
case Event::kEndDocument:
+ return true;
case Event::kBadDocument:
- return;
+ return false;
case Event::kStartElement:
depth++;
break;
@@ -163,6 +230,7 @@
break;
}
}
+ return true;
}
inline bool XmlPullParser::isGoodEvent(XmlPullParser::Event event) {
diff --git a/tools/aapt2/XmlPullParser_test.cpp b/tools/aapt2/XmlPullParser_test.cpp
new file mode 100644
index 0000000..1c99a43
--- /dev/null
+++ b/tools/aapt2/XmlPullParser_test.cpp
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#include "util/StringPiece.h"
+#include "XmlPullParser.h"
+
+#include <gtest/gtest.h>
+#include <sstream>
+
+namespace aapt {
+
+TEST(XmlPullParserTest, NextChildNodeTraversesCorrectly) {
+ std::stringstream str;
+ str << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<a><b><c xmlns:a=\"http://schema.org\"><d/></c><e/></b></a>";
+ XmlPullParser parser(str);
+
+ const size_t depthOuter = parser.getDepth();
+ ASSERT_TRUE(XmlPullParser::nextChildNode(&parser, depthOuter));
+
+ EXPECT_EQ(XmlPullParser::Event::kStartElement, parser.getEvent());
+ EXPECT_EQ(StringPiece16(u"a"), StringPiece16(parser.getElementName()));
+
+ const size_t depthA = parser.getDepth();
+ ASSERT_TRUE(XmlPullParser::nextChildNode(&parser, depthA));
+ EXPECT_EQ(XmlPullParser::Event::kStartElement, parser.getEvent());
+ EXPECT_EQ(StringPiece16(u"b"), StringPiece16(parser.getElementName()));
+
+ const size_t depthB = parser.getDepth();
+ ASSERT_TRUE(XmlPullParser::nextChildNode(&parser, depthB));
+ EXPECT_EQ(XmlPullParser::Event::kStartElement, parser.getEvent());
+ EXPECT_EQ(StringPiece16(u"c"), StringPiece16(parser.getElementName()));
+
+ ASSERT_TRUE(XmlPullParser::nextChildNode(&parser, depthB));
+ EXPECT_EQ(XmlPullParser::Event::kStartElement, parser.getEvent());
+ EXPECT_EQ(StringPiece16(u"e"), StringPiece16(parser.getElementName()));
+
+ ASSERT_FALSE(XmlPullParser::nextChildNode(&parser, depthOuter));
+ EXPECT_EQ(XmlPullParser::Event::kEndDocument, parser.getEvent());
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/ZipEntry.cpp b/tools/aapt2/ZipEntry.cpp
deleted file mode 100644
index 891b4e1..0000000
--- a/tools/aapt2/ZipEntry.cpp
+++ /dev/null
@@ -1,745 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-// Access to entries in a Zip archive.
-//
-
-#define LOG_TAG "zip"
-
-#include "ZipEntry.h"
-#include <utils/Log.h>
-
-#include <stdio.h>
-#include <string.h>
-#include <assert.h>
-
-namespace aapt {
-
-using namespace android;
-
-/*
- * Initialize a new ZipEntry structure from a FILE* positioned at a
- * CentralDirectoryEntry.
- *
- * On exit, the file pointer will be at the start of the next CDE or
- * at the EOCD.
- */
-status_t ZipEntry::initFromCDE(FILE* fp)
-{
- status_t result;
- long posn;
- bool hasDD;
-
- //ALOGV("initFromCDE ---\n");
-
- /* read the CDE */
- result = mCDE.read(fp);
- if (result != NO_ERROR) {
- ALOGD("mCDE.read failed\n");
- return result;
- }
-
- //mCDE.dump();
-
- /* using the info in the CDE, go load up the LFH */
- posn = ftell(fp);
- if (fseek(fp, mCDE.mLocalHeaderRelOffset, SEEK_SET) != 0) {
- ALOGD("local header seek failed (%ld)\n",
- mCDE.mLocalHeaderRelOffset);
- return UNKNOWN_ERROR;
- }
-
- result = mLFH.read(fp);
- if (result != NO_ERROR) {
- ALOGD("mLFH.read failed\n");
- return result;
- }
-
- if (fseek(fp, posn, SEEK_SET) != 0)
- return UNKNOWN_ERROR;
-
- //mLFH.dump();
-
- /*
- * We *might* need to read the Data Descriptor at this point and
- * integrate it into the LFH. If this bit is set, the CRC-32,
- * compressed size, and uncompressed size will be zero. In practice
- * these seem to be rare.
- */
- hasDD = (mLFH.mGPBitFlag & kUsesDataDescr) != 0;
- if (hasDD) {
- // do something clever
- //ALOGD("+++ has data descriptor\n");
- }
-
- /*
- * Sanity-check the LFH. Note that this will fail if the "kUsesDataDescr"
- * flag is set, because the LFH is incomplete. (Not a problem, since we
- * prefer the CDE values.)
- */
- if (!hasDD && !compareHeaders()) {
- ALOGW("warning: header mismatch\n");
- // keep going?
- }
-
- /*
- * If the mVersionToExtract is greater than 20, we may have an
- * issue unpacking the record -- could be encrypted, compressed
- * with something we don't support, or use Zip64 extensions. We
- * can defer worrying about that to when we're extracting data.
- */
-
- return NO_ERROR;
-}
-
-/*
- * Initialize a new entry. Pass in the file name and an optional comment.
- *
- * Initializes the CDE and the LFH.
- */
-void ZipEntry::initNew(const char* fileName, const char* comment)
-{
- assert(fileName != NULL && *fileName != '\0'); // name required
-
- /* most fields are properly initialized by constructor */
- mCDE.mVersionMadeBy = kDefaultMadeBy;
- mCDE.mVersionToExtract = kDefaultVersion;
- mCDE.mCompressionMethod = kCompressStored;
- mCDE.mFileNameLength = strlen(fileName);
- if (comment != NULL)
- mCDE.mFileCommentLength = strlen(comment);
- mCDE.mExternalAttrs = 0x81b60020; // matches what WinZip does
-
- if (mCDE.mFileNameLength > 0) {
- mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1];
- strcpy((char*) mCDE.mFileName, fileName);
- }
- if (mCDE.mFileCommentLength > 0) {
- /* TODO: stop assuming null-terminated ASCII here? */
- mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1];
- strcpy((char*) mCDE.mFileComment, comment);
- }
-
- copyCDEtoLFH();
-}
-
-/*
- * Initialize a new entry, starting with the ZipEntry from a different
- * archive.
- *
- * Initializes the CDE and the LFH.
- */
-status_t ZipEntry::initFromExternal(const ZipFile* /* pZipFile */,
- const ZipEntry* pEntry, const char* storageName)
-{
- mCDE = pEntry->mCDE;
- if (storageName && *storageName != 0) {
- mCDE.mFileNameLength = strlen(storageName);
- mCDE.mFileName = new unsigned char[mCDE.mFileNameLength + 1];
- strcpy((char*) mCDE.mFileName, storageName);
- }
-
- // Check whether we got all the memory needed.
- if ((mCDE.mFileNameLength > 0 && mCDE.mFileName == NULL) ||
- (mCDE.mFileCommentLength > 0 && mCDE.mFileComment == NULL) ||
- (mCDE.mExtraFieldLength > 0 && mCDE.mExtraField == NULL)) {
- return NO_MEMORY;
- }
-
- /* construct the LFH from the CDE */
- copyCDEtoLFH();
-
- /*
- * The LFH "extra" field is independent of the CDE "extra", so we
- * handle it here.
- */
- assert(mLFH.mExtraField == NULL);
- mLFH.mExtraFieldLength = pEntry->mLFH.mExtraFieldLength;
- if (mLFH.mExtraFieldLength > 0) {
- mLFH.mExtraField = new unsigned char[mLFH.mExtraFieldLength+1];
- if (mLFH.mExtraField == NULL)
- return NO_MEMORY;
- memcpy(mLFH.mExtraField, pEntry->mLFH.mExtraField,
- mLFH.mExtraFieldLength+1);
- }
-
- return NO_ERROR;
-}
-
-/*
- * Insert pad bytes in the LFH by tweaking the "extra" field. This will
- * potentially confuse something that put "extra" data in here earlier,
- * but I can't find an actual problem.
- */
-status_t ZipEntry::addPadding(int padding)
-{
- if (padding <= 0)
- return INVALID_OPERATION;
-
- //ALOGI("HEY: adding %d pad bytes to existing %d in %s\n",
- // padding, mLFH.mExtraFieldLength, mCDE.mFileName);
-
- if (mLFH.mExtraFieldLength > 0) {
- /* extend existing field */
- unsigned char* newExtra;
-
- newExtra = new unsigned char[mLFH.mExtraFieldLength + padding];
- if (newExtra == NULL)
- return NO_MEMORY;
- memset(newExtra + mLFH.mExtraFieldLength, 0, padding);
- memcpy(newExtra, mLFH.mExtraField, mLFH.mExtraFieldLength);
-
- delete[] mLFH.mExtraField;
- mLFH.mExtraField = newExtra;
- mLFH.mExtraFieldLength += padding;
- } else {
- /* create new field */
- mLFH.mExtraField = new unsigned char[padding];
- memset(mLFH.mExtraField, 0, padding);
- mLFH.mExtraFieldLength = padding;
- }
-
- return NO_ERROR;
-}
-
-/*
- * Set the fields in the LFH equal to the corresponding fields in the CDE.
- *
- * This does not touch the LFH "extra" field.
- */
-void ZipEntry::copyCDEtoLFH(void)
-{
- mLFH.mVersionToExtract = mCDE.mVersionToExtract;
- mLFH.mGPBitFlag = mCDE.mGPBitFlag;
- mLFH.mCompressionMethod = mCDE.mCompressionMethod;
- mLFH.mLastModFileTime = mCDE.mLastModFileTime;
- mLFH.mLastModFileDate = mCDE.mLastModFileDate;
- mLFH.mCRC32 = mCDE.mCRC32;
- mLFH.mCompressedSize = mCDE.mCompressedSize;
- mLFH.mUncompressedSize = mCDE.mUncompressedSize;
- mLFH.mFileNameLength = mCDE.mFileNameLength;
- // the "extra field" is independent
-
- delete[] mLFH.mFileName;
- if (mLFH.mFileNameLength > 0) {
- mLFH.mFileName = new unsigned char[mLFH.mFileNameLength+1];
- strcpy((char*) mLFH.mFileName, (const char*) mCDE.mFileName);
- } else {
- mLFH.mFileName = NULL;
- }
-}
-
-/*
- * Set some information about a file after we add it.
- */
-void ZipEntry::setDataInfo(long uncompLen, long compLen, unsigned long crc32,
- int compressionMethod)
-{
- mCDE.mCompressionMethod = compressionMethod;
- mCDE.mCRC32 = crc32;
- mCDE.mCompressedSize = compLen;
- mCDE.mUncompressedSize = uncompLen;
- mCDE.mCompressionMethod = compressionMethod;
- if (compressionMethod == kCompressDeflated) {
- mCDE.mGPBitFlag |= 0x0002; // indicates maximum compression used
- }
- copyCDEtoLFH();
-}
-
-/*
- * See if the data in mCDE and mLFH match up. This is mostly useful for
- * debugging these classes, but it can be used to identify damaged
- * archives.
- *
- * Returns "false" if they differ.
- */
-bool ZipEntry::compareHeaders(void) const
-{
- if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) {
- ALOGV("cmp: VersionToExtract\n");
- return false;
- }
- if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) {
- ALOGV("cmp: GPBitFlag\n");
- return false;
- }
- if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) {
- ALOGV("cmp: CompressionMethod\n");
- return false;
- }
- if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) {
- ALOGV("cmp: LastModFileTime\n");
- return false;
- }
- if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) {
- ALOGV("cmp: LastModFileDate\n");
- return false;
- }
- if (mCDE.mCRC32 != mLFH.mCRC32) {
- ALOGV("cmp: CRC32\n");
- return false;
- }
- if (mCDE.mCompressedSize != mLFH.mCompressedSize) {
- ALOGV("cmp: CompressedSize\n");
- return false;
- }
- if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) {
- ALOGV("cmp: UncompressedSize\n");
- return false;
- }
- if (mCDE.mFileNameLength != mLFH.mFileNameLength) {
- ALOGV("cmp: FileNameLength\n");
- return false;
- }
-#if 0 // this seems to be used for padding, not real data
- if (mCDE.mExtraFieldLength != mLFH.mExtraFieldLength) {
- ALOGV("cmp: ExtraFieldLength\n");
- return false;
- }
-#endif
- if (mCDE.mFileName != NULL) {
- if (strcmp((char*) mCDE.mFileName, (char*) mLFH.mFileName) != 0) {
- ALOGV("cmp: FileName\n");
- return false;
- }
- }
-
- return true;
-}
-
-
-/*
- * Convert the DOS date/time stamp into a UNIX time stamp.
- */
-time_t ZipEntry::getModWhen(void) const
-{
- struct tm parts;
-
- parts.tm_sec = (mCDE.mLastModFileTime & 0x001f) << 1;
- parts.tm_min = (mCDE.mLastModFileTime & 0x07e0) >> 5;
- parts.tm_hour = (mCDE.mLastModFileTime & 0xf800) >> 11;
- parts.tm_mday = (mCDE.mLastModFileDate & 0x001f);
- parts.tm_mon = ((mCDE.mLastModFileDate & 0x01e0) >> 5) -1;
- parts.tm_year = ((mCDE.mLastModFileDate & 0xfe00) >> 9) + 80;
- parts.tm_wday = parts.tm_yday = 0;
- parts.tm_isdst = -1; // DST info "not available"
-
- return mktime(&parts);
-}
-
-/*
- * Set the CDE/LFH timestamp from UNIX time.
- */
-void ZipEntry::setModWhen(time_t when)
-{
-#if !defined(_WIN32)
- struct tm tmResult;
-#endif
- time_t even;
- unsigned short zdate, ztime;
-
- struct tm* ptm;
-
- /* round up to an even number of seconds */
- even = (time_t)(((unsigned long)(when) + 1) & (~1));
-
- /* expand */
-#if !defined(_WIN32)
- ptm = localtime_r(&even, &tmResult);
-#else
- ptm = localtime(&even);
-#endif
-
- int year;
- year = ptm->tm_year;
- if (year < 80)
- year = 80;
-
- zdate = (year - 80) << 9 | (ptm->tm_mon+1) << 5 | ptm->tm_mday;
- ztime = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1;
-
- mCDE.mLastModFileTime = mLFH.mLastModFileTime = ztime;
- mCDE.mLastModFileDate = mLFH.mLastModFileDate = zdate;
-}
-
-
-/*
- * ===========================================================================
- * ZipEntry::LocalFileHeader
- * ===========================================================================
- */
-
-/*
- * Read a local file header.
- *
- * On entry, "fp" points to the signature at the start of the header.
- * On exit, "fp" points to the start of data.
- */
-status_t ZipEntry::LocalFileHeader::read(FILE* fp)
-{
- status_t result = NO_ERROR;
- unsigned char buf[kLFHLen];
-
- assert(mFileName == NULL);
- assert(mExtraField == NULL);
-
- if (fread(buf, 1, kLFHLen, fp) != kLFHLen) {
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
- ALOGD("whoops: didn't find expected signature\n");
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- mVersionToExtract = ZipEntry::getShortLE(&buf[0x04]);
- mGPBitFlag = ZipEntry::getShortLE(&buf[0x06]);
- mCompressionMethod = ZipEntry::getShortLE(&buf[0x08]);
- mLastModFileTime = ZipEntry::getShortLE(&buf[0x0a]);
- mLastModFileDate = ZipEntry::getShortLE(&buf[0x0c]);
- mCRC32 = ZipEntry::getLongLE(&buf[0x0e]);
- mCompressedSize = ZipEntry::getLongLE(&buf[0x12]);
- mUncompressedSize = ZipEntry::getLongLE(&buf[0x16]);
- mFileNameLength = ZipEntry::getShortLE(&buf[0x1a]);
- mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1c]);
-
- // TODO: validate sizes
-
- /* grab filename */
- if (mFileNameLength != 0) {
- mFileName = new unsigned char[mFileNameLength+1];
- if (mFileName == NULL) {
- result = NO_MEMORY;
- goto bail;
- }
- if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
- result = UNKNOWN_ERROR;
- goto bail;
- }
- mFileName[mFileNameLength] = '\0';
- }
-
- /* grab extra field */
- if (mExtraFieldLength != 0) {
- mExtraField = new unsigned char[mExtraFieldLength+1];
- if (mExtraField == NULL) {
- result = NO_MEMORY;
- goto bail;
- }
- if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
- result = UNKNOWN_ERROR;
- goto bail;
- }
- mExtraField[mExtraFieldLength] = '\0';
- }
-
-bail:
- return result;
-}
-
-/*
- * Write a local file header.
- */
-status_t ZipEntry::LocalFileHeader::write(FILE* fp)
-{
- unsigned char buf[kLFHLen];
-
- ZipEntry::putLongLE(&buf[0x00], kSignature);
- ZipEntry::putShortLE(&buf[0x04], mVersionToExtract);
- ZipEntry::putShortLE(&buf[0x06], mGPBitFlag);
- ZipEntry::putShortLE(&buf[0x08], mCompressionMethod);
- ZipEntry::putShortLE(&buf[0x0a], mLastModFileTime);
- ZipEntry::putShortLE(&buf[0x0c], mLastModFileDate);
- ZipEntry::putLongLE(&buf[0x0e], mCRC32);
- ZipEntry::putLongLE(&buf[0x12], mCompressedSize);
- ZipEntry::putLongLE(&buf[0x16], mUncompressedSize);
- ZipEntry::putShortLE(&buf[0x1a], mFileNameLength);
- ZipEntry::putShortLE(&buf[0x1c], mExtraFieldLength);
-
- if (fwrite(buf, 1, kLFHLen, fp) != kLFHLen)
- return UNKNOWN_ERROR;
-
- /* write filename */
- if (mFileNameLength != 0) {
- if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
- return UNKNOWN_ERROR;
- }
-
- /* write "extra field" */
- if (mExtraFieldLength != 0) {
- if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
- return UNKNOWN_ERROR;
- }
-
- return NO_ERROR;
-}
-
-
-/*
- * Dump the contents of a LocalFileHeader object.
- */
-void ZipEntry::LocalFileHeader::dump(void) const
-{
- ALOGD(" LocalFileHeader contents:\n");
- ALOGD(" versToExt=%u gpBits=0x%04x compression=%u\n",
- mVersionToExtract, mGPBitFlag, mCompressionMethod);
- ALOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
- mLastModFileTime, mLastModFileDate, mCRC32);
- ALOGD(" compressedSize=%lu uncompressedSize=%lu\n",
- mCompressedSize, mUncompressedSize);
- ALOGD(" filenameLen=%u extraLen=%u\n",
- mFileNameLength, mExtraFieldLength);
- if (mFileName != NULL)
- ALOGD(" filename: '%s'\n", mFileName);
-}
-
-
-/*
- * ===========================================================================
- * ZipEntry::CentralDirEntry
- * ===========================================================================
- */
-
-/*
- * Read the central dir entry that appears next in the file.
- *
- * On entry, "fp" should be positioned on the signature bytes for the
- * entry. On exit, "fp" will point at the signature word for the next
- * entry or for the EOCD.
- */
-status_t ZipEntry::CentralDirEntry::read(FILE* fp)
-{
- status_t result = NO_ERROR;
- unsigned char buf[kCDELen];
-
- /* no re-use */
- assert(mFileName == NULL);
- assert(mExtraField == NULL);
- assert(mFileComment == NULL);
-
- if (fread(buf, 1, kCDELen, fp) != kCDELen) {
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
- ALOGD("Whoops: didn't find expected signature\n");
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- mVersionMadeBy = ZipEntry::getShortLE(&buf[0x04]);
- mVersionToExtract = ZipEntry::getShortLE(&buf[0x06]);
- mGPBitFlag = ZipEntry::getShortLE(&buf[0x08]);
- mCompressionMethod = ZipEntry::getShortLE(&buf[0x0a]);
- mLastModFileTime = ZipEntry::getShortLE(&buf[0x0c]);
- mLastModFileDate = ZipEntry::getShortLE(&buf[0x0e]);
- mCRC32 = ZipEntry::getLongLE(&buf[0x10]);
- mCompressedSize = ZipEntry::getLongLE(&buf[0x14]);
- mUncompressedSize = ZipEntry::getLongLE(&buf[0x18]);
- mFileNameLength = ZipEntry::getShortLE(&buf[0x1c]);
- mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1e]);
- mFileCommentLength = ZipEntry::getShortLE(&buf[0x20]);
- mDiskNumberStart = ZipEntry::getShortLE(&buf[0x22]);
- mInternalAttrs = ZipEntry::getShortLE(&buf[0x24]);
- mExternalAttrs = ZipEntry::getLongLE(&buf[0x26]);
- mLocalHeaderRelOffset = ZipEntry::getLongLE(&buf[0x2a]);
-
- // TODO: validate sizes and offsets
-
- /* grab filename */
- if (mFileNameLength != 0) {
- mFileName = new unsigned char[mFileNameLength+1];
- if (mFileName == NULL) {
- result = NO_MEMORY;
- goto bail;
- }
- if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
- result = UNKNOWN_ERROR;
- goto bail;
- }
- mFileName[mFileNameLength] = '\0';
- }
-
- /* read "extra field" */
- if (mExtraFieldLength != 0) {
- mExtraField = new unsigned char[mExtraFieldLength+1];
- if (mExtraField == NULL) {
- result = NO_MEMORY;
- goto bail;
- }
- if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
- result = UNKNOWN_ERROR;
- goto bail;
- }
- mExtraField[mExtraFieldLength] = '\0';
- }
-
-
- /* grab comment, if any */
- if (mFileCommentLength != 0) {
- mFileComment = new unsigned char[mFileCommentLength+1];
- if (mFileComment == NULL) {
- result = NO_MEMORY;
- goto bail;
- }
- if (fread(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
- {
- result = UNKNOWN_ERROR;
- goto bail;
- }
- mFileComment[mFileCommentLength] = '\0';
- }
-
-bail:
- return result;
-}
-
-/*
- * Write a central dir entry.
- */
-status_t ZipEntry::CentralDirEntry::write(FILE* fp)
-{
- unsigned char buf[kCDELen];
-
- ZipEntry::putLongLE(&buf[0x00], kSignature);
- ZipEntry::putShortLE(&buf[0x04], mVersionMadeBy);
- ZipEntry::putShortLE(&buf[0x06], mVersionToExtract);
- ZipEntry::putShortLE(&buf[0x08], mGPBitFlag);
- ZipEntry::putShortLE(&buf[0x0a], mCompressionMethod);
- ZipEntry::putShortLE(&buf[0x0c], mLastModFileTime);
- ZipEntry::putShortLE(&buf[0x0e], mLastModFileDate);
- ZipEntry::putLongLE(&buf[0x10], mCRC32);
- ZipEntry::putLongLE(&buf[0x14], mCompressedSize);
- ZipEntry::putLongLE(&buf[0x18], mUncompressedSize);
- ZipEntry::putShortLE(&buf[0x1c], mFileNameLength);
- ZipEntry::putShortLE(&buf[0x1e], mExtraFieldLength);
- ZipEntry::putShortLE(&buf[0x20], mFileCommentLength);
- ZipEntry::putShortLE(&buf[0x22], mDiskNumberStart);
- ZipEntry::putShortLE(&buf[0x24], mInternalAttrs);
- ZipEntry::putLongLE(&buf[0x26], mExternalAttrs);
- ZipEntry::putLongLE(&buf[0x2a], mLocalHeaderRelOffset);
-
- if (fwrite(buf, 1, kCDELen, fp) != kCDELen)
- return UNKNOWN_ERROR;
-
- /* write filename */
- if (mFileNameLength != 0) {
- if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
- return UNKNOWN_ERROR;
- }
-
- /* write "extra field" */
- if (mExtraFieldLength != 0) {
- if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
- return UNKNOWN_ERROR;
- }
-
- /* write comment */
- if (mFileCommentLength != 0) {
- if (fwrite(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
- return UNKNOWN_ERROR;
- }
-
- return NO_ERROR;
-}
-
-/*
- * Dump the contents of a CentralDirEntry object.
- */
-void ZipEntry::CentralDirEntry::dump(void) const
-{
- ALOGD(" CentralDirEntry contents:\n");
- ALOGD(" versMadeBy=%u versToExt=%u gpBits=0x%04x compression=%u\n",
- mVersionMadeBy, mVersionToExtract, mGPBitFlag, mCompressionMethod);
- ALOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
- mLastModFileTime, mLastModFileDate, mCRC32);
- ALOGD(" compressedSize=%lu uncompressedSize=%lu\n",
- mCompressedSize, mUncompressedSize);
- ALOGD(" filenameLen=%u extraLen=%u commentLen=%u\n",
- mFileNameLength, mExtraFieldLength, mFileCommentLength);
- ALOGD(" diskNumStart=%u intAttr=0x%04x extAttr=0x%08lx relOffset=%lu\n",
- mDiskNumberStart, mInternalAttrs, mExternalAttrs,
- mLocalHeaderRelOffset);
-
- if (mFileName != NULL)
- ALOGD(" filename: '%s'\n", mFileName);
- if (mFileComment != NULL)
- ALOGD(" comment: '%s'\n", mFileComment);
-}
-
-/*
- * Copy-assignment operator for CentralDirEntry.
- */
-ZipEntry::CentralDirEntry& ZipEntry::CentralDirEntry::operator=(const ZipEntry::CentralDirEntry& src) {
- if (this == &src) {
- return *this;
- }
-
- // Free up old data.
- delete[] mFileName;
- delete[] mExtraField;
- delete[] mFileComment;
-
- // Copy scalars.
- mVersionMadeBy = src.mVersionMadeBy;
- mVersionToExtract = src.mVersionToExtract;
- mGPBitFlag = src.mGPBitFlag;
- mCompressionMethod = src.mCompressionMethod;
- mLastModFileTime = src.mLastModFileTime;
- mLastModFileDate = src.mLastModFileDate;
- mCRC32 = src.mCRC32;
- mCompressedSize = src.mCompressedSize;
- mUncompressedSize = src.mUncompressedSize;
- mFileNameLength = src.mFileNameLength;
- mExtraFieldLength = src.mExtraFieldLength;
- mFileCommentLength = src.mFileCommentLength;
- mDiskNumberStart = src.mDiskNumberStart;
- mInternalAttrs = src.mInternalAttrs;
- mExternalAttrs = src.mExternalAttrs;
- mLocalHeaderRelOffset = src.mLocalHeaderRelOffset;
-
- // Copy strings, if necessary.
- if (mFileNameLength > 0) {
- mFileName = new unsigned char[mFileNameLength + 1];
- if (mFileName != NULL)
- strcpy((char*)mFileName, (char*)src.mFileName);
- } else {
- mFileName = NULL;
- }
- if (mFileCommentLength > 0) {
- mFileComment = new unsigned char[mFileCommentLength + 1];
- if (mFileComment != NULL)
- strcpy((char*)mFileComment, (char*)src.mFileComment);
- } else {
- mFileComment = NULL;
- }
- if (mExtraFieldLength > 0) {
- /* we null-terminate this, though it may not be a string */
- mExtraField = new unsigned char[mExtraFieldLength + 1];
- if (mExtraField != NULL)
- memcpy(mExtraField, src.mExtraField, mExtraFieldLength + 1);
- } else {
- mExtraField = NULL;
- }
-
- return *this;
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/ZipEntry.h b/tools/aapt2/ZipEntry.h
deleted file mode 100644
index 2745a43..0000000
--- a/tools/aapt2/ZipEntry.h
+++ /dev/null
@@ -1,350 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-// Zip archive entries.
-//
-// The ZipEntry class is tightly meshed with the ZipFile class.
-//
-#ifndef __LIBS_ZIPENTRY_H
-#define __LIBS_ZIPENTRY_H
-
-#include <utils/Errors.h>
-
-#include <stdlib.h>
-#include <stdio.h>
-
-namespace aapt {
-
-using android::status_t;
-
-class ZipFile;
-
-/*
- * ZipEntry objects represent a single entry in a Zip archive.
- *
- * You can use one of these to get or set information about an entry, but
- * there are no functions here for accessing the data itself. (We could
- * tuck a pointer to the ZipFile in here for convenience, but that raises
- * the likelihood of using ZipEntry objects after discarding the ZipFile.)
- *
- * File information is stored in two places: next to the file data (the Local
- * File Header, and possibly a Data Descriptor), and at the end of the file
- * (the Central Directory Entry). The two must be kept in sync.
- */
-class ZipEntry {
-public:
- friend class ZipFile;
-
- ZipEntry(void)
- : mDeleted(false), mMarked(false)
- {}
- ~ZipEntry(void) {}
-
- /*
- * Returns "true" if the data is compressed.
- */
- bool isCompressed(void) const {
- return mCDE.mCompressionMethod != kCompressStored;
- }
- int getCompressionMethod(void) const { return mCDE.mCompressionMethod; }
-
- /*
- * Return the uncompressed length.
- */
- off_t getUncompressedLen(void) const { return mCDE.mUncompressedSize; }
-
- /*
- * Return the compressed length. For uncompressed data, this returns
- * the same thing as getUncompresesdLen().
- */
- off_t getCompressedLen(void) const { return mCDE.mCompressedSize; }
-
- /*
- * Return the offset of the local file header.
- */
- off_t getLFHOffset(void) const { return mCDE.mLocalHeaderRelOffset; }
-
- /*
- * Return the absolute file offset of the start of the compressed or
- * uncompressed data.
- */
- off_t getFileOffset(void) const {
- return mCDE.mLocalHeaderRelOffset +
- LocalFileHeader::kLFHLen +
- mLFH.mFileNameLength +
- mLFH.mExtraFieldLength;
- }
-
- /*
- * Return the data CRC.
- */
- unsigned long getCRC32(void) const { return mCDE.mCRC32; }
-
- /*
- * Return file modification time in UNIX seconds-since-epoch.
- */
- time_t getModWhen(void) const;
-
- /*
- * Return the archived file name.
- */
- const char* getFileName(void) const { return (const char*) mCDE.mFileName; }
-
- /*
- * Application-defined "mark". Can be useful when synchronizing the
- * contents of an archive with contents on disk.
- */
- bool getMarked(void) const { return mMarked; }
- void setMarked(bool val) { mMarked = val; }
-
- /*
- * Some basic functions for raw data manipulation. "LE" means
- * Little Endian.
- */
- static inline unsigned short getShortLE(const unsigned char* buf) {
- return buf[0] | (buf[1] << 8);
- }
- static inline unsigned long getLongLE(const unsigned char* buf) {
- return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
- }
- static inline void putShortLE(unsigned char* buf, short val) {
- buf[0] = (unsigned char) val;
- buf[1] = (unsigned char) (val >> 8);
- }
- static inline void putLongLE(unsigned char* buf, long val) {
- buf[0] = (unsigned char) val;
- buf[1] = (unsigned char) (val >> 8);
- buf[2] = (unsigned char) (val >> 16);
- buf[3] = (unsigned char) (val >> 24);
- }
-
- /* defined for Zip archives */
- enum {
- kCompressStored = 0, // no compression
- // shrunk = 1,
- // reduced 1 = 2,
- // reduced 2 = 3,
- // reduced 3 = 4,
- // reduced 4 = 5,
- // imploded = 6,
- // tokenized = 7,
- kCompressDeflated = 8, // standard deflate
- // Deflate64 = 9,
- // lib imploded = 10,
- // reserved = 11,
- // bzip2 = 12,
- };
-
- /*
- * Deletion flag. If set, the entry will be removed on the next
- * call to "flush".
- */
- bool getDeleted(void) const { return mDeleted; }
-
-protected:
- /*
- * Initialize the structure from the file, which is pointing at
- * our Central Directory entry.
- */
- status_t initFromCDE(FILE* fp);
-
- /*
- * Initialize the structure for a new file. We need the filename
- * and comment so that we can properly size the LFH area. The
- * filename is mandatory, the comment is optional.
- */
- void initNew(const char* fileName, const char* comment);
-
- /*
- * Initialize the structure with the contents of a ZipEntry from
- * another file. If fileName is non-NULL, override the name with fileName.
- */
- status_t initFromExternal(const ZipFile* pZipFile, const ZipEntry* pEntry,
- const char* fileName);
-
- /*
- * Add some pad bytes to the LFH. We do this by adding or resizing
- * the "extra" field.
- */
- status_t addPadding(int padding);
-
- /*
- * Set information about the data for this entry.
- */
- void setDataInfo(long uncompLen, long compLen, unsigned long crc32,
- int compressionMethod);
-
- /*
- * Set the modification date.
- */
- void setModWhen(time_t when);
-
- /*
- * Set the offset of the local file header, relative to the start of
- * the current file.
- */
- void setLFHOffset(off_t offset) {
- mCDE.mLocalHeaderRelOffset = (long) offset;
- }
-
- /* mark for deletion; used by ZipFile::remove() */
- void setDeleted(void) { mDeleted = true; }
-
-private:
- /* these are private and not defined */
- ZipEntry(const ZipEntry& src);
- ZipEntry& operator=(const ZipEntry& src);
-
- /* returns "true" if the CDE and the LFH agree */
- bool compareHeaders(void) const;
- void copyCDEtoLFH(void);
-
- bool mDeleted; // set if entry is pending deletion
- bool mMarked; // app-defined marker
-
- /*
- * Every entry in the Zip archive starts off with one of these.
- */
- class LocalFileHeader {
- public:
- LocalFileHeader(void) :
- mVersionToExtract(0),
- mGPBitFlag(0),
- mCompressionMethod(0),
- mLastModFileTime(0),
- mLastModFileDate(0),
- mCRC32(0),
- mCompressedSize(0),
- mUncompressedSize(0),
- mFileNameLength(0),
- mExtraFieldLength(0),
- mFileName(NULL),
- mExtraField(NULL)
- {}
- virtual ~LocalFileHeader(void) {
- delete[] mFileName;
- delete[] mExtraField;
- }
-
- status_t read(FILE* fp);
- status_t write(FILE* fp);
-
- // unsigned long mSignature;
- unsigned short mVersionToExtract;
- unsigned short mGPBitFlag;
- unsigned short mCompressionMethod;
- unsigned short mLastModFileTime;
- unsigned short mLastModFileDate;
- unsigned long mCRC32;
- unsigned long mCompressedSize;
- unsigned long mUncompressedSize;
- unsigned short mFileNameLength;
- unsigned short mExtraFieldLength;
- unsigned char* mFileName;
- unsigned char* mExtraField;
-
- enum {
- kSignature = 0x04034b50,
- kLFHLen = 30, // LocalFileHdr len, excl. var fields
- };
-
- void dump(void) const;
- };
-
- /*
- * Every entry in the Zip archive has one of these in the "central
- * directory" at the end of the file.
- */
- class CentralDirEntry {
- public:
- CentralDirEntry(void) :
- mVersionMadeBy(0),
- mVersionToExtract(0),
- mGPBitFlag(0),
- mCompressionMethod(0),
- mLastModFileTime(0),
- mLastModFileDate(0),
- mCRC32(0),
- mCompressedSize(0),
- mUncompressedSize(0),
- mFileNameLength(0),
- mExtraFieldLength(0),
- mFileCommentLength(0),
- mDiskNumberStart(0),
- mInternalAttrs(0),
- mExternalAttrs(0),
- mLocalHeaderRelOffset(0),
- mFileName(NULL),
- mExtraField(NULL),
- mFileComment(NULL)
- {}
- virtual ~CentralDirEntry(void) {
- delete[] mFileName;
- delete[] mExtraField;
- delete[] mFileComment;
- }
-
- status_t read(FILE* fp);
- status_t write(FILE* fp);
-
- CentralDirEntry& operator=(const CentralDirEntry& src);
-
- // unsigned long mSignature;
- unsigned short mVersionMadeBy;
- unsigned short mVersionToExtract;
- unsigned short mGPBitFlag;
- unsigned short mCompressionMethod;
- unsigned short mLastModFileTime;
- unsigned short mLastModFileDate;
- unsigned long mCRC32;
- unsigned long mCompressedSize;
- unsigned long mUncompressedSize;
- unsigned short mFileNameLength;
- unsigned short mExtraFieldLength;
- unsigned short mFileCommentLength;
- unsigned short mDiskNumberStart;
- unsigned short mInternalAttrs;
- unsigned long mExternalAttrs;
- unsigned long mLocalHeaderRelOffset;
- unsigned char* mFileName;
- unsigned char* mExtraField;
- unsigned char* mFileComment;
-
- void dump(void) const;
-
- enum {
- kSignature = 0x02014b50,
- kCDELen = 46, // CentralDirEnt len, excl. var fields
- };
- };
-
- enum {
- //kDataDescriptorSignature = 0x08074b50, // currently unused
- kDataDescriptorLen = 16, // four 32-bit fields
-
- kDefaultVersion = 20, // need deflate, nothing much else
- kDefaultMadeBy = 0x0317, // 03=UNIX, 17=spec v2.3
- kUsesDataDescr = 0x0008, // GPBitFlag bit 3
- };
-
- LocalFileHeader mLFH;
- CentralDirEntry mCDE;
-};
-
-}; // namespace aapt
-
-#endif // __LIBS_ZIPENTRY_H
diff --git a/tools/aapt2/ZipFile.cpp b/tools/aapt2/ZipFile.cpp
deleted file mode 100644
index 268c15e..0000000
--- a/tools/aapt2/ZipFile.cpp
+++ /dev/null
@@ -1,1306 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-// Access to Zip archives.
-//
-
-#define LOG_TAG "zip"
-
-#include <androidfw/ZipUtils.h>
-#include <utils/Log.h>
-
-#include "ZipFile.h"
-#include "Util.h"
-
-#include <zlib.h>
-#define DEF_MEM_LEVEL 8 // normally in zutil.h?
-
-#include <memory.h>
-#include <sys/stat.h>
-#include <errno.h>
-#include <assert.h>
-
-namespace aapt {
-
-using namespace android;
-
-/*
- * Some environments require the "b", some choke on it.
- */
-#define FILE_OPEN_RO "rb"
-#define FILE_OPEN_RW "r+b"
-#define FILE_OPEN_RW_CREATE "w+b"
-
-/* should live somewhere else? */
-static status_t errnoToStatus(int err)
-{
- if (err == ENOENT)
- return NAME_NOT_FOUND;
- else if (err == EACCES)
- return PERMISSION_DENIED;
- else
- return UNKNOWN_ERROR;
-}
-
-/*
- * Open a file and parse its guts.
- */
-status_t ZipFile::open(const char* zipFileName, int flags)
-{
- bool newArchive = false;
-
- assert(mZipFp == NULL); // no reopen
-
- if ((flags & kOpenTruncate))
- flags |= kOpenCreate; // trunc implies create
-
- if ((flags & kOpenReadOnly) && (flags & kOpenReadWrite))
- return INVALID_OPERATION; // not both
- if (!((flags & kOpenReadOnly) || (flags & kOpenReadWrite)))
- return INVALID_OPERATION; // not neither
- if ((flags & kOpenCreate) && !(flags & kOpenReadWrite))
- return INVALID_OPERATION; // create requires write
-
- if (flags & kOpenTruncate) {
- newArchive = true;
- } else {
- newArchive = (access(zipFileName, F_OK) != 0);
- if (!(flags & kOpenCreate) && newArchive) {
- /* not creating, must already exist */
- ALOGD("File %s does not exist", zipFileName);
- return NAME_NOT_FOUND;
- }
- }
-
- /* open the file */
- const char* openflags;
- if (flags & kOpenReadWrite) {
- if (newArchive)
- openflags = FILE_OPEN_RW_CREATE;
- else
- openflags = FILE_OPEN_RW;
- } else {
- openflags = FILE_OPEN_RO;
- }
- mZipFp = fopen(zipFileName, openflags);
- if (mZipFp == NULL) {
- int err = errno;
- ALOGD("fopen failed: %d\n", err);
- return errnoToStatus(err);
- }
-
- status_t result;
- if (!newArchive) {
- /*
- * Load the central directory. If that fails, then this probably
- * isn't a Zip archive.
- */
- result = readCentralDir();
- } else {
- /*
- * Newly-created. The EndOfCentralDir constructor actually
- * sets everything to be the way we want it (all zeroes). We
- * set mNeedCDRewrite so that we create *something* if the
- * caller doesn't add any files. (We could also just unlink
- * the file if it's brand new and nothing was added, but that's
- * probably doing more than we really should -- the user might
- * have a need for empty zip files.)
- */
- mNeedCDRewrite = true;
- result = NO_ERROR;
- }
-
- if (flags & kOpenReadOnly)
- mReadOnly = true;
- else
- assert(!mReadOnly);
-
- return result;
-}
-
-/*
- * Return the Nth entry in the archive.
- */
-ZipEntry* ZipFile::getEntryByIndex(int idx) const
-{
- if (idx < 0 || idx >= (int) mEntries.size())
- return NULL;
-
- return mEntries[idx];
-}
-
-/*
- * Find an entry by name.
- */
-ZipEntry* ZipFile::getEntryByName(const char* fileName) const
-{
- /*
- * Do a stupid linear string-compare search.
- *
- * There are various ways to speed this up, especially since it's rare
- * to intermingle changes to the archive with "get by name" calls. We
- * don't want to sort the mEntries vector itself, however, because
- * it's used to recreate the Central Directory.
- *
- * (Hash table works, parallel list of pointers in sorted order is good.)
- */
- int idx;
-
- for (idx = mEntries.size()-1; idx >= 0; idx--) {
- ZipEntry* pEntry = mEntries[idx];
- if (!pEntry->getDeleted() &&
- strcmp(fileName, pEntry->getFileName()) == 0)
- {
- return pEntry;
- }
- }
-
- return NULL;
-}
-
-/*
- * Empty the mEntries vector.
- */
-void ZipFile::discardEntries(void)
-{
- int count = mEntries.size();
-
- while (--count >= 0)
- delete mEntries[count];
-
- mEntries.clear();
-}
-
-
-/*
- * Find the central directory and read the contents.
- *
- * The fun thing about ZIP archives is that they may or may not be
- * readable from start to end. In some cases, notably for archives
- * that were written to stdout, the only length information is in the
- * central directory at the end of the file.
- *
- * Of course, the central directory can be followed by a variable-length
- * comment field, so we have to scan through it backwards. The comment
- * is at most 64K, plus we have 18 bytes for the end-of-central-dir stuff
- * itself, plus apparently sometimes people throw random junk on the end
- * just for the fun of it.
- *
- * This is all a little wobbly. If the wrong value ends up in the EOCD
- * area, we're hosed. This appears to be the way that everbody handles
- * it though, so we're in pretty good company if this fails.
- */
-status_t ZipFile::readCentralDir(void)
-{
- status_t result = NO_ERROR;
- unsigned char* buf = NULL;
- off_t fileLength, seekStart;
- long readAmount;
- int i;
-
- fseek(mZipFp, 0, SEEK_END);
- fileLength = ftell(mZipFp);
- rewind(mZipFp);
-
- /* too small to be a ZIP archive? */
- if (fileLength < EndOfCentralDir::kEOCDLen) {
- ALOGD("Length is %ld -- too small\n", (long)fileLength);
- result = INVALID_OPERATION;
- goto bail;
- }
-
- buf = new unsigned char[EndOfCentralDir::kMaxEOCDSearch];
- if (buf == NULL) {
- ALOGD("Failure allocating %d bytes for EOCD search",
- EndOfCentralDir::kMaxEOCDSearch);
- result = NO_MEMORY;
- goto bail;
- }
-
- if (fileLength > EndOfCentralDir::kMaxEOCDSearch) {
- seekStart = fileLength - EndOfCentralDir::kMaxEOCDSearch;
- readAmount = EndOfCentralDir::kMaxEOCDSearch;
- } else {
- seekStart = 0;
- readAmount = (long) fileLength;
- }
- if (fseek(mZipFp, seekStart, SEEK_SET) != 0) {
- ALOGD("Failure seeking to end of zip at %ld", (long) seekStart);
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- /* read the last part of the file into the buffer */
- if (fread(buf, 1, readAmount, mZipFp) != (size_t) readAmount) {
- ALOGD("short file? wanted %ld\n", readAmount);
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- /* find the end-of-central-dir magic */
- for (i = readAmount - 4; i >= 0; i--) {
- if (buf[i] == 0x50 &&
- ZipEntry::getLongLE(&buf[i]) == EndOfCentralDir::kSignature)
- {
- ALOGV("+++ Found EOCD at buf+%d\n", i);
- break;
- }
- }
- if (i < 0) {
- ALOGD("EOCD not found, not Zip\n");
- result = INVALID_OPERATION;
- goto bail;
- }
-
- /* extract eocd values */
- result = mEOCD.readBuf(buf + i, readAmount - i);
- if (result != NO_ERROR) {
- ALOGD("Failure reading %ld bytes of EOCD values", readAmount - i);
- goto bail;
- }
- //mEOCD.dump();
-
- if (mEOCD.mDiskNumber != 0 || mEOCD.mDiskWithCentralDir != 0 ||
- mEOCD.mNumEntries != mEOCD.mTotalNumEntries)
- {
- ALOGD("Archive spanning not supported\n");
- result = INVALID_OPERATION;
- goto bail;
- }
-
- /*
- * So far so good. "mCentralDirSize" is the size in bytes of the
- * central directory, so we can just seek back that far to find it.
- * We can also seek forward mCentralDirOffset bytes from the
- * start of the file.
- *
- * We're not guaranteed to have the rest of the central dir in the
- * buffer, nor are we guaranteed that the central dir will have any
- * sort of convenient size. We need to skip to the start of it and
- * read the header, then the other goodies.
- *
- * The only thing we really need right now is the file comment, which
- * we're hoping to preserve.
- */
- if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
- ALOGD("Failure seeking to central dir offset %ld\n",
- mEOCD.mCentralDirOffset);
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- /*
- * Loop through and read the central dir entries.
- */
- ALOGV("Scanning %d entries...\n", mEOCD.mTotalNumEntries);
- int entry;
- for (entry = 0; entry < mEOCD.mTotalNumEntries; entry++) {
- ZipEntry* pEntry = new ZipEntry;
-
- result = pEntry->initFromCDE(mZipFp);
- if (result != NO_ERROR) {
- ALOGD("initFromCDE failed\n");
- delete pEntry;
- goto bail;
- }
-
- mEntries.push_back(pEntry);
- }
-
-
- /*
- * If all went well, we should now be back at the EOCD.
- */
- {
- unsigned char checkBuf[4];
- if (fread(checkBuf, 1, 4, mZipFp) != 4) {
- ALOGD("EOCD check read failed\n");
- result = INVALID_OPERATION;
- goto bail;
- }
- if (ZipEntry::getLongLE(checkBuf) != EndOfCentralDir::kSignature) {
- ALOGD("EOCD read check failed\n");
- result = UNKNOWN_ERROR;
- goto bail;
- }
- ALOGV("+++ EOCD read check passed\n");
- }
-
-bail:
- delete[] buf;
- return result;
-}
-
-status_t ZipFile::add(const BigBuffer& buffer, const char* storageName, int compressionMethod,
- ZipEntry** ppEntry) {
- std::unique_ptr<uint8_t[]> data = util::copy(buffer);
- return add(data.get(), buffer.size(), storageName, compressionMethod, ppEntry);
-}
-
-
-/*
- * Add a new file to the archive.
- *
- * This requires creating and populating a ZipEntry structure, and copying
- * the data into the file at the appropriate position. The "appropriate
- * position" is the current location of the central directory, which we
- * casually overwrite (we can put it back later).
- *
- * If we were concerned about safety, we would want to make all changes
- * in a temp file and then overwrite the original after everything was
- * safely written. Not really a concern for us.
- */
-status_t ZipFile::addCommon(const char* fileName, const void* data, size_t size,
- const char* storageName, int sourceType, int compressionMethod,
- ZipEntry** ppEntry)
-{
- ZipEntry* pEntry = NULL;
- status_t result = NO_ERROR;
- long lfhPosn, startPosn, endPosn, uncompressedLen;
- FILE* inputFp = NULL;
- unsigned long crc;
- time_t modWhen;
-
- if (mReadOnly)
- return INVALID_OPERATION;
-
- assert(compressionMethod == ZipEntry::kCompressDeflated ||
- compressionMethod == ZipEntry::kCompressStored);
-
- /* make sure we're in a reasonable state */
- assert(mZipFp != NULL);
- assert(mEntries.size() == mEOCD.mTotalNumEntries);
-
- /* make sure it doesn't already exist */
- if (getEntryByName(storageName) != NULL)
- return ALREADY_EXISTS;
-
- if (!data) {
- inputFp = fopen(fileName, FILE_OPEN_RO);
- if (inputFp == NULL)
- return errnoToStatus(errno);
- }
-
- if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- pEntry = new ZipEntry;
- pEntry->initNew(storageName, NULL);
-
- /*
- * From here on out, failures are more interesting.
- */
- mNeedCDRewrite = true;
-
- /*
- * Write the LFH, even though it's still mostly blank. We need it
- * as a place-holder. In theory the LFH isn't necessary, but in
- * practice some utilities demand it.
- */
- lfhPosn = ftell(mZipFp);
- pEntry->mLFH.write(mZipFp);
- startPosn = ftell(mZipFp);
-
- /*
- * Copy the data in, possibly compressing it as we go.
- */
- if (sourceType == ZipEntry::kCompressStored) {
- if (compressionMethod == ZipEntry::kCompressDeflated) {
- bool failed = false;
- result = compressFpToFp(mZipFp, inputFp, data, size, &crc);
- if (result != NO_ERROR) {
- ALOGD("compression failed, storing\n");
- failed = true;
- } else {
- /*
- * Make sure it has compressed "enough". This probably ought
- * to be set through an API call, but I don't expect our
- * criteria to change over time.
- */
- long src = inputFp ? ftell(inputFp) : size;
- long dst = ftell(mZipFp) - startPosn;
- if (dst + (dst / 10) > src) {
- ALOGD("insufficient compression (src=%ld dst=%ld), storing\n",
- src, dst);
- failed = true;
- }
- }
-
- if (failed) {
- compressionMethod = ZipEntry::kCompressStored;
- if (inputFp) rewind(inputFp);
- fseek(mZipFp, startPosn, SEEK_SET);
- /* fall through to kCompressStored case */
- }
- }
- /* handle "no compression" request, or failed compression from above */
- if (compressionMethod == ZipEntry::kCompressStored) {
- if (inputFp) {
- result = copyFpToFp(mZipFp, inputFp, &crc);
- } else {
- result = copyDataToFp(mZipFp, data, size, &crc);
- }
- if (result != NO_ERROR) {
- // don't need to truncate; happens in CDE rewrite
- ALOGD("failed copying data in\n");
- goto bail;
- }
- }
-
- // currently seeked to end of file
- uncompressedLen = inputFp ? ftell(inputFp) : size;
- } else if (sourceType == ZipEntry::kCompressDeflated) {
- /* we should support uncompressed-from-compressed, but it's not
- * important right now */
- assert(compressionMethod == ZipEntry::kCompressDeflated);
-
- bool scanResult;
- int method;
- long compressedLen;
-
- scanResult = ZipUtils::examineGzip(inputFp, &method, &uncompressedLen,
- &compressedLen, &crc);
- if (!scanResult || method != ZipEntry::kCompressDeflated) {
- ALOGD("this isn't a deflated gzip file?");
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- result = copyPartialFpToFp(mZipFp, inputFp, compressedLen, NULL);
- if (result != NO_ERROR) {
- ALOGD("failed copying gzip data in\n");
- goto bail;
- }
- } else {
- assert(false);
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- /*
- * We could write the "Data Descriptor", but there doesn't seem to
- * be any point since we're going to go back and write the LFH.
- *
- * Update file offsets.
- */
- endPosn = ftell(mZipFp); // seeked to end of compressed data
-
- /*
- * Success! Fill out new values.
- */
- pEntry->setDataInfo(uncompressedLen, endPosn - startPosn, crc,
- compressionMethod);
- modWhen = getModTime(inputFp ? fileno(inputFp) : fileno(mZipFp));
- pEntry->setModWhen(modWhen);
- pEntry->setLFHOffset(lfhPosn);
- mEOCD.mNumEntries++;
- mEOCD.mTotalNumEntries++;
- mEOCD.mCentralDirSize = 0; // mark invalid; set by flush()
- mEOCD.mCentralDirOffset = endPosn;
-
- /*
- * Go back and write the LFH.
- */
- if (fseek(mZipFp, lfhPosn, SEEK_SET) != 0) {
- result = UNKNOWN_ERROR;
- goto bail;
- }
- pEntry->mLFH.write(mZipFp);
-
- /*
- * Add pEntry to the list.
- */
- mEntries.push_back(pEntry);
- if (ppEntry != NULL)
- *ppEntry = pEntry;
- pEntry = NULL;
-
-bail:
- if (inputFp != NULL)
- fclose(inputFp);
- delete pEntry;
- return result;
-}
-
-/*
- * Add an entry by copying it from another zip file. If "padding" is
- * nonzero, the specified number of bytes will be added to the "extra"
- * field in the header.
- *
- * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
- */
-status_t ZipFile::add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry,
- const char* storageName, int padding, ZipEntry** ppEntry)
-{
- ZipEntry* pEntry = NULL;
- status_t result;
- long lfhPosn, endPosn;
-
- if (mReadOnly)
- return INVALID_OPERATION;
-
- /* make sure we're in a reasonable state */
- assert(mZipFp != NULL);
- assert(mEntries.size() == mEOCD.mTotalNumEntries);
-
- if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- pEntry = new ZipEntry;
- if (pEntry == NULL) {
- result = NO_MEMORY;
- goto bail;
- }
-
- result = pEntry->initFromExternal(pSourceZip, pSourceEntry, storageName);
- if (result != NO_ERROR) {
- goto bail;
- }
- if (padding != 0) {
- result = pEntry->addPadding(padding);
- if (result != NO_ERROR)
- goto bail;
- }
-
- /*
- * From here on out, failures are more interesting.
- */
- mNeedCDRewrite = true;
-
- /*
- * Write the LFH. Since we're not recompressing the data, we already
- * have all of the fields filled out.
- */
- lfhPosn = ftell(mZipFp);
- pEntry->mLFH.write(mZipFp);
-
- /*
- * Copy the data over.
- *
- * If the "has data descriptor" flag is set, we want to copy the DD
- * fields as well. This is a fixed-size area immediately following
- * the data.
- */
- if (fseek(pSourceZip->mZipFp, pSourceEntry->getFileOffset(), SEEK_SET) != 0)
- {
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- off_t copyLen;
- copyLen = pSourceEntry->getCompressedLen();
- if ((pSourceEntry->mLFH.mGPBitFlag & ZipEntry::kUsesDataDescr) != 0)
- copyLen += ZipEntry::kDataDescriptorLen;
-
- if (copyPartialFpToFp(mZipFp, pSourceZip->mZipFp, copyLen, NULL)
- != NO_ERROR)
- {
- ALOGW("copy of '%s' failed\n", pEntry->mCDE.mFileName);
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- /*
- * Update file offsets.
- */
- endPosn = ftell(mZipFp);
-
- /*
- * Success! Fill out new values.
- */
- pEntry->setLFHOffset(lfhPosn); // sets mCDE.mLocalHeaderRelOffset
- mEOCD.mNumEntries++;
- mEOCD.mTotalNumEntries++;
- mEOCD.mCentralDirSize = 0; // mark invalid; set by flush()
- mEOCD.mCentralDirOffset = endPosn;
-
- /*
- * Add pEntry to the list.
- */
- mEntries.push_back(pEntry);
- if (ppEntry != NULL)
- *ppEntry = pEntry;
- pEntry = NULL;
-
- result = NO_ERROR;
-
-bail:
- delete pEntry;
- return result;
-}
-
-/*
- * Copy all of the bytes in "src" to "dst".
- *
- * On exit, "srcFp" will be seeked to the end of the file, and "dstFp"
- * will be seeked immediately past the data.
- */
-status_t ZipFile::copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32)
-{
- unsigned char tmpBuf[32768];
- size_t count;
-
- *pCRC32 = crc32(0L, Z_NULL, 0);
-
- while (1) {
- count = fread(tmpBuf, 1, sizeof(tmpBuf), srcFp);
- if (ferror(srcFp) || ferror(dstFp))
- return errnoToStatus(errno);
- if (count == 0)
- break;
-
- *pCRC32 = crc32(*pCRC32, tmpBuf, count);
-
- if (fwrite(tmpBuf, 1, count, dstFp) != count) {
- ALOGD("fwrite %d bytes failed\n", (int) count);
- return UNKNOWN_ERROR;
- }
- }
-
- return NO_ERROR;
-}
-
-/*
- * Copy all of the bytes in "src" to "dst".
- *
- * On exit, "dstFp" will be seeked immediately past the data.
- */
-status_t ZipFile::copyDataToFp(FILE* dstFp,
- const void* data, size_t size, unsigned long* pCRC32)
-{
- *pCRC32 = crc32(0L, Z_NULL, 0);
- if (size > 0) {
- *pCRC32 = crc32(*pCRC32, (const unsigned char*)data, size);
- if (fwrite(data, 1, size, dstFp) != size) {
- ALOGD("fwrite %d bytes failed\n", (int) size);
- return UNKNOWN_ERROR;
- }
- }
-
- return NO_ERROR;
-}
-
-/*
- * Copy some of the bytes in "src" to "dst".
- *
- * If "pCRC32" is NULL, the CRC will not be computed.
- *
- * On exit, "srcFp" will be seeked to the end of the file, and "dstFp"
- * will be seeked immediately past the data just written.
- */
-status_t ZipFile::copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length,
- unsigned long* pCRC32)
-{
- unsigned char tmpBuf[32768];
- size_t count;
-
- if (pCRC32 != NULL)
- *pCRC32 = crc32(0L, Z_NULL, 0);
-
- while (length) {
- long readSize;
-
- readSize = sizeof(tmpBuf);
- if (readSize > length)
- readSize = length;
-
- count = fread(tmpBuf, 1, readSize, srcFp);
- if ((long) count != readSize) { // error or unexpected EOF
- ALOGD("fread %d bytes failed\n", (int) readSize);
- return UNKNOWN_ERROR;
- }
-
- if (pCRC32 != NULL)
- *pCRC32 = crc32(*pCRC32, tmpBuf, count);
-
- if (fwrite(tmpBuf, 1, count, dstFp) != count) {
- ALOGD("fwrite %d bytes failed\n", (int) count);
- return UNKNOWN_ERROR;
- }
-
- length -= readSize;
- }
-
- return NO_ERROR;
-}
-
-/*
- * Compress all of the data in "srcFp" and write it to "dstFp".
- *
- * On exit, "srcFp" will be seeked to the end of the file, and "dstFp"
- * will be seeked immediately past the compressed data.
- */
-status_t ZipFile::compressFpToFp(FILE* dstFp, FILE* srcFp,
- const void* data, size_t size, unsigned long* pCRC32)
-{
- status_t result = NO_ERROR;
- const size_t kBufSize = 32768;
- unsigned char* inBuf = NULL;
- unsigned char* outBuf = NULL;
- z_stream zstream;
- bool atEof = false; // no feof() aviailable yet
- unsigned long crc;
- int zerr;
-
- /*
- * Create an input buffer and an output buffer.
- */
- inBuf = new unsigned char[kBufSize];
- outBuf = new unsigned char[kBufSize];
- if (inBuf == NULL || outBuf == NULL) {
- result = NO_MEMORY;
- goto bail;
- }
-
- /*
- * Initialize the zlib stream.
- */
- memset(&zstream, 0, sizeof(zstream));
- zstream.zalloc = Z_NULL;
- zstream.zfree = Z_NULL;
- zstream.opaque = Z_NULL;
- zstream.next_in = NULL;
- zstream.avail_in = 0;
- zstream.next_out = outBuf;
- zstream.avail_out = kBufSize;
- zstream.data_type = Z_UNKNOWN;
-
- zerr = deflateInit2(&zstream, Z_BEST_COMPRESSION,
- Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
- if (zerr != Z_OK) {
- result = UNKNOWN_ERROR;
- if (zerr == Z_VERSION_ERROR) {
- ALOGE("Installed zlib is not compatible with linked version (%s)\n",
- ZLIB_VERSION);
- } else {
- ALOGD("Call to deflateInit2 failed (zerr=%d)\n", zerr);
- }
- goto bail;
- }
-
- crc = crc32(0L, Z_NULL, 0);
-
- /*
- * Loop while we have data.
- */
- do {
- size_t getSize;
- int flush;
-
- /* only read if the input buffer is empty */
- if (zstream.avail_in == 0 && !atEof) {
- ALOGV("+++ reading %d bytes\n", (int)kBufSize);
- if (data) {
- getSize = size > kBufSize ? kBufSize : size;
- memcpy(inBuf, data, getSize);
- data = ((const char*)data) + getSize;
- size -= getSize;
- } else {
- getSize = fread(inBuf, 1, kBufSize, srcFp);
- if (ferror(srcFp)) {
- ALOGD("deflate read failed (errno=%d)\n", errno);
- goto z_bail;
- }
- }
- if (getSize < kBufSize) {
- ALOGV("+++ got %d bytes, EOF reached\n",
- (int)getSize);
- atEof = true;
- }
-
- crc = crc32(crc, inBuf, getSize);
-
- zstream.next_in = inBuf;
- zstream.avail_in = getSize;
- }
-
- if (atEof)
- flush = Z_FINISH; /* tell zlib that we're done */
- else
- flush = Z_NO_FLUSH; /* more to come! */
-
- zerr = deflate(&zstream, flush);
- if (zerr != Z_OK && zerr != Z_STREAM_END) {
- ALOGD("zlib deflate call failed (zerr=%d)\n", zerr);
- result = UNKNOWN_ERROR;
- goto z_bail;
- }
-
- /* write when we're full or when we're done */
- if (zstream.avail_out == 0 ||
- (zerr == Z_STREAM_END && zstream.avail_out != (uInt) kBufSize))
- {
- ALOGV("+++ writing %d bytes\n", (int) (zstream.next_out - outBuf));
- if (fwrite(outBuf, 1, zstream.next_out - outBuf, dstFp) !=
- (size_t)(zstream.next_out - outBuf))
- {
- ALOGD("write %d failed in deflate\n",
- (int) (zstream.next_out - outBuf));
- goto z_bail;
- }
-
- zstream.next_out = outBuf;
- zstream.avail_out = kBufSize;
- }
- } while (zerr == Z_OK);
-
- assert(zerr == Z_STREAM_END); /* other errors should've been caught */
-
- *pCRC32 = crc;
-
-z_bail:
- deflateEnd(&zstream); /* free up any allocated structures */
-
-bail:
- delete[] inBuf;
- delete[] outBuf;
-
- return result;
-}
-
-/*
- * Mark an entry as deleted.
- *
- * We will eventually need to crunch the file down, but if several files
- * are being removed (perhaps as part of an "update" process) we can make
- * things considerably faster by deferring the removal to "flush" time.
- */
-status_t ZipFile::remove(ZipEntry* pEntry)
-{
- /*
- * Should verify that pEntry is actually part of this archive, and
- * not some stray ZipEntry from a different file.
- */
-
- /* mark entry as deleted, and mark archive as dirty */
- pEntry->setDeleted();
- mNeedCDRewrite = true;
- return NO_ERROR;
-}
-
-/*
- * Flush any pending writes.
- *
- * In particular, this will crunch out deleted entries, and write the
- * Central Directory and EOCD if we have stomped on them.
- */
-status_t ZipFile::flush(void)
-{
- status_t result = NO_ERROR;
- long eocdPosn;
- int i, count;
-
- if (mReadOnly)
- return INVALID_OPERATION;
- if (!mNeedCDRewrite)
- return NO_ERROR;
-
- assert(mZipFp != NULL);
-
- result = crunchArchive();
- if (result != NO_ERROR)
- return result;
-
- if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0)
- return UNKNOWN_ERROR;
-
- count = mEntries.size();
- for (i = 0; i < count; i++) {
- ZipEntry* pEntry = mEntries[i];
- pEntry->mCDE.write(mZipFp);
- }
-
- eocdPosn = ftell(mZipFp);
- mEOCD.mCentralDirSize = eocdPosn - mEOCD.mCentralDirOffset;
-
- mEOCD.write(mZipFp);
-
- /*
- * If we had some stuff bloat up during compression and get replaced
- * with plain files, or if we deleted some entries, there's a lot
- * of wasted space at the end of the file. Remove it now.
- */
- if (ftruncate(fileno(mZipFp), ftell(mZipFp)) != 0) {
- ALOGW("ftruncate failed %ld: %s\n", ftell(mZipFp), strerror(errno));
- // not fatal
- }
-
- /* should we clear the "newly added" flag in all entries now? */
-
- mNeedCDRewrite = false;
- return NO_ERROR;
-}
-
-/*
- * Crunch deleted files out of an archive by shifting the later files down.
- *
- * Because we're not using a temp file, we do the operation inside the
- * current file.
- */
-status_t ZipFile::crunchArchive(void)
-{
- status_t result = NO_ERROR;
- int i, count;
- long delCount, adjust;
-
-#if 0
- printf("CONTENTS:\n");
- for (i = 0; i < (int) mEntries.size(); i++) {
- printf(" %d: lfhOff=%ld del=%d\n",
- i, mEntries[i]->getLFHOffset(), mEntries[i]->getDeleted());
- }
- printf(" END is %ld\n", (long) mEOCD.mCentralDirOffset);
-#endif
-
- /*
- * Roll through the set of files, shifting them as appropriate. We
- * could probably get a slight performance improvement by sliding
- * multiple files down at once (because we could use larger reads
- * when operating on batches of small files), but it's not that useful.
- */
- count = mEntries.size();
- delCount = adjust = 0;
- for (i = 0; i < count; i++) {
- ZipEntry* pEntry = mEntries[i];
- long span;
-
- if (pEntry->getLFHOffset() != 0) {
- long nextOffset;
-
- /* Get the length of this entry by finding the offset
- * of the next entry. Directory entries don't have
- * file offsets, so we need to find the next non-directory
- * entry.
- */
- nextOffset = 0;
- for (int ii = i+1; nextOffset == 0 && ii < count; ii++)
- nextOffset = mEntries[ii]->getLFHOffset();
- if (nextOffset == 0)
- nextOffset = mEOCD.mCentralDirOffset;
- span = nextOffset - pEntry->getLFHOffset();
-
- assert(span >= ZipEntry::LocalFileHeader::kLFHLen);
- } else {
- /* This is a directory entry. It doesn't have
- * any actual file contents, so there's no need to
- * move anything.
- */
- span = 0;
- }
-
- //printf("+++ %d: off=%ld span=%ld del=%d [count=%d]\n",
- // i, pEntry->getLFHOffset(), span, pEntry->getDeleted(), count);
-
- if (pEntry->getDeleted()) {
- adjust += span;
- delCount++;
-
- delete pEntry;
- mEntries.erase(mEntries.begin() + i);
-
- /* adjust loop control */
- count--;
- i--;
- } else if (span != 0 && adjust > 0) {
- /* shuffle this entry back */
- //printf("+++ Shuffling '%s' back %ld\n",
- // pEntry->getFileName(), adjust);
- result = filemove(mZipFp, pEntry->getLFHOffset() - adjust,
- pEntry->getLFHOffset(), span);
- if (result != NO_ERROR) {
- /* this is why you use a temp file */
- ALOGE("error during crunch - archive is toast\n");
- return result;
- }
-
- pEntry->setLFHOffset(pEntry->getLFHOffset() - adjust);
- }
- }
-
- /*
- * Fix EOCD info. We have to wait until the end to do some of this
- * because we use mCentralDirOffset to determine "span" for the
- * last entry.
- */
- mEOCD.mCentralDirOffset -= adjust;
- mEOCD.mNumEntries -= delCount;
- mEOCD.mTotalNumEntries -= delCount;
- mEOCD.mCentralDirSize = 0; // mark invalid; set by flush()
-
- assert(mEOCD.mNumEntries == mEOCD.mTotalNumEntries);
- assert(mEOCD.mNumEntries == count);
-
- return result;
-}
-
-/*
- * Works like memmove(), but on pieces of a file.
- */
-status_t ZipFile::filemove(FILE* fp, off_t dst, off_t src, size_t n)
-{
- if (dst == src || n <= 0)
- return NO_ERROR;
-
- unsigned char readBuf[32768];
-
- if (dst < src) {
- /* shift stuff toward start of file; must read from start */
- while (n != 0) {
- size_t getSize = sizeof(readBuf);
- if (getSize > n)
- getSize = n;
-
- if (fseek(fp, (long) src, SEEK_SET) != 0) {
- ALOGD("filemove src seek %ld failed\n", (long) src);
- return UNKNOWN_ERROR;
- }
-
- if (fread(readBuf, 1, getSize, fp) != getSize) {
- ALOGD("filemove read %ld off=%ld failed\n",
- (long) getSize, (long) src);
- return UNKNOWN_ERROR;
- }
-
- if (fseek(fp, (long) dst, SEEK_SET) != 0) {
- ALOGD("filemove dst seek %ld failed\n", (long) dst);
- return UNKNOWN_ERROR;
- }
-
- if (fwrite(readBuf, 1, getSize, fp) != getSize) {
- ALOGD("filemove write %ld off=%ld failed\n",
- (long) getSize, (long) dst);
- return UNKNOWN_ERROR;
- }
-
- src += getSize;
- dst += getSize;
- n -= getSize;
- }
- } else {
- /* shift stuff toward end of file; must read from end */
- assert(false); // write this someday, maybe
- return UNKNOWN_ERROR;
- }
-
- return NO_ERROR;
-}
-
-
-/*
- * Get the modification time from a file descriptor.
- */
-time_t ZipFile::getModTime(int fd)
-{
- struct stat sb;
-
- if (fstat(fd, &sb) < 0) {
- ALOGD("HEY: fstat on fd %d failed\n", fd);
- return (time_t) -1;
- }
-
- return sb.st_mtime;
-}
-
-
-#if 0 /* this is a bad idea */
-/*
- * Get a copy of the Zip file descriptor.
- *
- * We don't allow this if the file was opened read-write because we tend
- * to leave the file contents in an uncertain state between calls to
- * flush(). The duplicated file descriptor should only be valid for reads.
- */
-int ZipFile::getZipFd(void) const
-{
- if (!mReadOnly)
- return INVALID_OPERATION;
- assert(mZipFp != NULL);
-
- int fd;
- fd = dup(fileno(mZipFp));
- if (fd < 0) {
- ALOGD("didn't work, errno=%d\n", errno);
- }
-
- return fd;
-}
-#endif
-
-
-#if 0
-/*
- * Expand data.
- */
-bool ZipFile::uncompress(const ZipEntry* pEntry, void* buf) const
-{
- return false;
-}
-#endif
-
-// free the memory when you're done
-void* ZipFile::uncompress(const ZipEntry* entry)
-{
- size_t unlen = entry->getUncompressedLen();
- size_t clen = entry->getCompressedLen();
-
- void* buf = malloc(unlen);
- if (buf == NULL) {
- return NULL;
- }
-
- fseek(mZipFp, 0, SEEK_SET);
-
- off_t offset = entry->getFileOffset();
- if (fseek(mZipFp, offset, SEEK_SET) != 0) {
- goto bail;
- }
-
- switch (entry->getCompressionMethod())
- {
- case ZipEntry::kCompressStored: {
- ssize_t amt = fread(buf, 1, unlen, mZipFp);
- if (amt != (ssize_t)unlen) {
- goto bail;
- }
-#if 0
- printf("data...\n");
- const unsigned char* p = (unsigned char*)buf;
- const unsigned char* end = p+unlen;
- for (int i=0; i<32 && p < end; i++) {
- printf("0x%08x ", (int)(offset+(i*0x10)));
- for (int j=0; j<0x10 && p < end; j++) {
- printf(" %02x", *p);
- p++;
- }
- printf("\n");
- }
-#endif
-
- }
- break;
- case ZipEntry::kCompressDeflated: {
- if (!ZipUtils::inflateToBuffer(mZipFp, buf, unlen, clen)) {
- goto bail;
- }
- }
- break;
- default:
- goto bail;
- }
- return buf;
-
-bail:
- free(buf);
- return NULL;
-}
-
-
-/*
- * ===========================================================================
- * ZipFile::EndOfCentralDir
- * ===========================================================================
- */
-
-/*
- * Read the end-of-central-dir fields.
- *
- * "buf" should be positioned at the EOCD signature, and should contain
- * the entire EOCD area including the comment.
- */
-status_t ZipFile::EndOfCentralDir::readBuf(const unsigned char* buf, int len)
-{
- /* don't allow re-use */
- assert(mComment == NULL);
-
- if (len < kEOCDLen) {
- /* looks like ZIP file got truncated */
- ALOGD(" Zip EOCD: expected >= %d bytes, found %d\n",
- kEOCDLen, len);
- return INVALID_OPERATION;
- }
-
- /* this should probably be an assert() */
- if (ZipEntry::getLongLE(&buf[0x00]) != kSignature)
- return UNKNOWN_ERROR;
-
- mDiskNumber = ZipEntry::getShortLE(&buf[0x04]);
- mDiskWithCentralDir = ZipEntry::getShortLE(&buf[0x06]);
- mNumEntries = ZipEntry::getShortLE(&buf[0x08]);
- mTotalNumEntries = ZipEntry::getShortLE(&buf[0x0a]);
- mCentralDirSize = ZipEntry::getLongLE(&buf[0x0c]);
- mCentralDirOffset = ZipEntry::getLongLE(&buf[0x10]);
- mCommentLen = ZipEntry::getShortLE(&buf[0x14]);
-
- // TODO: validate mCentralDirOffset
-
- if (mCommentLen > 0) {
- if (kEOCDLen + mCommentLen > len) {
- ALOGD("EOCD(%d) + comment(%d) exceeds len (%d)\n",
- kEOCDLen, mCommentLen, len);
- return UNKNOWN_ERROR;
- }
- mComment = new unsigned char[mCommentLen];
- memcpy(mComment, buf + kEOCDLen, mCommentLen);
- }
-
- return NO_ERROR;
-}
-
-/*
- * Write an end-of-central-directory section.
- */
-status_t ZipFile::EndOfCentralDir::write(FILE* fp)
-{
- unsigned char buf[kEOCDLen];
-
- ZipEntry::putLongLE(&buf[0x00], kSignature);
- ZipEntry::putShortLE(&buf[0x04], mDiskNumber);
- ZipEntry::putShortLE(&buf[0x06], mDiskWithCentralDir);
- ZipEntry::putShortLE(&buf[0x08], mNumEntries);
- ZipEntry::putShortLE(&buf[0x0a], mTotalNumEntries);
- ZipEntry::putLongLE(&buf[0x0c], mCentralDirSize);
- ZipEntry::putLongLE(&buf[0x10], mCentralDirOffset);
- ZipEntry::putShortLE(&buf[0x14], mCommentLen);
-
- if (fwrite(buf, 1, kEOCDLen, fp) != kEOCDLen)
- return UNKNOWN_ERROR;
- if (mCommentLen > 0) {
- assert(mComment != NULL);
- if (fwrite(mComment, mCommentLen, 1, fp) != mCommentLen)
- return UNKNOWN_ERROR;
- }
-
- return NO_ERROR;
-}
-
-/*
- * Dump the contents of an EndOfCentralDir object.
- */
-void ZipFile::EndOfCentralDir::dump(void) const
-{
- ALOGD(" EndOfCentralDir contents:\n");
- ALOGD(" diskNum=%u diskWCD=%u numEnt=%u totalNumEnt=%u\n",
- mDiskNumber, mDiskWithCentralDir, mNumEntries, mTotalNumEntries);
- ALOGD(" centDirSize=%lu centDirOff=%lu commentLen=%u\n",
- mCentralDirSize, mCentralDirOffset, mCommentLen);
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/ZipFile.h b/tools/aapt2/ZipFile.h
deleted file mode 100644
index 9de92dd..0000000
--- a/tools/aapt2/ZipFile.h
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-// General-purpose Zip archive access. This class allows both reading and
-// writing to Zip archives, including deletion of existing entries.
-//
-#ifndef __LIBS_ZIPFILE_H
-#define __LIBS_ZIPFILE_H
-
-#include "BigBuffer.h"
-#include "ZipEntry.h"
-
-#include <stdio.h>
-#include <utils/Errors.h>
-#include <vector>
-
-namespace aapt {
-
-using android::status_t;
-
-/*
- * Manipulate a Zip archive.
- *
- * Some changes will not be visible in the until until "flush" is called.
- *
- * The correct way to update a file archive is to make all changes to a
- * copy of the archive in a temporary file, and then unlink/rename over
- * the original after everything completes. Because we're only interested
- * in using this for packaging, we don't worry about such things. Crashing
- * after making changes and before flush() completes could leave us with
- * an unusable Zip archive.
- */
-class ZipFile {
-public:
- ZipFile(void)
- : mZipFp(NULL), mReadOnly(false), mNeedCDRewrite(false)
- {}
- ~ZipFile(void) {
- if (!mReadOnly)
- flush();
- if (mZipFp != NULL)
- fclose(mZipFp);
- discardEntries();
- }
-
- /*
- * Open a new or existing archive.
- */
- enum {
- kOpenReadOnly = 0x01,
- kOpenReadWrite = 0x02,
- kOpenCreate = 0x04, // create if it doesn't exist
- kOpenTruncate = 0x08, // if it exists, empty it
- };
- status_t open(const char* zipFileName, int flags);
-
- /*
- * Add a file to the end of the archive. Specify whether you want the
- * library to try to store it compressed.
- *
- * If "storageName" is specified, the archive will use that instead
- * of "fileName".
- *
- * If there is already an entry with the same name, the call fails.
- * Existing entries with the same name must be removed first.
- *
- * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
- */
- status_t add(const char* fileName, int compressionMethod,
- ZipEntry** ppEntry)
- {
- return add(fileName, fileName, compressionMethod, ppEntry);
- }
- status_t add(const char* fileName, const char* storageName,
- int compressionMethod, ZipEntry** ppEntry)
- {
- return addCommon(fileName, NULL, 0, storageName,
- ZipEntry::kCompressStored,
- compressionMethod, ppEntry);
- }
-
- /*
- * Add a file that is already compressed with gzip.
- *
- * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
- */
- status_t addGzip(const char* fileName, const char* storageName,
- ZipEntry** ppEntry)
- {
- return addCommon(fileName, NULL, 0, storageName,
- ZipEntry::kCompressDeflated,
- ZipEntry::kCompressDeflated, ppEntry);
- }
-
- /*
- * Add a file from an in-memory data buffer.
- *
- * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
- */
- status_t add(const void* data, size_t size, const char* storageName,
- int compressionMethod, ZipEntry** ppEntry)
- {
- return addCommon(NULL, data, size, storageName,
- ZipEntry::kCompressStored,
- compressionMethod, ppEntry);
- }
-
- status_t add(const BigBuffer& data, const char* storageName,
- int compressionMethod, ZipEntry** ppEntry);
-
- /*
- * Add an entry by copying it from another zip file. If storageName is
- * non-NULL, the entry will be inserted with the name storageName, otherwise
- * it will have the same name as the source entry. If "padding" is
- * nonzero, the specified number of bytes will be added to the "extra"
- * field in the header.
- *
- * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
- */
- status_t add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry,
- const char* storageName, int padding, ZipEntry** ppEntry);
-
- /*
- * Mark an entry as having been removed. It is not actually deleted
- * from the archive or our internal data structures until flush() is
- * called.
- */
- status_t remove(ZipEntry* pEntry);
-
- /*
- * Flush changes. If mNeedCDRewrite is set, this writes the central dir.
- */
- status_t flush(void);
-
- /*
- * Expand the data into the buffer provided. The buffer must hold
- * at least <uncompressed len> bytes. Variation expands directly
- * to a file.
- *
- * Returns "false" if an error was encountered in the compressed data.
- */
- //bool uncompress(const ZipEntry* pEntry, void* buf) const;
- //bool uncompress(const ZipEntry* pEntry, FILE* fp) const;
- void* uncompress(const ZipEntry* pEntry);
-
- /*
- * Get an entry, by name. Returns NULL if not found.
- *
- * Does not return entries pending deletion.
- */
- ZipEntry* getEntryByName(const char* fileName) const;
-
- /*
- * Get the Nth entry in the archive.
- *
- * This will return an entry that is pending deletion.
- */
- int getNumEntries(void) const { return mEntries.size(); }
- ZipEntry* getEntryByIndex(int idx) const;
-
-private:
- /* these are private and not defined */
- ZipFile(const ZipFile& src);
- ZipFile& operator=(const ZipFile& src);
-
- class EndOfCentralDir {
- public:
- EndOfCentralDir(void) :
- mDiskNumber(0),
- mDiskWithCentralDir(0),
- mNumEntries(0),
- mTotalNumEntries(0),
- mCentralDirSize(0),
- mCentralDirOffset(0),
- mCommentLen(0),
- mComment(NULL)
- {}
- virtual ~EndOfCentralDir(void) {
- delete[] mComment;
- }
-
- status_t readBuf(const unsigned char* buf, int len);
- status_t write(FILE* fp);
-
- //unsigned long mSignature;
- unsigned short mDiskNumber;
- unsigned short mDiskWithCentralDir;
- unsigned short mNumEntries;
- unsigned short mTotalNumEntries;
- unsigned long mCentralDirSize;
- unsigned long mCentralDirOffset; // offset from first disk
- unsigned short mCommentLen;
- unsigned char* mComment;
-
- enum {
- kSignature = 0x06054b50,
- kEOCDLen = 22, // EndOfCentralDir len, excl. comment
-
- kMaxCommentLen = 65535, // longest possible in ushort
- kMaxEOCDSearch = kMaxCommentLen + EndOfCentralDir::kEOCDLen,
-
- };
-
- void dump(void) const;
- };
-
-
- /* read all entries in the central dir */
- status_t readCentralDir(void);
-
- /* crunch deleted entries out */
- status_t crunchArchive(void);
-
- /* clean up mEntries */
- void discardEntries(void);
-
- /* common handler for all "add" functions */
- status_t addCommon(const char* fileName, const void* data, size_t size,
- const char* storageName, int sourceType, int compressionMethod,
- ZipEntry** ppEntry);
-
- /* copy all of "srcFp" into "dstFp" */
- status_t copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32);
- /* copy all of "data" into "dstFp" */
- status_t copyDataToFp(FILE* dstFp,
- const void* data, size_t size, unsigned long* pCRC32);
- /* copy some of "srcFp" into "dstFp" */
- status_t copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length,
- unsigned long* pCRC32);
- /* like memmove(), but on parts of a single file */
- status_t filemove(FILE* fp, off_t dest, off_t src, size_t n);
- /* compress all of "srcFp" into "dstFp", using Deflate */
- status_t compressFpToFp(FILE* dstFp, FILE* srcFp,
- const void* data, size_t size, unsigned long* pCRC32);
-
- /* get modification date from a file descriptor */
- time_t getModTime(int fd);
-
- /*
- * We use stdio FILE*, which gives us buffering but makes dealing
- * with files >2GB awkward. Until we support Zip64, we're fine.
- */
- FILE* mZipFp; // Zip file pointer
-
- /* one of these per file */
- EndOfCentralDir mEOCD;
-
- /* did we open this read-only? */
- bool mReadOnly;
-
- /* set this when we trash the central dir */
- bool mNeedCDRewrite;
-
- /*
- * One ZipEntry per entry in the zip file. I'm using pointers instead
- * of objects because it's easier than making operator= work for the
- * classes and sub-classes.
- */
- std::vector<ZipEntry*> mEntries;
-};
-
-}; // namespace aapt
-
-#endif // __LIBS_ZIPFILE_H
diff --git a/tools/aapt2/compile/Compile.cpp b/tools/aapt2/compile/Compile.cpp
new file mode 100644
index 0000000..39088bc
--- /dev/null
+++ b/tools/aapt2/compile/Compile.cpp
@@ -0,0 +1,441 @@
+/*
+ * 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.
+ */
+
+#include "ConfigDescription.h"
+#include "Diagnostics.h"
+#include "Flags.h"
+#include "ResourceParser.h"
+#include "ResourceTable.h"
+#include "XmlDom.h"
+#include "XmlPullParser.h"
+
+#include "compile/IdAssigner.h"
+#include "compile/Png.h"
+#include "compile/XmlIdCollector.h"
+#include "flatten/FileExportWriter.h"
+#include "flatten/TableFlattener.h"
+#include "flatten/XmlFlattener.h"
+#include "util/Files.h"
+#include "util/Maybe.h"
+#include "util/Util.h"
+
+#include <fstream>
+#include <string>
+
+namespace aapt {
+
+struct ResourcePathData {
+ Source source;
+ std::u16string resourceDir;
+ std::u16string name;
+ std::string extension;
+
+ // Original config str. We keep this because when we parse the config, we may add on
+ // version qualifiers. We want to preserve the original input so the output is easily
+ // computed before hand.
+ std::string configStr;
+ ConfigDescription config;
+};
+
+/**
+ * Resource file paths are expected to look like:
+ * [--/res/]type[-config]/name
+ */
+static Maybe<ResourcePathData> extractResourcePathData(const std::string& path,
+ std::string* outError) {
+ std::vector<std::string> parts = util::split(path, file::sDirSep);
+ if (parts.size() < 2) {
+ if (outError) *outError = "bad resource path";
+ return {};
+ }
+
+ std::string& dir = parts[parts.size() - 2];
+ StringPiece dirStr = dir;
+
+ StringPiece configStr;
+ ConfigDescription config;
+ size_t dashPos = dir.find('-');
+ if (dashPos != std::string::npos) {
+ configStr = dirStr.substr(dashPos + 1, dir.size() - (dashPos + 1));
+ if (!ConfigDescription::parse(configStr, &config)) {
+ if (outError) {
+ std::stringstream errStr;
+ errStr << "invalid configuration '" << configStr << "'";
+ *outError = errStr.str();
+ }
+ return {};
+ }
+ dirStr = dirStr.substr(0, dashPos);
+ }
+
+ std::string& filename = parts[parts.size() - 1];
+ StringPiece name = filename;
+ StringPiece extension;
+ size_t dotPos = filename.find('.');
+ if (dotPos != std::string::npos) {
+ extension = name.substr(dotPos + 1, filename.size() - (dotPos + 1));
+ name = name.substr(0, dotPos);
+ }
+
+ return ResourcePathData{
+ Source{ path },
+ util::utf8ToUtf16(dirStr),
+ util::utf8ToUtf16(name),
+ extension.toString(),
+ configStr.toString(),
+ config
+ };
+}
+
+struct CompileOptions {
+ std::string outputPath;
+ Maybe<std::u16string> product;
+ bool verbose = false;
+};
+
+static std::string buildIntermediateFilename(const std::string outDir,
+ const ResourcePathData& data) {
+ std::stringstream name;
+ name << data.resourceDir;
+ if (!data.configStr.empty()) {
+ name << "-" << data.configStr;
+ }
+ name << "_" << data.name << "." << data.extension << ".flat";
+ std::string outPath = outDir;
+ file::appendPath(&outPath, name.str());
+ return outPath;
+}
+
+static bool compileTable(IAaptContext* context, const CompileOptions& options,
+ const ResourcePathData& pathData, const std::string& outputPath) {
+ ResourceTable table;
+ {
+ std::ifstream fin(pathData.source.path, std::ifstream::binary);
+ if (!fin) {
+ context->getDiagnostics()->error(DiagMessage(pathData.source) << strerror(errno));
+ return false;
+ }
+
+
+ // Parse the values file from XML.
+ XmlPullParser xmlParser(fin);
+
+ ResourceParserOptions parserOptions;
+ parserOptions.product = options.product;
+
+ // If the filename includes donottranslate, then the default translatable is false.
+ parserOptions.translatable = pathData.name.find(u"donottranslate") == std::string::npos;
+
+ ResourceParser resParser(context->getDiagnostics(), &table, pathData.source,
+ pathData.config, parserOptions);
+ if (!resParser.parse(&xmlParser)) {
+ return false;
+ }
+
+ fin.close();
+ }
+
+ // Ensure we have the compilation package at least.
+ table.createPackage(context->getCompilationPackage());
+
+ for (auto& pkg : table.packages) {
+ if (!pkg->id) {
+ // If no package ID was set while parsing (public identifiers), auto assign an ID.
+ pkg->id = context->getPackageId();
+ }
+ }
+
+ // Assign IDs to prepare the table for flattening.
+ IdAssigner idAssigner;
+ if (!idAssigner.consume(context, &table)) {
+ return false;
+ }
+
+ // Flatten the table.
+ BigBuffer buffer(1024);
+ TableFlattenerOptions tableFlattenerOptions;
+ tableFlattenerOptions.useExtendedChunks = true;
+ TableFlattener flattener(&buffer, tableFlattenerOptions);
+ if (!flattener.consume(context, &table)) {
+ return false;
+ }
+
+ // Build the output filename.
+ std::ofstream fout(outputPath, std::ofstream::binary);
+ if (!fout) {
+ context->getDiagnostics()->error(DiagMessage(Source{ outputPath }) << strerror(errno));
+ return false;
+ }
+
+ // Write it to disk.
+ if (!util::writeAll(fout, buffer)) {
+ context->getDiagnostics()->error(DiagMessage(Source{ outputPath }) << strerror(errno));
+ return false;
+ }
+ return true;
+}
+
+static bool compileXml(IAaptContext* context, const CompileOptions& options,
+ const ResourcePathData& pathData, const std::string& outputPath) {
+
+ std::unique_ptr<XmlResource> xmlRes;
+
+ {
+ std::ifstream fin(pathData.source.path, std::ifstream::binary);
+ if (!fin) {
+ context->getDiagnostics()->error(DiagMessage(pathData.source) << strerror(errno));
+ return false;
+ }
+
+ xmlRes = xml::inflate(&fin, context->getDiagnostics(), pathData.source);
+
+ fin.close();
+ }
+
+ if (!xmlRes) {
+ return false;
+ }
+
+ // Collect IDs that are defined here.
+ XmlIdCollector collector;
+ if (!collector.consume(context, xmlRes.get())) {
+ return false;
+ }
+
+ xmlRes->file.name = ResourceName{ {}, *parseResourceType(pathData.resourceDir), pathData.name };
+ xmlRes->file.config = pathData.config;
+ xmlRes->file.source = pathData.source;
+
+ BigBuffer buffer(1024);
+ ChunkWriter fileExportWriter = wrapBufferWithFileExportHeader(&buffer, &xmlRes->file);
+
+ XmlFlattenerOptions xmlFlattenerOptions;
+ xmlFlattenerOptions.keepRawValues = true;
+ XmlFlattener flattener(fileExportWriter.getBuffer(), xmlFlattenerOptions);
+ if (!flattener.consume(context, xmlRes.get())) {
+ return false;
+ }
+
+ fileExportWriter.finish();
+
+ std::ofstream fout(outputPath, std::ofstream::binary);
+ if (!fout) {
+ context->getDiagnostics()->error(DiagMessage(Source{ outputPath }) << strerror(errno));
+ return false;
+ }
+
+ // Write it to disk.
+ if (!util::writeAll(fout, buffer)) {
+ context->getDiagnostics()->error(DiagMessage(Source{ outputPath }) << strerror(errno));
+ return false;
+ }
+ return true;
+}
+
+static bool compilePng(IAaptContext* context, const CompileOptions& options,
+ const ResourcePathData& pathData, const std::string& outputPath) {
+ BigBuffer buffer(4096);
+ ResourceFile resFile;
+ resFile.name = ResourceName{ {}, *parseResourceType(pathData.resourceDir), pathData.name };
+ resFile.config = pathData.config;
+ resFile.source = pathData.source;
+
+ ChunkWriter fileExportWriter = wrapBufferWithFileExportHeader(&buffer, &resFile);
+
+ {
+ std::ifstream fin(pathData.source.path, std::ifstream::binary);
+ if (!fin) {
+ context->getDiagnostics()->error(DiagMessage(pathData.source) << strerror(errno));
+ return false;
+ }
+
+ Png png(context->getDiagnostics());
+ if (!png.process(pathData.source, &fin, fileExportWriter.getBuffer(), {})) {
+ return false;
+ }
+ }
+
+ fileExportWriter.finish();
+
+ std::ofstream fout(outputPath, std::ofstream::binary);
+ if (!fout) {
+ context->getDiagnostics()->error(DiagMessage(Source{ outputPath }) << strerror(errno));
+ return false;
+ }
+
+ if (!util::writeAll(fout, buffer)) {
+ context->getDiagnostics()->error(DiagMessage(Source{ outputPath }) << strerror(errno));
+ return false;
+ }
+ return true;
+}
+
+static bool compileFile(IAaptContext* context, const CompileOptions& options,
+ const ResourcePathData& pathData, const std::string& outputPath) {
+ BigBuffer buffer(256);
+ ResourceFile resFile;
+ resFile.name = ResourceName{ {}, *parseResourceType(pathData.resourceDir), pathData.name };
+ resFile.config = pathData.config;
+ resFile.source = pathData.source;
+
+ ChunkWriter fileExportWriter = wrapBufferWithFileExportHeader(&buffer, &resFile);
+
+ std::string errorStr;
+ Maybe<android::FileMap> f = file::mmapPath(pathData.source.path, &errorStr);
+ if (!f) {
+ context->getDiagnostics()->error(DiagMessage(pathData.source) << errorStr);
+ return false;
+ }
+
+ std::ofstream fout(outputPath, std::ofstream::binary);
+ if (!fout) {
+ context->getDiagnostics()->error(DiagMessage(Source{ outputPath }) << strerror(errno));
+ return false;
+ }
+
+ // Manually set the size and don't call finish(). This is because we are not copying from
+ // the buffer the entire file.
+ fileExportWriter.getChunkHeader()->size =
+ util::hostToDevice32(buffer.size() + f.value().getDataLength());
+ if (!util::writeAll(fout, buffer)) {
+ context->getDiagnostics()->error(DiagMessage(Source{ outputPath }) << strerror(errno));
+ return false;
+ }
+
+ if (!fout.write((const char*) f.value().getDataPtr(), f.value().getDataLength())) {
+ context->getDiagnostics()->error(DiagMessage(Source{ outputPath }) << strerror(errno));
+ return false;
+ }
+ return true;
+}
+
+class CompileContext : public IAaptContext {
+private:
+ StdErrDiagnostics mDiagnostics;
+
+public:
+ IDiagnostics* getDiagnostics() override {
+ return &mDiagnostics;
+ }
+
+ NameMangler* getNameMangler() override {
+ abort();
+ return nullptr;
+ }
+
+ StringPiece16 getCompilationPackage() override {
+ return {};
+ }
+
+ uint8_t getPackageId() override {
+ return 0x0;
+ }
+
+ ISymbolTable* getExternalSymbols() override {
+ abort();
+ return nullptr;
+ }
+};
+
+/**
+ * Entry point for compilation phase. Parses arguments and dispatches to the correct steps.
+ */
+int compile(const std::vector<StringPiece>& args) {
+ CompileOptions options;
+
+ Maybe<std::string> product;
+ Flags flags = Flags()
+ .requiredFlag("-o", "Output path", &options.outputPath)
+ .optionalFlag("--product", "Product type to compile", &product)
+ .optionalSwitch("-v", "Enables verbose logging", &options.verbose);
+ if (!flags.parse("aapt2 compile", args, &std::cerr)) {
+ return 1;
+ }
+
+ if (product) {
+ options.product = util::utf8ToUtf16(product.value());
+ }
+
+ CompileContext context;
+
+ std::vector<ResourcePathData> inputData;
+ inputData.reserve(flags.getArgs().size());
+
+ // Collect data from the path for each input file.
+ for (const std::string& arg : flags.getArgs()) {
+ std::string errorStr;
+ if (Maybe<ResourcePathData> pathData = extractResourcePathData(arg, &errorStr)) {
+ inputData.push_back(std::move(pathData.value()));
+ } else {
+ context.getDiagnostics()->error(DiagMessage() << errorStr << " (" << arg << ")");
+ return 1;
+ }
+ }
+
+ bool error = false;
+ for (ResourcePathData& pathData : inputData) {
+ if (options.verbose) {
+ context.getDiagnostics()->note(DiagMessage(pathData.source) << "processing");
+ }
+
+ if (pathData.resourceDir == u"values") {
+ // Overwrite the extension.
+ pathData.extension = "arsc";
+
+ const std::string outputFilename = buildIntermediateFilename(
+ options.outputPath, pathData);
+ if (!compileTable(&context, options, pathData, outputFilename)) {
+ error = true;
+ }
+
+ } else {
+ const std::string outputFilename = buildIntermediateFilename(options.outputPath,
+ pathData);
+ if (const ResourceType* type = parseResourceType(pathData.resourceDir)) {
+ if (*type != ResourceType::kRaw) {
+ if (pathData.extension == "xml") {
+ if (!compileXml(&context, options, pathData, outputFilename)) {
+ error = true;
+ }
+ } else if (pathData.extension == "png" || pathData.extension == "9.png") {
+ if (!compilePng(&context, options, pathData, outputFilename)) {
+ error = true;
+ }
+ } else {
+ if (!compileFile(&context, options, pathData, outputFilename)) {
+ error = true;
+ }
+ }
+ } else {
+ if (!compileFile(&context, options, pathData, outputFilename)) {
+ error = true;
+ }
+ }
+ } else {
+ context.getDiagnostics()->error(
+ DiagMessage() << "invalid file path '" << pathData.source << "'");
+ error = true;
+ }
+ }
+ }
+
+ if (error) {
+ return 1;
+ }
+ return 0;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/compile/IdAssigner.cpp b/tools/aapt2/compile/IdAssigner.cpp
new file mode 100644
index 0000000..80c6bbc
--- /dev/null
+++ b/tools/aapt2/compile/IdAssigner.cpp
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ */
+
+#include "ResourceTable.h"
+
+#include "compile/IdAssigner.h"
+#include "process/IResourceTableConsumer.h"
+#include "util/Util.h"
+
+#include <bitset>
+#include <cassert>
+#include <set>
+
+namespace aapt {
+
+bool IdAssigner::consume(IAaptContext* context, ResourceTable* table) {
+ std::bitset<256> usedTypeIds;
+ std::set<uint16_t> usedEntryIds;
+
+ for (auto& package : table->packages) {
+ assert(package->id && "packages must have manually assigned IDs");
+
+ usedTypeIds.reset();
+
+ // Type ID 0 is invalid, reserve it.
+ usedTypeIds.set(0);
+
+ // Collect used type IDs.
+ for (auto& type : package->types) {
+ if (type->id) {
+ usedEntryIds.clear();
+
+ if (usedTypeIds[type->id.value()]) {
+ // This ID is already taken!
+ context->getDiagnostics()->error(DiagMessage()
+ << "type '" << type->type << "' in "
+ << "package '" << package->name << "' has "
+ << "duplicate ID "
+ << std::hex << (int) type->id.value()
+ << std::dec);
+ return false;
+ }
+
+ // Mark the type ID as taken.
+ usedTypeIds.set(type->id.value());
+ }
+
+ // Collect used entry IDs.
+ for (auto& entry : type->entries) {
+ if (entry->id) {
+ // Mark entry ID as taken.
+ if (!usedEntryIds.insert(entry->id.value()).second) {
+ // This ID existed before!
+ ResourceNameRef nameRef =
+ { package->name, type->type, entry->name };
+ ResourceId takenId(package->id.value(), type->id.value(),
+ entry->id.value());
+ context->getDiagnostics()->error(DiagMessage()
+ << "resource '" << nameRef << "' "
+ << "has duplicate ID '"
+ << takenId << "'");
+ return false;
+ }
+ }
+ }
+
+ // Assign unused entry IDs.
+ const auto endUsedEntryIter = usedEntryIds.end();
+ auto nextUsedEntryIter = usedEntryIds.begin();
+ uint16_t nextId = 0;
+ for (auto& entry : type->entries) {
+ if (!entry->id) {
+ // Assign the next available entryID.
+ while (nextUsedEntryIter != endUsedEntryIter &&
+ nextId == *nextUsedEntryIter) {
+ nextId++;
+ ++nextUsedEntryIter;
+ }
+ entry->id = nextId++;
+ }
+ }
+ }
+
+ // Assign unused type IDs.
+ size_t nextTypeId = 0;
+ for (auto& type : package->types) {
+ if (!type->id) {
+ while (nextTypeId < usedTypeIds.size() && usedTypeIds[nextTypeId]) {
+ nextTypeId++;
+ }
+ type->id = static_cast<uint8_t>(nextTypeId);
+ nextTypeId++;
+ }
+ }
+ }
+ return true;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/compile/IdAssigner.h b/tools/aapt2/compile/IdAssigner.h
new file mode 100644
index 0000000..514df3a
--- /dev/null
+++ b/tools/aapt2/compile/IdAssigner.h
@@ -0,0 +1,34 @@
+/*
+ * 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_COMPILE_IDASSIGNER_H
+#define AAPT_COMPILE_IDASSIGNER_H
+
+#include "process/IResourceTableConsumer.h"
+
+namespace aapt {
+
+/**
+ * Assigns IDs to each resource in the table, respecting existing IDs and filling in gaps
+ * in between fixed ID assignments.
+ */
+struct IdAssigner : public IResourceTableConsumer {
+ bool consume(IAaptContext* context, ResourceTable* table) override;
+};
+
+} // namespace aapt
+
+#endif /* AAPT_COMPILE_IDASSIGNER_H */
diff --git a/tools/aapt2/compile/IdAssigner_test.cpp b/tools/aapt2/compile/IdAssigner_test.cpp
new file mode 100644
index 0000000..e25a17a
--- /dev/null
+++ b/tools/aapt2/compile/IdAssigner_test.cpp
@@ -0,0 +1,123 @@
+/*
+ * 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.
+ */
+
+#include "compile/IdAssigner.h"
+
+#include "test/Context.h"
+#include "test/Builders.h"
+
+#include <gtest/gtest.h>
+
+namespace aapt {
+
+::testing::AssertionResult verifyIds(ResourceTable* table);
+
+TEST(IdAssignerTest, AssignIds) {
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .addSimple(u"@android:attr/foo")
+ .addSimple(u"@android:attr/bar")
+ .addSimple(u"@android:id/foo")
+ .setPackageId(u"android", 0x01)
+ .build();
+
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+ IdAssigner assigner;
+
+ ASSERT_TRUE(assigner.consume(context.get(), table.get()));
+ ASSERT_TRUE(verifyIds(table.get()));
+}
+
+TEST(IdAssignerTest, AssignIdsWithReservedIds) {
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .addSimple(u"@android:attr/foo", ResourceId(0x01040006))
+ .addSimple(u"@android:attr/bar")
+ .addSimple(u"@android:id/foo")
+ .addSimple(u"@app:id/biz")
+ .setPackageId(u"android", 0x01)
+ .setPackageId(u"app", 0x7f)
+ .build();
+
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+ IdAssigner assigner;
+
+ ASSERT_TRUE(assigner.consume(context.get(), table.get()));
+ ASSERT_TRUE(verifyIds(table.get()));
+}
+
+TEST(IdAssignerTest, FailWhenNonUniqueIdsAssigned) {
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .addSimple(u"@android:attr/foo", ResourceId(0x01040006))
+ .addSimple(u"@android:attr/bar", ResourceId(0x01040006))
+ .setPackageId(u"android", 0x01)
+ .setPackageId(u"app", 0x7f)
+ .build();
+
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+ IdAssigner assigner;
+
+ ASSERT_FALSE(assigner.consume(context.get(), table.get()));
+}
+
+::testing::AssertionResult verifyIds(ResourceTable* table) {
+ std::set<uint8_t> packageIds;
+ for (auto& package : table->packages) {
+ if (!package->id) {
+ return ::testing::AssertionFailure() << "package " << package->name << " has no ID";
+ }
+
+ if (!packageIds.insert(package->id.value()).second) {
+ return ::testing::AssertionFailure() << "package " << package->name
+ << " has non-unique ID " << std::hex << (int) package->id.value() << std::dec;
+ }
+ }
+
+ for (auto& package : table->packages) {
+ std::set<uint8_t> typeIds;
+ for (auto& type : package->types) {
+ if (!type->id) {
+ return ::testing::AssertionFailure() << "type " << type->type << " of package "
+ << package->name << " has no ID";
+ }
+
+ if (!typeIds.insert(type->id.value()).second) {
+ return ::testing::AssertionFailure() << "type " << type->type
+ << " of package " << package->name << " has non-unique ID "
+ << std::hex << (int) type->id.value() << std::dec;
+ }
+ }
+
+
+ for (auto& type : package->types) {
+ std::set<uint16_t> entryIds;
+ for (auto& entry : type->entries) {
+ if (!entry->id) {
+ return ::testing::AssertionFailure() << "entry " << entry->name << " of type "
+ << type->type << " of package " << package->name << " has no ID";
+ }
+
+ if (!entryIds.insert(entry->id.value()).second) {
+ return ::testing::AssertionFailure() << "entry " << entry->name
+ << " of type " << type->type << " of package " << package->name
+ << " has non-unique ID "
+ << std::hex << (int) entry->id.value() << std::dec;
+ }
+ }
+ }
+ }
+ return ::testing::AssertionSuccess() << "all IDs are unique and assigned";
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/compile/Png.cpp b/tools/aapt2/compile/Png.cpp
new file mode 100644
index 0000000..9837c4e
--- /dev/null
+++ b/tools/aapt2/compile/Png.cpp
@@ -0,0 +1,1278 @@
+/*
+ * 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.
+ */
+
+#include "util/BigBuffer.h"
+#include "Png.h"
+#include "Source.h"
+#include "util/Util.h"
+
+#include <androidfw/ResourceTypes.h>
+#include <iostream>
+#include <png.h>
+#include <sstream>
+#include <string>
+#include <vector>
+#include <zlib.h>
+
+namespace aapt {
+
+constexpr bool kDebug = false;
+constexpr size_t kPngSignatureSize = 8u;
+
+struct PngInfo {
+ ~PngInfo() {
+ for (png_bytep row : rows) {
+ if (row != nullptr) {
+ delete[] row;
+ }
+ }
+
+ delete[] xDivs;
+ delete[] yDivs;
+ }
+
+ void* serialize9Patch() {
+ void* serialized = android::Res_png_9patch::serialize(info9Patch, xDivs, yDivs,
+ colors.data());
+ reinterpret_cast<android::Res_png_9patch*>(serialized)->deviceToFile();
+ return serialized;
+ }
+
+ uint32_t width = 0;
+ uint32_t height = 0;
+ std::vector<png_bytep> rows;
+
+ bool is9Patch = false;
+ android::Res_png_9patch info9Patch;
+ int32_t* xDivs = nullptr;
+ int32_t* yDivs = nullptr;
+ std::vector<uint32_t> colors;
+
+ // Layout padding.
+ bool haveLayoutBounds = false;
+ int32_t layoutBoundsLeft;
+ int32_t layoutBoundsTop;
+ int32_t layoutBoundsRight;
+ int32_t layoutBoundsBottom;
+
+ // Round rect outline description.
+ int32_t outlineInsetsLeft;
+ int32_t outlineInsetsTop;
+ int32_t outlineInsetsRight;
+ int32_t outlineInsetsBottom;
+ float outlineRadius;
+ uint8_t outlineAlpha;
+};
+
+static void readDataFromStream(png_structp readPtr, png_bytep data, png_size_t length) {
+ std::istream* input = reinterpret_cast<std::istream*>(png_get_io_ptr(readPtr));
+ if (!input->read(reinterpret_cast<char*>(data), length)) {
+ png_error(readPtr, strerror(errno));
+ }
+}
+
+static void writeDataToStream(png_structp writePtr, png_bytep data, png_size_t length) {
+ BigBuffer* outBuffer = reinterpret_cast<BigBuffer*>(png_get_io_ptr(writePtr));
+ png_bytep buf = outBuffer->nextBlock<png_byte>(length);
+ memcpy(buf, data, length);
+}
+
+static void flushDataToStream(png_structp /*writePtr*/) {
+}
+
+static void logWarning(png_structp readPtr, png_const_charp warningMessage) {
+ IDiagnostics* diag = reinterpret_cast<IDiagnostics*>(png_get_error_ptr(readPtr));
+ diag->warn(DiagMessage() << warningMessage);
+}
+
+
+static bool readPng(IDiagnostics* diag, png_structp readPtr, png_infop infoPtr, PngInfo* outInfo) {
+ if (setjmp(png_jmpbuf(readPtr))) {
+ diag->error(DiagMessage() << "failed reading png");
+ return false;
+ }
+
+ png_set_sig_bytes(readPtr, kPngSignatureSize);
+ png_read_info(readPtr, infoPtr);
+
+ int colorType, bitDepth, interlaceType, compressionType;
+ png_get_IHDR(readPtr, infoPtr, &outInfo->width, &outInfo->height, &bitDepth, &colorType,
+ &interlaceType, &compressionType, nullptr);
+
+ if (colorType == PNG_COLOR_TYPE_PALETTE) {
+ png_set_palette_to_rgb(readPtr);
+ }
+
+ if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) {
+ png_set_expand_gray_1_2_4_to_8(readPtr);
+ }
+
+ if (png_get_valid(readPtr, infoPtr, PNG_INFO_tRNS)) {
+ png_set_tRNS_to_alpha(readPtr);
+ }
+
+ if (bitDepth == 16) {
+ png_set_strip_16(readPtr);
+ }
+
+ if (!(colorType & PNG_COLOR_MASK_ALPHA)) {
+ png_set_add_alpha(readPtr, 0xFF, PNG_FILLER_AFTER);
+ }
+
+ if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
+ png_set_gray_to_rgb(readPtr);
+ }
+
+ png_set_interlace_handling(readPtr);
+ png_read_update_info(readPtr, infoPtr);
+
+ const uint32_t rowBytes = png_get_rowbytes(readPtr, infoPtr);
+ outInfo->rows.resize(outInfo->height);
+ for (size_t i = 0; i < outInfo->height; i++) {
+ outInfo->rows[i] = new png_byte[rowBytes];
+ }
+
+ png_read_image(readPtr, outInfo->rows.data());
+ png_read_end(readPtr, infoPtr);
+ return true;
+}
+
+static void checkNinePatchSerialization(android::Res_png_9patch* inPatch, void* data) {
+ size_t patchSize = inPatch->serializedSize();
+ void* newData = malloc(patchSize);
+ memcpy(newData, data, patchSize);
+ android::Res_png_9patch* outPatch = inPatch->deserialize(newData);
+ outPatch->fileToDevice();
+ // deserialization is done in place, so outPatch == newData
+ assert(outPatch == newData);
+ assert(outPatch->numXDivs == inPatch->numXDivs);
+ assert(outPatch->numYDivs == inPatch->numYDivs);
+ assert(outPatch->paddingLeft == inPatch->paddingLeft);
+ assert(outPatch->paddingRight == inPatch->paddingRight);
+ assert(outPatch->paddingTop == inPatch->paddingTop);
+ assert(outPatch->paddingBottom == inPatch->paddingBottom);
+/* for (int i = 0; i < outPatch->numXDivs; i++) {
+ assert(outPatch->getXDivs()[i] == inPatch->getXDivs()[i]);
+ }
+ for (int i = 0; i < outPatch->numYDivs; i++) {
+ assert(outPatch->getYDivs()[i] == inPatch->getYDivs()[i]);
+ }
+ for (int i = 0; i < outPatch->numColors; i++) {
+ assert(outPatch->getColors()[i] == inPatch->getColors()[i]);
+ }*/
+ free(newData);
+}
+
+/*static void dump_image(int w, int h, const png_byte* const* rows, int color_type) {
+ int i, j, rr, gg, bb, aa;
+
+ int bpp;
+ if (color_type == PNG_COLOR_TYPE_PALETTE || color_type == PNG_COLOR_TYPE_GRAY) {
+ bpp = 1;
+ } else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
+ bpp = 2;
+ } else if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
+ // We use a padding byte even when there is no alpha
+ bpp = 4;
+ } else {
+ printf("Unknown color type %d.\n", color_type);
+ }
+
+ for (j = 0; j < h; j++) {
+ const png_byte* row = rows[j];
+ for (i = 0; i < w; i++) {
+ rr = row[0];
+ gg = row[1];
+ bb = row[2];
+ aa = row[3];
+ row += bpp;
+
+ if (i == 0) {
+ printf("Row %d:", j);
+ }
+ switch (bpp) {
+ case 1:
+ printf(" (%d)", rr);
+ break;
+ case 2:
+ printf(" (%d %d", rr, gg);
+ break;
+ case 3:
+ printf(" (%d %d %d)", rr, gg, bb);
+ break;
+ case 4:
+ printf(" (%d %d %d %d)", rr, gg, bb, aa);
+ break;
+ }
+ if (i == (w - 1)) {
+ printf("\n");
+ }
+ }
+ }
+}*/
+
+#define MAX(a,b) ((a)>(b)?(a):(b))
+#define ABS(a) ((a)<0?-(a):(a))
+
+static void analyze_image(IDiagnostics* diag, const PngInfo& imageInfo, int grayscaleTolerance,
+ png_colorp rgbPalette, png_bytep alphaPalette,
+ int *paletteEntries, bool *hasTransparency, int *colorType,
+ png_bytepp outRows) {
+ int w = imageInfo.width;
+ int h = imageInfo.height;
+ int i, j, rr, gg, bb, aa, idx;
+ uint32_t colors[256], col;
+ int num_colors = 0;
+ int maxGrayDeviation = 0;
+
+ bool isOpaque = true;
+ bool isPalette = true;
+ bool isGrayscale = true;
+
+ // Scan the entire image and determine if:
+ // 1. Every pixel has R == G == B (grayscale)
+ // 2. Every pixel has A == 255 (opaque)
+ // 3. There are no more than 256 distinct RGBA colors
+
+ if (kDebug) {
+ printf("Initial image data:\n");
+ //dump_image(w, h, imageInfo.rows.data(), PNG_COLOR_TYPE_RGB_ALPHA);
+ }
+
+ for (j = 0; j < h; j++) {
+ const png_byte* row = imageInfo.rows[j];
+ png_bytep out = outRows[j];
+ for (i = 0; i < w; i++) {
+ rr = *row++;
+ gg = *row++;
+ bb = *row++;
+ aa = *row++;
+
+ int odev = maxGrayDeviation;
+ maxGrayDeviation = MAX(ABS(rr - gg), maxGrayDeviation);
+ maxGrayDeviation = MAX(ABS(gg - bb), maxGrayDeviation);
+ maxGrayDeviation = MAX(ABS(bb - rr), maxGrayDeviation);
+ if (maxGrayDeviation > odev) {
+ if (kDebug) {
+ printf("New max dev. = %d at pixel (%d, %d) = (%d %d %d %d)\n",
+ maxGrayDeviation, i, j, rr, gg, bb, aa);
+ }
+ }
+
+ // Check if image is really grayscale
+ if (isGrayscale) {
+ if (rr != gg || rr != bb) {
+ if (kDebug) {
+ printf("Found a non-gray pixel at %d, %d = (%d %d %d %d)\n",
+ i, j, rr, gg, bb, aa);
+ }
+ isGrayscale = false;
+ }
+ }
+
+ // Check if image is really opaque
+ if (isOpaque) {
+ if (aa != 0xff) {
+ if (kDebug) {
+ printf("Found a non-opaque pixel at %d, %d = (%d %d %d %d)\n",
+ i, j, rr, gg, bb, aa);
+ }
+ isOpaque = false;
+ }
+ }
+
+ // Check if image is really <= 256 colors
+ if (isPalette) {
+ col = (uint32_t) ((rr << 24) | (gg << 16) | (bb << 8) | aa);
+ bool match = false;
+ for (idx = 0; idx < num_colors; idx++) {
+ if (colors[idx] == col) {
+ match = true;
+ break;
+ }
+ }
+
+ // Write the palette index for the pixel to outRows optimistically
+ // We might overwrite it later if we decide to encode as gray or
+ // gray + alpha
+ *out++ = idx;
+ if (!match) {
+ if (num_colors == 256) {
+ if (kDebug) {
+ printf("Found 257th color at %d, %d\n", i, j);
+ }
+ isPalette = false;
+ } else {
+ colors[num_colors++] = col;
+ }
+ }
+ }
+ }
+ }
+
+ *paletteEntries = 0;
+ *hasTransparency = !isOpaque;
+ int bpp = isOpaque ? 3 : 4;
+ int paletteSize = w * h + bpp * num_colors;
+
+ if (kDebug) {
+ printf("isGrayscale = %s\n", isGrayscale ? "true" : "false");
+ printf("isOpaque = %s\n", isOpaque ? "true" : "false");
+ printf("isPalette = %s\n", isPalette ? "true" : "false");
+ printf("Size w/ palette = %d, gray+alpha = %d, rgb(a) = %d\n",
+ paletteSize, 2 * w * h, bpp * w * h);
+ printf("Max gray deviation = %d, tolerance = %d\n", maxGrayDeviation, grayscaleTolerance);
+ }
+
+ // Choose the best color type for the image.
+ // 1. Opaque gray - use COLOR_TYPE_GRAY at 1 byte/pixel
+ // 2. Gray + alpha - use COLOR_TYPE_PALETTE if the number of distinct combinations
+ // is sufficiently small, otherwise use COLOR_TYPE_GRAY_ALPHA
+ // 3. RGB(A) - use COLOR_TYPE_PALETTE if the number of distinct colors is sufficiently
+ // small, otherwise use COLOR_TYPE_RGB{_ALPHA}
+ if (isGrayscale) {
+ if (isOpaque) {
+ *colorType = PNG_COLOR_TYPE_GRAY; // 1 byte/pixel
+ } else {
+ // Use a simple heuristic to determine whether using a palette will
+ // save space versus using gray + alpha for each pixel.
+ // This doesn't take into account chunk overhead, filtering, LZ
+ // compression, etc.
+ if (isPalette && (paletteSize < 2 * w * h)) {
+ *colorType = PNG_COLOR_TYPE_PALETTE; // 1 byte/pixel + 4 bytes/color
+ } else {
+ *colorType = PNG_COLOR_TYPE_GRAY_ALPHA; // 2 bytes per pixel
+ }
+ }
+ } else if (isPalette && (paletteSize < bpp * w * h)) {
+ *colorType = PNG_COLOR_TYPE_PALETTE;
+ } else {
+ if (maxGrayDeviation <= grayscaleTolerance) {
+ diag->note(DiagMessage()
+ << "forcing image to gray (max deviation = "
+ << maxGrayDeviation << ")");
+ *colorType = isOpaque ? PNG_COLOR_TYPE_GRAY : PNG_COLOR_TYPE_GRAY_ALPHA;
+ } else {
+ *colorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
+ }
+ }
+
+ // Perform postprocessing of the image or palette data based on the final
+ // color type chosen
+
+ if (*colorType == PNG_COLOR_TYPE_PALETTE) {
+ // Create separate RGB and Alpha palettes and set the number of colors
+ *paletteEntries = num_colors;
+
+ // Create the RGB and alpha palettes
+ for (int idx = 0; idx < num_colors; idx++) {
+ col = colors[idx];
+ rgbPalette[idx].red = (png_byte) ((col >> 24) & 0xff);
+ rgbPalette[idx].green = (png_byte) ((col >> 16) & 0xff);
+ rgbPalette[idx].blue = (png_byte) ((col >> 8) & 0xff);
+ alphaPalette[idx] = (png_byte) (col & 0xff);
+ }
+ } else if (*colorType == PNG_COLOR_TYPE_GRAY || *colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
+ // If the image is gray or gray + alpha, compact the pixels into outRows
+ for (j = 0; j < h; j++) {
+ const png_byte* row = imageInfo.rows[j];
+ png_bytep out = outRows[j];
+ for (i = 0; i < w; i++) {
+ rr = *row++;
+ gg = *row++;
+ bb = *row++;
+ aa = *row++;
+
+ if (isGrayscale) {
+ *out++ = rr;
+ } else {
+ *out++ = (png_byte) (rr * 0.2126f + gg * 0.7152f + bb * 0.0722f);
+ }
+ if (!isOpaque) {
+ *out++ = aa;
+ }
+ }
+ }
+ }
+}
+
+static bool writePng(IDiagnostics* diag, png_structp writePtr, png_infop infoPtr, PngInfo* info,
+ int grayScaleTolerance) {
+ if (setjmp(png_jmpbuf(writePtr))) {
+ diag->error(DiagMessage() << "failed to write png");
+ return false;
+ }
+
+ uint32_t width, height;
+ int colorType, bitDepth, interlaceType, compressionType;
+
+ png_unknown_chunk unknowns[3];
+ unknowns[0].data = nullptr;
+ unknowns[1].data = nullptr;
+ unknowns[2].data = nullptr;
+
+ png_bytepp outRows = (png_bytepp) malloc((int) info->height * sizeof(png_bytep));
+ if (outRows == (png_bytepp) 0) {
+ printf("Can't allocate output buffer!\n");
+ exit(1);
+ }
+ for (uint32_t i = 0; i < info->height; i++) {
+ outRows[i] = (png_bytep) malloc(2 * (int) info->width);
+ if (outRows[i] == (png_bytep) 0) {
+ printf("Can't allocate output buffer!\n");
+ exit(1);
+ }
+ }
+
+ png_set_compression_level(writePtr, Z_BEST_COMPRESSION);
+
+ if (kDebug) {
+ diag->note(DiagMessage()
+ << "writing image: w = " << info->width
+ << ", h = " << info->height);
+ }
+
+ png_color rgbPalette[256];
+ png_byte alphaPalette[256];
+ bool hasTransparency;
+ int paletteEntries;
+
+ analyze_image(diag, *info, grayScaleTolerance, rgbPalette, alphaPalette,
+ &paletteEntries, &hasTransparency, &colorType, outRows);
+
+ // If the image is a 9-patch, we need to preserve it as a ARGB file to make
+ // sure the pixels will not be pre-dithered/clamped until we decide they are
+ if (info->is9Patch && (colorType == PNG_COLOR_TYPE_RGB ||
+ colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_PALETTE)) {
+ colorType = PNG_COLOR_TYPE_RGB_ALPHA;
+ }
+
+ if (kDebug) {
+ switch (colorType) {
+ case PNG_COLOR_TYPE_PALETTE:
+ diag->note(DiagMessage()
+ << "has " << paletteEntries
+ << " colors" << (hasTransparency ? " (with alpha)" : "")
+ << ", using PNG_COLOR_TYPE_PALLETTE");
+ break;
+ case PNG_COLOR_TYPE_GRAY:
+ diag->note(DiagMessage() << "is opaque gray, using PNG_COLOR_TYPE_GRAY");
+ break;
+ case PNG_COLOR_TYPE_GRAY_ALPHA:
+ diag->note(DiagMessage() << "is gray + alpha, using PNG_COLOR_TYPE_GRAY_ALPHA");
+ break;
+ case PNG_COLOR_TYPE_RGB:
+ diag->note(DiagMessage() << "is opaque RGB, using PNG_COLOR_TYPE_RGB");
+ break;
+ case PNG_COLOR_TYPE_RGB_ALPHA:
+ diag->note(DiagMessage() << "is RGB + alpha, using PNG_COLOR_TYPE_RGB_ALPHA");
+ break;
+ }
+ }
+
+ png_set_IHDR(writePtr, infoPtr, info->width, info->height, 8, colorType,
+ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+
+ if (colorType == PNG_COLOR_TYPE_PALETTE) {
+ png_set_PLTE(writePtr, infoPtr, rgbPalette, paletteEntries);
+ if (hasTransparency) {
+ png_set_tRNS(writePtr, infoPtr, alphaPalette, paletteEntries, (png_color_16p) 0);
+ }
+ png_set_filter(writePtr, 0, PNG_NO_FILTERS);
+ } else {
+ png_set_filter(writePtr, 0, PNG_ALL_FILTERS);
+ }
+
+ if (info->is9Patch) {
+ int chunkCount = 2 + (info->haveLayoutBounds ? 1 : 0);
+ int pIndex = info->haveLayoutBounds ? 2 : 1;
+ int bIndex = 1;
+ int oIndex = 0;
+
+ // Chunks ordered thusly because older platforms depend on the base 9 patch data being last
+ png_bytep chunkNames = info->haveLayoutBounds
+ ? (png_bytep)"npOl\0npLb\0npTc\0"
+ : (png_bytep)"npOl\0npTc";
+
+ // base 9 patch data
+ if (kDebug) {
+ diag->note(DiagMessage() << "adding 9-patch info..");
+ }
+ strcpy((char*)unknowns[pIndex].name, "npTc");
+ unknowns[pIndex].data = (png_byte*) info->serialize9Patch();
+ unknowns[pIndex].size = info->info9Patch.serializedSize();
+ // TODO: remove the check below when everything works
+ checkNinePatchSerialization(&info->info9Patch, unknowns[pIndex].data);
+
+ // automatically generated 9 patch outline data
+ int chunkSize = sizeof(png_uint_32) * 6;
+ strcpy((char*)unknowns[oIndex].name, "npOl");
+ unknowns[oIndex].data = (png_byte*) calloc(chunkSize, 1);
+ png_byte outputData[chunkSize];
+ memcpy(&outputData, &info->outlineInsetsLeft, 4 * sizeof(png_uint_32));
+ ((float*) outputData)[4] = info->outlineRadius;
+ ((png_uint_32*) outputData)[5] = info->outlineAlpha;
+ memcpy(unknowns[oIndex].data, &outputData, chunkSize);
+ unknowns[oIndex].size = chunkSize;
+
+ // optional optical inset / layout bounds data
+ if (info->haveLayoutBounds) {
+ int chunkSize = sizeof(png_uint_32) * 4;
+ strcpy((char*)unknowns[bIndex].name, "npLb");
+ unknowns[bIndex].data = (png_byte*) calloc(chunkSize, 1);
+ memcpy(unknowns[bIndex].data, &info->layoutBoundsLeft, chunkSize);
+ unknowns[bIndex].size = chunkSize;
+ }
+
+ for (int i = 0; i < chunkCount; i++) {
+ unknowns[i].location = PNG_HAVE_PLTE;
+ }
+ png_set_keep_unknown_chunks(writePtr, PNG_HANDLE_CHUNK_ALWAYS,
+ chunkNames, chunkCount);
+ png_set_unknown_chunks(writePtr, infoPtr, unknowns, chunkCount);
+
+#if PNG_LIBPNG_VER < 10600
+ // Deal with unknown chunk location bug in 1.5.x and earlier.
+ png_set_unknown_chunk_location(writePtr, infoPtr, 0, PNG_HAVE_PLTE);
+ if (info->haveLayoutBounds) {
+ png_set_unknown_chunk_location(writePtr, infoPtr, 1, PNG_HAVE_PLTE);
+ }
+#endif
+ }
+
+ png_write_info(writePtr, infoPtr);
+
+ png_bytepp rows;
+ if (colorType == PNG_COLOR_TYPE_RGB || colorType == PNG_COLOR_TYPE_RGB_ALPHA) {
+ if (colorType == PNG_COLOR_TYPE_RGB) {
+ png_set_filler(writePtr, 0, PNG_FILLER_AFTER);
+ }
+ rows = info->rows.data();
+ } else {
+ rows = outRows;
+ }
+ png_write_image(writePtr, rows);
+
+ if (kDebug) {
+ printf("Final image data:\n");
+ //dump_image(info->width, info->height, rows, colorType);
+ }
+
+ png_write_end(writePtr, infoPtr);
+
+ for (uint32_t i = 0; i < info->height; i++) {
+ free(outRows[i]);
+ }
+ free(outRows);
+ free(unknowns[0].data);
+ free(unknowns[1].data);
+ free(unknowns[2].data);
+
+ png_get_IHDR(writePtr, infoPtr, &width, &height, &bitDepth, &colorType, &interlaceType,
+ &compressionType, nullptr);
+
+ if (kDebug) {
+ diag->note(DiagMessage()
+ << "image written: w = " << width << ", h = " << height
+ << ", d = " << bitDepth << ", colors = " << colorType
+ << ", inter = " << interlaceType << ", comp = " << compressionType);
+ }
+ return true;
+}
+
+constexpr uint32_t kColorWhite = 0xffffffffu;
+constexpr uint32_t kColorTick = 0xff000000u;
+constexpr uint32_t kColorLayoutBoundsTick = 0xff0000ffu;
+
+enum class TickType {
+ kNone,
+ kTick,
+ kLayoutBounds,
+ kBoth
+};
+
+static TickType tickType(png_bytep p, bool transparent, const char** outError) {
+ png_uint_32 color = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
+
+ if (transparent) {
+ if (p[3] == 0) {
+ return TickType::kNone;
+ }
+ if (color == kColorLayoutBoundsTick) {
+ return TickType::kLayoutBounds;
+ }
+ if (color == kColorTick) {
+ return TickType::kTick;
+ }
+
+ // Error cases
+ if (p[3] != 0xff) {
+ *outError = "Frame pixels must be either solid or transparent "
+ "(not intermediate alphas)";
+ return TickType::kNone;
+ }
+
+ if (p[0] != 0 || p[1] != 0 || p[2] != 0) {
+ *outError = "Ticks in transparent frame must be black or red";
+ }
+ return TickType::kTick;
+ }
+
+ if (p[3] != 0xFF) {
+ *outError = "White frame must be a solid color (no alpha)";
+ }
+ if (color == kColorWhite) {
+ return TickType::kNone;
+ }
+ if (color == kColorTick) {
+ return TickType::kTick;
+ }
+ if (color == kColorLayoutBoundsTick) {
+ return TickType::kLayoutBounds;
+ }
+
+ if (p[0] != 0 || p[1] != 0 || p[2] != 0) {
+ *outError = "Ticks in white frame must be black or red";
+ return TickType::kNone;
+ }
+ return TickType::kTick;
+}
+
+enum class TickState {
+ kStart,
+ kInside1,
+ kOutside1
+};
+
+static bool getHorizontalTicks(png_bytep row, int width, bool transparent, bool required,
+ int32_t* outLeft, int32_t* outRight, const char** outError,
+ uint8_t* outDivs, bool multipleAllowed) {
+ *outLeft = *outRight = -1;
+ TickState state = TickState::kStart;
+ bool found = false;
+
+ for (int i = 1; i < width - 1; i++) {
+ if (tickType(row+i*4, transparent, outError) == TickType::kTick) {
+ if (state == TickState::kStart ||
+ (state == TickState::kOutside1 && multipleAllowed)) {
+ *outLeft = i-1;
+ *outRight = width-2;
+ found = true;
+ if (outDivs != NULL) {
+ *outDivs += 2;
+ }
+ state = TickState::kInside1;
+ } else if (state == TickState::kOutside1) {
+ *outError = "Can't have more than one marked region along edge";
+ *outLeft = i;
+ return false;
+ }
+ } else if (!*outError) {
+ if (state == TickState::kInside1) {
+ // We're done with this div. Move on to the next.
+ *outRight = i-1;
+ outRight += 2;
+ outLeft += 2;
+ state = TickState::kOutside1;
+ }
+ } else {
+ *outLeft = i;
+ return false;
+ }
+ }
+
+ if (required && !found) {
+ *outError = "No marked region found along edge";
+ *outLeft = -1;
+ return false;
+ }
+ return true;
+}
+
+static bool getVerticalTicks(png_bytepp rows, int offset, int height, bool transparent,
+ bool required, int32_t* outTop, int32_t* outBottom,
+ const char** outError, uint8_t* outDivs, bool multipleAllowed) {
+ *outTop = *outBottom = -1;
+ TickState state = TickState::kStart;
+ bool found = false;
+
+ for (int i = 1; i < height - 1; i++) {
+ if (tickType(rows[i]+offset, transparent, outError) == TickType::kTick) {
+ if (state == TickState::kStart ||
+ (state == TickState::kOutside1 && multipleAllowed)) {
+ *outTop = i-1;
+ *outBottom = height-2;
+ found = true;
+ if (outDivs != NULL) {
+ *outDivs += 2;
+ }
+ state = TickState::kInside1;
+ } else if (state == TickState::kOutside1) {
+ *outError = "Can't have more than one marked region along edge";
+ *outTop = i;
+ return false;
+ }
+ } else if (!*outError) {
+ if (state == TickState::kInside1) {
+ // We're done with this div. Move on to the next.
+ *outBottom = i-1;
+ outTop += 2;
+ outBottom += 2;
+ state = TickState::kOutside1;
+ }
+ } else {
+ *outTop = i;
+ return false;
+ }
+ }
+
+ if (required && !found) {
+ *outError = "No marked region found along edge";
+ *outTop = -1;
+ return false;
+ }
+ return true;
+}
+
+static bool getHorizontalLayoutBoundsTicks(png_bytep row, int width, bool transparent,
+ bool /* required */, int32_t* outLeft,
+ int32_t* outRight, const char** outError) {
+ *outLeft = *outRight = 0;
+
+ // Look for left tick
+ if (tickType(row + 4, transparent, outError) == TickType::kLayoutBounds) {
+ // Starting with a layout padding tick
+ int i = 1;
+ while (i < width - 1) {
+ (*outLeft)++;
+ i++;
+ if (tickType(row + i * 4, transparent, outError) != TickType::kLayoutBounds) {
+ break;
+ }
+ }
+ }
+
+ // Look for right tick
+ if (tickType(row + (width - 2) * 4, transparent, outError) == TickType::kLayoutBounds) {
+ // Ending with a layout padding tick
+ int i = width - 2;
+ while (i > 1) {
+ (*outRight)++;
+ i--;
+ if (tickType(row+i*4, transparent, outError) != TickType::kLayoutBounds) {
+ break;
+ }
+ }
+ }
+ return true;
+}
+
+static bool getVerticalLayoutBoundsTicks(png_bytepp rows, int offset, int height, bool transparent,
+ bool /* required */, int32_t* outTop, int32_t* outBottom,
+ const char** outError) {
+ *outTop = *outBottom = 0;
+
+ // Look for top tick
+ if (tickType(rows[1] + offset, transparent, outError) == TickType::kLayoutBounds) {
+ // Starting with a layout padding tick
+ int i = 1;
+ while (i < height - 1) {
+ (*outTop)++;
+ i++;
+ if (tickType(rows[i] + offset, transparent, outError) != TickType::kLayoutBounds) {
+ break;
+ }
+ }
+ }
+
+ // Look for bottom tick
+ if (tickType(rows[height - 2] + offset, transparent, outError) == TickType::kLayoutBounds) {
+ // Ending with a layout padding tick
+ int i = height - 2;
+ while (i > 1) {
+ (*outBottom)++;
+ i--;
+ if (tickType(rows[i] + offset, transparent, outError) != TickType::kLayoutBounds) {
+ break;
+ }
+ }
+ }
+ return true;
+}
+
+static void findMaxOpacity(png_bytepp rows, int startX, int startY, int endX, int endY,
+ int dX, int dY, int* outInset) {
+ uint8_t maxOpacity = 0;
+ int inset = 0;
+ *outInset = 0;
+ for (int x = startX, y = startY; x != endX && y != endY; x += dX, y += dY, inset++) {
+ png_byte* color = rows[y] + x * 4;
+ uint8_t opacity = color[3];
+ if (opacity > maxOpacity) {
+ maxOpacity = opacity;
+ *outInset = inset;
+ }
+ if (opacity == 0xff) return;
+ }
+}
+
+static uint8_t maxAlphaOverRow(png_bytep row, int startX, int endX) {
+ uint8_t maxAlpha = 0;
+ for (int x = startX; x < endX; x++) {
+ uint8_t alpha = (row + x * 4)[3];
+ if (alpha > maxAlpha) maxAlpha = alpha;
+ }
+ return maxAlpha;
+}
+
+static uint8_t maxAlphaOverCol(png_bytepp rows, int offsetX, int startY, int endY) {
+ uint8_t maxAlpha = 0;
+ for (int y = startY; y < endY; y++) {
+ uint8_t alpha = (rows[y] + offsetX * 4)[3];
+ if (alpha > maxAlpha) maxAlpha = alpha;
+ }
+ return maxAlpha;
+}
+
+static void getOutline(PngInfo* image) {
+ int midX = image->width / 2;
+ int midY = image->height / 2;
+ int endX = image->width - 2;
+ int endY = image->height - 2;
+
+ // find left and right extent of nine patch content on center row
+ if (image->width > 4) {
+ findMaxOpacity(image->rows.data(), 1, midY, midX, -1, 1, 0, &image->outlineInsetsLeft);
+ findMaxOpacity(image->rows.data(), endX, midY, midX, -1, -1, 0,
+ &image->outlineInsetsRight);
+ } else {
+ image->outlineInsetsLeft = 0;
+ image->outlineInsetsRight = 0;
+ }
+
+ // find top and bottom extent of nine patch content on center column
+ if (image->height > 4) {
+ findMaxOpacity(image->rows.data(), midX, 1, -1, midY, 0, 1, &image->outlineInsetsTop);
+ findMaxOpacity(image->rows.data(), midX, endY, -1, midY, 0, -1,
+ &image->outlineInsetsBottom);
+ } else {
+ image->outlineInsetsTop = 0;
+ image->outlineInsetsBottom = 0;
+ }
+
+ int innerStartX = 1 + image->outlineInsetsLeft;
+ int innerStartY = 1 + image->outlineInsetsTop;
+ int innerEndX = endX - image->outlineInsetsRight;
+ int innerEndY = endY - image->outlineInsetsBottom;
+ int innerMidX = (innerEndX + innerStartX) / 2;
+ int innerMidY = (innerEndY + innerStartY) / 2;
+
+ // assuming the image is a round rect, compute the radius by marching
+ // diagonally from the top left corner towards the center
+ image->outlineAlpha = std::max(
+ maxAlphaOverRow(image->rows[innerMidY], innerStartX, innerEndX),
+ maxAlphaOverCol(image->rows.data(), innerMidX, innerStartY, innerStartY));
+
+ int diagonalInset = 0;
+ findMaxOpacity(image->rows.data(), innerStartX, innerStartY, innerMidX, innerMidY, 1, 1,
+ &diagonalInset);
+
+ /* Determine source radius based upon inset:
+ * sqrt(r^2 + r^2) = sqrt(i^2 + i^2) + r
+ * sqrt(2) * r = sqrt(2) * i + r
+ * (sqrt(2) - 1) * r = sqrt(2) * i
+ * r = sqrt(2) / (sqrt(2) - 1) * i
+ */
+ image->outlineRadius = 3.4142f * diagonalInset;
+
+ if (kDebug) {
+ printf("outline insets %d %d %d %d, rad %f, alpha %x\n",
+ image->outlineInsetsLeft,
+ image->outlineInsetsTop,
+ image->outlineInsetsRight,
+ image->outlineInsetsBottom,
+ image->outlineRadius,
+ image->outlineAlpha);
+ }
+}
+
+static uint32_t getColor(png_bytepp rows, int left, int top, int right, int bottom) {
+ png_bytep color = rows[top] + left*4;
+
+ if (left > right || top > bottom) {
+ return android::Res_png_9patch::TRANSPARENT_COLOR;
+ }
+
+ while (top <= bottom) {
+ for (int i = left; i <= right; i++) {
+ png_bytep p = rows[top]+i*4;
+ if (color[3] == 0) {
+ if (p[3] != 0) {
+ return android::Res_png_9patch::NO_COLOR;
+ }
+ } else if (p[0] != color[0] || p[1] != color[1] ||
+ p[2] != color[2] || p[3] != color[3]) {
+ return android::Res_png_9patch::NO_COLOR;
+ }
+ }
+ top++;
+ }
+
+ if (color[3] == 0) {
+ return android::Res_png_9patch::TRANSPARENT_COLOR;
+ }
+ return (color[3]<<24) | (color[0]<<16) | (color[1]<<8) | color[2];
+}
+
+static bool do9Patch(PngInfo* image, std::string* outError) {
+ image->is9Patch = true;
+
+ int W = image->width;
+ int H = image->height;
+ int i, j;
+
+ const int maxSizeXDivs = W * sizeof(int32_t);
+ const int maxSizeYDivs = H * sizeof(int32_t);
+ int32_t* xDivs = image->xDivs = new int32_t[W];
+ int32_t* yDivs = image->yDivs = new int32_t[H];
+ uint8_t numXDivs = 0;
+ uint8_t numYDivs = 0;
+
+ int8_t numColors;
+ int numRows;
+ int numCols;
+ int top;
+ int left;
+ int right;
+ int bottom;
+ memset(xDivs, -1, maxSizeXDivs);
+ memset(yDivs, -1, maxSizeYDivs);
+ image->info9Patch.paddingLeft = image->info9Patch.paddingRight = -1;
+ image->info9Patch.paddingTop = image->info9Patch.paddingBottom = -1;
+ image->layoutBoundsLeft = image->layoutBoundsRight = 0;
+ image->layoutBoundsTop = image->layoutBoundsBottom = 0;
+
+ png_bytep p = image->rows[0];
+ bool transparent = p[3] == 0;
+ bool hasColor = false;
+
+ const char* errorMsg = nullptr;
+ int errorPixel = -1;
+ const char* errorEdge = nullptr;
+
+ int colorIndex = 0;
+ std::vector<png_bytep> newRows;
+
+ // Validate size...
+ if (W < 3 || H < 3) {
+ errorMsg = "Image must be at least 3x3 (1x1 without frame) pixels";
+ goto getout;
+ }
+
+ // Validate frame...
+ if (!transparent &&
+ (p[0] != 0xFF || p[1] != 0xFF || p[2] != 0xFF || p[3] != 0xFF)) {
+ errorMsg = "Must have one-pixel frame that is either transparent or white";
+ goto getout;
+ }
+
+ // Find left and right of sizing areas...
+ if (!getHorizontalTicks(p, W, transparent, true, &xDivs[0], &xDivs[1], &errorMsg, &numXDivs,
+ true)) {
+ errorPixel = xDivs[0];
+ errorEdge = "top";
+ goto getout;
+ }
+
+ // Find top and bottom of sizing areas...
+ if (!getVerticalTicks(image->rows.data(), 0, H, transparent, true, &yDivs[0], &yDivs[1],
+ &errorMsg, &numYDivs, true)) {
+ errorPixel = yDivs[0];
+ errorEdge = "left";
+ goto getout;
+ }
+
+ // Copy patch size data into image...
+ image->info9Patch.numXDivs = numXDivs;
+ image->info9Patch.numYDivs = numYDivs;
+
+ // Find left and right of padding area...
+ if (!getHorizontalTicks(image->rows[H-1], W, transparent, false,
+ &image->info9Patch.paddingLeft, &image->info9Patch.paddingRight,
+ &errorMsg, nullptr, false)) {
+ errorPixel = image->info9Patch.paddingLeft;
+ errorEdge = "bottom";
+ goto getout;
+ }
+
+ // Find top and bottom of padding area...
+ if (!getVerticalTicks(image->rows.data(), (W-1)*4, H, transparent, false,
+ &image->info9Patch.paddingTop, &image->info9Patch.paddingBottom,
+ &errorMsg, nullptr, false)) {
+ errorPixel = image->info9Patch.paddingTop;
+ errorEdge = "right";
+ goto getout;
+ }
+
+ // Find left and right of layout padding...
+ getHorizontalLayoutBoundsTicks(image->rows[H-1], W, transparent, false,
+ &image->layoutBoundsLeft, &image->layoutBoundsRight, &errorMsg);
+
+ getVerticalLayoutBoundsTicks(image->rows.data(), (W-1)*4, H, transparent, false,
+ &image->layoutBoundsTop, &image->layoutBoundsBottom, &errorMsg);
+
+ image->haveLayoutBounds = image->layoutBoundsLeft != 0
+ || image->layoutBoundsRight != 0
+ || image->layoutBoundsTop != 0
+ || image->layoutBoundsBottom != 0;
+
+ if (image->haveLayoutBounds) {
+ if (kDebug) {
+ printf("layoutBounds=%d %d %d %d\n", image->layoutBoundsLeft, image->layoutBoundsTop,
+ image->layoutBoundsRight, image->layoutBoundsBottom);
+ }
+ }
+
+ // use opacity of pixels to estimate the round rect outline
+ getOutline(image);
+
+ // If padding is not yet specified, take values from size.
+ if (image->info9Patch.paddingLeft < 0) {
+ image->info9Patch.paddingLeft = xDivs[0];
+ image->info9Patch.paddingRight = W - 2 - xDivs[1];
+ } else {
+ // Adjust value to be correct!
+ image->info9Patch.paddingRight = W - 2 - image->info9Patch.paddingRight;
+ }
+ if (image->info9Patch.paddingTop < 0) {
+ image->info9Patch.paddingTop = yDivs[0];
+ image->info9Patch.paddingBottom = H - 2 - yDivs[1];
+ } else {
+ // Adjust value to be correct!
+ image->info9Patch.paddingBottom = H - 2 - image->info9Patch.paddingBottom;
+ }
+
+/* if (kDebug) {
+ printf("Size ticks for %s: x0=%d, x1=%d, y0=%d, y1=%d\n", imageName,
+ xDivs[0], xDivs[1],
+ yDivs[0], yDivs[1]);
+ printf("padding ticks for %s: l=%d, r=%d, t=%d, b=%d\n", imageName,
+ image->info9Patch.paddingLeft, image->info9Patch.paddingRight,
+ image->info9Patch.paddingTop, image->info9Patch.paddingBottom);
+ }*/
+
+ // Remove frame from image.
+ newRows.resize(H - 2);
+ for (i = 0; i < H - 2; i++) {
+ newRows[i] = image->rows[i + 1];
+ memmove(newRows[i], newRows[i] + 4, (W - 2) * 4);
+ }
+ image->rows.swap(newRows);
+
+ image->width -= 2;
+ W = image->width;
+ image->height -= 2;
+ H = image->height;
+
+ // Figure out the number of rows and columns in the N-patch
+ numCols = numXDivs + 1;
+ if (xDivs[0] == 0) { // Column 1 is strechable
+ numCols--;
+ }
+ if (xDivs[numXDivs - 1] == W) {
+ numCols--;
+ }
+ numRows = numYDivs + 1;
+ if (yDivs[0] == 0) { // Row 1 is strechable
+ numRows--;
+ }
+ if (yDivs[numYDivs - 1] == H) {
+ numRows--;
+ }
+
+ // Make sure the amount of rows and columns will fit in the number of
+ // colors we can use in the 9-patch format.
+ if (numRows * numCols > 0x7F) {
+ errorMsg = "Too many rows and columns in 9-patch perimeter";
+ goto getout;
+ }
+
+ numColors = numRows * numCols;
+ image->info9Patch.numColors = numColors;
+ image->colors.resize(numColors);
+
+ // Fill in color information for each patch.
+
+ uint32_t c;
+ top = 0;
+
+ // The first row always starts with the top being at y=0 and the bottom
+ // being either yDivs[1] (if yDivs[0]=0) of yDivs[0]. In the former case
+ // the first row is stretchable along the Y axis, otherwise it is fixed.
+ // The last row always ends with the bottom being bitmap.height and the top
+ // being either yDivs[numYDivs-2] (if yDivs[numYDivs-1]=bitmap.height) or
+ // yDivs[numYDivs-1]. In the former case the last row is stretchable along
+ // the Y axis, otherwise it is fixed.
+ //
+ // The first and last columns are similarly treated with respect to the X
+ // axis.
+ //
+ // The above is to help explain some of the special casing that goes on the
+ // code below.
+
+ // The initial yDiv and whether the first row is considered stretchable or
+ // not depends on whether yDiv[0] was zero or not.
+ for (j = (yDivs[0] == 0 ? 1 : 0); j <= numYDivs && top < H; j++) {
+ if (j == numYDivs) {
+ bottom = H;
+ } else {
+ bottom = yDivs[j];
+ }
+ left = 0;
+ // The initial xDiv and whether the first column is considered
+ // stretchable or not depends on whether xDiv[0] was zero or not.
+ for (i = xDivs[0] == 0 ? 1 : 0; i <= numXDivs && left < W; i++) {
+ if (i == numXDivs) {
+ right = W;
+ } else {
+ right = xDivs[i];
+ }
+ c = getColor(image->rows.data(), left, top, right - 1, bottom - 1);
+ image->colors[colorIndex++] = c;
+ if (kDebug) {
+ if (c != android::Res_png_9patch::NO_COLOR) {
+ hasColor = true;
+ }
+ }
+ left = right;
+ }
+ top = bottom;
+ }
+
+ assert(colorIndex == numColors);
+
+ if (kDebug && hasColor) {
+ for (i = 0; i < numColors; i++) {
+ if (i == 0) printf("Colors:\n");
+ printf(" #%08x", image->colors[i]);
+ if (i == numColors - 1) printf("\n");
+ }
+ }
+getout:
+ if (errorMsg) {
+ std::stringstream err;
+ err << "9-patch malformed: " << errorMsg;
+ if (!errorEdge) {
+ err << "." << std::endl;
+ if (errorPixel >= 0) {
+ err << "Found at pixel #" << errorPixel << " along " << errorEdge << " edge";
+ } else {
+ err << "Found along " << errorEdge << " edge";
+ }
+ }
+ *outError = err.str();
+ return false;
+ }
+ return true;
+}
+
+
+bool Png::process(const Source& source, std::istream* input, BigBuffer* outBuffer,
+ const PngOptions& options) {
+ png_byte signature[kPngSignatureSize];
+
+ // Read the PNG signature first.
+ if (!input->read(reinterpret_cast<char*>(signature), kPngSignatureSize)) {
+ mDiag->error(DiagMessage() << strerror(errno));
+ return false;
+ }
+
+ // If the PNG signature doesn't match, bail early.
+ if (png_sig_cmp(signature, 0, kPngSignatureSize) != 0) {
+ mDiag->error(DiagMessage() << "not a valid png file");
+ return false;
+ }
+
+ bool result = false;
+ png_structp readPtr = nullptr;
+ png_infop infoPtr = nullptr;
+ png_structp writePtr = nullptr;
+ png_infop writeInfoPtr = nullptr;
+ PngInfo pngInfo = {};
+
+ readPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, nullptr, nullptr);
+ if (!readPtr) {
+ mDiag->error(DiagMessage() << "failed to allocate read ptr");
+ goto bail;
+ }
+
+ infoPtr = png_create_info_struct(readPtr);
+ if (!infoPtr) {
+ mDiag->error(DiagMessage() << "failed to allocate info ptr");
+ goto bail;
+ }
+
+ png_set_error_fn(readPtr, reinterpret_cast<png_voidp>(mDiag), nullptr, logWarning);
+
+ // Set the read function to read from std::istream.
+ png_set_read_fn(readPtr, (png_voidp) input, readDataFromStream);
+
+ if (!readPng(mDiag, readPtr, infoPtr, &pngInfo)) {
+ goto bail;
+ }
+
+ if (util::stringEndsWith<char>(source.path, ".9.png")) {
+ std::string errorMsg;
+ if (!do9Patch(&pngInfo, &errorMsg)) {
+ mDiag->error(DiagMessage() << errorMsg);
+ goto bail;
+ }
+ }
+
+ writePtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, nullptr, nullptr);
+ if (!writePtr) {
+ mDiag->error(DiagMessage() << "failed to allocate write ptr");
+ goto bail;
+ }
+
+ writeInfoPtr = png_create_info_struct(writePtr);
+ if (!writeInfoPtr) {
+ mDiag->error(DiagMessage() << "failed to allocate write info ptr");
+ goto bail;
+ }
+
+ png_set_error_fn(writePtr, nullptr, nullptr, logWarning);
+
+ // Set the write function to write to std::ostream.
+ png_set_write_fn(writePtr, (png_voidp)outBuffer, writeDataToStream, flushDataToStream);
+
+ if (!writePng(mDiag, writePtr, writeInfoPtr, &pngInfo, options.grayScaleTolerance)) {
+ goto bail;
+ }
+
+ result = true;
+bail:
+ if (readPtr) {
+ png_destroy_read_struct(&readPtr, &infoPtr, nullptr);
+ }
+
+ if (writePtr) {
+ png_destroy_write_struct(&writePtr, &writeInfoPtr);
+ }
+ return result;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/compile/Png.h b/tools/aapt2/compile/Png.h
new file mode 100644
index 0000000..345ff6c
--- /dev/null
+++ b/tools/aapt2/compile/Png.h
@@ -0,0 +1,47 @@
+/*
+ * 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_PNG_H
+#define AAPT_PNG_H
+
+#include "util/BigBuffer.h"
+#include "Diagnostics.h"
+#include "Source.h"
+
+#include <iostream>
+#include <string>
+
+namespace aapt {
+
+struct PngOptions {
+ int grayScaleTolerance = 0;
+};
+
+class Png {
+public:
+ Png(IDiagnostics* diag) : mDiag(diag) {
+ }
+
+ bool process(const Source& source, std::istream* input, BigBuffer* outBuffer,
+ const PngOptions& options);
+
+private:
+ IDiagnostics* mDiag;
+};
+
+} // namespace aapt
+
+#endif // AAPT_PNG_H
diff --git a/tools/aapt2/compile/XmlIdCollector.cpp b/tools/aapt2/compile/XmlIdCollector.cpp
new file mode 100644
index 0000000..dfdf710
--- /dev/null
+++ b/tools/aapt2/compile/XmlIdCollector.cpp
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+#include "ResourceUtils.h"
+#include "ResourceValues.h"
+#include "XmlDom.h"
+
+#include "compile/XmlIdCollector.h"
+
+#include <algorithm>
+#include <vector>
+
+namespace aapt {
+
+namespace {
+
+static bool cmpName(const SourcedResourceName& a, const ResourceNameRef& b) {
+ return a.name < b;
+}
+
+struct IdCollector : public xml::Visitor {
+ using xml::Visitor::visit;
+
+ std::vector<SourcedResourceName>* mOutSymbols;
+
+ IdCollector(std::vector<SourcedResourceName>* outSymbols) : mOutSymbols(outSymbols) {
+ }
+
+ void visit(xml::Element* element) override {
+ for (xml::Attribute& attr : element->attributes) {
+ ResourceNameRef name;
+ bool create = false;
+ if (ResourceUtils::tryParseReference(attr.value, &name, &create, nullptr)) {
+ if (create && name.type == ResourceType::kId) {
+ auto iter = std::lower_bound(mOutSymbols->begin(), mOutSymbols->end(),
+ name, cmpName);
+ if (iter == mOutSymbols->end() || iter->name != name) {
+ mOutSymbols->insert(iter, SourcedResourceName{ name.toResourceName(),
+ element->lineNumber });
+ }
+ }
+ }
+ }
+
+ xml::Visitor::visit(element);
+ }
+};
+
+} // namespace
+
+bool XmlIdCollector::consume(IAaptContext* context, XmlResource* xmlRes) {
+ xmlRes->file.exportedSymbols.clear();
+ IdCollector collector(&xmlRes->file.exportedSymbols);
+ xmlRes->root->accept(&collector);
+ return true;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/compile/XmlIdCollector.h b/tools/aapt2/compile/XmlIdCollector.h
new file mode 100644
index 0000000..96a58f2
--- /dev/null
+++ b/tools/aapt2/compile/XmlIdCollector.h
@@ -0,0 +1,30 @@
+/*
+ * 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_XMLIDCOLLECTOR_H
+#define AAPT_XMLIDCOLLECTOR_H
+
+#include "process/IResourceTableConsumer.h"
+
+namespace aapt {
+
+struct XmlIdCollector : public IXmlResourceConsumer {
+ bool consume(IAaptContext* context, XmlResource* xmlRes) override;
+};
+
+} // namespace aapt
+
+#endif /* AAPT_XMLIDCOLLECTOR_H */
diff --git a/tools/aapt2/compile/XmlIdCollector_test.cpp b/tools/aapt2/compile/XmlIdCollector_test.cpp
new file mode 100644
index 0000000..c703f451
--- /dev/null
+++ b/tools/aapt2/compile/XmlIdCollector_test.cpp
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+#include "compile/XmlIdCollector.h"
+#include "test/Builders.h"
+#include "test/Context.h"
+
+#include <algorithm>
+#include <gtest/gtest.h>
+
+namespace aapt {
+
+TEST(XmlIdCollectorTest, CollectsIds) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+
+ std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF(
+ <View xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/foo"
+ text="@+id/bar">
+ <SubView android:id="@+id/car"
+ class="@+id/bar"/>
+ </View>)EOF");
+
+ XmlIdCollector collector;
+ ASSERT_TRUE(collector.consume(context.get(), doc.get()));
+
+ EXPECT_EQ(1u, std::count(doc->file.exportedSymbols.begin(), doc->file.exportedSymbols.end(),
+ SourcedResourceName{ test::parseNameOrDie(u"@id/foo"), 3u }));
+
+ EXPECT_EQ(1u, std::count(doc->file.exportedSymbols.begin(), doc->file.exportedSymbols.end(),
+ SourcedResourceName{ test::parseNameOrDie(u"@id/bar"), 3u }));
+
+ EXPECT_EQ(1u, std::count(doc->file.exportedSymbols.begin(), doc->file.exportedSymbols.end(),
+ SourcedResourceName{ test::parseNameOrDie(u"@id/car"), 6u }));
+}
+
+TEST(XmlIdCollectorTest, DontCollectNonIds) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+
+ std::unique_ptr<XmlResource> doc = test::buildXmlDom("<View foo=\"@+string/foo\"/>");
+
+ XmlIdCollector collector;
+ ASSERT_TRUE(collector.consume(context.get(), doc.get()));
+
+ EXPECT_TRUE(doc->file.exportedSymbols.empty());
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/data/AndroidManifest.xml b/tools/aapt2/data/AndroidManifest.xml
index 8533c28..d3b2fbe 100644
--- a/tools/aapt2/data/AndroidManifest.xml
+++ b/tools/aapt2/data/AndroidManifest.xml
@@ -2,6 +2,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.app">
<application
- android:name=".Activity">
+ android:name=".ActivityMain">
</application>
</manifest>
diff --git a/tools/aapt2/data/Makefile b/tools/aapt2/data/Makefile
index 91ff5fe..37012de 100644
--- a/tools/aapt2/data/Makefile
+++ b/tools/aapt2/data/Makefile
@@ -21,63 +21,41 @@
# AAPT2 custom rules.
##
-PRIVATE_APK_UNALIGNED := $(LOCAL_OUT)/package-unaligned.apk
-PRIVATE_APK_ALIGNED := $(LOCAL_OUT)/package.apk
+PRIVATE_R_FILE := $(LOCAL_GEN)/$(subst .,/,$(LOCAL_PACKAGE))/R.java
+$(info PRIVATE_R_FILE = $(PRIVATE_R_FILE))
# Eg: framework.apk, etc.
PRIVATE_INCLUDES := $(FRAMEWORK)
$(info PRIVATE_INCLUDES = $(PRIVATE_INCLUDES))
-# Eg: gen/com/android/app/R.java
-PRIVATE_R_JAVA := $(LOCAL_GEN)/$(subst .,/,$(LOCAL_PACKAGE))/R.java
-$(info PRIVATE_R_JAVA = $(PRIVATE_R_JAVA))
-
# Eg: res/drawable/icon.png, res/values/styles.xml
PRIVATE_RESOURCES := $(shell find $(LOCAL_RESOURCE_DIR) -mindepth 1 -maxdepth 2 -type f)
$(info PRIVATE_RESOURCES = $(PRIVATE_RESOURCES))
-# Eg: drawable, values, layouts
-PRIVATE_RESOURCE_TYPES := \
- $(patsubst $(LOCAL_RESOURCE_DIR)/%/,%,$(sort $(dir $(PRIVATE_RESOURCES))))
-$(info PRIVATE_RESOURCE_TYPES = $(PRIVATE_RESOURCE_TYPES))
+PRIVATE_RESOURCE_OBJECTS := $(subst /,_,$(patsubst $(LOCAL_RESOURCE_DIR)/%,%,$(filter $(LOCAL_RESOURCE_DIR)/values%,$(PRIVATE_RESOURCES))))
+PRIVATE_RESOURCE_OBJECTS := $(addprefix $(LOCAL_OUT)/,$(PRIVATE_RESOURCE_OBJECTS:.xml=.arsc.flat))
+$(info PRIVATE_RESOURCE_OBJECTS = $(PRIVATE_RESOURCE_OBJECTS))
-# Eg: out/values-v4.apk, out/drawable-xhdpi.apk
-PRIVATE_INTERMEDIATE_TABLES := $(patsubst %,$(LOCAL_OUT)/%.apk,$(PRIVATE_RESOURCE_TYPES))
-$(info PRIVATE_INTERMEDIATE_TABLES = $(PRIVATE_INTERMEDIATE_TABLES))
+PRIVATE_FILE_OBJECTS := $(subst /,_,$(patsubst $(LOCAL_RESOURCE_DIR)/%,%,$(filter-out $(LOCAL_RESOURCE_DIR)/values%,$(PRIVATE_RESOURCES))))
+PRIVATE_FILE_OBJECTS := $(addprefix $(LOCAL_OUT)/,$(addsuffix .flat,$(PRIVATE_FILE_OBJECTS)))
+$(info PRIVATE_FILE_OBJECTS = $(PRIVATE_FILE_OBJECTS))
-# Generates rules for collect phase.
-# $1: Resource type (values-v4)
-# returns: out/values-v4.apk: res/values-v4/styles.xml res/values-v4/colors.xml
-define make-collect-rule
-$(LOCAL_OUT)/$1.apk: $(filter $(LOCAL_RESOURCE_DIR)/$1/%,$(PRIVATE_RESOURCES))
- $(AAPT) compile -o $$@ $$^
-endef
+.SECONDEXPANSION:
-# Collect: out/values-v4.apk <- res/values-v4/styles.xml res/values-v4/colors.xml
-$(foreach d,$(PRIVATE_RESOURCE_TYPES),$(eval $(call make-collect-rule,$d)))
+$(LOCAL_OUT)/%.arsc.flat: $(LOCAL_RESOURCE_DIR)/$$(subst _,/,%).xml
+ $(AAPT) compile -o $(LOCAL_OUT) $<
-# Link: out/package-unaligned.apk <- out/values-v4.apk out/drawable-v4.apk
-$(PRIVATE_APK_UNALIGNED): $(PRIVATE_INTERMEDIATE_TABLES) $(PRIVATE_INCLUDES) $(LOCAL_LIBS) AndroidManifest.xml
- $(AAPT) link --manifest AndroidManifest.xml $(addprefix -I ,$(PRIVATE_INCLUDES)) --java $(LOCAL_GEN) -o $@ $(PRIVATE_INTERMEDIATE_TABLES) $(LOCAL_LIBS) --proguard $(LOCAL_PROGUARD) -v
+$(LOCAL_OUT)/%.flat: $(LOCAL_RESOURCE_DIR)/$$(subst _,/,%)
+ $(AAPT) compile -o $(LOCAL_OUT) $<
-# R.java: gen/com/android/app/R.java <- out/resources.arsc
-# No action since R.java is generated when out/resources.arsc is.
-$(PRIVATE_R_JAVA): $(PRIVATE_APK_UNALIGNED)
-
-# Assemble: zip out/resources.arsc AndroidManifest.xml and res/**/*
-$(PRIVATE_APK_ALIGNED): $(PRIVATE_APK_UNALIGNED)
- $(ZIPALIGN) $< $@
+$(LOCAL_PROGUARD) $(LOCAL_OUT)/package.apk: AndroidManifest.xml
+$(PRIVATE_R_FILE) $(LOCAL_PROGUARD) $(LOCAL_OUT)/package.apk: $(PRIVATE_FILE_OBJECTS) $(PRIVATE_RESOURCE_OBJECTS)
+ $(AAPT) link -o $(LOCAL_OUT)/package.apk --manifest AndroidManifest.xml --java $(LOCAL_GEN) --proguard $(LOCAL_PROGUARD) -I $(PRIVATE_INCLUDES) $(filter-out AndroidManifest.xml,$^) -v
# Create the out directory if needed.
dummy := $(shell test -d $(LOCAL_OUT) || mkdir -p $(LOCAL_OUT))
-.PHONY: java
-java: $(PRIVATE_R_JAVA)
-
-.PHONY: assemble
-assemble: $(PRIVATE_APK_ALIGNED)
-
.PHONY: all
-all: assemble java
+all: $(LOCAL_OUT)/package.apk $(LOCAL_PROGUARD) $(PRIVATE_R_FILE)
.DEFAULT_GOAL := all
diff --git a/tools/aapt2/data/res/layout-v21/main.xml b/tools/aapt2/data/res/layout-v21/main.xml
new file mode 100644
index 0000000..959b349
--- /dev/null
+++ b/tools/aapt2/data/res/layout-v21/main.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:support="http://schemas.android.com/apk/res/android.appcompat"
+ android:id="@+id/view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+</LinearLayout>
diff --git a/tools/aapt2/data/res/layout/main.xml b/tools/aapt2/data/res/layout/main.xml
index 50a51d9..8a5e9e8 100644
--- a/tools/aapt2/data/res/layout/main.xml
+++ b/tools/aapt2/data/res/layout/main.xml
@@ -14,8 +14,8 @@
android:layout_width="1dp"
android:onClick="doClick"
android:text="@{user.name}"
+ android:background="#ffffff"
android:layout_height="match_parent"
- app:layout_width="@support:bool/allow"
app:flags="complex|weak"
android:colorAccent="#ffffff"/>
</LinearLayout>
diff --git a/tools/aapt2/data/res/raw/test.txt b/tools/aapt2/data/res/raw/test.txt
new file mode 100644
index 0000000..b14df64
--- /dev/null
+++ b/tools/aapt2/data/res/raw/test.txt
@@ -0,0 +1 @@
+Hi
diff --git a/tools/aapt2/data/res/values/styles.xml b/tools/aapt2/data/res/values/styles.xml
index d0b19a3..2bbdad1 100644
--- a/tools/aapt2/data/res/values/styles.xml
+++ b/tools/aapt2/data/res/values/styles.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:lib="http://schemas.android.com/apk/res/android.appcompat">
- <style name="App" parent="android.appcompat:Platform.AppCompat">
+ <style name="App">
<item name="android:background">@color/primary</item>
<item name="android:colorPrimary">@color/primary</item>
<item name="android:colorPrimaryDark">@color/primary_dark</item>
@@ -8,8 +8,8 @@
</style>
<attr name="custom" format="reference" />
<style name="Pop">
- <item name="custom">@drawable/image</item>
- <item name="android:focusable">@lib:bool/allow</item>
+ <item name="custom">@android:drawable/btn_default</item>
+ <item name="android:focusable">true</item>
</style>
<string name="yo">@string/wow</string>
diff --git a/tools/aapt2/data/res/values/test.xml b/tools/aapt2/data/res/values/test.xml
index d3ead34..d7ab1c8 100644
--- a/tools/aapt2/data/res/values/test.xml
+++ b/tools/aapt2/data/res/values/test.xml
@@ -3,7 +3,7 @@
<string name="hooha"><font bgcolor="#ffffff">Hey guys!</font> <xliff:g>My</xliff:g> name is <b>Adam</b>. How <b><i>are</i></b> you?</string>
<public name="hooha" type="string" id="0x7f020001"/>
<string name="wow">@android:string/ok</string>
- <public name="image" type="drawable" id="0x7f060000" />
+ <public name="layout_width" type="attr" />
<attr name="layout_width" format="boolean" />
<attr name="flags">
<flag name="complex" value="1" />
diff --git a/tools/aapt2/flatten/Archive.cpp b/tools/aapt2/flatten/Archive.cpp
new file mode 100644
index 0000000..6db13b8
--- /dev/null
+++ b/tools/aapt2/flatten/Archive.cpp
@@ -0,0 +1,181 @@
+/*
+ * 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.
+ */
+
+#include "flatten/Archive.h"
+#include "util/Files.h"
+#include "util/StringPiece.h"
+
+#include <fstream>
+#include <memory>
+#include <string>
+#include <vector>
+#include <ziparchive/zip_writer.h>
+
+namespace aapt {
+
+namespace {
+
+struct DirectoryWriter : public IArchiveWriter {
+ std::string mOutDir;
+ std::vector<std::unique_ptr<ArchiveEntry>> mEntries;
+
+ explicit DirectoryWriter(const StringPiece& outDir) : mOutDir(outDir.toString()) {
+ }
+
+ ArchiveEntry* writeEntry(const StringPiece& path, uint32_t flags,
+ const BigBuffer& buffer) override {
+ std::string fullPath = mOutDir;
+ file::appendPath(&fullPath, path);
+ file::mkdirs(file::getStem(fullPath));
+
+ std::ofstream fout(fullPath, std::ofstream::binary);
+ if (!fout) {
+ return nullptr;
+ }
+
+ if (!util::writeAll(fout, buffer)) {
+ return nullptr;
+ }
+
+ mEntries.push_back(util::make_unique<ArchiveEntry>(fullPath, flags, buffer.size()));
+ return mEntries.back().get();
+ }
+
+ ArchiveEntry* writeEntry(const StringPiece& path, uint32_t flags, android::FileMap* fileMap,
+ size_t offset, size_t len) override {
+ std::string fullPath = mOutDir;
+ file::appendPath(&fullPath, path);
+ file::mkdirs(file::getStem(fullPath));
+
+ std::ofstream fout(fullPath, std::ofstream::binary);
+ if (!fout) {
+ return nullptr;
+ }
+
+ if (!fout.write((const char*) fileMap->getDataPtr() + offset, len)) {
+ return nullptr;
+ }
+
+ mEntries.push_back(util::make_unique<ArchiveEntry>(fullPath, flags, len));
+ return mEntries.back().get();
+ }
+
+ virtual ~DirectoryWriter() {
+
+ }
+};
+
+struct ZipFileWriter : public IArchiveWriter {
+ FILE* mFile;
+ std::unique_ptr<ZipWriter> mWriter;
+ std::vector<std::unique_ptr<ArchiveEntry>> mEntries;
+
+ explicit ZipFileWriter(const StringPiece& path) {
+ mFile = fopen(path.data(), "w+b");
+ if (mFile) {
+ mWriter = util::make_unique<ZipWriter>(mFile);
+ }
+ }
+
+ ArchiveEntry* writeEntry(const StringPiece& path, uint32_t flags,
+ const BigBuffer& buffer) override {
+ if (!mWriter) {
+ return nullptr;
+ }
+
+ size_t zipFlags = 0;
+ if (flags & ArchiveEntry::kCompress) {
+ zipFlags |= ZipWriter::kCompress;
+ }
+
+ if (flags & ArchiveEntry::kAlign) {
+ zipFlags |= ZipWriter::kAlign32;
+ }
+
+ int32_t result = mWriter->StartEntry(path.data(), zipFlags);
+ if (result != 0) {
+ return nullptr;
+ }
+
+ for (const BigBuffer::Block& b : buffer) {
+ result = mWriter->WriteBytes(reinterpret_cast<const uint8_t*>(b.buffer.get()), b.size);
+ if (result != 0) {
+ return nullptr;
+ }
+ }
+
+ result = mWriter->FinishEntry();
+ if (result != 0) {
+ return nullptr;
+ }
+
+ mEntries.push_back(util::make_unique<ArchiveEntry>(path.toString(), flags, buffer.size()));
+ return mEntries.back().get();
+ }
+
+ ArchiveEntry* writeEntry(const StringPiece& path, uint32_t flags, android::FileMap* fileMap,
+ size_t offset, size_t len) override {
+ if (!mWriter) {
+ return nullptr;
+ }
+
+ size_t zipFlags = 0;
+ if (flags & ArchiveEntry::kCompress) {
+ zipFlags |= ZipWriter::kCompress;
+ }
+
+ if (flags & ArchiveEntry::kAlign) {
+ zipFlags |= ZipWriter::kAlign32;
+ }
+
+ int32_t result = mWriter->StartEntry(path.data(), zipFlags);
+ if (result != 0) {
+ return nullptr;
+ }
+
+ result = mWriter->WriteBytes((const char*) fileMap->getDataPtr() + offset, len);
+ if (result != 0) {
+ return nullptr;
+ }
+
+ result = mWriter->FinishEntry();
+ if (result != 0) {
+ return nullptr;
+ }
+
+ mEntries.push_back(util::make_unique<ArchiveEntry>(path.toString(), flags, len));
+ return mEntries.back().get();
+ }
+
+ virtual ~ZipFileWriter() {
+ if (mWriter) {
+ mWriter->Finish();
+ fclose(mFile);
+ }
+ }
+};
+
+} // namespace
+
+std::unique_ptr<IArchiveWriter> createDirectoryArchiveWriter(const StringPiece& path) {
+ return util::make_unique<DirectoryWriter>(path);
+}
+
+std::unique_ptr<IArchiveWriter> createZipFileArchiveWriter(const StringPiece& path) {
+ return util::make_unique<ZipFileWriter>(path);
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/flatten/Archive.h b/tools/aapt2/flatten/Archive.h
new file mode 100644
index 0000000..c4ddeb3
--- /dev/null
+++ b/tools/aapt2/flatten/Archive.h
@@ -0,0 +1,57 @@
+/*
+ * 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_FLATTEN_ARCHIVE_H
+#define AAPT_FLATTEN_ARCHIVE_H
+
+#include "util/BigBuffer.h"
+#include "util/Files.h"
+#include "util/StringPiece.h"
+
+#include <fstream>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace aapt {
+
+struct ArchiveEntry {
+ enum : uint32_t {
+ kCompress = 0x01,
+ kAlign = 0x02,
+ };
+
+ std::string path;
+ uint32_t flags;
+ size_t uncompressedSize;
+};
+
+struct IArchiveWriter {
+ virtual ~IArchiveWriter() = default;
+
+ virtual ArchiveEntry* writeEntry(const StringPiece& path, uint32_t flags,
+ const BigBuffer& buffer) = 0;
+ virtual ArchiveEntry* writeEntry(const StringPiece& path, uint32_t flags,
+ android::FileMap* fileMap, size_t offset, size_t len) = 0;
+};
+
+std::unique_ptr<IArchiveWriter> createDirectoryArchiveWriter(const StringPiece& path);
+
+std::unique_ptr<IArchiveWriter> createZipFileArchiveWriter(const StringPiece& path);
+
+} // namespace aapt
+
+#endif /* AAPT_FLATTEN_ARCHIVE_H */
diff --git a/tools/aapt2/flatten/ChunkWriter.h b/tools/aapt2/flatten/ChunkWriter.h
new file mode 100644
index 0000000..de1d87a
--- /dev/null
+++ b/tools/aapt2/flatten/ChunkWriter.h
@@ -0,0 +1,87 @@
+/*
+ * 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_FLATTEN_CHUNKWRITER_H
+#define AAPT_FLATTEN_CHUNKWRITER_H
+
+#include "util/BigBuffer.h"
+#include "util/Util.h"
+
+#include <androidfw/ResourceTypes.h>
+
+namespace aapt {
+
+class ChunkWriter {
+private:
+ BigBuffer* mBuffer;
+ size_t mStartSize = 0;
+ android::ResChunk_header* mHeader = nullptr;
+
+public:
+ explicit inline ChunkWriter(BigBuffer* buffer) : mBuffer(buffer) {
+ }
+
+ ChunkWriter(const ChunkWriter&) = delete;
+ ChunkWriter& operator=(const ChunkWriter&) = delete;
+ ChunkWriter(ChunkWriter&&) = default;
+ ChunkWriter& operator=(ChunkWriter&&) = default;
+
+ template <typename T>
+ inline T* startChunk(uint16_t type) {
+ mStartSize = mBuffer->size();
+ T* chunk = mBuffer->nextBlock<T>();
+ mHeader = &chunk->header;
+ mHeader->type = util::hostToDevice16(type);
+ mHeader->headerSize = util::hostToDevice16(sizeof(T));
+ return chunk;
+ }
+
+ template <typename T>
+ inline T* nextBlock(size_t count = 1) {
+ return mBuffer->nextBlock<T>(count);
+ }
+
+ inline BigBuffer* getBuffer() {
+ return mBuffer;
+ }
+
+ inline android::ResChunk_header* getChunkHeader() {
+ return mHeader;
+ }
+
+ inline size_t size() {
+ return mBuffer->size() - mStartSize;
+ }
+
+ inline android::ResChunk_header* finish() {
+ mBuffer->align4();
+ mHeader->size = util::hostToDevice32(mBuffer->size() - mStartSize);
+ return mHeader;
+ }
+};
+
+template <>
+inline android::ResChunk_header* ChunkWriter::startChunk(uint16_t type) {
+ mStartSize = mBuffer->size();
+ mHeader = mBuffer->nextBlock<android::ResChunk_header>();
+ mHeader->type = util::hostToDevice16(type);
+ mHeader->headerSize = util::hostToDevice16(sizeof(android::ResChunk_header));
+ return mHeader;
+}
+
+} // namespace aapt
+
+#endif /* AAPT_FLATTEN_CHUNKWRITER_H */
diff --git a/tools/aapt2/flatten/FileExportWriter.h b/tools/aapt2/flatten/FileExportWriter.h
new file mode 100644
index 0000000..7688fa7
--- /dev/null
+++ b/tools/aapt2/flatten/FileExportWriter.h
@@ -0,0 +1,67 @@
+/*
+ * 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_FLATTEN_FILEEXPORTWRITER_H
+#define AAPT_FLATTEN_FILEEXPORTWRITER_H
+
+#include "StringPool.h"
+
+#include "flatten/ResourceTypeExtensions.h"
+#include "flatten/ChunkWriter.h"
+#include "process/IResourceTableConsumer.h"
+#include "util/BigBuffer.h"
+#include "util/Util.h"
+
+#include <androidfw/ResourceTypes.h>
+#include <utils/misc.h>
+
+namespace aapt {
+
+static ChunkWriter wrapBufferWithFileExportHeader(BigBuffer* buffer, ResourceFile* res) {
+ ChunkWriter fileExportWriter(buffer);
+ FileExport_header* fileExport = fileExportWriter.startChunk<FileExport_header>(
+ RES_FILE_EXPORT_TYPE);
+
+ ExportedSymbol* symbolRefs = nullptr;
+ if (!res->exportedSymbols.empty()) {
+ symbolRefs = fileExportWriter.nextBlock<ExportedSymbol>(
+ res->exportedSymbols.size());
+ }
+ fileExport->exportedSymbolCount = util::hostToDevice32(res->exportedSymbols.size());
+
+ StringPool symbolExportPool;
+ memcpy(fileExport->magic, "AAPT", NELEM(fileExport->magic));
+ fileExport->config = res->config;
+ fileExport->config.swapHtoD();
+ fileExport->name.index = util::hostToDevice32(symbolExportPool.makeRef(res->name.toString())
+ .getIndex());
+ fileExport->source.index = util::hostToDevice32(symbolExportPool.makeRef(util::utf8ToUtf16(
+ res->source.path)).getIndex());
+
+ for (const SourcedResourceName& name : res->exportedSymbols) {
+ symbolRefs->name.index = util::hostToDevice32(symbolExportPool.makeRef(name.name.toString())
+ .getIndex());
+ symbolRefs->line = util::hostToDevice32(name.line);
+ symbolRefs++;
+ }
+
+ StringPool::flattenUtf16(fileExportWriter.getBuffer(), symbolExportPool);
+ return fileExportWriter;
+}
+
+} // namespace aapt
+
+#endif /* AAPT_FLATTEN_FILEEXPORTWRITER_H */
diff --git a/tools/aapt2/flatten/FileExportWriter_test.cpp b/tools/aapt2/flatten/FileExportWriter_test.cpp
new file mode 100644
index 0000000..32fc203
--- /dev/null
+++ b/tools/aapt2/flatten/FileExportWriter_test.cpp
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+#include "Resource.h"
+
+#include "flatten/FileExportWriter.h"
+#include "util/BigBuffer.h"
+#include "util/Util.h"
+
+#include "test/Common.h"
+
+#include <gtest/gtest.h>
+
+namespace aapt {
+
+TEST(FileExportWriterTest, FlattenResourceFileDataWithNoExports) {
+ ResourceFile resFile = {
+ test::parseNameOrDie(u"@android:layout/main.xml"),
+ test::parseConfigOrDie("sw600dp-v4"),
+ Source{ "res/layout/main.xml" },
+ };
+
+ BigBuffer buffer(1024);
+ ChunkWriter writer = wrapBufferWithFileExportHeader(&buffer, &resFile);
+ *writer.getBuffer()->nextBlock<uint32_t>() = 42u;
+ writer.finish();
+
+ std::unique_ptr<uint8_t[]> data = util::copy(buffer);
+
+ // There should be more data (string pool) besides the header and our data.
+ ASSERT_GT(buffer.size(), sizeof(FileExport_header) + sizeof(uint32_t));
+
+ // Write at the end of this chunk is our data.
+ uint32_t* val = (uint32_t*)(data.get() + buffer.size()) - 1;
+ EXPECT_EQ(*val, 42u);
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/flatten/ResourceTypeExtensions.h b/tools/aapt2/flatten/ResourceTypeExtensions.h
new file mode 100644
index 0000000..c1ff556
--- /dev/null
+++ b/tools/aapt2/flatten/ResourceTypeExtensions.h
@@ -0,0 +1,216 @@
+/*
+ * 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_RESOURCE_TYPE_EXTENSIONS_H
+#define AAPT_RESOURCE_TYPE_EXTENSIONS_H
+
+#include <androidfw/ResourceTypes.h>
+
+namespace aapt {
+
+/**
+ * New android::ResChunk_header types defined
+ * for AAPT to use.
+ *
+ * TODO(adamlesinski): Consider reserving these
+ * enums in androidfw/ResourceTypes.h to avoid
+ * future collisions.
+ */
+enum {
+ /**
+ * A chunk that contains an entire file that
+ * has been compiled.
+ */
+ RES_FILE_EXPORT_TYPE = 0x000c,
+
+ RES_TABLE_PUBLIC_TYPE = 0x000d,
+
+ /**
+ * A chunk that holds the string pool
+ * for source entries (path/to/source:line).
+ */
+ RES_TABLE_SOURCE_POOL_TYPE = 0x000e,
+
+ /**
+ * A chunk holding names of externally
+ * defined symbols and offsets to where
+ * they are referenced in the table.
+ */
+ RES_TABLE_SYMBOL_TABLE_TYPE = 0x000f,
+};
+
+/**
+ * New resource types that are meant to only be used
+ * by AAPT and will not end up on the device.
+ */
+struct ExtendedTypes {
+ enum {
+ /**
+ * A raw string value that hasn't had its escape sequences
+ * processed nor whitespace removed.
+ */
+ TYPE_RAW_STRING = 0xfe
+ };
+};
+
+/**
+ * Followed by exportedSymbolCount ExportedSymbol structs, followed by the string pool.
+ */
+struct FileExport_header {
+ android::ResChunk_header header;
+
+ /**
+ * MAGIC value. Must be 'AAPT' (0x41415054)
+ */
+ uint8_t magic[4];
+
+ /**
+ * Version of AAPT that built this file.
+ */
+ uint32_t version;
+
+ /**
+ * The resource name.
+ */
+ android::ResStringPool_ref name;
+
+ /**
+ * Configuration of this file.
+ */
+ android::ResTable_config config;
+
+ /**
+ * Original source path of this file.
+ */
+ android::ResStringPool_ref source;
+
+ /**
+ * Number of symbols exported by this file.
+ */
+ uint32_t exportedSymbolCount;
+};
+
+struct ExportedSymbol {
+ android::ResStringPool_ref name;
+ uint32_t line;
+};
+
+struct Public_header {
+ android::ResChunk_header header;
+
+ /**
+ * The ID of the type this structure refers to.
+ */
+ uint8_t typeId;
+
+ /**
+ * Reserved. Must be 0.
+ */
+ uint8_t res0;
+
+ /**
+ * Reserved. Must be 0.
+ */
+ uint16_t res1;
+
+ /**
+ * Number of public entries.
+ */
+ uint32_t count;
+};
+
+/**
+ * A structure representing source data for a resource entry.
+ * Appears after an android::ResTable_entry or android::ResTable_map_entry.
+ *
+ * TODO(adamlesinski): This causes some issues when runtime code checks
+ * the size of an android::ResTable_entry. It assumes it is an
+ * android::ResTable_map_entry if the size is bigger than an android::ResTable_entry
+ * which may not be true if this structure is present.
+ */
+struct ResTable_entry_source {
+ /**
+ * File path reference.
+ */
+ android::ResStringPool_ref path;
+
+ /**
+ * Line number this resource was defined on.
+ */
+ uint32_t line;
+
+ /**
+ * Comment string reference.
+ */
+ android::ResStringPool_ref comment;
+};
+
+struct Public_entry {
+ uint16_t entryId;
+
+ enum : uint16_t {
+ kUndefined = 0,
+ kPublic = 1,
+ kPrivate = 2,
+ };
+
+ uint16_t state;
+ android::ResStringPool_ref key;
+ ResTable_entry_source source;
+};
+
+/**
+ * A chunk with type RES_TABLE_SYMBOL_TABLE_TYPE.
+ * Following the header are count number of SymbolTable_entry
+ * structures, followed by an android::ResStringPool_header.
+ */
+struct SymbolTable_header {
+ android::ResChunk_header header;
+
+ /**
+ * Number of SymbolTable_entry structures following
+ * this header.
+ */
+ uint32_t count;
+};
+
+struct SymbolTable_entry {
+ /**
+ * Offset from the beginning of the resource table
+ * where the symbol entry is referenced.
+ */
+ uint32_t offset;
+
+ /**
+ * The index into the string pool where the name of this
+ * symbol exists.
+ */
+ android::ResStringPool_ref name;
+};
+
+/**
+ * An alternative struct to use instead of ResTable_map_entry. This one is a standard_layout
+ * struct.
+ */
+struct ResTable_entry_ext {
+ android::ResTable_entry entry;
+ android::ResTable_ref parent;
+ uint32_t count;
+};
+
+} // namespace aapt
+
+#endif // AAPT_RESOURCE_TYPE_EXTENSIONS_H
diff --git a/tools/aapt2/flatten/TableFlattener.cpp b/tools/aapt2/flatten/TableFlattener.cpp
new file mode 100644
index 0000000..6b90fb2
--- /dev/null
+++ b/tools/aapt2/flatten/TableFlattener.cpp
@@ -0,0 +1,709 @@
+/*
+ * 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.
+ */
+
+#include "ResourceTable.h"
+#include "ResourceValues.h"
+#include "ValueVisitor.h"
+
+#include "flatten/ChunkWriter.h"
+#include "flatten/ResourceTypeExtensions.h"
+#include "flatten/TableFlattener.h"
+#include "util/BigBuffer.h"
+
+#include <base/macros.h>
+#include <type_traits>
+#include <numeric>
+
+using namespace android;
+
+namespace aapt {
+
+namespace {
+
+template <typename T>
+static bool cmpIds(const T* a, const T* b) {
+ return a->id.value() < b->id.value();
+}
+
+static void strcpy16_htod(uint16_t* dst, size_t len, const StringPiece16& src) {
+ if (len == 0) {
+ return;
+ }
+
+ size_t i;
+ const char16_t* srcData = src.data();
+ for (i = 0; i < len - 1 && i < src.size(); i++) {
+ dst[i] = util::hostToDevice16((uint16_t) srcData[i]);
+ }
+ dst[i] = 0;
+}
+
+struct FlatEntry {
+ ResourceEntry* entry;
+ Value* value;
+
+ // The entry string pool index to the entry's name.
+ uint32_t entryKey;
+
+ // The source string pool index to the source file path.
+ uint32_t sourcePathKey;
+ uint32_t sourceLine;
+
+ // The source string pool index to the comment.
+ uint32_t commentKey;
+};
+
+class SymbolWriter {
+public:
+ struct Entry {
+ StringPool::Ref name;
+ size_t offset;
+ };
+
+ std::vector<Entry> symbols;
+
+ explicit SymbolWriter(StringPool* pool) : mPool(pool) {
+ }
+
+ void addSymbol(const ResourceNameRef& name, size_t offset) {
+ symbols.push_back(Entry{ mPool->makeRef(name.package.toString() + u":" +
+ toString(name.type).toString() + u"/" +
+ name.entry.toString()), offset });
+ }
+
+ void shiftAllOffsets(size_t offset) {
+ for (Entry& entry : symbols) {
+ entry.offset += offset;
+ }
+ }
+
+private:
+ StringPool* mPool;
+};
+
+struct MapFlattenVisitor : public RawValueVisitor {
+ using RawValueVisitor::visit;
+
+ SymbolWriter* mSymbols;
+ FlatEntry* mEntry;
+ BigBuffer* mBuffer;
+ size_t mEntryCount = 0;
+ Maybe<uint32_t> mParentIdent;
+ Maybe<ResourceNameRef> mParentName;
+
+ MapFlattenVisitor(SymbolWriter* symbols, FlatEntry* entry, BigBuffer* buffer) :
+ mSymbols(symbols), mEntry(entry), mBuffer(buffer) {
+ }
+
+ void flattenKey(Reference* key, ResTable_map* outEntry) {
+ if (!key->id) {
+ assert(key->name && "reference must have a name");
+
+ outEntry->name.ident = util::hostToDevice32(0);
+ mSymbols->addSymbol(key->name.value(), (mBuffer->size() - sizeof(ResTable_map)) +
+ offsetof(ResTable_map, name));
+ } else {
+ outEntry->name.ident = util::hostToDevice32(key->id.value().id);
+ }
+ }
+
+ void flattenValue(Item* value, ResTable_map* outEntry) {
+ if (Reference* ref = valueCast<Reference>(value)) {
+ if (!ref->id) {
+ assert(ref->name && "reference must have a name");
+
+ mSymbols->addSymbol(ref->name.value(), (mBuffer->size() - sizeof(ResTable_map)) +
+ offsetof(ResTable_map, value) + offsetof(Res_value, data));
+ }
+ }
+
+ bool result = value->flatten(&outEntry->value);
+ assert(result && "flatten failed");
+ }
+
+ void flattenEntry(Reference* key, Item* value) {
+ ResTable_map* outEntry = mBuffer->nextBlock<ResTable_map>();
+ flattenKey(key, outEntry);
+ flattenValue(value, outEntry);
+ outEntry->value.size = util::hostToDevice16(sizeof(outEntry->value));
+ mEntryCount++;
+ }
+
+ void visit(Attribute* attr) override {
+ {
+ Reference key(ResourceId{ ResTable_map::ATTR_TYPE });
+ BinaryPrimitive val(Res_value::TYPE_INT_DEC, attr->typeMask);
+ flattenEntry(&key, &val);
+ }
+
+ for (Attribute::Symbol& s : attr->symbols) {
+ BinaryPrimitive val(Res_value::TYPE_INT_DEC, s.value);
+ flattenEntry(&s.symbol, &val);
+ }
+ }
+
+ static bool cmpStyleEntries(const Style::Entry& a, const Style::Entry& b) {
+ if (a.key.id) {
+ if (b.key.id) {
+ return a.key.id.value() < b.key.id.value();
+ }
+ return true;
+ } else if (!b.key.id) {
+ return a.key.name.value() < b.key.name.value();
+ }
+ return false;
+ }
+
+ void visit(Style* style) override {
+ if (style->parent) {
+ if (!style->parent.value().id) {
+ assert(style->parent.value().name && "reference must have a name");
+ mParentName = style->parent.value().name;
+ } else {
+ mParentIdent = style->parent.value().id.value().id;
+ }
+ }
+
+ // Sort the style.
+ std::sort(style->entries.begin(), style->entries.end(), cmpStyleEntries);
+
+ for (Style::Entry& entry : style->entries) {
+ flattenEntry(&entry.key, entry.value.get());
+ }
+ }
+
+ void visit(Styleable* styleable) override {
+ for (auto& attrRef : styleable->entries) {
+ BinaryPrimitive val(Res_value{});
+ flattenEntry(&attrRef, &val);
+ }
+ }
+
+ void visit(Array* array) override {
+ for (auto& item : array->items) {
+ ResTable_map* outEntry = mBuffer->nextBlock<ResTable_map>();
+ flattenValue(item.get(), outEntry);
+ outEntry->value.size = util::hostToDevice16(sizeof(outEntry->value));
+ mEntryCount++;
+ }
+ }
+
+ void visit(Plural* plural) override {
+ const size_t count = plural->values.size();
+ for (size_t i = 0; i < count; i++) {
+ if (!plural->values[i]) {
+ continue;
+ }
+
+ ResourceId q;
+ switch (i) {
+ case Plural::Zero:
+ q.id = android::ResTable_map::ATTR_ZERO;
+ break;
+
+ case Plural::One:
+ q.id = android::ResTable_map::ATTR_ONE;
+ break;
+
+ case Plural::Two:
+ q.id = android::ResTable_map::ATTR_TWO;
+ break;
+
+ case Plural::Few:
+ q.id = android::ResTable_map::ATTR_FEW;
+ break;
+
+ case Plural::Many:
+ q.id = android::ResTable_map::ATTR_MANY;
+ break;
+
+ case Plural::Other:
+ q.id = android::ResTable_map::ATTR_OTHER;
+ break;
+
+ default:
+ assert(false);
+ break;
+ }
+
+ Reference key(q);
+ flattenEntry(&key, plural->values[i].get());
+ }
+ }
+};
+
+class PackageFlattener {
+public:
+ PackageFlattener(IDiagnostics* diag, TableFlattenerOptions options,
+ ResourceTablePackage* package, SymbolWriter* symbolWriter,
+ StringPool* sourcePool) :
+ mDiag(diag), mOptions(options), mPackage(package), mSymbols(symbolWriter),
+ mSourcePool(sourcePool) {
+ }
+
+ bool flattenPackage(BigBuffer* buffer) {
+ ChunkWriter pkgWriter(buffer);
+ ResTable_package* pkgHeader = pkgWriter.startChunk<ResTable_package>(
+ RES_TABLE_PACKAGE_TYPE);
+ pkgHeader->id = util::hostToDevice32(mPackage->id.value());
+
+ if (mPackage->name.size() >= arraysize(pkgHeader->name)) {
+ mDiag->error(DiagMessage() <<
+ "package name '" << mPackage->name << "' is too long");
+ return false;
+ }
+
+ // Copy the package name in device endianness.
+ strcpy16_htod(pkgHeader->name, arraysize(pkgHeader->name), mPackage->name);
+
+ // Serialize the types. We do this now so that our type and key strings
+ // are populated. We write those first.
+ BigBuffer typeBuffer(1024);
+ flattenTypes(&typeBuffer);
+
+ pkgHeader->typeStrings = util::hostToDevice32(pkgWriter.size());
+ StringPool::flattenUtf16(pkgWriter.getBuffer(), mTypePool);
+
+ pkgHeader->keyStrings = util::hostToDevice32(pkgWriter.size());
+ StringPool::flattenUtf16(pkgWriter.getBuffer(), mKeyPool);
+
+ // Add the ResTable_package header/type/key strings to the offset.
+ mSymbols->shiftAllOffsets(pkgWriter.size());
+
+ // Append the types.
+ buffer->appendBuffer(std::move(typeBuffer));
+
+ pkgWriter.finish();
+ return true;
+ }
+
+private:
+ IDiagnostics* mDiag;
+ TableFlattenerOptions mOptions;
+ ResourceTablePackage* mPackage;
+ StringPool mTypePool;
+ StringPool mKeyPool;
+ SymbolWriter* mSymbols;
+ StringPool* mSourcePool;
+
+ template <typename T, bool IsItem>
+ T* writeEntry(FlatEntry* entry, BigBuffer* buffer) {
+ static_assert(std::is_same<ResTable_entry, T>::value ||
+ std::is_same<ResTable_entry_ext, T>::value,
+ "T must be ResTable_entry or ResTable_entry_ext");
+
+ T* result = buffer->nextBlock<T>();
+ ResTable_entry* outEntry = (ResTable_entry*)(result);
+ if (entry->entry->symbolStatus.state == SymbolState::kPublic) {
+ outEntry->flags |= ResTable_entry::FLAG_PUBLIC;
+ }
+
+ if (entry->value->isWeak()) {
+ outEntry->flags |= ResTable_entry::FLAG_WEAK;
+ }
+
+ if (!IsItem) {
+ outEntry->flags |= ResTable_entry::FLAG_COMPLEX;
+ }
+
+ outEntry->key.index = util::hostToDevice32(entry->entryKey);
+ outEntry->size = sizeof(T);
+
+ if (mOptions.useExtendedChunks) {
+ // Write the extra source block. This will be ignored by the Android runtime.
+ ResTable_entry_source* sourceBlock = buffer->nextBlock<ResTable_entry_source>();
+ sourceBlock->path.index = util::hostToDevice32(entry->sourcePathKey);
+ sourceBlock->line = util::hostToDevice32(entry->sourceLine);
+ sourceBlock->comment.index = util::hostToDevice32(entry->commentKey);
+ outEntry->size += sizeof(*sourceBlock);
+ }
+
+ outEntry->flags = util::hostToDevice16(outEntry->flags);
+ outEntry->size = util::hostToDevice16(outEntry->size);
+ return result;
+ }
+
+ bool flattenValue(FlatEntry* entry, BigBuffer* buffer) {
+ if (Item* item = valueCast<Item>(entry->value)) {
+ writeEntry<ResTable_entry, true>(entry, buffer);
+ if (Reference* ref = valueCast<Reference>(entry->value)) {
+ if (!ref->id) {
+ assert(ref->name && "reference must have at least a name");
+ mSymbols->addSymbol(ref->name.value(),
+ buffer->size() + offsetof(Res_value, data));
+ }
+ }
+ Res_value* outValue = buffer->nextBlock<Res_value>();
+ bool result = item->flatten(outValue);
+ assert(result && "flatten failed");
+ outValue->size = util::hostToDevice16(sizeof(*outValue));
+ } else {
+ const size_t beforeEntry = buffer->size();
+ ResTable_entry_ext* outEntry = writeEntry<ResTable_entry_ext, false>(entry, buffer);
+ MapFlattenVisitor visitor(mSymbols, entry, buffer);
+ entry->value->accept(&visitor);
+ outEntry->count = util::hostToDevice32(visitor.mEntryCount);
+ if (visitor.mParentName) {
+ mSymbols->addSymbol(visitor.mParentName.value(),
+ beforeEntry + offsetof(ResTable_entry_ext, parent));
+ } else if (visitor.mParentIdent) {
+ outEntry->parent.ident = util::hostToDevice32(visitor.mParentIdent.value());
+ }
+ }
+ return true;
+ }
+
+ bool flattenConfig(const ResourceTableType* type, const ConfigDescription& config,
+ std::vector<FlatEntry>* entries, BigBuffer* buffer) {
+ ChunkWriter typeWriter(buffer);
+ ResTable_type* typeHeader = typeWriter.startChunk<ResTable_type>(RES_TABLE_TYPE_TYPE);
+ typeHeader->id = type->id.value();
+ typeHeader->config = config;
+ typeHeader->config.swapHtoD();
+
+ auto maxAccum = [](uint32_t max, const std::unique_ptr<ResourceEntry>& a) -> uint32_t {
+ return std::max(max, (uint32_t) a->id.value());
+ };
+
+ // Find the largest entry ID. That is how many entries we will have.
+ const uint32_t entryCount =
+ std::accumulate(type->entries.begin(), type->entries.end(), 0, maxAccum) + 1;
+
+ typeHeader->entryCount = util::hostToDevice32(entryCount);
+ uint32_t* indices = typeWriter.nextBlock<uint32_t>(entryCount);
+
+ assert((size_t) entryCount <= std::numeric_limits<uint16_t>::max() + 1);
+ memset(indices, 0xff, entryCount * sizeof(uint32_t));
+
+ typeHeader->entriesStart = util::hostToDevice32(typeWriter.size());
+
+ const size_t entryStart = typeWriter.getBuffer()->size();
+ for (FlatEntry& flatEntry : *entries) {
+ assert(flatEntry.entry->id.value() < entryCount);
+ indices[flatEntry.entry->id.value()] = util::hostToDevice32(
+ typeWriter.getBuffer()->size() - entryStart);
+ if (!flattenValue(&flatEntry, typeWriter.getBuffer())) {
+ mDiag->error(DiagMessage()
+ << "failed to flatten resource '"
+ << ResourceNameRef(mPackage->name, type->type, flatEntry.entry->name)
+ << "' for configuration '" << config << "'");
+ return false;
+ }
+ }
+ typeWriter.finish();
+ return true;
+ }
+
+ std::vector<ResourceTableType*> collectAndSortTypes() {
+ std::vector<ResourceTableType*> sortedTypes;
+ for (auto& type : mPackage->types) {
+ if (type->type == ResourceType::kStyleable && !mOptions.useExtendedChunks) {
+ // Styleables aren't real Resource Types, they are represented in the R.java
+ // file.
+ continue;
+ }
+
+ assert(type->id && "type must have an ID set");
+
+ sortedTypes.push_back(type.get());
+ }
+ std::sort(sortedTypes.begin(), sortedTypes.end(), cmpIds<ResourceTableType>);
+ return sortedTypes;
+ }
+
+ std::vector<ResourceEntry*> collectAndSortEntries(ResourceTableType* type) {
+ // Sort the entries by entry ID.
+ std::vector<ResourceEntry*> sortedEntries;
+ for (auto& entry : type->entries) {
+ assert(entry->id && "entry must have an ID set");
+ sortedEntries.push_back(entry.get());
+ }
+ std::sort(sortedEntries.begin(), sortedEntries.end(), cmpIds<ResourceEntry>);
+ return sortedEntries;
+ }
+
+ bool flattenTypeSpec(ResourceTableType* type, std::vector<ResourceEntry*>* sortedEntries,
+ BigBuffer* buffer) {
+ ChunkWriter typeSpecWriter(buffer);
+ ResTable_typeSpec* specHeader = typeSpecWriter.startChunk<ResTable_typeSpec>(
+ RES_TABLE_TYPE_SPEC_TYPE);
+ specHeader->id = type->id.value();
+
+ if (sortedEntries->empty()) {
+ typeSpecWriter.finish();
+ return true;
+ }
+
+ // We can't just take the size of the vector. There may be holes in the entry ID space.
+ // Since the entries are sorted by ID, the last one will be the biggest.
+ const size_t numEntries = sortedEntries->back()->id.value() + 1;
+
+ specHeader->entryCount = util::hostToDevice32(numEntries);
+
+ // Reserve space for the masks of each resource in this type. These
+ // show for which configuration axis the resource changes.
+ uint32_t* configMasks = typeSpecWriter.nextBlock<uint32_t>(numEntries);
+
+ const size_t actualNumEntries = sortedEntries->size();
+ for (size_t entryIndex = 0; entryIndex < actualNumEntries; entryIndex++) {
+ ResourceEntry* entry = sortedEntries->at(entryIndex);
+
+ // Populate the config masks for this entry.
+
+ if (entry->symbolStatus.state == SymbolState::kPublic) {
+ configMasks[entry->id.value()] |=
+ util::hostToDevice32(ResTable_typeSpec::SPEC_PUBLIC);
+ }
+
+ const size_t configCount = entry->values.size();
+ for (size_t i = 0; i < configCount; i++) {
+ const ConfigDescription& config = entry->values[i].config;
+ for (size_t j = i + 1; j < configCount; j++) {
+ configMasks[entry->id.value()] |= util::hostToDevice32(
+ config.diff(entry->values[j].config));
+ }
+ }
+ }
+ typeSpecWriter.finish();
+ return true;
+ }
+
+ bool flattenPublic(ResourceTableType* type, std::vector<ResourceEntry*>* sortedEntries,
+ BigBuffer* buffer) {
+ ChunkWriter publicWriter(buffer);
+ Public_header* publicHeader = publicWriter.startChunk<Public_header>(RES_TABLE_PUBLIC_TYPE);
+ publicHeader->typeId = type->id.value();
+
+ for (ResourceEntry* entry : *sortedEntries) {
+ if (entry->symbolStatus.state != SymbolState::kUndefined) {
+ // Write the public status of this entry.
+ Public_entry* publicEntry = publicWriter.nextBlock<Public_entry>();
+ publicEntry->entryId = util::hostToDevice32(entry->id.value());
+ publicEntry->key.index = util::hostToDevice32(mKeyPool.makeRef(
+ entry->name).getIndex());
+ publicEntry->source.path.index = util::hostToDevice32(mSourcePool->makeRef(
+ util::utf8ToUtf16(entry->symbolStatus.source.path)).getIndex());
+ if (entry->symbolStatus.source.line) {
+ publicEntry->source.line = util::hostToDevice32(
+ entry->symbolStatus.source.line.value());
+ }
+ publicEntry->source.comment.index = util::hostToDevice32(mSourcePool->makeRef(
+ entry->symbolStatus.comment).getIndex());
+
+ switch (entry->symbolStatus.state) {
+ case SymbolState::kPrivate:
+ publicEntry->state = Public_entry::kPrivate;
+ break;
+
+ case SymbolState::kPublic:
+ publicEntry->state = Public_entry::kPublic;
+ break;
+
+ default:
+ assert(false && "should not serialize any other state");
+ break;
+ }
+
+ // Don't hostToDevice until the last step.
+ publicHeader->count += 1;
+ }
+ }
+
+ publicHeader->count = util::hostToDevice32(publicHeader->count);
+ publicWriter.finish();
+ return true;
+ }
+
+ bool flattenTypes(BigBuffer* buffer) {
+ // Sort the types by their IDs. They will be inserted into the StringPool in this order.
+ std::vector<ResourceTableType*> sortedTypes = collectAndSortTypes();
+
+ size_t expectedTypeId = 1;
+ for (ResourceTableType* type : sortedTypes) {
+ // If there is a gap in the type IDs, fill in the StringPool
+ // with empty values until we reach the ID we expect.
+ while (type->id.value() > expectedTypeId) {
+ std::u16string typeName(u"?");
+ typeName += expectedTypeId;
+ mTypePool.makeRef(typeName);
+ expectedTypeId++;
+ }
+ expectedTypeId++;
+ mTypePool.makeRef(toString(type->type));
+
+ std::vector<ResourceEntry*> sortedEntries = collectAndSortEntries(type);
+
+ if (!flattenTypeSpec(type, &sortedEntries, buffer)) {
+ return false;
+ }
+
+ if (mOptions.useExtendedChunks) {
+ if (!flattenPublic(type, &sortedEntries, buffer)) {
+ return false;
+ }
+ }
+
+ // The binary resource table lists resource entries for each configuration.
+ // We store them inverted, where a resource entry lists the values for each
+ // configuration available. Here we reverse this to match the binary table.
+ std::map<ConfigDescription, std::vector<FlatEntry>> configToEntryListMap;
+ for (ResourceEntry* entry : sortedEntries) {
+ const uint32_t keyIndex = (uint32_t) mKeyPool.makeRef(entry->name).getIndex();
+
+ // Group values by configuration.
+ for (auto& configValue : entry->values) {
+ Value* value = configValue.value.get();
+
+ const StringPool::Ref sourceRef = mSourcePool->makeRef(
+ util::utf8ToUtf16(value->getSource().path));
+
+ uint32_t lineNumber = 0;
+ if (value->getSource().line) {
+ lineNumber = value->getSource().line.value();
+ }
+
+ const StringPool::Ref commentRef = mSourcePool->makeRef(value->getComment());
+
+ configToEntryListMap[configValue.config]
+ .push_back(FlatEntry{
+ entry,
+ value,
+ keyIndex,
+ (uint32_t) sourceRef.getIndex(),
+ lineNumber,
+ (uint32_t) commentRef.getIndex() });
+ }
+ }
+
+ // Flatten a configuration value.
+ for (auto& entry : configToEntryListMap) {
+ if (!flattenConfig(type, entry.first, &entry.second, buffer)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+};
+
+} // namespace
+
+bool TableFlattener::consume(IAaptContext* context, ResourceTable* table) {
+ // We must do this before writing the resources, since the string pool IDs may change.
+ table->stringPool.sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
+ int diff = a.context.priority - b.context.priority;
+ if (diff < 0) return true;
+ if (diff > 0) return false;
+ diff = a.context.config.compare(b.context.config);
+ if (diff < 0) return true;
+ if (diff > 0) return false;
+ return a.value < b.value;
+ });
+ table->stringPool.prune();
+
+ // Write the ResTable header.
+ ChunkWriter tableWriter(mBuffer);
+ ResTable_header* tableHeader = tableWriter.startChunk<ResTable_header>(RES_TABLE_TYPE);
+ tableHeader->packageCount = util::hostToDevice32(table->packages.size());
+
+ // Flatten the values string pool.
+ StringPool::flattenUtf8(tableWriter.getBuffer(), table->stringPool);
+
+ // If we have a reference to a symbol that doesn't exist, we don't know its resource ID.
+ // We encode the name of the symbol along with the offset of where to include the resource ID
+ // once it is found.
+ StringPool symbolPool;
+ std::vector<SymbolWriter::Entry> symbolOffsets;
+
+ // String pool holding the source paths of each value.
+ StringPool sourcePool;
+
+ BigBuffer packageBuffer(1024);
+
+ // Flatten each package.
+ for (auto& package : table->packages) {
+ const size_t beforePackageSize = packageBuffer.size();
+
+ // All packages will share a single global symbol pool.
+ SymbolWriter packageSymbolWriter(&symbolPool);
+
+ PackageFlattener flattener(context->getDiagnostics(), mOptions, package.get(),
+ &packageSymbolWriter, &sourcePool);
+ if (!flattener.flattenPackage(&packageBuffer)) {
+ return false;
+ }
+
+ // The symbols are offset only from their own Package start. Offset them from the
+ // start of the packageBuffer.
+ packageSymbolWriter.shiftAllOffsets(beforePackageSize);
+
+ // Extract all the symbols to offset
+ symbolOffsets.insert(symbolOffsets.end(),
+ std::make_move_iterator(packageSymbolWriter.symbols.begin()),
+ std::make_move_iterator(packageSymbolWriter.symbols.end()));
+ }
+
+ SymbolTable_entry* symbolEntryData = nullptr;
+ if (mOptions.useExtendedChunks) {
+ if (!symbolOffsets.empty()) {
+ // Sort the offsets so we can scan them linearly.
+ std::sort(symbolOffsets.begin(), symbolOffsets.end(),
+ [](const SymbolWriter::Entry& a, const SymbolWriter::Entry& b) -> bool {
+ return a.offset < b.offset;
+ });
+
+ // Write the Symbol header.
+ ChunkWriter symbolWriter(tableWriter.getBuffer());
+ SymbolTable_header* symbolHeader = symbolWriter.startChunk<SymbolTable_header>(
+ RES_TABLE_SYMBOL_TABLE_TYPE);
+ symbolHeader->count = util::hostToDevice32(symbolOffsets.size());
+
+ symbolEntryData = symbolWriter.nextBlock<SymbolTable_entry>(symbolOffsets.size());
+ StringPool::flattenUtf8(symbolWriter.getBuffer(), symbolPool);
+ symbolWriter.finish();
+ }
+
+ if (sourcePool.size() > 0) {
+ // Write out source pool.
+ ChunkWriter srcWriter(tableWriter.getBuffer());
+ srcWriter.startChunk<ResChunk_header>(RES_TABLE_SOURCE_POOL_TYPE);
+ StringPool::flattenUtf8(srcWriter.getBuffer(), sourcePool);
+ srcWriter.finish();
+ }
+ }
+
+ const size_t beforePackagesSize = tableWriter.size();
+
+ // Finally merge all the packages into the main buffer.
+ tableWriter.getBuffer()->appendBuffer(std::move(packageBuffer));
+
+ // Update the offsets to their final values.
+ if (symbolEntryData) {
+ for (SymbolWriter::Entry& entry : symbolOffsets) {
+ symbolEntryData->name.index = util::hostToDevice32(entry.name.getIndex());
+
+ // The symbols were all calculated with the packageBuffer offset. We need to
+ // add the beginning of the output buffer.
+ symbolEntryData->offset = util::hostToDevice32(entry.offset + beforePackagesSize);
+ symbolEntryData++;
+ }
+ }
+
+ tableWriter.finish();
+ return true;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/flatten/TableFlattener.h b/tools/aapt2/flatten/TableFlattener.h
new file mode 100644
index 0000000..901b129
--- /dev/null
+++ b/tools/aapt2/flatten/TableFlattener.h
@@ -0,0 +1,53 @@
+/*
+ * 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_FLATTEN_TABLEFLATTENER_H
+#define AAPT_FLATTEN_TABLEFLATTENER_H
+
+#include "process/IResourceTableConsumer.h"
+
+namespace aapt {
+
+class BigBuffer;
+class ResourceTable;
+
+struct TableFlattenerOptions {
+ /**
+ * Specifies whether to output extended chunks, like
+ * source information and missing symbol entries. Default
+ * is false.
+ *
+ * Set this to true when emitting intermediate resource table.
+ */
+ bool useExtendedChunks = false;
+};
+
+class TableFlattener : public IResourceTableConsumer {
+public:
+ TableFlattener(BigBuffer* buffer, TableFlattenerOptions options) :
+ mBuffer(buffer), mOptions(options) {
+ }
+
+ bool consume(IAaptContext* context, ResourceTable* table) override;
+
+private:
+ BigBuffer* mBuffer;
+ TableFlattenerOptions mOptions;
+};
+
+} // namespace aapt
+
+#endif /* AAPT_FLATTEN_TABLEFLATTENER_H */
diff --git a/tools/aapt2/flatten/TableFlattener_test.cpp b/tools/aapt2/flatten/TableFlattener_test.cpp
new file mode 100644
index 0000000..68a1f47
--- /dev/null
+++ b/tools/aapt2/flatten/TableFlattener_test.cpp
@@ -0,0 +1,265 @@
+/*
+ * 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.
+ */
+
+#include "flatten/TableFlattener.h"
+#include "unflatten/BinaryResourceParser.h"
+#include "util/Util.h"
+
+#include "test/Builders.h"
+#include "test/Context.h"
+
+#include <gtest/gtest.h>
+
+using namespace android;
+
+namespace aapt {
+
+class TableFlattenerTest : public ::testing::Test {
+public:
+ void SetUp() override {
+ mContext = test::ContextBuilder()
+ .setCompilationPackage(u"com.app.test")
+ .setPackageId(0x7f)
+ .build();
+ }
+
+ ::testing::AssertionResult flatten(ResourceTable* table, ResTable* outTable) {
+ BigBuffer buffer(1024);
+ TableFlattenerOptions options = {};
+ options.useExtendedChunks = true;
+ TableFlattener flattener(&buffer, options);
+ if (!flattener.consume(mContext.get(), table)) {
+ return ::testing::AssertionFailure() << "failed to flatten ResourceTable";
+ }
+
+ std::unique_ptr<uint8_t[]> data = util::copy(buffer);
+ if (outTable->add(data.get(), buffer.size(), -1, true) != NO_ERROR) {
+ return ::testing::AssertionFailure() << "flattened ResTable is corrupt";
+ }
+ return ::testing::AssertionSuccess();
+ }
+
+ ::testing::AssertionResult flatten(ResourceTable* table, ResourceTable* outTable) {
+ BigBuffer buffer(1024);
+ TableFlattenerOptions options = {};
+ options.useExtendedChunks = true;
+ TableFlattener flattener(&buffer, options);
+ if (!flattener.consume(mContext.get(), table)) {
+ return ::testing::AssertionFailure() << "failed to flatten ResourceTable";
+ }
+
+ std::unique_ptr<uint8_t[]> data = util::copy(buffer);
+ BinaryResourceParser parser(mContext.get(), outTable, {}, data.get(), buffer.size());
+ if (!parser.parse()) {
+ return ::testing::AssertionFailure() << "flattened ResTable is corrupt";
+ }
+ return ::testing::AssertionSuccess();
+ }
+
+ ::testing::AssertionResult exists(ResTable* table,
+ const StringPiece16& expectedName,
+ const ResourceId expectedId,
+ const ConfigDescription& expectedConfig,
+ const uint8_t expectedDataType, const uint32_t expectedData,
+ const uint32_t expectedSpecFlags) {
+ const ResourceName expectedResName = test::parseNameOrDie(expectedName);
+
+ table->setParameters(&expectedConfig);
+
+ ResTable_config config;
+ Res_value val;
+ uint32_t specFlags;
+ if (table->getResource(expectedId.id, &val, false, 0, &specFlags, &config) < 0) {
+ return ::testing::AssertionFailure() << "could not find resource with";
+ }
+
+ if (expectedDataType != val.dataType) {
+ return ::testing::AssertionFailure()
+ << "expected data type "
+ << std::hex << (int) expectedDataType << " but got data type "
+ << (int) val.dataType << std::dec << " instead";
+ }
+
+ if (expectedData != val.data) {
+ return ::testing::AssertionFailure()
+ << "expected data "
+ << std::hex << expectedData << " but got data "
+ << val.data << std::dec << " instead";
+ }
+
+ if (expectedSpecFlags != specFlags) {
+ return ::testing::AssertionFailure()
+ << "expected specFlags "
+ << std::hex << expectedSpecFlags << " but got specFlags "
+ << specFlags << std::dec << " instead";
+ }
+
+ ResTable::resource_name actualName;
+ if (!table->getResourceName(expectedId.id, false, &actualName)) {
+ return ::testing::AssertionFailure() << "failed to find resource name";
+ }
+
+ StringPiece16 package16(actualName.package, actualName.packageLen);
+ if (package16 != expectedResName.package) {
+ return ::testing::AssertionFailure()
+ << "expected package '" << expectedResName.package << "' but got '"
+ << package16 << "'";
+ }
+
+ StringPiece16 type16(actualName.type, actualName.typeLen);
+ if (type16 != toString(expectedResName.type)) {
+ return ::testing::AssertionFailure()
+ << "expected type '" << expectedResName.type
+ << "' but got '" << type16 << "'";
+ }
+
+ StringPiece16 name16(actualName.name, actualName.nameLen);
+ if (name16 != expectedResName.entry) {
+ return ::testing::AssertionFailure()
+ << "expected name '" << expectedResName.entry
+ << "' but got '" << name16 << "'";
+ }
+
+ if (expectedConfig != config) {
+ return ::testing::AssertionFailure()
+ << "expected config '" << expectedConfig << "' but got '"
+ << ConfigDescription(config) << "'";
+ }
+ return ::testing::AssertionSuccess();
+ }
+
+private:
+ std::unique_ptr<IAaptContext> mContext;
+};
+
+TEST_F(TableFlattenerTest, FlattenFullyLinkedTable) {
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .setPackageId(u"com.app.test", 0x7f)
+ .addSimple(u"@com.app.test:id/one", ResourceId(0x7f020000))
+ .addSimple(u"@com.app.test:id/two", ResourceId(0x7f020001))
+ .addValue(u"@com.app.test:id/three", ResourceId(0x7f020002),
+ test::buildReference(u"@com.app.test:id/one", ResourceId(0x7f020000)))
+ .addValue(u"@com.app.test:integer/one", ResourceId(0x7f030000),
+ util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 1u))
+ .addValue(u"@com.app.test:integer/one", ResourceId(0x7f030000),
+ test::parseConfigOrDie("v1"),
+ util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 2u))
+ .addString(u"@com.app.test:string/test", ResourceId(0x7f040000), u"foo")
+ .addString(u"@com.app.test:layout/bar", ResourceId(0x7f050000), u"res/layout/bar.xml")
+ .build();
+
+ ResTable resTable;
+ ASSERT_TRUE(flatten(table.get(), &resTable));
+
+ EXPECT_TRUE(exists(&resTable, u"@com.app.test:id/one", ResourceId(0x7f020000), {},
+ Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
+
+ EXPECT_TRUE(exists(&resTable, u"@com.app.test:id/two", ResourceId(0x7f020001), {},
+ Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
+
+ EXPECT_TRUE(exists(&resTable, u"@com.app.test:id/three", ResourceId(0x7f020002), {},
+ Res_value::TYPE_REFERENCE, 0x7f020000u, 0u));
+
+ EXPECT_TRUE(exists(&resTable, u"@com.app.test:integer/one", ResourceId(0x7f030000),
+ {}, Res_value::TYPE_INT_DEC, 1u,
+ ResTable_config::CONFIG_VERSION));
+
+ EXPECT_TRUE(exists(&resTable, u"@com.app.test:integer/one", ResourceId(0x7f030000),
+ test::parseConfigOrDie("v1"), Res_value::TYPE_INT_DEC, 2u,
+ ResTable_config::CONFIG_VERSION));
+
+ StringPiece16 fooStr = u"foo";
+ ssize_t idx = resTable.getTableStringBlock(0)->indexOfString(fooStr.data(), fooStr.size());
+ ASSERT_GE(idx, 0);
+ EXPECT_TRUE(exists(&resTable, u"@com.app.test:string/test", ResourceId(0x7f040000),
+ {}, Res_value::TYPE_STRING, (uint32_t) idx, 0u));
+
+ StringPiece16 barPath = u"res/layout/bar.xml";
+ idx = resTable.getTableStringBlock(0)->indexOfString(barPath.data(), barPath.size());
+ ASSERT_GE(idx, 0);
+ EXPECT_TRUE(exists(&resTable, u"@com.app.test:layout/bar", ResourceId(0x7f050000), {},
+ Res_value::TYPE_STRING, (uint32_t) idx, 0u));
+}
+
+TEST_F(TableFlattenerTest, FlattenEntriesWithGapsInIds) {
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .setPackageId(u"com.app.test", 0x7f)
+ .addSimple(u"@com.app.test:id/one", ResourceId(0x7f020001))
+ .addSimple(u"@com.app.test:id/three", ResourceId(0x7f020003))
+ .build();
+
+ ResTable resTable;
+ ASSERT_TRUE(flatten(table.get(), &resTable));
+
+ EXPECT_TRUE(exists(&resTable, u"@com.app.test:id/one", ResourceId(0x7f020001), {},
+ Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
+ EXPECT_TRUE(exists(&resTable, u"@com.app.test:id/three", ResourceId(0x7f020003), {},
+ Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
+}
+
+TEST_F(TableFlattenerTest, FlattenUnlinkedTable) {
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .setPackageId(u"com.app.test", 0x7f)
+ .addValue(u"@com.app.test:integer/one", ResourceId(0x7f020000),
+ test::buildReference(u"@android:integer/foo"))
+ .addValue(u"@com.app.test:style/Theme", ResourceId(0x7f030000), test::StyleBuilder()
+ .setParent(u"@android:style/Theme.Material")
+ .addItem(u"@android:attr/background", {})
+ .addItem(u"@android:attr/colorAccent",
+ test::buildReference(u"@com.app.test:color/green"))
+ .build())
+ .build();
+
+ {
+ // Need access to stringPool to make RawString.
+ Style* style = test::getValue<Style>(table.get(), u"@com.app.test:style/Theme");
+ style->entries[0].value = util::make_unique<RawString>(table->stringPool.makeRef(u"foo"));
+ }
+
+ ResourceTable finalTable;
+ ASSERT_TRUE(flatten(table.get(), &finalTable));
+
+ Reference* ref = test::getValue<Reference>(&finalTable, u"@com.app.test:integer/one");
+ ASSERT_NE(ref, nullptr);
+ AAPT_ASSERT_TRUE(ref->name);
+ EXPECT_EQ(ref->name.value(), test::parseNameOrDie(u"@android:integer/foo"));
+
+ Style* style = test::getValue<Style>(&finalTable, u"@com.app.test:style/Theme");
+ ASSERT_NE(style, nullptr);
+ AAPT_ASSERT_TRUE(style->parent);
+ AAPT_ASSERT_TRUE(style->parent.value().name);
+ EXPECT_EQ(style->parent.value().name.value(),
+ test::parseNameOrDie(u"@android:style/Theme.Material"));
+
+ ASSERT_EQ(2u, style->entries.size());
+
+ AAPT_ASSERT_TRUE(style->entries[0].key.name);
+ EXPECT_EQ(style->entries[0].key.name.value(),
+ test::parseNameOrDie(u"@android:attr/background"));
+ RawString* raw = valueCast<RawString>(style->entries[0].value.get());
+ ASSERT_NE(raw, nullptr);
+ EXPECT_EQ(*raw->value, u"foo");
+
+ AAPT_ASSERT_TRUE(style->entries[1].key.name);
+ EXPECT_EQ(style->entries[1].key.name.value(),
+ test::parseNameOrDie(u"@android:attr/colorAccent"));
+ ref = valueCast<Reference>(style->entries[1].value.get());
+ ASSERT_NE(ref, nullptr);
+ AAPT_ASSERT_TRUE(ref->name);
+ EXPECT_EQ(ref->name.value(), test::parseNameOrDie(u"@com.app.test:color/green"));
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/flatten/XmlFlattener.cpp b/tools/aapt2/flatten/XmlFlattener.cpp
new file mode 100644
index 0000000..4efb08b
--- /dev/null
+++ b/tools/aapt2/flatten/XmlFlattener.cpp
@@ -0,0 +1,316 @@
+/*
+ * 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.
+ */
+
+#include "SdkConstants.h"
+#include "XmlDom.h"
+
+#include "flatten/ChunkWriter.h"
+#include "flatten/ResourceTypeExtensions.h"
+#include "flatten/XmlFlattener.h"
+
+#include <androidfw/ResourceTypes.h>
+#include <vector>
+#include <utils/misc.h>
+
+using namespace android;
+
+namespace aapt {
+
+namespace {
+
+constexpr uint32_t kLowPriority = 0xffffffffu;
+
+struct XmlFlattenerVisitor : public xml::Visitor {
+ using xml::Visitor::visit;
+
+ BigBuffer* mBuffer;
+ XmlFlattenerOptions mOptions;
+ StringPool mPool;
+ std::map<uint8_t, StringPool> mPackagePools;
+
+ struct StringFlattenDest {
+ StringPool::Ref ref;
+ ResStringPool_ref* dest;
+ };
+ std::vector<StringFlattenDest> mStringRefs;
+
+ // Scratch vector to filter attributes. We avoid allocations
+ // making this a member.
+ std::vector<xml::Attribute*> mFilteredAttrs;
+
+
+ XmlFlattenerVisitor(BigBuffer* buffer, XmlFlattenerOptions options) :
+ mBuffer(buffer), mOptions(options) {
+ }
+
+ void addString(const StringPiece16& str, uint32_t priority, android::ResStringPool_ref* dest) {
+ if (!str.empty()) {
+ mStringRefs.push_back(StringFlattenDest{
+ mPool.makeRef(str, StringPool::Context{ priority }),
+ dest });
+ } else {
+ // The device doesn't think a string of size 0 is the same as null.
+ dest->index = util::deviceToHost32(-1);
+ }
+ }
+
+ void addString(const StringPool::Ref& ref, android::ResStringPool_ref* dest) {
+ mStringRefs.push_back(StringFlattenDest{ ref, dest });
+ }
+
+ void writeNamespace(xml::Namespace* node, uint16_t type) {
+ ChunkWriter writer(mBuffer);
+
+ ResXMLTree_node* flatNode = writer.startChunk<ResXMLTree_node>(type);
+ flatNode->lineNumber = util::hostToDevice32(node->lineNumber);
+ flatNode->comment.index = util::hostToDevice32(-1);
+
+ ResXMLTree_namespaceExt* flatNs = writer.nextBlock<ResXMLTree_namespaceExt>();
+ addString(node->namespacePrefix, kLowPriority, &flatNs->prefix);
+ addString(node->namespaceUri, kLowPriority, &flatNs->uri);
+
+ writer.finish();
+ }
+
+ void visit(xml::Namespace* node) override {
+ writeNamespace(node, android::RES_XML_START_NAMESPACE_TYPE);
+ xml::Visitor::visit(node);
+ writeNamespace(node, android::RES_XML_END_NAMESPACE_TYPE);
+ }
+
+ void visit(xml::Text* node) override {
+ if (util::trimWhitespace(node->text).empty()) {
+ // Skip whitespace only text nodes.
+ return;
+ }
+
+ ChunkWriter writer(mBuffer);
+ ResXMLTree_node* flatNode = writer.startChunk<ResXMLTree_node>(RES_XML_CDATA_TYPE);
+ flatNode->lineNumber = util::hostToDevice32(node->lineNumber);
+ flatNode->comment.index = util::hostToDevice32(-1);
+
+ ResXMLTree_cdataExt* flatText = writer.nextBlock<ResXMLTree_cdataExt>();
+ addString(node->text, kLowPriority, &flatText->data);
+
+ writer.finish();
+ }
+
+ void visit(xml::Element* node) override {
+ {
+ ChunkWriter startWriter(mBuffer);
+ ResXMLTree_node* flatNode = startWriter.startChunk<ResXMLTree_node>(
+ RES_XML_START_ELEMENT_TYPE);
+ flatNode->lineNumber = util::hostToDevice32(node->lineNumber);
+ flatNode->comment.index = util::hostToDevice32(-1);
+
+ ResXMLTree_attrExt* flatElem = startWriter.nextBlock<ResXMLTree_attrExt>();
+ addString(node->namespaceUri, kLowPriority, &flatElem->ns);
+ addString(node->name, kLowPriority, &flatElem->name);
+ flatElem->attributeStart = util::hostToDevice16(sizeof(*flatElem));
+ flatElem->attributeSize = util::hostToDevice16(sizeof(ResXMLTree_attribute));
+
+ writeAttributes(node, flatElem, &startWriter);
+
+ startWriter.finish();
+ }
+
+ xml::Visitor::visit(node);
+
+ {
+ ChunkWriter endWriter(mBuffer);
+ ResXMLTree_node* flatEndNode = endWriter.startChunk<ResXMLTree_node>(
+ RES_XML_END_ELEMENT_TYPE);
+ flatEndNode->lineNumber = util::hostToDevice32(node->lineNumber);
+ flatEndNode->comment.index = util::hostToDevice32(-1);
+
+ ResXMLTree_endElementExt* flatEndElem = endWriter.nextBlock<ResXMLTree_endElementExt>();
+ addString(node->namespaceUri, kLowPriority, &flatEndElem->ns);
+ addString(node->name, kLowPriority, &flatEndElem->name);
+
+ endWriter.finish();
+ }
+ }
+
+ static bool cmpXmlAttributeById(const xml::Attribute* a, const xml::Attribute* b) {
+ if (a->compiledAttribute) {
+ if (b->compiledAttribute) {
+ return a->compiledAttribute.value().id < b->compiledAttribute.value().id;
+ }
+ return true;
+ } else if (!b->compiledAttribute) {
+ int diff = a->namespaceUri.compare(b->namespaceUri);
+ if (diff < 0) {
+ return true;
+ } else if (diff > 0) {
+ return false;
+ }
+ return a->name < b->name;
+ }
+ return false;
+ }
+
+ void writeAttributes(xml::Element* node, ResXMLTree_attrExt* flatElem, ChunkWriter* writer) {
+ mFilteredAttrs.clear();
+ mFilteredAttrs.reserve(node->attributes.size());
+
+ // Filter the attributes.
+ for (xml::Attribute& attr : node->attributes) {
+ if (mOptions.maxSdkLevel && attr.compiledAttribute) {
+ size_t sdkLevel = findAttributeSdkLevel(attr.compiledAttribute.value().id);
+ if (sdkLevel > mOptions.maxSdkLevel.value()) {
+ continue;
+ }
+ }
+ mFilteredAttrs.push_back(&attr);
+ }
+
+ if (mFilteredAttrs.empty()) {
+ return;
+ }
+
+ const ResourceId kIdAttr(0x010100d0);
+
+ std::sort(mFilteredAttrs.begin(), mFilteredAttrs.end(), cmpXmlAttributeById);
+
+ flatElem->attributeCount = util::hostToDevice16(mFilteredAttrs.size());
+
+ ResXMLTree_attribute* flatAttr = writer->nextBlock<ResXMLTree_attribute>(
+ mFilteredAttrs.size());
+ uint16_t attributeIndex = 1;
+ for (const xml::Attribute* xmlAttr : mFilteredAttrs) {
+ // Assign the indices for specific attributes.
+ if (xmlAttr->compiledAttribute &&
+ xmlAttr->compiledAttribute.value().id == kIdAttr) {
+ flatElem->idIndex = util::hostToDevice16(attributeIndex);
+ } else if (xmlAttr->namespaceUri.empty()) {
+ if (xmlAttr->name == u"class") {
+ flatElem->classIndex = util::hostToDevice16(attributeIndex);
+ } else if (xmlAttr->name == u"style") {
+ flatElem->styleIndex = util::hostToDevice16(attributeIndex);
+ }
+ }
+ attributeIndex++;
+
+ // Add the namespaceUri to the list of StringRefs to encode.
+ addString(xmlAttr->namespaceUri, kLowPriority, &flatAttr->ns);
+
+ flatAttr->rawValue.index = util::hostToDevice32(-1);
+
+ if (!xmlAttr->compiledAttribute) {
+ // The attribute has no associated ResourceID, so the string order doesn't matter.
+ addString(xmlAttr->name, kLowPriority, &flatAttr->name);
+ } else {
+ // Attribute names are stored without packages, but we use
+ // their StringPool index to lookup their resource IDs.
+ // This will cause collisions, so we can't dedupe
+ // attribute names from different packages. We use separate
+ // pools that we later combine.
+ //
+ // Lookup the StringPool for this package and make the reference there.
+ const xml::AaptAttribute& aaptAttr = xmlAttr->compiledAttribute.value();
+
+ StringPool::Ref nameRef = mPackagePools[aaptAttr.id.packageId()].makeRef(
+ xmlAttr->name, StringPool::Context{ aaptAttr.id.id });
+
+ // Add it to the list of strings to flatten.
+ addString(nameRef, &flatAttr->name);
+
+ if (mOptions.keepRawValues) {
+ // Keep raw values (this is for static libraries).
+ // TODO(with a smarter inflater for binary XML, we can do without this).
+ addString(xmlAttr->value, kLowPriority, &flatAttr->rawValue);
+ }
+ }
+
+ if (xmlAttr->compiledValue) {
+ bool result = xmlAttr->compiledValue->flatten(&flatAttr->typedValue);
+ assert(result);
+ } else {
+ // Flatten as a regular string type.
+ flatAttr->typedValue.dataType = android::Res_value::TYPE_STRING;
+ addString(xmlAttr->value, kLowPriority, &flatAttr->rawValue);
+ addString(xmlAttr->value, kLowPriority,
+ (ResStringPool_ref*) &flatAttr->typedValue.data);
+ }
+
+ flatAttr->typedValue.size = util::hostToDevice16(sizeof(flatAttr->typedValue));
+ flatAttr++;
+ }
+ }
+};
+
+} // namespace
+
+bool XmlFlattener::flatten(IAaptContext* context, xml::Node* node) {
+ BigBuffer nodeBuffer(1024);
+ XmlFlattenerVisitor visitor(&nodeBuffer, mOptions);
+ node->accept(&visitor);
+
+ // Merge the package pools into the main pool.
+ for (auto& packagePoolEntry : visitor.mPackagePools) {
+ visitor.mPool.merge(std::move(packagePoolEntry.second));
+ }
+
+ // Sort the string pool so that attribute resource IDs show up first.
+ visitor.mPool.sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
+ return a.context.priority < b.context.priority;
+ });
+
+ // Now we flatten the string pool references into the correct places.
+ for (const auto& refEntry : visitor.mStringRefs) {
+ refEntry.dest->index = util::hostToDevice32(refEntry.ref.getIndex());
+ }
+
+ // Write the XML header.
+ ChunkWriter xmlHeaderWriter(mBuffer);
+ xmlHeaderWriter.startChunk<ResXMLTree_header>(RES_XML_TYPE);
+
+ // Flatten the StringPool.
+ StringPool::flattenUtf16(mBuffer, visitor.mPool);
+
+ {
+ // Write the array of resource IDs, indexed by StringPool order.
+ ChunkWriter resIdMapWriter(mBuffer);
+ resIdMapWriter.startChunk<ResChunk_header>(RES_XML_RESOURCE_MAP_TYPE);
+ for (const auto& str : visitor.mPool) {
+ ResourceId id = { str->context.priority };
+ if (id.id == kLowPriority || !id.isValid()) {
+ // When we see the first non-resource ID,
+ // we're done.
+ break;
+ }
+
+ *resIdMapWriter.nextBlock<uint32_t>() = id.id;
+ }
+ resIdMapWriter.finish();
+ }
+
+ // Move the nodeBuffer and append it to the out buffer.
+ mBuffer->appendBuffer(std::move(nodeBuffer));
+
+ // Finish the xml header.
+ xmlHeaderWriter.finish();
+ return true;
+}
+
+bool XmlFlattener::consume(IAaptContext* context, XmlResource* resource) {
+ if (!resource->root) {
+ return false;
+ }
+ return flatten(context, resource->root.get());
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/flatten/XmlFlattener.h b/tools/aapt2/flatten/XmlFlattener.h
new file mode 100644
index 0000000..b1fb3a7
--- /dev/null
+++ b/tools/aapt2/flatten/XmlFlattener.h
@@ -0,0 +1,59 @@
+/*
+ * 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_FLATTEN_XMLFLATTENER_H
+#define AAPT_FLATTEN_XMLFLATTENER_H
+
+#include "util/BigBuffer.h"
+
+#include "process/IResourceTableConsumer.h"
+
+namespace aapt {
+
+namespace xml {
+struct Node;
+}
+
+struct XmlFlattenerOptions {
+ /**
+ * Keep attribute raw string values along with typed values.
+ */
+ bool keepRawValues = false;
+
+ /**
+ * If set, the max SDK level of attribute to flatten. All others are ignored.
+ */
+ Maybe<size_t> maxSdkLevel;
+};
+
+class XmlFlattener : public IXmlResourceConsumer {
+public:
+ XmlFlattener(BigBuffer* buffer, XmlFlattenerOptions options) :
+ mBuffer(buffer), mOptions(options) {
+ }
+
+ bool consume(IAaptContext* context, XmlResource* resource) override;
+
+private:
+ BigBuffer* mBuffer;
+ XmlFlattenerOptions mOptions;
+
+ bool flatten(IAaptContext* context, xml::Node* node);
+};
+
+} // namespace aapt
+
+#endif /* AAPT_FLATTEN_XMLFLATTENER_H */
diff --git a/tools/aapt2/flatten/XmlFlattener_test.cpp b/tools/aapt2/flatten/XmlFlattener_test.cpp
new file mode 100644
index 0000000..318bcdd
--- /dev/null
+++ b/tools/aapt2/flatten/XmlFlattener_test.cpp
@@ -0,0 +1,209 @@
+/*
+ * 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.
+ */
+
+#include "flatten/XmlFlattener.h"
+#include "link/Linkers.h"
+#include "util/BigBuffer.h"
+#include "util/Util.h"
+
+#include "test/Builders.h"
+#include "test/Context.h"
+
+#include <androidfw/ResourceTypes.h>
+#include <gtest/gtest.h>
+
+namespace aapt {
+
+class XmlFlattenerTest : public ::testing::Test {
+public:
+ void SetUp() override {
+ mContext = test::ContextBuilder()
+ .setCompilationPackage(u"com.app.test")
+ .setNameManglerPolicy(NameManglerPolicy{ u"com.app.test" })
+ .setSymbolTable(test::StaticSymbolTableBuilder()
+ .addSymbol(u"@android:attr/id", ResourceId(0x010100d0),
+ test::AttributeBuilder().build())
+ .addSymbol(u"@com.app.test:id/id", ResourceId(0x7f020000))
+ .addSymbol(u"@android:attr/paddingStart", ResourceId(0x010103b3),
+ test::AttributeBuilder().build())
+ .addSymbol(u"@android:attr/colorAccent", ResourceId(0x01010435),
+ test::AttributeBuilder().build())
+ .build())
+ .build();
+ }
+
+ ::testing::AssertionResult flatten(XmlResource* doc, android::ResXMLTree* outTree,
+ XmlFlattenerOptions options = {}) {
+ BigBuffer buffer(1024);
+ XmlFlattener flattener(&buffer, options);
+ if (!flattener.consume(mContext.get(), doc)) {
+ return ::testing::AssertionFailure() << "failed to flatten XML Tree";
+ }
+
+ std::unique_ptr<uint8_t[]> data = util::copy(buffer);
+ if (outTree->setTo(data.get(), buffer.size(), true) != android::NO_ERROR) {
+ return ::testing::AssertionFailure() << "flattened XML is corrupt";
+ }
+ return ::testing::AssertionSuccess();
+ }
+
+protected:
+ std::unique_ptr<IAaptContext> mContext;
+};
+
+TEST_F(XmlFlattenerTest, FlattenXmlWithNoCompiledAttributes) {
+ std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF(
+ <View xmlns:test="http://com.test"
+ attr="hey">
+ <Layout test:hello="hi" />
+ <Layout>Some text</Layout>
+ </View>)EOF");
+
+
+ android::ResXMLTree tree;
+ ASSERT_TRUE(flatten(doc.get(), &tree));
+
+ ASSERT_EQ(tree.next(), android::ResXMLTree::START_NAMESPACE);
+
+ size_t len;
+ const char16_t* namespacePrefix = tree.getNamespacePrefix(&len);
+ EXPECT_EQ(StringPiece16(namespacePrefix, len), u"test");
+
+ const char16_t* namespaceUri = tree.getNamespaceUri(&len);
+ ASSERT_EQ(StringPiece16(namespaceUri, len), u"http://com.test");
+
+ ASSERT_EQ(tree.next(), android::ResXMLTree::START_TAG);
+
+ ASSERT_EQ(tree.getElementNamespace(&len), nullptr);
+ const char16_t* tagName = tree.getElementName(&len);
+ EXPECT_EQ(StringPiece16(tagName, len), u"View");
+
+ ASSERT_EQ(1u, tree.getAttributeCount());
+ ASSERT_EQ(tree.getAttributeNamespace(0, &len), nullptr);
+ const char16_t* attrName = tree.getAttributeName(0, &len);
+ EXPECT_EQ(StringPiece16(attrName, len), u"attr");
+
+ EXPECT_EQ(0, tree.indexOfAttribute(nullptr, 0, u"attr", StringPiece16(u"attr").size()));
+
+ ASSERT_EQ(tree.next(), android::ResXMLTree::START_TAG);
+
+ ASSERT_EQ(tree.getElementNamespace(&len), nullptr);
+ tagName = tree.getElementName(&len);
+ EXPECT_EQ(StringPiece16(tagName, len), u"Layout");
+
+ ASSERT_EQ(1u, tree.getAttributeCount());
+ const char16_t* attrNamespace = tree.getAttributeNamespace(0, &len);
+ EXPECT_EQ(StringPiece16(attrNamespace, len), u"http://com.test");
+
+ attrName = tree.getAttributeName(0, &len);
+ EXPECT_EQ(StringPiece16(attrName, len), u"hello");
+
+ ASSERT_EQ(tree.next(), android::ResXMLTree::END_TAG);
+ ASSERT_EQ(tree.next(), android::ResXMLTree::START_TAG);
+
+ ASSERT_EQ(tree.getElementNamespace(&len), nullptr);
+ tagName = tree.getElementName(&len);
+ EXPECT_EQ(StringPiece16(tagName, len), u"Layout");
+ ASSERT_EQ(0u, tree.getAttributeCount());
+
+ ASSERT_EQ(tree.next(), android::ResXMLTree::TEXT);
+ const char16_t* text = tree.getText(&len);
+ EXPECT_EQ(StringPiece16(text, len), u"Some text");
+
+ ASSERT_EQ(tree.next(), android::ResXMLTree::END_TAG);
+ ASSERT_EQ(tree.getElementNamespace(&len), nullptr);
+ tagName = tree.getElementName(&len);
+ EXPECT_EQ(StringPiece16(tagName, len), u"Layout");
+
+ ASSERT_EQ(tree.next(), android::ResXMLTree::END_TAG);
+ ASSERT_EQ(tree.getElementNamespace(&len), nullptr);
+ tagName = tree.getElementName(&len);
+ EXPECT_EQ(StringPiece16(tagName, len), u"View");
+
+ ASSERT_EQ(tree.next(), android::ResXMLTree::END_NAMESPACE);
+ namespacePrefix = tree.getNamespacePrefix(&len);
+ EXPECT_EQ(StringPiece16(namespacePrefix, len), u"test");
+
+ namespaceUri = tree.getNamespaceUri(&len);
+ ASSERT_EQ(StringPiece16(namespaceUri, len), u"http://com.test");
+
+ ASSERT_EQ(tree.next(), android::ResXMLTree::END_DOCUMENT);
+}
+
+TEST_F(XmlFlattenerTest, FlattenCompiledXmlAndStripSdk21) {
+ std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF(
+ <View xmlns:android="http://schemas.android.com/apk/res/android"
+ android:paddingStart="1dp"
+ android:colorAccent="#ffffff"/>)EOF");
+
+ XmlReferenceLinker linker;
+ ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
+ ASSERT_TRUE(linker.getSdkLevels().count(17) == 1);
+ ASSERT_TRUE(linker.getSdkLevels().count(21) == 1);
+
+ android::ResXMLTree tree;
+ XmlFlattenerOptions options;
+ options.maxSdkLevel = 17;
+ ASSERT_TRUE(flatten(doc.get(), &tree, options));
+
+ while (tree.next() != android::ResXMLTree::START_TAG) {
+ ASSERT_NE(tree.getEventType(), android::ResXMLTree::BAD_DOCUMENT);
+ ASSERT_NE(tree.getEventType(), android::ResXMLTree::END_DOCUMENT);
+ }
+
+ ASSERT_EQ(1u, tree.getAttributeCount());
+ EXPECT_EQ(uint32_t(0x010103b3), tree.getAttributeNameResID(0));
+}
+
+TEST_F(XmlFlattenerTest, AssignSpecialAttributeIndices) {
+ std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF(
+ <View xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@id/id"
+ class="str"
+ style="@id/id"/>)EOF");
+
+ android::ResXMLTree tree;
+ ASSERT_TRUE(flatten(doc.get(), &tree));
+
+ while (tree.next() != android::ResXMLTree::START_TAG) {
+ ASSERT_NE(tree.getEventType(), android::ResXMLTree::BAD_DOCUMENT);
+ ASSERT_NE(tree.getEventType(), android::ResXMLTree::END_DOCUMENT);
+ }
+
+ EXPECT_EQ(tree.indexOfClass(), 0);
+ EXPECT_EQ(tree.indexOfStyle(), 1);
+}
+
+/*
+ * The device ResXMLParser in libandroidfw differentiates between empty namespace and null
+ * namespace.
+ */
+TEST_F(XmlFlattenerTest, NoNamespaceIsNotTheSameAsEmptyNamespace) {
+ std::unique_ptr<XmlResource> doc = test::buildXmlDom("<View package=\"android\"/>");
+
+ android::ResXMLTree tree;
+ ASSERT_TRUE(flatten(doc.get(), &tree));
+
+ while (tree.next() != android::ResXMLTree::START_TAG) {
+ ASSERT_NE(tree.getEventType(), android::ResXMLTree::BAD_DOCUMENT);
+ ASSERT_NE(tree.getEventType(), android::ResXMLTree::END_DOCUMENT);
+ }
+
+ const StringPiece16 kPackage = u"package";
+ EXPECT_GE(tree.indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size()), 0);
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/java/AnnotationProcessor.cpp b/tools/aapt2/java/AnnotationProcessor.cpp
new file mode 100644
index 0000000..9c25d4e
--- /dev/null
+++ b/tools/aapt2/java/AnnotationProcessor.cpp
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+
+#include "java/AnnotationProcessor.h"
+#include "util/Util.h"
+
+#include <algorithm>
+
+namespace aapt {
+
+void AnnotationProcessor::appendCommentLine(const std::string& comment) {
+ static const std::string sDeprecated = "@deprecated";
+ static const std::string sSystemApi = "@SystemApi";
+
+ if (comment.find(sDeprecated) != std::string::npos) {
+ mAnnotationBitMask |= kDeprecated;
+ }
+
+ if (comment.find(sSystemApi) != std::string::npos) {
+ mAnnotationBitMask |= kSystemApi;
+ }
+
+ if (!mHasComments) {
+ mHasComments = true;
+ mComment << "/**";
+ }
+
+ mComment << "\n" << " * " << std::move(comment);
+}
+
+void AnnotationProcessor::appendComment(const StringPiece16& comment) {
+ // We need to process line by line to clean-up whitespace and append prefixes.
+ for (StringPiece16 line : util::tokenize(comment, u'\n')) {
+ line = util::trimWhitespace(line);
+ if (!line.empty()) {
+ appendCommentLine(util::utf16ToUtf8(line));
+ }
+ }
+}
+
+void AnnotationProcessor::appendComment(const StringPiece& comment) {
+ for (StringPiece line : util::tokenize(comment, '\n')) {
+ line = util::trimWhitespace(line);
+ if (!line.empty()) {
+ appendCommentLine(line.toString());
+ }
+ }
+}
+
+void AnnotationProcessor::writeToStream(std::ostream* out, const StringPiece& prefix) {
+ if (mHasComments) {
+ std::string result = mComment.str();
+ for (StringPiece line : util::tokenize<char>(result, '\n')) {
+ *out << prefix << line << "\n";
+ }
+ *out << prefix << " */" << "\n";
+ }
+
+ if (mAnnotationBitMask & kDeprecated) {
+ *out << prefix << "@Deprecated\n";
+ }
+
+ if (mAnnotationBitMask & kSystemApi) {
+ *out << prefix << "@android.annotation.SystemApi\n";
+ }
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/java/AnnotationProcessor.h b/tools/aapt2/java/AnnotationProcessor.h
new file mode 100644
index 0000000..e7f2be0
--- /dev/null
+++ b/tools/aapt2/java/AnnotationProcessor.h
@@ -0,0 +1,85 @@
+/*
+ * 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_ANNOTATIONPROCESSOR_H
+#define AAPT_JAVA_ANNOTATIONPROCESSOR_H
+
+#include "util/StringPiece.h"
+
+#include <sstream>
+#include <string>
+
+namespace aapt {
+
+/**
+ * Builds a JavaDoc comment from a set of XML comments.
+ * This will also look for instances of @SystemApi and convert them to
+ * actual Java annotations.
+ *
+ * Example:
+ *
+ * Input XML:
+ *
+ * <!-- This is meant to be hidden because
+ * It is system api. Also it is @deprecated
+ * @SystemApi
+ * -->
+ *
+ * Output JavaDoc:
+ *
+ * /\*
+ * * This is meant to be hidden because
+ * * It is system api. Also it is @deprecated
+ * * @SystemApi
+ * *\/
+ *
+ * Output Annotations:
+ *
+ * @Deprecated
+ * @android.annotation.SystemApi
+ *
+ */
+class AnnotationProcessor {
+public:
+ /**
+ * Adds more comments. Since resources can have various values with different configurations,
+ * we need to collect all the comments.
+ */
+ void appendComment(const StringPiece16& comment);
+ void appendComment(const StringPiece& comment);
+
+ /**
+ * Writes the comments and annotations to the stream, with the given prefix before each line.
+ */
+ void writeToStream(std::ostream* out, const StringPiece& prefix);
+
+private:
+ enum : uint32_t {
+ kDeprecated = 0x01,
+ kSystemApi = 0x02,
+ };
+
+ std::stringstream mComment;
+ std::stringstream mAnnotations;
+ bool mHasComments = false;
+ uint32_t mAnnotationBitMask = 0;
+
+ void appendCommentLine(const std::string& line);
+};
+
+} // namespace aapt
+
+#endif /* AAPT_JAVA_ANNOTATIONPROCESSOR_H */
diff --git a/tools/aapt2/java/AnnotationProcessor_test.cpp b/tools/aapt2/java/AnnotationProcessor_test.cpp
new file mode 100644
index 0000000..d5a2b38
--- /dev/null
+++ b/tools/aapt2/java/AnnotationProcessor_test.cpp
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+
+#include "ResourceParser.h"
+#include "ResourceTable.h"
+#include "ResourceValues.h"
+#include "XmlPullParser.h"
+
+#include "java/AnnotationProcessor.h"
+
+#include "test/Builders.h"
+#include "test/Context.h"
+
+#include <gtest/gtest.h>
+
+namespace aapt {
+
+struct AnnotationProcessorTest : public ::testing::Test {
+ std::unique_ptr<IAaptContext> mContext;
+ ResourceTable mTable;
+
+ void SetUp() override {
+ mContext = test::ContextBuilder().build();
+ }
+
+ ::testing::AssertionResult parse(const StringPiece& str) {
+ ResourceParserOptions options;
+ ResourceParser parser(mContext->getDiagnostics(), &mTable, Source{}, ConfigDescription{},
+ options);
+ std::stringstream in;
+ in << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" << str;
+ XmlPullParser xmlParser(in);
+ if (parser.parse(&xmlParser)) {
+ return ::testing::AssertionSuccess();
+ }
+ return ::testing::AssertionFailure();
+ }
+};
+
+TEST_F(AnnotationProcessorTest, EmitsDeprecated) {
+ ASSERT_TRUE(parse(R"EOF(
+ <resources>
+ <declare-styleable name="foo">
+ <!-- Some comment, and it should contain
+ a marker word, something that marks
+ this resource as nor needed.
+ {@deprecated That's the marker! } -->
+ <attr name="autoText" format="boolean" />
+ </declare-styleable>
+ </resources>)EOF"));
+
+ Attribute* attr = test::getValue<Attribute>(&mTable, u"@attr/autoText");
+ ASSERT_NE(nullptr, attr);
+
+ AnnotationProcessor processor;
+ processor.appendComment(attr->getComment());
+
+ std::stringstream result;
+ processor.writeToStream(&result, "");
+ std::string annotations = result.str();
+
+ EXPECT_NE(std::string::npos, annotations.find("@Deprecated"));
+}
+
+} // namespace aapt
+
+
diff --git a/tools/aapt2/java/ClassDefinitionWriter.h b/tools/aapt2/java/ClassDefinitionWriter.h
new file mode 100644
index 0000000..b8886f9
--- /dev/null
+++ b/tools/aapt2/java/ClassDefinitionWriter.h
@@ -0,0 +1,141 @@
+/*
+ * 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 "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 StringPiece16& 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 StringPiece16& 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
new file mode 100644
index 0000000..7280f3a
--- /dev/null
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -0,0 +1,343 @@
+/*
+ * 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.
+ */
+
+#include "NameMangler.h"
+#include "Resource.h"
+#include "ResourceTable.h"
+#include "ResourceValues.h"
+#include "ValueVisitor.h"
+
+#include "java/AnnotationProcessor.h"
+#include "java/ClassDefinitionWriter.h"
+#include "java/JavaClassGenerator.h"
+#include "util/Comparators.h"
+#include "util/StringPiece.h"
+
+#include <algorithm>
+#include <ostream>
+#include <set>
+#include <sstream>
+#include <tuple>
+
+namespace aapt {
+
+JavaClassGenerator::JavaClassGenerator(ResourceTable* table, JavaClassGeneratorOptions options) :
+ 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",
+ u"default", u"do", u"double", u"else", u"enum", u"extends",
+ u"final", u"finally", u"float", u"for", u"goto", u"if",
+ u"implements", u"import", u"instanceof", u"int", u"interface",
+ u"long", u"native", u"new", u"package", u"private", u"protected",
+ u"public", u"return", u"short", u"static", u"strictfp", u"super",
+ u"switch", u"synchronized", u"this", u"throw", u"throws",
+ u"transient", u"try", u"void", u"volatile", u"while", u"true",
+ u"false", u"null"
+};
+
+static bool isValidSymbol(const StringPiece16& symbol) {
+ return sJavaIdentifiers.find(symbol) == sJavaIdentifiers.end();
+}
+
+/*
+ * Java symbols can not contain . or -, but those are valid in a resource name.
+ * Replace those with '_'.
+ */
+static std::u16string transform(const StringPiece16& symbol) {
+ std::u16string output = symbol.toString();
+ for (char16_t& c : output) {
+ if (c == u'.' || c == u'-') {
+ c = u'_';
+ }
+ }
+ return output;
+}
+
+bool JavaClassGenerator::skipSymbol(SymbolState state) {
+ switch (mOptions.types) {
+ case JavaClassGeneratorOptions::SymbolTypes::kAll:
+ return false;
+ case JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate:
+ return state == SymbolState::kUndefined;
+ case JavaClassGeneratorOptions::SymbolTypes::kPublic:
+ return state != SymbolState::kPublic;
+ }
+ return true;
+}
+
+void JavaClassGenerator::writeStyleableEntryForClass(ClassDefinitionWriter* outClassDef,
+ AnnotationProcessor* processor,
+ const StringPiece16& packageNameToGenerate,
+ const std::u16string& entryName,
+ const Styleable* styleable) {
+ // This must be sorted by resource ID.
+ std::vector<std::pair<ResourceId, ResourceNameRef>> sortedAttributes;
+ sortedAttributes.reserve(styleable->entries.size());
+ for (const auto& attr : styleable->entries) {
+ // If we are not encoding final attributes, the styleable entry may have no ID
+ // if we are building a static library.
+ assert((!mOptions.useFinal || attr.id) && "no ID set for Styleable entry");
+ assert(attr.name && "no name set for Styleable entry");
+ sortedAttributes.emplace_back(attr.id ? attr.id.value() : ResourceId(0), attr.name.value());
+ }
+ std::sort(sortedAttributes.begin(), sortedAttributes.end());
+
+ auto accessorFunc = [](const std::pair<ResourceId, ResourceNameRef>& a) -> ResourceId {
+ return a.first;
+ };
+
+ // First we emit the array containing the IDs of each attribute.
+ outClassDef->addArrayMember(transform(entryName), processor,
+ sortedAttributes.begin(),
+ sortedAttributes.end(),
+ accessorFunc);
+
+ // Now we emit the indices into the array.
+ size_t attrCount = sortedAttributes.size();
+ for (size_t i = 0; i < attrCount; i++) {
+ std::stringstream name;
+ name << transform(entryName);
+
+ // We may reference IDs from other packages, so prefix the entry name with
+ // the package.
+ const ResourceNameRef& itemName = sortedAttributes[i].second;
+ if (!itemName.package.empty() && packageNameToGenerate != itemName.package) {
+ name << "_" << transform(itemName.package);
+ }
+ name << "_" << transform(itemName.entry);
+
+ outClassDef->addIntMember(name.str(), nullptr, i);
+ }
+}
+
+static void addAttributeFormatDoc(AnnotationProcessor* processor, Attribute* attr) {
+ const uint32_t typeMask = attr->typeMask;
+ 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>\".");
+ }
+
+ 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;");
+ }
+
+ if (typeMask & android::ResTable_map::TYPE_INTEGER) {
+ processor->appendComment("<p>May be an integer value, such as \"<code>100</code>\".");
+ }
+
+ 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>\".");
+ }
+
+ 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>\".");
+ }
+
+ if (typeMask & android::ResTable_map::TYPE_FLOAT) {
+ processor->appendComment(
+ "<p>May be a floating point value, such as \"<code>1.2</code>\".");
+ }
+
+ 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).");
+ }
+
+ 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.");
+ }
+
+ 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>");
+ } 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");
+ for (const Attribute::Symbol& symbol : attr->symbols) {
+ std::stringstream line;
+ line << "<tr><td>" << symbol.symbol.name.value().entry << "</td>"
+ << "<td>" << std::hex << symbol.value << std::dec << "</td>"
+ << "<td>" << util::trimWhitespace(symbol.symbol.getComment()) << "</td></tr>";
+ processor->appendComment(line.str());
+ }
+ processor->appendComment("</table>");
+ }
+}
+
+bool JavaClassGenerator::writeEntriesForClass(ClassDefinitionWriter* outClassDef,
+ const StringPiece16& packageNameToGenerate,
+ const ResourceTablePackage* package,
+ const ResourceTableType* type) {
+ for (const auto& entry : type->entries) {
+ if (skipSymbol(entry->symbolStatus.state)) {
+ continue;
+ }
+
+ ResourceId id(package->id.value(), type->id.value(), entry->id.value());
+ assert(id.isValid());
+
+ std::u16string unmangledPackage;
+ std::u16string unmangledName = entry->name;
+ if (NameMangler::unmangle(&unmangledName, &unmangledPackage)) {
+ // The entry name was mangled, and we successfully unmangled it.
+ // Check that we want to emit this symbol.
+ if (package->name != unmangledPackage) {
+ // Skip the entry if it doesn't belong to the package we're writing.
+ continue;
+ }
+ } else if (packageNameToGenerate != package->name) {
+ // We are processing a mangled package name,
+ // but this is a non-mangled resource.
+ continue;
+ }
+
+ if (!isValidSymbol(unmangledName)) {
+ ResourceNameRef resourceName(packageNameToGenerate, type->type, unmangledName);
+ std::stringstream err;
+ err << "invalid symbol name '" << resourceName << "'";
+ mError = err.str();
+ 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);
+ } else {
+ outClassDef->addResourceMember(transform(unmangledName), &processor, id);
+ }
+ }
+ return true;
+}
+
+bool JavaClassGenerator::generate(const StringPiece16& packageNameToGenerate, std::ostream* out) {
+ return generate(packageNameToGenerate, packageNameToGenerate, out);
+}
+
+bool JavaClassGenerator::generate(const StringPiece16& packageNameToGenerate,
+ const StringPiece16& outPackageName, std::ostream* out) {
+ generateHeader(outPackageName, out);
+
+ *out << "public final class R {\n";
+
+ for (const auto& package : mTable->packages) {
+ for (const auto& type : package->types) {
+ if (type->type == ResourceType::kAttrPrivate) {
+ continue;
+ }
+
+ ClassDefinitionWriterOptions classOptions;
+ classOptions.useFinalQualifier = mOptions.useFinal;
+ classOptions.forceCreationIfEmpty =
+ (mOptions.types == JavaClassGeneratorOptions::SymbolTypes::kPublic);
+ ClassDefinitionWriter classDef(toString(type->type), classOptions);
+ bool result = writeEntriesForClass(&classDef, packageNameToGenerate,
+ package.get(), type.get());
+ if (!result) {
+ return false;
+ }
+
+ if (type->type == ResourceType::kAttr) {
+ // Also include private attributes in this same class.
+ auto iter = std::lower_bound(package->types.begin(), package->types.end(),
+ ResourceType::kAttrPrivate, cmp::lessThanType);
+ if (iter != package->types.end() && (*iter)->type == ResourceType::kAttrPrivate) {
+ result = writeEntriesForClass(&classDef, packageNameToGenerate,
+ package.get(), iter->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.writeToStream(out, " ", &processor);
+ }
+ }
+
+ *out << "}\n";
+ out->flush();
+ return true;
+}
+
+
+
+} // namespace aapt
diff --git a/tools/aapt2/java/JavaClassGenerator.h b/tools/aapt2/java/JavaClassGenerator.h
new file mode 100644
index 0000000..023d6d6
--- /dev/null
+++ b/tools/aapt2/java/JavaClassGenerator.h
@@ -0,0 +1,96 @@
+/*
+ * 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_CLASS_GENERATOR_H
+#define AAPT_JAVA_CLASS_GENERATOR_H
+
+#include "ResourceTable.h"
+#include "ResourceValues.h"
+
+#include "util/StringPiece.h"
+
+#include <ostream>
+#include <string>
+
+namespace aapt {
+
+class AnnotationProcessor;
+class ClassDefinitionWriter;
+
+struct JavaClassGeneratorOptions {
+ /*
+ * Specifies whether to use the 'final' modifier
+ * on resource entries. Default is true.
+ */
+ bool useFinal = true;
+
+ enum class SymbolTypes {
+ kAll,
+ kPublicPrivate,
+ kPublic,
+ };
+
+ SymbolTypes types = SymbolTypes::kAll;
+};
+
+/*
+ * Generates the R.java file for a resource table.
+ */
+class JavaClassGenerator {
+public:
+ JavaClassGenerator(ResourceTable* table, JavaClassGeneratorOptions options);
+
+ /*
+ * Writes the R.java file to `out`. Only symbols belonging to `package` are written.
+ * All symbols technically belong to a single package, but linked libraries will
+ * have their names mangled, denoting that they came from a different package.
+ * We need to generate these symbols in a separate file.
+ * Returns true on success.
+ */
+ bool generate(const StringPiece16& packageNameToGenerate, std::ostream* out);
+
+ bool generate(const StringPiece16& packageNameToGenerate,
+ const StringPiece16& outputPackageName,
+ std::ostream* out);
+
+ const std::string& getError() const;
+
+private:
+ bool writeEntriesForClass(ClassDefinitionWriter* outClassDef,
+ const StringPiece16& packageNameToGenerate,
+ const ResourceTablePackage* package,
+ const ResourceTableType* type);
+
+ void writeStyleableEntryForClass(ClassDefinitionWriter* outClassDef,
+ AnnotationProcessor* processor,
+ const StringPiece16& packageNameToGenerate,
+ const std::u16string& entryName,
+ const Styleable* styleable);
+
+ bool skipSymbol(SymbolState state);
+
+ ResourceTable* mTable;
+ JavaClassGeneratorOptions mOptions;
+ std::string mError;
+};
+
+inline const std::string& JavaClassGenerator::getError() const {
+ return mError;
+}
+
+} // namespace aapt
+
+#endif // AAPT_JAVA_CLASS_GENERATOR_H
diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
new file mode 100644
index 0000000..e9e7881
--- /dev/null
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -0,0 +1,233 @@
+/*
+ * 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.
+ */
+
+#include "java/JavaClassGenerator.h"
+#include "util/Util.h"
+
+#include "test/Builders.h"
+
+#include <gtest/gtest.h>
+#include <sstream>
+#include <string>
+
+namespace aapt {
+
+TEST(JavaClassGeneratorTest, FailWhenEntryIsJavaKeyword) {
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .setPackageId(u"android", 0x01)
+ .addSimple(u"@android:id/class", ResourceId(0x01020000))
+ .build();
+
+ JavaClassGenerator generator(table.get(), {});
+
+ std::stringstream out;
+ EXPECT_FALSE(generator.generate(u"android", &out));
+}
+
+TEST(JavaClassGeneratorTest, TransformInvalidJavaIdentifierCharacter) {
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .setPackageId(u"android", 0x01)
+ .addSimple(u"@android:id/hey-man", ResourceId(0x01020000))
+ .addSimple(u"@android:attr/cool.attr", ResourceId(0x01010000))
+ .addValue(u"@android:styleable/hey.dude", ResourceId(0x01030000),
+ test::StyleableBuilder()
+ .addItem(u"@android:attr/cool.attr", ResourceId(0x01010000))
+ .build())
+ .build();
+
+ JavaClassGenerator generator(table.get(), {});
+
+ std::stringstream out;
+ EXPECT_TRUE(generator.generate(u"android", &out));
+
+ std::string output = out.str();
+
+ EXPECT_NE(std::string::npos,
+ output.find("public static final int hey_man=0x01020000;"));
+
+ EXPECT_NE(std::string::npos,
+ output.find("public static final int[] hey_dude={"));
+
+ EXPECT_NE(std::string::npos,
+ output.find("public static final int hey_dude_cool_attr=0;"));
+}
+
+TEST(JavaClassGeneratorTest, CorrectPackageNameIsUsed) {
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .setPackageId(u"android", 0x01)
+ .addSimple(u"@android:id/one", ResourceId(0x01020000))
+ .addSimple(u"@android:id/com.foo$two", ResourceId(0x01020001))
+ .build();
+
+ JavaClassGenerator generator(table.get(), {});
+ std::stringstream out;
+ ASSERT_TRUE(generator.generate(u"android", u"com.android.internal", &out));
+
+ std::string output = out.str();
+ EXPECT_NE(std::string::npos, output.find("package com.android.internal;"));
+ EXPECT_NE(std::string::npos, output.find("public static final int one=0x01020000;"));
+ EXPECT_EQ(std::string::npos, output.find("two"));
+ EXPECT_EQ(std::string::npos, output.find("com_foo$two"));
+}
+
+TEST(JavaClassGeneratorTest, AttrPrivateIsWrittenAsAttr) {
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .setPackageId(u"android", 0x01)
+ .addSimple(u"@android:attr/two", ResourceId(0x01010001))
+ .addSimple(u"@android:^attr-private/one", ResourceId(0x01010000))
+ .build();
+
+ JavaClassGenerator generator(table.get(), {});
+ std::stringstream out;
+ ASSERT_TRUE(generator.generate(u"android", &out));
+
+ std::string output = out.str();
+ EXPECT_NE(std::string::npos, output.find("public static final class attr"));
+ EXPECT_EQ(std::string::npos, output.find("public static final class ^attr-private"));
+}
+
+TEST(JavaClassGeneratorTest, OnlyWritePublicResources) {
+ StdErrDiagnostics diag;
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .setPackageId(u"android", 0x01)
+ .addSimple(u"@android:id/one", ResourceId(0x01020000))
+ .addSimple(u"@android:id/two", ResourceId(0x01020001))
+ .addSimple(u"@android:id/three", ResourceId(0x01020002))
+ .setSymbolState(u"@android:id/one", ResourceId(0x01020000), SymbolState::kPublic)
+ .setSymbolState(u"@android:id/two", ResourceId(0x01020001), SymbolState::kPrivate)
+ .build();
+
+ JavaClassGeneratorOptions options;
+ options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
+ {
+ JavaClassGenerator generator(table.get(), options);
+ std::stringstream out;
+ ASSERT_TRUE(generator.generate(u"android", &out));
+ std::string output = out.str();
+ EXPECT_NE(std::string::npos, output.find("public static final int one=0x01020000;"));
+ EXPECT_EQ(std::string::npos, output.find("two"));
+ EXPECT_EQ(std::string::npos, output.find("three"));
+ }
+
+ options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
+ {
+ JavaClassGenerator generator(table.get(), options);
+ std::stringstream out;
+ ASSERT_TRUE(generator.generate(u"android", &out));
+ std::string output = out.str();
+ EXPECT_NE(std::string::npos, output.find("public static final int one=0x01020000;"));
+ EXPECT_NE(std::string::npos, output.find("public static final int two=0x01020001;"));
+ EXPECT_EQ(std::string::npos, output.find("three"));
+ }
+
+ options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
+ {
+ JavaClassGenerator generator(table.get(), options);
+ std::stringstream out;
+ ASSERT_TRUE(generator.generate(u"android", &out));
+ std::string output = out.str();
+ EXPECT_NE(std::string::npos, output.find("public static final int one=0x01020000;"));
+ EXPECT_NE(std::string::npos, output.find("public static final int two=0x01020001;"));
+ EXPECT_NE(std::string::npos, output.find("public static final int three=0x01020002;"));
+ }
+}
+
+/*
+ * TODO(adamlesinski): Re-enable this once we get merging working again.
+ * TEST(JavaClassGeneratorTest, EmitPackageMangledSymbols) {
+ ASSERT_TRUE(addResource(ResourceName{ {}, ResourceType::kId, u"foo" },
+ ResourceId{ 0x01, 0x02, 0x0000 }));
+ ResourceTable table;
+ table.setPackage(u"com.lib");
+ ASSERT_TRUE(table.addResource(ResourceName{ {}, ResourceType::kId, u"test" }, {},
+ Source{ "lib.xml", 33 }, util::make_unique<Id>()));
+ ASSERT_TRUE(mTable->merge(std::move(table)));
+
+ Linker linker(mTable,
+ std::make_shared<MockResolver>(mTable, std::map<ResourceName, ResourceId>()),
+ {});
+ ASSERT_TRUE(linker.linkAndValidate());
+
+ JavaClassGenerator generator(mTable, {});
+
+ std::stringstream out;
+ EXPECT_TRUE(generator.generate(mTable->getPackage(), out));
+ std::string output = out.str();
+ EXPECT_NE(std::string::npos, output.find("int foo ="));
+ EXPECT_EQ(std::string::npos, output.find("int test ="));
+
+ out.str("");
+ EXPECT_TRUE(generator.generate(u"com.lib", out));
+ output = out.str();
+ EXPECT_NE(std::string::npos, output.find("int test ="));
+ EXPECT_EQ(std::string::npos, output.find("int foo ="));
+}*/
+
+TEST(JavaClassGeneratorTest, EmitOtherPackagesAttributesInStyleable) {
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .setPackageId(u"android", 0x01)
+ .setPackageId(u"com.lib", 0x02)
+ .addSimple(u"@android:attr/bar", ResourceId(0x01010000))
+ .addSimple(u"@com.lib:attr/bar", ResourceId(0x02010000))
+ .addValue(u"@android:styleable/foo", ResourceId(0x01030000),
+ test::StyleableBuilder()
+ .addItem(u"@android:attr/bar", ResourceId(0x01010000))
+ .addItem(u"@com.lib:attr/bar", ResourceId(0x02010000))
+ .build())
+ .build();
+
+ JavaClassGenerator generator(table.get(), {});
+
+ std::stringstream out;
+ EXPECT_TRUE(generator.generate(u"android", &out));
+
+ std::string output = out.str();
+ EXPECT_NE(std::string::npos, output.find("int foo_bar="));
+ EXPECT_NE(std::string::npos, output.find("int foo_com_lib_bar="));
+}
+
+TEST(JavaClassGeneratorTest, CommentsForSimpleResourcesArePresent) {
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .setPackageId(u"android", 0x01)
+ .addSimple(u"@android:id/foo", ResourceId(0x01010000))
+ .build();
+ test::getValue<Id>(table.get(), u"@android:id/foo")
+ ->setComment(std::u16string(u"This is a comment\n@deprecated"));
+
+ JavaClassGenerator generator(table.get(), {});
+
+ std::stringstream out;
+ ASSERT_TRUE(generator.generate(u"android", &out));
+ std::string actual = out.str();
+
+ EXPECT_NE(std::string::npos, actual.find(
+ R"EOF(/**
+ * This is a comment
+ * @deprecated
+ */
+ @Deprecated
+ public static final int foo=0x01010000;)EOF"));
+}
+
+TEST(JavaClassGeneratorTest, CommentsForEnumAndFlagAttributesArePresent) {
+
+}
+
+TEST(JavaClassGeneratorTest, CommentsForStyleablesAndNestedAttributesArePresent) {
+
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/java/ManifestClassGenerator.cpp b/tools/aapt2/java/ManifestClassGenerator.cpp
new file mode 100644
index 0000000..d963d89
--- /dev/null
+++ b/tools/aapt2/java/ManifestClassGenerator.cpp
@@ -0,0 +1,126 @@
+/*
+ * 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.
+ */
+
+#include "Source.h"
+#include "XmlDom.h"
+
+#include "java/AnnotationProcessor.h"
+#include "java/ClassDefinitionWriter.h"
+#include "java/ManifestClassGenerator.h"
+#include "util/Maybe.h"
+
+#include <algorithm>
+
+namespace aapt {
+
+static Maybe<StringPiece16> extractJavaIdentifier(IDiagnostics* diag, const Source& source,
+ const StringPiece16& value) {
+ const StringPiece16 sep = u".";
+ auto iter = std::find_end(value.begin(), value.end(), sep.begin(), sep.end());
+
+ StringPiece16 result;
+ if (iter != value.end()) {
+ result.assign(iter + sep.size(), value.end() - (iter + sep.size()));
+ } else {
+ result = value;
+ }
+
+ if (result.empty()) {
+ diag->error(DiagMessage(source) << "empty symbol");
+ return {};
+ }
+
+ iter = util::findNonAlphaNumericAndNotInSet(result, u"_");
+ if (iter != result.end()) {
+ diag->error(DiagMessage(source)
+ << "invalid character '" << StringPiece16(iter, 1)
+ << "' in '" << result << "'");
+ return {};
+ }
+
+ if (*result.begin() >= u'0' && *result.begin() <= u'9') {
+ diag->error(DiagMessage(source) << "symbol can not start with a digit");
+ return {};
+ }
+
+ return result;
+}
+
+static bool writeSymbol(IDiagnostics* diag, ClassDefinitionWriter* outClassDef, const Source& source,
+ xml::Element* el) {
+ xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, u"name");
+ if (!attr) {
+ diag->error(DiagMessage(source) << "<" << el->name << "> must define 'android:name'");
+ return false;
+ }
+
+ Maybe<StringPiece16> result = extractJavaIdentifier(diag, source.withLine(el->lineNumber),
+ attr->value);
+ if (!result) {
+ return false;
+ }
+
+ AnnotationProcessor processor;
+ processor.appendComment(el->comment);
+ outClassDef->addStringMember(result.value(), &processor, attr->value);
+ return true;
+}
+
+bool ManifestClassGenerator::generate(IDiagnostics* diag, const StringPiece16& package,
+ XmlResource* res, std::ostream* out) {
+ xml::Element* el = xml::findRootElement(res->root.get());
+ if (!el) {
+ return false;
+ }
+
+ if (el->name != u"manifest" && !el->namespaceUri.empty()) {
+ diag->error(DiagMessage(res->file.source) << "no <manifest> root tag defined");
+ return false;
+ }
+
+ *out << "package " << package << ";\n\n"
+ << "public final class Manifest {\n";
+
+ 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);
+ }
+ }
+ 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);
+ }
+ }
+ classDef.writeToStream(out, " ");
+
+ *out << "}\n";
+ return !error;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/java/ManifestClassGenerator.h b/tools/aapt2/java/ManifestClassGenerator.h
new file mode 100644
index 0000000..0f0998f
--- /dev/null
+++ b/tools/aapt2/java/ManifestClassGenerator.h
@@ -0,0 +1,35 @@
+/*
+ * 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_MANIFESTCLASSGENERATOR_H
+#define AAPT_JAVA_MANIFESTCLASSGENERATOR_H
+
+#include "Diagnostics.h"
+#include "process/IResourceTableConsumer.h"
+#include "util/StringPiece.h"
+
+#include <iostream>
+
+namespace aapt {
+
+struct ManifestClassGenerator {
+ bool generate(IDiagnostics* diag, const StringPiece16& package, XmlResource* res,
+ std::ostream* out);
+};
+
+} // namespace aapt
+
+#endif /* AAPT_JAVA_MANIFESTCLASSGENERATOR_H */
diff --git a/tools/aapt2/java/ManifestClassGenerator_test.cpp b/tools/aapt2/java/ManifestClassGenerator_test.cpp
new file mode 100644
index 0000000..4081287
--- /dev/null
+++ b/tools/aapt2/java/ManifestClassGenerator_test.cpp
@@ -0,0 +1,121 @@
+/*
+ * 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.
+ */
+
+#include "java/ManifestClassGenerator.h"
+
+#include "test/Builders.h"
+#include "test/Context.h"
+
+#include <gtest/gtest.h>
+
+namespace aapt {
+
+TEST(ManifestClassGeneratorTest, NameIsProperlyGeneratedFromSymbol) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+ std::unique_ptr<XmlResource> manifest = test::buildXmlDom(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
+ <permission android:name="android.permission.ACCESS_INTERNET" />
+ <permission android:name="android.DO_DANGEROUS_THINGS" />
+ <permission android:name="com.test.sample.permission.HUH" />
+ <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();
+
+ const size_t permissionClassPos = actual.find("public static final class permission {");
+ const size_t permissionGroupClassPos =
+ actual.find("public static final class permission_group {");
+ ASSERT_NE(std::string::npos, permissionClassPos);
+ ASSERT_NE(std::string::npos, permissionGroupClassPos);
+
+ //
+ // Make sure these permissions are in the permission class.
+ //
+
+ size_t pos = actual.find("public static final String ACCESS_INTERNET="
+ "\"android.permission.ACCESS_INTERNET\";");
+ EXPECT_GT(pos, permissionClassPos);
+ EXPECT_LT(pos, permissionGroupClassPos);
+
+ pos = actual.find("public static final String DO_DANGEROUS_THINGS="
+ "\"android.DO_DANGEROUS_THINGS\";");
+ EXPECT_GT(pos, permissionClassPos);
+ EXPECT_LT(pos, permissionGroupClassPos);
+
+ pos = actual.find("public static final String HUH=\"com.test.sample.permission.HUH\";");
+ EXPECT_GT(pos, permissionClassPos);
+ EXPECT_LT(pos, permissionGroupClassPos);
+
+ //
+ // Make sure these permissions are in the permission_group class
+ //
+
+ pos = actual.find("public static final String PERMISSION="
+ "\"foo.bar.PERMISSION\";");
+ EXPECT_GT(pos, permissionGroupClassPos);
+ EXPECT_LT(pos, std::string::npos);
+}
+
+TEST(ManifestClassGeneratorTest, CommentsAndAnnotationsArePresent) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+ std::unique_ptr<XmlResource> manifest = test::buildXmlDom(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- Required to access the internet.
+ Added in API 1. -->
+ <permission android:name="android.permission.ACCESS_INTERNET" />
+ <!-- @deprecated This permission is for playing outside. -->
+ <permission android:name="android.permission.PLAY_OUTSIDE" />
+ <!-- This is a private permission for system only!
+ @hide
+ @SystemApi -->
+ <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();
+
+ EXPECT_NE(std::string::npos, actual.find(
+R"EOF( /**
+ * Required to access the internet.
+ * Added in API 1.
+ */
+ public static final String ACCESS_INTERNET="android.permission.ACCESS_INTERNET";)EOF"));
+
+ EXPECT_NE(std::string::npos, actual.find(
+R"EOF( /**
+ * @deprecated This permission is for playing outside.
+ */
+ @Deprecated
+ public static final String PLAY_OUTSIDE="android.permission.PLAY_OUTSIDE";)EOF"));
+
+ EXPECT_NE(std::string::npos, actual.find(
+R"EOF( /**
+ * This is a private permission for system only!
+ * @hide
+ * @SystemApi
+ */
+ @android.annotation.SystemApi
+ public static final String SECRET="android.permission.SECRET";)EOF"));
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp
new file mode 100644
index 0000000..4431477
--- /dev/null
+++ b/tools/aapt2/java/ProguardRules.cpp
@@ -0,0 +1,245 @@
+/*
+ * 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.
+ */
+
+#include "XmlDom.h"
+
+#include "java/ProguardRules.h"
+#include "util/Util.h"
+
+#include <memory>
+#include <string>
+
+namespace aapt {
+namespace proguard {
+
+class BaseVisitor : public xml::Visitor {
+public:
+ BaseVisitor(const Source& source, KeepSet* keepSet) : mSource(source), mKeepSet(keepSet) {
+ }
+
+ virtual void visit(xml::Text*) override {};
+
+ virtual void visit(xml::Namespace* node) override {
+ for (const auto& child : node->children) {
+ child->accept(this);
+ }
+ }
+
+ virtual void visit(xml::Element* node) override {
+ if (!node->namespaceUri.empty()) {
+ Maybe<std::u16string> maybePackage = util::extractPackageFromNamespace(
+ node->namespaceUri);
+ if (maybePackage) {
+ // This is a custom view, let's figure out the class name from this.
+ std::u16string package = maybePackage.value() + u"." + node->name;
+ if (util::isJavaClassName(package)) {
+ addClass(node->lineNumber, package);
+ }
+ }
+ } else if (util::isJavaClassName(node->name)) {
+ addClass(node->lineNumber, node->name);
+ }
+
+ for (const auto& child: node->children) {
+ child->accept(this);
+ }
+ }
+
+protected:
+ void addClass(size_t lineNumber, const std::u16string& className) {
+ mKeepSet->addClass(Source(mSource.path, lineNumber), className);
+ }
+
+ void addMethod(size_t lineNumber, const std::u16string& methodName) {
+ mKeepSet->addMethod(Source(mSource.path, lineNumber), methodName);
+ }
+
+private:
+ Source mSource;
+ KeepSet* mKeepSet;
+};
+
+struct LayoutVisitor : public BaseVisitor {
+ LayoutVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
+ }
+
+ virtual void visit(xml::Element* node) override {
+ bool checkClass = false;
+ bool checkName = false;
+ if (node->namespaceUri.empty()) {
+ checkClass = node->name == u"view" || node->name == u"fragment";
+ } else if (node->namespaceUri == xml::kSchemaAndroid) {
+ checkName = node->name == u"fragment";
+ }
+
+ for (const auto& attr : node->attributes) {
+ if (checkClass && attr.namespaceUri.empty() && attr.name == u"class" &&
+ util::isJavaClassName(attr.value)) {
+ addClass(node->lineNumber, attr.value);
+ } else if (checkName && attr.namespaceUri == xml::kSchemaAndroid &&
+ attr.name == u"name" && util::isJavaClassName(attr.value)) {
+ addClass(node->lineNumber, attr.value);
+ } else if (attr.namespaceUri == xml::kSchemaAndroid && attr.name == u"onClick") {
+ addMethod(node->lineNumber, attr.value);
+ }
+ }
+
+ BaseVisitor::visit(node);
+ }
+};
+
+struct XmlResourceVisitor : public BaseVisitor {
+ XmlResourceVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
+ }
+
+ virtual void visit(xml::Element* node) override {
+ bool checkFragment = false;
+ if (node->namespaceUri.empty()) {
+ checkFragment = node->name == u"PreferenceScreen" || node->name == u"header";
+ }
+
+ if (checkFragment) {
+ xml::Attribute* attr = node->findAttribute(xml::kSchemaAndroid, u"fragment");
+ if (attr && util::isJavaClassName(attr->value)) {
+ addClass(node->lineNumber, attr->value);
+ }
+ }
+
+ BaseVisitor::visit(node);
+ }
+};
+
+struct TransitionVisitor : public BaseVisitor {
+ TransitionVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
+ }
+
+ virtual void visit(xml::Element* node) override {
+ bool checkClass = node->namespaceUri.empty() &&
+ (node->name == u"transition" || node->name == u"pathMotion");
+ if (checkClass) {
+ xml::Attribute* attr = node->findAttribute({}, u"class");
+ if (attr && util::isJavaClassName(attr->value)) {
+ addClass(node->lineNumber, attr->value);
+ }
+ }
+
+ BaseVisitor::visit(node);
+ }
+};
+
+struct ManifestVisitor : public BaseVisitor {
+ ManifestVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
+ }
+
+ virtual void visit(xml::Element* node) override {
+ if (node->namespaceUri.empty()) {
+ bool getName = false;
+ if (node->name == u"manifest") {
+ xml::Attribute* attr = node->findAttribute({}, u"package");
+ if (attr) {
+ mPackage = attr->value;
+ }
+ } else if (node->name == u"application") {
+ getName = true;
+ xml::Attribute* attr = node->findAttribute(xml::kSchemaAndroid, u"backupAgent");
+ if (attr) {
+ Maybe<std::u16string> result = util::getFullyQualifiedClassName(mPackage,
+ attr->value);
+ if (result) {
+ addClass(node->lineNumber, result.value());
+ }
+ }
+ } else if (node->name == u"activity" || node->name == u"service" ||
+ node->name == u"receiver" || node->name == u"provider" ||
+ node->name == u"instrumentation") {
+ getName = true;
+ }
+
+ if (getName) {
+ xml::Attribute* attr = node->findAttribute(xml::kSchemaAndroid, u"name");
+ if (attr) {
+ Maybe<std::u16string> result = util::getFullyQualifiedClassName(mPackage,
+ attr->value);
+ if (result) {
+ addClass(node->lineNumber, result.value());
+ }
+ }
+ }
+ }
+ BaseVisitor::visit(node);
+ }
+
+ std::u16string mPackage;
+};
+
+bool collectProguardRulesForManifest(const Source& source, XmlResource* res, KeepSet* keepSet) {
+ ManifestVisitor visitor(source, keepSet);
+ if (res->root) {
+ res->root->accept(&visitor);
+ return true;
+ }
+ return false;
+}
+
+bool collectProguardRules(const Source& source, XmlResource* res, KeepSet* keepSet) {
+ if (!res->root) {
+ return false;
+ }
+
+ switch (res->file.name.type) {
+ case ResourceType::kLayout: {
+ LayoutVisitor visitor(source, keepSet);
+ res->root->accept(&visitor);
+ break;
+ }
+
+ case ResourceType::kXml: {
+ XmlResourceVisitor visitor(source, keepSet);
+ res->root->accept(&visitor);
+ break;
+ }
+
+ case ResourceType::kTransition: {
+ TransitionVisitor visitor(source, keepSet);
+ res->root->accept(&visitor);
+ break;
+ }
+
+ default:
+ break;
+ }
+ return true;
+}
+
+bool writeKeepSet(std::ostream* out, const KeepSet& keepSet) {
+ for (const auto& entry : keepSet.mKeepSet) {
+ for (const Source& source : entry.second) {
+ *out << "// Referenced at " << source << "\n";
+ }
+ *out << "-keep class " << entry.first << " { <init>(...); }\n" << std::endl;
+ }
+
+ for (const auto& entry : keepSet.mKeepMethodSet) {
+ for (const Source& source : entry.second) {
+ *out << "// Referenced at " << source << "\n";
+ }
+ *out << "-keepclassmembers class * { *** " << entry.first << "(...); }\n" << std::endl;
+ }
+ return true;
+}
+
+} // namespace proguard
+} // namespace aapt
diff --git a/tools/aapt2/java/ProguardRules.h b/tools/aapt2/java/ProguardRules.h
new file mode 100644
index 0000000..be61eb9
--- /dev/null
+++ b/tools/aapt2/java/ProguardRules.h
@@ -0,0 +1,58 @@
+/*
+ * 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_PROGUARD_RULES_H
+#define AAPT_PROGUARD_RULES_H
+
+#include "Resource.h"
+#include "Source.h"
+
+#include "process/IResourceTableConsumer.h"
+
+#include <map>
+#include <ostream>
+#include <set>
+#include <string>
+
+namespace aapt {
+namespace proguard {
+
+class KeepSet {
+public:
+ inline void addClass(const Source& source, const std::u16string& className) {
+ mKeepSet[className].insert(source);
+ }
+
+ inline void addMethod(const Source& source, const std::u16string& methodName) {
+ mKeepMethodSet[methodName].insert(source);
+ }
+
+private:
+ friend bool writeKeepSet(std::ostream* out, const KeepSet& keepSet);
+
+ std::map<std::u16string, std::set<Source>> mKeepSet;
+ std::map<std::u16string, std::set<Source>> mKeepMethodSet;
+};
+
+bool collectProguardRulesForManifest(const Source& source, XmlResource* res, KeepSet* keepSet);
+bool collectProguardRules(const Source& source, XmlResource* res, KeepSet* keepSet);
+
+bool writeKeepSet(std::ostream* out, const KeepSet& keepSet);
+
+} // namespace proguard
+} // namespace aapt
+
+#endif // AAPT_PROGUARD_RULES_H
diff --git a/tools/aapt2/link/AutoVersioner.cpp b/tools/aapt2/link/AutoVersioner.cpp
new file mode 100644
index 0000000..c7e603e
--- /dev/null
+++ b/tools/aapt2/link/AutoVersioner.cpp
@@ -0,0 +1,142 @@
+/*
+ * 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.
+ */
+
+#include "ConfigDescription.h"
+#include "ResourceTable.h"
+#include "SdkConstants.h"
+#include "ValueVisitor.h"
+
+#include "link/Linkers.h"
+#include "util/Comparators.h"
+
+#include <algorithm>
+#include <cassert>
+
+namespace aapt {
+
+bool shouldGenerateVersionedResource(const ResourceEntry* entry, const ConfigDescription& config,
+ const int sdkVersionToGenerate) {
+ assert(sdkVersionToGenerate > config.sdkVersion);
+ const auto endIter = entry->values.end();
+ auto iter = std::lower_bound(entry->values.begin(), endIter, config, cmp::lessThanConfig);
+
+ // The source config came from this list, so it should be here.
+ assert(iter != entry->values.end());
+ ++iter;
+
+ // The next configuration either only varies in sdkVersion, or it is completely different
+ // and therefore incompatible. If it is incompatible, we must generate the versioned resource.
+
+ // NOTE: The ordering of configurations takes sdkVersion as higher precedence than other
+ // qualifiers, so we need to iterate through the entire list to be sure there
+ // are no higher sdk level versions of this resource.
+ ConfigDescription tempConfig(config);
+ for (; iter != endIter; ++iter) {
+ tempConfig.sdkVersion = iter->config.sdkVersion;
+ if (tempConfig == iter->config) {
+ // The two configs are the same, check the sdk version.
+ return sdkVersionToGenerate < iter->config.sdkVersion;
+ }
+ }
+
+ // No match was found, so we should generate the versioned resource.
+ return true;
+}
+
+bool AutoVersioner::consume(IAaptContext* context, ResourceTable* table) {
+ for (auto& package : table->packages) {
+ for (auto& type : package->types) {
+ if (type->type != ResourceType::kStyle) {
+ continue;
+ }
+
+ for (auto& entry : type->entries) {
+ for (size_t i = 0; i < entry->values.size(); i++) {
+ ResourceConfigValue& configValue = entry->values[i];
+ if (configValue.config.sdkVersion >= SDK_LOLLIPOP_MR1) {
+ // If this configuration is only used on L-MR1 then we don't need
+ // to do anything since we use private attributes since that version.
+ continue;
+ }
+
+ if (Style* style = valueCast<Style>(configValue.value.get())) {
+ Maybe<size_t> minSdkStripped;
+ std::vector<Style::Entry> stripped;
+
+ auto iter = style->entries.begin();
+ while (iter != style->entries.end()) {
+ assert(iter->key.id && "IDs must be assigned and linked");
+
+ // Find the SDK level that is higher than the configuration allows.
+ const size_t sdkLevel = findAttributeSdkLevel(iter->key.id.value());
+ if (sdkLevel > std::max<size_t>(configValue.config.sdkVersion, 1)) {
+ // Record that we are about to strip this.
+ stripped.emplace_back(std::move(*iter));
+
+ // We use the smallest SDK level to generate the new style.
+ if (minSdkStripped) {
+ minSdkStripped = std::min(minSdkStripped.value(), sdkLevel);
+ } else {
+ minSdkStripped = sdkLevel;
+ }
+
+ // Erase this from this style.
+ iter = style->entries.erase(iter);
+ continue;
+ }
+ ++iter;
+ }
+
+ if (minSdkStripped && !stripped.empty()) {
+ // We found attributes from a higher SDK level. Check that
+ // there is no other defined resource for the version we want to
+ // generate.
+ if (shouldGenerateVersionedResource(entry.get(),
+ configValue.config,
+ minSdkStripped.value())) {
+ // Let's create a new Style for this versioned resource.
+ ConfigDescription newConfig(configValue.config);
+ newConfig.sdkVersion = minSdkStripped.value();
+
+ std::unique_ptr<Style> newStyle(style->clone(&table->stringPool));
+ newStyle->setComment(style->getComment());
+ newStyle->setSource(style->getSource());
+
+ // Move the previously stripped attributes into this style.
+ newStyle->entries.insert(newStyle->entries.end(),
+ std::make_move_iterator(stripped.begin()),
+ std::make_move_iterator(stripped.end()));
+
+ // Insert the new Resource into the correct place.
+ auto iter = std::lower_bound(entry->values.begin(),
+ entry->values.end(),
+ newConfig,
+ cmp::lessThanConfig);
+
+ entry->values.insert(
+ iter,
+ ResourceConfigValue{ newConfig, std::move(newStyle) });
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return true;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/link/AutoVersioner_test.cpp b/tools/aapt2/link/AutoVersioner_test.cpp
new file mode 100644
index 0000000..29bcc93
--- /dev/null
+++ b/tools/aapt2/link/AutoVersioner_test.cpp
@@ -0,0 +1,127 @@
+/*
+ * 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.
+ */
+
+#include "ConfigDescription.h"
+
+#include "link/Linkers.h"
+
+#include "test/Builders.h"
+#include "test/Context.h"
+
+#include <gtest/gtest.h>
+
+namespace aapt {
+
+TEST(AutoVersionerTest, GenerateVersionedResources) {
+ const ConfigDescription defaultConfig = {};
+ const ConfigDescription landConfig = test::parseConfigOrDie("land");
+ const ConfigDescription sw600dpLandConfig = test::parseConfigOrDie("sw600dp-land");
+
+ ResourceEntry entry(u"foo");
+ entry.values.push_back(ResourceConfigValue{ defaultConfig });
+ entry.values.push_back(ResourceConfigValue{ landConfig });
+ entry.values.push_back(ResourceConfigValue{ sw600dpLandConfig });
+
+ EXPECT_TRUE(shouldGenerateVersionedResource(&entry, defaultConfig, 17));
+ EXPECT_TRUE(shouldGenerateVersionedResource(&entry, landConfig, 17));
+}
+
+TEST(AutoVersionerTest, GenerateVersionedResourceWhenHigherVersionExists) {
+ const ConfigDescription defaultConfig = {};
+ const ConfigDescription sw600dpV13Config = test::parseConfigOrDie("sw600dp-v13");
+ const ConfigDescription v21Config = test::parseConfigOrDie("v21");
+
+ ResourceEntry entry(u"foo");
+ entry.values.push_back(ResourceConfigValue{ defaultConfig });
+ entry.values.push_back(ResourceConfigValue{ sw600dpV13Config });
+ entry.values.push_back(ResourceConfigValue{ v21Config });
+
+ EXPECT_TRUE(shouldGenerateVersionedResource(&entry, defaultConfig, 17));
+ EXPECT_FALSE(shouldGenerateVersionedResource(&entry, defaultConfig, 22));
+}
+
+TEST(AutoVersionerTest, VersionStylesForTable) {
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .setPackageId(u"app", 0x7f)
+ .addValue(u"@app:style/Foo", ResourceId(0x7f020000), test::parseConfigOrDie("v4"),
+ test::StyleBuilder()
+ .addItem(u"@android:attr/onClick", ResourceId(0x0101026f),
+ util::make_unique<Id>())
+ .addItem(u"@android:attr/paddingStart", ResourceId(0x010103b3),
+ util::make_unique<Id>())
+ .addItem(u"@android:attr/requiresSmallestWidthDp",
+ ResourceId(0x01010364), util::make_unique<Id>())
+ .addItem(u"@android:attr/colorAccent", ResourceId(0x01010435),
+ util::make_unique<Id>())
+ .build())
+ .addValue(u"@app:style/Foo", ResourceId(0x7f020000), test::parseConfigOrDie("v21"),
+ test::StyleBuilder()
+ .addItem(u"@android:attr/paddingEnd", ResourceId(0x010103b4),
+ util::make_unique<Id>())
+ .build())
+ .build();
+
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder()
+ .setCompilationPackage(u"app")
+ .setPackageId(0x7f)
+ .build();
+
+ AutoVersioner versioner;
+ ASSERT_TRUE(versioner.consume(context.get(), table.get()));
+
+ Style* style = test::getValueForConfig<Style>(table.get(), u"@app:style/Foo",
+ test::parseConfigOrDie("v4"));
+ ASSERT_NE(style, nullptr);
+ ASSERT_EQ(style->entries.size(), 1u);
+ AAPT_ASSERT_TRUE(style->entries.front().key.name);
+ EXPECT_EQ(style->entries.front().key.name.value(),
+ test::parseNameOrDie(u"@android:attr/onClick"));
+
+ style = test::getValueForConfig<Style>(table.get(), u"@app:style/Foo",
+ test::parseConfigOrDie("v13"));
+ ASSERT_NE(style, nullptr);
+ ASSERT_EQ(style->entries.size(), 2u);
+ AAPT_ASSERT_TRUE(style->entries[0].key.name);
+ EXPECT_EQ(style->entries[0].key.name.value(),
+ test::parseNameOrDie(u"@android:attr/onClick"));
+ AAPT_ASSERT_TRUE(style->entries[1].key.name);
+ EXPECT_EQ(style->entries[1].key.name.value(),
+ test::parseNameOrDie(u"@android:attr/requiresSmallestWidthDp"));
+
+ style = test::getValueForConfig<Style>(table.get(), u"@app:style/Foo",
+ test::parseConfigOrDie("v17"));
+ ASSERT_NE(style, nullptr);
+ ASSERT_EQ(style->entries.size(), 3u);
+ AAPT_ASSERT_TRUE(style->entries[0].key.name);
+ EXPECT_EQ(style->entries[0].key.name.value(),
+ test::parseNameOrDie(u"@android:attr/onClick"));
+ AAPT_ASSERT_TRUE(style->entries[1].key.name);
+ EXPECT_EQ(style->entries[1].key.name.value(),
+ test::parseNameOrDie(u"@android:attr/requiresSmallestWidthDp"));
+ AAPT_ASSERT_TRUE(style->entries[2].key.name);
+ EXPECT_EQ(style->entries[2].key.name.value(),
+ test::parseNameOrDie(u"@android:attr/paddingStart"));
+
+ style = test::getValueForConfig<Style>(table.get(), u"@app:style/Foo",
+ test::parseConfigOrDie("v21"));
+ ASSERT_NE(style, nullptr);
+ ASSERT_EQ(style->entries.size(), 1u);
+ AAPT_ASSERT_TRUE(style->entries.front().key.name);
+ EXPECT_EQ(style->entries.front().key.name.value(),
+ test::parseNameOrDie(u"@android:attr/paddingEnd"));
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
new file mode 100644
index 0000000..9ce3734
--- /dev/null
+++ b/tools/aapt2/link/Link.cpp
@@ -0,0 +1,801 @@
+/*
+ * 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.
+ */
+
+#include "AppInfo.h"
+#include "Debug.h"
+#include "Flags.h"
+#include "NameMangler.h"
+#include "XmlDom.h"
+
+#include "compile/IdAssigner.h"
+#include "flatten/Archive.h"
+#include "flatten/TableFlattener.h"
+#include "flatten/XmlFlattener.h"
+#include "java/JavaClassGenerator.h"
+#include "java/ManifestClassGenerator.h"
+#include "java/ProguardRules.h"
+#include "link/Linkers.h"
+#include "link/ManifestFixer.h"
+#include "link/TableMerger.h"
+#include "process/IResourceTableConsumer.h"
+#include "process/SymbolTable.h"
+#include "unflatten/BinaryResourceParser.h"
+#include "unflatten/FileExportHeaderReader.h"
+#include "util/Files.h"
+#include "util/StringPiece.h"
+
+#include <fstream>
+#include <sys/stat.h>
+#include <utils/FileMap.h>
+#include <vector>
+
+namespace aapt {
+
+struct LinkOptions {
+ std::string outputPath;
+ std::string manifestPath;
+ std::vector<std::string> includePaths;
+ Maybe<std::string> generateJavaClassPath;
+ std::vector<std::string> extraJavaPackages;
+ Maybe<std::string> generateProguardRulesPath;
+ bool noAutoVersion = false;
+ bool staticLib = false;
+ bool verbose = false;
+ bool outputToDirectory = false;
+ Maybe<std::u16string> privateSymbols;
+ Maybe<std::u16string> minSdkVersionDefault;
+ Maybe<std::u16string> targetSdkVersionDefault;
+};
+
+struct LinkContext : public IAaptContext {
+ StdErrDiagnostics mDiagnostics;
+ std::unique_ptr<NameMangler> mNameMangler;
+ std::u16string mCompilationPackage;
+ uint8_t mPackageId;
+ std::unique_ptr<ISymbolTable> mSymbols;
+
+ IDiagnostics* getDiagnostics() override {
+ return &mDiagnostics;
+ }
+
+ NameMangler* getNameMangler() override {
+ return mNameMangler.get();
+ }
+
+ StringPiece16 getCompilationPackage() override {
+ return mCompilationPackage;
+ }
+
+ uint8_t getPackageId() override {
+ return mPackageId;
+ }
+
+ ISymbolTable* getExternalSymbols() override {
+ return mSymbols.get();
+ }
+};
+
+struct LinkCommand {
+ LinkOptions mOptions;
+ LinkContext mContext;
+
+ std::string buildResourceFileName(const ResourceFile& resFile) {
+ std::stringstream out;
+ out << "res/" << resFile.name.type;
+ if (resFile.config != ConfigDescription{}) {
+ out << "-" << resFile.config;
+ }
+ out << "/";
+
+ if (mContext.getNameMangler()->shouldMangle(resFile.name.package)) {
+ out << NameMangler::mangleEntry(resFile.name.package, resFile.name.entry);
+ } else {
+ out << resFile.name.entry;
+ }
+ out << file::getExtension(resFile.source.path);
+ return out.str();
+ }
+
+ /**
+ * Creates a SymbolTable that loads symbols from the various APKs and caches the
+ * results for faster lookup.
+ */
+ std::unique_ptr<ISymbolTable> createSymbolTableFromIncludePaths() {
+ AssetManagerSymbolTableBuilder builder;
+ for (const std::string& path : mOptions.includePaths) {
+ if (mOptions.verbose) {
+ mContext.getDiagnostics()->note(
+ DiagMessage(Source{ path }) << "loading include path");
+ }
+
+ std::unique_ptr<android::AssetManager> assetManager =
+ util::make_unique<android::AssetManager>();
+ int32_t cookie = 0;
+ if (!assetManager->addAssetPath(android::String8(path.data(), path.size()), &cookie)) {
+ mContext.getDiagnostics()->error(
+ DiagMessage(Source{ path }) << "failed to load include path");
+ return {};
+ }
+ builder.add(std::move(assetManager));
+ }
+ return builder.build();
+ }
+
+ /**
+ * Loads the resource table (not inside an apk) at the given path.
+ */
+ std::unique_ptr<ResourceTable> loadTable(const std::string& input) {
+ std::string errorStr;
+ Maybe<android::FileMap> map = file::mmapPath(input, &errorStr);
+ if (!map) {
+ mContext.getDiagnostics()->error(DiagMessage(Source{ input }) << errorStr);
+ return {};
+ }
+
+ std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
+ BinaryResourceParser parser(&mContext, table.get(), Source{ input },
+ map.value().getDataPtr(), map.value().getDataLength());
+ if (!parser.parse()) {
+ return {};
+ }
+ return table;
+ }
+
+ /**
+ * Inflates an XML file from the source path.
+ */
+ std::unique_ptr<XmlResource> loadXml(const std::string& path) {
+ std::ifstream fin(path, std::ifstream::binary);
+ if (!fin) {
+ mContext.getDiagnostics()->error(DiagMessage(Source{ path }) << strerror(errno));
+ return {};
+ }
+
+ return xml::inflate(&fin, mContext.getDiagnostics(), Source{ path });
+ }
+
+ /**
+ * Inflates a binary XML file from the source path.
+ */
+ std::unique_ptr<XmlResource> loadBinaryXmlSkipFileExport(const std::string& path) {
+ // Read header for symbol info and export info.
+ std::string errorStr;
+ Maybe<android::FileMap> maybeF = file::mmapPath(path, &errorStr);
+ if (!maybeF) {
+ mContext.getDiagnostics()->error(DiagMessage(path) << errorStr);
+ return {};
+ }
+
+ ssize_t offset = getWrappedDataOffset(maybeF.value().getDataPtr(),
+ maybeF.value().getDataLength(), &errorStr);
+ if (offset < 0) {
+ mContext.getDiagnostics()->error(DiagMessage(path) << errorStr);
+ return {};
+ }
+
+ std::unique_ptr<XmlResource> xmlRes = xml::inflate(
+ (const uint8_t*) maybeF.value().getDataPtr() + (size_t) offset,
+ maybeF.value().getDataLength() - offset,
+ mContext.getDiagnostics(), Source(path));
+ if (!xmlRes) {
+ return {};
+ }
+ return xmlRes;
+ }
+
+ Maybe<ResourceFile> loadFileExportHeader(const std::string& path) {
+ // Read header for symbol info and export info.
+ std::string errorStr;
+ Maybe<android::FileMap> maybeF = file::mmapPath(path, &errorStr);
+ if (!maybeF) {
+ mContext.getDiagnostics()->error(DiagMessage(path) << errorStr);
+ return {};
+ }
+
+ ResourceFile resFile;
+ ssize_t offset = unwrapFileExportHeader(maybeF.value().getDataPtr(),
+ maybeF.value().getDataLength(),
+ &resFile, &errorStr);
+ if (offset < 0) {
+ mContext.getDiagnostics()->error(DiagMessage(path) << errorStr);
+ return {};
+ }
+ return std::move(resFile);
+ }
+
+ bool copyFileToArchive(const std::string& path, const std::string& outPath, uint32_t flags,
+ IArchiveWriter* writer) {
+ std::string errorStr;
+ Maybe<android::FileMap> maybeF = file::mmapPath(path, &errorStr);
+ if (!maybeF) {
+ mContext.getDiagnostics()->error(DiagMessage(path) << errorStr);
+ return false;
+ }
+
+ ssize_t offset = getWrappedDataOffset(maybeF.value().getDataPtr(),
+ maybeF.value().getDataLength(),
+ &errorStr);
+ if (offset < 0) {
+ mContext.getDiagnostics()->error(DiagMessage(path) << errorStr);
+ return false;
+ }
+
+ ArchiveEntry* entry = writer->writeEntry(outPath, flags, &maybeF.value(),
+ offset, maybeF.value().getDataLength() - offset);
+ if (!entry) {
+ mContext.getDiagnostics()->error(
+ DiagMessage(mOptions.outputPath) << "failed to write file " << outPath);
+ return false;
+ }
+ return true;
+ }
+
+ Maybe<AppInfo> extractAppInfoFromManifest(XmlResource* xmlRes) {
+ // Make sure the first element is <manifest> with package attribute.
+ if (xml::Element* manifestEl = xml::findRootElement(xmlRes->root.get())) {
+ if (manifestEl->namespaceUri.empty() && manifestEl->name == u"manifest") {
+ if (xml::Attribute* packageAttr = manifestEl->findAttribute({}, u"package")) {
+ return AppInfo{ packageAttr->value };
+ }
+ }
+ }
+ return {};
+ }
+
+ bool verifyNoExternalPackages(ResourceTable* table) {
+ bool error = false;
+ for (const auto& package : table->packages) {
+ if (mContext.getCompilationPackage() != package->name ||
+ !package->id || package->id.value() != mContext.getPackageId()) {
+ // We have a package that is not related to the one we're building!
+ for (const auto& type : package->types) {
+ for (const auto& entry : type->entries) {
+ for (const auto& configValue : entry->values) {
+ mContext.getDiagnostics()->error(
+ DiagMessage(configValue.value->getSource())
+ << "defined resource '"
+ << ResourceNameRef(package->name,
+ type->type,
+ entry->name)
+ << "' for external package '"
+ << package->name << "'");
+ error = true;
+ }
+ }
+ }
+ }
+ }
+ return !error;
+ }
+
+ std::unique_ptr<IArchiveWriter> makeArchiveWriter() {
+ if (mOptions.outputToDirectory) {
+ return createDirectoryArchiveWriter(mOptions.outputPath);
+ } else {
+ return createZipFileArchiveWriter(mOptions.outputPath);
+ }
+ }
+
+ bool flattenTable(ResourceTable* table, IArchiveWriter* writer) {
+ BigBuffer buffer(1024);
+ TableFlattenerOptions options = {};
+ options.useExtendedChunks = mOptions.staticLib;
+ TableFlattener flattener(&buffer, options);
+ if (!flattener.consume(&mContext, table)) {
+ return false;
+ }
+
+ ArchiveEntry* entry = writer->writeEntry("resources.arsc", ArchiveEntry::kAlign, buffer);
+ if (!entry) {
+ mContext.getDiagnostics()->error(
+ DiagMessage() << "failed to write resources.arsc to archive");
+ return false;
+ }
+ return true;
+ }
+
+ bool flattenXml(XmlResource* xmlRes, const StringPiece& path, Maybe<size_t> maxSdkLevel,
+ IArchiveWriter* writer) {
+ BigBuffer buffer(1024);
+ XmlFlattenerOptions options = {};
+ options.keepRawValues = mOptions.staticLib;
+ options.maxSdkLevel = maxSdkLevel;
+ XmlFlattener flattener(&buffer, options);
+ if (!flattener.consume(&mContext, xmlRes)) {
+ return false;
+ }
+
+ ArchiveEntry* entry = writer->writeEntry(path, ArchiveEntry::kCompress, buffer);
+ if (!entry) {
+ mContext.getDiagnostics()->error(
+ DiagMessage() << "failed to write " << path << " to archive");
+ return false;
+ }
+ return true;
+ }
+
+ bool writeJavaFile(ResourceTable* table, const StringPiece16& packageNameToGenerate,
+ const StringPiece16& outPackage, JavaClassGeneratorOptions javaOptions) {
+ if (!mOptions.generateJavaClassPath) {
+ return true;
+ }
+
+ std::string outPath = mOptions.generateJavaClassPath.value();
+ file::appendPath(&outPath, file::packageToPath(util::utf16ToUtf8(outPackage)));
+ file::mkdirs(outPath);
+ file::appendPath(&outPath, "R.java");
+
+ std::ofstream fout(outPath, std::ofstream::binary);
+ if (!fout) {
+ mContext.getDiagnostics()->error(DiagMessage() << strerror(errno));
+ return false;
+ }
+
+ JavaClassGenerator generator(table, javaOptions);
+ if (!generator.generate(packageNameToGenerate, outPackage, &fout)) {
+ mContext.getDiagnostics()->error(DiagMessage(outPath) << generator.getError());
+ return false;
+ }
+ return true;
+ }
+
+ bool writeManifestJavaFile(XmlResource* manifestXml) {
+ if (!mOptions.generateJavaClassPath) {
+ return true;
+ }
+
+ std::string outPath = mOptions.generateJavaClassPath.value();
+ file::appendPath(&outPath,
+ file::packageToPath(util::utf16ToUtf8(mContext.getCompilationPackage())));
+ file::mkdirs(outPath);
+ file::appendPath(&outPath, "Manifest.java");
+
+ std::ofstream fout(outPath, std::ofstream::binary);
+ if (!fout) {
+ mContext.getDiagnostics()->error(DiagMessage() << strerror(errno));
+ return false;
+ }
+
+ ManifestClassGenerator generator;
+ if (!generator.generate(mContext.getDiagnostics(), mContext.getCompilationPackage(),
+ manifestXml, &fout)) {
+ return false;
+ }
+
+ if (!fout) {
+ mContext.getDiagnostics()->error(DiagMessage() << strerror(errno));
+ return false;
+ }
+ return true;
+ }
+
+ bool writeProguardFile(const proguard::KeepSet& keepSet) {
+ if (!mOptions.generateProguardRulesPath) {
+ return true;
+ }
+
+ std::ofstream fout(mOptions.generateProguardRulesPath.value(), std::ofstream::binary);
+ if (!fout) {
+ mContext.getDiagnostics()->error(DiagMessage() << strerror(errno));
+ return false;
+ }
+
+ proguard::writeKeepSet(&fout, keepSet);
+ if (!fout) {
+ mContext.getDiagnostics()->error(DiagMessage() << strerror(errno));
+ return false;
+ }
+ return true;
+ }
+
+ int run(const std::vector<std::string>& inputFiles) {
+ // Load the AndroidManifest.xml
+ std::unique_ptr<XmlResource> manifestXml = loadXml(mOptions.manifestPath);
+ if (!manifestXml) {
+ return 1;
+ }
+
+ if (Maybe<AppInfo> maybeAppInfo = extractAppInfoFromManifest(manifestXml.get())) {
+ mContext.mCompilationPackage = maybeAppInfo.value().package;
+ } else {
+ mContext.getDiagnostics()->error(DiagMessage(mOptions.manifestPath)
+ << "no package specified in <manifest> tag");
+ return 1;
+ }
+
+ if (!util::isJavaPackageName(mContext.mCompilationPackage)) {
+ mContext.getDiagnostics()->error(DiagMessage(mOptions.manifestPath)
+ << "invalid package name '"
+ << mContext.mCompilationPackage
+ << "'");
+ return 1;
+ }
+
+ mContext.mNameMangler = util::make_unique<NameMangler>(
+ NameManglerPolicy{ mContext.mCompilationPackage });
+
+ if (mContext.mCompilationPackage == u"android") {
+ mContext.mPackageId = 0x01;
+ } else {
+ mContext.mPackageId = 0x7f;
+ }
+
+ mContext.mSymbols = createSymbolTableFromIncludePaths();
+ if (!mContext.mSymbols) {
+ return 1;
+ }
+
+ if (mOptions.verbose) {
+ mContext.getDiagnostics()->note(
+ DiagMessage() << "linking package '" << mContext.mCompilationPackage << "' "
+ << "with package ID " << std::hex << (int) mContext.mPackageId);
+ }
+
+ ResourceTable mergedTable;
+ TableMerger tableMerger(&mContext, &mergedTable);
+
+ struct FilesToProcess {
+ Source source;
+ ResourceFile file;
+ };
+
+ bool error = false;
+ std::queue<FilesToProcess> filesToProcess;
+ for (const std::string& input : inputFiles) {
+ if (util::stringEndsWith<char>(input, ".apk")) {
+ // TODO(adamlesinski): Load resources from a static library APK
+ // Merge the table into TableMerger.
+
+ } else if (util::stringEndsWith<char>(input, ".arsc.flat")) {
+ if (mOptions.verbose) {
+ mContext.getDiagnostics()->note(DiagMessage() << "linking " << input);
+ }
+
+ std::unique_ptr<ResourceTable> table = loadTable(input);
+ if (!table) {
+ return 1;
+ }
+
+ if (!tableMerger.merge(Source(input), table.get())) {
+ return 1;
+ }
+
+ } else {
+ // Extract the exported IDs here so we can build the resource table.
+ if (Maybe<ResourceFile> maybeF = loadFileExportHeader(input)) {
+ ResourceFile& f = maybeF.value();
+
+ if (f.name.package.empty()) {
+ f.name.package = mContext.getCompilationPackage().toString();
+ }
+
+ Maybe<ResourceName> mangledName = mContext.getNameMangler()->mangleName(f.name);
+
+ // Add this file to the table.
+ if (!mergedTable.addFileReference(mangledName ? mangledName.value() : f.name,
+ f.config, f.source,
+ util::utf8ToUtf16(buildResourceFileName(f)),
+ mContext.getDiagnostics())) {
+ error = true;
+ }
+
+ // Add the exports of this file to the table.
+ for (SourcedResourceName& exportedSymbol : f.exportedSymbols) {
+ if (exportedSymbol.name.package.empty()) {
+ exportedSymbol.name.package = mContext.getCompilationPackage()
+ .toString();
+ }
+
+ Maybe<ResourceName> mangledName = mContext.getNameMangler()->mangleName(
+ exportedSymbol.name);
+ std::unique_ptr<Id> id = util::make_unique<Id>();
+ id->setSource(f.source.withLine(exportedSymbol.line));
+ if (!mergedTable.addResourceAllowMangled(
+ mangledName ? mangledName.value() : exportedSymbol.name,
+ {}, std::move(id), mContext.getDiagnostics())) {
+ error = true;
+ }
+ }
+
+ filesToProcess.push(FilesToProcess{ Source(input), std::move(f) });
+ } else {
+ return 1;
+ }
+ }
+ }
+
+ if (error) {
+ mContext.getDiagnostics()->error(DiagMessage() << "failed parsing input");
+ return 1;
+ }
+
+ if (!verifyNoExternalPackages(&mergedTable)) {
+ return 1;
+ }
+
+ if (!mOptions.staticLib) {
+ PrivateAttributeMover mover;
+ if (!mover.consume(&mContext, &mergedTable)) {
+ mContext.getDiagnostics()->error(
+ DiagMessage() << "failed moving private attributes");
+ return 1;
+ }
+ }
+
+ {
+ IdAssigner idAssigner;
+ if (!idAssigner.consume(&mContext, &mergedTable)) {
+ mContext.getDiagnostics()->error(DiagMessage() << "failed assigning IDs");
+ return 1;
+ }
+ }
+
+ mContext.mNameMangler = util::make_unique<NameMangler>(
+ NameManglerPolicy{ mContext.mCompilationPackage, tableMerger.getMergedPackages() });
+ mContext.mSymbols = JoinedSymbolTableBuilder()
+ .addSymbolTable(util::make_unique<SymbolTableWrapper>(&mergedTable))
+ .addSymbolTable(std::move(mContext.mSymbols))
+ .build();
+
+ {
+ ReferenceLinker linker;
+ if (!linker.consume(&mContext, &mergedTable)) {
+ mContext.getDiagnostics()->error(DiagMessage() << "failed linking references");
+ return 1;
+ }
+ }
+
+ proguard::KeepSet proguardKeepSet;
+
+ std::unique_ptr<IArchiveWriter> archiveWriter = makeArchiveWriter();
+ if (!archiveWriter) {
+ mContext.getDiagnostics()->error(DiagMessage() << "failed to create archive");
+ return 1;
+ }
+
+ {
+ ManifestFixerOptions manifestFixerOptions;
+ manifestFixerOptions.minSdkVersionDefault = mOptions.minSdkVersionDefault;
+ manifestFixerOptions.targetSdkVersionDefault = mOptions.targetSdkVersionDefault;
+ ManifestFixer manifestFixer(manifestFixerOptions);
+ if (!manifestFixer.consume(&mContext, manifestXml.get())) {
+ error = true;
+ }
+
+ XmlReferenceLinker manifestLinker;
+ if (manifestLinker.consume(&mContext, manifestXml.get())) {
+ if (!proguard::collectProguardRulesForManifest(Source(mOptions.manifestPath),
+ manifestXml.get(),
+ &proguardKeepSet)) {
+ error = true;
+ }
+
+ if (mOptions.generateJavaClassPath) {
+ if (!writeManifestJavaFile(manifestXml.get())) {
+ error = true;
+ }
+ }
+
+ if (!flattenXml(manifestXml.get(), "AndroidManifest.xml", {},
+ archiveWriter.get())) {
+ error = true;
+ }
+ } else {
+ error = true;
+ }
+ }
+
+ for (; !filesToProcess.empty(); filesToProcess.pop()) {
+ FilesToProcess& f = filesToProcess.front();
+ if (f.file.name.type != ResourceType::kRaw &&
+ util::stringEndsWith<char>(f.source.path, ".xml.flat")) {
+ if (mOptions.verbose) {
+ mContext.getDiagnostics()->note(DiagMessage() << "linking " << f.source.path);
+ }
+
+ std::unique_ptr<XmlResource> xmlRes = loadBinaryXmlSkipFileExport(f.source.path);
+ if (!xmlRes) {
+ return 1;
+ }
+
+ xmlRes->file = std::move(f.file);
+
+ XmlReferenceLinker xmlLinker;
+ if (xmlLinker.consume(&mContext, xmlRes.get())) {
+ if (!proguard::collectProguardRules(xmlRes->file.source, xmlRes.get(),
+ &proguardKeepSet)) {
+ error = true;
+ }
+
+ Maybe<size_t> maxSdkLevel;
+ if (!mOptions.noAutoVersion) {
+ maxSdkLevel = std::max<size_t>(xmlRes->file.config.sdkVersion, 1u);
+ }
+
+ if (!flattenXml(xmlRes.get(), buildResourceFileName(xmlRes->file), maxSdkLevel,
+ archiveWriter.get())) {
+ error = true;
+ }
+
+ if (!mOptions.noAutoVersion) {
+ Maybe<ResourceTable::SearchResult> result = mergedTable.findResource(
+ xmlRes->file.name);
+ for (int sdkLevel : xmlLinker.getSdkLevels()) {
+ if (sdkLevel > xmlRes->file.config.sdkVersion &&
+ shouldGenerateVersionedResource(result.value().entry,
+ xmlRes->file.config,
+ sdkLevel)) {
+ xmlRes->file.config.sdkVersion = sdkLevel;
+ if (!mergedTable.addFileReference(xmlRes->file.name,
+ xmlRes->file.config,
+ xmlRes->file.source,
+ util::utf8ToUtf16(
+ buildResourceFileName(xmlRes->file)),
+ mContext.getDiagnostics())) {
+ error = true;
+ continue;
+ }
+
+ if (!flattenXml(xmlRes.get(), buildResourceFileName(xmlRes->file),
+ sdkLevel, archiveWriter.get())) {
+ error = true;
+ }
+ }
+ }
+ }
+
+ } else {
+ error = true;
+ }
+ } else {
+ if (mOptions.verbose) {
+ mContext.getDiagnostics()->note(DiagMessage() << "copying " << f.source.path);
+ }
+
+ if (!copyFileToArchive(f.source.path, buildResourceFileName(f.file), 0,
+ archiveWriter.get())) {
+ error = true;
+ }
+ }
+ }
+
+ if (error) {
+ mContext.getDiagnostics()->error(DiagMessage() << "failed linking file resources");
+ return 1;
+ }
+
+ if (!mOptions.noAutoVersion) {
+ AutoVersioner versioner;
+ if (!versioner.consume(&mContext, &mergedTable)) {
+ mContext.getDiagnostics()->error(DiagMessage() << "failed versioning styles");
+ return 1;
+ }
+ }
+
+ if (!flattenTable(&mergedTable, archiveWriter.get())) {
+ mContext.getDiagnostics()->error(DiagMessage() << "failed to write resources.arsc");
+ return 1;
+ }
+
+ if (mOptions.generateJavaClassPath) {
+ JavaClassGeneratorOptions options;
+ if (mOptions.staticLib) {
+ options.useFinal = false;
+ }
+
+ StringPiece16 actualPackage = mContext.getCompilationPackage();
+ StringPiece16 outputPackage = mContext.getCompilationPackage();
+
+ if (mOptions.privateSymbols) {
+ // If we defined a private symbols package, we only emit Public symbols
+ // to the original package, and private and public symbols to the private package.
+
+ options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
+ if (!writeJavaFile(&mergedTable, mContext.getCompilationPackage(),
+ mContext.getCompilationPackage(), options)) {
+ return 1;
+ }
+
+ options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
+ outputPackage = mOptions.privateSymbols.value();
+ }
+
+ if (!writeJavaFile(&mergedTable, actualPackage, outputPackage, options)) {
+ return 1;
+ }
+
+ for (std::string& extraPackage : mOptions.extraJavaPackages) {
+ if (!writeJavaFile(&mergedTable, actualPackage, util::utf8ToUtf16(extraPackage),
+ options)) {
+ return 1;
+ }
+ }
+ }
+
+ if (mOptions.generateProguardRulesPath) {
+ if (!writeProguardFile(proguardKeepSet)) {
+ return 1;
+ }
+ }
+
+ if (mOptions.verbose) {
+ Debug::printTable(&mergedTable);
+ for (; !tableMerger.getFileMergeQueue()->empty();
+ tableMerger.getFileMergeQueue()->pop()) {
+ const FileToMerge& f = tableMerger.getFileMergeQueue()->front();
+ mContext.getDiagnostics()->note(
+ DiagMessage() << f.srcPath << " -> " << f.dstPath << " from (0x"
+ << std::hex << (uintptr_t) f.srcTable << std::dec);
+ }
+ }
+
+ return 0;
+ }
+};
+
+int link(const std::vector<StringPiece>& args) {
+ LinkOptions options;
+ Maybe<std::string> privateSymbolsPackage;
+ Maybe<std::string> minSdkVersion, targetSdkVersion;
+ Flags flags = Flags()
+ .requiredFlag("-o", "Output path", &options.outputPath)
+ .requiredFlag("--manifest", "Path to the Android manifest to build",
+ &options.manifestPath)
+ .optionalFlagList("-I", "Adds an Android APK to link against", &options.includePaths)
+ .optionalFlag("--java", "Directory in which to generate R.java",
+ &options.generateJavaClassPath)
+ .optionalFlag("--proguard", "Output file for generated Proguard rules",
+ &options.generateProguardRulesPath)
+ .optionalSwitch("--no-auto-version",
+ "Disables automatic style and layout SDK versioning",
+ &options.noAutoVersion)
+ .optionalSwitch("--output-to-dir", "Outputs the APK contents to a directory specified "
+ "by -o",
+ &options.outputToDirectory)
+ .optionalFlag("--min-sdk-version", "Default minimum SDK version to use for "
+ "AndroidManifest.xml", &minSdkVersion)
+ .optionalFlag("--target-sdk-version", "Default target SDK version to use for "
+ "AndroidManifest.xml", &targetSdkVersion)
+ .optionalSwitch("--static-lib", "Generate a static Android library", &options.staticLib)
+ .optionalFlag("--private-symbols", "Package name to use when generating R.java for "
+ "private symbols.\n"
+ "If not specified, public and private symbols will use the application's "
+ "package name", &privateSymbolsPackage)
+ .optionalFlagList("--extra-packages", "Generate the same R.java but with different "
+ "package names", &options.extraJavaPackages)
+ .optionalSwitch("-v", "Enables verbose logging", &options.verbose);
+
+ if (!flags.parse("aapt2 link", args, &std::cerr)) {
+ return 1;
+ }
+
+ if (privateSymbolsPackage) {
+ options.privateSymbols = util::utf8ToUtf16(privateSymbolsPackage.value());
+ }
+
+ if (minSdkVersion) {
+ options.minSdkVersionDefault = util::utf8ToUtf16(minSdkVersion.value());
+ }
+
+ if (targetSdkVersion) {
+ options.targetSdkVersionDefault = util::utf8ToUtf16(targetSdkVersion.value());
+ }
+
+ LinkCommand cmd = { options };
+ return cmd.run(flags.getArgs());
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/link/Linkers.h b/tools/aapt2/link/Linkers.h
new file mode 100644
index 0000000..7b3fc35
--- /dev/null
+++ b/tools/aapt2/link/Linkers.h
@@ -0,0 +1,102 @@
+/*
+ * 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_LINKER_LINKERS_H
+#define AAPT_LINKER_LINKERS_H
+
+#include "process/IResourceTableConsumer.h"
+
+#include <set>
+
+namespace aapt {
+
+class ResourceTable;
+struct ResourceEntry;
+struct ConfigDescription;
+
+/**
+ * Determines whether a versioned resource should be created. If a versioned resource already
+ * exists, it takes precedence.
+ */
+bool shouldGenerateVersionedResource(const ResourceEntry* entry, const ConfigDescription& config,
+ const int sdkVersionToGenerate);
+
+struct AutoVersioner : public IResourceTableConsumer {
+ bool consume(IAaptContext* context, ResourceTable* table) override;
+};
+
+struct XmlAutoVersioner : public IXmlResourceConsumer {
+ bool consume(IAaptContext* context, XmlResource* resource) override;
+};
+
+/**
+ * If any attribute resource values are defined as public, this consumer will move all private
+ * attribute resource values to a private ^private-attr type, avoiding backwards compatibility
+ * issues with new apps running on old platforms.
+ *
+ * The Android platform ignores resource attributes it doesn't recognize, so an app developer can
+ * use new attributes in their layout XML files without worrying about versioning. This assumption
+ * actually breaks on older platforms. OEMs may add private attributes that are used internally.
+ * AAPT originally assigned all private attributes IDs immediately proceeding the public attributes'
+ * IDs.
+ *
+ * This means that on a newer Android platform, an ID previously assigned to a private attribute
+ * may end up assigned to a public attribute.
+ *
+ * App developers assume using the newer attribute is safe on older platforms because it will
+ * be ignored. Instead, the platform thinks the new attribute is an older, private attribute and
+ * will interpret it as such. This leads to unintended styling and exceptions thrown due to
+ * unexpected types.
+ *
+ * By moving the private attributes to a completely different type, this ID conflict will never
+ * occur.
+ */
+struct PrivateAttributeMover : public IResourceTableConsumer {
+ bool consume(IAaptContext* context, ResourceTable* table) override;
+};
+
+/**
+ * Resolves all references to resources in the ResourceTable and assigns them IDs.
+ * The ResourceTable must already have IDs assigned to each resource.
+ * Once the ResourceTable is processed by this linker, it is ready to be flattened.
+ */
+struct ReferenceLinker : public IResourceTableConsumer {
+ bool consume(IAaptContext* context, ResourceTable* table) override;
+};
+
+/**
+ * Resolves attributes in the XmlResource and compiles string values to resource values.
+ * Once an XmlResource is processed by this linker, it is ready to be flattened.
+ */
+class XmlReferenceLinker : public IXmlResourceConsumer {
+private:
+ std::set<int> mSdkLevelsFound;
+
+public:
+ bool consume(IAaptContext* context, XmlResource* resource) override;
+
+ /**
+ * Once the XmlResource has been consumed, this returns the various SDK levels in which
+ * framework attributes used within the XML document were defined.
+ */
+ inline const std::set<int>& getSdkLevels() const {
+ return mSdkLevelsFound;
+ }
+};
+
+} // namespace aapt
+
+#endif /* AAPT_LINKER_LINKERS_H */
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
new file mode 100644
index 0000000..52d9426
--- /dev/null
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ */
+
+#include "ResourceUtils.h"
+#include "XmlDom.h"
+
+#include "link/ManifestFixer.h"
+#include "util/Util.h"
+
+namespace aapt {
+
+static bool verifyManifest(IAaptContext* context, const Source& source, xml::Element* manifestEl) {
+ bool error = false;
+
+ xml::Attribute* attr = manifestEl->findAttribute({}, u"package");
+ if (!attr) {
+ context->getDiagnostics()->error(DiagMessage(source.withLine(manifestEl->lineNumber))
+ << "missing 'package' attribute");
+ error = true;
+ } else if (ResourceUtils::isReference(attr->value)) {
+ context->getDiagnostics()->error(DiagMessage(source.withLine(manifestEl->lineNumber))
+ << "value for attribute 'package' must not be a "
+ "reference");
+ error = true;
+ } else if (!util::isJavaPackageName(attr->value)) {
+ context->getDiagnostics()->error(DiagMessage(source.withLine(manifestEl->lineNumber))
+ << "invalid package name '" << attr->value << "'");
+ error = true;
+ }
+
+ return !error;
+}
+
+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 (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() });
+ }
+ return true;
+}
+
+bool ManifestFixer::consume(IAaptContext* context, XmlResource* doc) {
+ xml::Element* root = xml::findRootElement(doc->root.get());
+ if (!root || !root->namespaceUri.empty() || root->name != u"manifest") {
+ context->getDiagnostics()->error(DiagMessage(doc->file.source)
+ << "root tag must be <manifest>");
+ return false;
+ }
+
+ if (!verifyManifest(context, doc->file.source, 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)) {
+ 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));
+ }
+
+ return true;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/link/ManifestFixer.h b/tools/aapt2/link/ManifestFixer.h
new file mode 100644
index 0000000..16e161d
--- /dev/null
+++ b/tools/aapt2/link/ManifestFixer.h
@@ -0,0 +1,44 @@
+/*
+ * 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_LINK_MANIFESTFIXER_H
+#define AAPT_LINK_MANIFESTFIXER_H
+
+#include "process/IResourceTableConsumer.h"
+
+namespace aapt {
+
+struct ManifestFixerOptions {
+ Maybe<std::u16string> minSdkVersionDefault;
+ Maybe<std::u16string> targetSdkVersionDefault;
+};
+
+/**
+ * Verifies that the manifest is correctly formed and inserts defaults
+ * where specified with ManifestFixerOptions.
+ */
+struct ManifestFixer : public IXmlResourceConsumer {
+ ManifestFixerOptions mOptions;
+
+ ManifestFixer(const ManifestFixerOptions& options) : mOptions(options) {
+ }
+
+ bool consume(IAaptContext* context, XmlResource* doc) override;
+};
+
+} // namespace aapt
+
+#endif /* AAPT_LINK_MANIFESTFIXER_H */
diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp
new file mode 100644
index 0000000..5c5d8af
--- /dev/null
+++ b/tools/aapt2/link/ManifestFixer_test.cpp
@@ -0,0 +1,165 @@
+/*
+ * 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.
+ */
+
+#include "link/ManifestFixer.h"
+
+#include "test/Builders.h"
+#include "test/Context.h"
+
+#include <gtest/gtest.h>
+
+namespace aapt {
+
+struct ManifestFixerTest : public ::testing::Test {
+ std::unique_ptr<IAaptContext> mContext;
+
+ void SetUp() override {
+ mContext = test::ContextBuilder()
+ .setCompilationPackage(u"android")
+ .setPackageId(0x01)
+ .setNameManglerPolicy(NameManglerPolicy{ u"android" })
+ .setSymbolTable(test::StaticSymbolTableBuilder()
+ .addSymbol(u"@android:attr/package", ResourceId(0x01010000),
+ test::AttributeBuilder()
+ .setTypeMask(android::ResTable_map::TYPE_STRING)
+ .build())
+ .addSymbol(u"@android:attr/minSdkVersion", ResourceId(0x01010001),
+ test::AttributeBuilder()
+ .setTypeMask(android::ResTable_map::TYPE_STRING |
+ android::ResTable_map::TYPE_INTEGER)
+ .build())
+ .addSymbol(u"@android:attr/targetSdkVersion", ResourceId(0x01010002),
+ test::AttributeBuilder()
+ .setTypeMask(android::ResTable_map::TYPE_STRING |
+ android::ResTable_map::TYPE_INTEGER)
+ .build())
+ .addSymbol(u"@android:string/str", ResourceId(0x01060000))
+ .build())
+ .build();
+ }
+
+ std::unique_ptr<XmlResource> verify(const StringPiece& str) {
+ return verifyWithOptions(str, {});
+ }
+
+ std::unique_ptr<XmlResource> verifyWithOptions(const StringPiece& str,
+ const ManifestFixerOptions& options) {
+ std::unique_ptr<XmlResource> doc = test::buildXmlDom(str);
+ ManifestFixer fixer(options);
+ if (fixer.consume(mContext.get(), doc.get())) {
+ return doc;
+ }
+ return {};
+ }
+};
+
+TEST_F(ManifestFixerTest, EnsureManifestIsRootTag) {
+ EXPECT_EQ(nullptr, verify("<other-tag />"));
+ EXPECT_EQ(nullptr, verify("<ns:manifest xmlns:ns=\"com\" />"));
+ EXPECT_NE(nullptr, verify("<manifest package=\"android\"></manifest>"));
+}
+
+TEST_F(ManifestFixerTest, EnsureManifestHasPackage) {
+ EXPECT_NE(nullptr, verify("<manifest package=\"android\" />"));
+ EXPECT_NE(nullptr, verify("<manifest package=\"com.android\" />"));
+ EXPECT_NE(nullptr, verify("<manifest package=\"com.android.google\" />"));
+ EXPECT_EQ(nullptr, verify("<manifest package=\"com.android.google.Class$1\" />"));
+ EXPECT_EQ(nullptr,
+ verify("<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" "
+ "android:package=\"com.android\" />"));
+ EXPECT_EQ(nullptr, verify("<manifest package=\"@string/str\" />"));
+}
+
+
+
+TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) {
+ ManifestFixerOptions options = { std::u16string(u"8"), std::u16string(u"22") };
+
+ std::unique_ptr<XmlResource> doc = verifyWithOptions(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android">
+ <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="21" />
+ </manifest>)EOF", options);
+ ASSERT_NE(nullptr, doc);
+
+ xml::Element* el;
+ xml::Attribute* attr;
+
+ el = xml::findRootElement(doc->root.get());
+ ASSERT_NE(nullptr, el);
+ el = el->findChild({}, u"uses-sdk");
+ ASSERT_NE(nullptr, el);
+ attr = el->findAttribute(xml::kSchemaAndroid, u"minSdkVersion");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(u"7", attr->value);
+ attr = el->findAttribute(xml::kSchemaAndroid, u"targetSdkVersion");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(u"21", attr->value);
+
+ doc = verifyWithOptions(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android">
+ <uses-sdk android:targetSdkVersion="21" />
+ </manifest>)EOF", options);
+ ASSERT_NE(nullptr, doc);
+
+ el = xml::findRootElement(doc->root.get());
+ ASSERT_NE(nullptr, el);
+ el = el->findChild({}, u"uses-sdk");
+ ASSERT_NE(nullptr, el);
+ attr = el->findAttribute(xml::kSchemaAndroid, u"minSdkVersion");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(u"8", attr->value);
+ attr = el->findAttribute(xml::kSchemaAndroid, u"targetSdkVersion");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(u"21", attr->value);
+
+ doc = verifyWithOptions(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android">
+ <uses-sdk />
+ </manifest>)EOF", options);
+ ASSERT_NE(nullptr, doc);
+
+ el = xml::findRootElement(doc->root.get());
+ ASSERT_NE(nullptr, el);
+ el = el->findChild({}, u"uses-sdk");
+ ASSERT_NE(nullptr, el);
+ attr = el->findAttribute(xml::kSchemaAndroid, u"minSdkVersion");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(u"8", attr->value);
+ attr = el->findAttribute(xml::kSchemaAndroid, u"targetSdkVersion");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(u"22", attr->value);
+
+ doc = verifyWithOptions(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android" />)EOF", options);
+ ASSERT_NE(nullptr, doc);
+
+ el = xml::findRootElement(doc->root.get());
+ ASSERT_NE(nullptr, el);
+ el = el->findChild({}, u"uses-sdk");
+ ASSERT_NE(nullptr, el);
+ attr = el->findAttribute(xml::kSchemaAndroid, u"minSdkVersion");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(u"8", attr->value);
+ attr = el->findAttribute(xml::kSchemaAndroid, u"targetSdkVersion");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(u"22", attr->value);
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/link/PrivateAttributeMover.cpp b/tools/aapt2/link/PrivateAttributeMover.cpp
new file mode 100644
index 0000000..5a2f5f0
--- /dev/null
+++ b/tools/aapt2/link/PrivateAttributeMover.cpp
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+
+#include "ResourceTable.h"
+
+#include "link/Linkers.h"
+
+#include <algorithm>
+#include <iterator>
+
+namespace aapt {
+
+template <typename InputContainer, typename OutputIterator, typename Predicate>
+OutputIterator moveIf(InputContainer& inputContainer, OutputIterator result,
+ Predicate pred) {
+ const auto last = inputContainer.end();
+ auto newEnd = std::find_if(inputContainer.begin(), inputContainer.end(), pred);
+ if (newEnd == last) {
+ return result;
+ }
+
+ *result = std::move(*newEnd);
+
+ auto first = newEnd;
+ ++first;
+
+ for (; first != last; ++first) {
+ if (bool(pred(*first))) {
+ // We want to move this guy
+ *result = std::move(*first);
+ ++result;
+ } else {
+ // We want to keep this guy, but we will need to move it up the list to replace
+ // missing items.
+ *newEnd = std::move(*first);
+ ++newEnd;
+ }
+ }
+
+ inputContainer.erase(newEnd, last);
+ return result;
+}
+
+bool PrivateAttributeMover::consume(IAaptContext* context, ResourceTable* table) {
+ for (auto& package : table->packages) {
+ ResourceTableType* type = package->findType(ResourceType::kAttr);
+ if (!type) {
+ continue;
+ }
+
+ if (type->symbolStatus.state != SymbolState::kPublic) {
+ // No public attributes, so we can safely leave these private attributes where they are.
+ return true;
+ }
+
+ ResourceTableType* privAttrType = package->findOrCreateType(ResourceType::kAttrPrivate);
+ assert(privAttrType->entries.empty());
+
+ moveIf(type->entries, std::back_inserter(privAttrType->entries),
+ [](const std::unique_ptr<ResourceEntry>& entry) -> bool {
+ return entry->symbolStatus.state != SymbolState::kPublic;
+ });
+ break;
+ }
+ return true;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/link/PrivateAttributeMover_test.cpp b/tools/aapt2/link/PrivateAttributeMover_test.cpp
new file mode 100644
index 0000000..dbe0c92
--- /dev/null
+++ b/tools/aapt2/link/PrivateAttributeMover_test.cpp
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+#include "link/Linkers.h"
+#include "test/Builders.h"
+#include "test/Context.h"
+
+#include <gtest/gtest.h>
+
+namespace aapt {
+
+TEST(PrivateAttributeMoverTest, MovePrivateAttributes) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .addSimple(u"@android:attr/publicA")
+ .addSimple(u"@android:attr/privateA")
+ .addSimple(u"@android:attr/publicB")
+ .addSimple(u"@android:attr/privateB")
+ .setSymbolState(u"@android:attr/publicA", ResourceId(0x01010000), SymbolState::kPublic)
+ .setSymbolState(u"@android:attr/publicB", ResourceId(0x01010000), SymbolState::kPublic)
+ .build();
+
+ PrivateAttributeMover mover;
+ ASSERT_TRUE(mover.consume(context.get(), table.get()));
+
+ ResourceTablePackage* package = table->findPackage(u"android");
+ ASSERT_NE(package, nullptr);
+
+ ResourceTableType* type = package->findType(ResourceType::kAttr);
+ ASSERT_NE(type, nullptr);
+ ASSERT_EQ(type->entries.size(), 2u);
+ EXPECT_NE(type->findEntry(u"publicA"), nullptr);
+ EXPECT_NE(type->findEntry(u"publicB"), nullptr);
+
+ type = package->findType(ResourceType::kAttrPrivate);
+ ASSERT_NE(type, nullptr);
+ ASSERT_EQ(type->entries.size(), 2u);
+ EXPECT_NE(type->findEntry(u"privateA"), nullptr);
+ EXPECT_NE(type->findEntry(u"privateB"), nullptr);
+}
+
+TEST(PrivateAttributeMoverTest, LeavePrivateAttributesWhenNoPublicAttributesDefined) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .addSimple(u"@android:attr/privateA")
+ .addSimple(u"@android:attr/privateB")
+ .build();
+
+ PrivateAttributeMover mover;
+ ASSERT_TRUE(mover.consume(context.get(), table.get()));
+
+ ResourceTablePackage* package = table->findPackage(u"android");
+ ASSERT_NE(package, nullptr);
+
+ ResourceTableType* type = package->findType(ResourceType::kAttr);
+ ASSERT_NE(type, nullptr);
+ ASSERT_EQ(type->entries.size(), 2u);
+
+ type = package->findType(ResourceType::kAttrPrivate);
+ ASSERT_EQ(type, nullptr);
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
new file mode 100644
index 0000000..8c924b5
--- /dev/null
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -0,0 +1,277 @@
+/*
+ * 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.
+ */
+
+#include "Diagnostics.h"
+#include "ResourceTable.h"
+#include "ResourceUtils.h"
+#include "ResourceValues.h"
+#include "util/Util.h"
+#include "ValueVisitor.h"
+
+#include "link/Linkers.h"
+#include "link/ReferenceLinkerVisitor.h"
+#include "process/IResourceTableConsumer.h"
+#include "process/SymbolTable.h"
+
+#include <androidfw/ResourceTypes.h>
+#include <cassert>
+
+namespace aapt {
+
+namespace {
+
+/**
+ * The ReferenceLinkerVisitor will follow all references and make sure they point
+ * to resources that actually exist, either in the local resource table, or as external
+ * symbols. Once the target resource has been found, the ID of the resource will be assigned
+ * to the reference object.
+ *
+ * NOTE: All of the entries in the ResourceTable must be assigned IDs.
+ */
+class StyleAndReferenceLinkerVisitor : public ValueVisitor {
+private:
+ ReferenceLinkerVisitor mReferenceVisitor;
+ IAaptContext* mContext;
+ ISymbolTable* mSymbols;
+ IPackageDeclStack* mPackageDecls;
+ StringPool* mStringPool;
+ bool mError = false;
+
+ const ISymbolTable::Symbol* findAttributeSymbol(Reference* reference) {
+ assert(reference);
+ assert(reference->name || reference->id);
+
+ if (reference->name) {
+ // Transform the package name if it is an alias.
+ Maybe<ResourceName> realName = mPackageDecls->transformPackage(
+ reference->name.value(), mContext->getCompilationPackage());
+
+ // Mangle the reference name if it should be mangled.
+ Maybe<ResourceName> mangledName = mContext->getNameMangler()->mangleName(
+ realName ? realName.value() : reference->name.value());
+
+ const ISymbolTable::Symbol* s = nullptr;
+ if (mangledName) {
+ s = mSymbols->findByName(mangledName.value());
+ } else if (realName) {
+ s = mSymbols->findByName(realName.value());
+ } else {
+ s = mSymbols->findByName(reference->name.value());
+ }
+
+ if (s && s->attribute) {
+ return s;
+ }
+ }
+
+ if (reference->id) {
+ if (const ISymbolTable::Symbol* s = mSymbols->findById(reference->id.value())) {
+ if (s->attribute) {
+ return s;
+ }
+ }
+ }
+ return nullptr;
+ }
+
+ /**
+ * Transform a RawString value into a more specific, appropriate value, based on the
+ * Attribute. If a non RawString value is passed in, this is an identity transform.
+ */
+ std::unique_ptr<Item> parseValueWithAttribute(std::unique_ptr<Item> value,
+ const Attribute* attr) {
+ if (RawString* rawString = valueCast<RawString>(value.get())) {
+ std::unique_ptr<Item> transformed = ResourceUtils::parseItemForAttribute(
+ *rawString->value, attr);
+
+ // If we could not parse as any specific type, try a basic STRING.
+ if (!transformed && (attr->typeMask & android::ResTable_map::TYPE_STRING)) {
+ util::StringBuilder stringBuilder;
+ stringBuilder.append(*rawString->value);
+ if (stringBuilder) {
+ transformed = util::make_unique<String>(
+ mStringPool->makeRef(stringBuilder.str()));
+ }
+ }
+
+ if (transformed) {
+ return transformed;
+ }
+ };
+ return value;
+ }
+
+ void buildAttributeMismatchMessage(DiagMessage* msg, const Attribute* attr,
+ const Item* value) {
+ *msg << "expected";
+ if (attr->typeMask & android::ResTable_map::TYPE_BOOLEAN) {
+ *msg << " boolean";
+ }
+
+ if (attr->typeMask & android::ResTable_map::TYPE_COLOR) {
+ *msg << " color";
+ }
+
+ if (attr->typeMask & android::ResTable_map::TYPE_DIMENSION) {
+ *msg << " dimension";
+ }
+
+ if (attr->typeMask & android::ResTable_map::TYPE_ENUM) {
+ *msg << " enum";
+ }
+
+ if (attr->typeMask & android::ResTable_map::TYPE_FLAGS) {
+ *msg << " flags";
+ }
+
+ if (attr->typeMask & android::ResTable_map::TYPE_FLOAT) {
+ *msg << " float";
+ }
+
+ if (attr->typeMask & android::ResTable_map::TYPE_FRACTION) {
+ *msg << " fraction";
+ }
+
+ if (attr->typeMask & android::ResTable_map::TYPE_INTEGER) {
+ *msg << " integer";
+ }
+
+ if (attr->typeMask & android::ResTable_map::TYPE_REFERENCE) {
+ *msg << " reference";
+ }
+
+ if (attr->typeMask & android::ResTable_map::TYPE_STRING) {
+ *msg << " string";
+ }
+
+ *msg << " but got " << *value;
+ }
+
+public:
+ using ValueVisitor::visit;
+
+ StyleAndReferenceLinkerVisitor(IAaptContext* context, ISymbolTable* symbols,
+ StringPool* stringPool, IPackageDeclStack* decl) :
+ mReferenceVisitor(context, symbols, decl), mContext(context), mSymbols(symbols),
+ mPackageDecls(decl), mStringPool(stringPool) {
+ }
+
+ void visit(Reference* reference) override {
+ mReferenceVisitor.visit(reference);
+ }
+
+ /**
+ * We visit the Style specially because during this phase, values of attributes are
+ * all RawString values. Now that we are expected to resolve all symbols, we can
+ * lookup the attributes to find out which types are allowed for the attributes' values.
+ */
+ void visit(Style* style) override {
+ if (style->parent) {
+ visit(&style->parent.value());
+ }
+
+ for (Style::Entry& entry : style->entries) {
+ if (const ISymbolTable::Symbol* s = findAttributeSymbol(&entry.key)) {
+ // Assign our style key the correct ID.
+ entry.key.id = s->id;
+
+ // Try to convert the value to a more specific, typed value based on the
+ // attribute it is set to.
+ entry.value = parseValueWithAttribute(std::move(entry.value), s->attribute.get());
+
+ // Link/resolve the final value (mostly if it's a reference).
+ entry.value->accept(this);
+
+ // Now verify that the type of this item is compatible with the attribute it
+ // is defined for.
+ android::Res_value val = {};
+ entry.value->flatten(&val);
+
+ // Always allow references.
+ const uint32_t typeMask = s->attribute->typeMask |
+ android::ResTable_map::TYPE_REFERENCE;
+
+ if (!(typeMask & ResourceUtils::androidTypeToAttributeTypeMask(val.dataType))) {
+ // The actual type of this item is incompatible with the attribute.
+ DiagMessage msg(style->getSource());
+ buildAttributeMismatchMessage(&msg, s->attribute.get(), entry.value.get());
+ mContext->getDiagnostics()->error(msg);
+ mError = true;
+ }
+ } else {
+ DiagMessage msg(style->getSource());
+ msg << "style attribute '";
+ if (entry.key.name) {
+ msg << entry.key.name.value().package << ":" << entry.key.name.value().entry;
+ } else {
+ msg << entry.key.id.value();
+ }
+ msg << "' not found";
+ mContext->getDiagnostics()->error(msg);
+ mError = true;
+ }
+ }
+ }
+
+ inline bool hasError() {
+ return mError || mReferenceVisitor.hasError();
+ }
+};
+
+struct EmptyDeclStack : public IPackageDeclStack {
+ Maybe<ResourceName> transformPackage(const ResourceName& name,
+ const StringPiece16& localPackage) const override {
+ if (name.package.empty()) {
+ return ResourceName{ localPackage.toString(), name.type, name.entry };
+ }
+ return {};
+ }
+};
+
+} // namespace
+
+bool ReferenceLinker::consume(IAaptContext* context, ResourceTable* table) {
+ EmptyDeclStack declStack;
+ bool error = false;
+ for (auto& package : table->packages) {
+ for (auto& type : package->types) {
+ for (auto& entry : type->entries) {
+ // Symbol state information may be lost if there is no value for the resource.
+ if (entry->symbolStatus.state != SymbolState::kUndefined && entry->values.empty()) {
+ context->getDiagnostics()->error(
+ DiagMessage(entry->symbolStatus.source)
+ << "no definition for declared symbol '"
+ << ResourceNameRef(package->name, type->type, entry->name)
+ << "'");
+ error = true;
+ }
+
+ for (auto& configValue : entry->values) {
+ StyleAndReferenceLinkerVisitor visitor(context,
+ context->getExternalSymbols(),
+ &table->stringPool, &declStack);
+ configValue.value->accept(&visitor);
+ if (visitor.hasError()) {
+ error = true;
+ }
+ }
+ }
+ }
+ }
+ return !error;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/link/ReferenceLinkerVisitor.h b/tools/aapt2/link/ReferenceLinkerVisitor.h
new file mode 100644
index 0000000..c70531b
--- /dev/null
+++ b/tools/aapt2/link/ReferenceLinkerVisitor.h
@@ -0,0 +1,109 @@
+/*
+ * 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_LINKER_REFERENCELINKERVISITOR_H
+#define AAPT_LINKER_REFERENCELINKERVISITOR_H
+
+#include "Resource.h"
+#include "ResourceValues.h"
+#include "ValueVisitor.h"
+
+#include "process/IResourceTableConsumer.h"
+#include "process/SymbolTable.h"
+
+#include <cassert>
+
+namespace aapt {
+
+/**
+ * The ReferenceLinkerVisitor will follow all references and make sure they point
+ * to resources that actually exist in the given ISymbolTable.
+ * Once the target resource has been found, the ID of the resource will be assigned
+ * to the reference object.
+ */
+class ReferenceLinkerVisitor : public ValueVisitor {
+ using ValueVisitor::visit;
+private:
+ IAaptContext* mContext;
+ ISymbolTable* mSymbols;
+ IPackageDeclStack* mPackageDecls;
+ bool mError = false;
+
+public:
+ ReferenceLinkerVisitor(IAaptContext* context, ISymbolTable* symbols, IPackageDeclStack* decls) :
+ mContext(context), mSymbols(symbols), mPackageDecls(decls) {
+ }
+
+ /**
+ * Lookup a reference and ensure it exists, either in our local table, or as an external
+ * symbol. Once found, assign the ID of the target resource to this reference object.
+ */
+ void visit(Reference* reference) override {
+ assert(reference);
+ assert(reference->name || reference->id);
+
+ // We prefer to lookup by name if the name is set. Otherwise it could be
+ // an out-of-date ID.
+ if (reference->name) {
+ // Transform the package name if it is an alias.
+ Maybe<ResourceName> realName = mPackageDecls->transformPackage(
+ reference->name.value(), mContext->getCompilationPackage());
+
+ // Mangle the reference name if it should be mangled.
+ Maybe<ResourceName> mangledName = mContext->getNameMangler()->mangleName(
+ realName ? realName.value() : reference->name.value());
+
+ const ISymbolTable::Symbol* s = nullptr;
+ if (mangledName) {
+ s = mSymbols->findByName(mangledName.value());
+ } else if (realName) {
+ s = mSymbols->findByName(realName.value());
+ } else {
+ s = mSymbols->findByName(reference->name.value());
+ }
+
+ if (s) {
+ reference->id = s->id;
+ return;
+ }
+
+ DiagMessage errorMsg;
+ errorMsg << "reference to " << reference->name.value();
+ if (realName) {
+ errorMsg << " (aka " << realName.value() << ")";
+ }
+ errorMsg << " was not found";
+ mContext->getDiagnostics()->error(errorMsg);
+ mError = true;
+ return;
+ }
+
+ if (!mSymbols->findById(reference->id.value())) {
+ mContext->getDiagnostics()->error(DiagMessage()
+ << "reference to " << reference->id.value()
+ << " was not found");
+ mError = true;
+ }
+ }
+
+ inline bool hasError() {
+ return mError;
+ }
+};
+
+} // namespace aapt
+
+#endif /* AAPT_LINKER_REFERENCELINKERVISITOR_H */
diff --git a/tools/aapt2/link/ReferenceLinker_test.cpp b/tools/aapt2/link/ReferenceLinker_test.cpp
new file mode 100644
index 0000000..5e7641a
--- /dev/null
+++ b/tools/aapt2/link/ReferenceLinker_test.cpp
@@ -0,0 +1,159 @@
+/*
+ * 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.
+ */
+
+#include "link/Linkers.h"
+#include "process/SymbolTable.h"
+
+#include "test/Builders.h"
+#include "test/Context.h"
+
+#include <gtest/gtest.h>
+
+namespace aapt {
+
+TEST(ReferenceLinkerTest, LinkSimpleReferences) {
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .setPackageId(u"com.app.test", 0x7f)
+ .addReference(u"@com.app.test:string/foo", ResourceId(0x7f020000),
+ u"@com.app.test:string/bar")
+
+ // Test use of local reference (w/o package name).
+ .addReference(u"@com.app.test:string/bar", ResourceId(0x7f020001), u"@string/baz")
+
+ .addReference(u"@com.app.test:string/baz", ResourceId(0x7f020002),
+ u"@android:string/ok")
+ .build();
+
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder()
+ .setCompilationPackage(u"com.app.test")
+ .setPackageId(0x7f)
+ .setNameManglerPolicy(NameManglerPolicy{ u"com.app.test" })
+ .setSymbolTable(JoinedSymbolTableBuilder()
+ .addSymbolTable(util::make_unique<SymbolTableWrapper>(table.get()))
+ .addSymbolTable(test::StaticSymbolTableBuilder()
+ .addSymbol(u"@android:string/ok", ResourceId(0x01040034))
+ .build())
+ .build())
+ .build();
+
+ ReferenceLinker linker;
+ ASSERT_TRUE(linker.consume(context.get(), table.get()));
+
+ Reference* ref = test::getValue<Reference>(table.get(), u"@com.app.test:string/foo");
+ ASSERT_NE(ref, nullptr);
+ AAPT_ASSERT_TRUE(ref->id);
+ EXPECT_EQ(ref->id.value(), ResourceId(0x7f020001));
+
+ ref = test::getValue<Reference>(table.get(), u"@com.app.test:string/bar");
+ ASSERT_NE(ref, nullptr);
+ AAPT_ASSERT_TRUE(ref->id);
+ EXPECT_EQ(ref->id.value(), ResourceId(0x7f020002));
+
+ ref = test::getValue<Reference>(table.get(), u"@com.app.test:string/baz");
+ ASSERT_NE(ref, nullptr);
+ AAPT_ASSERT_TRUE(ref->id);
+ EXPECT_EQ(ref->id.value(), ResourceId(0x01040034));
+}
+
+TEST(ReferenceLinkerTest, LinkStyleAttributes) {
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .setPackageId(u"com.app.test", 0x7f)
+ .addValue(u"@com.app.test:style/Theme", test::StyleBuilder()
+ .setParent(u"@android:style/Theme.Material")
+ .addItem(u"@android:attr/foo", ResourceUtils::tryParseColor(u"#ff00ff"))
+ .addItem(u"@android:attr/bar", {} /* placeholder */)
+ .build())
+ .build();
+
+ {
+ // We need to fill in the value for the attribute android:attr/bar after we build the
+ // table, because we need access to the string pool.
+ Style* style = test::getValue<Style>(table.get(), u"@com.app.test:style/Theme");
+ ASSERT_NE(style, nullptr);
+ style->entries.back().value = util::make_unique<RawString>(
+ table->stringPool.makeRef(u"one|two"));
+ }
+
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder()
+ .setCompilationPackage(u"com.app.test")
+ .setPackageId(0x7f)
+ .setNameManglerPolicy(NameManglerPolicy{ u"com.app.test" })
+ .setSymbolTable(test::StaticSymbolTableBuilder()
+ .addSymbol(u"@android:style/Theme.Material", ResourceId(0x01060000))
+ .addSymbol(u"@android:attr/foo", ResourceId(0x01010001),
+ test::AttributeBuilder()
+ .setTypeMask(android::ResTable_map::TYPE_COLOR)
+ .build())
+ .addSymbol(u"@android:attr/bar", ResourceId(0x01010002),
+ test::AttributeBuilder()
+ .setTypeMask(android::ResTable_map::TYPE_FLAGS)
+ .addItem(u"one", 0x01)
+ .addItem(u"two", 0x02)
+ .build())
+ .build())
+ .build();
+
+ ReferenceLinker linker;
+ ASSERT_TRUE(linker.consume(context.get(), table.get()));
+
+ Style* style = test::getValue<Style>(table.get(), u"@com.app.test:style/Theme");
+ ASSERT_NE(style, nullptr);
+ AAPT_ASSERT_TRUE(style->parent);
+ AAPT_ASSERT_TRUE(style->parent.value().id);
+ EXPECT_EQ(style->parent.value().id.value(), ResourceId(0x01060000));
+
+ ASSERT_EQ(2u, style->entries.size());
+
+ AAPT_ASSERT_TRUE(style->entries[0].key.id);
+ EXPECT_EQ(style->entries[0].key.id.value(), ResourceId(0x01010001));
+ ASSERT_NE(valueCast<BinaryPrimitive>(style->entries[0].value.get()), nullptr);
+
+ AAPT_ASSERT_TRUE(style->entries[1].key.id);
+ EXPECT_EQ(style->entries[1].key.id.value(), ResourceId(0x01010002));
+ ASSERT_NE(valueCast<BinaryPrimitive>(style->entries[1].value.get()), nullptr);
+}
+
+TEST(ReferenceLinkerTest, LinkMangledReferencesAndAttributes) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder()
+ .setCompilationPackage(u"com.app.test")
+ .setPackageId(0x7f)
+ .setNameManglerPolicy(NameManglerPolicy{ u"com.app.test", { u"com.android.support" } })
+ .setSymbolTable(test::StaticSymbolTableBuilder()
+ .addSymbol(u"@com.app.test:attr/com.android.support$foo",
+ ResourceId(0x7f010000), test::AttributeBuilder()
+ .setTypeMask(android::ResTable_map::TYPE_COLOR).build())
+ .build())
+ .build();
+
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .setPackageId(u"com.app.test", 0x7f)
+ .addValue(u"@com.app.test:style/Theme", ResourceId(0x7f020000),
+ test::StyleBuilder().addItem(u"@com.android.support:attr/foo",
+ ResourceUtils::tryParseColor(u"#ff0000"))
+ .build())
+ .build();
+
+ ReferenceLinker linker;
+ ASSERT_TRUE(linker.consume(context.get(), table.get()));
+
+ Style* style = test::getValue<Style>(table.get(), u"@com.app.test:style/Theme");
+ ASSERT_NE(style, nullptr);
+ ASSERT_EQ(1u, style->entries.size());
+ AAPT_ASSERT_TRUE(style->entries.front().key.id);
+ EXPECT_EQ(style->entries.front().key.id.value(), ResourceId(0x7f010000));
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
new file mode 100644
index 0000000..1eea410
--- /dev/null
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -0,0 +1,212 @@
+/*
+ * 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.
+ */
+
+#include "ResourceTable.h"
+#include "ResourceValues.h"
+#include "ValueVisitor.h"
+
+#include "link/TableMerger.h"
+#include "util/Comparators.h"
+#include "util/Util.h"
+
+#include <cassert>
+
+namespace aapt {
+
+TableMerger::TableMerger(IAaptContext* context, ResourceTable* outTable) :
+ mContext(context), mMasterTable(outTable) {
+ // Create the desired package that all tables will be merged into.
+ mMasterPackage = mMasterTable->createPackage(
+ mContext->getCompilationPackage(), mContext->getPackageId());
+ assert(mMasterPackage && "package name or ID already taken");
+}
+
+/**
+ * This will merge packages with the same package name (or no package name).
+ */
+bool TableMerger::merge(const Source& src, ResourceTable* table) {
+ const uint8_t desiredPackageId = mContext->getPackageId();
+
+ bool error = false;
+ for (auto& package : table->packages) {
+ // Warn of packages with an unrelated ID.
+ if (package->id && package->id.value() != 0x0 && package->id.value() != desiredPackageId) {
+ mContext->getDiagnostics()->warn(DiagMessage(src)
+ << "ignoring package " << package->name);
+ continue;
+ }
+
+ if (package->name.empty() || mContext->getCompilationPackage() == package->name) {
+ // Merge here. Once the entries are merged and mangled, any references to
+ // them are still valid. This is because un-mangled references are
+ // mangled, then looked up at resolution time.
+ // Also, when linking, we convert references with no package name to use
+ // the compilation package name.
+ if (!doMerge(src, table, package.get(), false)) {
+ error = true;
+ }
+ }
+ }
+ return !error;
+}
+
+/**
+ * This will merge and mangle resources from a static library.
+ */
+bool TableMerger::mergeAndMangle(const Source& src, const StringPiece16& packageName,
+ ResourceTable* table) {
+ bool error = false;
+ for (auto& package : table->packages) {
+ // Warn of packages with an unrelated ID.
+ if (packageName != package->name) {
+ mContext->getDiagnostics()->warn(DiagMessage(src)
+ << "ignoring package " << package->name);
+ continue;
+ }
+
+ bool mangle = packageName != mContext->getCompilationPackage();
+ mMergedPackages.insert(package->name);
+ if (!doMerge(src, table, package.get(), mangle)) {
+ error = true;
+ }
+ }
+ return !error;
+}
+
+bool TableMerger::doMerge(const Source& src, ResourceTable* srcTable,
+ ResourceTablePackage* srcPackage, const bool manglePackage) {
+ bool error = false;
+
+ for (auto& srcType : srcPackage->types) {
+ ResourceTableType* dstType = mMasterPackage->findOrCreateType(srcType->type);
+ if (srcType->symbolStatus.state == SymbolState::kPublic) {
+ if (dstType->symbolStatus.state == SymbolState::kPublic && dstType->id && srcType->id
+ && dstType->id.value() == srcType->id.value()) {
+ // Both types are public and have different IDs.
+ mContext->getDiagnostics()->error(DiagMessage(src)
+ << "can not merge type '"
+ << srcType->type
+ << "': conflicting public IDs");
+ error = true;
+ continue;
+ }
+
+ dstType->symbolStatus = std::move(srcType->symbolStatus);
+ dstType->id = srcType->id;
+ }
+
+ for (auto& srcEntry : srcType->entries) {
+ ResourceEntry* dstEntry;
+ if (manglePackage) {
+ dstEntry = dstType->findOrCreateEntry(NameMangler::mangleEntry(
+ srcPackage->name, srcEntry->name));
+ } else {
+ dstEntry = dstType->findOrCreateEntry(srcEntry->name);
+ }
+
+ if (srcEntry->symbolStatus.state != SymbolState::kUndefined) {
+ if (srcEntry->symbolStatus.state == SymbolState::kPublic) {
+ if (dstEntry->symbolStatus.state == SymbolState::kPublic &&
+ dstEntry->id && srcEntry->id &&
+ dstEntry->id.value() != srcEntry->id.value()) {
+ // Both entries are public and have different IDs.
+ mContext->getDiagnostics()->error(DiagMessage(src)
+ << "can not merge entry '"
+ << srcEntry->name
+ << "': conflicting public IDs");
+ error = true;
+ continue;
+ }
+
+ if (srcEntry->id) {
+ dstEntry->id = srcEntry->id;
+ }
+ }
+
+ if (dstEntry->symbolStatus.state != SymbolState::kPublic &&
+ dstEntry->symbolStatus.state != srcEntry->symbolStatus.state) {
+ dstEntry->symbolStatus = std::move(srcEntry->symbolStatus);
+ }
+ }
+
+ for (ResourceConfigValue& srcValue : srcEntry->values) {
+ auto iter = std::lower_bound(dstEntry->values.begin(), dstEntry->values.end(),
+ srcValue.config, cmp::lessThanConfig);
+
+ if (iter != dstEntry->values.end() && iter->config == srcValue.config) {
+ const int collisionResult = ResourceTable::resolveValueCollision(
+ iter->value.get(), srcValue.value.get());
+ if (collisionResult == 0) {
+ // Error!
+ ResourceNameRef resourceName(srcPackage->name,
+ srcType->type,
+ srcEntry->name);
+
+ mContext->getDiagnostics()->error(DiagMessage(srcValue.value->getSource())
+ << "resource '" << resourceName
+ << "' has a conflicting value for "
+ << "configuration ("
+ << srcValue.config << ")");
+ mContext->getDiagnostics()->note(DiagMessage(iter->value->getSource())
+ << "originally defined here");
+ error = true;
+ continue;
+ } else if (collisionResult < 0) {
+ // Keep our existing value.
+ continue;
+ }
+
+ } else {
+ // Insert a place holder value. We will fill it in below.
+ iter = dstEntry->values.insert(iter, ResourceConfigValue{ srcValue.config });
+ }
+
+ if (manglePackage) {
+ iter->value = cloneAndMangle(srcTable, srcPackage->name, srcValue.value.get());
+ } else {
+ iter->value = clone(srcValue.value.get());
+ }
+ }
+ }
+ }
+ return !error;
+}
+
+std::unique_ptr<Value> TableMerger::cloneAndMangle(ResourceTable* table,
+ const std::u16string& package,
+ Value* value) {
+ if (FileReference* f = valueCast<FileReference>(value)) {
+ // Mangle the path.
+ StringPiece16 prefix, entry, suffix;
+ if (util::extractResFilePathParts(*f->path, &prefix, &entry, &suffix)) {
+ std::u16string mangledEntry = NameMangler::mangleEntry(package, entry.toString());
+ std::u16string newPath = prefix.toString() + mangledEntry + suffix.toString();
+ mFilesToMerge.push(FileToMerge{ table, *f->path, newPath });
+ std::unique_ptr<FileReference> fileRef = util::make_unique<FileReference>(
+ mMasterTable->stringPool.makeRef(newPath));
+ fileRef->setComment(f->getComment());
+ fileRef->setSource(f->getSource());
+ return std::move(fileRef);
+ }
+ }
+ return clone(value);
+}
+
+std::unique_ptr<Value> TableMerger::clone(Value* value) {
+ return std::unique_ptr<Value>(value->clone(&mMasterTable->stringPool));
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/link/TableMerger.h b/tools/aapt2/link/TableMerger.h
new file mode 100644
index 0000000..c903f1b
--- /dev/null
+++ b/tools/aapt2/link/TableMerger.h
@@ -0,0 +1,91 @@
+/*
+ * 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_TABLEMERGER_H
+#define AAPT_TABLEMERGER_H
+
+#include "ResourceTable.h"
+#include "ResourceValues.h"
+#include "util/Util.h"
+
+#include "process/IResourceTableConsumer.h"
+
+#include <queue>
+#include <set>
+
+namespace aapt {
+
+struct FileToMerge {
+ ResourceTable* srcTable;
+ std::u16string srcPath;
+ std::u16string dstPath;
+};
+
+/**
+ * TableMerger takes resource tables and merges all packages within the tables that have the same
+ * package ID.
+ *
+ * If a package has a different name, all the entries in that table have their names mangled
+ * to include the package name. This way there are no collisions. In order to do this correctly,
+ * the TableMerger needs to also mangle any FileReference paths. Once these are mangled,
+ * the original source path of the file, along with the new destination path is recorded in the
+ * queue returned from getFileMergeQueue().
+ *
+ * Once the merging is complete, a separate process can go collect the files from the various
+ * source APKs and either copy or process their XML and put them in the correct location in
+ * the final APK.
+ */
+class TableMerger {
+public:
+ TableMerger(IAaptContext* context, ResourceTable* outTable);
+
+ inline std::queue<FileToMerge>* getFileMergeQueue() {
+ return &mFilesToMerge;
+ }
+
+ inline const std::set<std::u16string>& getMergedPackages() const {
+ return mMergedPackages;
+ }
+
+ /**
+ * Merges resources from the same or empty package. This is for local sources.
+ */
+ bool merge(const Source& src, ResourceTable* table);
+
+ /**
+ * Merges resources from the given package, mangling the name. This is for static libraries.
+ */
+ bool mergeAndMangle(const Source& src, const StringPiece16& package, ResourceTable* table);
+
+private:
+ IAaptContext* mContext;
+ ResourceTable* mMasterTable;
+ ResourceTablePackage* mMasterPackage;
+
+ std::set<std::u16string> mMergedPackages;
+ std::queue<FileToMerge> mFilesToMerge;
+
+ bool doMerge(const Source& src, ResourceTable* srcTable, ResourceTablePackage* srcPackage,
+ const bool manglePackage);
+
+ std::unique_ptr<Value> cloneAndMangle(ResourceTable* table, const std::u16string& package,
+ Value* value);
+ std::unique_ptr<Value> clone(Value* value);
+};
+
+} // namespace aapt
+
+#endif /* AAPT_TABLEMERGER_H */
diff --git a/tools/aapt2/link/TableMerger_test.cpp b/tools/aapt2/link/TableMerger_test.cpp
new file mode 100644
index 0000000..0af4314
--- /dev/null
+++ b/tools/aapt2/link/TableMerger_test.cpp
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ */
+
+#include "link/TableMerger.h"
+
+#include "test/Builders.h"
+#include "test/Context.h"
+
+#include <gtest/gtest.h>
+
+namespace aapt {
+
+struct TableMergerTest : public ::testing::Test {
+ std::unique_ptr<IAaptContext> mContext;
+
+ void SetUp() override {
+ mContext = test::ContextBuilder()
+ // We are compiling this package.
+ .setCompilationPackage(u"com.app.a")
+
+ // Merge all packages that have this package ID.
+ .setPackageId(0x7f)
+
+ // Mangle all packages that do not have this package name.
+ .setNameManglerPolicy(NameManglerPolicy{ u"com.app.a", { u"com.app.b" } })
+
+ .build();
+ }
+};
+
+TEST_F(TableMergerTest, SimpleMerge) {
+ std::unique_ptr<ResourceTable> tableA = test::ResourceTableBuilder()
+ .setPackageId(u"com.app.a", 0x7f)
+ .addReference(u"@com.app.a:id/foo", u"@com.app.a:id/bar")
+ .addReference(u"@com.app.a:id/bar", u"@com.app.b:id/foo")
+ .addValue(u"@com.app.a:styleable/view", test::StyleableBuilder()
+ .addItem(u"@com.app.b:id/foo")
+ .build())
+ .build();
+
+ std::unique_ptr<ResourceTable> tableB = test::ResourceTableBuilder()
+ .setPackageId(u"com.app.b", 0x7f)
+ .addSimple(u"@com.app.b:id/foo")
+ .build();
+
+ ResourceTable finalTable;
+ TableMerger merger(mContext.get(), &finalTable);
+
+ ASSERT_TRUE(merger.merge({}, tableA.get()));
+ ASSERT_TRUE(merger.mergeAndMangle({}, u"com.app.b", tableB.get()));
+
+ EXPECT_TRUE(merger.getMergedPackages().count(u"com.app.b") != 0);
+
+ // Entries from com.app.a should not be mangled.
+ AAPT_EXPECT_TRUE(finalTable.findResource(test::parseNameOrDie(u"@com.app.a:id/foo")));
+ AAPT_EXPECT_TRUE(finalTable.findResource(test::parseNameOrDie(u"@com.app.a:id/bar")));
+ AAPT_EXPECT_TRUE(finalTable.findResource(test::parseNameOrDie(u"@com.app.a:styleable/view")));
+
+ // The unmangled name should not be present.
+ AAPT_EXPECT_FALSE(finalTable.findResource(test::parseNameOrDie(u"@com.app.b:id/foo")));
+
+ // Look for the mangled name.
+ AAPT_EXPECT_TRUE(finalTable.findResource(test::parseNameOrDie(u"@com.app.a:id/com.app.b$foo")));
+}
+
+TEST_F(TableMergerTest, MergeFileReferences) {
+ std::unique_ptr<ResourceTable> tableA = test::ResourceTableBuilder()
+ .setPackageId(u"com.app.a", 0x7f)
+ .addFileReference(u"@com.app.a:xml/file", u"res/xml/file.xml")
+ .build();
+ std::unique_ptr<ResourceTable> tableB = test::ResourceTableBuilder()
+ .setPackageId(u"com.app.b", 0x7f)
+ .addFileReference(u"@com.app.b:xml/file", u"res/xml/file.xml")
+ .build();
+
+ ResourceTable finalTable;
+ TableMerger merger(mContext.get(), &finalTable);
+
+ ASSERT_TRUE(merger.merge({}, tableA.get()));
+ ASSERT_TRUE(merger.mergeAndMangle({}, u"com.app.b", tableB.get()));
+
+ FileReference* f = test::getValue<FileReference>(&finalTable, u"@com.app.a:xml/file");
+ ASSERT_NE(f, nullptr);
+ EXPECT_EQ(std::u16string(u"res/xml/file.xml"), *f->path);
+
+ f = test::getValue<FileReference>(&finalTable, u"@com.app.a:xml/com.app.b$file");
+ ASSERT_NE(f, nullptr);
+ EXPECT_EQ(std::u16string(u"res/xml/com.app.b$file.xml"), *f->path);
+
+ std::queue<FileToMerge>* filesToMerge = merger.getFileMergeQueue();
+ ASSERT_FALSE(filesToMerge->empty());
+
+ FileToMerge& fileToMerge = filesToMerge->front();
+ EXPECT_EQ(fileToMerge.srcTable, tableB.get());
+ EXPECT_EQ(fileToMerge.srcPath, u"res/xml/file.xml");
+ EXPECT_EQ(fileToMerge.dstPath, u"res/xml/com.app.b$file.xml");
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/link/XmlReferenceLinker.cpp b/tools/aapt2/link/XmlReferenceLinker.cpp
new file mode 100644
index 0000000..147b9bf
--- /dev/null
+++ b/tools/aapt2/link/XmlReferenceLinker.cpp
@@ -0,0 +1,134 @@
+/*
+ * 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.
+ */
+
+#include "Diagnostics.h"
+#include "ResourceUtils.h"
+#include "SdkConstants.h"
+#include "XmlDom.h"
+
+#include "link/Linkers.h"
+#include "link/ReferenceLinkerVisitor.h"
+#include "process/IResourceTableConsumer.h"
+#include "process/SymbolTable.h"
+#include "util/Util.h"
+
+namespace aapt {
+
+namespace {
+
+class XmlReferenceLinkerVisitor : public xml::PackageAwareVisitor {
+private:
+ IAaptContext* mContext;
+ ISymbolTable* mSymbols;
+ std::set<int>* mSdkLevelsFound;
+ ReferenceLinkerVisitor mReferenceLinkerVisitor;
+ bool mError = false;
+
+public:
+ using xml::PackageAwareVisitor::visit;
+
+ XmlReferenceLinkerVisitor(IAaptContext* context, ISymbolTable* symbols,
+ std::set<int>* sdkLevelsFound) :
+ mContext(context), mSymbols(symbols), mSdkLevelsFound(sdkLevelsFound),
+ mReferenceLinkerVisitor(context, symbols, this) {
+ }
+
+ void visit(xml::Element* el) override {
+ for (xml::Attribute& attr : el->attributes) {
+ Maybe<std::u16string> maybePackage =
+ util::extractPackageFromNamespace(attr.namespaceUri);
+ if (maybePackage) {
+ // There is a valid package name for this attribute. We will look this up.
+ StringPiece16 package = maybePackage.value();
+ if (package.empty()) {
+ // Empty package means the 'current' or 'local' package.
+ package = mContext->getCompilationPackage();
+ }
+
+ attr.compiledAttribute = compileAttribute(
+ ResourceName{ package.toString(), ResourceType::kAttr, attr.name });
+
+ // Convert the string value into a compiled Value if this is a valid attribute.
+ if (attr.compiledAttribute) {
+ // Record all SDK levels from which the attributes were defined.
+ const int sdkLevel = findAttributeSdkLevel(attr.compiledAttribute.value().id);
+ if (sdkLevel > 1) {
+ mSdkLevelsFound->insert(sdkLevel);
+ }
+
+ const Attribute* attribute = &attr.compiledAttribute.value().attribute;
+ attr.compiledValue = ResourceUtils::parseItemForAttribute(attr.value,
+ attribute);
+ if (!attr.compiledValue &&
+ !(attribute->typeMask & android::ResTable_map::TYPE_STRING)) {
+ // We won't be able to encode this as a string.
+ mContext->getDiagnostics()->error(
+ DiagMessage() << "'" << attr.value << "' "
+ << "is incompatible with attribute "
+ << package << ":" << attr.name << " " << *attribute);
+ mError = true;
+ }
+ } else {
+ mContext->getDiagnostics()->error(
+ DiagMessage() << "attribute '" << package << ":" << attr.name
+ << "' was not found");
+ mError = true;
+
+ }
+ } else {
+ // We still encode references.
+ attr.compiledValue = ResourceUtils::tryParseReference(attr.value);
+ }
+
+ if (attr.compiledValue) {
+ // With a compiledValue, we must resolve the reference and assign it an ID.
+ attr.compiledValue->accept(&mReferenceLinkerVisitor);
+ }
+ }
+
+ // Call the super implementation.
+ xml::PackageAwareVisitor::visit(el);
+ }
+
+ Maybe<xml::AaptAttribute> compileAttribute(const ResourceName& name) {
+ Maybe<ResourceName> mangledName = mContext->getNameMangler()->mangleName(name);
+ if (const ISymbolTable::Symbol* symbol = mSymbols->findByName(
+ mangledName ? mangledName.value() : name)) {
+ if (symbol->attribute) {
+ return xml::AaptAttribute{ symbol->id, *symbol->attribute };
+ }
+ }
+ return {};
+ }
+
+ inline bool hasError() {
+ return mError || mReferenceLinkerVisitor.hasError();
+ }
+};
+
+} // namespace
+
+bool XmlReferenceLinker::consume(IAaptContext* context, XmlResource* resource) {
+ mSdkLevelsFound.clear();
+ XmlReferenceLinkerVisitor visitor(context, context->getExternalSymbols(), &mSdkLevelsFound);
+ if (resource->root) {
+ resource->root->accept(&visitor);
+ return !visitor.hasError();
+ }
+ return false;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/link/XmlReferenceLinker_test.cpp b/tools/aapt2/link/XmlReferenceLinker_test.cpp
new file mode 100644
index 0000000..7f91ec3
--- /dev/null
+++ b/tools/aapt2/link/XmlReferenceLinker_test.cpp
@@ -0,0 +1,250 @@
+/*
+ * 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.
+ */
+
+#include "link/Linkers.h"
+
+#include "test/Builders.h"
+#include "test/Context.h"
+
+#include <gtest/gtest.h>
+
+namespace aapt {
+
+class XmlReferenceLinkerTest : public ::testing::Test {
+public:
+ void SetUp() override {
+ mContext = test::ContextBuilder()
+ .setCompilationPackage(u"com.app.test")
+ .setNameManglerPolicy(
+ NameManglerPolicy{ u"com.app.test", { u"com.android.support" } })
+ .setSymbolTable(test::StaticSymbolTableBuilder()
+ .addSymbol(u"@android:attr/layout_width", ResourceId(0x01010000),
+ test::AttributeBuilder()
+ .setTypeMask(android::ResTable_map::TYPE_ENUM |
+ android::ResTable_map::TYPE_DIMENSION)
+ .addItem(u"match_parent", 0xffffffff)
+ .build())
+ .addSymbol(u"@android:attr/background", ResourceId(0x01010001),
+ test::AttributeBuilder()
+ .setTypeMask(android::ResTable_map::TYPE_COLOR).build())
+ .addSymbol(u"@android:attr/attr", ResourceId(0x01010002),
+ test::AttributeBuilder().build())
+ .addSymbol(u"@android:attr/text", ResourceId(0x01010003),
+ test::AttributeBuilder()
+ .setTypeMask(android::ResTable_map::TYPE_STRING)
+ .build())
+
+ // Add one real symbol that was introduces in v21
+ .addSymbol(u"@android:attr/colorAccent", ResourceId(0x01010435),
+ test::AttributeBuilder().build())
+
+ .addSymbol(u"@android:id/id", ResourceId(0x01030000))
+ .addSymbol(u"@com.app.test:id/id", ResourceId(0x7f030000))
+ .addSymbol(u"@com.app.test:color/green", ResourceId(0x7f020000))
+ .addSymbol(u"@com.app.test:color/red", ResourceId(0x7f020001))
+ .addSymbol(u"@com.app.test:attr/colorAccent", ResourceId(0x7f010000),
+ test::AttributeBuilder()
+ .setTypeMask(android::ResTable_map::TYPE_COLOR).build())
+ .addSymbol(u"@com.app.test:attr/com.android.support$colorAccent",
+ ResourceId(0x7f010001), test::AttributeBuilder()
+ .setTypeMask(android::ResTable_map::TYPE_COLOR).build())
+ .addSymbol(u"@com.app.test:attr/attr", ResourceId(0x7f010002),
+ test::AttributeBuilder().build())
+ .build())
+ .build();
+ }
+
+protected:
+ std::unique_ptr<IAaptContext> mContext;
+};
+
+static xml::Element* getRootElement(XmlResource* doc) {
+ xml::Node* node = doc->root.get();
+ while (xml::nodeCast<xml::Namespace>(node)) {
+ if (node->children.empty()) {
+ return nullptr;
+ }
+ node = node->children.front().get();
+ }
+
+ if (xml::Element* el = xml::nodeCast<xml::Element>(node)) {
+ return el;
+ }
+ return nullptr;
+}
+
+TEST_F(XmlReferenceLinkerTest, LinkBasicAttributes) {
+ std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF(
+ <View xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:background="@color/green"
+ android:text="hello"
+ class="hello" />)EOF");
+
+ XmlReferenceLinker linker;
+ ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
+
+ xml::Element* viewEl = getRootElement(doc.get());
+ ASSERT_NE(viewEl, nullptr);
+
+ xml::Attribute* xmlAttr = viewEl->findAttribute(u"http://schemas.android.com/apk/res/android",
+ u"layout_width");
+ ASSERT_NE(xmlAttr, nullptr);
+ AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
+ EXPECT_EQ(xmlAttr->compiledAttribute.value().id, ResourceId(0x01010000));
+ ASSERT_NE(xmlAttr->compiledValue, nullptr);
+ ASSERT_NE(valueCast<BinaryPrimitive>(xmlAttr->compiledValue.get()), nullptr);
+
+ xmlAttr = viewEl->findAttribute(u"http://schemas.android.com/apk/res/android", u"background");
+ ASSERT_NE(xmlAttr, nullptr);
+ AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
+ EXPECT_EQ(xmlAttr->compiledAttribute.value().id, ResourceId(0x01010001));
+ ASSERT_NE(xmlAttr->compiledValue, nullptr);
+ Reference* ref = valueCast<Reference>(xmlAttr->compiledValue.get());
+ ASSERT_NE(ref, nullptr);
+ AAPT_ASSERT_TRUE(ref->name);
+ EXPECT_EQ(ref->name.value(), test::parseNameOrDie(u"@color/green")); // Make sure the name
+ // didn't change.
+ AAPT_ASSERT_TRUE(ref->id);
+ EXPECT_EQ(ref->id.value(), ResourceId(0x7f020000));
+
+ xmlAttr = viewEl->findAttribute(u"http://schemas.android.com/apk/res/android", u"text");
+ ASSERT_NE(xmlAttr, nullptr);
+ AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
+ ASSERT_FALSE(xmlAttr->compiledValue); // Strings don't get compiled for memory sake.
+
+ xmlAttr = viewEl->findAttribute(u"", u"class");
+ ASSERT_NE(xmlAttr, nullptr);
+ AAPT_ASSERT_FALSE(xmlAttr->compiledAttribute);
+ ASSERT_EQ(xmlAttr->compiledValue, nullptr);
+}
+
+TEST_F(XmlReferenceLinkerTest, SdkLevelsAreRecorded) {
+ std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF(
+ <View xmlns:android="http://schemas.android.com/apk/res/android"
+ android:colorAccent="#ffffff" />)EOF");
+
+ XmlReferenceLinker linker;
+ ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
+ EXPECT_TRUE(linker.getSdkLevels().count(21) == 1);
+}
+
+TEST_F(XmlReferenceLinkerTest, LinkMangledAttributes) {
+ std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF(
+ <View xmlns:support="http://schemas.android.com/apk/res/com.android.support"
+ support:colorAccent="#ff0000" />)EOF");
+
+ XmlReferenceLinker linker;
+ ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
+
+ xml::Element* viewEl = getRootElement(doc.get());
+ ASSERT_NE(viewEl, nullptr);
+
+ xml::Attribute* xmlAttr = viewEl->findAttribute(
+ u"http://schemas.android.com/apk/res/com.android.support", u"colorAccent");
+ ASSERT_NE(xmlAttr, nullptr);
+ AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
+ EXPECT_EQ(xmlAttr->compiledAttribute.value().id, ResourceId(0x7f010001));
+ ASSERT_NE(valueCast<BinaryPrimitive>(xmlAttr->compiledValue.get()), nullptr);
+}
+
+TEST_F(XmlReferenceLinkerTest, LinkAutoResReference) {
+ std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF(
+ <View xmlns:app="http://schemas.android.com/apk/res-auto"
+ app:colorAccent="@app:color/red" />)EOF");
+
+ XmlReferenceLinker linker;
+ ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
+
+ xml::Element* viewEl = getRootElement(doc.get());
+ ASSERT_NE(viewEl, nullptr);
+
+ xml::Attribute* xmlAttr = viewEl->findAttribute(u"http://schemas.android.com/apk/res-auto",
+ u"colorAccent");
+ ASSERT_NE(xmlAttr, nullptr);
+ AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
+ EXPECT_EQ(xmlAttr->compiledAttribute.value().id, ResourceId(0x7f010000));
+ Reference* ref = valueCast<Reference>(xmlAttr->compiledValue.get());
+ ASSERT_NE(ref, nullptr);
+ AAPT_ASSERT_TRUE(ref->name);
+ AAPT_ASSERT_TRUE(ref->id);
+ EXPECT_EQ(ref->id.value(), ResourceId(0x7f020001));
+}
+
+TEST_F(XmlReferenceLinkerTest, LinkViewWithShadowedPackageAlias) {
+ std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF(
+ <View xmlns:app="http://schemas.android.com/apk/res/android"
+ app:attr="@app:id/id">
+ <View xmlns:app="http://schemas.android.com/apk/res/com.app.test"
+ app:attr="@app:id/id"/>
+ </View>)EOF");
+
+ XmlReferenceLinker linker;
+ ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
+
+ xml::Element* viewEl = getRootElement(doc.get());
+ ASSERT_NE(viewEl, nullptr);
+
+ // All attributes and references in this element should be referring to "android" (0x01).
+ xml::Attribute* xmlAttr = viewEl->findAttribute(u"http://schemas.android.com/apk/res/android",
+ u"attr");
+ ASSERT_NE(xmlAttr, nullptr);
+ AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
+ EXPECT_EQ(xmlAttr->compiledAttribute.value().id, ResourceId(0x01010002));
+ Reference* ref = valueCast<Reference>(xmlAttr->compiledValue.get());
+ ASSERT_NE(ref, nullptr);
+ AAPT_ASSERT_TRUE(ref->id);
+ EXPECT_EQ(ref->id.value(), ResourceId(0x01030000));
+
+ ASSERT_FALSE(viewEl->getChildElements().empty());
+ viewEl = viewEl->getChildElements().front();
+ ASSERT_NE(viewEl, nullptr);
+
+ // All attributes and references in this element should be referring to "com.app.test" (0x7f).
+ xmlAttr = viewEl->findAttribute(u"http://schemas.android.com/apk/res/com.app.test", u"attr");
+ ASSERT_NE(xmlAttr, nullptr);
+ AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
+ EXPECT_EQ(xmlAttr->compiledAttribute.value().id, ResourceId(0x7f010002));
+ ref = valueCast<Reference>(xmlAttr->compiledValue.get());
+ ASSERT_NE(ref, nullptr);
+ AAPT_ASSERT_TRUE(ref->id);
+ EXPECT_EQ(ref->id.value(), ResourceId(0x7f030000));
+}
+
+TEST_F(XmlReferenceLinkerTest, LinkViewWithLocalPackageAndAliasOfTheSameName) {
+ std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF(
+ <View xmlns:android="http://schemas.android.com/apk/res/com.app.test"
+ android:attr="@id/id"/>)EOF");
+
+ XmlReferenceLinker linker;
+ ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
+
+ xml::Element* viewEl = getRootElement(doc.get());
+ ASSERT_NE(viewEl, nullptr);
+
+ // All attributes and references in this element should be referring to "com.app.test" (0x7f).
+ xml::Attribute* xmlAttr = viewEl->findAttribute(
+ u"http://schemas.android.com/apk/res/com.app.test", u"attr");
+ ASSERT_NE(xmlAttr, nullptr);
+ AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
+ EXPECT_EQ(xmlAttr->compiledAttribute.value().id, ResourceId(0x7f010002));
+ Reference* ref = valueCast<Reference>(xmlAttr->compiledValue.get());
+ ASSERT_NE(ref, nullptr);
+ AAPT_ASSERT_TRUE(ref->id);
+ EXPECT_EQ(ref->id.value(), ResourceId(0x7f030000));
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/process.dot b/tools/aapt2/process.dot
deleted file mode 100644
index 4741952..0000000
--- a/tools/aapt2/process.dot
+++ /dev/null
@@ -1,108 +0,0 @@
-digraph aapt {
- out_package [label="out/default/package.apk"];
- out_fr_package [label="out/fr/package.apk"];
- out_table_aligned [label="out/default/resources-aligned.arsc"];
- out_table_fr_aligned [label="out/fr/resources-aligned.arsc"];
- out_res_layout_main_xml [label="out/res/layout/main.xml"];
- out_res_layout_v21_main_xml [color=red,label="out/res/layout-v21/main.xml"];
- out_res_layout_fr_main_xml [label="out/res/layout-fr/main.xml"];
- out_res_layout_fr_v21_main_xml [color=red,label="out/res/layout-fr-v21/main.xml"];
- out_table [label="out/default/resources.arsc"];
- out_fr_table [label="out/fr/resources.arsc"];
- out_values_table [label="out/values/resources.arsc"];
- out_layout_table [label="out/layout/resources.arsc"];
- out_values_fr_table [label="out/values-fr/resources.arsc"];
- out_layout_fr_table [label="out/layout-fr/resources.arsc"];
- res_values_strings_xml [label="res/values/strings.xml"];
- res_values_attrs_xml [label="res/values/attrs.xml"];
- res_layout_main_xml [label="res/layout/main.xml"];
- res_layout_fr_main_xml [label="res/layout-fr/main.xml"];
- res_values_fr_strings_xml [label="res/values-fr/strings.xml"];
-
- lib_apk_resources_arsc [label="lib.apk:resources.arsc",color=green];
- lib_apk_res_layout_main_xml [label="lib.apk:res/layout/main.xml",color=green];
- lib_apk_res_drawable_icon_png [label="lib.apk:res/drawable/icon.png",color=green];
- lib_apk_fr_res_layout_main_xml [label="lib.apk:res/layout-fr/main.xml",color=green];
- lib_apk_fr_res_drawable_icon_png [label="lib.apk:res/drawable-fr/icon.png",color=green];
- out_res_layout_lib_main_xml [label="out/res/layout/lib-main.xml"];
-
- out_package -> package_default;
- out_fr_package -> package_fr;
-
- package_default [shape=box,label="Assemble",color=blue];
- package_default -> out_table_aligned;
- package_default -> out_res_layout_main_xml;
- package_default -> out_res_layout_v21_main_xml [color=red];
- package_default -> out_res_layout_lib_main_xml;
-
- package_fr [shape=box,label="Assemble",color=blue];
- package_fr -> out_table_fr_aligned;
- package_fr -> out_res_layout_fr_main_xml;
- package_fr -> out_res_layout_fr_v21_main_xml [color=red];
-
- out_table_aligned -> align_tables;
- out_table_fr_aligned -> align_tables;
-
- align_tables [shape=box,label="Align",color=blue];
- align_tables -> out_table;
- align_tables -> out_fr_table;
-
- out_table -> link_tables;
-
- link_tables [shape=box,label="Link",color=blue];
- link_tables -> out_values_table;
- link_tables -> out_layout_table;
- link_tables -> lib_apk_resources_arsc;
-
- out_values_table -> compile_values;
-
- compile_values [shape=box,label="Collect",color=blue];
- compile_values -> res_values_strings_xml;
- compile_values -> res_values_attrs_xml;
-
- out_layout_table -> collect_xml;
-
- collect_xml [shape=box,label="Collect",color=blue];
- collect_xml -> res_layout_main_xml;
-
- out_fr_table -> link_fr_tables;
-
- link_fr_tables [shape=box,label="Link",color=blue];
- link_fr_tables -> out_values_fr_table;
- link_fr_tables -> out_layout_fr_table;
- link_fr_tables -> lib_apk_resources_arsc;
-
- out_values_fr_table -> compile_values_fr;
-
- compile_values_fr [shape=box,label="Collect",color=blue];
- compile_values_fr -> res_values_fr_strings_xml;
-
- out_layout_fr_table -> collect_xml_fr;
-
- collect_xml_fr [shape=box,label="Collect",color=blue];
- collect_xml_fr -> res_layout_fr_main_xml;
-
- compile_res_layout_main_xml [shape=box,label="Compile",color=blue];
-
- out_res_layout_main_xml -> compile_res_layout_main_xml;
-
- out_res_layout_v21_main_xml -> compile_res_layout_main_xml [color=red];
-
- compile_res_layout_main_xml -> res_layout_main_xml;
- compile_res_layout_main_xml -> out_table_aligned;
-
- compile_res_layout_fr_main_xml [shape=box,label="Compile",color=blue];
-
- out_res_layout_fr_main_xml -> compile_res_layout_fr_main_xml;
-
- out_res_layout_fr_v21_main_xml -> compile_res_layout_fr_main_xml [color=red];
-
- compile_res_layout_fr_main_xml -> res_layout_fr_main_xml;
- compile_res_layout_fr_main_xml -> out_table_fr_aligned;
-
- out_res_layout_lib_main_xml -> compile_res_layout_lib_main_xml;
-
- compile_res_layout_lib_main_xml [shape=box,label="Compile",color=blue];
- compile_res_layout_lib_main_xml -> out_table_aligned;
- compile_res_layout_lib_main_xml -> lib_apk_res_layout_main_xml;
-}
diff --git a/tools/aapt2/process/IResourceTableConsumer.h b/tools/aapt2/process/IResourceTableConsumer.h
new file mode 100644
index 0000000..24ad05d
--- /dev/null
+++ b/tools/aapt2/process/IResourceTableConsumer.h
@@ -0,0 +1,75 @@
+/*
+ * 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_PROCESS_IRESOURCETABLECONSUMER_H
+#define AAPT_PROCESS_IRESOURCETABLECONSUMER_H
+
+#include "Diagnostics.h"
+#include "NameMangler.h"
+#include "Resource.h"
+#include "ResourceValues.h"
+#include "Source.h"
+
+#include <iostream>
+#include <list>
+#include <sstream>
+
+namespace aapt {
+
+class ResourceTable;
+struct ISymbolTable;
+
+struct IAaptContext {
+ virtual ~IAaptContext() = default;
+
+ virtual ISymbolTable* getExternalSymbols() = 0;
+ virtual IDiagnostics* getDiagnostics() = 0;
+ virtual StringPiece16 getCompilationPackage() = 0;
+ virtual uint8_t getPackageId() = 0;
+ virtual NameMangler* getNameMangler() = 0;
+};
+
+struct IResourceTableConsumer {
+ virtual ~IResourceTableConsumer() = default;
+
+ virtual bool consume(IAaptContext* context, ResourceTable* table) = 0;
+};
+
+namespace xml {
+struct Node;
+}
+
+struct XmlResource {
+ ResourceFile file;
+ std::unique_ptr<xml::Node> root;
+};
+
+struct IXmlResourceConsumer {
+ virtual ~IXmlResourceConsumer() = default;
+
+ virtual bool consume(IAaptContext* context, XmlResource* resource) = 0;
+};
+
+struct IPackageDeclStack {
+ virtual ~IPackageDeclStack() = default;
+
+ virtual Maybe<ResourceName> transformPackage(const ResourceName& name,
+ const StringPiece16& localPackage) const = 0;
+};
+
+} // namespace aapt
+
+#endif /* AAPT_PROCESS_IRESOURCETABLECONSUMER_H */
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
new file mode 100644
index 0000000..9a8b263
--- /dev/null
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -0,0 +1,204 @@
+/*
+ * 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.
+ */
+
+#include "ConfigDescription.h"
+#include "Resource.h"
+#include "ValueVisitor.h"
+
+#include "process/SymbolTable.h"
+#include "util/Comparators.h"
+#include "util/Util.h"
+
+#include <androidfw/AssetManager.h>
+#include <androidfw/ResourceTypes.h>
+
+namespace aapt {
+
+const ISymbolTable::Symbol* SymbolTableWrapper::findByName(const ResourceName& name) {
+ if (const std::shared_ptr<Symbol>& s = mCache.get(name)) {
+ return s.get();
+ }
+
+ Maybe<ResourceTable::SearchResult> result = mTable->findResource(name);
+ if (!result) {
+ if (name.type == ResourceType::kAttr) {
+ // Recurse and try looking up a private attribute.
+ return findByName(ResourceName(name.package, ResourceType::kAttrPrivate, name.entry));
+ }
+ return {};
+ }
+
+ ResourceTable::SearchResult sr = result.value();
+
+ // If no ID exists, we treat the symbol as missing. SymbolTables are used to
+ // find symbols to link.
+ if (!sr.package->id || !sr.type->id || !sr.entry->id) {
+ return {};
+ }
+
+ std::shared_ptr<Symbol> symbol = std::make_shared<Symbol>();
+ symbol->id = ResourceId(sr.package->id.value(), sr.type->id.value(), sr.entry->id.value());
+
+ if (name.type == ResourceType::kAttr || name.type == ResourceType::kAttrPrivate) {
+ const ConfigDescription kDefaultConfig;
+ auto iter = std::lower_bound(sr.entry->values.begin(), sr.entry->values.end(),
+ kDefaultConfig, cmp::lessThanConfig);
+
+ if (iter != sr.entry->values.end() && iter->config == kDefaultConfig) {
+ // This resource has an Attribute.
+ if (Attribute* attr = valueCast<Attribute>(iter->value.get())) {
+ symbol->attribute = std::unique_ptr<Attribute>(attr->clone(nullptr));
+ } else {
+ return {};
+ }
+ }
+ }
+
+ if (name.type == ResourceType::kAttrPrivate) {
+ // Masquerade this entry as kAttr.
+ mCache.put(ResourceName(name.package, ResourceType::kAttr, name.entry), symbol);
+ } else {
+ mCache.put(name, symbol);
+ }
+ return symbol.get();
+}
+
+
+static std::shared_ptr<ISymbolTable::Symbol> lookupIdInTable(const android::ResTable& table,
+ ResourceId id) {
+ android::Res_value val = {};
+ ssize_t block = table.getResource(id.id, &val, true);
+ if (block >= 0) {
+ std::shared_ptr<ISymbolTable::Symbol> s = std::make_shared<ISymbolTable::Symbol>();
+ s->id = id;
+ return s;
+ }
+
+ // Try as a bag.
+ const android::ResTable::bag_entry* entry;
+ ssize_t count = table.lockBag(id.id, &entry);
+ if (count < 0) {
+ table.unlockBag(entry);
+ return nullptr;
+ }
+
+ // We found a resource.
+ std::shared_ptr<ISymbolTable::Symbol> s = std::make_shared<ISymbolTable::Symbol>();
+ s->id = id;
+
+ // Check to see if it is an attribute.
+ for (size_t i = 0; i < (size_t) count; i++) {
+ if (entry[i].map.name.ident == android::ResTable_map::ATTR_TYPE) {
+ s->attribute = util::make_unique<Attribute>(false);
+ s->attribute->typeMask = entry[i].map.value.data;
+ break;
+ }
+ }
+
+ if (s->attribute) {
+ for (size_t i = 0; i < (size_t) count; i++) {
+ if (!Res_INTERNALID(entry[i].map.name.ident)) {
+ android::ResTable::resource_name entryName;
+ if (!table.getResourceName(entry[i].map.name.ident, false, &entryName)) {
+ table.unlockBag(entry);
+ return nullptr;
+ }
+
+ const ResourceType* parsedType = parseResourceType(
+ StringPiece16(entryName.type, entryName.typeLen));
+ if (!parsedType) {
+ table.unlockBag(entry);
+ return nullptr;
+ }
+
+ Attribute::Symbol symbol;
+ symbol.symbol.name = ResourceNameRef(
+ StringPiece16(entryName.package, entryName.packageLen),
+ *parsedType,
+ StringPiece16(entryName.name, entryName.nameLen)).toResourceName();
+ symbol.symbol.id = ResourceId(entry[i].map.name.ident);
+ symbol.value = entry[i].map.value.data;
+ s->attribute->symbols.push_back(std::move(symbol));
+ }
+ }
+ }
+ table.unlockBag(entry);
+ return s;
+}
+
+const ISymbolTable::Symbol* AssetManagerSymbolTableBuilder::AssetManagerSymbolTable::findByName(
+ const ResourceName& name) {
+ if (const std::shared_ptr<Symbol>& s = mCache.get(name)) {
+ return s.get();
+ }
+
+ for (const auto& asset : mAssets) {
+ const android::ResTable& table = asset->getResources(false);
+ StringPiece16 typeStr = toString(name.type);
+ ResourceId resId = table.identifierForName(name.entry.data(), name.entry.size(),
+ typeStr.data(), typeStr.size(),
+ name.package.data(), name.package.size());
+ if (!resId.isValid()) {
+ continue;
+ }
+
+ std::shared_ptr<Symbol> s = lookupIdInTable(table, resId);
+ if (s) {
+ mCache.put(name, s);
+ return s.get();
+ }
+ }
+ return nullptr;
+}
+
+const ISymbolTable::Symbol* AssetManagerSymbolTableBuilder::AssetManagerSymbolTable::findById(
+ ResourceId id) {
+ if (const std::shared_ptr<Symbol>& s = mIdCache.get(id)) {
+ return s.get();
+ }
+
+ for (const auto& asset : mAssets) {
+ const android::ResTable& table = asset->getResources(false);
+
+ std::shared_ptr<Symbol> s = lookupIdInTable(table, id);
+ if (s) {
+ mIdCache.put(id, s);
+ return s.get();
+ }
+ }
+ return nullptr;
+}
+
+const ISymbolTable::Symbol* JoinedSymbolTableBuilder::JoinedSymbolTable::findByName(
+ const ResourceName& name) {
+ for (auto& symbolTable : mSymbolTables) {
+ if (const Symbol* s = symbolTable->findByName(name)) {
+ return s;
+ }
+ }
+ return {};
+}
+
+const ISymbolTable::Symbol* JoinedSymbolTableBuilder::JoinedSymbolTable::findById(ResourceId id) {
+ for (auto& symbolTable : mSymbolTables) {
+ if (const Symbol* s = symbolTable->findById(id)) {
+ return s;
+ }
+ }
+ return {};
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h
new file mode 100644
index 0000000..22096ed
--- /dev/null
+++ b/tools/aapt2/process/SymbolTable.h
@@ -0,0 +1,152 @@
+/*
+ * 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_PROCESS_SYMBOLTABLE_H
+#define AAPT_PROCESS_SYMBOLTABLE_H
+
+#include "Resource.h"
+#include "ResourceTable.h"
+#include "ResourceValues.h"
+#include "util/Util.h"
+
+#include <utils/JenkinsHash.h>
+#include <utils/LruCache.h>
+
+#include <androidfw/AssetManager.h>
+#include <algorithm>
+#include <map>
+#include <memory>
+#include <vector>
+
+namespace aapt {
+
+struct ISymbolTable {
+ virtual ~ISymbolTable() = default;
+
+ struct Symbol {
+ ResourceId id;
+ std::unique_ptr<Attribute> attribute;
+ bool isPublic;
+ };
+
+ /**
+ * Never hold on to the result between calls to findByName or findById. The results
+ * are typically stored in a cache which may evict entries.
+ */
+ virtual const Symbol* findByName(const ResourceName& name) = 0;
+ virtual const Symbol* findById(ResourceId id) = 0;
+};
+
+inline android::hash_t hash_type(const ResourceName& name) {
+ std::hash<std::u16string> strHash;
+ android::hash_t hash = 0;
+ hash = android::JenkinsHashMix(hash, strHash(name.package));
+ hash = android::JenkinsHashMix(hash, (uint32_t) name.type);
+ hash = android::JenkinsHashMix(hash, strHash(name.entry));
+ return hash;
+}
+
+inline android::hash_t hash_type(const ResourceId& id) {
+ return android::hash_type(id.id);
+}
+
+/**
+ * Presents a ResourceTable as an ISymbolTable, caching results.
+ * Instances of this class must outlive the encompassed ResourceTable.
+ * Since symbols are cached, the ResourceTable should not change during the
+ * lifetime of this SymbolTableWrapper.
+ *
+ * If a resource in the ResourceTable does not have a ResourceID assigned to it,
+ * it is ignored.
+ *
+ * Lookups by ID are ignored.
+ */
+class SymbolTableWrapper : public ISymbolTable {
+private:
+ ResourceTable* mTable;
+
+ // We use shared_ptr because unique_ptr is not supported and
+ // we need automatic deletion.
+ android::LruCache<ResourceName, std::shared_ptr<Symbol>> mCache;
+
+public:
+ SymbolTableWrapper(ResourceTable* table) : mTable(table), mCache(200) {
+ }
+
+ const Symbol* findByName(const ResourceName& name) override;
+
+ // Unsupported, all queries to ResourceTable should be done by name.
+ const Symbol* findById(ResourceId id) override {
+ return {};
+ }
+};
+
+class AssetManagerSymbolTableBuilder {
+private:
+ struct AssetManagerSymbolTable : public ISymbolTable {
+ std::vector<std::unique_ptr<android::AssetManager>> mAssets;
+
+ // We use shared_ptr because unique_ptr is not supported and
+ // we need automatic deletion.
+ android::LruCache<ResourceName, std::shared_ptr<Symbol>> mCache;
+ android::LruCache<ResourceId, std::shared_ptr<Symbol>> mIdCache;
+
+ AssetManagerSymbolTable() : mCache(200), mIdCache(200) {
+ }
+
+ const Symbol* findByName(const ResourceName& name) override;
+ const Symbol* findById(ResourceId id) override;
+ };
+
+ std::unique_ptr<AssetManagerSymbolTable> mSymbolTable =
+ util::make_unique<AssetManagerSymbolTable>();
+
+public:
+ AssetManagerSymbolTableBuilder& add(std::unique_ptr<android::AssetManager> assetManager) {
+ mSymbolTable->mAssets.push_back(std::move(assetManager));
+ return *this;
+ }
+
+ std::unique_ptr<ISymbolTable> build() {
+ return std::move(mSymbolTable);
+ }
+};
+
+class JoinedSymbolTableBuilder {
+private:
+ struct JoinedSymbolTable : public ISymbolTable {
+ std::vector<std::unique_ptr<ISymbolTable>> mSymbolTables;
+
+ const Symbol* findByName(const ResourceName& name) override;
+ const Symbol* findById(ResourceId id) override;
+ };
+
+ std::unique_ptr<JoinedSymbolTable> mSymbolTable = util::make_unique<JoinedSymbolTable>();
+
+public:
+ JoinedSymbolTableBuilder& addSymbolTable(std::unique_ptr<ISymbolTable> table) {
+ mSymbolTable->mSymbolTables.push_back(std::move(table));
+ return *this;
+ }
+
+ std::unique_ptr<ISymbolTable> build() {
+ return std::move(mSymbolTable);
+ }
+};
+
+} // namespace aapt
+
+#endif /* AAPT_PROCESS_SYMBOLTABLE_H */
diff --git a/tools/aapt2/process/SymbolTable_test.cpp b/tools/aapt2/process/SymbolTable_test.cpp
new file mode 100644
index 0000000..1dc3b4f
--- /dev/null
+++ b/tools/aapt2/process/SymbolTable_test.cpp
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+#include "process/SymbolTable.h"
+#include "test/Builders.h"
+#include "test/Context.h"
+
+#include <gtest/gtest.h>
+
+namespace aapt {
+
+TEST(SymbolTableWrapperTest, FindSymbolsWithIds) {
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .addSimple(u"@android:id/foo", ResourceId(0x01020000))
+ .addSimple(u"@android:id/bar")
+ .addValue(u"@android:attr/foo", ResourceId(0x01010000),
+ test::AttributeBuilder().build())
+ .build();
+
+ SymbolTableWrapper symbolTable(table.get());
+ EXPECT_NE(symbolTable.findByName(test::parseNameOrDie(u"@android:id/foo")), nullptr);
+ EXPECT_EQ(symbolTable.findByName(test::parseNameOrDie(u"@android:id/bar")), nullptr);
+
+ const ISymbolTable::Symbol* s = symbolTable.findByName(
+ test::parseNameOrDie(u"@android:attr/foo"));
+ ASSERT_NE(s, nullptr);
+ EXPECT_NE(s->attribute, nullptr);
+}
+
+TEST(SymbolTableWrapperTest, FindPrivateAttrSymbol) {
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .addValue(u"@android:^attr-private/foo", ResourceId(0x01010000),
+ test::AttributeBuilder().build())
+ .build();
+
+ SymbolTableWrapper symbolTable(table.get());
+ const ISymbolTable::Symbol* s = symbolTable.findByName(
+ test::parseNameOrDie(u"@android:attr/foo"));
+ ASSERT_NE(s, nullptr);
+ EXPECT_NE(s->attribute, nullptr);
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
new file mode 100644
index 0000000..89cd972
--- /dev/null
+++ b/tools/aapt2/test/Builders.h
@@ -0,0 +1,227 @@
+/*
+ * 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_TEST_BUILDERS_H
+#define AAPT_TEST_BUILDERS_H
+
+#include "ResourceTable.h"
+#include "ResourceValues.h"
+#include "XmlDom.h"
+#include "util/Util.h"
+
+#include "test/Common.h"
+
+#include <memory>
+
+namespace aapt {
+namespace test {
+
+class ResourceTableBuilder {
+private:
+ DummyDiagnosticsImpl mDiagnostics;
+ std::unique_ptr<ResourceTable> mTable = util::make_unique<ResourceTable>();
+
+public:
+ ResourceTableBuilder() = default;
+
+ ResourceTableBuilder& setPackageId(const StringPiece16& packageName, uint8_t id) {
+ ResourceTablePackage* package = mTable->createPackage(packageName, id);
+ assert(package);
+ return *this;
+ }
+
+ ResourceTableBuilder& addSimple(const StringPiece16& name, const ResourceId id = {}) {
+ return addValue(name, id, util::make_unique<Id>());
+ }
+
+ ResourceTableBuilder& addReference(const StringPiece16& name, const StringPiece16& ref) {
+ return addReference(name, {}, ref);
+ }
+
+ ResourceTableBuilder& addReference(const StringPiece16& name, const ResourceId id,
+ const StringPiece16& ref) {
+ return addValue(name, id, util::make_unique<Reference>(parseNameOrDie(ref)));
+ }
+
+ ResourceTableBuilder& addString(const StringPiece16& name, const StringPiece16& str) {
+ return addString(name, {}, str);
+ }
+
+ ResourceTableBuilder& addString(const StringPiece16& name, const ResourceId id,
+ const StringPiece16& str) {
+ return addValue(name, id, util::make_unique<String>(mTable->stringPool.makeRef(str)));
+ }
+
+ ResourceTableBuilder& addFileReference(const StringPiece16& name, const StringPiece16& path) {
+ return addFileReference(name, {}, path);
+ }
+
+ ResourceTableBuilder& addFileReference(const StringPiece16& name, const ResourceId id,
+ const StringPiece16& path) {
+ return addValue(name, id,
+ util::make_unique<FileReference>(mTable->stringPool.makeRef(path)));
+ }
+
+
+ ResourceTableBuilder& addValue(const StringPiece16& name,
+ std::unique_ptr<Value> value) {
+ return addValue(name, {}, std::move(value));
+ }
+
+ ResourceTableBuilder& addValue(const StringPiece16& name, const ResourceId id,
+ std::unique_ptr<Value> value) {
+ return addValue(name, id, {}, std::move(value));
+ }
+
+ ResourceTableBuilder& addValue(const StringPiece16& name, const ResourceId id,
+ const ConfigDescription& config,
+ std::unique_ptr<Value> value) {
+ ResourceName resName = parseNameOrDie(name);
+ bool result = mTable->addResourceAllowMangled(resName, id, config, std::move(value),
+ &mDiagnostics);
+ assert(result);
+ return *this;
+ }
+
+ ResourceTableBuilder& setSymbolState(const StringPiece16& name, ResourceId id,
+ SymbolState state) {
+ ResourceName resName = parseNameOrDie(name);
+ Symbol symbol;
+ symbol.state = state;
+ bool result = mTable->setSymbolStateAllowMangled(resName, id, symbol, &mDiagnostics);
+ assert(result);
+ return *this;
+ }
+
+ std::unique_ptr<ResourceTable> build() {
+ return std::move(mTable);
+ }
+};
+
+inline std::unique_ptr<Reference> buildReference(const StringPiece16& ref,
+ Maybe<ResourceId> id = {}) {
+ std::unique_ptr<Reference> reference = util::make_unique<Reference>(parseNameOrDie(ref));
+ reference->id = id;
+ return reference;
+}
+
+template <typename T>
+class ValueBuilder {
+private:
+ std::unique_ptr<Value> mValue;
+
+public:
+ template <typename... Args>
+ ValueBuilder(Args&&... args) : mValue(new T{ std::forward<Args>(args)... }) {
+ }
+
+ template <typename... Args>
+ ValueBuilder& setSource(Args&&... args) {
+ mValue->setSource(Source{ std::forward<Args>(args)... });
+ return *this;
+ }
+
+ ValueBuilder& setComment(const StringPiece16& str) {
+ mValue->setComment(str);
+ return *this;
+ }
+
+ std::unique_ptr<Value> build() {
+ return std::move(mValue);
+ }
+};
+
+class AttributeBuilder {
+private:
+ std::unique_ptr<Attribute> mAttr;
+
+public:
+ AttributeBuilder(bool weak = false) : mAttr(util::make_unique<Attribute>(weak)) {
+ mAttr->typeMask = android::ResTable_map::TYPE_ANY;
+ }
+
+ AttributeBuilder& setTypeMask(uint32_t typeMask) {
+ mAttr->typeMask = typeMask;
+ return *this;
+ }
+
+ AttributeBuilder& addItem(const StringPiece16& name, uint32_t value) {
+ mAttr->symbols.push_back(Attribute::Symbol{
+ Reference(ResourceName{ {}, ResourceType::kId, name.toString()}),
+ value});
+ return *this;
+ }
+
+ std::unique_ptr<Attribute> build() {
+ return std::move(mAttr);
+ }
+};
+
+class StyleBuilder {
+private:
+ std::unique_ptr<Style> mStyle = util::make_unique<Style>();
+
+public:
+ StyleBuilder& setParent(const StringPiece16& str) {
+ mStyle->parent = Reference(parseNameOrDie(str));
+ return *this;
+ }
+
+ StyleBuilder& addItem(const StringPiece16& str, std::unique_ptr<Item> value) {
+ mStyle->entries.push_back(Style::Entry{ Reference(parseNameOrDie(str)), std::move(value) });
+ return *this;
+ }
+
+ StyleBuilder& addItem(const StringPiece16& str, ResourceId id, std::unique_ptr<Item> value) {
+ addItem(str, std::move(value));
+ mStyle->entries.back().key.id = id;
+ return *this;
+ }
+
+ std::unique_ptr<Style> build() {
+ return std::move(mStyle);
+ }
+};
+
+class StyleableBuilder {
+private:
+ std::unique_ptr<Styleable> mStyleable = util::make_unique<Styleable>();
+
+public:
+ StyleableBuilder& addItem(const StringPiece16& str, Maybe<ResourceId> id = {}) {
+ mStyleable->entries.push_back(Reference(parseNameOrDie(str)));
+ mStyleable->entries.back().id = id;
+ return *this;
+ }
+
+ std::unique_ptr<Styleable> build() {
+ return std::move(mStyleable);
+ }
+};
+
+inline std::unique_ptr<XmlResource> buildXmlDom(const StringPiece& str) {
+ std::stringstream in;
+ in << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" << str;
+ StdErrDiagnostics diag;
+ std::unique_ptr<XmlResource> doc = xml::inflate(&in, &diag, {});
+ assert(doc);
+ return doc;
+}
+
+} // namespace test
+} // namespace aapt
+
+#endif /* AAPT_TEST_BUILDERS_H */
diff --git a/tools/aapt2/test/Common.h b/tools/aapt2/test/Common.h
new file mode 100644
index 0000000..6fdaebb
--- /dev/null
+++ b/tools/aapt2/test/Common.h
@@ -0,0 +1,93 @@
+/*
+ * 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_TEST_COMMON_H
+#define AAPT_TEST_COMMON_H
+
+#include "ConfigDescription.h"
+#include "Debug.h"
+#include "ResourceTable.h"
+#include "ResourceUtils.h"
+#include "ValueVisitor.h"
+
+#include "process/IResourceTableConsumer.h"
+#include "util/StringPiece.h"
+
+#include <gtest/gtest.h>
+#include <iostream>
+
+//
+// GTEST 1.7 doesn't explicitly cast to bool, which causes explicit operators to fail to compile.
+//
+#define AAPT_ASSERT_TRUE(v) ASSERT_TRUE(bool(v))
+#define AAPT_ASSERT_FALSE(v) ASSERT_FALSE(bool(v))
+#define AAPT_EXPECT_TRUE(v) EXPECT_TRUE(bool(v))
+#define AAPT_EXPECT_FALSE(v) EXPECT_FALSE(bool(v))
+
+namespace aapt {
+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 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 ResourceName parseNameOrDie(const StringPiece16& str) {
+ ResourceNameRef ref;
+ bool result = ResourceUtils::tryParseReference(str, &ref);
+ assert(result && "invalid resource name");
+ return ref.toResourceName();
+}
+
+inline ConfigDescription parseConfigOrDie(const StringPiece& str) {
+ ConfigDescription config;
+ bool result = ConfigDescription::parse(str, &config);
+ assert(result && "invalid configuration");
+ return config;
+}
+
+template <typename T> T* getValueForConfig(ResourceTable* table, const StringPiece16& resName,
+ const ConfigDescription& config) {
+ Maybe<ResourceTable::SearchResult> result = table->findResource(parseNameOrDie(resName));
+ if (result) {
+ ResourceEntry* entry = result.value().entry;
+ auto iter = std::lower_bound(entry->values.begin(), entry->values.end(), config,
+ [](const ResourceConfigValue& a, const ConfigDescription& b)
+ -> bool {
+ return a.config < b;
+ });
+ if (iter != entry->values.end() && iter->config == config) {
+ return valueCast<T>(iter->value.get());
+ }
+ }
+ return nullptr;
+}
+
+template <typename T> T* getValue(ResourceTable* table, const StringPiece16& resName) {
+ return getValueForConfig<T>(table, resName, {});
+}
+
+} // namespace test
+} // namespace aapt
+
+#endif /* AAPT_TEST_COMMON_H */
diff --git a/tools/aapt2/test/Context.h b/tools/aapt2/test/Context.h
new file mode 100644
index 0000000..4fa4918
--- /dev/null
+++ b/tools/aapt2/test/Context.h
@@ -0,0 +1,156 @@
+/*
+ * 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_TEST_CONTEXT_H
+#define AAPT_TEST_CONTEXT_H
+
+#include "NameMangler.h"
+#include "util/Util.h"
+
+#include "process/IResourceTableConsumer.h"
+#include "process/SymbolTable.h"
+#include "test/Common.h"
+
+#include <cassert>
+#include <list>
+
+namespace aapt {
+namespace test {
+
+class Context : public IAaptContext {
+private:
+ friend class ContextBuilder;
+
+ Context() = default;
+
+ Maybe<std::u16string> mCompilationPackage;
+ Maybe<uint8_t> mPackageId;
+ std::unique_ptr<IDiagnostics> mDiagnostics = util::make_unique<StdErrDiagnostics>();
+ std::unique_ptr<ISymbolTable> mSymbols;
+ std::unique_ptr<NameMangler> mNameMangler;
+
+public:
+ ISymbolTable* getExternalSymbols() override {
+ assert(mSymbols && "test symbols not set");
+ return mSymbols.get();
+ }
+
+ void setSymbolTable(std::unique_ptr<ISymbolTable> symbols) {
+ mSymbols = std::move(symbols);
+ }
+
+ IDiagnostics* getDiagnostics() override {
+ assert(mDiagnostics && "test diagnostics not set");
+ return mDiagnostics.get();
+ }
+
+ StringPiece16 getCompilationPackage() override {
+ assert(mCompilationPackage && "package name not set");
+ return mCompilationPackage.value();
+ }
+
+ uint8_t getPackageId() override {
+ assert(mPackageId && "package ID not set");
+ return mPackageId.value();
+ }
+
+ NameMangler* getNameMangler() override {
+ assert(mNameMangler && "test name mangler not set");
+ return mNameMangler.get();
+ }
+};
+
+class ContextBuilder {
+private:
+ std::unique_ptr<Context> mContext = std::unique_ptr<Context>(new Context());
+
+public:
+ ContextBuilder& setCompilationPackage(const StringPiece16& package) {
+ mContext->mCompilationPackage = package.toString();
+ return *this;
+ }
+
+ ContextBuilder& setPackageId(uint8_t id) {
+ mContext->mPackageId = id;
+ return *this;
+ }
+
+ ContextBuilder& setSymbolTable(std::unique_ptr<ISymbolTable> symbols) {
+ mContext->mSymbols = std::move(symbols);
+ return *this;
+ }
+
+ ContextBuilder& setDiagnostics(std::unique_ptr<IDiagnostics> diag) {
+ mContext->mDiagnostics = std::move(diag);
+ return *this;
+ }
+
+ ContextBuilder& setNameManglerPolicy(NameManglerPolicy policy) {
+ mContext->mNameMangler = util::make_unique<NameMangler>(policy);
+ return *this;
+ }
+
+ std::unique_ptr<Context> build() {
+ return std::move(mContext);
+ }
+};
+
+class StaticSymbolTableBuilder {
+private:
+ struct SymbolTable : public ISymbolTable {
+ std::list<std::unique_ptr<Symbol>> mSymbols;
+ std::map<ResourceName, Symbol*> mNameMap;
+ std::map<ResourceId, Symbol*> mIdMap;
+
+ const Symbol* findByName(const ResourceName& name) override {
+ auto iter = mNameMap.find(name);
+ if (iter != mNameMap.end()) {
+ return iter->second;
+ }
+ return nullptr;
+ }
+
+ const Symbol* findById(ResourceId id) override {
+ auto iter = mIdMap.find(id);
+ if (iter != mIdMap.end()) {
+ return iter->second;
+ }
+ return nullptr;
+ }
+ };
+
+ std::unique_ptr<SymbolTable> mSymbolTable = util::make_unique<SymbolTable>();
+
+public:
+ StaticSymbolTableBuilder& addSymbol(const StringPiece16& name, ResourceId id,
+ std::unique_ptr<Attribute> attr = {}) {
+ std::unique_ptr<ISymbolTable::Symbol> symbol = util::make_unique<ISymbolTable::Symbol>(
+ id, std::move(attr));
+ mSymbolTable->mNameMap[parseNameOrDie(name)] = symbol.get();
+ mSymbolTable->mIdMap[id] = symbol.get();
+ mSymbolTable->mSymbols.push_back(std::move(symbol));
+ return *this;
+ }
+
+ std::unique_ptr<ISymbolTable> build() {
+ return std::move(mSymbolTable);
+ }
+};
+
+} // namespace test
+} // namespace aapt
+
+#endif /* AAPT_TEST_CONTEXT_H */
diff --git a/tools/aapt2/todo.txt b/tools/aapt2/todo.txt
deleted file mode 100644
index acc8bfb..0000000
--- a/tools/aapt2/todo.txt
+++ /dev/null
@@ -1,29 +0,0 @@
-XML Files
-X Collect declared IDs
-X Build StringPool
-X Flatten
-
-Resource Table Operations
-X Build Resource Table (with StringPool) from XML.
-X Modify Resource Table.
-X - Copy and transform resources.
-X - Pre-17/21 attr correction.
-X Perform analysis of types.
-X Flatten.
-X Assign resource IDs.
-X Assign public resource IDs.
-X Merge resource tables
-- Assign private attributes to different typespace.
-- Align resource tables
-
-Splits
-- Collect all resources (ids from layouts).
-- Generate resource table from base resources.
-- Generate resource table from individual resources of the required type.
-- Align resource tables (same type/name = same ID).
-
-Fat Apk
-X Collect all resources (ids from layouts).
-X Generate resource tables for all configurations.
-- Align individual resource tables.
-- Merge resource tables.
diff --git a/tools/aapt2/unflatten/BinaryResourceParser.cpp b/tools/aapt2/unflatten/BinaryResourceParser.cpp
new file mode 100644
index 0000000..0d17e84
--- /dev/null
+++ b/tools/aapt2/unflatten/BinaryResourceParser.cpp
@@ -0,0 +1,849 @@
+/*
+ * 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.
+ */
+
+#include "ResourceTable.h"
+#include "ResourceUtils.h"
+#include "ResourceValues.h"
+#include "Source.h"
+#include "ValueVisitor.h"
+
+#include "flatten/ResourceTypeExtensions.h"
+#include "unflatten/BinaryResourceParser.h"
+#include "unflatten/ResChunkPullParser.h"
+#include "util/Util.h"
+
+#include <androidfw/ResourceTypes.h>
+#include <androidfw/TypeWrappers.h>
+#include <base/macros.h>
+
+#include <map>
+#include <string>
+
+namespace aapt {
+
+using namespace android;
+
+/*
+ * Visitor that converts a reference's resource ID to a resource name,
+ * given a mapping from resource ID to resource name.
+ */
+class ReferenceIdToNameVisitor : public ValueVisitor {
+private:
+ const std::map<ResourceId, ResourceName>* mMapping;
+
+public:
+ using ValueVisitor::visit;
+
+ ReferenceIdToNameVisitor(const std::map<ResourceId, ResourceName>* mapping) :
+ mMapping(mapping) {
+ assert(mMapping);
+ }
+
+ void visit(Reference* reference) override {
+ if (!reference->id || !reference->id.value().isValid()) {
+ return;
+ }
+
+ ResourceId id = reference->id.value();
+ auto cacheIter = mMapping->find(id);
+ if (cacheIter != mMapping->end()) {
+ reference->name = cacheIter->second;
+ reference->id = {};
+ }
+ }
+};
+
+BinaryResourceParser::BinaryResourceParser(IAaptContext* context, ResourceTable* table,
+ const Source& source, const void* data, size_t len) :
+ mContext(context), mTable(table), mSource(source), mData(data), mDataLen(len) {
+}
+
+bool BinaryResourceParser::parse() {
+ ResChunkPullParser parser(mData, mDataLen);
+
+ bool error = false;
+ while(ResChunkPullParser::isGoodEvent(parser.next())) {
+ if (parser.getChunk()->type != android::RES_TABLE_TYPE) {
+ mContext->getDiagnostics()->warn(DiagMessage(mSource)
+ << "unknown chunk of type '"
+ << (int) parser.getChunk()->type << "'");
+ continue;
+ }
+
+ if (!parseTable(parser.getChunk())) {
+ error = true;
+ }
+ }
+
+ if (parser.getEvent() == ResChunkPullParser::Event::BadDocument) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "corrupt resource table: "
+ << parser.getLastError());
+ return false;
+ }
+ return !error;
+}
+
+bool BinaryResourceParser::getSymbol(const void* data, ResourceNameRef* outSymbol) {
+ if (!mSymbolEntries || mSymbolEntryCount == 0) {
+ return false;
+ }
+
+ if ((uintptr_t) data < (uintptr_t) mData) {
+ return false;
+ }
+
+ // We only support 32 bit offsets right now.
+ const uintptr_t offset = (uintptr_t) data - (uintptr_t) mData;
+ if (offset > std::numeric_limits<uint32_t>::max()) {
+ return false;
+ }
+
+ for (size_t i = 0; i < mSymbolEntryCount; i++) {
+ if (util::deviceToHost32(mSymbolEntries[i].offset) == offset) {
+ // This offset is a symbol!
+ const StringPiece16 str = util::getString(
+ mSymbolPool, util::deviceToHost32(mSymbolEntries[i].name.index));
+
+ StringPiece16 typeStr;
+ ResourceUtils::extractResourceName(str, &outSymbol->package, &typeStr,
+ &outSymbol->entry);
+ const ResourceType* type = parseResourceType(typeStr);
+ if (!type) {
+ return false;
+ }
+
+ outSymbol->type = *type;
+
+ // Since we scan the symbol table in order, we can start looking for the
+ // next symbol from this point.
+ mSymbolEntryCount -= i + 1;
+ mSymbolEntries += i + 1;
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Parses the SymbolTable_header, which is present on non-final resource tables
+ * after the compile phase.
+ *
+ * | SymbolTable_header |
+ * |--------------------|
+ * |SymbolTable_entry 0 |
+ * |SymbolTable_entry 1 |
+ * | ... |
+ * |SymbolTable_entry n |
+ * |--------------------|
+ *
+ */
+bool BinaryResourceParser::parseSymbolTable(const ResChunk_header* chunk) {
+ const SymbolTable_header* header = convertTo<SymbolTable_header>(chunk);
+ if (!header) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "corrupt SymbolTable_header");
+ return false;
+ }
+
+ const uint32_t entrySizeBytes =
+ util::deviceToHost32(header->count) * sizeof(SymbolTable_entry);
+ if (entrySizeBytes > getChunkDataLen(&header->header)) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "SymbolTable_header data section too long");
+ return false;
+ }
+
+ mSymbolEntries = (const SymbolTable_entry*) getChunkData(&header->header);
+ mSymbolEntryCount = util::deviceToHost32(header->count);
+
+ // Skip over the symbol entries and parse the StringPool chunk that should be next.
+ ResChunkPullParser parser(getChunkData(&header->header) + entrySizeBytes,
+ getChunkDataLen(&header->header) - entrySizeBytes);
+ if (!ResChunkPullParser::isGoodEvent(parser.next())) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "failed to parse chunk in SymbolTable: "
+ << parser.getLastError());
+ return false;
+ }
+
+ const ResChunk_header* nextChunk = parser.getChunk();
+ if (util::deviceToHost16(nextChunk->type) != android::RES_STRING_POOL_TYPE) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "expected string pool in SymbolTable but got "
+ << "chunk of type "
+ << (int) util::deviceToHost16(nextChunk->type));
+ return false;
+ }
+
+ if (mSymbolPool.setTo(nextChunk, util::deviceToHost32(nextChunk->size)) != NO_ERROR) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "corrupt string pool in SymbolTable: "
+ << mSymbolPool.getError());
+ return false;
+ }
+ return true;
+}
+
+/**
+ * Parses the resource table, which contains all the packages, types, and entries.
+ */
+bool BinaryResourceParser::parseTable(const ResChunk_header* chunk) {
+ const ResTable_header* tableHeader = convertTo<ResTable_header>(chunk);
+ if (!tableHeader) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource) << "corrupt ResTable_header chunk");
+ return false;
+ }
+
+ ResChunkPullParser parser(getChunkData(&tableHeader->header),
+ getChunkDataLen(&tableHeader->header));
+ while (ResChunkPullParser::isGoodEvent(parser.next())) {
+ switch (util::deviceToHost16(parser.getChunk()->type)) {
+ case android::RES_STRING_POOL_TYPE:
+ if (mValuePool.getError() == NO_INIT) {
+ status_t err = mValuePool.setTo(parser.getChunk(),
+ util::deviceToHost32(parser.getChunk()->size));
+ if (err != NO_ERROR) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "corrupt string pool in ResTable: "
+ << mValuePool.getError());
+ return false;
+ }
+
+ // Reserve some space for the strings we are going to add.
+ mTable->stringPool.hintWillAdd(mValuePool.size(), mValuePool.styleCount());
+ } else {
+ mContext->getDiagnostics()->warn(DiagMessage(mSource)
+ << "unexpected string pool in ResTable");
+ }
+ break;
+
+ case RES_TABLE_SYMBOL_TABLE_TYPE:
+ if (!parseSymbolTable(parser.getChunk())) {
+ return false;
+ }
+ break;
+
+ case RES_TABLE_SOURCE_POOL_TYPE: {
+ status_t err = mSourcePool.setTo(getChunkData(parser.getChunk()),
+ getChunkDataLen(parser.getChunk()));
+ if (err != NO_ERROR) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "corrupt source string pool in ResTable: "
+ << mSourcePool.getError());
+ return false;
+ }
+ break;
+ }
+
+ case android::RES_TABLE_PACKAGE_TYPE:
+ if (!parsePackage(parser.getChunk())) {
+ return false;
+ }
+ break;
+
+ default:
+ mContext->getDiagnostics()
+ ->warn(DiagMessage(mSource)
+ << "unexpected chunk type "
+ << (int) util::deviceToHost16(parser.getChunk()->type));
+ break;
+ }
+ }
+
+ if (parser.getEvent() == ResChunkPullParser::Event::BadDocument) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "corrupt resource table: " << parser.getLastError());
+ return false;
+ }
+ return true;
+}
+
+
+bool BinaryResourceParser::parsePackage(const ResChunk_header* chunk) {
+ const ResTable_package* packageHeader = convertTo<ResTable_package>(chunk);
+ if (!packageHeader) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "corrupt ResTable_package chunk");
+ return false;
+ }
+
+ uint32_t packageId = util::deviceToHost32(packageHeader->id);
+ if (packageId > std::numeric_limits<uint8_t>::max()) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "package ID is too big (" << packageId << ")");
+ return false;
+ }
+
+ // Extract the package name.
+ size_t len = strnlen16((const char16_t*) packageHeader->name, arraysize(packageHeader->name));
+ std::u16string packageName;
+ packageName.resize(len);
+ for (size_t i = 0; i < len; i++) {
+ packageName[i] = util::deviceToHost16(packageHeader->name[i]);
+ }
+
+ ResourceTablePackage* package = mTable->createPackage(packageName, (uint8_t) packageId);
+ if (!package) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "incompatible package '" << packageName
+ << "' with ID " << packageId);
+ return false;
+ }
+
+ ResChunkPullParser parser(getChunkData(&packageHeader->header),
+ getChunkDataLen(&packageHeader->header));
+ while (ResChunkPullParser::isGoodEvent(parser.next())) {
+ switch (util::deviceToHost16(parser.getChunk()->type)) {
+ case android::RES_STRING_POOL_TYPE:
+ if (mTypePool.getError() == NO_INIT) {
+ status_t err = mTypePool.setTo(parser.getChunk(),
+ util::deviceToHost32(parser.getChunk()->size));
+ if (err != NO_ERROR) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "corrupt type string pool in "
+ << "ResTable_package: "
+ << mTypePool.getError());
+ return false;
+ }
+ } else if (mKeyPool.getError() == NO_INIT) {
+ status_t err = mKeyPool.setTo(parser.getChunk(),
+ util::deviceToHost32(parser.getChunk()->size));
+ if (err != NO_ERROR) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "corrupt key string pool in "
+ << "ResTable_package: "
+ << mKeyPool.getError());
+ return false;
+ }
+ } else {
+ mContext->getDiagnostics()->warn(DiagMessage(mSource) << "unexpected string pool");
+ }
+ break;
+
+ case android::RES_TABLE_TYPE_SPEC_TYPE:
+ if (!parseTypeSpec(parser.getChunk())) {
+ return false;
+ }
+ break;
+
+ case android::RES_TABLE_TYPE_TYPE:
+ if (!parseType(package, parser.getChunk())) {
+ return false;
+ }
+ break;
+
+ case RES_TABLE_PUBLIC_TYPE:
+ if (!parsePublic(package, parser.getChunk())) {
+ return false;
+ }
+ break;
+
+ default:
+ mContext->getDiagnostics()
+ ->warn(DiagMessage(mSource)
+ << "unexpected chunk type "
+ << (int) util::deviceToHost16(parser.getChunk()->type));
+ break;
+ }
+ }
+
+ if (parser.getEvent() == ResChunkPullParser::Event::BadDocument) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "corrupt ResTable_package: "
+ << parser.getLastError());
+ return false;
+ }
+
+ // Now go through the table and change local resource ID references to
+ // symbolic references.
+ ReferenceIdToNameVisitor visitor(&mIdIndex);
+ for (auto& package : mTable->packages) {
+ for (auto& type : package->types) {
+ for (auto& entry : type->entries) {
+ for (auto& configValue : entry->values) {
+ configValue.value->accept(&visitor);
+ }
+ }
+ }
+ }
+ return true;
+}
+
+bool BinaryResourceParser::parsePublic(const ResourceTablePackage* package,
+ const ResChunk_header* chunk) {
+ const Public_header* header = convertTo<Public_header>(chunk);
+ if (!header) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "corrupt Public_header chunk");
+ return false;
+ }
+
+ if (header->typeId == 0) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "invalid type ID "
+ << (int) header->typeId);
+ return false;
+ }
+
+ StringPiece16 typeStr16 = util::getString(mTypePool, header->typeId - 1);
+ const ResourceType* parsedType = parseResourceType(typeStr16);
+ if (!parsedType) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "invalid type '" << typeStr16 << "'");
+ return false;
+ }
+
+ const uintptr_t chunkEnd = (uintptr_t) chunk + util::deviceToHost32(chunk->size);
+ const Public_entry* entry = (const Public_entry*) getChunkData(&header->header);
+ for (uint32_t i = 0; i < util::deviceToHost32(header->count); i++) {
+ if ((uintptr_t) entry + sizeof(*entry) > chunkEnd) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "Public_entry data section is too long");
+ return false;
+ }
+
+ const ResourceId resId(package->id.value(), header->typeId,
+ util::deviceToHost16(entry->entryId));
+
+ const ResourceName name(package->name, *parsedType,
+ util::getString(mKeyPool, entry->key.index).toString());
+
+ Symbol symbol;
+ if (mSourcePool.getError() == NO_ERROR) {
+ symbol.source.path = util::utf16ToUtf8(util::getString(
+ mSourcePool, util::deviceToHost32(entry->source.path.index)));
+ symbol.source.line = util::deviceToHost32(entry->source.line);
+ }
+
+ StringPiece16 comment = util::getString(mSourcePool,
+ util::deviceToHost32(entry->source.comment.index));
+ if (!comment.empty()) {
+ symbol.comment = comment.toString();
+ }
+
+ switch (util::deviceToHost16(entry->state)) {
+ case Public_entry::kPrivate:
+ symbol.state = SymbolState::kPrivate;
+ break;
+
+ case Public_entry::kPublic:
+ symbol.state = SymbolState::kPublic;
+ break;
+ }
+
+ if (!mTable->setSymbolStateAllowMangled(name, resId, symbol, mContext->getDiagnostics())) {
+ return false;
+ }
+
+ // Add this resource name->id mapping to the index so
+ // that we can resolve all ID references to name references.
+ auto cacheIter = mIdIndex.find(resId);
+ if (cacheIter == mIdIndex.end()) {
+ mIdIndex.insert({ resId, name });
+ }
+
+ entry++;
+ }
+ return true;
+}
+
+bool BinaryResourceParser::parseTypeSpec(const ResChunk_header* chunk) {
+ if (mTypePool.getError() != NO_ERROR) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "missing type string pool");
+ return false;
+ }
+
+ const ResTable_typeSpec* typeSpec = convertTo<ResTable_typeSpec>(chunk);
+ if (!typeSpec) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "corrupt ResTable_typeSpec chunk");
+ return false;
+ }
+
+ if (typeSpec->id == 0) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "ResTable_typeSpec has invalid id: " << typeSpec->id);
+ return false;
+ }
+ return true;
+}
+
+bool BinaryResourceParser::parseType(const ResourceTablePackage* package,
+ const ResChunk_header* chunk) {
+ if (mTypePool.getError() != NO_ERROR) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "missing type string pool");
+ return false;
+ }
+
+ if (mKeyPool.getError() != NO_ERROR) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "missing key string pool");
+ return false;
+ }
+
+ const ResTable_type* type = convertTo<ResTable_type>(chunk);
+ if (!type) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "corrupt ResTable_type chunk");
+ return false;
+ }
+
+ if (type->id == 0) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "ResTable_type has invalid id: " << (int) type->id);
+ return false;
+ }
+
+ ConfigDescription config;
+ config.copyFromDtoH(type->config);
+
+ StringPiece16 typeStr16 = util::getString(mTypePool, type->id - 1);
+
+ const ResourceType* parsedType = parseResourceType(typeStr16);
+ if (!parsedType) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "invalid type name '" << typeStr16
+ << "' for type with ID " << (int) type->id);
+ return false;
+ }
+
+ TypeVariant tv(type);
+ for (auto it = tv.beginEntries(); it != tv.endEntries(); ++it) {
+ const ResTable_entry* entry = *it;
+ if (!entry) {
+ continue;
+ }
+
+ const ResourceName name(package->name, *parsedType,
+ util::getString(mKeyPool,
+ util::deviceToHost32(entry->key.index)).toString());
+
+ const ResourceId resId(package->id.value(), type->id, static_cast<uint16_t>(it.index()));
+
+ std::unique_ptr<Value> resourceValue;
+ const ResTable_entry_source* sourceBlock = nullptr;
+
+ if (entry->flags & ResTable_entry::FLAG_COMPLEX) {
+ const ResTable_map_entry* mapEntry = static_cast<const ResTable_map_entry*>(entry);
+ if (util::deviceToHost32(mapEntry->size) - sizeof(*mapEntry) == sizeof(*sourceBlock)) {
+ const uint8_t* data = (const uint8_t*) mapEntry;
+ data += util::deviceToHost32(mapEntry->size) - sizeof(*sourceBlock);
+ sourceBlock = (const ResTable_entry_source*) data;
+ }
+
+ // TODO(adamlesinski): Check that the entry count is valid.
+ resourceValue = parseMapEntry(name, config, mapEntry);
+ } else {
+ if (util::deviceToHost32(entry->size) - sizeof(*entry) == sizeof(*sourceBlock)) {
+ const uint8_t* data = (const uint8_t*) entry;
+ data += util::deviceToHost32(entry->size) - sizeof(*sourceBlock);
+ sourceBlock = (const ResTable_entry_source*) data;
+ }
+
+ const Res_value* value = (const Res_value*)(
+ (const uint8_t*) entry + util::deviceToHost32(entry->size));
+ resourceValue = parseValue(name, config, value, entry->flags);
+ }
+
+ assert(resourceValue && "failed to interpret valid resource");
+
+ Source source = mSource;
+ if (sourceBlock) {
+ size_t len;
+ const char* str = mSourcePool.string8At(util::deviceToHost32(sourceBlock->path.index),
+ &len);
+ if (str) {
+ source.path.assign(str, len);
+ }
+ source.line = util::deviceToHost32(sourceBlock->line);
+ }
+
+ StringPiece16 comment = util::getString(mSourcePool,
+ util::deviceToHost32(sourceBlock->comment.index));
+ if (!comment.empty()) {
+ resourceValue->setComment(comment);
+ }
+
+ resourceValue->setSource(source);
+ if (!mTable->addResourceAllowMangled(name, config, std::move(resourceValue),
+ mContext->getDiagnostics())) {
+ return false;
+ }
+
+ if ((entry->flags & ResTable_entry::FLAG_PUBLIC) != 0) {
+ Symbol symbol;
+ symbol.state = SymbolState::kPublic;
+ symbol.source = mSource.withLine(0);
+ if (!mTable->setSymbolStateAllowMangled(name, resId, symbol,
+ mContext->getDiagnostics())) {
+ return false;
+ }
+ }
+
+ // Add this resource name->id mapping to the index so
+ // that we can resolve all ID references to name references.
+ auto cacheIter = mIdIndex.find(resId);
+ if (cacheIter == mIdIndex.end()) {
+ mIdIndex.insert({ resId, name });
+ }
+ }
+ return true;
+}
+
+std::unique_ptr<Item> BinaryResourceParser::parseValue(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const Res_value* value,
+ uint16_t flags) {
+ if (name.type == ResourceType::kId) {
+ return util::make_unique<Id>();
+ }
+
+ const uint32_t data = util::deviceToHost32(value->data);
+
+ if (value->dataType == Res_value::TYPE_STRING) {
+ StringPiece16 str = util::getString(mValuePool, data);
+
+ const ResStringPool_span* spans = mValuePool.styleAt(data);
+
+ // Check if the string has a valid style associated with it.
+ if (spans != nullptr && spans->name.index != ResStringPool_span::END) {
+ StyleString styleStr = { str.toString() };
+ while (spans->name.index != ResStringPool_span::END) {
+ styleStr.spans.push_back(Span{
+ util::getString(mValuePool, spans->name.index).toString(),
+ spans->firstChar,
+ spans->lastChar
+ });
+ spans++;
+ }
+ return util::make_unique<StyledString>(mTable->stringPool.makeRef(
+ styleStr, StringPool::Context{1, config}));
+ } else {
+ if (name.type != ResourceType::kString &&
+ util::stringStartsWith<char16_t>(str, u"res/")) {
+ // This must be a FileReference.
+ return util::make_unique<FileReference>(mTable->stringPool.makeRef(
+ str, StringPool::Context{ 0, config }));
+ }
+
+ // There are no styles associated with this string, so treat it as
+ // a simple string.
+ return util::make_unique<String>(mTable->stringPool.makeRef(
+ str, StringPool::Context{1, config}));
+ }
+ }
+
+ if (value->dataType == Res_value::TYPE_REFERENCE ||
+ value->dataType == Res_value::TYPE_ATTRIBUTE) {
+ const Reference::Type type = (value->dataType == Res_value::TYPE_REFERENCE) ?
+ Reference::Type::kResource : Reference::Type::kAttribute;
+
+ if (data != 0) {
+ // This is a normal reference.
+ return util::make_unique<Reference>(data, type);
+ }
+
+ // This reference has an invalid ID. Check if it is an unresolved symbol.
+ ResourceNameRef symbol;
+ if (getSymbol(&value->data, &symbol)) {
+ return util::make_unique<Reference>(symbol, type);
+ }
+
+ // This is not an unresolved symbol, so it must be the magic @null reference.
+ Res_value nullType = {};
+ nullType.dataType = Res_value::TYPE_REFERENCE;
+ return util::make_unique<BinaryPrimitive>(nullType);
+ }
+
+ if (value->dataType == ExtendedTypes::TYPE_RAW_STRING) {
+ return util::make_unique<RawString>(mTable->stringPool.makeRef(
+ util::getString(mValuePool, data), StringPool::Context{ 1, config }));
+ }
+
+ // Treat this as a raw binary primitive.
+ return util::make_unique<BinaryPrimitive>(*value);
+}
+
+std::unique_ptr<Value> BinaryResourceParser::parseMapEntry(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const ResTable_map_entry* map) {
+ switch (name.type) {
+ case ResourceType::kStyle:
+ return parseStyle(name, config, map);
+ case ResourceType::kAttrPrivate:
+ // fallthrough
+ case ResourceType::kAttr:
+ return parseAttr(name, config, map);
+ case ResourceType::kArray:
+ return parseArray(name, config, map);
+ case ResourceType::kStyleable:
+ return parseStyleable(name, config, map);
+ case ResourceType::kPlurals:
+ return parsePlural(name, config, map);
+ default:
+ assert(false && "unknown map type");
+ break;
+ }
+ return {};
+}
+
+std::unique_ptr<Style> BinaryResourceParser::parseStyle(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const ResTable_map_entry* map) {
+ std::unique_ptr<Style> style = util::make_unique<Style>();
+ if (util::deviceToHost32(map->parent.ident) == 0) {
+ // The parent is either not set or it is an unresolved symbol.
+ // Check to see if it is a symbol.
+ ResourceNameRef symbol;
+ if (getSymbol(&map->parent.ident, &symbol)) {
+ style->parent = Reference(symbol.toResourceName());
+ }
+ } else {
+ // The parent is a regular reference to a resource.
+ style->parent = Reference(util::deviceToHost32(map->parent.ident));
+ }
+
+ for (const ResTable_map& mapEntry : map) {
+ style->entries.emplace_back();
+ Style::Entry& styleEntry = style->entries.back();
+
+ if (util::deviceToHost32(mapEntry.name.ident) == 0) {
+ // The map entry's key (attribute) is not set. This must be
+ // a symbol reference, so resolve it.
+ ResourceNameRef symbol;
+ bool result = getSymbol(&mapEntry.name.ident, &symbol);
+ assert(result);
+ styleEntry.key.name = symbol.toResourceName();
+ } else {
+ // The map entry's key (attribute) is a regular reference.
+ styleEntry.key.id = ResourceId(util::deviceToHost32(mapEntry.name.ident));
+ }
+
+ // Parse the attribute's value.
+ styleEntry.value = parseValue(name, config, &mapEntry.value, 0);
+ assert(styleEntry.value);
+ }
+ return style;
+}
+
+std::unique_ptr<Attribute> BinaryResourceParser::parseAttr(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const ResTable_map_entry* map) {
+ const bool isWeak = (util::deviceToHost16(map->flags) & ResTable_entry::FLAG_WEAK) != 0;
+ std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(isWeak);
+
+ // First we must discover what type of attribute this is. Find the type mask.
+ auto typeMaskIter = std::find_if(begin(map), end(map), [](const ResTable_map& entry) -> bool {
+ return util::deviceToHost32(entry.name.ident) == ResTable_map::ATTR_TYPE;
+ });
+
+ if (typeMaskIter != end(map)) {
+ attr->typeMask = util::deviceToHost32(typeMaskIter->value.data);
+ }
+
+ if (attr->typeMask & (ResTable_map::TYPE_ENUM | ResTable_map::TYPE_FLAGS)) {
+ for (const ResTable_map& mapEntry : map) {
+ if (Res_INTERNALID(util::deviceToHost32(mapEntry.name.ident))) {
+ continue;
+ }
+
+ Attribute::Symbol symbol;
+ symbol.value = util::deviceToHost32(mapEntry.value.data);
+ if (util::deviceToHost32(mapEntry.name.ident) == 0) {
+ // The map entry's key (id) is not set. This must be
+ // a symbol reference, so resolve it.
+ ResourceNameRef symbolName;
+ bool result = getSymbol(&mapEntry.name.ident, &symbolName);
+ assert(result);
+ symbol.symbol.name = symbolName.toResourceName();
+ } else {
+ // The map entry's key (id) is a regular reference.
+ symbol.symbol.id = ResourceId(util::deviceToHost32(mapEntry.name.ident));
+ }
+
+ attr->symbols.push_back(std::move(symbol));
+ }
+ }
+
+ // TODO(adamlesinski): Find min, max, i80n, etc attributes.
+ return attr;
+}
+
+std::unique_ptr<Array> BinaryResourceParser::parseArray(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const ResTable_map_entry* map) {
+ std::unique_ptr<Array> array = util::make_unique<Array>();
+ for (const ResTable_map& mapEntry : map) {
+ array->items.push_back(parseValue(name, config, &mapEntry.value, 0));
+ }
+ return array;
+}
+
+std::unique_ptr<Styleable> BinaryResourceParser::parseStyleable(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const ResTable_map_entry* map) {
+ std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
+ for (const ResTable_map& mapEntry : map) {
+ if (util::deviceToHost32(mapEntry.name.ident) == 0) {
+ // The map entry's key (attribute) is not set. This must be
+ // a symbol reference, so resolve it.
+ ResourceNameRef symbol;
+ bool result = getSymbol(&mapEntry.name.ident, &symbol);
+ assert(result);
+ styleable->entries.emplace_back(symbol);
+ } else {
+ // The map entry's key (attribute) is a regular reference.
+ styleable->entries.emplace_back(util::deviceToHost32(mapEntry.name.ident));
+ }
+ }
+ return styleable;
+}
+
+std::unique_ptr<Plural> BinaryResourceParser::parsePlural(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const ResTable_map_entry* map) {
+ std::unique_ptr<Plural> plural = util::make_unique<Plural>();
+ for (const ResTable_map& mapEntry : map) {
+ std::unique_ptr<Item> item = parseValue(name, config, &mapEntry.value, 0);
+
+ switch (util::deviceToHost32(mapEntry.name.ident)) {
+ case android::ResTable_map::ATTR_ZERO:
+ plural->values[Plural::Zero] = std::move(item);
+ break;
+ case android::ResTable_map::ATTR_ONE:
+ plural->values[Plural::One] = std::move(item);
+ break;
+ case android::ResTable_map::ATTR_TWO:
+ plural->values[Plural::Two] = std::move(item);
+ break;
+ case android::ResTable_map::ATTR_FEW:
+ plural->values[Plural::Few] = std::move(item);
+ break;
+ case android::ResTable_map::ATTR_MANY:
+ plural->values[Plural::Many] = std::move(item);
+ break;
+ case android::ResTable_map::ATTR_OTHER:
+ plural->values[Plural::Other] = std::move(item);
+ break;
+ }
+ }
+ return plural;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/unflatten/BinaryResourceParser.h b/tools/aapt2/unflatten/BinaryResourceParser.h
new file mode 100644
index 0000000..02c4081
--- /dev/null
+++ b/tools/aapt2/unflatten/BinaryResourceParser.h
@@ -0,0 +1,153 @@
+/*
+ * 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_BINARY_RESOURCE_PARSER_H
+#define AAPT_BINARY_RESOURCE_PARSER_H
+
+#include "ResourceTable.h"
+#include "ResourceValues.h"
+#include "Source.h"
+
+#include "process/IResourceTableConsumer.h"
+#include "util/Util.h"
+
+#include <androidfw/ResourceTypes.h>
+#include <string>
+
+namespace aapt {
+
+struct SymbolTable_entry;
+
+/*
+ * Parses a binary resource table (resources.arsc) and adds the entries
+ * to a ResourceTable. This is different than the libandroidfw ResTable
+ * in that it scans the table from top to bottom and doesn't require
+ * support for random access. It is also able to parse non-runtime
+ * chunks and types.
+ */
+class BinaryResourceParser {
+public:
+ /*
+ * Creates a parser, which will read `len` bytes from `data`, and
+ * add any resources parsed to `table`. `source` is for logging purposes.
+ */
+ BinaryResourceParser(IAaptContext* context, ResourceTable* table, const Source& source,
+ const void* data, size_t dataLen);
+
+ BinaryResourceParser(const BinaryResourceParser&) = delete; // No copy.
+
+ /*
+ * Parses the binary resource table and returns true if successful.
+ */
+ bool parse();
+
+private:
+ // Helper method to retrieve the symbol name for a given table offset specified
+ // as a pointer.
+ bool getSymbol(const void* data, ResourceNameRef* outSymbol);
+
+ bool parseTable(const android::ResChunk_header* chunk);
+ bool parseSymbolTable(const android::ResChunk_header* chunk);
+ bool parsePackage(const android::ResChunk_header* chunk);
+ bool parsePublic(const ResourceTablePackage* package, const android::ResChunk_header* chunk);
+ bool parseTypeSpec(const android::ResChunk_header* chunk);
+ bool parseType(const ResourceTablePackage* package, const android::ResChunk_header* chunk);
+
+ std::unique_ptr<Item> parseValue(const ResourceNameRef& name, const ConfigDescription& config,
+ const android::Res_value* value, uint16_t flags);
+
+ std::unique_ptr<Value> parseMapEntry(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const android::ResTable_map_entry* map);
+
+ std::unique_ptr<Style> parseStyle(const ResourceNameRef& name, const ConfigDescription& config,
+ const android::ResTable_map_entry* map);
+
+ std::unique_ptr<Attribute> parseAttr(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const android::ResTable_map_entry* map);
+
+ std::unique_ptr<Array> parseArray(const ResourceNameRef& name, const ConfigDescription& config,
+ const android::ResTable_map_entry* map);
+
+ std::unique_ptr<Plural> parsePlural(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const android::ResTable_map_entry* map);
+
+ std::unique_ptr<Styleable> parseStyleable(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const android::ResTable_map_entry* map);
+
+ IAaptContext* mContext;
+ ResourceTable* mTable;
+
+ const Source mSource;
+
+ const void* mData;
+ const size_t mDataLen;
+
+ // The array of symbol entries. Each element points to an offset
+ // in the table and an index into the symbol table string pool.
+ const SymbolTable_entry* mSymbolEntries = nullptr;
+
+ // Number of symbol entries.
+ size_t mSymbolEntryCount = 0;
+
+ // The symbol table string pool. Holds the names of symbols
+ // referenced in this table but not defined nor resolved to an
+ // ID.
+ android::ResStringPool mSymbolPool;
+
+ // The source string pool. Resource entries may have an extra
+ // field that points into this string pool, which denotes where
+ // the resource was parsed from originally.
+ android::ResStringPool mSourcePool;
+
+ // The standard value string pool for resource values.
+ android::ResStringPool mValuePool;
+
+ // The string pool that holds the names of the types defined
+ // in this table.
+ android::ResStringPool mTypePool;
+
+ // The string pool that holds the names of the entries defined
+ // in this table.
+ android::ResStringPool mKeyPool;
+
+ // A mapping of resource ID to resource name. When we finish parsing
+ // we use this to convert all resource IDs to symbolic references.
+ std::map<ResourceId, ResourceName> mIdIndex;
+};
+
+} // namespace aapt
+
+namespace android {
+
+/**
+ * Iterator functionality for ResTable_map_entry.
+ */
+
+inline const ResTable_map* begin(const ResTable_map_entry* map) {
+ return (const ResTable_map*)((const uint8_t*) map + aapt::util::deviceToHost32(map->size));
+}
+
+inline const ResTable_map* end(const ResTable_map_entry* map) {
+ return begin(map) + aapt::util::deviceToHost32(map->count);
+}
+
+} // namespace android
+
+#endif // AAPT_BINARY_RESOURCE_PARSER_H
diff --git a/tools/aapt2/unflatten/FileExportHeaderReader.h b/tools/aapt2/unflatten/FileExportHeaderReader.h
new file mode 100644
index 0000000..e552ea1
--- /dev/null
+++ b/tools/aapt2/unflatten/FileExportHeaderReader.h
@@ -0,0 +1,159 @@
+/*
+ * 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_UNFLATTEN_FILEEXPORTHEADERREADER_H
+#define AAPT_UNFLATTEN_FILEEXPORTHEADERREADER_H
+
+#include "ResChunkPullParser.h"
+#include "Resource.h"
+#include "ResourceUtils.h"
+
+#include "flatten/ResourceTypeExtensions.h"
+#include "util/StringPiece.h"
+#include "util/Util.h"
+
+#include <androidfw/ResourceTypes.h>
+#include <sstream>
+#include <string>
+
+namespace aapt {
+
+static ssize_t parseFileExportHeaderImpl(const void* data, const size_t len,
+ const FileExport_header** outFileExport,
+ const ExportedSymbol** outExportedSymbolIndices,
+ android::ResStringPool* outStringPool,
+ std::string* outError) {
+ ResChunkPullParser parser(data, len);
+ if (!ResChunkPullParser::isGoodEvent(parser.next())) {
+ if (outError) *outError = parser.getLastError();
+ return -1;
+ }
+
+ if (util::deviceToHost16(parser.getChunk()->type) != RES_FILE_EXPORT_TYPE) {
+ if (outError) *outError = "no FileExport_header found";
+ return -1;
+ }
+
+ const FileExport_header* fileExport = convertTo<FileExport_header>(parser.getChunk());
+ if (!fileExport) {
+ if (outError) *outError = "corrupt FileExport_header";
+ return -1;
+ }
+
+ if (memcmp(fileExport->magic, "AAPT", sizeof(fileExport->magic)) != 0) {
+ if (outError) *outError = "invalid magic value";
+ return -1;
+ }
+
+ const size_t exportedSymbolCount = util::deviceToHost32(fileExport->exportedSymbolCount);
+
+ // Verify that we have enough space for all those symbols.
+ size_t dataLen = getChunkDataLen(&fileExport->header);
+ if (exportedSymbolCount > dataLen / sizeof(ExportedSymbol)) {
+ if (outError) *outError = "too many symbols";
+ return -1;
+ }
+
+ const size_t symbolIndicesSize = exportedSymbolCount * sizeof(ExportedSymbol);
+
+ const void* strPoolData = getChunkData(&fileExport->header) + symbolIndicesSize;
+ const size_t strPoolDataLen = dataLen - symbolIndicesSize;
+ if (outStringPool->setTo(strPoolData, strPoolDataLen, false) != android::NO_ERROR) {
+ if (outError) *outError = "corrupt string pool";
+ return -1;
+ }
+
+ *outFileExport = fileExport;
+ *outExportedSymbolIndices = (const ExportedSymbol*) getChunkData(
+ &fileExport->header);
+ return util::deviceToHost16(fileExport->header.headerSize) + symbolIndicesSize +
+ outStringPool->bytes();
+}
+
+static ssize_t getWrappedDataOffset(const void* data, size_t len, std::string* outError) {
+ const FileExport_header* header = nullptr;
+ const ExportedSymbol* entries = nullptr;
+ android::ResStringPool pool;
+ return parseFileExportHeaderImpl(data, len, &header, &entries, &pool, outError);
+}
+
+/**
+ * Reads the FileExport_header and populates outRes with the values in that header.
+ */
+static ssize_t unwrapFileExportHeader(const void* data, size_t len, ResourceFile* outRes,
+ std::string* outError) {
+
+ const FileExport_header* fileExport = nullptr;
+ const ExportedSymbol* entries = nullptr;
+ android::ResStringPool symbolPool;
+ const ssize_t offset = parseFileExportHeaderImpl(data, len, &fileExport, &entries, &symbolPool,
+ outError);
+ if (offset < 0) {
+ return offset;
+ }
+
+ const size_t exportedSymbolCount = util::deviceToHost32(fileExport->exportedSymbolCount);
+ outRes->exportedSymbols.clear();
+ outRes->exportedSymbols.reserve(exportedSymbolCount);
+
+ for (size_t i = 0; i < exportedSymbolCount; i++) {
+ const StringPiece16 str = util::getString(symbolPool,
+ util::deviceToHost32(entries[i].name.index));
+ StringPiece16 packageStr, typeStr, entryStr;
+ ResourceUtils::extractResourceName(str, &packageStr, &typeStr, &entryStr);
+ const ResourceType* resType = parseResourceType(typeStr);
+ if (!resType || entryStr.empty()) {
+ if (outError) {
+ std::stringstream errorStr;
+ errorStr << "invalid exported symbol at index="
+ << util::deviceToHost32(entries[i].name.index)
+ << " (" << str << ")";
+ *outError = errorStr.str();
+ }
+ return -1;
+ }
+
+ outRes->exportedSymbols.push_back(SourcedResourceName{
+ ResourceName{ packageStr.toString(), *resType, entryStr.toString() },
+ util::deviceToHost32(entries[i].line) });
+ }
+
+ const StringPiece16 str = util::getString(symbolPool,
+ util::deviceToHost32(fileExport->name.index));
+ StringPiece16 packageStr, typeStr, entryStr;
+ ResourceUtils::extractResourceName(str, &packageStr, &typeStr, &entryStr);
+ const ResourceType* resType = parseResourceType(typeStr);
+ if (!resType || entryStr.empty()) {
+ if (outError) {
+ std::stringstream errorStr;
+ errorStr << "invalid resource name at index="
+ << util::deviceToHost32(fileExport->name.index)
+ << " (" << str << ")";
+ *outError = errorStr.str();
+ }
+ return -1;
+ }
+
+ outRes->name = ResourceName{ packageStr.toString(), *resType, entryStr.toString() };
+ outRes->source.path = util::utf16ToUtf8(
+ util::getString(symbolPool, util::deviceToHost32(fileExport->source.index)));
+ outRes->config.copyFromDtoH(fileExport->config);
+ return offset;
+}
+
+} // namespace aapt
+
+#endif /* AAPT_UNFLATTEN_FILEEXPORTHEADERREADER_H */
diff --git a/tools/aapt2/unflatten/FileExportHeaderReader_test.cpp b/tools/aapt2/unflatten/FileExportHeaderReader_test.cpp
new file mode 100644
index 0000000..a76c83b
--- /dev/null
+++ b/tools/aapt2/unflatten/FileExportHeaderReader_test.cpp
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+#include "Resource.h"
+
+#include "flatten/FileExportWriter.h"
+#include "unflatten/FileExportHeaderReader.h"
+#include "util/BigBuffer.h"
+#include "util/Util.h"
+
+#include "test/Common.h"
+
+#include <gtest/gtest.h>
+
+namespace aapt {
+
+TEST(FileExportHeaderReaderTest, ReadHeaderWithNoSymbolExports) {
+ ResourceFile resFile = {
+ test::parseNameOrDie(u"@android:layout/main.xml"),
+ test::parseConfigOrDie("sw600dp-v4"),
+ Source{ "res/layout/main.xml" },
+ };
+
+ BigBuffer buffer(1024);
+ ChunkWriter writer = wrapBufferWithFileExportHeader(&buffer, &resFile);
+ *writer.getBuffer()->nextBlock<uint32_t>() = 42u;
+ writer.finish();
+
+ std::unique_ptr<uint8_t[]> data = util::copy(buffer);
+
+ ResourceFile actualResFile;
+
+ ssize_t offset = unwrapFileExportHeader(data.get(), buffer.size(), &actualResFile, nullptr);
+ ASSERT_GT(offset, 0);
+
+ EXPECT_EQ(offset, getWrappedDataOffset(data.get(), buffer.size(), nullptr));
+
+ EXPECT_EQ(actualResFile.config, test::parseConfigOrDie("sw600dp-v4"));
+ EXPECT_EQ(actualResFile.name, test::parseNameOrDie(u"@android:layout/main.xml"));
+ EXPECT_EQ(actualResFile.source.path, "res/layout/main.xml");
+
+ EXPECT_EQ(*(uint32_t*)(data.get() + offset), 42u);
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/unflatten/ResChunkPullParser.cpp b/tools/aapt2/unflatten/ResChunkPullParser.cpp
new file mode 100644
index 0000000..6f8bb1b
--- /dev/null
+++ b/tools/aapt2/unflatten/ResChunkPullParser.cpp
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+#include "unflatten/ResChunkPullParser.h"
+#include "util/Util.h"
+
+#include <androidfw/ResourceTypes.h>
+#include <cstddef>
+
+namespace aapt {
+
+using android::ResChunk_header;
+
+ResChunkPullParser::Event ResChunkPullParser::next() {
+ if (!isGoodEvent(mEvent)) {
+ return mEvent;
+ }
+
+ if (mEvent == Event::StartDocument) {
+ mCurrentChunk = mData;
+ } else {
+ mCurrentChunk = (const ResChunk_header*)
+ (((const char*) mCurrentChunk) + util::deviceToHost32(mCurrentChunk->size));
+ }
+
+ const std::ptrdiff_t diff = (const char*) mCurrentChunk - (const char*) mData;
+ assert(diff >= 0 && "diff is negative");
+ const size_t offset = static_cast<const size_t>(diff);
+
+ if (offset == mLen) {
+ mCurrentChunk = nullptr;
+ return (mEvent = Event::EndDocument);
+ } else if (offset + sizeof(ResChunk_header) > mLen) {
+ mLastError = "chunk is past the end of the document";
+ mCurrentChunk = nullptr;
+ return (mEvent = Event::BadDocument);
+ }
+
+ if (util::deviceToHost16(mCurrentChunk->headerSize) < sizeof(ResChunk_header)) {
+ mLastError = "chunk has too small header";
+ mCurrentChunk = nullptr;
+ return (mEvent = Event::BadDocument);
+ } else if (util::deviceToHost32(mCurrentChunk->size) <
+ util::deviceToHost16(mCurrentChunk->headerSize)) {
+ mLastError = "chunk's total size is smaller than header";
+ mCurrentChunk = nullptr;
+ return (mEvent = Event::BadDocument);
+ } else if (offset + util::deviceToHost32(mCurrentChunk->size) > mLen) {
+ mLastError = "chunk's data extends past the end of the document";
+ mCurrentChunk = nullptr;
+ return (mEvent = Event::BadDocument);
+ }
+ return (mEvent = Event::Chunk);
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/unflatten/ResChunkPullParser.h b/tools/aapt2/unflatten/ResChunkPullParser.h
new file mode 100644
index 0000000..a51d5bf
--- /dev/null
+++ b/tools/aapt2/unflatten/ResChunkPullParser.h
@@ -0,0 +1,124 @@
+/*
+ * 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_RES_CHUNK_PULL_PARSER_H
+#define AAPT_RES_CHUNK_PULL_PARSER_H
+
+#include "util/Util.h"
+
+#include <androidfw/ResourceTypes.h>
+#include <string>
+
+namespace aapt {
+
+/**
+ * A pull parser, modeled after XmlPullParser, that reads
+ * android::ResChunk_header structs from a block of data.
+ *
+ * An android::ResChunk_header specifies a type, headerSize,
+ * and size. The pull parser will verify that the chunk's size
+ * doesn't extend beyond the available data, and will iterate
+ * over each chunk in the given block of data.
+ *
+ * Processing nested chunks is done by creating a new ResChunkPullParser
+ * pointing to the data portion of a chunk.
+ */
+class ResChunkPullParser {
+public:
+ enum class Event {
+ StartDocument,
+ EndDocument,
+ BadDocument,
+
+ Chunk,
+ };
+
+ /**
+ * Returns false if the event is EndDocument or BadDocument.
+ */
+ static bool isGoodEvent(Event event);
+
+ /**
+ * Create a ResChunkPullParser to read android::ResChunk_headers
+ * from the memory pointed to by data, of len bytes.
+ */
+ ResChunkPullParser(const void* data, size_t len);
+
+ ResChunkPullParser(const ResChunkPullParser&) = delete;
+
+ Event getEvent() const;
+ const std::string& getLastError() const;
+ const android::ResChunk_header* getChunk() const;
+
+ /**
+ * Move to the next android::ResChunk_header.
+ */
+ Event next();
+
+private:
+ Event mEvent;
+ const android::ResChunk_header* mData;
+ size_t mLen;
+ const android::ResChunk_header* mCurrentChunk;
+ std::string mLastError;
+};
+
+template <typename T>
+inline static const T* convertTo(const android::ResChunk_header* chunk) {
+ if (util::deviceToHost16(chunk->headerSize) < sizeof(T)) {
+ return nullptr;
+ }
+ return reinterpret_cast<const T*>(chunk);
+}
+
+inline static const uint8_t* getChunkData(const android::ResChunk_header* chunk) {
+ return reinterpret_cast<const uint8_t*>(chunk) + util::deviceToHost16(chunk->headerSize);
+}
+
+inline static uint32_t getChunkDataLen(const android::ResChunk_header* chunk) {
+ return util::deviceToHost32(chunk->size) - util::deviceToHost16(chunk->headerSize);
+}
+
+//
+// Implementation
+//
+
+inline bool ResChunkPullParser::isGoodEvent(ResChunkPullParser::Event event) {
+ return event != Event::EndDocument && event != Event::BadDocument;
+}
+
+inline ResChunkPullParser::ResChunkPullParser(const void* data, size_t len) :
+ mEvent(Event::StartDocument),
+ mData(reinterpret_cast<const android::ResChunk_header*>(data)),
+ mLen(len),
+ mCurrentChunk(nullptr) {
+}
+
+inline ResChunkPullParser::Event ResChunkPullParser::getEvent() const {
+ return mEvent;
+}
+
+inline const std::string& ResChunkPullParser::getLastError() const {
+ return mLastError;
+}
+
+inline const android::ResChunk_header* ResChunkPullParser::getChunk() const {
+ return mCurrentChunk;
+}
+
+} // namespace aapt
+
+#endif // AAPT_RES_CHUNK_PULL_PARSER_H
diff --git a/tools/aapt2/util/BigBuffer.cpp b/tools/aapt2/util/BigBuffer.cpp
new file mode 100644
index 0000000..c88e3c1
--- /dev/null
+++ b/tools/aapt2/util/BigBuffer.cpp
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#include "util/BigBuffer.h"
+
+#include <algorithm>
+#include <memory>
+#include <vector>
+
+namespace aapt {
+
+void* BigBuffer::nextBlockImpl(size_t size) {
+ if (!mBlocks.empty()) {
+ Block& block = mBlocks.back();
+ if (block.mBlockSize - block.size >= size) {
+ void* outBuffer = block.buffer.get() + block.size;
+ block.size += size;
+ mSize += size;
+ return outBuffer;
+ }
+ }
+
+ const size_t actualSize = std::max(mBlockSize, size);
+
+ Block block = {};
+
+ // Zero-allocate the block's buffer.
+ block.buffer = std::unique_ptr<uint8_t[]>(new uint8_t[actualSize]());
+ assert(block.buffer);
+
+ block.size = size;
+ block.mBlockSize = actualSize;
+
+ mBlocks.push_back(std::move(block));
+ mSize += size;
+ return mBlocks.back().buffer.get();
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/util/BigBuffer.h b/tools/aapt2/util/BigBuffer.h
new file mode 100644
index 0000000..cad2a2e
--- /dev/null
+++ b/tools/aapt2/util/BigBuffer.h
@@ -0,0 +1,161 @@
+/*
+ * 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_BIG_BUFFER_H
+#define AAPT_BIG_BUFFER_H
+
+#include <cassert>
+#include <cstring>
+#include <memory>
+#include <type_traits>
+#include <vector>
+
+namespace aapt {
+
+/**
+ * Inspired by protobuf's ZeroCopyOutputStream, offers blocks of memory
+ * in which to write without knowing the full size of the entire payload.
+ * This is essentially a list of memory blocks. As one fills up, another
+ * block is allocated and appended to the end of the list.
+ */
+class BigBuffer {
+public:
+ /**
+ * A contiguous block of allocated memory.
+ */
+ struct Block {
+ /**
+ * Pointer to the memory.
+ */
+ std::unique_ptr<uint8_t[]> buffer;
+
+ /**
+ * Size of memory that is currently occupied. The actual
+ * allocation may be larger.
+ */
+ size_t size;
+
+ private:
+ friend class BigBuffer;
+
+ /**
+ * The size of the memory block allocation.
+ */
+ size_t mBlockSize;
+ };
+
+ typedef std::vector<Block>::const_iterator const_iterator;
+
+ /**
+ * Create a BigBuffer with block allocation sizes
+ * of blockSize.
+ */
+ BigBuffer(size_t blockSize);
+
+ BigBuffer(const BigBuffer&) = delete; // No copying.
+
+ BigBuffer(BigBuffer&& rhs);
+
+ /**
+ * Number of occupied bytes in all the allocated blocks.
+ */
+ size_t size() const;
+
+ /**
+ * Returns a pointer to an array of T, where T is
+ * a POD type. The elements are zero-initialized.
+ */
+ template <typename T>
+ T* nextBlock(size_t count = 1);
+
+ /**
+ * Moves the specified BigBuffer into this one. When this method
+ * returns, buffer is empty.
+ */
+ void appendBuffer(BigBuffer&& buffer);
+
+ /**
+ * Pads the block with 'bytes' bytes of zero values.
+ */
+ void pad(size_t bytes);
+
+ /**
+ * Pads the block so that it aligns on a 4 byte boundary.
+ */
+ void align4();
+
+ const_iterator begin() const;
+ const_iterator end() const;
+
+private:
+ /**
+ * Returns a pointer to a buffer of the requested size.
+ * The buffer is zero-initialized.
+ */
+ void* nextBlockImpl(size_t size);
+
+ size_t mBlockSize;
+ size_t mSize;
+ std::vector<Block> mBlocks;
+};
+
+inline BigBuffer::BigBuffer(size_t blockSize) : mBlockSize(blockSize), mSize(0) {
+}
+
+inline BigBuffer::BigBuffer(BigBuffer&& rhs) :
+ mBlockSize(rhs.mBlockSize), mSize(rhs.mSize), mBlocks(std::move(rhs.mBlocks)) {
+}
+
+inline size_t BigBuffer::size() const {
+ return mSize;
+}
+
+template <typename T>
+inline T* BigBuffer::nextBlock(size_t count) {
+ static_assert(std::is_standard_layout<T>::value, "T must be standard_layout type");
+ assert(count != 0);
+ return reinterpret_cast<T*>(nextBlockImpl(sizeof(T) * count));
+}
+
+inline void BigBuffer::appendBuffer(BigBuffer&& buffer) {
+ std::move(buffer.mBlocks.begin(), buffer.mBlocks.end(), std::back_inserter(mBlocks));
+ mSize += buffer.mSize;
+ buffer.mBlocks.clear();
+ buffer.mSize = 0;
+}
+
+inline void BigBuffer::pad(size_t bytes) {
+ nextBlock<char>(bytes);
+}
+
+inline void BigBuffer::align4() {
+ const size_t unaligned = mSize % 4;
+ if (unaligned != 0) {
+ pad(4 - unaligned);
+ }
+}
+
+inline BigBuffer::const_iterator BigBuffer::begin() const {
+ return mBlocks.begin();
+}
+
+inline BigBuffer::const_iterator BigBuffer::end() const {
+ return mBlocks.end();
+}
+
+} // namespace aapt
+
+#endif // AAPT_BIG_BUFFER_H
diff --git a/tools/aapt2/util/BigBuffer_test.cpp b/tools/aapt2/util/BigBuffer_test.cpp
new file mode 100644
index 0000000..2a24f12
--- /dev/null
+++ b/tools/aapt2/util/BigBuffer_test.cpp
@@ -0,0 +1,98 @@
+/*
+ * 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.
+ */
+
+#include "util/BigBuffer.h"
+
+#include <gtest/gtest.h>
+
+namespace aapt {
+
+TEST(BigBufferTest, AllocateSingleBlock) {
+ BigBuffer buffer(4);
+
+ EXPECT_NE(nullptr, buffer.nextBlock<char>(2));
+ EXPECT_EQ(2u, buffer.size());
+}
+
+TEST(BigBufferTest, ReturnSameBlockIfNextAllocationFits) {
+ BigBuffer buffer(16);
+
+ char* b1 = buffer.nextBlock<char>(8);
+ EXPECT_NE(nullptr, b1);
+
+ char* b2 = buffer.nextBlock<char>(4);
+ EXPECT_NE(nullptr, b2);
+
+ EXPECT_EQ(b1 + 8, b2);
+}
+
+TEST(BigBufferTest, AllocateExactSizeBlockIfLargerThanBlockSize) {
+ BigBuffer buffer(16);
+
+ EXPECT_NE(nullptr, buffer.nextBlock<char>(32));
+ EXPECT_EQ(32u, buffer.size());
+}
+
+TEST(BigBufferTest, AppendAndMoveBlock) {
+ BigBuffer buffer(16);
+
+ uint32_t* b1 = buffer.nextBlock<uint32_t>();
+ ASSERT_NE(nullptr, b1);
+ *b1 = 33;
+
+ {
+ BigBuffer buffer2(16);
+ b1 = buffer2.nextBlock<uint32_t>();
+ ASSERT_NE(nullptr, b1);
+ *b1 = 44;
+
+ buffer.appendBuffer(std::move(buffer2));
+ EXPECT_EQ(0u, buffer2.size());
+ EXPECT_EQ(buffer2.begin(), buffer2.end());
+ }
+
+ EXPECT_EQ(2 * sizeof(uint32_t), buffer.size());
+
+ auto b = buffer.begin();
+ ASSERT_NE(b, buffer.end());
+ ASSERT_EQ(sizeof(uint32_t), b->size);
+ ASSERT_EQ(33u, *reinterpret_cast<uint32_t*>(b->buffer.get()));
+ ++b;
+
+ ASSERT_NE(b, buffer.end());
+ ASSERT_EQ(sizeof(uint32_t), b->size);
+ ASSERT_EQ(44u, *reinterpret_cast<uint32_t*>(b->buffer.get()));
+ ++b;
+
+ ASSERT_EQ(b, buffer.end());
+}
+
+TEST(BigBufferTest, PadAndAlignProperly) {
+ BigBuffer buffer(16);
+
+ ASSERT_NE(buffer.nextBlock<char>(2), nullptr);
+ ASSERT_EQ(2u, buffer.size());
+ buffer.pad(2);
+ ASSERT_EQ(4u, buffer.size());
+ buffer.align4();
+ ASSERT_EQ(4u, buffer.size());
+ buffer.pad(2);
+ ASSERT_EQ(6u, buffer.size());
+ buffer.align4();
+ ASSERT_EQ(8u, buffer.size());
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/util/Comparators.h b/tools/aapt2/util/Comparators.h
new file mode 100644
index 0000000..0ee0bf3
--- /dev/null
+++ b/tools/aapt2/util/Comparators.h
@@ -0,0 +1,37 @@
+/*
+ * 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_UTIL_COMPARATORS_H
+#define AAPT_UTIL_COMPARATORS_H
+
+#include "ConfigDescription.h"
+#include "ResourceTable.h"
+
+namespace aapt {
+namespace cmp {
+
+inline bool lessThanConfig(const ResourceConfigValue& a, const ConfigDescription& b) {
+ return a.config < b;
+}
+
+inline bool lessThanType(const std::unique_ptr<ResourceTableType>& a, ResourceType b) {
+ return a->type < b;
+}
+
+} // namespace cmp
+} // namespace aapt
+
+#endif /* AAPT_UTIL_COMPARATORS_H */
diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp
new file mode 100644
index 0000000..a81dc7b
--- /dev/null
+++ b/tools/aapt2/util/Files.cpp
@@ -0,0 +1,238 @@
+/*
+ * 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.
+ */
+
+#include "util/Files.h"
+#include "util/Util.h"
+
+#include <cerrno>
+#include <cstdio>
+#include <dirent.h>
+#include <string>
+#include <sys/stat.h>
+
+#ifdef _WIN32
+// Windows includes.
+#include <direct.h>
+#endif
+
+namespace aapt {
+namespace file {
+
+FileType getFileType(const StringPiece& path) {
+ struct stat sb;
+ if (stat(path.data(), &sb) < 0) {
+ if (errno == ENOENT || errno == ENOTDIR) {
+ return FileType::kNonexistant;
+ }
+ return FileType::kUnknown;
+ }
+
+ if (S_ISREG(sb.st_mode)) {
+ return FileType::kRegular;
+ } else if (S_ISDIR(sb.st_mode)) {
+ return FileType::kDirectory;
+ } else if (S_ISCHR(sb.st_mode)) {
+ return FileType::kCharDev;
+ } else if (S_ISBLK(sb.st_mode)) {
+ return FileType::kBlockDev;
+ } else if (S_ISFIFO(sb.st_mode)) {
+ return FileType::kFifo;
+#if defined(S_ISLNK)
+ } else if (S_ISLNK(sb.st_mode)) {
+ return FileType::kSymlink;
+#endif
+#if defined(S_ISSOCK)
+ } else if (S_ISSOCK(sb.st_mode)) {
+ return FileType::kSocket;
+#endif
+ } else {
+ return FileType::kUnknown;
+ }
+}
+
+std::vector<std::string> listFiles(const StringPiece& root, std::string* outError) {
+ DIR* dir = opendir(root.data());
+ if (dir == nullptr) {
+ if (outError) {
+ std::stringstream errorStr;
+ errorStr << "unable to open file: " << strerror(errno);
+ *outError = errorStr.str();
+ return {};
+ }
+ }
+
+ std::vector<std::string> files;
+ dirent* entry;
+ while ((entry = readdir(dir))) {
+ files.emplace_back(entry->d_name);
+ }
+
+ closedir(dir);
+ return files;
+}
+
+inline static int mkdirImpl(const StringPiece& path) {
+#ifdef _WIN32
+ return _mkdir(path.toString().c_str());
+#else
+ return mkdir(path.toString().c_str(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP);
+#endif
+}
+
+bool mkdirs(const StringPiece& path) {
+ const char* start = path.begin();
+ const char* end = path.end();
+ for (const char* current = start; current != end; ++current) {
+ if (*current == sDirSep) {
+ StringPiece parentPath(start, current - start);
+ int result = mkdirImpl(parentPath);
+ if (result < 0 && errno != EEXIST) {
+ return false;
+ }
+ }
+ }
+ return mkdirImpl(path) == 0 || errno == EEXIST;
+}
+
+StringPiece getStem(const StringPiece& path) {
+ const char* start = path.begin();
+ const char* end = path.end();
+ for (const char* current = end - 1; current != start - 1; --current) {
+ if (*current == sDirSep) {
+ return StringPiece(start, current - start);
+ }
+ }
+ return {};
+}
+
+StringPiece getFilename(const StringPiece& path) {
+ const char* end = path.end();
+ const char* lastDirSep = path.begin();
+ for (const char* c = path.begin(); c != end; ++c) {
+ if (*c == sDirSep) {
+ lastDirSep = c + 1;
+ }
+ }
+ return StringPiece(lastDirSep, end - lastDirSep);
+}
+
+StringPiece getExtension(const StringPiece& path) {
+ StringPiece filename = getFilename(path);
+ const char* const end = filename.end();
+ const char* c = std::find(filename.begin(), end, '.');
+ if (c != end) {
+ return StringPiece(c, end - c);
+ }
+ return {};
+}
+
+std::string packageToPath(const StringPiece& package) {
+ std::string outPath;
+ for (StringPiece part : util::tokenize<char>(package, '.')) {
+ appendPath(&outPath, part);
+ }
+ return outPath;
+}
+
+Maybe<android::FileMap> mmapPath(const StringPiece& path, std::string* outError) {
+ std::unique_ptr<FILE, decltype(fclose)*> f = { fopen(path.data(), "rb"), fclose };
+ if (!f) {
+ if (outError) *outError = strerror(errno);
+ return {};
+ }
+
+ int fd = fileno(f.get());
+
+ struct stat fileStats = {};
+ if (fstat(fd, &fileStats) != 0) {
+ if (outError) *outError = strerror(errno);
+ return {};
+ }
+
+ android::FileMap fileMap;
+ if (!fileMap.create(path.data(), fd, 0, fileStats.st_size, true)) {
+ if (outError) *outError = strerror(errno);
+ return {};
+ }
+ return std::move(fileMap);
+}
+
+bool FileFilter::setPattern(const StringPiece& pattern) {
+ mPatternTokens = util::splitAndLowercase(pattern, ':');
+ return true;
+}
+
+bool FileFilter::operator()(const std::string& filename, FileType type) const {
+ if (filename == "." || filename == "..") {
+ return false;
+ }
+
+ const char kDir[] = "dir";
+ const char kFile[] = "file";
+ const size_t filenameLen = filename.length();
+ bool chatty = true;
+ for (const std::string& token : mPatternTokens) {
+ const char* tokenStr = token.c_str();
+ if (*tokenStr == '!') {
+ chatty = false;
+ tokenStr++;
+ }
+
+ if (strncasecmp(tokenStr, kDir, sizeof(kDir)) == 0) {
+ if (type != FileType::kDirectory) {
+ continue;
+ }
+ tokenStr += sizeof(kDir);
+ }
+
+ if (strncasecmp(tokenStr, kFile, sizeof(kFile)) == 0) {
+ if (type != FileType::kRegular) {
+ continue;
+ }
+ tokenStr += sizeof(kFile);
+ }
+
+ bool ignore = false;
+ size_t n = strlen(tokenStr);
+ if (*tokenStr == '*') {
+ // Math suffix.
+ tokenStr++;
+ n--;
+ if (n <= filenameLen) {
+ ignore = strncasecmp(tokenStr, filename.c_str() + filenameLen - n, n) == 0;
+ }
+ } else if (n > 1 && tokenStr[n - 1] == '*') {
+ // Match prefix.
+ ignore = strncasecmp(tokenStr, filename.c_str(), n - 1) == 0;
+ } else {
+ ignore = strcasecmp(tokenStr, filename.c_str()) == 0;
+ }
+
+ if (ignore) {
+ if (chatty) {
+ mDiag->warn(DiagMessage() << "skipping "
+ << (type == FileType::kDirectory ? "dir '" : "file '")
+ << filename << "' due to ignore pattern '"
+ << token << "'");
+ }
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace file
+} // namespace aapt
diff --git a/tools/aapt2/util/Files.h b/tools/aapt2/util/Files.h
new file mode 100644
index 0000000..c58ba5d
--- /dev/null
+++ b/tools/aapt2/util/Files.h
@@ -0,0 +1,159 @@
+/*
+ * 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_FILES_H
+#define AAPT_FILES_H
+
+#include "Diagnostics.h"
+#include "Maybe.h"
+#include "Source.h"
+
+#include "util/StringPiece.h"
+
+#include <utils/FileMap.h>
+#include <cassert>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace aapt {
+namespace file {
+
+#ifdef _WIN32
+constexpr const char sDirSep = '\\';
+#else
+constexpr const char sDirSep = '/';
+#endif
+
+enum class FileType {
+ kUnknown = 0,
+ kNonexistant,
+ kRegular,
+ kDirectory,
+ kCharDev,
+ kBlockDev,
+ kFifo,
+ kSymlink,
+ kSocket,
+};
+
+FileType getFileType(const StringPiece& path);
+
+/*
+ * Lists files under the directory `root`. Files are listed
+ * with just their leaf (filename) names.
+ */
+std::vector<std::string> listFiles(const StringPiece& root);
+
+/*
+ * Appends a path to `base`, separated by the directory separator.
+ */
+void appendPath(std::string* base, const StringPiece& part);
+
+/*
+ * Appends a series of paths to `base`, separated by the
+ * system directory separator.
+ */
+template <typename... Ts >
+void appendPath(std::string* base, const StringPiece& part, const Ts&... parts);
+
+/*
+ * Makes all the directories in `path`. The last element in the path
+ * is interpreted as a directory.
+ */
+bool mkdirs(const StringPiece& path);
+
+/**
+ * Returns all but the last part of the path.
+ */
+StringPiece getStem(const StringPiece& path);
+
+/**
+ * Returns the last part of the path with extension.
+ */
+StringPiece getFilename(const StringPiece& path);
+
+/**
+ * Returns the extension of the path. This is the entire string after
+ * the first '.' of the last part of the path.
+ */
+StringPiece getExtension(const StringPiece& path);
+
+/**
+ * Converts a package name (com.android.app) to a path: com/android/app
+ */
+std::string packageToPath(const StringPiece& package);
+
+/**
+ * Creates a FileMap for the file at path.
+ */
+Maybe<android::FileMap> mmapPath(const StringPiece& path, std::string* outError);
+
+/*
+ * Filter that determines which resource files/directories are
+ * processed by AAPT. Takes a pattern string supplied by the user.
+ * Pattern format is specified in the
+ * FileFilter::setPattern(const std::string&) method.
+ */
+class FileFilter {
+public:
+ FileFilter(IDiagnostics* diag) : mDiag(diag) {
+ }
+
+ /*
+ * Patterns syntax:
+ * - Delimiter is :
+ * - Entry can start with the flag ! to avoid printing a warning
+ * about the file being ignored.
+ * - Entry can have the flag "<dir>" to match only directories
+ * or <file> to match only files. Default is to match both.
+ * - Entry can be a simplified glob "<prefix>*" or "*<suffix>"
+ * where prefix/suffix must have at least 1 character (so that
+ * we don't match a '*' catch-all pattern.)
+ * - The special filenames "." and ".." are always ignored.
+ * - Otherwise the full string is matched.
+ * - match is not case-sensitive.
+ */
+ bool setPattern(const StringPiece& pattern);
+
+ /**
+ * Applies the filter, returning true for pass, false for fail.
+ */
+ bool operator()(const std::string& filename, FileType type) const;
+
+private:
+ IDiagnostics* mDiag;
+ std::vector<std::string> mPatternTokens;
+};
+
+inline void appendPath(std::string* base, const StringPiece& part) {
+ assert(base);
+ *base += sDirSep;
+ base->append(part.data(), part.size());
+}
+
+template <typename... Ts >
+void appendPath(std::string* base, const StringPiece& part, const Ts&... parts) {
+ assert(base);
+ *base += sDirSep;
+ base->append(part.data(), part.size());
+ appendPath(base, parts...);
+}
+
+} // namespace file
+} // namespace aapt
+
+#endif // AAPT_FILES_H
diff --git a/tools/aapt2/util/Maybe.h b/tools/aapt2/util/Maybe.h
new file mode 100644
index 0000000..1f7d5ce
--- /dev/null
+++ b/tools/aapt2/util/Maybe.h
@@ -0,0 +1,280 @@
+/*
+ * 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_MAYBE_H
+#define AAPT_MAYBE_H
+
+#include <cassert>
+#include <type_traits>
+#include <utility>
+
+namespace aapt {
+
+/**
+ * Either holds a valid value of type T, or holds Nothing.
+ * The value is stored inline in this structure, so no
+ * heap memory is used when creating a Maybe<T> object.
+ */
+template <typename T>
+class Maybe {
+public:
+ /**
+ * Construct Nothing.
+ */
+ Maybe();
+
+ ~Maybe();
+
+ Maybe(const Maybe& rhs);
+
+ template <typename U>
+ Maybe(const Maybe<U>& rhs);
+
+ Maybe(Maybe&& rhs);
+
+ template <typename U>
+ Maybe(Maybe<U>&& rhs);
+
+ Maybe& operator=(const Maybe& rhs);
+
+ template <typename U>
+ Maybe& operator=(const Maybe<U>& rhs);
+
+ Maybe& operator=(Maybe&& rhs);
+
+ template <typename U>
+ Maybe& operator=(Maybe<U>&& rhs);
+
+ /**
+ * Construct a Maybe holding a value.
+ */
+ Maybe(const T& value);
+
+ /**
+ * Construct a Maybe holding a value.
+ */
+ Maybe(T&& value);
+
+ /**
+ * True if this holds a value, false if
+ * it holds Nothing.
+ */
+ explicit operator bool() const;
+
+ /**
+ * Gets the value if one exists, or else
+ * panics.
+ */
+ T& value();
+
+ /**
+ * Gets the value if one exists, or else
+ * panics.
+ */
+ const T& value() const;
+
+private:
+ template <typename U>
+ friend class Maybe;
+
+ template <typename U>
+ Maybe& copy(const Maybe<U>& rhs);
+
+ template <typename U>
+ Maybe& move(Maybe<U>&& rhs);
+
+ void destroy();
+
+ bool mNothing;
+
+ typename std::aligned_storage<sizeof(T), alignof(T)>::type mStorage;
+};
+
+template <typename T>
+Maybe<T>::Maybe()
+: mNothing(true) {
+}
+
+template <typename T>
+Maybe<T>::~Maybe() {
+ if (!mNothing) {
+ destroy();
+ }
+}
+
+template <typename T>
+Maybe<T>::Maybe(const Maybe& rhs)
+: mNothing(rhs.mNothing) {
+ if (!rhs.mNothing) {
+ new (&mStorage) T(reinterpret_cast<const T&>(rhs.mStorage));
+ }
+}
+
+template <typename T>
+template <typename U>
+Maybe<T>::Maybe(const Maybe<U>& rhs)
+: mNothing(rhs.mNothing) {
+ if (!rhs.mNothing) {
+ new (&mStorage) T(reinterpret_cast<const U&>(rhs.mStorage));
+ }
+}
+
+template <typename T>
+Maybe<T>::Maybe(Maybe&& rhs)
+: mNothing(rhs.mNothing) {
+ if (!rhs.mNothing) {
+ rhs.mNothing = true;
+
+ // Move the value from rhs.
+ new (&mStorage) T(std::move(reinterpret_cast<T&>(rhs.mStorage)));
+ rhs.destroy();
+ }
+}
+
+template <typename T>
+template <typename U>
+Maybe<T>::Maybe(Maybe<U>&& rhs)
+: mNothing(rhs.mNothing) {
+ if (!rhs.mNothing) {
+ rhs.mNothing = true;
+
+ // Move the value from rhs.
+ new (&mStorage) T(std::move(reinterpret_cast<U&>(rhs.mStorage)));
+ rhs.destroy();
+ }
+}
+
+template <typename T>
+inline Maybe<T>& Maybe<T>::operator=(const Maybe& rhs) {
+ // Delegate to the actual assignment.
+ return copy(rhs);
+}
+
+template <typename T>
+template <typename U>
+inline Maybe<T>& Maybe<T>::operator=(const Maybe<U>& rhs) {
+ return copy(rhs);
+}
+
+template <typename T>
+template <typename U>
+Maybe<T>& Maybe<T>::copy(const Maybe<U>& rhs) {
+ if (mNothing && rhs.mNothing) {
+ // Both are nothing, nothing to do.
+ return *this;
+ } else if (!mNothing && !rhs.mNothing) {
+ // We both are something, so assign rhs to us.
+ reinterpret_cast<T&>(mStorage) = reinterpret_cast<const U&>(rhs.mStorage);
+ } else if (mNothing) {
+ // We are nothing but rhs is something.
+ mNothing = rhs.mNothing;
+
+ // Copy the value from rhs.
+ new (&mStorage) T(reinterpret_cast<const U&>(rhs.mStorage));
+ } else {
+ // We are something but rhs is nothing, so destroy our value.
+ mNothing = rhs.mNothing;
+ destroy();
+ }
+ return *this;
+}
+
+template <typename T>
+inline Maybe<T>& Maybe<T>::operator=(Maybe&& rhs) {
+ // Delegate to the actual assignment.
+ return move(std::forward<Maybe<T>>(rhs));
+}
+
+template <typename T>
+template <typename U>
+inline Maybe<T>& Maybe<T>::operator=(Maybe<U>&& rhs) {
+ return move(std::forward<Maybe<U>>(rhs));
+}
+
+template <typename T>
+template <typename U>
+Maybe<T>& Maybe<T>::move(Maybe<U>&& rhs) {
+ if (mNothing && rhs.mNothing) {
+ // Both are nothing, nothing to do.
+ return *this;
+ } else if (!mNothing && !rhs.mNothing) {
+ // We both are something, so move assign rhs to us.
+ rhs.mNothing = true;
+ reinterpret_cast<T&>(mStorage) = std::move(reinterpret_cast<U&>(rhs.mStorage));
+ rhs.destroy();
+ } else if (mNothing) {
+ // We are nothing but rhs is something.
+ mNothing = false;
+ rhs.mNothing = true;
+
+ // Move the value from rhs.
+ new (&mStorage) T(std::move(reinterpret_cast<U&>(rhs.mStorage)));
+ rhs.destroy();
+ } else {
+ // We are something but rhs is nothing, so destroy our value.
+ mNothing = true;
+ destroy();
+ }
+ return *this;
+}
+
+template <typename T>
+Maybe<T>::Maybe(const T& value)
+: mNothing(false) {
+ new (&mStorage) T(value);
+}
+
+template <typename T>
+Maybe<T>::Maybe(T&& value)
+: mNothing(false) {
+ new (&mStorage) T(std::forward<T>(value));
+}
+
+template <typename T>
+Maybe<T>::operator bool() const {
+ return !mNothing;
+}
+
+template <typename T>
+T& Maybe<T>::value() {
+ assert(!mNothing && "Maybe<T>::value() called on Nothing");
+ return reinterpret_cast<T&>(mStorage);
+}
+
+template <typename T>
+const T& Maybe<T>::value() const {
+ assert(!mNothing && "Maybe<T>::value() called on Nothing");
+ return reinterpret_cast<const T&>(mStorage);
+}
+
+template <typename T>
+void Maybe<T>::destroy() {
+ reinterpret_cast<T&>(mStorage).~T();
+}
+
+template <typename T>
+inline Maybe<typename std::remove_reference<T>::type> make_value(T&& value) {
+ return Maybe<typename std::remove_reference<T>::type>(std::forward<T>(value));
+}
+
+template <typename T>
+inline Maybe<T> make_nothing() {
+ return Maybe<T>();
+}
+
+} // namespace aapt
+
+#endif // AAPT_MAYBE_H
diff --git a/tools/aapt2/util/Maybe_test.cpp b/tools/aapt2/util/Maybe_test.cpp
new file mode 100644
index 0000000..d2c33ca
--- /dev/null
+++ b/tools/aapt2/util/Maybe_test.cpp
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+
+#include "test/Common.h"
+#include "util/Maybe.h"
+
+#include <gtest/gtest.h>
+#include <string>
+
+namespace aapt {
+
+struct Dummy {
+ Dummy() {
+ data = new int;
+ *data = 1;
+ std::cerr << "Construct Dummy{0x" << (void *) this
+ << "} with data=0x" << (void*) data
+ << std::endl;
+ }
+
+ Dummy(const Dummy& rhs) {
+ data = nullptr;
+ if (rhs.data) {
+ data = new int;
+ *data = *rhs.data;
+ }
+ std::cerr << "CopyConstruct Dummy{0x" << (void *) this
+ << "} from Dummy{0x" << (const void*) &rhs
+ << "}" << std::endl;
+ }
+
+ Dummy(Dummy&& rhs) {
+ data = rhs.data;
+ rhs.data = nullptr;
+ std::cerr << "MoveConstruct Dummy{0x" << (void *) this
+ << "} from Dummy{0x" << (const void*) &rhs
+ << "}" << std::endl;
+ }
+
+ Dummy& operator=(const Dummy& rhs) {
+ delete data;
+ data = nullptr;
+
+ if (rhs.data) {
+ data = new int;
+ *data = *rhs.data;
+ }
+ std::cerr << "CopyAssign Dummy{0x" << (void *) this
+ << "} from Dummy{0x" << (const void*) &rhs
+ << "}" << std::endl;
+ return *this;
+ }
+
+ Dummy& operator=(Dummy&& rhs) {
+ delete data;
+ data = rhs.data;
+ rhs.data = nullptr;
+ std::cerr << "MoveAssign Dummy{0x" << (void *) this
+ << "} from Dummy{0x" << (const void*) &rhs
+ << "}" << std::endl;
+ return *this;
+ }
+
+ ~Dummy() {
+ std::cerr << "Destruct Dummy{0x" << (void *) this
+ << "} with data=0x" << (void*) data
+ << std::endl;
+ delete data;
+ }
+
+ int* data;
+};
+
+TEST(MaybeTest, MakeNothing) {
+ Maybe<int> val = make_nothing<int>();
+ AAPT_EXPECT_FALSE(val);
+
+ Maybe<std::string> val2 = make_nothing<std::string>();
+ AAPT_EXPECT_FALSE(val2);
+
+ val2 = make_nothing<std::string>();
+ AAPT_EXPECT_FALSE(val2);
+}
+
+TEST(MaybeTest, MakeSomething) {
+ Maybe<int> val = make_value(23);
+ AAPT_ASSERT_TRUE(val);
+ EXPECT_EQ(23, val.value());
+
+ Maybe<std::string> val2 = make_value(std::string("hey"));
+ AAPT_ASSERT_TRUE(val2);
+ EXPECT_EQ(std::string("hey"), val2.value());
+}
+
+TEST(MaybeTest, Lifecycle) {
+ Maybe<Dummy> val = make_nothing<Dummy>();
+
+ Maybe<Dummy> val2 = make_value(Dummy());
+}
+
+TEST(MaybeTest, MoveAssign) {
+ Maybe<Dummy> val;
+ {
+ Maybe<Dummy> val2 = Dummy();
+ val = std::move(val2);
+ }
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/util/StringPiece.h b/tools/aapt2/util/StringPiece.h
new file mode 100644
index 0000000..31deb45
--- /dev/null
+++ b/tools/aapt2/util/StringPiece.h
@@ -0,0 +1,238 @@
+/*
+ * 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_STRING_PIECE_H
+#define AAPT_STRING_PIECE_H
+
+#include <ostream>
+#include <string>
+#include <utils/String8.h>
+#include <utils/Unicode.h>
+
+namespace aapt {
+
+/**
+ * Read only wrapper around basic C strings.
+ * Prevents excessive copying.
+ *
+ * WARNING: When creating from std::basic_string<>, moving the original
+ * std::basic_string<> will invalidate the data held in a BasicStringPiece<>.
+ * BasicStringPiece<> should only be used transitively.
+ */
+template <typename TChar>
+class BasicStringPiece {
+public:
+ using const_iterator = const TChar*;
+ using difference_type = size_t;
+
+ BasicStringPiece();
+ BasicStringPiece(const BasicStringPiece<TChar>& str);
+ BasicStringPiece(const std::basic_string<TChar>& str);
+ BasicStringPiece(const TChar* str);
+ BasicStringPiece(const TChar* str, size_t len);
+
+ BasicStringPiece<TChar>& operator=(const BasicStringPiece<TChar>& rhs);
+ BasicStringPiece<TChar>& assign(const TChar* str, size_t len);
+
+ BasicStringPiece<TChar> substr(size_t start, size_t len) const;
+ BasicStringPiece<TChar> substr(BasicStringPiece<TChar>::const_iterator begin,
+ BasicStringPiece<TChar>::const_iterator end) const;
+
+ const TChar* data() const;
+ size_t length() const;
+ size_t size() const;
+ bool empty() const;
+ std::basic_string<TChar> toString() const;
+
+ int compare(const BasicStringPiece<TChar>& rhs) const;
+ bool operator<(const BasicStringPiece<TChar>& rhs) const;
+ bool operator>(const BasicStringPiece<TChar>& rhs) const;
+ bool operator==(const BasicStringPiece<TChar>& rhs) const;
+ bool operator!=(const BasicStringPiece<TChar>& rhs) const;
+
+ const_iterator begin() const;
+ const_iterator end() const;
+
+private:
+ const TChar* mData;
+ size_t mLength;
+};
+
+using StringPiece = BasicStringPiece<char>;
+using StringPiece16 = BasicStringPiece<char16_t>;
+
+//
+// BasicStringPiece implementation.
+//
+
+template <typename TChar>
+inline BasicStringPiece<TChar>::BasicStringPiece() : mData(nullptr) , mLength(0) {
+}
+
+template <typename TChar>
+inline BasicStringPiece<TChar>::BasicStringPiece(const BasicStringPiece<TChar>& str) :
+ mData(str.mData), mLength(str.mLength) {
+}
+
+template <typename TChar>
+inline BasicStringPiece<TChar>::BasicStringPiece(const std::basic_string<TChar>& str) :
+ mData(str.data()), mLength(str.length()) {
+}
+
+template <>
+inline BasicStringPiece<char>::BasicStringPiece(const char* str) :
+ mData(str), mLength(str != nullptr ? strlen(str) : 0) {
+}
+
+template <>
+inline BasicStringPiece<char16_t>::BasicStringPiece(const char16_t* str) :
+ mData(str), mLength(str != nullptr ? strlen16(str) : 0) {
+}
+
+template <typename TChar>
+inline BasicStringPiece<TChar>::BasicStringPiece(const TChar* str, size_t len) :
+ mData(str), mLength(len) {
+}
+
+template <typename TChar>
+inline BasicStringPiece<TChar>& BasicStringPiece<TChar>::operator=(
+ const BasicStringPiece<TChar>& rhs) {
+ mData = rhs.mData;
+ mLength = rhs.mLength;
+ return *this;
+}
+
+template <typename TChar>
+inline BasicStringPiece<TChar>& BasicStringPiece<TChar>::assign(const TChar* str, size_t len) {
+ mData = str;
+ mLength = len;
+ return *this;
+}
+
+
+template <typename TChar>
+inline BasicStringPiece<TChar> BasicStringPiece<TChar>::substr(size_t start, size_t len) const {
+ if (start + len > mLength) {
+ return BasicStringPiece<TChar>();
+ }
+ return BasicStringPiece<TChar>(mData + start, len);
+}
+
+template <typename TChar>
+inline BasicStringPiece<TChar> BasicStringPiece<TChar>::substr(
+ BasicStringPiece<TChar>::const_iterator begin,
+ BasicStringPiece<TChar>::const_iterator end) const {
+ return BasicStringPiece<TChar>(begin, end - begin);
+}
+
+template <typename TChar>
+inline const TChar* BasicStringPiece<TChar>::data() const {
+ return mData;
+}
+
+template <typename TChar>
+inline size_t BasicStringPiece<TChar>::length() const {
+ return mLength;
+}
+
+template <typename TChar>
+inline size_t BasicStringPiece<TChar>::size() const {
+ return mLength;
+}
+
+template <typename TChar>
+inline bool BasicStringPiece<TChar>::empty() const {
+ return mLength == 0;
+}
+
+template <typename TChar>
+inline std::basic_string<TChar> BasicStringPiece<TChar>::toString() const {
+ return std::basic_string<TChar>(mData, mLength);
+}
+
+template <>
+inline int BasicStringPiece<char>::compare(const BasicStringPiece<char>& rhs) const {
+ const char nullStr = '\0';
+ const char* b1 = mData != nullptr ? mData : &nullStr;
+ const char* e1 = b1 + mLength;
+ const char* b2 = rhs.mData != nullptr ? rhs.mData : &nullStr;
+ const char* e2 = b2 + rhs.mLength;
+
+ while (b1 < e1 && b2 < e2) {
+ const int d = static_cast<int>(*b1++) - static_cast<int>(*b2++);
+ if (d) {
+ return d;
+ }
+ }
+ return static_cast<int>(mLength - rhs.mLength);
+}
+
+inline ::std::ostream& operator<<(::std::ostream& out, const BasicStringPiece<char16_t>& str) {
+ android::String8 utf8(str.data(), str.size());
+ return out.write(utf8.string(), utf8.size());
+}
+
+
+template <>
+inline int BasicStringPiece<char16_t>::compare(const BasicStringPiece<char16_t>& rhs) const {
+ const char16_t nullStr = u'\0';
+ const char16_t* b1 = mData != nullptr ? mData : &nullStr;
+ const char16_t* b2 = rhs.mData != nullptr ? rhs.mData : &nullStr;
+ return strzcmp16(b1, mLength, b2, rhs.mLength);
+}
+
+template <typename TChar>
+inline bool BasicStringPiece<TChar>::operator<(const BasicStringPiece<TChar>& rhs) const {
+ return compare(rhs) < 0;
+}
+
+template <typename TChar>
+inline bool BasicStringPiece<TChar>::operator>(const BasicStringPiece<TChar>& rhs) const {
+ return compare(rhs) > 0;
+}
+
+template <typename TChar>
+inline bool BasicStringPiece<TChar>::operator==(const BasicStringPiece<TChar>& rhs) const {
+ return compare(rhs) == 0;
+}
+
+template <typename TChar>
+inline bool BasicStringPiece<TChar>::operator!=(const BasicStringPiece<TChar>& rhs) const {
+ return compare(rhs) != 0;
+}
+
+template <typename TChar>
+inline typename BasicStringPiece<TChar>::const_iterator BasicStringPiece<TChar>::begin() const {
+ return mData;
+}
+
+template <typename TChar>
+inline typename BasicStringPiece<TChar>::const_iterator BasicStringPiece<TChar>::end() const {
+ return mData + mLength;
+}
+
+inline ::std::ostream& operator<<(::std::ostream& out, const BasicStringPiece<char>& str) {
+ return out.write(str.data(), str.size());
+}
+
+} // namespace aapt
+
+inline ::std::ostream& operator<<(::std::ostream& out, const std::u16string& str) {
+ android::String8 utf8(str.data(), str.size());
+ return out.write(utf8.string(), utf8.size());
+}
+
+#endif // AAPT_STRING_PIECE_H
diff --git a/tools/aapt2/util/StringPiece_test.cpp b/tools/aapt2/util/StringPiece_test.cpp
new file mode 100644
index 0000000..d49b67f
--- /dev/null
+++ b/tools/aapt2/util/StringPiece_test.cpp
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+#include <algorithm>
+#include <gtest/gtest.h>
+#include <string>
+#include <vector>
+
+#include "util/StringPiece.h"
+
+namespace aapt {
+
+TEST(StringPieceTest, CompareNonNullTerminatedPiece) {
+ StringPiece a("hello world", 5);
+ StringPiece b("hello moon", 5);
+ EXPECT_EQ(a, b);
+
+ StringPiece16 a16(u"hello world", 5);
+ StringPiece16 b16(u"hello moon", 5);
+ EXPECT_EQ(a16, b16);
+}
+
+TEST(StringPieceTest, PiecesHaveCorrectSortOrder) {
+ std::u16string testing(u"testing");
+ std::u16string banana(u"banana");
+ std::u16string car(u"car");
+
+ EXPECT_TRUE(StringPiece16(testing) > banana);
+ EXPECT_TRUE(StringPiece16(testing) > car);
+ EXPECT_TRUE(StringPiece16(banana) < testing);
+ EXPECT_TRUE(StringPiece16(banana) < car);
+ EXPECT_TRUE(StringPiece16(car) < testing);
+ EXPECT_TRUE(StringPiece16(car) > banana);
+}
+
+TEST(StringPieceTest, PiecesHaveCorrectSortOrderUtf8) {
+ std::string testing("testing");
+ std::string banana("banana");
+ std::string car("car");
+
+ EXPECT_TRUE(StringPiece(testing) > banana);
+ EXPECT_TRUE(StringPiece(testing) > car);
+ EXPECT_TRUE(StringPiece(banana) < testing);
+ EXPECT_TRUE(StringPiece(banana) < car);
+ EXPECT_TRUE(StringPiece(car) < testing);
+ EXPECT_TRUE(StringPiece(car) > banana);
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp
new file mode 100644
index 0000000..59b8385
--- /dev/null
+++ b/tools/aapt2/util/Util.cpp
@@ -0,0 +1,507 @@
+/*
+ * 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.
+ */
+
+#include "util/BigBuffer.h"
+#include "util/Maybe.h"
+#include "util/StringPiece.h"
+#include "util/Util.h"
+
+#include <algorithm>
+#include <ostream>
+#include <string>
+#include <utils/Unicode.h>
+#include <vector>
+
+namespace aapt {
+namespace util {
+
+constexpr const char16_t* kSchemaAuto = u"http://schemas.android.com/apk/res-auto";
+constexpr const char16_t* kSchemaPrefix = u"http://schemas.android.com/apk/res/";
+
+static std::vector<std::string> splitAndTransform(const StringPiece& str, char sep,
+ const std::function<char(char)>& f) {
+ std::vector<std::string> parts;
+ const StringPiece::const_iterator end = std::end(str);
+ StringPiece::const_iterator start = std::begin(str);
+ StringPiece::const_iterator current;
+ do {
+ current = std::find(start, end, sep);
+ parts.emplace_back(str.substr(start, current).toString());
+ if (f) {
+ std::string& part = parts.back();
+ std::transform(part.begin(), part.end(), part.begin(), f);
+ }
+ start = current + 1;
+ } while (current != end);
+ return parts;
+}
+
+std::vector<std::string> split(const StringPiece& str, char sep) {
+ return splitAndTransform(str, sep, nullptr);
+}
+
+std::vector<std::string> splitAndLowercase(const StringPiece& str, char sep) {
+ return splitAndTransform(str, sep, ::tolower);
+}
+
+StringPiece16 trimWhitespace(const StringPiece16& str) {
+ if (str.size() == 0 || str.data() == nullptr) {
+ return str;
+ }
+
+ const char16_t* start = str.data();
+ const char16_t* end = str.data() + str.length();
+
+ while (start != end && util::isspace16(*start)) {
+ start++;
+ }
+
+ while (end != start && util::isspace16(*(end - 1))) {
+ end--;
+ }
+
+ return StringPiece16(start, end - start);
+}
+
+StringPiece trimWhitespace(const StringPiece& str) {
+ if (str.size() == 0 || str.data() == nullptr) {
+ return str;
+ }
+
+ const char* start = str.data();
+ const char* end = str.data() + str.length();
+
+ while (start != end && isspace(*start)) {
+ start++;
+ }
+
+ while (end != start && isspace(*(end - 1))) {
+ end--;
+ }
+
+ return StringPiece(start, end - start);
+}
+
+StringPiece16::const_iterator findNonAlphaNumericAndNotInSet(const StringPiece16& str,
+ const StringPiece16& allowedChars) {
+ const auto endIter = str.end();
+ for (auto iter = str.begin(); iter != endIter; ++iter) {
+ char16_t c = *iter;
+ if ((c >= u'a' && c <= u'z') ||
+ (c >= u'A' && c <= u'Z') ||
+ (c >= u'0' && c <= u'9')) {
+ continue;
+ }
+
+ bool match = false;
+ for (char16_t i : allowedChars) {
+ if (c == i) {
+ match = true;
+ break;
+ }
+ }
+
+ if (!match) {
+ return iter;
+ }
+ }
+ return endIter;
+}
+
+bool isJavaClassName(const StringPiece16& str) {
+ size_t pieces = 0;
+ for (const StringPiece16& piece : tokenize(str, u'.')) {
+ pieces++;
+ if (piece.empty()) {
+ return false;
+ }
+
+ // Can't have starting or trailing $ character.
+ if (piece.data()[0] == u'$' || piece.data()[piece.size() - 1] == u'$') {
+ return false;
+ }
+
+ if (findNonAlphaNumericAndNotInSet(piece, u"$_") != piece.end()) {
+ return false;
+ }
+ }
+ return pieces >= 2;
+}
+
+bool isJavaPackageName(const StringPiece16& str) {
+ if (str.empty()) {
+ return false;
+ }
+
+ size_t pieces = 0;
+ for (const StringPiece16& piece : tokenize(str, u'.')) {
+ pieces++;
+ if (piece.empty()) {
+ return false;
+ }
+
+ if (piece.data()[0] == u'_' || piece.data()[piece.size() - 1] == u'_') {
+ return false;
+ }
+
+ if (findNonAlphaNumericAndNotInSet(piece, u"_") != piece.end()) {
+ return false;
+ }
+ }
+ return pieces >= 1;
+}
+
+Maybe<std::u16string> getFullyQualifiedClassName(const StringPiece16& package,
+ const StringPiece16& className) {
+ if (className.empty()) {
+ return {};
+ }
+
+ if (util::isJavaClassName(className)) {
+ return className.toString();
+ }
+
+ if (package.empty()) {
+ return {};
+ }
+
+ std::u16string result(package.data(), package.size());
+ if (className.data()[0] != u'.') {
+ result += u'.';
+ }
+ result.append(className.data(), className.size());
+ if (!isJavaClassName(result)) {
+ return {};
+ }
+ return result;
+}
+
+static size_t consumeDigits(const char16_t* start, const char16_t* end) {
+ const char16_t* c = start;
+ for (; c != end && *c >= u'0' && *c <= u'9'; c++) {}
+ return static_cast<size_t>(c - start);
+}
+
+bool verifyJavaStringFormat(const StringPiece16& str) {
+ const char16_t* c = str.begin();
+ const char16_t* const end = str.end();
+
+ size_t argCount = 0;
+ bool nonpositional = false;
+ while (c != end) {
+ if (*c == u'%' && c + 1 < end) {
+ c++;
+
+ if (*c == u'%') {
+ c++;
+ continue;
+ }
+
+ argCount++;
+
+ size_t numDigits = consumeDigits(c, end);
+ if (numDigits > 0) {
+ c += numDigits;
+ if (c != end && *c != u'$') {
+ // The digits were a size, but not a positional argument.
+ nonpositional = true;
+ }
+ } else if (*c == u'<') {
+ // Reusing last argument, bad idea since positions can be moved around
+ // during translation.
+ nonpositional = true;
+
+ c++;
+
+ // Optionally we can have a $ after
+ if (c != end && *c == u'$') {
+ c++;
+ }
+ } else {
+ nonpositional = true;
+ }
+
+ // Ignore size, width, flags, etc.
+ while (c != end && (*c == u'-' ||
+ *c == u'#' ||
+ *c == u'+' ||
+ *c == u' ' ||
+ *c == u',' ||
+ *c == u'(' ||
+ (*c >= u'0' && *c <= '9'))) {
+ c++;
+ }
+
+ /*
+ * This is a shortcut to detect strings that are going to Time.format()
+ * instead of String.format()
+ *
+ * Comparison of String.format() and Time.format() args:
+ *
+ * String: ABC E GH ST X abcdefgh nost x
+ * Time: DEFGHKMS W Za d hkm s w yz
+ *
+ * Therefore we know it's definitely Time if we have:
+ * DFKMWZkmwyz
+ */
+ if (c != end) {
+ switch (*c) {
+ case 'D':
+ case 'F':
+ case 'K':
+ case 'M':
+ case 'W':
+ case 'Z':
+ case 'k':
+ case 'm':
+ case 'w':
+ case 'y':
+ case 'z':
+ return true;
+ }
+ }
+ }
+
+ if (c != end) {
+ c++;
+ }
+ }
+
+ if (argCount > 1 && nonpositional) {
+ // Multiple arguments were specified, but some or all were non positional. Translated
+ // strings may rearrange the order of the arguments, which will break the string.
+ return false;
+ }
+ return true;
+}
+
+static Maybe<char16_t> parseUnicodeCodepoint(const char16_t** start, const char16_t* end) {
+ char16_t code = 0;
+ for (size_t i = 0; i < 4 && *start != end; i++, (*start)++) {
+ char16_t c = **start;
+ int a;
+ if (c >= '0' && c <= '9') {
+ a = c - '0';
+ } else if (c >= 'a' && c <= 'f') {
+ a = c - 'a' + 10;
+ } else if (c >= 'A' && c <= 'F') {
+ a = c - 'A' + 10;
+ } else {
+ return make_nothing<char16_t>();
+ }
+ code = (code << 4) | a;
+ }
+ return make_value(code);
+}
+
+StringBuilder& StringBuilder::append(const StringPiece16& str) {
+ if (!mError.empty()) {
+ return *this;
+ }
+
+ const char16_t* const end = str.end();
+ const char16_t* start = str.begin();
+ const char16_t* current = start;
+ while (current != end) {
+ if (mLastCharWasEscape) {
+ switch (*current) {
+ case u't':
+ mStr += u'\t';
+ break;
+ case u'n':
+ mStr += u'\n';
+ break;
+ case u'#':
+ mStr += u'#';
+ break;
+ case u'@':
+ mStr += u'@';
+ break;
+ case u'?':
+ mStr += u'?';
+ break;
+ case u'"':
+ mStr += u'"';
+ break;
+ case u'\'':
+ mStr += u'\'';
+ break;
+ case u'\\':
+ mStr += u'\\';
+ break;
+ case u'u': {
+ current++;
+ Maybe<char16_t> c = parseUnicodeCodepoint(¤t, end);
+ if (!c) {
+ mError = "invalid unicode escape sequence";
+ return *this;
+ }
+ mStr += c.value();
+ current -= 1;
+ break;
+ }
+
+ default:
+ // Ignore.
+ break;
+ }
+ mLastCharWasEscape = false;
+ start = current + 1;
+ } else if (*current == u'"') {
+ if (!mQuote && mTrailingSpace) {
+ // We found an opening quote, and we have
+ // trailing space, so we should append that
+ // space now.
+ if (mTrailingSpace) {
+ // We had trailing whitespace, so
+ // replace with a single space.
+ if (!mStr.empty()) {
+ mStr += u' ';
+ }
+ mTrailingSpace = false;
+ }
+ }
+ mQuote = !mQuote;
+ mStr.append(start, current - start);
+ start = current + 1;
+ } else if (*current == u'\'' && !mQuote) {
+ // This should be escaped.
+ mError = "unescaped apostrophe";
+ return *this;
+ } else if (*current == u'\\') {
+ // This is an escape sequence, convert to the real value.
+ if (!mQuote && mTrailingSpace) {
+ // We had trailing whitespace, so
+ // replace with a single space.
+ if (!mStr.empty()) {
+ mStr += u' ';
+ }
+ mTrailingSpace = false;
+ }
+ mStr.append(start, current - start);
+ start = current + 1;
+ mLastCharWasEscape = true;
+ } else if (!mQuote) {
+ // This is not quoted text, so look for whitespace.
+ if (isspace16(*current)) {
+ // We found whitespace, see if we have seen some
+ // before.
+ if (!mTrailingSpace) {
+ // We didn't see a previous adjacent space,
+ // so mark that we did.
+ mTrailingSpace = true;
+ mStr.append(start, current - start);
+ }
+
+ // Keep skipping whitespace.
+ start = current + 1;
+ } else if (mTrailingSpace) {
+ // We saw trailing space before, so replace all
+ // that trailing space with one space.
+ if (!mStr.empty()) {
+ mStr += u' ';
+ }
+ mTrailingSpace = false;
+ }
+ }
+ current++;
+ }
+ mStr.append(start, end - start);
+ return *this;
+}
+
+std::u16string utf8ToUtf16(const StringPiece& utf8) {
+ ssize_t utf16Length = utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(utf8.data()),
+ utf8.length());
+ if (utf16Length <= 0) {
+ return {};
+ }
+
+ std::u16string utf16;
+ utf16.resize(utf16Length);
+ utf8_to_utf16(reinterpret_cast<const uint8_t*>(utf8.data()), utf8.length(), &*utf16.begin());
+ return utf16;
+}
+
+std::string utf16ToUtf8(const StringPiece16& utf16) {
+ ssize_t utf8Length = utf16_to_utf8_length(utf16.data(), utf16.length());
+ if (utf8Length <= 0) {
+ return {};
+ }
+
+ std::string utf8;
+ utf8.resize(utf8Length);
+ utf16_to_utf8(utf16.data(), utf16.length(), &*utf8.begin());
+ return utf8;
+}
+
+bool writeAll(std::ostream& out, const BigBuffer& buffer) {
+ for (const auto& b : buffer) {
+ if (!out.write(reinterpret_cast<const char*>(b.buffer.get()), b.size)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+std::unique_ptr<uint8_t[]> copy(const BigBuffer& buffer) {
+ std::unique_ptr<uint8_t[]> data = std::unique_ptr<uint8_t[]>(new uint8_t[buffer.size()]);
+ uint8_t* p = data.get();
+ for (const auto& block : buffer) {
+ memcpy(p, block.buffer.get(), block.size);
+ p += block.size;
+ }
+ return data;
+}
+
+Maybe<std::u16string> extractPackageFromNamespace(const std::u16string& namespaceUri) {
+ if (stringStartsWith<char16_t>(namespaceUri, kSchemaPrefix)) {
+ StringPiece16 schemaPrefix = kSchemaPrefix;
+ StringPiece16 package = namespaceUri;
+ return package.substr(schemaPrefix.size(), package.size() - schemaPrefix.size())
+ .toString();
+ } else if (namespaceUri == kSchemaAuto) {
+ return std::u16string();
+ }
+ return {};
+}
+
+bool extractResFilePathParts(const StringPiece16& path, StringPiece16* outPrefix,
+ StringPiece16* outEntry, StringPiece16* outSuffix) {
+ if (!stringStartsWith<char16_t>(path, u"res/")) {
+ return false;
+ }
+
+ StringPiece16::const_iterator lastOccurence = path.end();
+ for (auto iter = path.begin() + StringPiece16(u"res/").size(); iter != path.end(); ++iter) {
+ if (*iter == u'/') {
+ lastOccurence = iter;
+ }
+ }
+
+ if (lastOccurence == path.end()) {
+ return false;
+ }
+
+ auto iter = std::find(lastOccurence, path.end(), u'.');
+ *outSuffix = StringPiece16(iter, path.end() - iter);
+ *outEntry = StringPiece16(lastOccurence + 1, iter - lastOccurence - 1);
+ *outPrefix = StringPiece16(path.begin(), lastOccurence - path.begin() + 1);
+ return true;
+}
+
+} // namespace util
+} // namespace aapt
diff --git a/tools/aapt2/util/Util.h b/tools/aapt2/util/Util.h
new file mode 100644
index 0000000..80552a5
--- /dev/null
+++ b/tools/aapt2/util/Util.h
@@ -0,0 +1,364 @@
+/*
+ * 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_UTIL_H
+#define AAPT_UTIL_H
+
+#include "util/BigBuffer.h"
+#include "util/Maybe.h"
+#include "util/StringPiece.h"
+
+#include <androidfw/ResourceTypes.h>
+#include <functional>
+#include <memory>
+#include <ostream>
+#include <string>
+#include <vector>
+
+namespace aapt {
+namespace util {
+
+std::vector<std::string> split(const StringPiece& str, char sep);
+std::vector<std::string> splitAndLowercase(const StringPiece& str, char sep);
+
+/**
+ * Returns true if the string starts with prefix.
+ */
+template <typename T>
+bool stringStartsWith(const BasicStringPiece<T>& str, const BasicStringPiece<T>& prefix) {
+ if (str.size() < prefix.size()) {
+ return false;
+ }
+ return str.substr(0, prefix.size()) == prefix;
+}
+
+/**
+ * Returns true if the string ends with suffix.
+ */
+template <typename T>
+bool stringEndsWith(const BasicStringPiece<T>& str, const BasicStringPiece<T>& suffix) {
+ if (str.size() < suffix.size()) {
+ return false;
+ }
+ return str.substr(str.size() - suffix.size(), suffix.size()) == suffix;
+}
+
+/**
+ * Creates a new StringPiece16 that points to a substring
+ * of the original string without leading or trailing whitespace.
+ */
+StringPiece16 trimWhitespace(const StringPiece16& str);
+
+StringPiece trimWhitespace(const StringPiece& str);
+
+/**
+ * UTF-16 isspace(). It basically checks for lower range characters that are
+ * whitespace.
+ */
+inline bool isspace16(char16_t c) {
+ return c < 0x0080 && isspace(c);
+}
+
+/**
+ * Returns an iterator to the first character that is not alpha-numeric and that
+ * is not in the allowedChars set.
+ */
+StringPiece16::const_iterator findNonAlphaNumericAndNotInSet(const StringPiece16& str,
+ const StringPiece16& allowedChars);
+
+/**
+ * Tests that the string is a valid Java class name.
+ */
+bool isJavaClassName(const StringPiece16& str);
+
+/**
+ * Tests that the string is a valid Java package name.
+ */
+bool isJavaPackageName(const StringPiece16& str);
+
+/**
+ * Converts the class name to a fully qualified class name from the given `package`. Ex:
+ *
+ * asdf --> package.asdf
+ * .asdf --> package.asdf
+ * .a.b --> package.a.b
+ * asdf.adsf --> asdf.adsf
+ */
+Maybe<std::u16string> getFullyQualifiedClassName(const StringPiece16& package,
+ const StringPiece16& className);
+
+
+/**
+ * Makes a std::unique_ptr<> with the template parameter inferred by the compiler.
+ * This will be present in C++14 and can be removed then.
+ */
+template <typename T, class... Args>
+std::unique_ptr<T> make_unique(Args&&... args) {
+ return std::unique_ptr<T>(new T{std::forward<Args>(args)...});
+}
+
+/**
+ * Writes a set of items to the std::ostream, joining the times with the provided
+ * separator.
+ */
+template <typename Iterator>
+::std::function<::std::ostream&(::std::ostream&)> joiner(Iterator begin, Iterator end,
+ const char* sep) {
+ return [begin, end, sep](::std::ostream& out) -> ::std::ostream& {
+ for (auto iter = begin; iter != end; ++iter) {
+ if (iter != begin) {
+ out << sep;
+ }
+ out << *iter;
+ }
+ return out;
+ };
+}
+
+inline ::std::function<::std::ostream&(::std::ostream&)> formatSize(size_t size) {
+ return [size](::std::ostream& out) -> ::std::ostream& {
+ constexpr size_t K = 1024u;
+ constexpr size_t M = K * K;
+ constexpr size_t G = M * K;
+ if (size < K) {
+ out << size << "B";
+ } else if (size < M) {
+ out << (double(size) / K) << " KiB";
+ } else if (size < G) {
+ out << (double(size) / M) << " MiB";
+ } else {
+ out << (double(size) / G) << " GiB";
+ }
+ return out;
+ };
+}
+
+/**
+ * Helper method to extract a string from a StringPool.
+ */
+inline StringPiece16 getString(const android::ResStringPool& pool, size_t idx) {
+ size_t len;
+ const char16_t* str = pool.stringAt(idx, &len);
+ if (str != nullptr) {
+ return StringPiece16(str, len);
+ }
+ return StringPiece16();
+}
+
+/**
+ * Checks that the Java string format contains no non-positional arguments (arguments without
+ * explicitly specifying an index) when there are more than one argument. This is an error
+ * because translations may rearrange the order of the arguments in the string, which will
+ * break the string interpolation.
+ */
+bool verifyJavaStringFormat(const StringPiece16& str);
+
+class StringBuilder {
+public:
+ StringBuilder& append(const StringPiece16& str);
+ const std::u16string& str() const;
+ const std::string& error() const;
+ operator bool() const;
+
+private:
+ std::u16string mStr;
+ bool mQuote = false;
+ bool mTrailingSpace = false;
+ bool mLastCharWasEscape = false;
+ std::string mError;
+};
+
+inline const std::u16string& StringBuilder::str() const {
+ return mStr;
+}
+
+inline const std::string& StringBuilder::error() const {
+ return mError;
+}
+
+inline StringBuilder::operator bool() const {
+ return mError.empty();
+}
+
+/**
+ * Converts a UTF8 string to a UTF16 string.
+ */
+std::u16string utf8ToUtf16(const StringPiece& utf8);
+std::string utf16ToUtf8(const StringPiece16& utf8);
+
+/**
+ * Writes the entire BigBuffer to the output stream.
+ */
+bool writeAll(std::ostream& out, const BigBuffer& buffer);
+
+/*
+ * Copies the entire BigBuffer into a single buffer.
+ */
+std::unique_ptr<uint8_t[]> copy(const BigBuffer& buffer);
+
+/**
+ * A Tokenizer implemented as an iterable collection. It does not allocate
+ * any memory on the heap nor use standard containers.
+ */
+template <typename Char>
+class Tokenizer {
+public:
+ class iterator {
+ public:
+ iterator(const iterator&) = default;
+ iterator& operator=(const iterator&) = default;
+
+ iterator& operator++();
+ BasicStringPiece<Char> operator*();
+ bool operator==(const iterator& rhs) const;
+ bool operator!=(const iterator& rhs) const;
+
+ private:
+ friend class Tokenizer<Char>;
+
+ iterator(BasicStringPiece<Char> s, Char sep, BasicStringPiece<Char> tok);
+
+ BasicStringPiece<Char> str;
+ Char separator;
+ BasicStringPiece<Char> token;
+ };
+
+ Tokenizer(BasicStringPiece<Char> str, Char sep);
+ iterator begin();
+ iterator end();
+
+private:
+ const iterator mBegin;
+ const iterator mEnd;
+};
+
+template <typename Char>
+inline Tokenizer<Char> tokenize(BasicStringPiece<Char> str, Char sep) {
+ return Tokenizer<Char>(str, sep);
+}
+
+template <typename Char>
+typename Tokenizer<Char>::iterator& Tokenizer<Char>::iterator::operator++() {
+ const Char* start = token.end();
+ const Char* end = str.end();
+ if (start == end) {
+ token.assign(token.end(), 0);
+ return *this;
+ }
+
+ start += 1;
+ const Char* current = start;
+ while (current != end) {
+ if (*current == separator) {
+ token.assign(start, current - start);
+ return *this;
+ }
+ ++current;
+ }
+ token.assign(start, end - start);
+ return *this;
+}
+
+template <typename Char>
+inline BasicStringPiece<Char> Tokenizer<Char>::iterator::operator*() {
+ return token;
+}
+
+template <typename Char>
+inline bool Tokenizer<Char>::iterator::operator==(const iterator& rhs) const {
+ // We check equality here a bit differently.
+ // We need to know that the addresses are the same.
+ return token.begin() == rhs.token.begin() && token.end() == rhs.token.end();
+}
+
+template <typename Char>
+inline bool Tokenizer<Char>::iterator::operator!=(const iterator& rhs) const {
+ return !(*this == rhs);
+}
+
+template <typename Char>
+inline Tokenizer<Char>::iterator::iterator(BasicStringPiece<Char> s, Char sep,
+ BasicStringPiece<Char> tok) :
+ str(s), separator(sep), token(tok) {
+}
+
+template <typename Char>
+inline typename Tokenizer<Char>::iterator Tokenizer<Char>::begin() {
+ return mBegin;
+}
+
+template <typename Char>
+inline typename Tokenizer<Char>::iterator Tokenizer<Char>::end() {
+ return mEnd;
+}
+
+template <typename Char>
+inline Tokenizer<Char>::Tokenizer(BasicStringPiece<Char> str, Char sep) :
+ mBegin(++iterator(str, sep, BasicStringPiece<Char>(str.begin() - 1, 0))),
+ mEnd(str, sep, BasicStringPiece<Char>(str.end(), 0)) {
+}
+
+inline uint16_t hostToDevice16(uint16_t value) {
+ return htods(value);
+}
+
+inline uint32_t hostToDevice32(uint32_t value) {
+ return htodl(value);
+}
+
+inline uint16_t deviceToHost16(uint16_t value) {
+ return dtohs(value);
+}
+
+inline uint32_t deviceToHost32(uint32_t value) {
+ return dtohl(value);
+}
+
+/**
+ * Returns a package name if the namespace URI is of the form:
+ * http://schemas.android.com/apk/res/<package>
+ *
+ * Special case: if namespaceUri is http://schemas.android.com/apk/res-auto,
+ * returns an empty package name.
+ */
+Maybe<std::u16string> extractPackageFromNamespace(const std::u16string& namespaceUri);
+
+/**
+ * Given a path like: res/xml-sw600dp/foo.xml
+ *
+ * Extracts "res/xml-sw600dp/" into outPrefix.
+ * Extracts "foo" into outEntry.
+ * Extracts ".xml" into outSuffix.
+ *
+ * Returns true if successful.
+ */
+bool extractResFilePathParts(const StringPiece16& path, StringPiece16* outPrefix,
+ StringPiece16* outEntry, StringPiece16* outSuffix);
+
+} // namespace util
+
+/**
+ * Stream operator for functions. Calls the function with the stream as an argument.
+ * In the aapt namespace for lookup.
+ */
+inline ::std::ostream& operator<<(::std::ostream& out,
+ ::std::function<::std::ostream&(::std::ostream&)> f) {
+ return f(out);
+}
+
+} // namespace aapt
+
+#endif // AAPT_UTIL_H
diff --git a/tools/aapt2/util/Util_test.cpp b/tools/aapt2/util/Util_test.cpp
new file mode 100644
index 0000000..9db9fb7
--- /dev/null
+++ b/tools/aapt2/util/Util_test.cpp
@@ -0,0 +1,196 @@
+/*
+ * 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.
+ */
+
+#include "test/Common.h"
+#include "util/StringPiece.h"
+#include "util/Util.h"
+
+#include <gtest/gtest.h>
+#include <string>
+
+namespace aapt {
+
+TEST(UtilTest, TrimOnlyWhitespace) {
+ const std::u16string full = u"\n ";
+
+ StringPiece16 trimmed = util::trimWhitespace(full);
+ EXPECT_TRUE(trimmed.empty());
+ EXPECT_EQ(0u, trimmed.size());
+}
+
+TEST(UtilTest, StringEndsWith) {
+ EXPECT_TRUE(util::stringEndsWith<char>("hello.xml", ".xml"));
+}
+
+TEST(UtilTest, StringStartsWith) {
+ EXPECT_TRUE(util::stringStartsWith<char>("hello.xml", "he"));
+}
+
+TEST(UtilTest, StringBuilderSplitEscapeSequence) {
+ EXPECT_EQ(StringPiece16(u"this is a new\nline."),
+ util::StringBuilder().append(u"this is a new\\")
+ .append(u"nline.")
+ .str());
+}
+
+TEST(UtilTest, StringBuilderWhitespaceRemoval) {
+ EXPECT_EQ(StringPiece16(u"hey guys this is so cool"),
+ util::StringBuilder().append(u" hey guys ")
+ .append(u" this is so cool ")
+ .str());
+
+ EXPECT_EQ(StringPiece16(u" wow, so many \t spaces. what?"),
+ util::StringBuilder().append(u" \" wow, so many \t ")
+ .append(u"spaces. \"what? ")
+ .str());
+
+ EXPECT_EQ(StringPiece16(u"where is the pie?"),
+ util::StringBuilder().append(u" where \t ")
+ .append(u" \nis the "" pie?")
+ .str());
+}
+
+TEST(UtilTest, StringBuilderEscaping) {
+ EXPECT_EQ(StringPiece16(u"hey guys\n this \t is so\\ cool"),
+ util::StringBuilder().append(u" hey guys\\n ")
+ .append(u" this \\t is so\\\\ cool ")
+ .str());
+
+ EXPECT_EQ(StringPiece16(u"@?#\\\'"),
+ util::StringBuilder().append(u"\\@\\?\\#\\\\\\'")
+ .str());
+}
+
+TEST(UtilTest, StringBuilderMisplacedQuote) {
+ util::StringBuilder builder{};
+ EXPECT_FALSE(builder.append(u"they're coming!"));
+}
+
+TEST(UtilTest, StringBuilderUnicodeCodes) {
+ EXPECT_EQ(StringPiece16(u"\u00AF\u0AF0 woah"),
+ util::StringBuilder().append(u"\\u00AF\\u0AF0 woah")
+ .str());
+
+ EXPECT_FALSE(util::StringBuilder().append(u"\\u00 yo"));
+}
+
+TEST(UtilTest, TokenizeInput) {
+ auto tokenizer = util::tokenize(StringPiece16(u"this| is|the|end"), u'|');
+ auto iter = tokenizer.begin();
+ ASSERT_EQ(*iter, StringPiece16(u"this"));
+ ++iter;
+ ASSERT_EQ(*iter, StringPiece16(u" is"));
+ ++iter;
+ ASSERT_EQ(*iter, StringPiece16(u"the"));
+ ++iter;
+ ASSERT_EQ(*iter, StringPiece16(u"end"));
+ ++iter;
+ ASSERT_EQ(tokenizer.end(), iter);
+}
+
+TEST(UtilTest, TokenizeAtEnd) {
+ auto tokenizer = util::tokenize(StringPiece16(u"one."), u'.');
+ auto iter = tokenizer.begin();
+ ASSERT_EQ(*iter, StringPiece16(u"one"));
+ ++iter;
+ ASSERT_NE(iter, tokenizer.end());
+ ASSERT_EQ(*iter, StringPiece16());
+}
+
+TEST(UtilTest, IsJavaClassName) {
+ EXPECT_TRUE(util::isJavaClassName(u"android.test.Class"));
+ EXPECT_TRUE(util::isJavaClassName(u"android.test.Class$Inner"));
+ EXPECT_TRUE(util::isJavaClassName(u"android_test.test.Class"));
+ EXPECT_TRUE(util::isJavaClassName(u"_android_.test._Class_"));
+ EXPECT_FALSE(util::isJavaClassName(u"android.test.$Inner"));
+ EXPECT_FALSE(util::isJavaClassName(u"android.test.Inner$"));
+ EXPECT_FALSE(util::isJavaClassName(u".test.Class"));
+ EXPECT_FALSE(util::isJavaClassName(u"android"));
+}
+
+TEST(UtilTest, IsJavaPackageName) {
+ EXPECT_TRUE(util::isJavaPackageName(u"android"));
+ EXPECT_TRUE(util::isJavaPackageName(u"android.test"));
+ EXPECT_TRUE(util::isJavaPackageName(u"android.test_thing"));
+ EXPECT_FALSE(util::isJavaPackageName(u"_android"));
+ EXPECT_FALSE(util::isJavaPackageName(u"android_"));
+ EXPECT_FALSE(util::isJavaPackageName(u"android."));
+ EXPECT_FALSE(util::isJavaPackageName(u".android"));
+ EXPECT_FALSE(util::isJavaPackageName(u"android._test"));
+ EXPECT_FALSE(util::isJavaPackageName(u".."));
+}
+
+TEST(UtilTest, FullyQualifiedClassName) {
+ Maybe<std::u16string> res = util::getFullyQualifiedClassName(u"android", u"asdf");
+ AAPT_ASSERT_TRUE(res);
+ EXPECT_EQ(res.value(), u"android.asdf");
+
+ res = util::getFullyQualifiedClassName(u"android", u".asdf");
+ AAPT_ASSERT_TRUE(res);
+ EXPECT_EQ(res.value(), u"android.asdf");
+
+ res = util::getFullyQualifiedClassName(u"android", u".a.b");
+ AAPT_ASSERT_TRUE(res);
+ EXPECT_EQ(res.value(), u"android.a.b");
+
+ res = util::getFullyQualifiedClassName(u"android", u"a.b");
+ AAPT_ASSERT_TRUE(res);
+ EXPECT_EQ(res.value(), u"a.b");
+
+ res = util::getFullyQualifiedClassName(u"", u"a.b");
+ AAPT_ASSERT_TRUE(res);
+ EXPECT_EQ(res.value(), u"a.b");
+
+ res = util::getFullyQualifiedClassName(u"", u"");
+ AAPT_ASSERT_FALSE(res);
+
+ res = util::getFullyQualifiedClassName(u"android", u"./Apple");
+ AAPT_ASSERT_FALSE(res);
+}
+
+TEST(UtilTest, ExtractResourcePathComponents) {
+ StringPiece16 prefix, entry, suffix;
+ ASSERT_TRUE(util::extractResFilePathParts(u"res/xml-sw600dp/entry.xml", &prefix, &entry,
+ &suffix));
+ EXPECT_EQ(prefix, u"res/xml-sw600dp/");
+ EXPECT_EQ(entry, u"entry");
+ EXPECT_EQ(suffix, u".xml");
+
+ ASSERT_TRUE(util::extractResFilePathParts(u"res/xml-sw600dp/entry.9.png", &prefix, &entry,
+ &suffix));
+
+ EXPECT_EQ(prefix, u"res/xml-sw600dp/");
+ EXPECT_EQ(entry, u"entry");
+ EXPECT_EQ(suffix, u".9.png");
+
+ EXPECT_FALSE(util::extractResFilePathParts(u"AndroidManifest.xml", &prefix, &entry, &suffix));
+ EXPECT_FALSE(util::extractResFilePathParts(u"res/.xml", &prefix, &entry, &suffix));
+
+ ASSERT_TRUE(util::extractResFilePathParts(u"res//.", &prefix, &entry, &suffix));
+ EXPECT_EQ(prefix, u"res//");
+ EXPECT_EQ(entry, u"");
+ EXPECT_EQ(suffix, u".");
+}
+
+TEST(UtilTest, VerifyJavaStringFormat) {
+ ASSERT_TRUE(util::verifyJavaStringFormat(u"%09.34f"));
+ ASSERT_TRUE(util::verifyJavaStringFormat(u"%9$.34f %8$"));
+ ASSERT_TRUE(util::verifyJavaStringFormat(u"%% %%"));
+ ASSERT_FALSE(util::verifyJavaStringFormat(u"%09$f %f"));
+ ASSERT_FALSE(util::verifyJavaStringFormat(u"%09f %08s"));
+}
+
+} // namespace aapt
diff --git a/tools/layoutlib/bridge/src/android/text/Hyphenator_Delegate.java b/tools/layoutlib/bridge/src/android/text/Hyphenator_Delegate.java
index 5a59597..44ce731 100644
--- a/tools/layoutlib/bridge/src/android/text/Hyphenator_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/text/Hyphenator_Delegate.java
@@ -20,9 +20,10 @@
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
import java.io.File;
+import java.nio.ByteBuffer;
/**
- * Delegate that overrides implementation for certain methods in {@link android.text.StaticLayout}
+ * Delegate that overrides implementation for certain methods in {@link android.text.Hyphenator}
* <p/>
* Through the layoutlib_create tool, selected methods of StaticLayout have been replaced
* by calls to methods of the same name in this delegate class.
@@ -38,7 +39,7 @@
return null;
}
- /*package*/ static long loadHyphenator(String patternData) {
+ /*package*/ static long loadHyphenator(ByteBuffer buf, int offset) {
return sDelegateManager.addNewDelegate(new Hyphenator_Delegate());
}
}
diff --git a/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java b/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java
index 1b0ba51..65c0a07 100644
--- a/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java
@@ -13,6 +13,7 @@
import android.text.Primitive.PrimitiveType;
import android.text.StaticLayout.LineBreaks;
+import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -52,8 +53,8 @@
}
@LayoutlibDelegate
- /*package*/ static long nLoadHyphenator(String patternData) {
- return Hyphenator_Delegate.loadHyphenator(patternData);
+ /*package*/ static long nLoadHyphenator(ByteBuffer buf, int offset) {
+ return Hyphenator_Delegate.loadHyphenator(buf, offset);
}
@LayoutlibDelegate
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 0da6bb6..3c260a8 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -241,8 +241,15 @@
}
@Override
+ public void overridePendingAppTransitionMultiThumbFuture(
+ IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback startedCallback,
+ boolean scaleUp) throws RemoteException {
+
+ }
+
+ @Override
public void overridePendingAppTransitionMultiThumb(AppTransitionAnimationSpec[] specs,
- IRemoteCallback callback, boolean scaleUp) {
+ IRemoteCallback callback0, IRemoteCallback callback1, boolean scaleUp) {
// TODO Auto-generated method stub
}
@@ -513,4 +520,25 @@
// TODO Auto-generated method stub
return null;
}
+
+ @Override
+ public int getDockedStackSide() throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public void setDockedStackResizing(boolean resizing) throws RemoteException {
+ }
+
+ @Override
+ public void cancelTaskWindowTransition(int taskId) {
+ }
+
+ @Override
+ public void cancelTaskThumbnailTransition(int taskId) {
+ }
+
+ @Override
+ public void endProlongedAnimations() {
+ }
}
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 b2dc29a..0fcfa78 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
@@ -73,6 +73,7 @@
import android.os.Parcel;
import android.os.PowerManager;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.os.UserHandle;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
@@ -409,7 +410,7 @@
pushParser(blockParser);
return Pair.of(
mBridgeInflater.inflate(blockParser, parent, attachToRoot),
- true);
+ Boolean.TRUE);
} finally {
popParser();
}
@@ -447,7 +448,7 @@
pushParser(blockParser);
return Pair.of(
mBridgeInflater.inflate(blockParser, parent, attachToRoot),
- false);
+ Boolean.FALSE);
} finally {
popParser();
}
@@ -470,7 +471,7 @@
resource.getName()), null);
}
- return Pair.of(null, false);
+ return Pair.of(null, Boolean.FALSE);
}
@SuppressWarnings("deprecation")
@@ -1130,6 +1131,11 @@
public boolean unlinkToDeath(DeathRecipient recipient, int flags) {
return false;
}
+
+ @Override
+ public void shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ String[] args, ResultReceiver resultReceiver) {
+ }
};
}
return mBinder;
@@ -1365,6 +1371,18 @@
}
@Override
+ public File getDeviceEncryptedFilesDir() {
+ // pass
+ return null;
+ }
+
+ @Override
+ public File getCredentialEncryptedFilesDir() {
+ // pass
+ return null;
+ }
+
+ @Override
public File getNoBackupFilesDir() {
// pass
return null;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
index a410c53..e9b7819 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
@@ -152,6 +152,11 @@
}
@Override
+ public boolean isLightDeviceIdleMode() throws RemoteException {
+ return false;
+ }
+
+ @Override
public boolean isScreenBrightnessBoosted() throws RemoteException {
return false;
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
index c2d8d0c..3662573 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
@@ -48,7 +48,7 @@
@Override
public void resized(Rect rect, Rect rect2, Rect rect3, Rect rect4, Rect rect5, Rect rect6,
- boolean b, Configuration configuration) throws RemoteException {
+ boolean b, Configuration configuration, Rect rect7) throws RemoteException {
// pass for now.
}
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 2997907..1ec0547 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
@@ -95,6 +95,13 @@
}
@Override
+ public void repositionChild(IWindow childWindow, int x, int y, int width, int height,
+ long deferTransactionUntilFrame, Rect outFrame) {
+ // pass for now.
+ return;
+ }
+
+ @Override
public void performDeferredDestroy(IWindow window) {
// pass for now.
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
index 567002e..42e55e2 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
@@ -117,11 +117,11 @@
density = iconLoader.getDensity();
String path = iconLoader.getPath();
// look for a cached bitmap
- Bitmap bitmap = Bridge.getCachedBitmap(path, true /*isFramework*/);
+ Bitmap bitmap = Bridge.getCachedBitmap(path, Boolean.TRUE /*isFramework*/);
if (bitmap == null) {
try {
bitmap = Bitmap_Delegate.createBitmap(stream, false /*isMutable*/, density);
- Bridge.setCachedBitmap(path, bitmap, true /*isFramework*/);
+ Bridge.setCachedBitmap(path, bitmap, Boolean.TRUE /*isFramework*/);
} catch (IOException e) {
return;
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java
index 9588035..80d7c68 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java
@@ -21,6 +21,7 @@
import android.graphics.BlendComposite;
import android.graphics.BlendComposite.BlendingMode;
+import android.graphics.PorterDuff;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffColorFilter_Delegate;
import android.graphics.PorterDuffXfermode_Delegate;
@@ -34,6 +35,8 @@
*/
public final class PorterDuffUtility {
+ private static final int MODES_COUNT = Mode.values().length;
+
// Make the class non-instantiable.
private PorterDuffUtility() {
}
@@ -43,12 +46,11 @@
* {@link Mode#SRC_OVER} for invalid modes.
*/
public static Mode getPorterDuffMode(int porterDuffMode) {
- Mode[] values = Mode.values();
- if (porterDuffMode >= 0 && porterDuffMode < values.length) {
- return values[porterDuffMode];
+ if (porterDuffMode >= 0 && porterDuffMode < MODES_COUNT) {
+ return PorterDuff.intToMode(porterDuffMode);
}
Bridge.getLog().error(LayoutLog.TAG_BROKEN,
- String.format("Unknown PorterDuff.Mode: %1$d", porterDuffMode), null /*data*/);
+ String.format("Unknown PorterDuff.Mode: %1$d", porterDuffMode), null);
assert false;
return Mode.SRC_OVER;
}
diff --git a/wifi/java/android/net/wifi/BatchedScanResult.aidl b/wifi/java/android/net/wifi/BatchedScanResult.aidl
deleted file mode 100644
index a70bc0a..0000000
--- a/wifi/java/android/net/wifi/BatchedScanResult.aidl
+++ /dev/null
@@ -1,19 +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 android.net.wifi;
-
-parcelable BatchedScanResult;
diff --git a/wifi/java/android/net/wifi/BatchedScanResult.java b/wifi/java/android/net/wifi/BatchedScanResult.java
index 7598b92..5bafdd8 100644
--- a/wifi/java/android/net/wifi/BatchedScanResult.java
+++ b/wifi/java/android/net/wifi/BatchedScanResult.java
@@ -25,8 +25,9 @@
/**
* Describes the Results of a batched set of wifi scans where the firmware performs many
* scans and stores the timestamped results without waking the main processor each time.
- * @hide pending review
+ * @hide
*/
+@Deprecated
public class BatchedScanResult implements Parcelable {
private static final String TAG = "BatchedScanResult";
diff --git a/wifi/java/android/net/wifi/BatchedScanSettings.aidl b/wifi/java/android/net/wifi/BatchedScanSettings.aidl
deleted file mode 100644
index 8cfc508..0000000
--- a/wifi/java/android/net/wifi/BatchedScanSettings.aidl
+++ /dev/null
@@ -1,19 +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 android.net.wifi;
-
-parcelable BatchedScanSettings;
diff --git a/wifi/java/android/net/wifi/BatchedScanSettings.java b/wifi/java/android/net/wifi/BatchedScanSettings.java
deleted file mode 100644
index 54801ad..0000000
--- a/wifi/java/android/net/wifi/BatchedScanSettings.java
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * 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 android.net.wifi;
-
-import android.os.Parcelable;
-import android.os.Parcel;
-
-import java.util.ArrayList;
-import java.util.Collection;
-
-/**
- * Describes the settings for batched wifi scans where the firmware performs many
- * scans and stores the timestamped results without waking the main processor each time.
- * This can give information over time with minimal battery impact.
- * @hide pending review
- */
-public class BatchedScanSettings implements Parcelable {
- private static final String TAG = "BatchedScanSettings";
-
- /** Used to indicate no preference for an int value */
- public final static int UNSPECIFIED = Integer.MAX_VALUE;
-
- // TODO - make MIN/mAX as standard for wifi batch capability requirement.
- public final static int MIN_SCANS_PER_BATCH = 2;
- public final static int MAX_SCANS_PER_BATCH = 20;
- public final static int DEFAULT_SCANS_PER_BATCH = MAX_SCANS_PER_BATCH;
-
- public final static int MIN_AP_PER_SCAN = 2;
- public final static int MAX_AP_PER_SCAN = 16;
- public final static int DEFAULT_AP_PER_SCAN = 16;
-
- public final static int MIN_INTERVAL_SEC = 10;
- public final static int MAX_INTERVAL_SEC = 500;
- public final static int DEFAULT_INTERVAL_SEC = 30;
-
- public final static int MIN_AP_FOR_DISTANCE = 0;
- public final static int MAX_AP_FOR_DISTANCE = MAX_AP_PER_SCAN;
- public final static int DEFAULT_AP_FOR_DISTANCE = 0;
-
- public final static int MAX_WIFI_CHANNEL = 196;
-
- /** The expected number of scans per batch. Note that the firmware may drop scans
- * leading to fewer scans during the normal batch scan duration. This value need not
- * be specified (may be set to {@link UNSPECIFIED}) by the application and we will try
- * to scan as many times as the firmware can support. If another app requests fewer
- * scans per batch we will attempt to honor that.
- */
- public int maxScansPerBatch;
-
- /** The maximum desired AP listed per scan. Fewer AP may be returned if that's all
- * that the driver detected. If another application requests more AP per scan that
- * will take precedence. The if more channels are detected than we request, the APs
- * with the lowest signal strength will be dropped.
- */
- public int maxApPerScan;
-
- /** The channels used in the scan. If all channels should be used, {@code null} may be
- * specified. If another application requests more channels or all channels, that
- * will take precedence.
- */
- public Collection<String> channelSet;
-
- /** The time between the start of two sequential scans, in seconds. If another
- * application requests more frequent scans, that will take precedence. If this
- * value is less than the duration of a scan, the next scan should start immediately.
- */
- public int scanIntervalSec;
-
- /** The number of the best (strongest signal) APs for which the firmware will
- * attempt to get distance information (RTT). Not all firmware supports this
- * feature, so it may be ignored. If another application requests a greater
- * number, that will take precedence.
- */
- public int maxApForDistance;
-
- public BatchedScanSettings() {
- clear();
- }
-
- public void clear() {
- maxScansPerBatch = UNSPECIFIED;
- maxApPerScan = UNSPECIFIED;
- channelSet = null;
- scanIntervalSec = UNSPECIFIED;
- maxApForDistance = UNSPECIFIED;
- }
-
- public BatchedScanSettings(BatchedScanSettings source) {
- maxScansPerBatch = source.maxScansPerBatch;
- maxApPerScan = source.maxApPerScan;
- if (source.channelSet != null) {
- channelSet = new ArrayList(source.channelSet);
- }
- scanIntervalSec = source.scanIntervalSec;
- maxApForDistance = source.maxApForDistance;
- }
-
- private boolean channelSetIsValid() {
- if (channelSet == null || channelSet.isEmpty()) return true;
- for (String channel : channelSet) {
- try {
- int i = Integer.parseInt(channel);
- if (i > 0 && i <= MAX_WIFI_CHANNEL) continue;
- } catch (NumberFormatException e) {}
- if (channel.equals("A") || channel.equals("B")) continue;
- return false;
- }
- return true;
- }
- /** @hide */
- public boolean isInvalid() {
- if (maxScansPerBatch != UNSPECIFIED && (maxScansPerBatch < MIN_SCANS_PER_BATCH ||
- maxScansPerBatch > MAX_SCANS_PER_BATCH)) return true;
- if (maxApPerScan != UNSPECIFIED && (maxApPerScan < MIN_AP_PER_SCAN ||
- maxApPerScan > MAX_AP_PER_SCAN)) return true;
- if (channelSetIsValid() == false) return true;
- if (scanIntervalSec != UNSPECIFIED && (scanIntervalSec < MIN_INTERVAL_SEC ||
- scanIntervalSec > MAX_INTERVAL_SEC)) return true;
- if (maxApForDistance != UNSPECIFIED && (maxApForDistance < MIN_AP_FOR_DISTANCE ||
- maxApForDistance > MAX_AP_FOR_DISTANCE)) return true;
- return false;
- }
-
- /** @hide */
- public void constrain() {
- if (scanIntervalSec == UNSPECIFIED) {
- scanIntervalSec = DEFAULT_INTERVAL_SEC;
- } else if (scanIntervalSec < MIN_INTERVAL_SEC) {
- scanIntervalSec = MIN_INTERVAL_SEC;
- } else if (scanIntervalSec > MAX_INTERVAL_SEC) {
- scanIntervalSec = MAX_INTERVAL_SEC;
- }
-
- if (maxScansPerBatch == UNSPECIFIED) {
- maxScansPerBatch = DEFAULT_SCANS_PER_BATCH;
- } else if (maxScansPerBatch < MIN_SCANS_PER_BATCH) {
- maxScansPerBatch = MIN_SCANS_PER_BATCH;
- } else if (maxScansPerBatch > MAX_SCANS_PER_BATCH) {
- maxScansPerBatch = MAX_SCANS_PER_BATCH;
- }
-
- if (maxApPerScan == UNSPECIFIED) {
- maxApPerScan = DEFAULT_AP_PER_SCAN;
- } else if (maxApPerScan < MIN_AP_PER_SCAN) {
- maxApPerScan = MIN_AP_PER_SCAN;
- } else if (maxApPerScan > MAX_AP_PER_SCAN) {
- maxApPerScan = MAX_AP_PER_SCAN;
- }
-
- if (maxApForDistance == UNSPECIFIED) {
- maxApForDistance = DEFAULT_AP_FOR_DISTANCE;
- } else if (maxApForDistance < MIN_AP_FOR_DISTANCE) {
- maxApForDistance = MIN_AP_FOR_DISTANCE;
- } else if (maxApForDistance > MAX_AP_FOR_DISTANCE) {
- maxApForDistance = MAX_AP_FOR_DISTANCE;
- }
- }
-
-
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof BatchedScanSettings == false) return false;
- BatchedScanSettings o = (BatchedScanSettings)obj;
- if (maxScansPerBatch != o.maxScansPerBatch ||
- maxApPerScan != o.maxApPerScan ||
- scanIntervalSec != o.scanIntervalSec ||
- maxApForDistance != o.maxApForDistance) return false;
- if (channelSet == null) {
- return (o.channelSet == null);
- }
- return channelSet.equals(o.channelSet);
- }
-
- @Override
- public int hashCode() {
- return maxScansPerBatch +
- (maxApPerScan * 3) +
- (scanIntervalSec * 5) +
- (maxApForDistance * 7) +
- (channelSet.hashCode() * 11);
- }
-
- @Override
- public String toString() {
- StringBuffer sb = new StringBuffer();
- String none = "<none>";
-
- sb.append("BatchScanSettings [maxScansPerBatch: ").
- append(maxScansPerBatch == UNSPECIFIED ? none : maxScansPerBatch).
- append(", maxApPerScan: ").append(maxApPerScan == UNSPECIFIED? none : maxApPerScan).
- append(", scanIntervalSec: ").
- append(scanIntervalSec == UNSPECIFIED ? none : scanIntervalSec).
- append(", maxApForDistance: ").
- append(maxApForDistance == UNSPECIFIED ? none : maxApForDistance).
- append(", channelSet: ");
- if (channelSet == null) {
- sb.append("ALL");
- } else {
- sb.append("<");
- for (String channel : channelSet) {
- sb.append(" " + channel);
- }
- sb.append(">");
- }
- sb.append("]");
- return sb.toString();
- }
-
- /** Implement the Parcelable interface {@hide} */
- public int describeContents() {
- return 0;
- }
-
- /** Implement the Parcelable interface {@hide} */
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(maxScansPerBatch);
- dest.writeInt(maxApPerScan);
- dest.writeInt(scanIntervalSec);
- dest.writeInt(maxApForDistance);
- dest.writeInt(channelSet == null ? 0 : channelSet.size());
- if (channelSet != null) {
- for (String channel : channelSet) dest.writeString(channel);
- }
- }
-
- /** Implement the Parcelable interface {@hide} */
- public static final Creator<BatchedScanSettings> CREATOR =
- new Creator<BatchedScanSettings>() {
- public BatchedScanSettings createFromParcel(Parcel in) {
- BatchedScanSettings settings = new BatchedScanSettings();
- settings.maxScansPerBatch = in.readInt();
- settings.maxApPerScan = in.readInt();
- settings.scanIntervalSec = in.readInt();
- settings.maxApForDistance = in.readInt();
- int channelCount = in.readInt();
- if (channelCount > 0) {
- settings.channelSet = new ArrayList(channelCount);
- while (channelCount-- > 0) {
- settings.channelSet.add(in.readString());
- }
- }
- return settings;
- }
-
- public BatchedScanSettings[] newArray(int size) {
- return new BatchedScanSettings[size];
- }
- };
-}
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 23be8e0..bad4e9c 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -16,12 +16,9 @@
package android.net.wifi;
-import android.net.wifi.BatchedScanResult;
-import android.net.wifi.BatchedScanSettings;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.ScanSettings;
-import android.net.wifi.WifiChannel;
import android.net.wifi.ScanResult;
import android.net.wifi.ScanInfo;
import android.net.wifi.WifiConnectionStatistics;
@@ -61,12 +58,8 @@
boolean pingSupplicant();
- List<WifiChannel> getChannelList();
-
void startScan(in ScanSettings requested, in WorkSource ws);
- void startLocationRestrictedScan(in WorkSource ws);
-
List<ScanResult> getScanResults(String callingPackage);
void disconnect();
@@ -125,10 +118,6 @@
void setWifiApConfiguration(in WifiConfiguration wifiConfig);
- void startWifi();
-
- void stopWifi();
-
void addToBlacklist(String bssid);
void clearBlacklist();
@@ -141,16 +130,6 @@
void enableTdlsWithMacAddress(String remoteMacAddress, boolean enable);
- boolean requestBatchedScan(in BatchedScanSettings requested, IBinder binder, in WorkSource ws);
-
- void stopBatchedScan(in BatchedScanSettings requested);
-
- List<BatchedScanResult> getBatchedScanResults(String callingPackage);
-
- boolean isBatchedScanSupported();
-
- void pollBatchedScan();
-
String getWpsNfcConfigurationToken(int netId);
void enableVerboseLogging(int verbose);
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index 8a20012..dc06789 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -309,6 +309,15 @@
* @hide
*/
public static class InformationElement {
+ public static final int EID_SSID = 0;
+ public static final int EID_BSS_LOAD = 11;
+ public static final int EID_HT_OPERATION = 61;
+ public static final int EID_INTERWORKING = 107;
+ public static final int EID_ROAMING_CONSORTIUM = 111;
+ public static final int EID_EXTENDED_CAPS = 127;
+ public static final int EID_VHT_OPERATION = 192;
+ public static final int EID_VSA = 221;
+
public int id;
public byte[] bytes;
diff --git a/wifi/java/android/net/wifi/WifiChannel.aidl b/wifi/java/android/net/wifi/WifiChannel.aidl
deleted file mode 100644
index c3d06bd..0000000
--- a/wifi/java/android/net/wifi/WifiChannel.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * Copyright (c) 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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.wifi;
-
-parcelable WifiChannel;
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index ff8d6d4d..bd030e8 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -27,7 +27,6 @@
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.wifi.ScanSettings;
-import android.net.wifi.WifiChannel;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
@@ -440,8 +439,11 @@
/**
* A batch of access point scans has been completed and the results areavailable.
* Call {@link #getBatchedScanResults()} to obtain the results.
- * @hide pending review
+ * @deprecated This API is nolonger supported.
+ * Use {@link android.net.wifi.WifiScanner} API
+ * @hide
*/
+ @Deprecated
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String BATCHED_SCAN_RESULTS_AVAILABLE_ACTION =
"android.net.wifi.BATCHED_RESULTS";
@@ -904,22 +906,6 @@
}
}
- /**
- * Get a list of available channels for customized scan.
- *
- * @see {@link WifiChannel}
- *
- * @return the channel list, or null if not available
- * @hide
- */
- public List<WifiChannel> getChannelList() {
- try {
- return mService.getChannelList();
- } catch (RemoteException e) {
- return null;
- }
- }
-
/* Keep this list in sync with wifi_hal.h */
/** @hide */
public static final int WIFI_FEATURE_INFRA = 0x0001; // Basic infrastructure mode
@@ -1086,17 +1072,8 @@
public WifiActivityEnergyInfo getControllerActivityEnergyInfo(int updateType) {
if (mService == null) return null;
try {
- WifiActivityEnergyInfo record;
- if (!isEnhancedPowerReportingSupported()) {
- return null;
- }
synchronized(this) {
- record = mService.reportActivityInfo();
- if (record != null && record.isValid()) {
- return record;
- } else {
- return null;
- }
+ return mService.reportActivityInfo();
}
} catch (RemoteException e) {
Log.e(TAG, "getControllerActivityEnergyInfo: " + e);
@@ -1134,142 +1111,41 @@
* startLocationRestrictedScan()
* Trigger a scan which will not make use of DFS channels and is thus not suitable for
* establishing wifi connection.
+ * @deprecated This API is nolonger supported.
+ * Use {@link android.net.wifi.WifiScanner} API
* @hide
*/
+ @Deprecated
@SystemApi
public boolean startLocationRestrictedScan(WorkSource workSource) {
- try {
- mService.startLocationRestrictedScan(workSource);
- return true;
- } catch (RemoteException e) {
- return false;
- }
- }
-
- /**
- * Request a scan for access points in specified channel list. Each channel is specified by its
- * frequency in MHz, e.g. "5500" (do NOT include "DFS" even though it is). The availability of
- * the results is made known later in the same way as {@link #startScan}.
- *
- * Note:
- *
- * 1. Customized scan is for non-connection purposes, i.e. it won't trigger a wifi connection
- * even though it finds some known networks.
- *
- * 2. Customized scan result may include access points that is not specified in the channel
- * list. An app will need to do frequency filtering if it wants to get pure results for the
- * channel list it specified.
- *
- * @hide
- */
- public boolean startCustomizedScan(ScanSettings requested) {
- try {
- mService.startScan(requested, null);
- return true;
- } catch (RemoteException e) {
- return false;
- }
- }
-
- /** @hide */
- public boolean startCustomizedScan(ScanSettings requested, WorkSource workSource) {
- try {
- mService.startScan(requested, workSource);
- return true;
- } catch (RemoteException e) {
- return false;
- }
- }
-
- /**
- * Request a batched scan for access points. To end your requested batched scan,
- * call stopBatchedScan with the same Settings.
- *
- * If there are mulitple requests for batched scans, the more demanding settings will
- * take precidence.
- *
- * @param requested {@link BatchedScanSettings} the scan settings requested.
- * @return false on known error
- * @hide
- */
- public boolean requestBatchedScan(BatchedScanSettings requested) {
- try {
- return mService.requestBatchedScan(requested, new Binder(), null);
- } catch (RemoteException e) { return false; }
- }
- /** @hide */
- public boolean requestBatchedScan(BatchedScanSettings requested, WorkSource workSource) {
- try {
- return mService.requestBatchedScan(requested, new Binder(), workSource);
- } catch (RemoteException e) { return false; }
+ return false;
}
/**
* Check if the Batched Scan feature is supported.
*
* @return false if not supported.
+ * @deprecated This API is nolonger supported.
+ * Use {@link android.net.wifi.WifiScanner} API
* @hide
*/
+ @Deprecated
@SystemApi
public boolean isBatchedScanSupported() {
- try {
- return mService.isBatchedScanSupported();
- } catch (RemoteException e) { return false; }
- }
-
- /**
- * End a requested batch scan for this applicaiton. Note that batched scan may
- * still occur if other apps are using them.
- *
- * @param requested {@link BatchedScanSettings} the scan settings you previously requested
- * and now wish to stop. A value of null here will stop all scans requested by the
- * calling App.
- * @hide
- */
- public void stopBatchedScan(BatchedScanSettings requested) {
- try {
- mService.stopBatchedScan(requested);
- } catch (RemoteException e) {}
+ return false;
}
/**
* Retrieve the latest batched scan result. This should be called immediately after
* {@link BATCHED_SCAN_RESULTS_AVAILABLE_ACTION} is received.
+ * @deprecated This API is nolonger supported.
+ * Use {@link android.net.wifi.WifiScanner} API
* @hide
*/
+ @Deprecated
@SystemApi
public List<BatchedScanResult> getBatchedScanResults() {
- try {
- return mService.getBatchedScanResults(mContext.getOpPackageName());
- } catch (RemoteException e) {
- return null;
- }
- }
-
- /**
- * Force a re-reading of batched scan results. This will attempt
- * to read more information from the chip, but will do so at the expense
- * of previous data. Rate limited to the current scan frequency.
- *
- * pollBatchedScan will always wait 1 period from the start of the batch
- * before trying to read from the chip, so if your #scans/batch == 1 this will
- * have no effect.
- *
- * If you had already waited 1 period before calling, this should have
- * immediate (though async) effect.
- *
- * If you call before that 1 period is up this will set up a timer and fetch
- * results when the 1 period is up.
- *
- * Servicing a pollBatchedScan request (immediate or after timed delay) starts a
- * new batch, so if you were doing 10 scans/batch and called in the 4th scan, you
- * would get data in the 4th and then again 10 scans later.
- * @hide
- */
- public void pollBatchedScan() {
- try {
- mService.pollBatchedScan();
- } catch (RemoteException e) { }
+ return null;
}
/**
@@ -1634,48 +1510,6 @@
}
}
- /**
- * Start the driver and connect to network.
- *
- * This function will over-ride WifiLock and device idle status. For example,
- * even if the device is idle or there is only a scan-only lock held,
- * a start wifi would mean that wifi connection is kept active until
- * a stopWifi() is sent.
- *
- * This API is used by WifiStateTracker
- *
- * @return {@code true} if the operation succeeds else {@code false}
- * @hide
- */
- public boolean startWifi() {
- try {
- mService.startWifi();
- return true;
- } catch (RemoteException e) {
- return false;
- }
- }
-
- /**
- * Disconnect from a network (if any) and stop the driver.
- *
- * This function will over-ride WifiLock and device idle status. Wi-Fi
- * stays inactive until a startWifi() is issued.
- *
- * This API is used by WifiStateTracker
- *
- * @return {@code true} if the operation succeeds else {@code false}
- * @hide
- */
- public boolean stopWifi() {
- try {
- mService.stopWifi();
- return true;
- } catch (RemoteException e) {
- return false;
- }
- }
-
/**
* Add a bssid to the supplicant blacklist
*
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index a65f250..c26ca6e 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -709,6 +709,7 @@
validateChannel();
HotlistSettings settings = new HotlistSettings();
settings.bssidInfos = bssidInfos;
+ settings.apLostThreshold = apLostThreshold;
sAsyncChannel.sendMessage(CMD_SET_HOTLIST, 0, putListener(listener), settings);
}